Big update
This commit is contained in:
parent
a1f4960bca
commit
ea4a062aac
5 changed files with 272 additions and 154 deletions
|
@ -63,3 +63,6 @@ class PlayState(object):
|
|||
self.context_menu_play = False
|
||||
# Which Kodi player is/has been active? (either int 1, 2 or 3)
|
||||
self.active_players = set()
|
||||
# Have we initiated playback via Plex Companion or Alexa - so from the
|
||||
# Plex side of things?
|
||||
self.initiated_by_plex = False
|
||||
|
|
|
@ -322,12 +322,10 @@ class KodiMonitor(xbmc.Monitor):
|
|||
LOG.debug('Current Kodi playlist: %s', kodi_playlist)
|
||||
kodi_item = PL.playlist_item_from_kodi(kodi_playlist[position])
|
||||
if isinstance(self.playqueue.items[0], PL.PlaylistItemDummy):
|
||||
# Get rid of the very first element in the queue that Kodi marked
|
||||
# as unplayed (the one to init the queue)
|
||||
LOG.debug('Deleting the very first playqueue item')
|
||||
js.playlist_remove(self.playqueue.playlistid, 0)
|
||||
del self.playqueue.items[0]
|
||||
position = 0
|
||||
# This dummy item will be deleted by webservice soon - it won't
|
||||
# play
|
||||
LOG.debug('Dummy item detected')
|
||||
position = 1
|
||||
elif kodi_item != self.playqueue.items[position]:
|
||||
LOG.debug('Different playqueue items: %s vs. %s ',
|
||||
kodi_item, self.playqueue.items[position])
|
||||
|
@ -487,9 +485,6 @@ def _record_playstate(status, ended):
|
|||
playcount += 1
|
||||
time = 0
|
||||
with kodi_db.KodiVideoDB() as kodidb:
|
||||
LOG.error('Setting file_id %s, time %s, totaltime %s, playcount %s, '
|
||||
'last_played %s',
|
||||
db_item['kodi_fileid'], time, totaltime, playcount, last_played)
|
||||
kodidb.set_resume(db_item['kodi_fileid'],
|
||||
time,
|
||||
totaltime,
|
||||
|
|
|
@ -5,8 +5,7 @@ Collection of functions associated with Kodi and Plex playlists and playqueues
|
|||
"""
|
||||
from __future__ import absolute_import, division, unicode_literals
|
||||
from logging import getLogger
|
||||
|
||||
import xbmc
|
||||
import threading
|
||||
|
||||
from .plex_api import API
|
||||
from .plex_db import PlexDB
|
||||
|
@ -139,20 +138,59 @@ class PlayQueue(object):
|
|||
self.force_transcode = None
|
||||
LOG.debug('Playlist cleared: %s', self)
|
||||
|
||||
def init(self, plex_id, plex_type=None, position=None, synched=True,
|
||||
force_transcode=None):
|
||||
def play(self, plex_id, plex_type=None, startpos=None, position=None,
|
||||
synched=True, force_transcode=None):
|
||||
"""
|
||||
Initializes the playQueue with e.g. trailers and additional file parts
|
||||
Pass synched=False if you're sure that this item has not been synched
|
||||
to Kodi
|
||||
|
||||
Or resolves webservice paths to actual paths
|
||||
"""
|
||||
LOG.debug('Play called with plex_id %s, plex_type %s, position %s, '
|
||||
'synched %s, force_transcode %s, startpos %s', plex_id,
|
||||
plex_type, position, synched, force_transcode, startpos)
|
||||
resolve = False
|
||||
try:
|
||||
if plex_id == self.items[startpos].plex_id:
|
||||
resolve = True
|
||||
except IndexError:
|
||||
pass
|
||||
if resolve:
|
||||
LOG.info('Resolving playback')
|
||||
self._resolve(plex_id, startpos)
|
||||
else:
|
||||
LOG.info('Initializing playback')
|
||||
self.init(plex_id,
|
||||
plex_type,
|
||||
startpos,
|
||||
position,
|
||||
synched,
|
||||
force_transcode)
|
||||
|
||||
def _resolve(self, plex_id, startpos):
|
||||
"""
|
||||
The Plex playqueue has already been initialized. We resolve the path
|
||||
from original webservice http://127.0.0.1 to the "correct" Plex one
|
||||
"""
|
||||
self.index = startpos + 1
|
||||
xml = PF.GetPlexMetadata(plex_id)
|
||||
if xml in (None, 401):
|
||||
raise PlaylistError('Could not get Plex metadata %s for %s',
|
||||
plex_id, self.items[startpos])
|
||||
api = API(xml[0])
|
||||
resume = self._resume_playback(None, xml[0])
|
||||
self._kodi_add_xml(xml[0], api, resume)
|
||||
# Add additional file parts, if any exist
|
||||
self._add_additional_parts(xml)
|
||||
|
||||
def init(self, plex_id, plex_type=None, startpos=None, position=None,
|
||||
synched=True, force_transcode=None):
|
||||
"""
|
||||
Initializes the Plex and PKC playqueue for playback
|
||||
"""
|
||||
LOG.error('Current Kodi playlist: %s',
|
||||
js.playlist_get_items(self.playlistid))
|
||||
LOG.debug('Initializing with plex_id %s, plex_type %s, position %s, '
|
||||
'synched %s, force_transcode %s, index %s', plex_id,
|
||||
plex_type, position, synched, force_transcode, self.index)
|
||||
self.index = position
|
||||
if self.kodi_pl.size() != len(self.items):
|
||||
while len(self.items) < self.kodi_pl.size():
|
||||
# The original item that Kodi put into the playlist, e.g.
|
||||
# {
|
||||
# u'title': u'',
|
||||
|
@ -161,13 +199,10 @@ class PlayQueue(object):
|
|||
# u'label': u''
|
||||
# }
|
||||
# We CANNOT delete that item right now - so let's add a dummy
|
||||
# on the PKC side
|
||||
LOG.debug('Detected Kodi playlist size %s to be off for PKC: %s',
|
||||
self.kodi_pl.size(), len(self.items))
|
||||
while len(self.items) < self.kodi_pl.size():
|
||||
LOG.debug('Adding a dummy item to our playqueue')
|
||||
playlistitem = PlaylistItemDummy()
|
||||
self.items.insert(0, playlistitem)
|
||||
# on the PKC side to keep all indicees lined up.
|
||||
# The failing item will be deleted in webservice.py
|
||||
LOG.debug('Adding a dummy item to our playqueue')
|
||||
self.items.insert(0, PlaylistItemDummy())
|
||||
self.force_transcode = force_transcode
|
||||
if synched:
|
||||
with PlexDB(lock=False) as plexdb:
|
||||
|
@ -316,7 +351,11 @@ class PlayQueue(object):
|
|||
raise PlaylistError('Position %s too large for playlist length %s'
|
||||
% (pos, len(self.items)))
|
||||
LOG.debug('Adding item to Kodi playlist at position %s: %s', pos, item)
|
||||
if item.kodi_id is not None and item.kodi_type is not None:
|
||||
if listitem:
|
||||
self.kodi_pl.add(url=listitem.getPath(),
|
||||
listitem=listitem,
|
||||
index=pos)
|
||||
elif item.kodi_id is not None and item.kodi_type is not None:
|
||||
# This method ensures we have full Kodi metadata, potentially
|
||||
# with more artwork, for example, than Plex provides
|
||||
if pos == len(self.items):
|
||||
|
@ -330,24 +369,24 @@ class PlayQueue(object):
|
|||
raise PlaylistError('Kodi did not add item to playlist: %s',
|
||||
answ)
|
||||
else:
|
||||
if not listitem:
|
||||
if item.xml is None:
|
||||
LOG.debug('Need to get metadata for item %s', item)
|
||||
item.xml = PF.GetPlexMetadata(item.plex_id)
|
||||
if item.xml in (None, 401):
|
||||
raise PlaylistError('Could not get metadata for %s', item)
|
||||
listitem = widgets.get_listitem(item.xml, resume=True)
|
||||
url = 'http://127.0.0.1:%s/plex/play/file.strm' % v.WEBSERVICE_PORT
|
||||
args = {
|
||||
'plex_id': self.plex_id,
|
||||
'plex_type': self.plex_type
|
||||
}
|
||||
if item.force_transcode:
|
||||
args['transcode'] = 'true'
|
||||
url = utils.extend_url(url, args)
|
||||
item.file = url
|
||||
listitem.setPath(url.encode('utf-8'))
|
||||
self.kodi_pl.add(url=listitem.getPath(),
|
||||
if item.xml is None:
|
||||
LOG.debug('Need to get metadata for item %s', item)
|
||||
item.xml = PF.GetPlexMetadata(item.plex_id)
|
||||
if item.xml in (None, 401):
|
||||
raise PlaylistError('Could not get metadata for %s', item)
|
||||
api = API(item.xml[0])
|
||||
listitem = widgets.get_listitem(item.xml, resume=True)
|
||||
url = 'http://127.0.0.1:%s/plex/play/file.strm' % v.WEBSERVICE_PORT
|
||||
args = {
|
||||
'plex_id': item.plex_id,
|
||||
'plex_type': api.plex_type()
|
||||
}
|
||||
if item.force_transcode:
|
||||
args['transcode'] = 'true'
|
||||
url = utils.extend_url(url, args)
|
||||
item.file = url
|
||||
listitem.setPath(url.encode('utf-8'))
|
||||
self.kodi_pl.add(url=url.encode('utf-8'),
|
||||
listitem=listitem,
|
||||
index=pos)
|
||||
|
||||
|
@ -372,9 +411,6 @@ class PlayQueue(object):
|
|||
except (TypeError, AttributeError, KeyError, IndexError):
|
||||
raise PlaylistError('Could not add item %s to playlist %s'
|
||||
% (item, self))
|
||||
if len(xml) != len(self.items) + 1:
|
||||
raise PlaylistError('Could not add item %s to playlist %s - wrong'
|
||||
' length received' % (item, self))
|
||||
for actual_pos, xml_video_element in enumerate(xml):
|
||||
api = API(xml_video_element)
|
||||
if api.plex_id() == item.plex_id:
|
||||
|
@ -395,7 +431,7 @@ class PlayQueue(object):
|
|||
LOG.debug('Removing position %s on the Kodi side for %s', pos, self)
|
||||
answ = js.playlist_remove(self.playlistid, pos)
|
||||
if 'error' in answ:
|
||||
raise PlaylistError('Could not remove item: %s' % answ)
|
||||
raise PlaylistError('Could not remove item: %s' % answ['error'])
|
||||
|
||||
def plex_move_item(self, before, after):
|
||||
"""
|
||||
|
@ -436,9 +472,71 @@ class PlayQueue(object):
|
|||
self.items.insert(after, self.items.pop(before))
|
||||
LOG.debug('Done moving items for %s', self)
|
||||
|
||||
def start_playback(self, pos=0):
|
||||
LOG.info('Starting playback at %s for %s', pos, self)
|
||||
xbmc.Player().play(self.kodi_pl, startpos=pos, windowed=False)
|
||||
def init_from_xml(self, xml, offset=None, start_plex_id=None, repeat=None,
|
||||
transient_token=None):
|
||||
"""
|
||||
Play all items contained in the xml passed in. Called by Plex Companion.
|
||||
Either supply the ratingKey of the starting Plex element. Or set
|
||||
playqueue.selectedItemID
|
||||
|
||||
offset [float]: will seek to position offset after playback start
|
||||
start_plex_id [int]: the plex_id of the element that should be
|
||||
played
|
||||
repeat [int]: 0: don't repear
|
||||
1: repeat item
|
||||
2: repeat everything
|
||||
transient_token [unicode]: temporary token received from the PMS
|
||||
|
||||
Will stop current playback and start playback at the end
|
||||
"""
|
||||
LOG.debug("init_from_xml called with offset %s, start_plex_id %s",
|
||||
offset, start_plex_id)
|
||||
app.APP.player.stop()
|
||||
self.clear()
|
||||
self.update_details_from_xml(xml)
|
||||
self.repeat = 0 if not repeat else repeat
|
||||
self.plex_transient_token = transient_token
|
||||
for pos, xml_video_element in enumerate(xml):
|
||||
playlistitem = PlaylistItem(xml_video_element=xml_video_element)
|
||||
self.kodi_add_item(playlistitem, pos)
|
||||
self.items.append(playlistitem)
|
||||
# Where do we start playback?
|
||||
if start_plex_id is not None:
|
||||
for startpos, item in enumerate(self.items):
|
||||
if item.plex_id == start_plex_id:
|
||||
break
|
||||
else:
|
||||
startpos = 0
|
||||
else:
|
||||
for startpos, item in enumerate(self.items):
|
||||
if item.id == self.selectedItemID:
|
||||
break
|
||||
else:
|
||||
startpos = 0
|
||||
self.start_playback(pos=startpos, offset=offset)
|
||||
|
||||
def start_playback(self, pos=0, offset=0):
|
||||
"""
|
||||
Seek immediately after kicking off playback is not reliable.
|
||||
Threaded, since we need to return BEFORE seeking
|
||||
"""
|
||||
LOG.info('Starting playback at %s offset %s for %s', pos, offset, self)
|
||||
thread = threading.Thread(target=self._threaded_playback,
|
||||
args=(self.kodi_pl, pos, offset))
|
||||
thread.start()
|
||||
|
||||
@staticmethod
|
||||
def _threaded_playback(kodi_playlist, pos, offset):
|
||||
app.APP.player.play(kodi_playlist, startpos=pos, windowed=False)
|
||||
if offset:
|
||||
i = 0
|
||||
while not app.APP.is_playing:
|
||||
app.APP.monitor.waitForAbort(0.1)
|
||||
i += 1
|
||||
if i > 50:
|
||||
LOG.warn('Could not seek to %s', offset)
|
||||
return
|
||||
js.seek_to(offset)
|
||||
|
||||
|
||||
class PlaylistItem(object):
|
||||
|
@ -1069,7 +1167,7 @@ def move_playlist_item(playlist, before_pos, after_pos):
|
|||
LOG.debug('Done moving for %s', playlist)
|
||||
|
||||
|
||||
def get_PMS_playlist(playlist, playlist_id=None):
|
||||
def get_PMS_playlist(playlist=None, playlist_id=None):
|
||||
"""
|
||||
Fetches the PMS playlist/playqueue as an XML. Pass in playlist_id if we
|
||||
need to fetch a new playlist
|
||||
|
@ -1077,7 +1175,7 @@ def get_PMS_playlist(playlist, playlist_id=None):
|
|||
Returns None if something went wrong
|
||||
"""
|
||||
playlist_id = playlist_id if playlist_id else playlist.id
|
||||
if playlist.kind == 'playList':
|
||||
if playlist and playlist.kind == 'playList':
|
||||
xml = DU().downloadUrl("{server}/playlists/%s/items" % playlist_id)
|
||||
else:
|
||||
xml = DU().downloadUrl("{server}/playQueues/%s" % playlist_id)
|
||||
|
|
|
@ -15,7 +15,6 @@ from .plex_api import API
|
|||
from . import utils
|
||||
from . import plex_functions as PF
|
||||
from . import playlist_func as PL
|
||||
from . import playback
|
||||
from . import json_rpc as js
|
||||
from . import playqueue as PQ
|
||||
from . import variables as v
|
||||
|
@ -40,6 +39,8 @@ def update_playqueue_from_PMS(playqueue,
|
|||
|
||||
repeat = 0, 1, 2
|
||||
offset = time offset in Plextime (milliseconds)
|
||||
|
||||
Will (re)start playback
|
||||
"""
|
||||
LOG.info('New playqueue %s received from Plex companion with offset '
|
||||
'%s, repeat %s', playqueue_id, offset, repeat)
|
||||
|
@ -47,21 +48,15 @@ def update_playqueue_from_PMS(playqueue,
|
|||
if transient_token is None:
|
||||
transient_token = playqueue.plex_transient_token
|
||||
with app.APP.lock_playqueues:
|
||||
xml = PL.get_PMS_playlist(playqueue, playqueue_id)
|
||||
try:
|
||||
xml.attrib
|
||||
except AttributeError:
|
||||
xml = PL.get_PMS_playlist(playlist_id=playqueue_id)
|
||||
if xml is None:
|
||||
LOG.error('Could now download playqueue %s', playqueue_id)
|
||||
return
|
||||
playqueue.clear()
|
||||
try:
|
||||
PL.get_playlist_details_from_xml(playqueue, xml)
|
||||
except PL.PlaylistError:
|
||||
LOG.error('Could not get playqueue ID %s', playqueue_id)
|
||||
return
|
||||
playqueue.repeat = 0 if not repeat else int(repeat)
|
||||
playqueue.plex_transient_token = transient_token
|
||||
playback.play_xml(playqueue, xml, offset)
|
||||
raise PL.PlaylistError()
|
||||
app.PLAYSTATE.initiated_by_plex = True
|
||||
playqueue.init_from_xml(xml,
|
||||
offset=offset,
|
||||
repeat=0 if not repeat else int(repeat),
|
||||
transient_token=transient_token)
|
||||
|
||||
|
||||
class PlexCompanion(backgroundthread.KillableThread):
|
||||
|
@ -81,45 +76,48 @@ class PlexCompanion(backgroundthread.KillableThread):
|
|||
|
||||
@staticmethod
|
||||
def _process_alexa(data):
|
||||
app.PLAYSTATE.initiated_by_plex = True
|
||||
xml = PF.GetPlexMetadata(data['key'])
|
||||
try:
|
||||
xml[0].attrib
|
||||
except (AttributeError, IndexError, TypeError):
|
||||
LOG.error('Could not download Plex metadata for: %s', data)
|
||||
return
|
||||
raise PL.PlaylistError()
|
||||
api = API(xml[0])
|
||||
if api.plex_type() == v.PLEX_TYPE_ALBUM:
|
||||
LOG.debug('Plex music album detected')
|
||||
PQ.init_playqueue_from_plex_children(
|
||||
api.plex_id(),
|
||||
transient_token=data.get('token'))
|
||||
xml = PF.GetAllPlexChildren(api.plex_id())
|
||||
try:
|
||||
xml[0].attrib
|
||||
except (TypeError, IndexError, AttributeError):
|
||||
LOG.error('Could not download the album xml for %s', data)
|
||||
raise PL.PlaylistError()
|
||||
playqueue = PQ.get_playqueue_from_type('audio')
|
||||
playqueue.init_from_xml(xml,
|
||||
transient_token=data.get('token'))
|
||||
elif data['containerKey'].startswith('/playQueues/'):
|
||||
_, container_key, _ = PF.ParseContainerKey(data['containerKey'])
|
||||
xml = PF.DownloadChunks('{server}/playQueues/%s' % container_key)
|
||||
if xml is None:
|
||||
# "Play error"
|
||||
utils.dialog('notification',
|
||||
utils.lang(29999),
|
||||
utils.lang(30128),
|
||||
icon='{error}')
|
||||
return
|
||||
LOG.error('Could not get playqueue for %s', data)
|
||||
raise PL.PlaylistError()
|
||||
playqueue = PQ.get_playqueue_from_type(
|
||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.plex_type()])
|
||||
playqueue.clear()
|
||||
PL.get_playlist_details_from_xml(playqueue, xml)
|
||||
playqueue.plex_transient_token = data.get('token')
|
||||
if data.get('offset') != '0':
|
||||
if data.get('offset') not in ('0', None):
|
||||
offset = float(data['offset']) / 1000.0
|
||||
else:
|
||||
offset = None
|
||||
playback.play_xml(playqueue, xml, offset)
|
||||
playqueue.init_from_xml(xml,
|
||||
offset=offset,
|
||||
transient_token=data.get('token'))
|
||||
else:
|
||||
app.CONN.plex_transient_token = data.get('token')
|
||||
if data.get('offset') != '0':
|
||||
if data.get('offset') not in (None, '0'):
|
||||
app.PLAYSTATE.resume_playback = True
|
||||
playback.playback_triage(api.plex_id(),
|
||||
api.plex_type(),
|
||||
resolve=False)
|
||||
path = ('http://127.0.0.1:%s/plex/play/file.strm?plex_id=%s'
|
||||
% (v.WEBSERVICE_PORT, api.plex_id()))
|
||||
path += '&plex_type=%s' % api.plex_type()
|
||||
executebuiltin(('PlayMedia(%s)' % path).encode('utf-8'))
|
||||
|
||||
@staticmethod
|
||||
def _process_node(data):
|
||||
|
@ -150,7 +148,7 @@ class PlexCompanion(backgroundthread.KillableThread):
|
|||
xml[0].attrib
|
||||
except (AttributeError, IndexError, TypeError):
|
||||
LOG.error('Could not download Plex metadata')
|
||||
return
|
||||
raise PL.PlaylistError()
|
||||
api = API(xml[0])
|
||||
playqueue = PQ.get_playqueue_from_type(
|
||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.plex_type()])
|
||||
|
@ -167,20 +165,23 @@ class PlexCompanion(backgroundthread.KillableThread):
|
|||
"""
|
||||
playqueue = PQ.get_playqueue_from_type(
|
||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[data['type']])
|
||||
pos = js.get_position(playqueue.playlistid)
|
||||
if 'audioStreamID' in data:
|
||||
index = playqueue.items[pos].kodi_stream_index(
|
||||
data['audioStreamID'], 'audio')
|
||||
app.APP.player.setAudioStream(index)
|
||||
elif 'subtitleStreamID' in data:
|
||||
if data['subtitleStreamID'] == '0':
|
||||
app.APP.player.showSubtitles(False)
|
||||
else:
|
||||
try:
|
||||
pos = js.get_position(playqueue.playlistid)
|
||||
if 'audioStreamID' in data:
|
||||
index = playqueue.items[pos].kodi_stream_index(
|
||||
data['subtitleStreamID'], 'subtitle')
|
||||
app.APP.player.setSubtitleStream(index)
|
||||
else:
|
||||
LOG.error('Unknown setStreams command: %s', data)
|
||||
data['audioStreamID'], 'audio')
|
||||
app.APP.player.setAudioStream(index)
|
||||
elif 'subtitleStreamID' in data:
|
||||
if data['subtitleStreamID'] == '0':
|
||||
app.APP.player.showSubtitles(False)
|
||||
else:
|
||||
index = playqueue.items[pos].kodi_stream_index(
|
||||
data['subtitleStreamID'], 'subtitle')
|
||||
app.APP.player.setSubtitleStream(index)
|
||||
else:
|
||||
LOG.error('Unknown setStreams command: %s', data)
|
||||
except KeyError:
|
||||
LOG.warn('Could not process stream data: %s', data)
|
||||
|
||||
@staticmethod
|
||||
def _process_refresh(data):
|
||||
|
@ -220,23 +221,29 @@ class PlexCompanion(backgroundthread.KillableThread):
|
|||
"""
|
||||
LOG.debug('Processing: %s', task)
|
||||
data = task['data']
|
||||
if task['action'] == 'alexa':
|
||||
with app.APP.lock_playqueues:
|
||||
self._process_alexa(data)
|
||||
elif (task['action'] == 'playlist' and
|
||||
data.get('address') == 'node.plexapp.com'):
|
||||
self._process_node(data)
|
||||
elif task['action'] == 'playlist':
|
||||
with app.APP.lock_playqueues:
|
||||
self._process_playlist(data)
|
||||
elif task['action'] == 'refreshPlayQueue':
|
||||
with app.APP.lock_playqueues:
|
||||
self._process_refresh(data)
|
||||
elif task['action'] == 'setStreams':
|
||||
try:
|
||||
try:
|
||||
if task['action'] == 'alexa':
|
||||
with app.APP.lock_playqueues:
|
||||
self._process_alexa(data)
|
||||
elif (task['action'] == 'playlist' and
|
||||
data.get('address') == 'node.plexapp.com'):
|
||||
self._process_node(data)
|
||||
elif task['action'] == 'playlist':
|
||||
with app.APP.lock_playqueues:
|
||||
self._process_playlist(data)
|
||||
elif task['action'] == 'refreshPlayQueue':
|
||||
with app.APP.lock_playqueues:
|
||||
self._process_refresh(data)
|
||||
elif task['action'] == 'setStreams':
|
||||
self._process_streams(data)
|
||||
except KeyError:
|
||||
pass
|
||||
except PL.PlaylistError:
|
||||
LOG.error('Could not process companion data: %s', data)
|
||||
# "Play Error"
|
||||
utils.dialog('notification',
|
||||
utils.lang(29999),
|
||||
utils.lang(30128),
|
||||
icon='{error}')
|
||||
app.PLAYSTATE.initiated_by_plex = False
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
|
|
|
@ -13,16 +13,16 @@ import Queue
|
|||
import xbmc
|
||||
import xbmcvfs
|
||||
|
||||
from .plex_api import API
|
||||
from .plex_db import PlexDB
|
||||
from . import backgroundthread, utils, variables as v, app, playqueue as PQ
|
||||
from . import playlist_func as PL, json_rpc as js
|
||||
from . import playlist_func as PL, json_rpc as js, plex_functions as PF
|
||||
|
||||
|
||||
LOG = getLogger('PLEX.webservice')
|
||||
|
||||
|
||||
class WebService(backgroundthread.KillableThread):
|
||||
|
||||
''' Run a webservice to trigger playback.
|
||||
'''
|
||||
def is_alive(self):
|
||||
|
@ -48,7 +48,7 @@ class WebService(backgroundthread.KillableThread):
|
|||
conn.request('QUIT', '/')
|
||||
conn.getresponse()
|
||||
except Exception as error:
|
||||
xbmc.log('Plex.WebService abort error: %s' % error, xbmc.LOGWARNING)
|
||||
xbmc.log('PLEX.webservice abort error: %s' % error, xbmc.LOGWARNING)
|
||||
|
||||
def suspend(self):
|
||||
"""
|
||||
|
@ -124,7 +124,7 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||
# Silence "[Errno 10054] An existing connection was forcibly
|
||||
# closed by the remote host"
|
||||
return
|
||||
xbmc.log('Plex.WebService handle error: %s' % error, xbmc.LOGWARNING)
|
||||
xbmc.log('PLEX.webservice handle error: %s' % error, xbmc.LOGWARNING)
|
||||
|
||||
def do_QUIT(self):
|
||||
''' send 200 OK response, and set server.stop to True
|
||||
|
@ -144,7 +144,12 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||
if '?' in path:
|
||||
path = path.split('?', 1)[1]
|
||||
params = dict(utils.parse_qsl(path))
|
||||
if 'plex_id' not in params:
|
||||
LOG.error('No plex_id received for path %s', path)
|
||||
return
|
||||
|
||||
if 'plex_type' in params and params['plex_type'].lower() == 'none':
|
||||
del params['plex_type']
|
||||
if 'plex_type' not in params:
|
||||
LOG.debug('Need to look-up plex_type')
|
||||
with PlexDB(lock=False) as plexdb:
|
||||
|
@ -154,9 +159,20 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||
else:
|
||||
LOG.debug('No plex_type found, using Kodi player id')
|
||||
players = js.get_players()
|
||||
params['plex_type'] = v.PLEX_TYPE_CLIP if 'video' in players \
|
||||
else v.PLEX_TYPE_SONG
|
||||
|
||||
if players:
|
||||
params['plex_type'] = v.PLEX_TYPE_CLIP if 'video' in players \
|
||||
else v.PLEX_TYPE_SONG
|
||||
LOG.debug('Using the following plex_type: %s',
|
||||
params['plex_type'])
|
||||
else:
|
||||
xml = PF.GetPlexMetadata(params['plex_id'])
|
||||
if xml in (None, 401):
|
||||
LOG.error('Could not get metadata for %s', params)
|
||||
return
|
||||
api = API(xml[0])
|
||||
params['plex_type'] = api.plex_type()
|
||||
LOG.debug('Got metadata, using plex_type %s',
|
||||
params['plex_type'])
|
||||
return params
|
||||
|
||||
def do_HEAD(self):
|
||||
|
@ -172,7 +188,7 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||
def handle_request(self, headers_only=False):
|
||||
'''Send headers and reponse
|
||||
'''
|
||||
xbmc.log('Plex.WebService handle_request called. headers %s, path: %s'
|
||||
xbmc.log('PLEX.webservice handle_request called. headers %s, path: %s'
|
||||
% (headers_only, self.path), xbmc.LOGDEBUG)
|
||||
try:
|
||||
if b'extrafanart' in self.path or b'extrathumbs' in self.path:
|
||||
|
@ -311,11 +327,13 @@ class QueuePlay(backgroundthread.KillableThread):
|
|||
self.synched = not params['synched'].lower() == 'false'
|
||||
|
||||
def _get_playqueue(self):
|
||||
if (self.plex_type in v.PLEX_VIDEOTYPES and
|
||||
xbmc.getCondVisibility('Window.IsVisible(Home.xml)')):
|
||||
playqueue = PQ.get_playqueue_from_type(v.KODI_TYPE_VIDEO)
|
||||
if ((self.plex_type in v.PLEX_VIDEOTYPES and
|
||||
not app.PLAYSTATE.initiated_by_plex and
|
||||
xbmc.getCondVisibility('Window.IsVisible(Home.xml)'))):
|
||||
# Video launched from a widget - which starts a Kodi AUDIO playlist
|
||||
# We will empty everything and start with a fresh VIDEO playlist
|
||||
LOG.debug('Widget video playback detected; relaunching')
|
||||
LOG.debug('Widget video playback detected')
|
||||
video_widget_playback = True
|
||||
# Release default.py
|
||||
utils.window('plex.playlist.ready', value='true')
|
||||
|
@ -335,14 +353,9 @@ class QueuePlay(backgroundthread.KillableThread):
|
|||
else:
|
||||
LOG.debug('Audio playback detected')
|
||||
playqueue = PQ.get_playqueue_from_type(v.KODI_TYPE_AUDIO)
|
||||
playqueue.clear(kodi=False)
|
||||
return playqueue, video_widget_playback
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
We cannot use js.get_players() to reliably get the active player
|
||||
Use Kodimonitor's OnNotification and OnAdd
|
||||
"""
|
||||
LOG.debug('##===---- Starting QueuePlay ----===##')
|
||||
abort = False
|
||||
play_folder = False
|
||||
|
@ -358,6 +371,8 @@ class QueuePlay(backgroundthread.KillableThread):
|
|||
# Position to add next element to queue - we're doing this at the end
|
||||
# of our current playqueue
|
||||
position = playqueue.kodi_pl.size()
|
||||
# Set to start_position + 1 because first item will fail
|
||||
utils.window('plex.playlist.start', str(start_position + 1))
|
||||
LOG.debug('start_position %s, position %s for current playqueue: %s',
|
||||
start_position, position, playqueue)
|
||||
while True:
|
||||
|
@ -370,7 +385,7 @@ class QueuePlay(backgroundthread.KillableThread):
|
|||
LOG.debug('Wrapping up')
|
||||
if xbmc.getCondVisibility('VideoPlayer.Content(livetv)'):
|
||||
# avoid issues with ongoing Live TV playback
|
||||
xbmc.Player().stop()
|
||||
app.APP.player.stop()
|
||||
count = 50
|
||||
while not utils.window('plex.playlist.ready'):
|
||||
xbmc.sleep(50)
|
||||
|
@ -392,48 +407,47 @@ class QueuePlay(backgroundthread.KillableThread):
|
|||
LOG.info('Start normal playback')
|
||||
# Release default.py
|
||||
utils.window('plex.playlist.play', value='true')
|
||||
# Remove the playlist element we just added with the
|
||||
# right path
|
||||
xbmc.sleep(1000)
|
||||
playqueue.kodi_remove_item(start_position)
|
||||
del playqueue.items[start_position]
|
||||
LOG.debug('Done wrapping up')
|
||||
break
|
||||
self.load_params(params)
|
||||
if play_folder:
|
||||
# position = play.play_folder(position)
|
||||
item = PL.PlaylistItem(plex_id=self.plex_id,
|
||||
plex_type=self.plex_type,
|
||||
kodi_id=self.kodi_id,
|
||||
kodi_type=self.kodi_type)
|
||||
item.force_transcode = self.force_transcode
|
||||
playqueue.add_item(item, position)
|
||||
playlistitem = PL.PlaylistItem(plex_id=self.plex_id,
|
||||
plex_type=self.plex_type,
|
||||
kodi_id=self.kodi_id,
|
||||
kodi_type=self.kodi_type)
|
||||
playlistitem.force_transcode = self.force_transcode
|
||||
playqueue.add_item(playlistitem, position)
|
||||
position += 1
|
||||
else:
|
||||
if self.server.pending.count(params['plex_id']) != len(self.server.pending):
|
||||
# E.g. when selecting "play" for an entire video genre
|
||||
LOG.debug('Folder playback detected')
|
||||
play_folder = True
|
||||
# Set to start_position + 1 because first item will fail
|
||||
utils.window('plex.playlist.start', str(start_position + 1))
|
||||
playqueue.init(self.plex_id,
|
||||
xbmc.executebuiltin('Activateutils.window(busydialognocancel)')
|
||||
playqueue.play(self.plex_id,
|
||||
plex_type=self.plex_type,
|
||||
startpos=start_position,
|
||||
position=position,
|
||||
synched=self.synched,
|
||||
force_transcode=self.force_transcode)
|
||||
# Do NOT start playback here - because Kodi already started
|
||||
# it!
|
||||
# playqueue.start_playback(position)
|
||||
position = playqueue.index
|
||||
if play_folder:
|
||||
xbmc.executebuiltin('Activateutils.window(busydialognocancel)')
|
||||
except PL.PlaylistError as error:
|
||||
abort = True
|
||||
LOG.warn('Not playing due to the following: %s', error)
|
||||
except Exception:
|
||||
abort = True
|
||||
utils.ERROR()
|
||||
utils.ERROR(notify=True)
|
||||
try:
|
||||
self.server.queue.task_done()
|
||||
except ValueError:
|
||||
# "task_done() called too many times"
|
||||
# "task_done() called too many times" when aborting
|
||||
pass
|
||||
if abort:
|
||||
xbmc.Player().stop()
|
||||
app.APP.player.stop()
|
||||
playqueue.clear()
|
||||
self.server.queue.queue.clear()
|
||||
if play_folder:
|
||||
|
@ -444,6 +458,7 @@ class QueuePlay(backgroundthread.KillableThread):
|
|||
|
||||
utils.window('plex.playlist.ready', clear=True)
|
||||
utils.window('plex.playlist.start', clear=True)
|
||||
app.PLAYSTATE.initiated_by_plex = False
|
||||
self.server.threads.remove(self)
|
||||
self.server.pending = []
|
||||
LOG.debug('##===---- QueuePlay Stopped ----===##')
|
||||
|
|
Loading…
Reference in a new issue