From ca52117c4f702dbda487e324e93f6f23cd25af8c Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 25 Oct 2019 13:41:54 +0200 Subject: [PATCH 01/18] Fix resume not working in some cases --- resources/lib/timing.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/resources/lib/timing.py b/resources/lib/timing.py index 818a3629..5ace8a90 100644 --- a/resources/lib/timing.py +++ b/resources/lib/timing.py @@ -63,25 +63,21 @@ def kodi_now(): def millis_to_kodi_time(milliseconds): """ - Converts time in milliseconds to the time dict used by the Kodi JSON RPC: + Converts time in milliseconds [int or float] to the time dict used by the + Kodi JSON RPC: { 'hours': [int], 'minutes': [int], 'seconds'[int], 'milliseconds': [int] } - Pass in the time in milliseconds as an int """ seconds = int(milliseconds / 1000) minutes = int(seconds / 60) - seconds = seconds % 60 - hours = int(minutes / 60) - minutes = minutes % 60 - milliseconds = milliseconds % 1000 - return {'hours': hours, - 'minutes': minutes, - 'seconds': seconds, - 'milliseconds': milliseconds} + return {'hours': int(minutes / 60), + 'minutes': int(minutes % 60), + 'seconds': int(seconds % 60), + 'milliseconds': int(milliseconds % 1000)} def kodi_time_to_millis(time): From 76e1b1c629bfb3c3b6a3cb890ae91ec6d40ac457 Mon Sep 17 00:00:00 2001 From: croneter Date: Mon, 28 Oct 2019 18:01:49 +0100 Subject: [PATCH 02/18] Fix resume when casting to PKC --- resources/lib/playback.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/lib/playback.py b/resources/lib/playback.py index 786d2735..00c882bd 100644 --- a/resources/lib/playback.py +++ b/resources/lib/playback.py @@ -563,9 +563,9 @@ def play_xml(playqueue, xml, offset=None, start_plex_id=None): Either supply the ratingKey of the starting Plex element. Or set playqueue.selectedItemID """ - offset = int(offset) if offset else None - LOG.info("play_xml called with offset %s, start_plex_id %s", - offset, start_plex_id) + offset = int(offset) / 1000 if offset else None + LOG.debug("play_xml called with offset %s, start_plex_id %s", + offset, start_plex_id) start_item = start_plex_id if start_plex_id is not None \ else playqueue.selectedItemID for startpos, video in enumerate(xml): From 9f2210a5e7c87d0d3a51c0446aa7abf6af5636c8 Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 25 Oct 2019 17:06:50 +0200 Subject: [PATCH 03/18] Rewire PKC resume mechanism --- resources/lib/playback.py | 159 ++++++++++++++---------------- resources/lib/playback_starter.py | 7 +- resources/lib/playlist_func.py | 1 + resources/lib/plex_api/base.py | 7 +- resources/lib/plex_companion.py | 5 +- 5 files changed, 91 insertions(+), 88 deletions(-) diff --git a/resources/lib/playback.py b/resources/lib/playback.py index 00c882bd..12c49d91 100644 --- a/resources/lib/playback.py +++ b/resources/lib/playback.py @@ -30,7 +30,8 @@ RESOLVE = True ############################################################################### -def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True): +def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True, + resume=False): """ Hit this function for addon path playback, Plex trailers, etc. Will setup playback first, then on second call complete playback. @@ -47,7 +48,7 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True): service.py Python instance """ try: - _playback_triage(plex_id, plex_type, path, resolve) + _playback_triage(plex_id, plex_type, path, resolve, resume) finally: # Reset some playback variables the user potentially set to init # playback @@ -56,10 +57,10 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True): app.PLAYSTATE.resume_playback = None -def _playback_triage(plex_id, plex_type, path, resolve): +def _playback_triage(plex_id, plex_type, path, resolve, resume): plex_id = utils.cast(int, plex_id) - LOG.info('playback_triage called with plex_id %s, plex_type %s, path %s, ' - 'resolve %s', plex_id, plex_type, path, resolve) + LOG.debug('playback_triage called with plex_id %s, plex_type %s, path %s, ' + 'resolve %s, resume %s', plex_id, plex_type, path, resolve, resume) global RESOLVE # If started via Kodi context menu, we never resolve RESOLVE = resolve if not app.PLAYSTATE.context_menu_play else False @@ -85,12 +86,12 @@ def _playback_triage(plex_id, plex_type, path, resolve): except KeyError: # Kodi bug - Playlist plays (not Playqueue) will ALWAYS be audio for # add-on paths - LOG.info('No position returned from player! Assuming playlist') + LOG.debug('No position returned from player! Assuming playlist') playqueue = PQ.get_playqueue_from_type(v.KODI_PLAYLIST_TYPE_AUDIO) try: pos = js.get_position(playqueue.playlistid) except KeyError: - LOG.info('Assuming video instead of audio playlist playback') + LOG.debug('Assuming video instead of audio playlist playback') playqueue = PQ.get_playqueue_from_type(v.KODI_PLAYLIST_TYPE_VIDEO) try: pos = js.get_position(playqueue.playlistid) @@ -108,12 +109,12 @@ def _playback_triage(plex_id, plex_type, path, resolve): try: item = items[pos] except IndexError: - LOG.info('Could not apply playlist hack! Probably Widget playback') + LOG.debug('Could not apply playlist hack! Probably Widget playback') else: if ('id' not in item and item.get('type') == 'unknown' and item.get('title') == ''): - LOG.info('Kodi playlist play detected') - _playlist_playback(plex_id, plex_type) + LOG.debug('Kodi playlist play detected') + _playlist_playback(plex_id) return # Can return -1 (as in "no playlist") @@ -127,7 +128,7 @@ def _playback_triage(plex_id, plex_type, path, resolve): initiate = True else: if item.plex_id != plex_id: - LOG.debug('Received new plex_id %s, expected %s', + LOG.debug('Received new plex_id%s, expected %s', plex_id, item.plex_id) initiate = True else: @@ -136,13 +137,14 @@ def _playback_triage(plex_id, plex_type, path, resolve): LOG.debug('Detected re-playing of the same item') initiate = True if initiate: - _playback_init(plex_id, plex_type, playqueue, pos) + _playback_init(plex_id, plex_type, playqueue, pos, resume) else: - # kick off playback on second pass + # kick off playback on second pass, resume was already set on first + # pass (threaded_playback will seek to resume) _conclude_playback(playqueue, pos) -def _playlist_playback(plex_id, plex_type): +def _playlist_playback(plex_id): """ Really annoying Kodi behavior: Kodi will throw the ENTIRE playlist some- where, causing Playlist.onAdd to fire for each item like this: @@ -175,11 +177,11 @@ def _playlist_playback(plex_id, plex_type): _conclude_playback(playqueue, pos=0) -def _playback_init(plex_id, plex_type, playqueue, pos): +def _playback_init(plex_id, plex_type, playqueue, pos, force_resume): """ Playback setup if Kodi starts playing an item for the first time. """ - LOG.info('Initializing PKC playback') + LOG.debug('Initializing PKC playback') # Stop playback so we don't get an error message that the last item of the # queue failed to play app.APP.player.stop() @@ -210,19 +212,8 @@ def _playback_init(plex_id, plex_type, playqueue, pos): # playqueues # Release default.py _ensure_resolve() - api = API(xml[0]) - if api.resume_point() and (app.SYNC.direct_paths or - app.PLAYSTATE.context_menu_play): - # Since Kodi won't ask if user wants to resume playback - - # we need to ask ourselves - resume = resume_dialog(int(api.resume_point())) - if resume is None: - LOG.info('User cancelled resume dialog') - return - elif app.SYNC.direct_paths: - resume = False - else: - resume = app.PLAYSTATE.resume_playback or False + LOG.debug('Using force_resume %s', force_resume) + resume = force_resume or False trailers = False if (not resume and plex_type == v.PLEX_TYPE_MOVIE and utils.settings('enableCinema') == "true"): @@ -251,11 +242,17 @@ def _playback_init(plex_id, plex_type, playqueue, pos): PL.get_playlist_details_from_xml(playqueue, xml) stack = _prep_playlist_stack(xml, resume) _process_stack(playqueue, stack) + offset = _use_kodi_db_offset(playqueue.items[pos].plex_id, + playqueue.items[pos].plex_type, + playqueue.items[pos].offset) if resume else 0 # New thread to release this one sooner (e.g. harddisk spinning up) thread = Thread(target=threaded_playback, - args=(playqueue.kodi_pl, pos, None)) + args=(playqueue.kodi_pl, pos, offset)) thread.setDaemon(True) - LOG.info('Done initializing playback, starting Kodi player at pos %s', pos) + LOG.debug('Done initializing playback, starting Kodi player at pos %s and ' + 'offset %s', pos, offset) + # Ensure that PKC playqueue monitor ignores the changes we just made + playqueue.pkc_edit = True # By design, PKC will start Kodi playback using Player().play(). Kodi # caches paths like our plugin://pkc. If we use Player().play() between # 2 consecutive startups of exactly the same Kodi library item, Kodi's @@ -263,8 +260,6 @@ def _playback_init(plex_id, plex_type, playqueue, pos): # plugin://pkc will be lost; Kodi will try to startup playback for an empty # path: log entry is "CGUIWindowVideoBase::OnPlayMedia " thread.start() - # Ensure that PKC playqueue monitor ignores the changes we just made - playqueue.pkc_edit = True def _ensure_resolve(abort=False): @@ -376,7 +371,7 @@ def _prep_playlist_stack(xml, resume): 'part': part, 'playcount': api.viewcount(), 'offset': api.resume_point(), - 'resume': resume if i + 1 == len(xml) and part == 0 else False, + 'resume': resume if part == 0 and i + 1 == len(xml) else None, 'id': api.item_id() }) return stack @@ -413,33 +408,20 @@ def _process_stack(playqueue, stack): pos += 1 -def _set_resume(listitem, item, api): - if item.plex_type in (v.PLEX_TYPE_SONG, v.PLEX_TYPE_CLIP): - return - if item.resume is True: - # Do NOT use item.offset directly but get it from the DB - # (user might have initiated same video twice) - with PlexDB(lock=False) as plexdb: - db_item = plexdb.item_by_id(item.plex_id, item.plex_type) - if db_item: - file_id = db_item['kodi_fileid'] - with KodiVideoDB(lock=False) as kodidb: - item.offset = kodidb.get_resume(file_id) - LOG.info('Resuming playback at %s', item.offset) - if v.KODIVERSION >= 18 and api: - # Kodi 18 Alpha 3 broke StartOffset - try: - percent = (item.offset or api.resume_point()) / api.runtime() * 100.0 - except ZeroDivisionError: - percent = 0.0 - LOG.debug('Resuming at %s percent', percent) - listitem.setProperty('StartPercent', str(percent)) - else: - listitem.setProperty('StartOffset', str(item.offset)) - listitem.setProperty('resumetime', str(item.offset)) - elif v.KODIVERSION >= 18: - # Make sure that the video starts from the beginning - listitem.setProperty('StartPercent', '0') +def _use_kodi_db_offset(plex_id, plex_type, plex_offset): + """ + Do NOT use item.offset directly but get it from the Kodi DB (Plex might not + have gotten the last resume point) + """ + if plex_type not in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_EPISODE): + return plex_offset + with PlexDB(lock=False) as plexdb: + db_item = plexdb.item_by_id(plex_id, plex_type) + if db_item: + with KodiVideoDB(lock=False) as kodidb: + return kodidb.get_resume(db_item['kodi_fileid']) + else: + return plex_offset def _conclude_playback(playqueue, pos): @@ -457,19 +439,14 @@ def _conclude_playback(playqueue, pos): start playback return PKC listitem attached to result """ - LOG.info('Concluding playback for playqueue position %s', pos) + LOG.debug('Concluding playback for playqueue position %s', pos) item = playqueue.items[pos] - if item.xml is not None: - # Got a Plex element - api = API(item.xml) - api.part = item.part or 0 - listitem = api.listitem(listitem=transfer.PKCListItem) - set_playurl(api, item) - else: - listitem = transfer.PKCListItem() - api = None + api = API(item.xml) + api.part = item.part or 0 + listitem = api.listitem(listitem=transfer.PKCListItem, resume=False) + set_playurl(api, item) if not item.file: - LOG.info('Did not get a playurl, aborting playback silently') + LOG.debug('Did not get a playurl, aborting playback silently') _ensure_resolve() return listitem.setPath(item.file.encode('utf-8')) @@ -478,9 +455,8 @@ def _conclude_playback(playqueue, pos): elif item.playmethod in (v.PLAYBACK_METHOD_DIRECT_STREAM, v.PLAYBACK_METHOD_TRANSCODE): audio_subtitle_prefs(api, listitem) - _set_resume(listitem, item, api) transfer.send(listitem) - LOG.info('Done concluding playback') + LOG.debug('Done concluding playback') def process_indirect(key, offset, resolve=True): @@ -494,8 +470,8 @@ def process_indirect(key, offset, resolve=True): 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) + LOG.debug('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 @@ -513,7 +489,7 @@ def process_indirect(key, offset, resolve=True): return api = API(xml[0]) - listitem = api.listitem(listitem=transfer.PKCListItem) + listitem = api.listitem(listitem=transfer.PKCListItem, resume=False) playqueue = PQ.get_playqueue_from_type( v.KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE[api.plex_type]) playqueue.clear() @@ -552,7 +528,7 @@ def process_indirect(key, offset, resolve=True): args={'item': utils.try_encode(playurl), 'listitem': listitem}) thread.setDaemon(True) - LOG.info('Done initializing PKC playback, starting Kodi player') + LOG.debug('Done initializing PKC playback, starting Kodi player') thread.start() @@ -581,21 +557,38 @@ def play_xml(playqueue, xml, offset=None, start_plex_id=None): LOG.debug('Playqueue after play_xml update: %s', playqueue) thread = Thread(target=threaded_playback, args=(playqueue.kodi_pl, startpos, offset)) - LOG.info('Done play_xml, starting Kodi player at position %s', startpos) + LOG.debug('Done play_xml, starting Kodi player at position %s', startpos) thread.start() def threaded_playback(kodi_playlist, startpos, offset): """ - Seek immediately after kicking off playback is not reliable. + Seek immediately after kicking off playback is not reliable. We even seek + to 0 (starting position) in case Kodi wants to resume but we want to start + over. + + offset: resume position in seconds [int/float] """ + LOG.debug('threaded_playback with startpos %s, offset %s', + startpos, offset) app.APP.player.play(kodi_playlist, None, False, startpos) - if offset and offset != '0': + if offset: i = 0 while not app.APP.is_playing or not js.get_player_ids(): app.APP.monitor.waitForAbort(0.1) i += 1 - if i > 100: + if i > 200: LOG.error('Could not seek to %s', offset) return - js.seek_to(int(offset)) + i = 0 + answ = js.seek_to(offset * 1000) + while 'error' in answ: + # Kodi sometimes returns {u'message': u'Failed to execute method.', + # u'code': -32100} if user quickly switches videos + i += 1 + if i > 10: + LOG.error('Failed to seek to %s', offset) + return + app.APP.monitor.waitForAbort(0.1) + answ = js.seek_to(offset * 1000) + LOG.debug('Seek to offset %s successful', offset) diff --git a/resources/lib/playback_starter.py b/resources/lib/playback_starter.py index 9c84e18d..c46996ed 100644 --- a/resources/lib/playback_starter.py +++ b/resources/lib/playback_starter.py @@ -37,10 +37,15 @@ class PlaybackTask(backgroundthread.Task): resolve = False if params.get('handle') == '-1' else True LOG.debug('Received mode: %s, params: %s', mode, params) if mode == 'play': + if params.get('resume'): + resume = params.get('resume') == '1' + else: + resume = None playback.playback_triage(plex_id=params.get('plex_id'), plex_type=params.get('plex_type'), path=params.get('path'), - resolve=resolve) + resolve=resolve, + resume=resume) elif mode == 'plex_node': playback.process_indirect(params['key'], params['offset'], diff --git a/resources/lib/playlist_func.py b/resources/lib/playlist_func.py index dcc4ce4c..c1500bc2 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -213,6 +213,7 @@ class PlaylistItem(object): "'guid': '{self.guid}', " "'playmethod': '{self.playmethod}', " "'playcount': {self.playcount}, " + "'resume': {self.resume}," "'offset': {self.offset}, " "'force_transcode': {self.force_transcode}, " "'part': {self.part}".format(self=self)) diff --git a/resources/lib/plex_api/base.py b/resources/lib/plex_api/base.py index e14bcf21..8c887854 100644 --- a/resources/lib/plex_api/base.py +++ b/resources/lib/plex_api/base.py @@ -605,11 +605,16 @@ class Base(object): % (v.ADDON_ID, url, v.PLEX_TYPE_CLIP)) return url - def listitem(self, listitem=xbmcgui.ListItem): + def listitem(self, listitem=xbmcgui.ListItem, resume=True): """ Returns a xbmcgui.ListItem() (or PKCListItem) for this Plex element + + Pass resume=False in order to NOT set a resume point (but let Kodi + automatically handle it) """ item = widgets.generate_item(self) + if not resume and 'resume' in item: + del item['resume'] item = widgets.prepare_listitem(item) return widgets.create_listitem(item, as_tuple=False, listitem=listitem) diff --git a/resources/lib/plex_companion.py b/resources/lib/plex_companion.py index 80ab3bd2..0ab2b0cb 100644 --- a/resources/lib/plex_companion.py +++ b/resources/lib/plex_companion.py @@ -151,11 +151,10 @@ class PlexCompanion(backgroundthread.KillableThread): playback.play_xml(playqueue, xml, offset) else: app.CONN.plex_transient_token = data.get('token') - if data.get('offset') != '0': - app.PLAYSTATE.resume_playback = True playback.playback_triage(api.plex_id, api.plex_type, - resolve=False) + resolve=False, + resume=data.get('offset') not in ('0', None)) @staticmethod def _process_node(data): From 730ac203ad1d1f2acdacb18cb12e2107301c8235 Mon Sep 17 00:00:00 2001 From: croneter Date: Tue, 29 Oct 2019 09:59:10 +0100 Subject: [PATCH 04/18] Remove hack to detect replaying of the last video - does not work anymore --- resources/lib/kodimonitor.py | 74 ++++-------------------------------- 1 file changed, 7 insertions(+), 67 deletions(-) diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index 0067c8ba..aa864961 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -17,14 +17,15 @@ from .plex_api import API from .plex_db import PlexDB from . import kodi_db from .downloadutils import DownloadUtils as DU -from . import utils, timing, plex_functions as PF, playback +from . import utils, timing, plex_functions as PF from . import json_rpc as js, playqueue as PQ, playlist_func as PL from . import backgroundthread, app, variables as v LOG = getLogger('PLEX.kodimonitor') # "Start from beginning", "Play from beginning" -STRINGS = (utils.lang(12021).encode('utf-8'), utils.lang(12023).encode('utf-8')) +STRINGS = (utils.lang(12021).encode('utf-8'), + utils.lang(12023).encode('utf-8')) class KodiMonitor(xbmc.Monitor): @@ -33,7 +34,6 @@ class KodiMonitor(xbmc.Monitor): """ def __init__(self): self._already_slept = False - self.hack_replay = None xbmc.Monitor.__init__(self) for playerid in app.PLAYSTATE.player_states: app.PLAYSTATE.player_states[playerid] = copy.deepcopy(app.PLAYSTATE.template) @@ -57,9 +57,6 @@ class KodiMonitor(xbmc.Monitor): Monitor the PKC settings for changes made by the user """ LOG.debug('PKC settings change detected') - # Assume that the user changed something so we can try to reconnect - # app.APP.suspend = False - # app.APP.resume_threads(block=False) def onNotification(self, sender, method, data): """ @@ -69,28 +66,12 @@ class KodiMonitor(xbmc.Monitor): data = loads(data, 'utf-8') LOG.debug("Method: %s Data: %s", method, data) - # Hack - if not method == 'Player.OnStop': - self.hack_replay = None - if method == "Player.OnPlay": with app.APP.lock_playqueues: self.PlayBackStart(data) elif method == "Player.OnStop": - # Should refresh our video nodes, e.g. on deck - # xbmc.executebuiltin('ReloadSkin()') - if (self.hack_replay and not data.get('end') and - self.hack_replay == data['item']): - # Hack for add-on paths - self.hack_replay = None - with app.APP.lock_playqueues: - self._hack_addon_paths_replay_video() - elif data.get('end'): - with app.APP.lock_playqueues: - _playback_cleanup(ended=True) - else: - with app.APP.lock_playqueues: - _playback_cleanup() + with app.APP.lock_playqueues: + _playback_cleanup(ended=data.get('end')) elif method == 'Playlist.OnAdd': if 'item' in data and data['item'].get('type') == v.KODI_TYPE_SHOW: # Hitting the "browse" button on tv show info dialog @@ -126,39 +107,6 @@ class KodiMonitor(xbmc.Monitor): elif method == 'Other.plugin.video.plexkodiconnect_play_action': self._start_next_episode(data) - @staticmethod - def _hack_addon_paths_replay_video(): - """ - Hack we need for RESUMABLE items because Kodi lost the path of the - last played item that is now being replayed (see playback.py's - Player().play()) Also see playqueue.py _compare_playqueues() - - Needed if user re-starts the same video from the library using addon - paths. (Video is only added to playqueue, then immediately stoppen. - There is no playback initialized by Kodi.) Log excerpts: - Method: Playlist.OnAdd Data: - {u'item': {u'type': u'movie', u'id': 4}, - u'playlistid': 1, - u'position': 0} - Now we would hack! - Method: Player.OnStop Data: - {u'item': {u'type': u'movie', u'id': 4}, - u'end': False} - (within the same micro-second!) - """ - LOG.info('Detected re-start of playback of last item') - old = app.PLAYSTATE.old_player_states[1] - kwargs = { - 'plex_id': old['plex_id'], - 'plex_type': old['plex_type'], - 'path': old['file'], - 'resolve': False - } - task = backgroundthread.FunctionAsTask(playback.playback_triage, - None, - **kwargs) - backgroundthread.BGThreader.addTasksToFront([task]) - def _playlist_onadd(self, data): """ Called if an item is added to a Kodi playlist. Example data dict: @@ -171,15 +119,7 @@ class KodiMonitor(xbmc.Monitor): } Will NOT be called if playback initiated by Kodi widgets """ - if 'id' not in data['item']: - return - old = app.PLAYSTATE.old_player_states[data['playlistid']] - if (not app.SYNC.direct_paths and - data['position'] == 0 and data['playlistid'] == 1 and - not PQ.PLAYQUEUES[data['playlistid']].items and - data['item']['type'] == old['kodi_type'] and - data['item']['id'] == old['kodi_id']): - self.hack_replay = data['item'] + pass def _playlist_onremove(self, data): """ @@ -451,7 +391,7 @@ def _playback_cleanup(ended=False): app.PLAYSTATE.active_players = set() app.PLAYSTATE.item = None utils.delete_temporary_subtitles() - LOG.info('Finished PKC playback cleanup') + LOG.debug('Finished PKC playback cleanup') def _record_playstate(status, ended): From 8830cf22db8d4edc5d9def2cea52fb81d9b70a4e Mon Sep 17 00:00:00 2001 From: croneter Date: Wed, 30 Oct 2019 17:53:40 +0100 Subject: [PATCH 05/18] Fix Kodi crashing when casting from Plex Web --- resources/lib/plex_companion.py | 37 ++++++--------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/resources/lib/plex_companion.py b/resources/lib/plex_companion.py index 80ab3bd2..fd3b46ed 100644 --- a/resources/lib/plex_companion.py +++ b/resources/lib/plex_companion.py @@ -59,10 +59,8 @@ def update_playqueue_from_PMS(playqueue, # reconnects and Kodi is already playing something - silly, really # For all other cases, a new playqueue is generated by Plex LOG.debug('Update for existing playqueue detected') - new = False - else: - new = True - playqueue.clear() + return + playqueue.clear() # Get new metadata for the playqueue first try: PL.get_playlist_details_from_xml(playqueue, xml) @@ -71,33 +69,10 @@ def update_playqueue_from_PMS(playqueue, return playqueue.repeat = 0 if not repeat else int(repeat) playqueue.plex_transient_token = transient_token - if new: - playback.play_xml(playqueue, - xml, - offset=offset, - start_plex_id=start_plex_id) - return - # Updates to playqueues could potentially become a bit more ugly... - if app.APP.is_playing: - try: - playerid = js.get_player_ids()[0] - except IndexError: - LOG.error('Unexpectately could not get Kodi player id') - return - if app.PLAYSTATE.player_states[playerid]['plex_id'] == start_plex_id: - # Nothing to do - let's not seek to avoid jumps in playback - return - pos = playqueue.position_from_plex_id(start_plex_id) - LOG.debug('Skipping to position %s for %s', pos, playqueue) - js.skipto(pos) - if offset: - js.seek_to(offset) - return - # Need to initiate playback again using our existing playqueue - app.APP.player.play(playqueue.kodi_pl, - None, - False, - playqueue.position_from_plex_id(start_plex_id)) + playback.play_xml(playqueue, + xml, + offset=offset, + start_plex_id=start_plex_id) class PlexCompanion(backgroundthread.KillableThread): From 80d55a53883e9d6c60d0189bb4f7f044f57d054f Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 31 Oct 2019 07:47:34 +0100 Subject: [PATCH 06/18] Fix PKC throwing error if m3u playlist contains resume information --- resources/lib/playlists/db.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resources/lib/playlists/db.py b/resources/lib/playlists/db.py index 265fddee..6b63c39a 100644 --- a/resources/lib/playlists/db.py +++ b/resources/lib/playlists/db.py @@ -64,7 +64,11 @@ def _m3u_iterator(text): lines = iter(text.split('\n')) for line in lines: if line.startswith('#EXTINF:'): - yield next(lines).strip() + next_line = next(lines).strip() + if next_line.startswith('#EXT-KX-OFFSET:'): + yield next(lines).strip() + else: + yield next_line def m3u_to_plex_ids(playlist): From 845cfb44d5c814f2c51a9fc277506c2084b623d9 Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 31 Oct 2019 12:51:21 +0100 Subject: [PATCH 07/18] Support Plex search across all media and Plex Media Servers --- default.py | 7 +++++++ resources/lib/entrypoint.py | 35 +++++++++++++++++++++++++--------- resources/lib/plex_api/base.py | 6 ++++++ resources/lib/variables.py | 3 +++ resources/lib/widgets.py | 5 +++-- 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/default.py b/default.py index 86f33252..a05dc5d4 100644 --- a/default.py +++ b/default.py @@ -61,6 +61,13 @@ class Main(): elif mode == 'channels': entrypoint.browse_plex(key='/channels/all') + elif mode == 'search': + # "Search" + entrypoint.browse_plex(key='/hubs/search', + args={'includeCollections': 1, + 'includeExternalMedia': 1}, + prompt=utils.lang(137)) + elif mode == 'route_to_extras': # Hack so we can store this path in the Kodi DB handle = ('plugin://%s?mode=extras&plex_id=%s' diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index d8b3b883..8880501e 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -146,6 +146,8 @@ def show_main_menu(content_type=None): if content_type: path += '&content_type=%s' % content_type directory_item('Plex Hub', path) + # Plex Search "Search" + directory_item(utils.lang(137), "plugin://%s?mode=search" % v.ADDON_ID) # Plex Watch later if content_type not in ('image', 'audio'): directory_item(utils.lang(39211), @@ -466,7 +468,7 @@ def watchlater(): def browse_plex(key=None, plex_type=None, section_id=None, synched=True, - prompt=None): + args=None, prompt=None): """ Lists the content of a Plex folder, e.g. channels. Either pass in key (to be used directly for PMS url {server}) or the section_id @@ -474,28 +476,43 @@ def browse_plex(key=None, plex_type=None, section_id=None, synched=True, Pass synched=False if the items have NOT been synched to the Kodi DB """ LOG.debug('Browsing to key %s, section %s, plex_type: %s, synched: %s, ' - 'prompt "%s"', key, section_id, plex_type, synched, prompt) + 'prompt "%s", args %s', key, section_id, plex_type, synched, + prompt, args) if not _wait_for_auth(): xbmcplugin.endOfDirectory(int(sys.argv[1]), False) return app.init(entrypoint=True) + args = args or {} if prompt: prompt = utils.dialog('input', prompt) if prompt is None: # User cancelled return prompt = prompt.strip().decode('utf-8') - if '?' not in key: - key = '%s?query=%s' % (key, prompt) - else: - key = '%s&query=%s' % (key, prompt) - xml = DU().downloadUrl('{server}%s' % key) + args['query'] = prompt + xml = DU().downloadUrl(utils.extend_url('{server}%s' % key, args)) try: - xml.attrib - except AttributeError: + xml[0].attrib + except (TypeError, IndexError, AttributeError): LOG.error('Could not browse to key %s, section %s', key, section_id) return + if xml[0].tag == 'Hub': + # E.g. when hitting the endpoint '/hubs/search' + answ = utils.etree.Element(xml.tag, attrib=xml.attrib) + for hub in xml: + if not utils.cast(int, hub.get('size')): + # Empty category + continue + for entry in hub: + api = API(entry) + if api.plex_type == v.PLEX_TYPE_TAG: + # Append the type before the actual element for all "tags" + # like genres, actors, etc. + entry.attrib['tag'] = '%s: %s' % (hub.get('title'), + api.tag_label()) + answ.append(entry) + xml = answ show_listing(xml, plex_type, section_id, synched, key) diff --git a/resources/lib/plex_api/base.py b/resources/lib/plex_api/base.py index e14bcf21..219d9079 100644 --- a/resources/lib/plex_api/base.py +++ b/resources/lib/plex_api/base.py @@ -57,6 +57,12 @@ class Base(object): """ return self.xml.tag + def tag_label(self): + """ + Returns the 'tag' attribute of the xml + """ + return self.xml.get('tag') + @property def attrib(self): """ diff --git a/resources/lib/variables.py b/resources/lib/variables.py index d848bf95..e12d07d3 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -181,6 +181,9 @@ PLEX_TYPE_PHOTO = 'photo' PLEX_TYPE_PLAYLIST = 'playlist' PLEX_TYPE_CHANNEL = 'channel' +# E.g. PMS answer when hitting the PMS endpoint /hubs/search +PLEX_TYPE_TAG = 'tag' + # Used for /:/timeline XML messages PLEX_PLAYLIST_TYPE_VIDEO = 'video' PLEX_PLAYLIST_TYPE_AUDIO = 'music' diff --git a/resources/lib/widgets.py b/resources/lib/widgets.py index 44789e07..368e2977 100644 --- a/resources/lib/widgets.py +++ b/resources/lib/widgets.py @@ -105,9 +105,10 @@ def _generate_folder(api): return content else: art = api.artwork() + title = api.title() if api.plex_type != v.PLEX_TYPE_TAG else api.tag_label() return { - 'title': api.title(), - 'label': api.title(), + 'title': title, + 'label': title, 'file': api.directory_path(section_id=SECTION_ID, plex_type=PLEX_TYPE, old_key=KEY), From 0a06a6ad7881a39a687bc51caf53b1eb44be591b Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 31 Oct 2019 13:01:08 +0100 Subject: [PATCH 08/18] Always use the current Kodi language when communicating with the PMS --- resources/lib/clientinfo.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/lib/clientinfo.py b/resources/lib/clientinfo.py index 54378598..48df4554 100644 --- a/resources/lib/clientinfo.py +++ b/resources/lib/clientinfo.py @@ -3,6 +3,8 @@ from __future__ import absolute_import, division, unicode_literals from logging import getLogger +import xbmc + from . import utils from . import variables as v @@ -31,7 +33,7 @@ def getXArgsDeviceInfo(options=None, include_token=True): 'Connection': 'keep-alive', "Content-Type": "application/x-www-form-urlencoded", # "Access-Control-Allow-Origin": "*", - # 'X-Plex-Language': 'en', + 'Accept-Language': xbmc.getLanguage(xbmc.ISO_639_1), 'X-Plex-Device': v.DEVICE, 'X-Plex-Model': v.MODEL, 'X-Plex-Device-Name': v.DEVICENAME, From c063694999573ad3a5700bd0a071eab8a2537fa9 Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 31 Oct 2019 13:09:43 +0100 Subject: [PATCH 09/18] Beta version bump 2.9.12 --- README.md | 2 +- addon.xml | 15 +++++++++++---- changelog.txt | 7 +++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f132faf5..503e56b5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![stable version](https://img.shields.io/badge/stable_version-2.9.11-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) -[![beta version](https://img.shields.io/badge/beta_version-2.9.11-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) +[![beta version](https://img.shields.io/badge/beta_version-2.9.12-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) [![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation) [![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq) diff --git a/addon.xml b/addon.xml index bc1b5b9d..ef8b9c6b 100644 --- a/addon.xml +++ b/addon.xml @@ -1,11 +1,11 @@ - + - - + + video audio image @@ -83,7 +83,14 @@ Natūralioji „Plex“ integracija į „Kodi“ Prijunkite „Kodi“ prie „Plex Medija Serverio“. Šiame papildinyje daroma prielaida, kad valdote visus savo vaizdo įrašus naudodami „Plex“ (ir nė vieno su „Kodi“). Galite prarasti jau saugomus „Kodi“ vaizdo įrašų ir muzikos duomenų bazių duomenis (kadangi šis papildinys juos tiesiogiai pakeičia). Naudokite savo pačių rizika! Naudokite savo pačių rizika - version 2.9.11: + version 2.9.12 (beta only): +- Fix resume not working in some cases +- Support Plex search across all media and Plex Media Servers: Navigate to the PlexKodiConnect Add-on, then "Search" +- Always use the current Kodi language when communicating with the PMS (restart Kodi when changing the language!) +- Fix Kodi crashing when casting from e.g. Plex Web or Plex for Windows +- Fix PKC throwing error if m3u playlist contains resume information + +version 2.9.11: - version 2.9.10 for everyone version 2.9.10 (beta only): diff --git a/changelog.txt b/changelog.txt index 1c1ac47b..939d41cb 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,10 @@ +version 2.9.12 (beta only): +- Fix resume not working in some cases +- Support Plex search across all media and Plex Media Servers: Navigate to the PlexKodiConnect Add-on, then "Search" +- Always use the current Kodi language when communicating with the PMS (restart Kodi when changing the language!) +- Fix Kodi crashing when casting from e.g. Plex Web or Plex for Windows +- Fix PKC throwing error if m3u playlist contains resume information + version 2.9.11: - version 2.9.10 for everyone From e73c14bcf44118e1a8956a4982c0a61f5be87edf Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 31 Oct 2019 20:09:31 +0100 Subject: [PATCH 10/18] Fix PKC resuming instead of playing from the beginning --- resources/lib/playback.py | 40 ++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/resources/lib/playback.py b/resources/lib/playback.py index 12c49d91..df54d18a 100644 --- a/resources/lib/playback.py +++ b/resources/lib/playback.py @@ -572,23 +572,25 @@ def threaded_playback(kodi_playlist, startpos, offset): LOG.debug('threaded_playback with startpos %s, offset %s', startpos, offset) app.APP.player.play(kodi_playlist, None, False, startpos) - if offset: - i = 0 - while not app.APP.is_playing or not js.get_player_ids(): - app.APP.monitor.waitForAbort(0.1) - i += 1 - if i > 200: - LOG.error('Could not seek to %s', offset) - return - i = 0 + offset = offset if offset else 0 + i = 0 + while not app.APP.is_playing or not js.get_player_ids(): + if app.APP.monitor.waitForAbort(0.1): + # PKC needs to quit + return + i += 1 + if i > 200: + LOG.error('Could not seek to %s', offset) + return + i = 0 + answ = js.seek_to(offset * 1000) + while 'error' in answ: + # Kodi sometimes returns {u'message': u'Failed to execute method.', + # u'code': -32100} if user quickly switches videos + i += 1 + if i > 10: + LOG.error('Failed to seek to %s', offset) + return + app.APP.monitor.waitForAbort(0.1) answ = js.seek_to(offset * 1000) - while 'error' in answ: - # Kodi sometimes returns {u'message': u'Failed to execute method.', - # u'code': -32100} if user quickly switches videos - i += 1 - if i > 10: - LOG.error('Failed to seek to %s', offset) - return - app.APP.monitor.waitForAbort(0.1) - answ = js.seek_to(offset * 1000) - LOG.debug('Seek to offset %s successful', offset) + LOG.debug('Seek to offset %s successful', offset) From 2a11b378573944758c9541346472ef11abd41f28 Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 31 Oct 2019 20:11:27 +0100 Subject: [PATCH 11/18] Beta version bump 2.9.13 --- README.md | 2 +- addon.xml | 7 +++++-- changelog.txt | 3 +++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 503e56b5..78db3c46 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![stable version](https://img.shields.io/badge/stable_version-2.9.11-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) -[![beta version](https://img.shields.io/badge/beta_version-2.9.12-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) +[![beta version](https://img.shields.io/badge/beta_version-2.9.13-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) [![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation) [![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq) diff --git a/addon.xml b/addon.xml index ef8b9c6b..7ca7a31d 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + @@ -83,7 +83,10 @@ Natūralioji „Plex“ integracija į „Kodi“ Prijunkite „Kodi“ prie „Plex Medija Serverio“. Šiame papildinyje daroma prielaida, kad valdote visus savo vaizdo įrašus naudodami „Plex“ (ir nė vieno su „Kodi“). Galite prarasti jau saugomus „Kodi“ vaizdo įrašų ir muzikos duomenų bazių duomenis (kadangi šis papildinys juos tiesiogiai pakeičia). Naudokite savo pačių rizika! Naudokite savo pačių rizika - version 2.9.12 (beta only): + version 2.9.13 (beta only): +- Fix PKC resuming instead of playing from the beginning + +version 2.9.12 (beta only): - Fix resume not working in some cases - Support Plex search across all media and Plex Media Servers: Navigate to the PlexKodiConnect Add-on, then "Search" - Always use the current Kodi language when communicating with the PMS (restart Kodi when changing the language!) diff --git a/changelog.txt b/changelog.txt index 939d41cb..0fe2c4a7 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,6 @@ +version 2.9.13 (beta only): +- Fix PKC resuming instead of playing from the beginning + version 2.9.12 (beta only): - Fix resume not working in some cases - Support Plex search across all media and Plex Media Servers: Navigate to the PlexKodiConnect Add-on, then "Search" From da90e61ca8013364462827d9377d530779d34e3d Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 1 Nov 2019 11:44:31 +0100 Subject: [PATCH 12/18] Fix resume when starting playback via PMS or when force transcoding --- resources/lib/playback.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/resources/lib/playback.py b/resources/lib/playback.py index df54d18a..a48e5459 100644 --- a/resources/lib/playback.py +++ b/resources/lib/playback.py @@ -177,7 +177,7 @@ def _playlist_playback(plex_id): _conclude_playback(playqueue, pos=0) -def _playback_init(plex_id, plex_type, playqueue, pos, force_resume): +def _playback_init(plex_id, plex_type, playqueue, pos, resume): """ Playback setup if Kodi starts playing an item for the first time. """ @@ -212,8 +212,18 @@ def _playback_init(plex_id, plex_type, playqueue, pos, force_resume): # playqueues # Release default.py _ensure_resolve() - LOG.debug('Using force_resume %s', force_resume) - resume = force_resume or False + api = API(xml[0]) + if (app.PLAYSTATE.context_menu_play and + api.resume_point() and + api.plex_type in v.PLEX_VIDEOTYPES): + # User chose to either play via PMS or to force transcode + # Need to prompt whether we should resume_playback + resume = resume_dialog(int(api.resume_point())) + if resume is None: + # User cancelled dialog + return + LOG.debug('Using resume %s', resume) + resume = resume or False trailers = False if (not resume and plex_type == v.PLEX_TYPE_MOVIE and utils.settings('enableCinema') == "true"): @@ -292,6 +302,7 @@ def resume_dialog(resume): # "Resume from {0:s}" # "Start from beginning" resume = datetime.timedelta(seconds=resume) + LOG.debug('Showing PKC resume dialog for resume: %s', resume) answ = utils.dialog('contextmenu', [utils.lang(12022).replace('{0:s}', '{0}').format(unicode(resume)), utils.lang(12021)]) From 5e3f3daf905b11a4dd74655e3a5adceb9bd090ea Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 1 Nov 2019 13:00:34 +0100 Subject: [PATCH 13/18] Get rid of ContextMonitor and the dedicated Python thread - with new resume mechanics, this is not needed anymore --- resources/lib/kodimonitor.py | 38 ---------------------------------- resources/lib/playback.py | 4 ---- resources/lib/service_entry.py | 4 ---- 3 files changed, 46 deletions(-) diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index aa864961..e143140f 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -11,7 +11,6 @@ import json import binascii import xbmc -import xbmcgui from .plex_api import API from .plex_db import PlexDB @@ -23,10 +22,6 @@ from . import backgroundthread, app, variables as v LOG = getLogger('PLEX.kodimonitor') -# "Start from beginning", "Play from beginning" -STRINGS = (utils.lang(12021).encode('utf-8'), - utils.lang(12023).encode('utf-8')) - class KodiMonitor(xbmc.Monitor): """ @@ -593,36 +588,3 @@ def _videolibrary_onupdate(data): PF.scrobble(db_item['plex_id'], 'watched') else: PF.scrobble(db_item['plex_id'], 'unwatched') - - -class ContextMonitor(backgroundthread.KillableThread): - """ - Detect the resume dialog for widgets. Could also be used to detect - external players (see Emby implementation) - - Let's not register this thread because it won't quit due to - xbmc.getCondVisibility - It should still exit at some point due to xbmc.abortRequested - """ - def run(self): - LOG.info("----===## Starting ContextMonitor ##===----") - # app.APP.register_thread(self) - try: - self._run() - finally: - # app.APP.deregister_thread(self) - LOG.info("##===---- ContextMonitor Stopped ----===##") - - def _run(self): - while not self.isCanceled(): - # The following function will block if called while PKC should - # exit! - if xbmc.getCondVisibility('Window.IsVisible(DialogContextMenu.xml)'): - if xbmc.getInfoLabel('Control.GetLabel(1002)') in STRINGS: - # Remember that the item IS indeed resumable - control = int(xbmcgui.Window(10106).getFocusId()) - app.PLAYSTATE.resume_playback = True if control == 1001 else False - else: - # Different context menu is displayed - app.PLAYSTATE.resume_playback = False - xbmc.sleep(100) diff --git a/resources/lib/playback.py b/resources/lib/playback.py index df54d18a..796cbe7b 100644 --- a/resources/lib/playback.py +++ b/resources/lib/playback.py @@ -54,7 +54,6 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True, # playback app.PLAYSTATE.context_menu_play = False app.PLAYSTATE.force_transcode = False - app.PLAYSTATE.resume_playback = None def _playback_triage(plex_id, plex_type, path, resolve, resume): @@ -133,9 +132,6 @@ def _playback_triage(plex_id, plex_type, path, resolve, resume): initiate = True else: initiate = False - if not initiate and app.PLAYSTATE.resume_playback is not None: - LOG.debug('Detected re-playing of the same item') - initiate = True if initiate: _playback_init(plex_id, plex_type, playqueue, pos, resume) else: diff --git a/resources/lib/service_entry.py b/resources/lib/service_entry.py index 70e97a90..b7b10aed 100644 --- a/resources/lib/service_entry.py +++ b/resources/lib/service_entry.py @@ -95,7 +95,6 @@ class Service(object): self.setup = None self.alexa = None self.playqueue = None - self.context_monitor = None # Flags for other threads self.connection_check_running = False self.auth_running = False @@ -422,9 +421,6 @@ class Service(object): # Some plumbing app.init() app.APP.monitor = kodimonitor.KodiMonitor() - self.context_monitor = kodimonitor.ContextMonitor() - # Start immediately to catch user input even before auth - self.context_monitor.start() app.APP.player = xbmc.Player() # Initialize the PKC playqueues PQ.init_playqueues() From 8abbe74145be17b5e35d72b695cc3175aa06afcb Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 1 Nov 2019 13:07:15 +0100 Subject: [PATCH 14/18] Get rid of some obsolete imports --- resources/lib/kodi_db/video.py | 2 +- resources/lib/playback_decision.py | 2 +- resources/lib/plex_api/file.py | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/lib/kodi_db/video.py b/resources/lib/kodi_db/video.py index c2b01981..2c4d409a 100644 --- a/resources/lib/kodi_db/video.py +++ b/resources/lib/kodi_db/video.py @@ -5,7 +5,7 @@ from logging import getLogger from sqlite3 import IntegrityError from . import common -from .. import path_ops, timing, variables as v, app +from .. import path_ops, timing, variables as v LOG = getLogger('PLEX.kodi_db.video') diff --git a/resources/lib/playback_decision.py b/resources/lib/playback_decision.py index 25970099..522caf77 100644 --- a/resources/lib/playback_decision.py +++ b/resources/lib/playback_decision.py @@ -6,7 +6,7 @@ from requests import exceptions from .downloadutils import DownloadUtils as DU from .plex_api import API -from . import plex_functions as PF, utils, app, variables as v +from . import plex_functions as PF, utils, variables as v LOG = getLogger('PLEX.playback_decision') diff --git a/resources/lib/plex_api/file.py b/resources/lib/plex_api/file.py index e200e0d9..51ef98f1 100644 --- a/resources/lib/plex_api/file.py +++ b/resources/lib/plex_api/file.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, division, unicode_literals -from ..utils import cast from .. import utils, variables as v, app From db80f2b69a55faa4f92cd711d32b298138ccb990 Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 1 Nov 2019 13:15:34 +0100 Subject: [PATCH 15/18] Optimize clean-up of file table in the Kodi video database --- resources/lib/kodimonitor.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index aa864961..d0877d67 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -468,13 +468,15 @@ def _clean_file_table(): This function tries for at most 5 seconds to clean the file table. """ LOG.debug('Start cleaning Kodi files table') - app.APP.monitor.waitForAbort(2) + if app.APP.monitor.waitForAbort(2): + # PKC should exit + return try: - with kodi_db.KodiVideoDB() as kodidb_1: - with kodi_db.KodiVideoDB(lock=False) as kodidb_2: - for file_id in kodidb_1.obsolete_file_ids(): - LOG.debug('Removing obsolete Kodi file_id %s', file_id) - kodidb_2.remove_file(file_id, remove_orphans=False) + with kodi_db.KodiVideoDB() as kodidb: + obsolete_file_ids = list(kodidb.obsolete_file_ids()) + for file_id in obsolete_file_ids: + LOG.debug('Removing obsolete Kodi file_id %s', file_id) + kodidb.remove_file(file_id, remove_orphans=False) except utils.OperationalError: LOG.debug('Database was locked, unable to clean file table') else: From f56004f92a7432b3744eab20068e35c0add66cbf Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 1 Nov 2019 13:31:24 +0100 Subject: [PATCH 16/18] Beta version bump 2.9.14 --- README.md | 3 ++- addon.xml | 10 ++++++++-- changelog.txt | 6 ++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 78db3c46..5b5247a3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![stable version](https://img.shields.io/badge/stable_version-2.9.11-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) -[![beta version](https://img.shields.io/badge/beta_version-2.9.13-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) +[![beta version](https://img.shields.io/badge/beta_version-2.9.14-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) [![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation) [![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq) @@ -50,6 +50,7 @@ Some people argue that PKC is 'hacky' because of the way it directly accesses th ### PKC Features +- Kodi 19 is not yet supported (PKC is written in Python 2) - Support for Kodi 18 Leia - Support for Kodi 17 Krypton - [Amazon Alexa voice recognition](https://www.plex.tv/apps/streaming-devices/amazon-alexa) diff --git a/addon.xml b/addon.xml index 7ca7a31d..3acec88d 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + @@ -83,7 +83,13 @@ Natūralioji „Plex“ integracija į „Kodi“ Prijunkite „Kodi“ prie „Plex Medija Serverio“. Šiame papildinyje daroma prielaida, kad valdote visus savo vaizdo įrašus naudodami „Plex“ (ir nė vieno su „Kodi“). Galite prarasti jau saugomus „Kodi“ vaizdo įrašų ir muzikos duomenų bazių duomenis (kadangi šis papildinys juos tiesiogiai pakeičia). Naudokite savo pačių rizika! Naudokite savo pačių rizika - version 2.9.13 (beta only): + version 2.9.14 (beta only): +- Fix resume when starting playback via PMS or when force transcoding +- Get rid of ContextMonitor and the dedicated Python thread - with new resume mechanics, this is not needed anymore +- Optimize clean-up of file table in the Kodi video database after stopping playback +- Get rid of some obsolete imports + +version 2.9.13 (beta only): - Fix PKC resuming instead of playing from the beginning version 2.9.12 (beta only): diff --git a/changelog.txt b/changelog.txt index 0fe2c4a7..cf5a3871 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +version 2.9.14 (beta only): +- Fix resume when starting playback via PMS or when force transcoding +- Get rid of ContextMonitor and the dedicated Python thread - with new resume mechanics, this is not needed anymore +- Optimize clean-up of file table in the Kodi video database after stopping playback +- Get rid of some obsolete imports + version 2.9.13 (beta only): - Fix PKC resuming instead of playing from the beginning From a9b5ba4162bc6ce82a88a8ba67da664f80881653 Mon Sep 17 00:00:00 2001 From: croneter Date: Fri, 1 Nov 2019 13:40:45 +0100 Subject: [PATCH 17/18] Get rid of some obsolete code for the ContextMonitor we dropped --- resources/lib/app/playstate.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/resources/lib/app/playstate.py b/resources/lib/app/playstate.py index d5235ff4..688ee401 100644 --- a/resources/lib/app/playstate.py +++ b/resources/lib/app/playstate.py @@ -56,12 +56,6 @@ class PlayState(object): # Currently playing PKC item, a PlaylistItem() self.item = None - # Set by SpecialMonitor - did user choose to resume playback or start from the - # beginning? - # Set to None if resume dialog has not been shown - # True if dialog has been shown and user selected to resume - # False if dialog has been shown and user chose to start from beginning - self.resume_playback = None # Was the playback initiated by the user using the Kodi context menu? self.context_menu_play = False # Set by context menu - shall we force-transcode the next playing item? From d70f27df2ca96cd85af398a4e75041f61056968a Mon Sep 17 00:00:00 2001 From: croneter Date: Sat, 2 Nov 2019 12:12:31 +0100 Subject: [PATCH 18/18] Stable and beta version bump 2.10.0 --- README.md | 7 +++---- addon.xml | 8 ++++++-- changelog.txt | 4 ++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5b5247a3..eb839862 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![stable version](https://img.shields.io/badge/stable_version-2.9.11-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) -[![beta version](https://img.shields.io/badge/beta_version-2.9.14-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) +[![stable version](https://img.shields.io/badge/stable_version-2.10.0-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) +[![beta version](https://img.shields.io/badge/beta_version-2.10.0-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) [![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation) [![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq) @@ -50,9 +50,8 @@ Some people argue that PKC is 'hacky' because of the way it directly accesses th ### PKC Features -- Kodi 19 is not yet supported (PKC is written in Python 2) +- Kodi 19 Matrix is not yet supported (PKC is written in Python 2) - Support for Kodi 18 Leia -- Support for Kodi 17 Krypton - [Amazon Alexa voice recognition](https://www.plex.tv/apps/streaming-devices/amazon-alexa) - [Cinema Trailers & Extras](https://support.plex.tv/articles/202934883-cinema-trailers-extras/) - [Plex Watch Later / Plex It!](https://support.plex.tv/hc/en-us/sections/200211783-Plex-It-) diff --git a/addon.xml b/addon.xml index 3acec88d..24607d7d 100644 --- a/addon.xml +++ b/addon.xml @@ -1,5 +1,5 @@ - + @@ -83,7 +83,11 @@ Natūralioji „Plex“ integracija į „Kodi“ Prijunkite „Kodi“ prie „Plex Medija Serverio“. Šiame papildinyje daroma prielaida, kad valdote visus savo vaizdo įrašus naudodami „Plex“ (ir nė vieno su „Kodi“). Galite prarasti jau saugomus „Kodi“ vaizdo įrašų ir muzikos duomenų bazių duomenis (kadangi šis papildinys juos tiesiogiai pakeičia). Naudokite savo pačių rizika! Naudokite savo pačių rizika - version 2.9.14 (beta only): + version 2.10.0: +- version 2.9.12 - 2.9.14 for everyone +- Get rid of some obsolete code for the ContextMonitor we dropped + +version 2.9.14 (beta only): - Fix resume when starting playback via PMS or when force transcoding - Get rid of ContextMonitor and the dedicated Python thread - with new resume mechanics, this is not needed anymore - Optimize clean-up of file table in the Kodi video database after stopping playback diff --git a/changelog.txt b/changelog.txt index cf5a3871..34b5542a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +version 2.10.0: +- version 2.9.12 - 2.9.14 for everyone +- Get rid of some obsolete code for the ContextMonitor we dropped + version 2.9.14 (beta only): - Fix resume when starting playback via PMS or when force transcoding - Get rid of ContextMonitor and the dedicated Python thread - with new resume mechanics, this is not needed anymore