From 845cfb44d5c814f2c51a9fc277506c2084b623d9 Mon Sep 17 00:00:00 2001 From: croneter Date: Thu, 31 Oct 2019 12:51:21 +0100 Subject: [PATCH] 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),