Ensure correct Container.Type is set for PKC widgets

This commit is contained in:
croneter 2019-07-06 21:20:23 +02:00
parent 9a670f498c
commit fc237383aa
8 changed files with 210 additions and 135 deletions

View file

@ -52,6 +52,9 @@ class Main():
synched=params.get('synched') != 'false', synched=params.get('synched') != 'false',
prompt=params.get('prompt')) prompt=params.get('prompt'))
elif mode == 'show_section':
entrypoint.show_section(params.get('section_index'))
elif mode == 'watchlater': elif mode == 'watchlater':
entrypoint.watchlater() entrypoint.watchlater()

View file

@ -20,11 +20,13 @@ from . import plex_functions as PF
from . import variables as v from . import variables as v
# Be careful - your using app in another Python instance! # Be careful - your using app in another Python instance!
from . import app, widgets from . import app, widgets
from .library_sync.nodes import NODE_TYPES
LOG = getLogger('PLEX.entrypoint') 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 Returns either 'video', 'audio' or 'image', based how the user navigated to
the current view. 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. Shows the main PKC menu listing with all libraries, Channel, settings, etc.
""" """
content_type = content_type or guess_content_type() content_type = content_type or guess_video_or_audio()
LOG.debug('Do main listing for content_type: %s', content_type) LOG.debug('Do main listing for %s', content_type)
xbmcplugin.setContent(int(sys.argv[1]), 'files') xbmcplugin.setContent(int(sys.argv[1]), v.CONTENT_TYPE_FILE)
# Get nodes from the window props # Get nodes from the window props
totalnodes = int(utils.window('Plex.nodes.total') or 0) totalnodes = int(utils.window('Plex.nodes.total') or 0)
for i in range(totalnodes): 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 # Should only be called if the user selects widgets
LOG.info('Detected user selecting widgets') LOG.info('Detected user selecting widgets')
directory_item(label, path) 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 # Playlists
if content_type != 'image': if content_type != 'image':
path = 'plugin://%s?mode=playlists' % v.ADDON_ID 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])) xbmcplugin.endOfDirectory(int(sys.argv[1]))
def show_listing(xml, plex_type=None, section_id=None, synched=True, key=None, def show_section(section_index):
content_type=None): """
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 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: try:
xml[0] xml[0]
except IndexError: except IndexError:
LOG.info('xml received from the PMS is empty: %s', xml.attrib) LOG.info('xml received from the PMS is empty: %s, %s',
xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) xml.tag, xml.attrib)
xbmcplugin.endOfDirectory(int(sys.argv[1]))
return return
if content_type == 'video': api = API(xml[0])
xbmcplugin.setContent(int(sys.argv[1]), 'videos') # Determine content type for Kodi's Container.content
elif content_type == 'audio': if key == '/hubs/home/continueWatching':
xbmcplugin.setContent(int(sys.argv[1]), 'artists') # Mix of movies and episodes
elif plex_type in (v.PLEX_TYPE_PLAYLIST, v.PLEX_TYPE_CHANNEL): plex_type = v.PLEX_TYPE_VIDEO
xbmcplugin.setContent(int(sys.argv[1]), 'videos') elif key == '/hubs/home/recentlyAdded?type=2':
elif plex_type: # "Recently Added TV", potentially a mix of Seasons and Episodes
xbmcplugin.setContent(int(sys.argv[1]), plex_type = v.PLEX_TYPE_VIDEO
v.MEDIATYPE_FROM_PLEX_TYPE[plex_type]) 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: 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 # Initialization
widgets.PLEX_TYPE = plex_type widgets.PLEX_TYPE = plex_type
widgets.SYNCHED = synched 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: if plex_type == v.PLEX_TYPE_SHOW and key and 'recentlyAdded' in key:
widgets.APPEND_SHOW_TITLE = utils.settings('RecentTvAppendShow') == 'true' widgets.APPEND_SHOW_TITLE = utils.settings('RecentTvAppendShow') == 'true'
widgets.APPEND_SXXEXX = utils.settings('RecentTvAppendSeason') == 'true' widgets.APPEND_SXXEXX = utils.settings('RecentTvAppendSeason') == 'true'
if content_type and xml[0].tag == 'Playlist': if api.tag == 'Playlist':
# Certain views mix playlist types audio and video # Only show video playlists if navigation started for videos
for entry in reversed(xml): # and vice-versa for audio playlists
if entry.get('playlistType') != content_type: content = guess_video_or_audio()
xml.remove(entry) if content:
for entry in reversed(xml):
tmp_api = API(entry)
if tmp_api.playlist_type() != content:
xml.remove(entry)
if xml.get('librarySectionID'): if xml.get('librarySectionID'):
widgets.SECTION_ID = utils.cast(int, xml.get('librarySectionID')) widgets.SECTION_ID = utils.cast(int, xml.get('librarySectionID'))
elif section_id: elif section_id:
@ -355,15 +385,14 @@ def playlists(content_type):
Lists all Plex playlists of the media type plex_playlist_type Lists all Plex playlists of the media type plex_playlist_type
content_type: 'audio', 'video' content_type: 'audio', 'video'
""" """
content_type = content_type or guess_content_type() LOG.debug('Listing Plex playlists for content type %s', content_type)
LOG.debug('Listing Plex %s playlists', content_type)
if not _wait_for_auth(): if not _wait_for_auth():
return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) return xbmcplugin.endOfDirectory(int(sys.argv[1]), False)
app.init(entrypoint=True) app.init(entrypoint=True)
from .playlists.pms import all_playlists from .playlists.pms import all_playlists
xml = all_playlists() xml = all_playlists()
if xml is None: if xml is None:
return return xbmcplugin.endOfDirectory(handle=int(sys.argv[1]))
if content_type is not None: if content_type is not None:
# This will be skipped if user selects a widget # This will be skipped if user selects a widget
# Buggy xml.remove(child) requires reversed() # Buggy xml.remove(child) requires reversed()
@ -371,7 +400,7 @@ def playlists(content_type):
api = API(entry) api = API(entry)
if not api.playlist_type() == content_type: if not api.playlist_type() == content_type:
xml.remove(entry) xml.remove(entry)
show_listing(xml, content_type=content_type) show_listing(xml)
def hub(content_type): def hub(content_type):
@ -380,7 +409,7 @@ def hub(content_type):
content_type: content_type:
audio, video, image 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) LOG.debug('Showing Plex Hub entries for %s', content_type)
if not _wait_for_auth(): if not _wait_for_auth():
return xbmcplugin.endOfDirectory(int(sys.argv[1]), False) return xbmcplugin.endOfDirectory(int(sys.argv[1]), False)
@ -410,7 +439,7 @@ def hub(content_type):
append = True append = True
if not append: if not append:
xml.remove(entry) xml.remove(entry)
show_listing(xml, content_type=content_type) show_listing(xml)
def watchlater(): 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, ' LOG.debug('Browsing to key %s, section %s, plex_type: %s, synched: %s, '
'prompt "%s"', key, section_id, plex_type, synched, prompt) 'prompt "%s"', key, section_id, plex_type, synched, prompt)
if not _wait_for_auth(): 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) app.init(entrypoint=True)
if prompt: if prompt:
prompt = utils.dialog('input', prompt) prompt = utils.dialog('input', prompt)

View file

@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, division, unicode_literals from __future__ import absolute_import, division, unicode_literals
import urllib import urllib
import copy
from ..utils import etree from ..utils import etree
from .. import variables as v, utils from .. import variables as v, utils
@ -23,10 +24,9 @@ NODE_TYPES = {
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/onDeck', 'key': '/library/sections/{self.section_id}/onDeck',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'movies', v.CONTENT_TYPE_MOVIE,
True), True),
('pkc_ondeck', ('pkc_ondeck',
utils.lang(39502), # "PKC On Deck (faster)" utils.lang(39502), # "PKC On Deck (faster)"
@ -38,20 +38,18 @@ NODE_TYPES = {
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/recentlyAdded', 'key': '/library/sections/{self.section_id}/recentlyAdded',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'movies', v.CONTENT_TYPE_MOVIE,
False), False),
('all', ('all',
'{self.name}', # We're using this section's name '{self.name}', # We're using this section's name
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/all', 'key': '/library/sections/{self.section_id}/all',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'movies', v.CONTENT_TYPE_MOVIE,
False), False),
('recommended', ('recommended',
utils.lang(30230), # "Recommended" utils.lang(30230), # "Recommended"
@ -59,30 +57,27 @@ NODE_TYPES = {
'mode': 'browseplex', 'mode': 'browseplex',
'key': ('/library/sections/{self.section_id}&%s' 'key': ('/library/sections/{self.section_id}&%s'
% urllib.urlencode({'sort': 'rating:desc'})), % urllib.urlencode({'sort': 'rating:desc'})),
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'movies', v.CONTENT_TYPE_MOVIE,
False), False),
('genres', ('genres',
utils.lang(135), # "Genres" utils.lang(135), # "Genres"
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/genre', 'key': '/library/sections/{self.section_id}/genre',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'movies', v.CONTENT_TYPE_MOVIE,
False), False),
('sets', ('sets',
utils.lang(39501), # "Collections" utils.lang(39501), # "Collections"
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/collection', 'key': '/library/sections/{self.section_id}/collection',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'movies', v.CONTENT_TYPE_MOVIE,
False), False),
('random', ('random',
utils.lang(30227), # "Random" utils.lang(30227), # "Random"
@ -90,20 +85,18 @@ NODE_TYPES = {
'mode': 'browseplex', 'mode': 'browseplex',
'key': ('/library/sections/{self.section_id}&%s' 'key': ('/library/sections/{self.section_id}&%s'
% urllib.urlencode({'sort': 'random'})), % urllib.urlencode({'sort': 'random'})),
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'movies', v.CONTENT_TYPE_MOVIE,
False), False),
('lastplayed', ('lastplayed',
utils.lang(568), # "Last played" utils.lang(568), # "Last played"
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/recentlyViewed', 'key': '/library/sections/{self.section_id}/recentlyViewed',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'movies', v.CONTENT_TYPE_MOVIE,
False), False),
('browse', ('browse',
utils.lang(39702), # "Browse by folder" utils.lang(39702), # "Browse by folder"
@ -111,19 +104,20 @@ NODE_TYPES = {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/folder', 'key': '/library/sections/{self.section_id}/folder',
'plex_type': '{self.section_type}', 'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}',
'folder': True
}, },
'movies', v.CONTENT_TYPE_MOVIE,
True), True),
('more', ('more',
utils.lang(22082), # "More..." utils.lang(22082), # "More..."
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}', '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), True),
), ),
########################################################### ###########################################################
@ -133,30 +127,27 @@ NODE_TYPES = {
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/onDeck', 'key': '/library/sections/{self.section_id}/onDeck',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'episodes', v.CONTENT_TYPE_EPISODE,
True), True),
('recent', ('recent',
utils.lang(30174), # "Recently Added" utils.lang(30174), # "Recently Added"
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/recentlyAdded', 'key': '/library/sections/{self.section_id}/recentlyAdded',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'episodes', v.CONTENT_TYPE_EPISODE,
False), False),
('all', ('all',
'{self.name}', # We're using this section's name '{self.name}', # We're using this section's name
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/all', 'key': '/library/sections/{self.section_id}/all',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'tvshows', v.CONTENT_TYPE_SHOW,
False), False),
('recommended', ('recommended',
utils.lang(30230), # "Recommended" utils.lang(30230), # "Recommended"
@ -164,30 +155,27 @@ NODE_TYPES = {
'mode': 'browseplex', 'mode': 'browseplex',
'key': ('/library/sections/{self.section_id}&%s' 'key': ('/library/sections/{self.section_id}&%s'
% urllib.urlencode({'sort': 'rating:desc'})), % urllib.urlencode({'sort': 'rating:desc'})),
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'tvshows', v.CONTENT_TYPE_SHOW,
False), False),
('genres', ('genres',
utils.lang(135), # "Genres" utils.lang(135), # "Genres"
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/genre', 'key': '/library/sections/{self.section_id}/genre',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'tvshows', v.CONTENT_TYPE_SHOW,
False), False),
('sets', ('sets',
utils.lang(39501), # "Collections" utils.lang(39501), # "Collections"
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/collection', 'key': '/library/sections/{self.section_id}/collection',
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'tvshows', v.CONTENT_TYPE_SHOW,
True), # There are no sets/collections for shows with Kodi True), # There are no sets/collections for shows with Kodi
('random', ('random',
utils.lang(30227), # "Random" utils.lang(30227), # "Random"
@ -195,10 +183,9 @@ NODE_TYPES = {
'mode': 'browseplex', 'mode': 'browseplex',
'key': ('/library/sections/{self.section_id}&%s' 'key': ('/library/sections/{self.section_id}&%s'
% urllib.urlencode({'sort': 'random'})), % urllib.urlencode({'sort': 'random'})),
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'tvshows', v.CONTENT_TYPE_SHOW,
False), False),
('lastplayed', ('lastplayed',
utils.lang(568), # "Last played" utils.lang(568), # "Last played"
@ -206,30 +193,29 @@ NODE_TYPES = {
'mode': 'browseplex', 'mode': 'browseplex',
'key': ('/library/sections/{self.section_id}/recentlyViewed&%s' 'key': ('/library/sections/{self.section_id}/recentlyViewed&%s'
% urllib.urlencode({'type': v.PLEX_TYPE_NUMBER_FROM_PLEX_TYPE[v.PLEX_TYPE_EPISODE]})), % urllib.urlencode({'type': v.PLEX_TYPE_NUMBER_FROM_PLEX_TYPE[v.PLEX_TYPE_EPISODE]})),
'plex_type': '{self.section_type}',
'section_id': '{self.section_id}' 'section_id': '{self.section_id}'
}, },
'episodes', v.CONTENT_TYPE_EPISODE,
False), False),
('browse', ('browse',
utils.lang(39702), # "Browse by folder" utils.lang(39702), # "Browse by folder"
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}/folder', '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), True),
('more', ('more',
utils.lang(22082), # "More..." utils.lang(22082), # "More..."
{ {
'mode': 'browseplex', 'mode': 'browseplex',
'key': '/library/sections/{self.section_id}', '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), 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 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 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), if 'folder' in args:
'type': 'folder'}) 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, 'label').text = node_name
etree.SubElement(xml, 'icon').text = ICON_PATH etree.SubElement(xml, 'icon').text = ICON_PATH
etree.SubElement(xml, 'content').text = section.content etree.SubElement(xml, 'content').text = section.content

View file

@ -132,7 +132,7 @@ class Section(object):
@section_type.setter @section_type.setter
def section_type(self, value): def section_type(self, value):
self._section_type = 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 # Default values whether we sync or not based on the Plex type
if value == v.PLEX_TYPE_PHOTO: if value == v.PLEX_TYPE_PHOTO:
self.sync_to_kodi = False self.sync_to_kodi = False
@ -239,25 +239,24 @@ class Section(object):
raise RuntimeError('Index not initialized') raise RuntimeError('Index not initialized')
# Main list entry for this section - which will show the different # Main list entry for this section - which will show the different
# nodes as "submenus" once the user navigates into this section # 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: if self.sync_to_kodi and self.section_type in v.PLEX_VIDEOTYPES:
path = 'library://video/Plex-{0}/{0}_all.xml' args = {
path = path.format(self.section_id) 'mode': 'show_section',
index = 'library://video/Plex-%s' % self.section_id 'section_index': self.index
}
path = utils.extend_url('plugin://%s' % v.ADDON_ID, args)
else: else:
# No xmls to link to - let's show the listings on the fly args = {
index = addon_index 'mode': 'browseplex',
args['key'] = '/library/sections/%s/all' % self.section_id '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) 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.index' % self.node, value=index)
utils.window('%s.title' % self.node, value=self.name) utils.window('%s.title' % self.node, value=self.name)
utils.window('%s.type' % self.node, value=self.content) utils.window('%s.type' % self.node, value=self.content)
@ -274,8 +273,6 @@ class Section(object):
utils.window('%s.path' % self.node, utils.window('%s.path' % self.node,
value='ActivateWindow(pictures,%s,return)' % path) value='ActivateWindow(pictures,%s,return)' % path)
utils.window('%s.id' % self.node, value=str(self.section_id)) 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: if not self.sync_to_kodi:
self.remove_files_from_kodi() self.remove_files_from_kodi()
return return
@ -312,18 +309,22 @@ class Section(object):
def _build_node(self, node_type, node_name, args, content, pms_node): def _build_node(self, node_type, node_name, args, content, pms_node):
self.content = content self.content = content
node_name = node_name.format(self=self) node_name = node_name.format(self=self)
xml_name = '%s_%s.xml' % (self.section_id, node_type) if pms_node:
path = path_ops.path.join(self.path, xml_name) # Do NOT write a Kodi video library xml - can't use type="filter"
if not path_ops.exists(path): # to point back to plugin://plugin.video.plexkodiconnect
if pms_node: xml = nodes.node_pms(self, node_name, args)
# Even the xml will point back to the PKC add-on args.pop('folder', None)
xml = nodes.node_pms(self, node_name, args) path = self.addon_path(args)
else: 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 # Let's use Kodi's logic to sort/filter the Kodi library
xml = getattr(nodes, 'node_%s' % node_type)(self, node_name) 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 self.order += 1
path = 'library://video/Plex-%s/%s' % (self.section_id, xml_name)
self._window_node(path, node_name, node_type, pms_node) self._window_node(path, node_name, node_type, pms_node)
def _write_xml(self, xml, xml_name): def _write_xml(self, xml, xml_name):
@ -337,7 +338,7 @@ class Section(object):
LOG.debug('Creating smart playlist for section %s: %s', LOG.debug('Creating smart playlist for section %s: %s',
self.name, self.playlist_path) self.name, self.playlist_path)
xml = etree.Element('smartplaylist', 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, 'name').text = self.name
etree.SubElement(xml, 'match').text = 'all' etree.SubElement(xml, 'match').text = 'all'
rule = etree.SubElement(xml, 'rule', attrib={'field': 'tag', 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.content' % node, clear=True)
utils.window('%s.path' % node, clear=True) utils.window('%s.path' % node, clear=True)
utils.window('%s.id' % 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 # 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 typus in (x[0] for y in nodes.NODE_TYPES.values() for x in y):
for kind in WINDOW_ARGS: for kind in WINDOW_ARGS:

View file

@ -71,6 +71,13 @@ class Base(object):
""" """
return cast(int, self.xml.get('ratingKey')) 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 @property
def plex_type(self): def plex_type(self):
""" """

View file

@ -91,9 +91,10 @@ class File(object):
key = '/library/sections/%s/%s' % (section_id, key) key = '/library/sections/%s/%s' % (section_id, key)
params = { params = {
'mode': 'browseplex', 'mode': 'browseplex',
'key': key, 'key': key
'plex_type': plex_type or self.plex_type
} }
if plex_type or self.plex_type:
params['plex_type'] = plex_type or self.plex_type
if not synched: if not synched:
# No item to be found in the Kodi DB # No item to be found in the Kodi DB
params['synched'] = 'false' params['synched'] = 'false'

View file

@ -165,6 +165,7 @@ PLEX_TYPE_VIDEO = 'video'
PLEX_TYPE_MOVIE = 'movie' PLEX_TYPE_MOVIE = 'movie'
PLEX_TYPE_CLIP = 'clip' # e.g. trailers PLEX_TYPE_CLIP = 'clip' # e.g. trailers
PLEX_TYPE_SET = 'collection' # sets/collections PLEX_TYPE_SET = 'collection' # sets/collections
PLEX_TYPE_GENRE = 'genre'
PLEX_TYPE_MIXED = 'mixed' PLEX_TYPE_MIXED = 'mixed'
PLEX_TYPE_EPISODE = 'episode' PLEX_TYPE_EPISODE = 'episode'
@ -201,7 +202,7 @@ KODI_PLAYLIST_TYPE_FROM_PLEX_PLAYLIST_TYPE = {
KODI_TYPE_VIDEO = 'video' KODI_TYPE_VIDEO = 'video'
KODI_TYPE_MOVIE = 'movie' KODI_TYPE_MOVIE = 'movie'
KODI_TYPE_SET = 'set' # for movie sets of several movies 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_EPISODE = 'episode'
KODI_TYPE_SEASON = 'season' KODI_TYPE_SEASON = 'season'
@ -216,6 +217,24 @@ KODI_TYPE_MUSICVIDEO = 'musicvideo'
KODI_TYPE_PHOTO = 'photo' KODI_TYPE_PHOTO = 'photo'
KODI_TYPE_PLAYLIST = 'playlist' 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_VIDEOTYPES = (
KODI_TYPE_VIDEO, KODI_TYPE_VIDEO,
@ -306,7 +325,6 @@ PLEX_TYPE_FROM_KODI_TYPE = {
KODI_TYPE_EPISODE: PLEX_TYPE_EPISODE, KODI_TYPE_EPISODE: PLEX_TYPE_EPISODE,
KODI_TYPE_SEASON: PLEX_TYPE_SEASON, KODI_TYPE_SEASON: PLEX_TYPE_SEASON,
KODI_TYPE_SHOW: PLEX_TYPE_SHOW, KODI_TYPE_SHOW: PLEX_TYPE_SHOW,
KODI_TYPE_CLIP: PLEX_TYPE_CLIP,
KODI_TYPE_ARTIST: PLEX_TYPE_ARTIST, KODI_TYPE_ARTIST: PLEX_TYPE_ARTIST,
KODI_TYPE_ALBUM: PLEX_TYPE_ALBUM, KODI_TYPE_ALBUM: PLEX_TYPE_ALBUM,
KODI_TYPE_SONG: PLEX_TYPE_SONG, KODI_TYPE_SONG: PLEX_TYPE_SONG,
@ -418,24 +436,29 @@ PLEX_TYPE_NUMBER_FROM_PLEX_TYPE = {
PLEX_TYPE_ALBUM: 9, PLEX_TYPE_ALBUM: 9,
PLEX_TYPE_SONG: 10, PLEX_TYPE_SONG: 10,
PLEX_TYPE_CLIP: 12, PLEX_TYPE_CLIP: 12,
'playlist': 15, PLEX_TYPE_PLAYLIST: 15,
PLEX_TYPE_SET: 18 PLEX_TYPE_SET: 18
} }
# To be used with e.g. Kodi Widgets # To be used with e.g. Kodi Widgets
MEDIATYPE_FROM_PLEX_TYPE = { CONTENT_FROM_PLEX_TYPE = {
PLEX_TYPE_MOVIE: 'movies', PLEX_TYPE_MOVIE: CONTENT_TYPE_MOVIE,
PLEX_TYPE_SHOW: 'tvshows', PLEX_TYPE_SHOW: CONTENT_TYPE_SHOW,
PLEX_TYPE_SEASON: 'tvshows', PLEX_TYPE_SEASON: CONTENT_TYPE_SEASON,
PLEX_TYPE_EPISODE: 'episodes', PLEX_TYPE_EPISODE: CONTENT_TYPE_EPISODE,
PLEX_TYPE_ARTIST: 'artists', PLEX_TYPE_ARTIST: CONTENT_TYPE_ARTIST,
PLEX_TYPE_ALBUM: 'albumbs', PLEX_TYPE_ALBUM: CONTENT_TYPE_ALBUM,
PLEX_TYPE_SONG: 'songs', PLEX_TYPE_SONG: CONTENT_TYPE_SONG,
PLEX_TYPE_CLIP: 'videos', PLEX_TYPE_CLIP: CONTENT_TYPE_CLIP,
PLEX_TYPE_SET: 'movies', PLEX_TYPE_SET: CONTENT_TYPE_SET,
PLEX_TYPE_PHOTO: 'photos', PLEX_TYPE_PHOTO: CONTENT_TYPE_PHOTO,
'mixed': 'tvshows', 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 = { KODI_TO_PLEX_ARTWORK = {

View file

@ -80,6 +80,21 @@ def generate_item(api):
def _generate_folder(api): def _generate_folder(api):
'''Generates "folder"/"directory" items that user can further navigate''' '''Generates "folder"/"directory" items that user can further navigate'''
art = api.artwork() 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 { return {
'title': api.title(), 'title': api.title(),
'label': api.title(), 'label': api.title(),
@ -94,7 +109,7 @@ def _generate_folder(api):
'fanart': art['fanart'] if 'fanart' in art else 'fanart': art['fanart'] if 'fanart' in art else
'special://home/addons/%s/fanart.jpg' % v.ADDON_ID}, 'special://home/addons/%s/fanart.jpg' % v.ADDON_ID},
'isFolder': True, 'isFolder': True,
'type': '', 'type': typus,
'IsPlayable': 'false', 'IsPlayable': 'false',
} }