From 6e6d6cc11063aafcdcec43fa1a01d6f63be5c05e Mon Sep 17 00:00:00 2001 From: croneter Date: Tue, 17 Jul 2018 13:48:09 +0200 Subject: [PATCH] New Playlists menu item for video libraries --- default.py | 3 ++ resources/lib/entrypoint.py | 64 +++++++++++++++++++++++++++++++------ resources/lib/plex_api.py | 25 +++++++++------ resources/lib/videonodes.py | 27 ++++++++++++---- 4 files changed, 94 insertions(+), 25 deletions(-) diff --git a/default.py b/default.py index 6a3f75c4..9940e799 100644 --- a/default.py +++ b/default.py @@ -136,6 +136,9 @@ class Main(): plexId = itemid or None entrypoint.get_video_files(plexId, params) + elif mode == 'playlists': + entrypoint.playlists(params.get('type')) + else: entrypoint.show_main_menu(content_type=params.get('content_type')) diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index 14eb2c9e..79cd0ee0 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -524,6 +524,24 @@ def extra_fanart(plex_id, plex_path): xbmcplugin.endOfDirectory(int(argv[1])) +def _wait_for_auth(): + """ + Call to be sure that PKC is authenticated, e.g. for widgets on Kodi + startup. Will wait for at most 30s, then fail if not authenticated. + + Will set xbmcplugin.endOfDirectory(int(argv[1]), False) if failed + """ + counter = 0 + while utils.window('plex_authenticated') != 'true': + counter += 1 + if counter == 300: + LOG.error('Aborting view, we were not authenticated for PMS') + xbmcplugin.endOfDirectory(int(argv[1]), False) + return False + sleep(100) + return True + + def on_deck_episodes(viewid, tagname, limit): """ Retrieves Plex On Deck items, currently only for TV shows @@ -539,15 +557,8 @@ def on_deck_episodes(viewid, tagname, limit): if utils.settings('OnDeckTVextended') == 'false': # Chances are that this view is used on Kodi startup # Wait till we've connected to a PMS. At most 30s - counter = 0 - while utils.window('plex_authenticated') != 'true': - counter += 1 - if counter == 300: - LOG.error('Aborting On Deck view, we were not authenticated ' - 'for the PMS') - xbmcplugin.endOfDirectory(int(argv[1]), False) - return - sleep(100) + if not _wait_for_auth(): + return xml = DU().downloadUrl('{server}/library/sections/%s/onDeck' % viewid) if xml in (None, 401): LOG.error('Could not download PMS xml for view %s', viewid) @@ -659,6 +670,41 @@ def on_deck_episodes(viewid, tagname, limit): xbmcplugin.endOfDirectory(handle=int(argv[1])) +def playlists(kodi_playlist_type): + """ + Lists all Plex playlists of the media type kodi_playlist_type + """ + LOG.debug('Listing Plex %s playlists', kodi_playlist_type) + if not _wait_for_auth(): + return + xbmcplugin.setContent(int(argv[1]), 'files') + from .playlists.pms import all_playlists + xml = all_playlists() + if xml is None: + return + for item in xml: + api = API(item) + if not v.KODI_PLAYLIST_TYPE_FROM_PLEX[api.playlist_type()] \ + == kodi_playlist_type: + continue + listitem = ListItem(api.title()) + listitem.setArt({'thumb': api._one_artwork('composite')}) + url = "plugin://%s/" % v.ADDON_ID + key = api.path_and_plex_id() + params = { + 'mode': "browseplex", + 'key': key, + } + LOG.debug('adding item: %s', params) + xbmcplugin.addDirectoryItem(handle=int(argv[1]), + url="%s?%s" % (url, urlencode(params)), + isFolder=True, + listitem=listitem) + xbmcplugin.endOfDirectory( + handle=int(argv[1]), + cacheToDisc=utils.settings('enableTextureCache') == 'true') + + def watchlater(): """ Listing for plex.tv Watch Later section (if signed in to plex.tv) diff --git a/resources/lib/plex_api.py b/resources/lib/plex_api.py index 32f91b97..64e4665c 100644 --- a/resources/lib/plex_api.py +++ b/resources/lib/plex_api.py @@ -820,16 +820,21 @@ class API(object): artwork = self.item.get(art_kind) if artwork and not artwork.startswith('http'): if '/composite/' in artwork: - # e.g. Plex collections where artwork already contains width - # and height. Need to upscale for better resolution - artwork, args = artwork.split('?') - args = dict(parse_qsl(args)) - width = int(args.get('width', 400)) - height = int(args.get('height', 400)) - # Adjust to 4k resolution 3,840x2,160 - scaling = 3840.0 / float(max(width, height)) - width = int(scaling * width) - height = int(scaling * height) + try: + # e.g. Plex collections where artwork already contains + # width and height. Need to upscale for better resolution + artwork, args = artwork.split('?') + args = dict(parse_qsl(args)) + width = int(args.get('width', 400)) + height = int(args.get('height', 400)) + # Adjust to 4k resolution 3,840x2,160 + scaling = 3840.0 / float(max(width, height)) + width = int(scaling * width) + height = int(scaling * height) + except ValueError: + # e.g. playlists + width = 3840 + height = 3840 artwork = '%s?width=%s&height=%s' % (artwork, width, height) artwork = ('%s/photo/:/transcode?width=3840&height=3840&' 'minSize=1&upscale=0&url=%s' diff --git a/resources/lib/videonodes.py b/resources/lib/videonodes.py index 73b32613..ede2abd0 100644 --- a/resources/lib/videonodes.py +++ b/resources/lib/videonodes.py @@ -135,7 +135,8 @@ class VideoNodes(object): '10': "random", '11': "recommended", '12': "ondeck", - '13': 'browsefiles' + '13': 'browsefiles', + '14': 'playlists' } mediatypes = { # label according to nodetype per mediatype @@ -150,7 +151,8 @@ class VideoNodes(object): '10': 30227, '11': 30230, '12': 39500, - '13': 39702 + '13': 39702, + '14': 136 }, 'tvshows': @@ -165,7 +167,8 @@ class VideoNodes(object): '10': 30227, # '11': 30230, '12': 39500, - '13': 39702 + '13': 39702, + '14': 136 }, 'homevideos': @@ -173,7 +176,8 @@ class VideoNodes(object): '1': tagname, '2': 30251, '11': 30253, - '13': 39702 + '13': 39702, + '14': 136 }, 'photos': @@ -209,7 +213,8 @@ class VideoNodes(object): '10': '8', # "random", '11': '5', # "recommended", '12': '1', # "ondeck" - '13': '9' # browse by folder + '13': '9', # browse by folder + '14': '10' # Playlists } nodes = mediatypes[mediatype] @@ -257,6 +262,12 @@ class VideoNodes(object): nodetype = 'inprogress' elif nodetype == 'browsefiles': path = 'plugin://plugin.video.plexkodiconnect?mode=browseplex&key=/library/sections/%s/folder' % viewid + elif nodetype == 'playlists': + path = 'plugin://plugin.video.plexkodiconnect?mode=playlists' + if mediatype in ('movies', 'tvshows', 'homevideos'): + path += '&type=%s' % v.KODI_PLAYLIST_TYPE_VIDEO + else: + path += '&type=%s' % v.KODI_PLAYLIST_TYPE_AUDIO else: path = "library://video/Plex-%s/%s_%s.xml" % (dirname, viewid, nodetype) @@ -298,7 +309,11 @@ class VideoNodes(object): continue # Create the root - if (nodetype in ("nextepisodes", "ondeck", 'recentepisodes', 'browsefiles') or mediatype == "homevideos"): + if (nodetype in ("nextepisodes", + "ondeck", + 'recentepisodes', + 'browsefiles', + 'playlists') or mediatype == "homevideos"): # Folder type with plugin path root = self.commonRoot(order=sortorder[node], label=label,