diff --git a/default.py b/default.py index 745ba009..86f33252 100644 --- a/default.py +++ b/default.py @@ -52,6 +52,9 @@ class Main(): synched=params.get('synched') != 'false', prompt=params.get('prompt')) + elif mode == 'show_section': + entrypoint.show_section(params.get('section_index')) + elif mode == 'watchlater': entrypoint.watchlater() diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index 3225515f..6a23f767 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -20,11 +20,13 @@ from . import plex_functions as PF from . import variables as v # Be careful - your using app in another Python instance! from . import app, widgets +from .library_sync.nodes import NODE_TYPES + LOG = getLogger('PLEX.entrypoint') -def guess_content_type(): +def guess_video_or_audio(): """ Returns either 'video', 'audio' or 'image', based how the user navigated to the current view. @@ -102,9 +104,9 @@ def show_main_menu(content_type=None): """ Shows the main PKC menu listing with all libraries, Channel, settings, etc. """ - content_type = content_type or guess_content_type() - LOG.debug('Do main listing for content_type: %s', content_type) - xbmcplugin.setContent(int(sys.argv[1]), 'files') + content_type = content_type or guess_video_or_audio() + LOG.debug('Do main listing for %s', content_type) + xbmcplugin.setContent(int(sys.argv[1]), v.CONTENT_TYPE_FILE) # Get nodes from the window props totalnodes = int(utils.window('Plex.nodes.total') or 0) for i in range(totalnodes): @@ -133,14 +135,6 @@ def show_main_menu(content_type=None): # Should only be called if the user selects widgets LOG.info('Detected user selecting widgets') directory_item(label, path) - if not path.startswith('library://'): - # Already using add-on paths (e.g. section not synched) - continue - # Add ANOTHER menu item that uses add-on paths instead of direct - # paths in order to let the user navigate into all submenus - addon_index = utils.window('Plex.nodes.%s.addon_index' % i) - # Append "(More...)" to the label - directory_item('%s (%s)' % (label, utils.lang(22082)), addon_index) # Playlists if content_type != 'image': path = 'plugin://%s?mode=playlists' % v.ADDON_ID @@ -169,32 +163,64 @@ def show_main_menu(content_type=None): xbmcplugin.endOfDirectory(int(sys.argv[1])) -def show_listing(xml, plex_type=None, section_id=None, synched=True, key=None, - content_type=None): +def show_section(section_index): + """ + Displays menu for an entire Plex section. We're using add-on paths instead + of Kodi video library xmls to be able to use type="filter" library xmls + and thus set the "content" + + Only used for synched Plex sections - otherwise, PMS xml for the section + is used directly + """ + LOG.debug('Do section listing for section index %s', section_index) + xbmcplugin.setContent(int(sys.argv[1]), v.CONTENT_TYPE_FILE) + # Get nodes from the window props + node = 'Plex.nodes.%s' % section_index + content = utils.window('%s.type' % node) + plex_type = v.PLEX_TYPE_MOVIE if content == v.CONTENT_TYPE_MOVIE \ + else v.PLEX_TYPE_SHOW + for node_type, _, _, _, _ in NODE_TYPES[plex_type]: + label = utils.window('%s.%s.title' % (node, node_type)) + path = utils.window('%s.%s.index' % (node, node_type)) + directory_item(label, path) + xbmcplugin.endOfDirectory(int(sys.argv[1])) + + +def show_listing(xml, plex_type=None, section_id=None, synched=True, key=None): """ Pass synched=False if the items have not been synched to the Kodi DB + + Kodi content type will be set using the very first item returned by the PMS """ - content_type = content_type or guess_content_type() - LOG.debug('show_listing: content_type %s, section_id %s, synched %s, ' - 'key %s, plex_type %s', content_type, section_id, synched, key, - plex_type) try: xml[0] except IndexError: - LOG.info('xml received from the PMS is empty: %s', xml.attrib) - xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) + LOG.info('xml received from the PMS is empty: %s, %s', + xml.tag, xml.attrib) + xbmcplugin.endOfDirectory(int(sys.argv[1])) return - if content_type == 'video': - xbmcplugin.setContent(int(sys.argv[1]), 'videos') - elif content_type == 'audio': - xbmcplugin.setContent(int(sys.argv[1]), 'artists') - elif plex_type in (v.PLEX_TYPE_PLAYLIST, v.PLEX_TYPE_CHANNEL): - xbmcplugin.setContent(int(sys.argv[1]), 'videos') - elif plex_type: - xbmcplugin.setContent(int(sys.argv[1]), - v.MEDIATYPE_FROM_PLEX_TYPE[plex_type]) + api = API(xml[0]) + # Determine content type for Kodi's Container.content + if key == '/hubs/home/continueWatching': + # Mix of movies and episodes + plex_type = v.PLEX_TYPE_VIDEO + elif key == '/hubs/home/recentlyAdded?type=2': + # "Recently Added TV", potentially a mix of Seasons and Episodes + plex_type = v.PLEX_TYPE_VIDEO + elif api.plex_type is None and api.fast_key and '?collection=' in api.fast_key: + # Collections/Kodi sets + plex_type = v.PLEX_TYPE_SET + elif api.plex_type is None and plex_type: + # e.g. browse by folder - folders will be listed first + # Retain plex_type + pass else: - xbmcplugin.setContent(int(sys.argv[1]), 'files') + plex_type = api.plex_type + content_type = v.CONTENT_FROM_PLEX_TYPE[plex_type] + LOG.debug('show_listing: section_id %s, synched %s, key %s, plex_type %s, ' + 'content type %s', + section_id, synched, key, plex_type, content_type) + xbmcplugin.setContent(int(sys.argv[1]), content_type) # Initialization widgets.PLEX_TYPE = plex_type widgets.SYNCHED = synched @@ -204,11 +230,15 @@ def show_listing(xml, plex_type=None, section_id=None, synched=True, key=None, if plex_type == v.PLEX_TYPE_SHOW and key and 'recentlyAdded' in key: widgets.APPEND_SHOW_TITLE = utils.settings('RecentTvAppendShow') == 'true' widgets.APPEND_SXXEXX = utils.settings('RecentTvAppendSeason') == 'true' - if content_type and xml[0].tag == 'Playlist': - # Certain views mix playlist types audio and video - for entry in reversed(xml): - if entry.get('playlistType') != content_type: - xml.remove(entry) + if api.tag == 'Playlist': + # Only show video playlists if navigation started for videos + # and vice-versa for audio playlists + content = guess_video_or_audio() + if content: + for entry in reversed(xml): + tmp_api = API(entry) + if tmp_api.playlist_type() != content: + xml.remove(entry) if xml.get('librarySectionID'): widgets.SECTION_ID = utils.cast(int, xml.get('librarySectionID')) elif section_id: @@ -355,15 +385,14 @@ def playlists(content_type): Lists all Plex playlists of the media type plex_playlist_type content_type: 'audio', 'video' """ - content_type = content_type or guess_content_type() - LOG.debug('Listing Plex %s playlists', content_type) + LOG.debug('Listing Plex playlists for content type %s', content_type) if not _wait_for_auth(): return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) app.init(entrypoint=True) from .playlists.pms import all_playlists xml = all_playlists() if xml is None: - return + return xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) if content_type is not None: # This will be skipped if user selects a widget # Buggy xml.remove(child) requires reversed() @@ -371,7 +400,7 @@ def playlists(content_type): api = API(entry) if not api.playlist_type() == content_type: xml.remove(entry) - show_listing(xml, content_type=content_type) + show_listing(xml) def hub(content_type): @@ -380,7 +409,7 @@ def hub(content_type): content_type: audio, video, image """ - content_type = content_type or guess_content_type() + content_type = content_type or guess_video_or_audio() LOG.debug('Showing Plex Hub entries for %s', content_type) if not _wait_for_auth(): return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) @@ -410,7 +439,7 @@ def hub(content_type): append = True if not append: xml.remove(entry) - show_listing(xml, content_type=content_type) + show_listing(xml) def watchlater(): @@ -447,7 +476,8 @@ def browse_plex(key=None, plex_type=None, section_id=None, synched=True, LOG.debug('Browsing to key %s, section %s, plex_type: %s, synched: %s, ' 'prompt "%s"', key, section_id, plex_type, synched, prompt) if not _wait_for_auth(): - return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) + xbmcplugin.endOfDirectory(int(sys.argv[1]), False) + return app.init(entrypoint=True) if prompt: prompt = utils.dialog('input', prompt) diff --git a/resources/lib/library_sync/nodes.py b/resources/lib/library_sync/nodes.py index 76b41c16..844f76cc 100644 --- a/resources/lib/library_sync/nodes.py +++ b/resources/lib/library_sync/nodes.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, division, unicode_literals import urllib +import copy from ..utils import etree from .. import variables as v, utils @@ -23,10 +24,9 @@ NODE_TYPES = { { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/onDeck', - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'movies', + v.CONTENT_TYPE_MOVIE, True), ('pkc_ondeck', utils.lang(39502), # "PKC On Deck (faster)" @@ -38,20 +38,18 @@ NODE_TYPES = { { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/recentlyAdded', - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'movies', + v.CONTENT_TYPE_MOVIE, False), ('all', '{self.name}', # We're using this section's name { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/all', - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'movies', + v.CONTENT_TYPE_MOVIE, False), ('recommended', utils.lang(30230), # "Recommended" @@ -59,30 +57,27 @@ NODE_TYPES = { 'mode': 'browseplex', 'key': ('/library/sections/{self.section_id}&%s' % urllib.urlencode({'sort': 'rating:desc'})), - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'movies', + v.CONTENT_TYPE_MOVIE, False), ('genres', utils.lang(135), # "Genres" { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/genre', - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'movies', + v.CONTENT_TYPE_MOVIE, False), ('sets', utils.lang(39501), # "Collections" { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/collection', - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'movies', + v.CONTENT_TYPE_MOVIE, False), ('random', utils.lang(30227), # "Random" @@ -90,20 +85,18 @@ NODE_TYPES = { 'mode': 'browseplex', 'key': ('/library/sections/{self.section_id}&%s' % urllib.urlencode({'sort': 'random'})), - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'movies', + v.CONTENT_TYPE_MOVIE, False), ('lastplayed', utils.lang(568), # "Last played" { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/recentlyViewed', - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'movies', + v.CONTENT_TYPE_MOVIE, False), ('browse', utils.lang(39702), # "Browse by folder" @@ -111,19 +104,20 @@ NODE_TYPES = { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/folder', 'plex_type': '{self.section_type}', - 'section_id': '{self.section_id}' + 'section_id': '{self.section_id}', + 'folder': True }, - 'movies', + v.CONTENT_TYPE_MOVIE, True), ('more', utils.lang(22082), # "More..." { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}', - 'plex_type': '{self.section_type}', - 'section_id': '{self.section_id}' + 'section_id': '{self.section_id}', + 'folder': True }, - 'movies', + v.CONTENT_TYPE_FILE, True), ), ########################################################### @@ -133,30 +127,27 @@ NODE_TYPES = { { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/onDeck', - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'episodes', + v.CONTENT_TYPE_EPISODE, True), ('recent', utils.lang(30174), # "Recently Added" { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/recentlyAdded', - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'episodes', + v.CONTENT_TYPE_EPISODE, False), ('all', '{self.name}', # We're using this section's name { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/all', - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'tvshows', + v.CONTENT_TYPE_SHOW, False), ('recommended', utils.lang(30230), # "Recommended" @@ -164,30 +155,27 @@ NODE_TYPES = { 'mode': 'browseplex', 'key': ('/library/sections/{self.section_id}&%s' % urllib.urlencode({'sort': 'rating:desc'})), - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'tvshows', + v.CONTENT_TYPE_SHOW, False), ('genres', utils.lang(135), # "Genres" { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/genre', - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'tvshows', + v.CONTENT_TYPE_SHOW, False), ('sets', utils.lang(39501), # "Collections" { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/collection', - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'tvshows', + v.CONTENT_TYPE_SHOW, True), # There are no sets/collections for shows with Kodi ('random', utils.lang(30227), # "Random" @@ -195,10 +183,9 @@ NODE_TYPES = { 'mode': 'browseplex', 'key': ('/library/sections/{self.section_id}&%s' % urllib.urlencode({'sort': 'random'})), - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'tvshows', + v.CONTENT_TYPE_SHOW, False), ('lastplayed', utils.lang(568), # "Last played" @@ -206,30 +193,29 @@ NODE_TYPES = { 'mode': 'browseplex', 'key': ('/library/sections/{self.section_id}/recentlyViewed&%s' % urllib.urlencode({'type': v.PLEX_TYPE_NUMBER_FROM_PLEX_TYPE[v.PLEX_TYPE_EPISODE]})), - 'plex_type': '{self.section_type}', 'section_id': '{self.section_id}' }, - 'episodes', + v.CONTENT_TYPE_EPISODE, False), ('browse', utils.lang(39702), # "Browse by folder" { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}/folder', - 'plex_type': '{self.section_type}', - 'section_id': '{self.section_id}' + 'section_id': '{self.section_id}', + 'folder': True }, - 'episodes', + v.CONTENT_TYPE_EPISODE, True), ('more', utils.lang(22082), # "More..." { 'mode': 'browseplex', 'key': '/library/sections/{self.section_id}', - 'plex_type': '{self.section_type}', - 'section_id': '{self.section_id}' + 'section_id': '{self.section_id}', + 'folder': True }, - 'episodes', + v.CONTENT_TYPE_FILE, True), ), } @@ -239,9 +225,19 @@ def node_pms(section, node_name, args): """ Nodes where the logic resides with the PMS - we're NOT building an xml that filters and sorts, but point to PKC add-on path + + Be sure to set args['folder'] = True if the listing is a folder and does + not contain playable elements like movies, episodes or tracks """ - xml = etree.Element('node', attrib={'order': unicode(section.order), - 'type': 'folder'}) + if 'folder' in args: + args = copy.deepcopy(args) + args.pop('folder') + folder = True + else: + folder = False + xml = etree.Element('node', + attrib={'order': unicode(section.order), + 'type': 'folder' if folder else 'filter'}) etree.SubElement(xml, 'label').text = node_name etree.SubElement(xml, 'icon').text = ICON_PATH etree.SubElement(xml, 'content').text = section.content diff --git a/resources/lib/library_sync/sections.py b/resources/lib/library_sync/sections.py index 8a2d46b4..b6999b8d 100644 --- a/resources/lib/library_sync/sections.py +++ b/resources/lib/library_sync/sections.py @@ -132,7 +132,7 @@ class Section(object): @section_type.setter def section_type(self, value): self._section_type = value - self.content = v.MEDIATYPE_FROM_PLEX_TYPE[value] + self.content = v.CONTENT_FROM_PLEX_TYPE[value] # Default values whether we sync or not based on the Plex type if value == v.PLEX_TYPE_PHOTO: self.sync_to_kodi = False @@ -239,25 +239,24 @@ class Section(object): raise RuntimeError('Index not initialized') # Main list entry for this section - which will show the different # nodes as "submenus" once the user navigates into this section - args = { - 'mode': 'browseplex', - 'key': '/library/sections/%s' % self.section_id, - 'plex_type': self.section_type, - 'section_id': unicode(self.section_id) - } - if not self.sync_to_kodi: - args['synched'] = 'false' - addon_index = self.addon_path(args) if self.sync_to_kodi and self.section_type in v.PLEX_VIDEOTYPES: - path = 'library://video/Plex-{0}/{0}_all.xml' - path = path.format(self.section_id) - index = 'library://video/Plex-%s' % self.section_id + args = { + 'mode': 'show_section', + 'section_index': self.index + } + path = utils.extend_url('plugin://%s' % v.ADDON_ID, args) else: - # No xmls to link to - let's show the listings on the fly - index = addon_index - args['key'] = '/library/sections/%s/all' % self.section_id + args = { + 'mode': 'browseplex', + 'key': '/library/sections/%s/all' % self.section_id, + 'section_id': unicode(self.section_id) + } + if not self.sync_to_kodi: + args['synched'] = 'false' + # No library xmls to speed things up + # Immediately show the PMS options for this section path = self.addon_path(args) - # .index will list all possible nodes for this library + index = path utils.window('%s.index' % self.node, value=index) utils.window('%s.title' % self.node, value=self.name) utils.window('%s.type' % self.node, value=self.content) @@ -274,8 +273,6 @@ class Section(object): utils.window('%s.path' % self.node, value='ActivateWindow(pictures,%s,return)' % path) utils.window('%s.id' % self.node, value=str(self.section_id)) - # To let the user navigate into this node when selecting widgets - utils.window('%s.addon_index' % self.node, value=addon_index) if not self.sync_to_kodi: self.remove_files_from_kodi() return @@ -312,18 +309,22 @@ class Section(object): def _build_node(self, node_type, node_name, args, content, pms_node): self.content = content node_name = node_name.format(self=self) - xml_name = '%s_%s.xml' % (self.section_id, node_type) - path = path_ops.path.join(self.path, xml_name) - if not path_ops.exists(path): - if pms_node: - # Even the xml will point back to the PKC add-on - xml = nodes.node_pms(self, node_name, args) - else: + if pms_node: + # Do NOT write a Kodi video library xml - can't use type="filter" + # to point back to plugin://plugin.video.plexkodiconnect + xml = nodes.node_pms(self, node_name, args) + args.pop('folder', None) + path = self.addon_path(args) + else: + # Write a Kodi video library xml + xml_name = '%s_%s.xml' % (self.section_id, node_type) + path = path_ops.path.join(self.path, xml_name) + if not path_ops.exists(path): # Let's use Kodi's logic to sort/filter the Kodi library xml = getattr(nodes, 'node_%s' % node_type)(self, node_name) - self._write_xml(xml, xml_name) + self._write_xml(xml, xml_name) + path = 'library://video/Plex-%s/%s' % (self.section_id, xml_name) self.order += 1 - path = 'library://video/Plex-%s/%s' % (self.section_id, xml_name) self._window_node(path, node_name, node_type, pms_node) def _write_xml(self, xml, xml_name): @@ -337,7 +338,7 @@ class Section(object): LOG.debug('Creating smart playlist for section %s: %s', self.name, self.playlist_path) xml = etree.Element('smartplaylist', - attrib={'type': v.MEDIATYPE_FROM_PLEX_TYPE[self.section_type]}) + attrib={'type': v.CONTENT_FROM_PLEX_TYPE[self.section_type]}) etree.SubElement(xml, 'name').text = self.name etree.SubElement(xml, 'match').text = 'all' rule = etree.SubElement(xml, 'rule', attrib={'field': 'tag', @@ -647,7 +648,6 @@ def _clear_window_vars(index): utils.window('%s.content' % node, clear=True) utils.window('%s.path' % node, clear=True) utils.window('%s.id' % node, clear=True) - utils.window('%s.addon_index' % node, clear=True) # Just clear everything here, ignore the plex_type for typus in (x[0] for y in nodes.NODE_TYPES.values() for x in y): for kind in WINDOW_ARGS: diff --git a/resources/lib/plex_api/base.py b/resources/lib/plex_api/base.py index 5228accb..096cd47e 100644 --- a/resources/lib/plex_api/base.py +++ b/resources/lib/plex_api/base.py @@ -71,6 +71,13 @@ class Base(object): """ return cast(int, self.xml.get('ratingKey')) + @property + def fast_key(self): + """ + Returns the 'fastKey' as unicode or None + """ + return self.xml.get('fastKey') + @property def plex_type(self): """ diff --git a/resources/lib/plex_api/file.py b/resources/lib/plex_api/file.py index 96ae64d8..e200e0d9 100644 --- a/resources/lib/plex_api/file.py +++ b/resources/lib/plex_api/file.py @@ -91,9 +91,10 @@ class File(object): key = '/library/sections/%s/%s' % (section_id, key) params = { 'mode': 'browseplex', - 'key': key, - 'plex_type': plex_type or self.plex_type + 'key': key } + if plex_type or self.plex_type: + params['plex_type'] = plex_type or self.plex_type if not synched: # No item to be found in the Kodi DB params['synched'] = 'false' diff --git a/resources/lib/variables.py b/resources/lib/variables.py index 46b808fb..2656d58f 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -165,6 +165,7 @@ PLEX_TYPE_VIDEO = 'video' PLEX_TYPE_MOVIE = 'movie' PLEX_TYPE_CLIP = 'clip' # e.g. trailers PLEX_TYPE_SET = 'collection' # sets/collections +PLEX_TYPE_GENRE = 'genre' PLEX_TYPE_MIXED = 'mixed' PLEX_TYPE_EPISODE = 'episode' @@ -201,7 +202,7 @@ KODI_PLAYLIST_TYPE_FROM_PLEX_PLAYLIST_TYPE = { KODI_TYPE_VIDEO = 'video' KODI_TYPE_MOVIE = 'movie' KODI_TYPE_SET = 'set' # for movie sets of several movies -KODI_TYPE_CLIP = 'clip' # e.g. trailers +KODI_TYPE_CLIP = 'video' # e.g. trailers KODI_TYPE_EPISODE = 'episode' KODI_TYPE_SEASON = 'season' @@ -216,6 +217,24 @@ KODI_TYPE_MUSICVIDEO = 'musicvideo' KODI_TYPE_PHOTO = 'photo' KODI_TYPE_PLAYLIST = 'playlist' +KODI_TYPE_GENRE = 'genre' + +# Kodi content types, primarily used for xbmcplugin.setContent() +CONTENT_TYPE_MOVIE = 'movies' +CONTENT_TYPE_SHOW = 'tvshows' +CONTENT_TYPE_SEASON = 'seasons' +CONTENT_TYPE_EPISODE = 'episodes' +CONTENT_TYPE_ARTIST = 'artists' +CONTENT_TYPE_ALBUM = 'albums' +CONTENT_TYPE_SONG = 'songs' +CONTENT_TYPE_CLIP = 'movies' +CONTENT_TYPE_SET = 'sets' +CONTENT_TYPE_PHOTO = 'photos' +CONTENT_TYPE_GENRE = 'genres' +CONTENT_TYPE_VIDEO = 'videos' +CONTENT_TYPE_PLAYLIST = 'playlists' +CONTENT_TYPE_FILE = 'files' + KODI_VIDEOTYPES = ( KODI_TYPE_VIDEO, @@ -306,7 +325,6 @@ PLEX_TYPE_FROM_KODI_TYPE = { KODI_TYPE_EPISODE: PLEX_TYPE_EPISODE, KODI_TYPE_SEASON: PLEX_TYPE_SEASON, KODI_TYPE_SHOW: PLEX_TYPE_SHOW, - KODI_TYPE_CLIP: PLEX_TYPE_CLIP, KODI_TYPE_ARTIST: PLEX_TYPE_ARTIST, KODI_TYPE_ALBUM: PLEX_TYPE_ALBUM, KODI_TYPE_SONG: PLEX_TYPE_SONG, @@ -418,24 +436,29 @@ PLEX_TYPE_NUMBER_FROM_PLEX_TYPE = { PLEX_TYPE_ALBUM: 9, PLEX_TYPE_SONG: 10, PLEX_TYPE_CLIP: 12, - 'playlist': 15, + PLEX_TYPE_PLAYLIST: 15, PLEX_TYPE_SET: 18 } # To be used with e.g. Kodi Widgets -MEDIATYPE_FROM_PLEX_TYPE = { - PLEX_TYPE_MOVIE: 'movies', - PLEX_TYPE_SHOW: 'tvshows', - PLEX_TYPE_SEASON: 'tvshows', - PLEX_TYPE_EPISODE: 'episodes', - PLEX_TYPE_ARTIST: 'artists', - PLEX_TYPE_ALBUM: 'albumbs', - PLEX_TYPE_SONG: 'songs', - PLEX_TYPE_CLIP: 'videos', - PLEX_TYPE_SET: 'movies', - PLEX_TYPE_PHOTO: 'photos', - 'mixed': 'tvshows', +CONTENT_FROM_PLEX_TYPE = { + PLEX_TYPE_MOVIE: CONTENT_TYPE_MOVIE, + PLEX_TYPE_SHOW: CONTENT_TYPE_SHOW, + PLEX_TYPE_SEASON: CONTENT_TYPE_SEASON, + PLEX_TYPE_EPISODE: CONTENT_TYPE_EPISODE, + PLEX_TYPE_ARTIST: CONTENT_TYPE_ARTIST, + PLEX_TYPE_ALBUM: CONTENT_TYPE_ALBUM, + PLEX_TYPE_SONG: CONTENT_TYPE_SONG, + PLEX_TYPE_CLIP: CONTENT_TYPE_CLIP, + PLEX_TYPE_SET: CONTENT_TYPE_SET, + PLEX_TYPE_PHOTO: CONTENT_TYPE_PHOTO, + PLEX_TYPE_GENRE: CONTENT_TYPE_GENRE, + PLEX_TYPE_VIDEO: CONTENT_TYPE_VIDEO, + PLEX_TYPE_PLAYLIST: CONTENT_TYPE_PLAYLIST, + PLEX_TYPE_CHANNEL: CONTENT_TYPE_FILE, + 'mixed': CONTENT_TYPE_SHOW, + None: CONTENT_TYPE_FILE } KODI_TO_PLEX_ARTWORK = { diff --git a/resources/lib/widgets.py b/resources/lib/widgets.py index 4afe1d2b..4f0eb46a 100644 --- a/resources/lib/widgets.py +++ b/resources/lib/widgets.py @@ -80,6 +80,21 @@ def generate_item(api): def _generate_folder(api): '''Generates "folder"/"directory" items that user can further navigate''' art = api.artwork() + typus = '' + if api.plex_type == v.PLEX_TYPE_GENRE: + # Unfortunately, 'genre' is not yet supported by Kodi + # typus = v.KODI_TYPE_GENRE + pass + elif api.plex_type == v.PLEX_TYPE_SHOW: + typus = v.KODI_TYPE_SHOW + elif api.plex_type == v.PLEX_TYPE_SEASON: + typus = v.KODI_TYPE_SEASON + elif api.plex_type == v.PLEX_TYPE_ARTIST: + typus = v.KODI_TYPE_ARTIST + elif api.plex_type == v.PLEX_TYPE_ALBUM: + typus = v.KODI_TYPE_ALBUM + elif api.fast_key and '?collection=' in api.fast_key: + typus = v.KODI_TYPE_SET return { 'title': api.title(), 'label': api.title(), @@ -94,7 +109,7 @@ def _generate_folder(api): 'fanart': art['fanart'] if 'fanart' in art else 'special://home/addons/%s/fanart.jpg' % v.ADDON_ID}, 'isFolder': True, - 'type': '', + 'type': typus, 'IsPlayable': 'false', }