137 lines
4.9 KiB
Python
137 lines
4.9 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
from __future__ import absolute_import, division, unicode_literals
|
|
from logging import getLogger
|
|
|
|
from .plex_api import API
|
|
from . import utils, context_entry, transfer, backgroundthread, variables as v
|
|
from . import app, plex_functions as PF, playqueue as PQ
|
|
|
|
###############################################################################
|
|
|
|
LOG = getLogger('PLEX.playback_starter')
|
|
|
|
###############################################################################
|
|
|
|
|
|
class PlaybackTask(backgroundthread.Task):
|
|
"""
|
|
Processes new plays
|
|
"""
|
|
def __init__(self, command):
|
|
self.command = command
|
|
super(PlaybackTask, self).__init__()
|
|
|
|
def run(self):
|
|
LOG.debug('Starting PlaybackTask with %s', self.command)
|
|
item = self.command
|
|
try:
|
|
_, params = item.split('?', 1)
|
|
except ValueError:
|
|
# E.g. other add-ons scanning for Extras folder
|
|
LOG.debug('Detected 3rd party add-on call - ignoring')
|
|
transfer.send(True)
|
|
return
|
|
params = dict(utils.parse_qsl(params))
|
|
mode = params.get('mode')
|
|
resolve = False if params.get('handle') == '-1' else True
|
|
LOG.debug('Received mode: %s, params: %s', mode, params)
|
|
if mode == 'plex_node':
|
|
process_indirect(params['key'],
|
|
params['offset'],
|
|
resolve=resolve)
|
|
elif mode == 'context_menu':
|
|
context_entry.ContextMenu(kodi_id=params.get('kodi_id'),
|
|
kodi_type=params.get('kodi_type'))
|
|
LOG.debug('Finished PlaybackTask')
|
|
|
|
|
|
def process_indirect(key, offset, resolve=True):
|
|
"""
|
|
Called e.g. for Plex "Play later" - Plex items where we need to fetch an
|
|
additional xml for the actual playurl. In the PMS metadata, indirect="1" is
|
|
set.
|
|
|
|
Will release default.py with setResolvedUrl
|
|
|
|
Set resolve to False if playback should be kicked off directly, not via
|
|
setResolvedUrl
|
|
"""
|
|
LOG.info('process_indirect called with key: %s, offset: %s, resolve: %s',
|
|
key, offset, resolve)
|
|
global RESOLVE
|
|
RESOLVE = resolve
|
|
offset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(offset)) if offset != '0' else None
|
|
if key.startswith('http') or key.startswith('{server}'):
|
|
xml = PF.get_playback_xml(key, app.CONN.server_name)
|
|
elif key.startswith('/system/services'):
|
|
xml = PF.get_playback_xml('http://node.plexapp.com:32400%s' % key,
|
|
'plexapp.com',
|
|
authenticate=False,
|
|
token=app.ACCOUNT.plex_token)
|
|
else:
|
|
xml = PF.get_playback_xml('{server}%s' % key, app.CONN.server_name)
|
|
if xml is None:
|
|
_ensure_resolve(abort=True)
|
|
return
|
|
|
|
api = API(xml[0])
|
|
listitem = transfer.PKCListItem()
|
|
api.create_listitem(listitem)
|
|
playqueue = PQ.get_playqueue_from_type(
|
|
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.plex_type()])
|
|
playqueue.clear()
|
|
item = PQ.PlaylistItem(xml_video_element=xml[0])
|
|
item.offset = offset
|
|
item.playmethod = 'DirectStream'
|
|
|
|
# Need to get yet another xml to get the final playback url
|
|
try:
|
|
xml = PF.get_playback_xml('http://node.plexapp.com:32400%s'
|
|
% xml[0][0][0].attrib['key'],
|
|
'plexapp.com',
|
|
authenticate=False,
|
|
token=app.ACCOUNT.plex_token)
|
|
except (TypeError, IndexError, AttributeError):
|
|
LOG.error('XML malformed: %s', xml.attrib)
|
|
xml = None
|
|
if xml is None:
|
|
_ensure_resolve(abort=True)
|
|
return
|
|
try:
|
|
playurl = xml[0].attrib['key']
|
|
except (TypeError, IndexError, AttributeError):
|
|
LOG.error('Last xml malformed: %s\n%s', xml.tag, xml.attrib)
|
|
_ensure_resolve(abort=True)
|
|
return
|
|
|
|
item.file = playurl
|
|
listitem.setPath(playurl.encode('utf-8'))
|
|
playqueue.items.append(item)
|
|
if resolve is True:
|
|
transfer.send(listitem)
|
|
else:
|
|
LOG.info('Done initializing PKC playback, starting Kodi player')
|
|
app.APP.player.play(item=playurl.encode('utf-8'),
|
|
listitem=listitem)
|
|
|
|
|
|
def _ensure_resolve(abort=False):
|
|
"""
|
|
Will check whether RESOLVE=True and if so, fail Kodi playback startup
|
|
with the path 'PKC_Dummy_Path_Which_Fails' using setResolvedUrl (and some
|
|
pickling)
|
|
|
|
This way we're making sure that other Python instances (calling default.py)
|
|
will be destroyed.
|
|
"""
|
|
if RESOLVE:
|
|
# Releases the other Python thread without a ListItem
|
|
transfer.send(True)
|
|
# Shows PKC error message
|
|
# transfer.send(None)
|
|
if abort:
|
|
# Reset some playback variables
|
|
app.PLAYSTATE.context_menu_play = False
|
|
app.PLAYSTATE.force_transcode = False
|
|
app.PLAYSTATE.resume_playback = False
|