Revamp playback start, part 5

This commit is contained in:
tomkat83 2018-01-22 11:20:37 +01:00
parent 2791da9f65
commit 287b888b6f
7 changed files with 109 additions and 9 deletions

View file

@ -1505,6 +1505,16 @@ class API():
""" """
return self.item.attrib.get('year', None) return self.item.attrib.get('year', None)
def getResume(self):
"""
Returns the resume point of time in seconds as int. 0 if not found
"""
try:
resume = float(self.item.attrib['viewOffset'])
except (KeyError, ValueError):
resume = 0.0
return int(resume * v.PLEX_TO_KODI_TIMEFACTOR)
def getRuntime(self): def getRuntime(self):
""" """
Resume point of time and runtime/totaltime in rounded to seconds. Resume point of time and runtime/totaltime in rounded to seconds.
@ -2521,7 +2531,9 @@ class API():
'mpaa': self.getMpaa(), 'mpaa': self.getMpaa(),
'aired': self.getPremiereDate() 'aired': self.getPremiereDate()
} }
listItem.setProperty('resumetime', str(userdata['Resume'])) # Do NOT set resumetime - otherwise Kodi always resumes at that time
# even if the user chose to start element from the beginning
# listItem.setProperty('resumetime', str(userdata['Resume']))
listItem.setProperty('totaltime', str(userdata['Runtime'])) listItem.setProperty('totaltime', str(userdata['Runtime']))
if typus == v.PLEX_TYPE_EPISODE: if typus == v.PLEX_TYPE_EPISODE:

View file

@ -3,11 +3,15 @@ PKC Kodi Monitoring implementation
""" """
from logging import getLogger from logging import getLogger
from json import loads from json import loads
from threading import Thread
from xbmc import Monitor, Player, sleep from xbmc import Monitor, Player, sleep, getCondVisibility, getInfoLabel, \
getLocalizedString
from xbmcgui import Window
import plexdb_functions as plexdb import plexdb_functions as plexdb
from utils import window, settings, CatchExceptions, plex_command from utils import window, settings, CatchExceptions, plex_command, \
thread_methods
from PlexFunctions import scrobble from PlexFunctions import scrobble
from kodidb_functions import kodiid_from_filename from kodidb_functions import kodiid_from_filename
from plexbmchelper.subscribers import LOCKER from plexbmchelper.subscribers import LOCKER
@ -391,3 +395,31 @@ class KodiMonitor(Monitor):
else: else:
window('plex_%s.playmethod' % currentFile, value="DirectPlay") window('plex_%s.playmethod' % currentFile, value="DirectPlay")
LOG.debug('Window properties set for direct paths!') LOG.debug('Window properties set for direct paths!')
@thread_methods
class SpecialMonitor(Thread):
"""
Detect the resume dialog for widgets.
Could also be used to detect external players (see Emby implementation)
"""
def run(self):
LOG.info("----====# Starting Special Monitor #====----")
player = Player()
while not self.thread_stopped():
is_playing = player.isPlaying()
if (not is_playing and
getCondVisibility('Window.IsVisible(DialogContextMenu.xml)') and
not getCondVisibility('Window.IsVisible(MyVideoNav.xml)') and
getInfoLabel('Control.GetLabel(1002)') == getLocalizedString(12021)):
control = int(Window(10106).getFocusId())
if control == 1002:
# Start from beginning
LOG.info("Resume dialog: Start from beginning selected")
state.RESUME_PLAYBACK = False
else:
LOG.info("Resume dialog: resume selected")
state.RESUME_PLAYBACK = True
sleep(200)
LOG.info("#====---- Special Monitor Stopped ----====#")

View file

@ -32,7 +32,7 @@ def pickle_me(obj, window_var='plex_result'):
obj can be pretty much any Python object. However, classes and obj can be pretty much any Python object. However, classes and
functions won't work. See the Pickle documentation functions won't work. See the Pickle documentation
""" """
log('%sStart pickling: %s' % (PREFIX, obj), level=LOGDEBUG) log('%sStart pickling' % PREFIX, level=LOGDEBUG)
pickl_window(window_var, value=dumps(obj)) pickl_window(window_var, value=dumps(obj))
log('%sSuccessfully pickled' % PREFIX, level=LOGDEBUG) log('%sSuccessfully pickled' % PREFIX, level=LOGDEBUG)

View file

@ -66,6 +66,44 @@ def playback_triage(plex_id=None, plex_type=None, path=None):
conclude_playback(playqueue, pos) conclude_playback(playqueue, pos)
def play_resume(playqueue, xml, stack):
"""
If there exists a resume point, Kodi will ask the user whether to continue
playback. We thus need to use setResolvedUrl "correctly". Mind that there
might be several parts!
"""
result = Playback_Successful()
listitem = PKC_ListItem()
# Only get the very first item of our playqueue (i.e. the very first part)
stack_item = stack.pop(0)
api = API(xml[0])
item = PL.playlist_item_from_xml(playqueue,
xml[0],
kodi_id=stack_item['kodi_id'],
kodi_type=stack_item['kodi_type'])
api.setPartNumber(item.part)
item.playcount = stack_item['playcount']
item.offset = stack_item['offset']
item.part = stack_item['part']
item.init_done = True
api.CreateListItemFromPlexItem(listitem)
playutils = PlayUtils(api, item)
playurl = playutils.getPlayUrl()
listitem.setPath(playurl)
if item.playmethod in ('DirectStream', 'DirectPlay'):
listitem.setSubtitles(api.externalSubs())
else:
playutils.audio_subtitle_prefs(listitem)
result.listitem = listitem
# Add to our playlist
playqueue.items.append(item)
# This will release default.py with setResolvedUrl
pickle_me(result)
# Add remaining parts to the playlist, if any
if stack:
_process_stack(playqueue, stack)
def playback_init(plex_id, plex_type, playqueue): def playback_init(plex_id, plex_type, playqueue):
""" """
Playback setup if Kodi starts playing an item for the first time. Playback setup if Kodi starts playing an item for the first time.
@ -112,10 +150,16 @@ def playback_init(plex_id, plex_type, playqueue):
playqueue.clear() playqueue.clear()
PL.get_playlist_details_from_xml(playqueue, xml) PL.get_playlist_details_from_xml(playqueue, xml)
stack = _prep_playlist_stack(xml) stack = _prep_playlist_stack(xml)
# if resume:
# # Need to handle this differently so only 1 dialog is displayed whether
# # user wants to resume to start at the beginning
# LOG.info('Resume detected')
# play_resume(playqueue, xml, stack)
# return
# Release our default.py before starting our own Kodi player instance # Release our default.py before starting our own Kodi player instance
pickle_me(Playback_Successful()) pickle_me(Playback_Successful())
# Sleep a bit to let setResolvedUrl do its thing - bit ugly # Sleep a bit to let setResolvedUrl do its thing - bit ugly
sleep(200) sleep(100)
_process_stack(playqueue, stack) _process_stack(playqueue, stack)
# New thread to release this one sooner (e.g. harddisk spinning up) # New thread to release this one sooner (e.g. harddisk spinning up)
thread = Thread(target=Player().play, thread = Thread(target=Player().play,
@ -138,7 +182,6 @@ def _prep_playlist_stack(xml):
# We will never store clips (trailers) in the Kodi DB # We will never store clips (trailers) in the Kodi DB
kodi_id = None kodi_id = None
kodi_type = None kodi_type = None
resume, _ = api.getRuntime()
for part, _ in enumerate(item[0]): for part, _ in enumerate(item[0]):
api.setPartNumber(part) api.setPartNumber(part)
if kodi_id is None: if kodi_id is None:
@ -164,7 +207,7 @@ def _prep_playlist_stack(xml):
'listitem': listitem, 'listitem': listitem,
'part': part, 'part': part,
'playcount': api.getViewCount(), 'playcount': api.getViewCount(),
'offset': resume 'offset': api.getResume()
}) })
return stack return stack
@ -213,6 +256,7 @@ def conclude_playback(playqueue, pos):
start playback start playback
return PKC listitem attached to result return PKC listitem attached to result
""" """
LOG.info('Concluding playback for playqueue position %s', pos)
result = Playback_Successful() result = Playback_Successful()
listitem = PKC_ListItem() listitem = PKC_ListItem()
item = playqueue.items[pos] item = playqueue.items[pos]
@ -226,10 +270,16 @@ def conclude_playback(playqueue, pos):
else: else:
playurl = item.file playurl = item.file
listitem.setPath(playurl) listitem.setPath(playurl)
if item.playmethod in ("DirectStream", "DirectPlay"): if item.playmethod in ('DirectStream', 'DirectPlay'):
listitem.setSubtitles(api.externalSubs()) listitem.setSubtitles(api.externalSubs())
else: else:
playutils.audio_subtitle_prefs(listitem) playutils.audio_subtitle_prefs(listitem)
listitem.setPath(playurl) listitem.setPath(playurl)
if state.RESUME_PLAYBACK is True:
state.RESUME_PLAYBACK = False
LOG.info('Resuming playback at %s', item.offset)
listitem.setProperty('StartOffset', str(item.offset))
listitem.setProperty('resumetime', str(item.offset))
result.listitem = listitem result.listitem = listitem
pickle_me(result) pickle_me(result)
LOG.info('Done concluding playback')

View file

@ -47,6 +47,7 @@ class PlayUtils():
}) })
self.item.playmethod = 'Transcode' self.item.playmethod = 'Transcode'
LOG.info("The playurl is: %s", playurl) LOG.info("The playurl is: %s", playurl)
self.item.file = playurl
return playurl return playurl
def isDirectPlay(self): def isDirectPlay(self):

View file

@ -128,6 +128,9 @@ PLAYSTATE = {
# paths for playback (since we're not receiving a Kodi id) # paths for playback (since we're not receiving a Kodi id)
PLEX_IDS = {} PLEX_IDS = {}
PLAYED_INFO = {} PLAYED_INFO = {}
# Set by SpecialMonitor - did user choose to resume playback or start from the
# beginning?
RESUME_PLAYBACK = False
# Kodi webserver details # Kodi webserver details
WEBSERVER_PORT = 8080 WEBSERVER_PORT = 8080

View file

@ -31,7 +31,7 @@ sys_path.append(_base_resource)
from utils import settings, window, language as lang, dialog, tryDecode from utils import settings, window, language as lang, dialog, tryDecode
from userclient import UserClient from userclient import UserClient
import initialsetup import initialsetup
from kodimonitor import KodiMonitor from kodimonitor import KodiMonitor, SpecialMonitor
from librarysync import LibrarySync from librarysync import LibrarySync
import videonodes import videonodes
from websocket_client import PMS_Websocket, Alexa_Websocket from websocket_client import PMS_Websocket, Alexa_Websocket
@ -155,6 +155,7 @@ class Service():
self.alexa = Alexa_Websocket() self.alexa = Alexa_Websocket()
self.library = LibrarySync() self.library = LibrarySync()
self.plexCompanion = PlexCompanion() self.plexCompanion = PlexCompanion()
self.specialMonitor = SpecialMonitor()
self.playback_starter = Playback_Starter() self.playback_starter = Playback_Starter()
if settings('enableTextureCache') == "true": if settings('enableTextureCache') == "true":
self.image_cache_thread = Image_Cache_Thread() self.image_cache_thread = Image_Cache_Thread()
@ -197,6 +198,7 @@ class Service():
sound=False) sound=False)
# Start monitoring kodi events # Start monitoring kodi events
self.kodimonitor_running = KodiMonitor() self.kodimonitor_running = KodiMonitor()
self.specialMonitor.start()
# Start the Websocket Client # Start the Websocket Client
if not self.ws_running: if not self.ws_running:
self.ws_running = True self.ws_running = True