Revamp playback start, part 3
This commit is contained in:
parent
fb7eafb27a
commit
7ecaa376a2
3 changed files with 90 additions and 103 deletions
|
@ -198,6 +198,10 @@ class KodiMonitor(Monitor):
|
|||
Will NOT be called if playback initiated by Kodi widgets
|
||||
"""
|
||||
playqueue = PQ.PLAYQUEUES[data['playlistid']]
|
||||
# Have we initiated the playqueue already? If not, ignore this
|
||||
if not playqueue.items:
|
||||
LOG.debug('Playqueue not initiated - ignoring')
|
||||
return
|
||||
# Did PKC cause this add? Then lets not do anything
|
||||
if playqueue.is_kodi_onadd() is False:
|
||||
LOG.debug('PKC added this item to the playqueue - ignoring')
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
Used to kick off Kodi playback
|
||||
"""
|
||||
from logging import getLogger
|
||||
from threading import Thread, Lock
|
||||
from threading import Thread
|
||||
from urllib import urlencode
|
||||
|
||||
from xbmc import Player, getCondVisibility, sleep
|
||||
from xbmc import Player, sleep
|
||||
|
||||
from PlexAPI import API
|
||||
from PlexFunctions import GetPlexMetadata, init_plex_playqueue
|
||||
|
@ -16,14 +16,14 @@ from playutils import PlayUtils
|
|||
from PKC_listitem import PKC_ListItem
|
||||
from pickler import pickle_me, Playback_Successful
|
||||
import json_rpc as js
|
||||
from utils import window, settings, dialog, language as lang, Lock_Function
|
||||
from utils import window, settings, dialog, language as lang
|
||||
from plexbmchelper.subscribers import LOCKER
|
||||
import variables as v
|
||||
import state
|
||||
|
||||
###############################################################################
|
||||
|
||||
LOG = getLogger("PLEX." + __name__)
|
||||
LOCKER = Lock_Function(Lock())
|
||||
|
||||
###############################################################################
|
||||
|
||||
|
@ -34,42 +34,43 @@ def playback_triage(plex_id=None, plex_type=None, path=None):
|
|||
Hit this function for addon path playback, Plex trailers, etc.
|
||||
Will setup playback first, then on second call complete playback.
|
||||
|
||||
Returns Playback_Successful() with potentially a PKC_ListItem() attached
|
||||
(to be consumed by setResolvedURL)
|
||||
Will set Playback_Successful() with potentially a PKC_ListItem() attached
|
||||
(to be consumed by setResolvedURL in default.py)
|
||||
|
||||
If trailers or additional (movie-)parts are added, default.py is released
|
||||
and a completely new player instance is called with a new playlist. This
|
||||
circumvents most issues with Kodi & playqueues
|
||||
"""
|
||||
LOG.info('playback_triage called with plex_id %s, plex_type %s, path %s',
|
||||
plex_id, plex_type, path)
|
||||
if not state.AUTHENTICATED:
|
||||
LOG.error('Not yet authenticated for PMS, abort starting playback')
|
||||
# Release default.py
|
||||
pickle_me(Playback_Successful())
|
||||
# "Unauthorized for PMS"
|
||||
dialog('notification', lang(29999), lang(30017))
|
||||
# Don't cause second notification to appear
|
||||
return Playback_Successful()
|
||||
return
|
||||
playqueue = PQ.get_playqueue_from_type(
|
||||
v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[plex_type])
|
||||
pos = js.get_position(playqueue.playlistid)
|
||||
# Can return -1 (as in "no playlist")
|
||||
pos = pos if pos != -1 else 0
|
||||
LOG.info('playQueue position: %s for %s', pos, playqueue)
|
||||
# Have we already initiated playback?
|
||||
init_done = True
|
||||
try:
|
||||
item = playqueue.items[pos]
|
||||
playqueue.items[pos]
|
||||
except IndexError:
|
||||
init_done = False
|
||||
else:
|
||||
init_done = item.init_done
|
||||
# Either init the playback now, or - on 2nd pass - kick off playback
|
||||
if init_done is False:
|
||||
playback_init(plex_id, path, playqueue)
|
||||
playback_init(plex_id, plex_type, playqueue)
|
||||
else:
|
||||
# kick off playback on second pass
|
||||
conclude_playback(playqueue, pos)
|
||||
|
||||
|
||||
def playback_init(plex_id, path, playqueue):
|
||||
def playback_init(plex_id, plex_type, playqueue):
|
||||
"""
|
||||
Playback setup. Path is the original path PKC default.py has been called
|
||||
with
|
||||
Playback setup if Kodi starts playing an item for the first time.
|
||||
"""
|
||||
LOG.info('Initializing PKC playback')
|
||||
contextmenu_play = window('plex_contextplay') == 'true'
|
||||
window('plex_contextplay', clear=True)
|
||||
xml = GetPlexMetadata(plex_id)
|
||||
|
@ -77,25 +78,15 @@ def playback_init(plex_id, path, playqueue):
|
|||
xml[0].attrib
|
||||
except (IndexError, TypeError, AttributeError):
|
||||
LOG.error('Could not get a PMS xml for plex id %s', plex_id)
|
||||
# Release default.py
|
||||
pickle_me(Playback_Successful())
|
||||
# "Play error"
|
||||
dialog('notification', lang(29999), lang(30128))
|
||||
dialog('notification', lang(29999), lang(30128), icon='{error}')
|
||||
return
|
||||
result = Playback_Successful()
|
||||
listitem = PKC_ListItem()
|
||||
# Set the original path again so Kodi will return a 2nd time to PKC
|
||||
listitem.setPath(path)
|
||||
api = API(xml[0])
|
||||
plex_type = api.getType()
|
||||
size_playlist = playqueue.kodi_pl.size()
|
||||
# Can return -1
|
||||
start_pos = max(playqueue.kodi_pl.getposition(), 0)
|
||||
LOG.info("Playlist size %s", size_playlist)
|
||||
LOG.info("Playlist starting position %s", start_pos)
|
||||
resume, _ = api.getRuntime()
|
||||
trailers = False
|
||||
if (plex_type == v.PLEX_TYPE_MOVIE and
|
||||
not resume and
|
||||
size_playlist < 2 and
|
||||
if (plex_type == v.PLEX_TYPE_MOVIE and not resume and
|
||||
settings('enableCinema') == "true"):
|
||||
if settings('askCinema') == "true":
|
||||
# "Play trailers?"
|
||||
|
@ -103,7 +94,8 @@ def playback_init(plex_id, path, playqueue):
|
|||
trailers = True if trailers else False
|
||||
else:
|
||||
trailers = True
|
||||
# Post to the PMS. REUSE THE PLAYQUEUE!
|
||||
LOG.info('Playing trailers: %s', trailers)
|
||||
# Post to the PMS to create a playqueue - in any case due to Plex Companion
|
||||
xml = init_plex_playqueue(plex_id,
|
||||
xml.attrib.get('librarySectionUUID'),
|
||||
mediatype=plex_type,
|
||||
|
@ -111,55 +103,44 @@ def playback_init(plex_id, path, playqueue):
|
|||
if xml is None:
|
||||
LOG.error('Could not get a playqueue xml for plex id %s, UUID %s',
|
||||
plex_id, xml.attrib.get('librarySectionUUID'))
|
||||
# Release default.py
|
||||
pickle_me(Playback_Successful())
|
||||
# "Play error"
|
||||
dialog('notification', lang(29999), lang(30128))
|
||||
dialog('notification', lang(29999), lang(30128), icon='{error}')
|
||||
return
|
||||
# Should already be empty, but just in case
|
||||
playqueue.clear()
|
||||
PL.get_playlist_details_from_xml(playqueue, xml)
|
||||
stack = _prep_playlist_stack(xml)
|
||||
force_playback = False
|
||||
if (not getCondVisibility('Window.IsVisible(MyVideoNav.xml)') and
|
||||
not getCondVisibility('Window.IsVisible(VideoFullScreen.xml)')):
|
||||
LOG.info("Detected playback from widget")
|
||||
force_playback = True
|
||||
if force_playback is False:
|
||||
# Return the listelement for setResolvedURL
|
||||
result.listitem = listitem
|
||||
pickle_me(result)
|
||||
# Wait for the setResolvedUrl to have taken its course - ugly
|
||||
sleep(50)
|
||||
_process_stack(playqueue, stack)
|
||||
else:
|
||||
# Need to kickoff playback, not using setResolvedURL
|
||||
pickle_me(result)
|
||||
_process_stack(playqueue, stack)
|
||||
# Need a separate thread because Player won't return in time
|
||||
listitem.setProperty('StartOffset', str(resume))
|
||||
thread = Thread(target=Player().play,
|
||||
args=(playqueue.kodi_pl, ))
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
# Release our default.py before starting our own Kodi player instance
|
||||
pickle_me(Playback_Successful())
|
||||
# Sleep a bit to let setResolvedUrl do its thing - bit ugly
|
||||
sleep(200)
|
||||
_process_stack(playqueue, stack)
|
||||
# New thread to release this one sooner (e.g. harddisk spinning up)
|
||||
thread = Thread(target=Player().play,
|
||||
args=(playqueue.kodi_pl, ))
|
||||
thread.setDaemon(True)
|
||||
LOG.info('Done initializing PKC playback, starting Kodi player')
|
||||
thread.start()
|
||||
|
||||
|
||||
def _prep_playlist_stack(xml):
|
||||
stack = []
|
||||
for item in xml:
|
||||
api = API(item)
|
||||
with plexdb.Get_Plex_DB() as plex_db:
|
||||
plex_dbitem = plex_db.getItem_byId(api.getRatingKey())
|
||||
try:
|
||||
kodi_id = plex_dbitem[0]
|
||||
kodi_type = plex_dbitem[4]
|
||||
except TypeError:
|
||||
if api.getType() != v.PLEX_TYPE_CLIP:
|
||||
with plexdb.Get_Plex_DB() as plex_db:
|
||||
plex_dbitem = plex_db.getItem_byId(api.getRatingKey())
|
||||
kodi_id = plex_dbitem[0] if plex_dbitem else None
|
||||
kodi_type = plex_dbitem[4] if plex_dbitem else None
|
||||
else:
|
||||
# We will never store clips (trailers) in the Kodi DB
|
||||
kodi_id = None
|
||||
kodi_type = None
|
||||
for part_no, _ in enumerate(item[0]):
|
||||
api.setPartNumber(part_no)
|
||||
if kodi_id is not None:
|
||||
# We don't need the URL, item is in the Kodi library
|
||||
path = None
|
||||
listitem = None
|
||||
else:
|
||||
for part, _ in enumerate(item[0]):
|
||||
api.setPartNumber(part)
|
||||
if kodi_id is None:
|
||||
# Need to redirect again to PKC to conclude playback
|
||||
params = {
|
||||
'mode': 'play',
|
||||
|
@ -167,17 +148,20 @@ def _prep_playlist_stack(xml):
|
|||
'plex_type': api.getType()
|
||||
}
|
||||
path = ('plugin://plugin.video.plexkodiconnect?%s'
|
||||
% (urlencode(params)))
|
||||
% urlencode(params))
|
||||
listitem = api.CreateListItemFromPlexItem()
|
||||
api.set_listitem_artwork(listitem)
|
||||
listitem.setPath(path)
|
||||
else:
|
||||
# Will add directly via the Kodi DB
|
||||
path = None
|
||||
listitem = None
|
||||
stack.append({
|
||||
'kodi_id': kodi_id,
|
||||
'kodi_type': kodi_type,
|
||||
'file': path,
|
||||
'xml_video_element': item,
|
||||
'listitem': listitem,
|
||||
'part_no': part_no
|
||||
'part': part
|
||||
})
|
||||
return stack
|
||||
|
||||
|
@ -185,26 +169,28 @@ def _prep_playlist_stack(xml):
|
|||
def _process_stack(playqueue, stack):
|
||||
"""
|
||||
Takes our stack and adds the items to the PKC and Kodi playqueues.
|
||||
This needs to be done AFTER setResolvedURL
|
||||
"""
|
||||
for i, item in enumerate(stack):
|
||||
if item['kodi_id'] is not None:
|
||||
# Use Kodi id & JSON so we get full artwork
|
||||
playlist_item = PL.add_item_to_kodi_playlist(
|
||||
playqueue,
|
||||
i,
|
||||
kodi_id=item['kodi_id'],
|
||||
kodi_type=item['kodi_type'],
|
||||
xml_video_element=item['xml_video_element'])
|
||||
else:
|
||||
# getposition() can return -1
|
||||
pos = max(playqueue.kodi_pl.getposition(), 0) + 1
|
||||
for item in stack:
|
||||
if item['kodi_id'] is None:
|
||||
playlist_item = PL.add_listitem_to_Kodi_playlist(
|
||||
playqueue,
|
||||
i,
|
||||
pos,
|
||||
item['listitem'],
|
||||
file=item['file'],
|
||||
xml_video_element=item['xml_video_element'])
|
||||
playlist_item.part = item['part_no']
|
||||
else:
|
||||
# Directly add element so we have full metadata
|
||||
playlist_item = PL.add_item_to_kodi_playlist(
|
||||
playqueue,
|
||||
pos,
|
||||
kodi_id=item['kodi_id'],
|
||||
kodi_type=item['kodi_type'],
|
||||
xml_video_element=item['xml_video_element'])
|
||||
playlist_item.part = item['part']
|
||||
playlist_item.init_done = True
|
||||
pos += 1
|
||||
|
||||
|
||||
def conclude_playback(playqueue, pos):
|
||||
|
|
|
@ -324,7 +324,8 @@ def playlist_item_from_plex(plex_id):
|
|||
return item
|
||||
|
||||
|
||||
def playlist_item_from_xml(playlist, xml_video_element):
|
||||
def playlist_item_from_xml(playlist, xml_video_element, kodi_id=None,
|
||||
kodi_type=None):
|
||||
"""
|
||||
Returns a playlist element for the playqueue using the Plex xml
|
||||
|
||||
|
@ -338,7 +339,10 @@ def playlist_item_from_xml(playlist, xml_video_element):
|
|||
item.guid = xml_video_element.attrib.get('guid')
|
||||
if item.guid is not None:
|
||||
item.guid = escape_html(item.guid)
|
||||
if item.plex_id:
|
||||
if kodi_id is not None:
|
||||
item.kodi_id = kodi_id
|
||||
item.kodi_type = kodi_type
|
||||
elif item.plex_id is not None:
|
||||
with plexdb.Get_Plex_DB() as plex_db:
|
||||
db_element = plex_db.getItem_byId(item.plex_id)
|
||||
try:
|
||||
|
@ -369,20 +373,13 @@ def get_playlist_details_from_xml(playlist, xml):
|
|||
Takes a PMS xml as input and overwrites all the playlist's details, e.g.
|
||||
playlist.id with the XML's playQueueID
|
||||
"""
|
||||
try:
|
||||
playlist.id = xml.attrib['%sID' % playlist.kind]
|
||||
playlist.version = xml.attrib['%sVersion' % playlist.kind]
|
||||
playlist.shuffled = xml.attrib['%sShuffled' % playlist.kind]
|
||||
playlist.selectedItemID = xml.attrib.get(
|
||||
'%sSelectedItemID' % playlist.kind)
|
||||
playlist.selectedItemOffset = xml.attrib.get(
|
||||
'%sSelectedItemOffset' % playlist.kind)
|
||||
except:
|
||||
LOG.error('Could not parse xml answer from PMS for playlist %s',
|
||||
playlist)
|
||||
import traceback
|
||||
LOG.error(traceback.format_exc())
|
||||
raise KeyError
|
||||
playlist.id = xml.attrib['%sID' % playlist.kind]
|
||||
playlist.version = xml.attrib['%sVersion' % playlist.kind]
|
||||
playlist.shuffled = xml.attrib['%sShuffled' % playlist.kind]
|
||||
playlist.selectedItemID = xml.attrib.get(
|
||||
'%sSelectedItemID' % playlist.kind)
|
||||
playlist.selectedItemOffset = xml.attrib.get(
|
||||
'%sSelectedItemOffset' % playlist.kind)
|
||||
LOG.debug('Updated playlist from xml: %s', playlist)
|
||||
|
||||
|
||||
|
@ -702,7 +699,7 @@ def add_listitem_to_Kodi_playlist(playlist, pos, listitem, file,
|
|||
pos, playlist)
|
||||
# Add the item into Kodi playlist
|
||||
playlist.kodi_onadd()
|
||||
playlist.kodi_pl.add(file, listitem, index=pos)
|
||||
playlist.kodi_pl.add(url=file, listitem=listitem, index=pos)
|
||||
# We need to add this to our internal queue as well
|
||||
if xml_video_element is not None:
|
||||
item = playlist_item_from_xml(playlist, xml_video_element)
|
||||
|
|
Loading…
Reference in a new issue