diff --git a/addon.xml b/addon.xml index 7c699048..b7a9df67 100644 --- a/addon.xml +++ b/addon.xml @@ -1,7 +1,7 @@ diff --git a/changelog.txt b/changelog.txt index f9eb03a3..4e056649 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,9 @@ +version 1.6.5 (beta only) +- Plex Channels! +- Browse video nodes by folder/path +- Update Danish translation +- Code optimization + version 1.6.4 (beta only) - Amazon Alexa support! Be mindful to check the Alexa forum thread first; there are still many issues completely unrelated to PKC - Enable skipping for Plex Companion diff --git a/default.py b/default.py index bfc9b517..1887faea 100644 --- a/default.py +++ b/default.py @@ -36,13 +36,14 @@ from utils import window, pickl_window, reset, passwordsXML, language as lang,\ dialog from pickler import unpickle_me from PKC_listitem import convert_PKC_to_listitem +import variables as v ############################################################################### import loghandler loghandler.config() -log = logging.getLogger("PLEX.default") +log = logging.getLogger('PLEX.default') ############################################################################### @@ -50,144 +51,160 @@ HANDLE = int(argv[1]) class Main(): - # MAIN ENTRY POINT # @utils.profiling() def __init__(self): - log.debug("Full sys.argv received: %s" % argv) + log.debug('Full sys.argv received: %s' % argv) # Parse parameters params = dict(parse_qsl(argv[2][1:])) - try: - mode = params['mode'] - itemid = params.get('id', '') - except: - mode = "" - itemid = '' + mode = params.get('mode', '') + itemid = params.get('id', '') if mode == 'play': - # Put the request into the "queue" - while window('plex_play_new_item'): - sleep(50) - window('plex_play_new_item', - value='%s%s' % (mode, argv[2])) - # Wait for the result - while not pickl_window('plex_result'): - sleep(50) - result = unpickle_me() - if result is None: - log.error('Error encountered, aborting') - dialog('notification', - heading='{plex}', - message=lang(30128), - icon='{error}', - time=3000) - setResolvedUrl(HANDLE, False, ListItem()) - elif result.listitem: - listitem = convert_PKC_to_listitem(result.listitem) - setResolvedUrl(HANDLE, True, listitem) - return + self.play() - modes = { - 'reset': reset, - 'resetauth': entrypoint.resetAuth, - 'passwords': passwordsXML, - 'getsubfolders': entrypoint.GetSubFolders, - 'nextup': entrypoint.getNextUpEpisodes, - 'inprogressepisodes': entrypoint.getInProgressEpisodes, - 'recentepisodes': entrypoint.getRecentEpisodes, - 'refreshplaylist': entrypoint.refreshPlaylist, - 'switchuser': entrypoint.switchPlexUser, - 'deviceid': entrypoint.resetDeviceId, - 'browseplex': entrypoint.BrowsePlexContent, - 'ondeck': entrypoint.getOnDeck, - 'chooseServer': entrypoint.chooseServer, - 'watchlater': entrypoint.watchlater, - 'enterPMS': entrypoint.enterPMS, - 'togglePlexTV': entrypoint.togglePlexTV, - 'Plex_Node': entrypoint.Plex_Node - } + elif mode == 'ondeck': + entrypoint.getOnDeck(itemid, + params.get('type'), + params.get('tagname'), + int(params.get('limit'))) - if "/extrafanart" in argv[0]: - plexpath = argv[2][1:] - plexid = params.get('id') - entrypoint.getExtraFanArt(plexid, plexpath) - entrypoint.getVideoFiles(plexid, plexpath) - return + elif mode == 'recentepisodes': + entrypoint.getRecentEpisodes(itemid, + params.get('type'), + params.get('tagname'), + int(params.get('limit'))) - if mode == 'fanart': + elif mode == 'nextup': + entrypoint.getNextUpEpisodes(params['tagname'], + int(params['limit'])) + + elif mode == 'inprogressepisodes': + entrypoint.getInProgressEpisodes(params['tagname'], + int(params['limit'])) + + elif mode == 'Plex_Node': + entrypoint.Plex_Node(itemid, params.get('viewOffset')) + + elif mode == 'browseplex': + entrypoint.browse_plex(key=params.get('key'), + plex_section_id=params.get('id')) + + elif mode == 'getsubfolders': + entrypoint.GetSubFolders(itemid) + + elif mode == 'watchlater': + entrypoint.watchlater() + + elif mode == 'channels': + entrypoint.channels() + + elif mode == 'settings': + executebuiltin('Addon.OpenSettings(%s)' % v.ADDON_ID) + + elif mode == 'enterPMS': + entrypoint.enterPMS() + + elif mode == 'reset': + reset() + + elif mode == 'togglePlexTV': + entrypoint.togglePlexTV() + + elif mode == 'resetauth': + entrypoint.resetAuth() + + elif mode == 'passwords': + passwordsXML() + + elif mode == 'switchuser': + entrypoint.switchPlexUser() + + elif mode in ('manualsync', 'repair'): + if window('plex_online') != 'true': + # Server is not online, do not run the sync + dialog('ok', + heading=lang(29999), + message=lang(39205)) + log.error('Not connected to a PMS.') + else: + if mode == 'repair': + window('plex_runLibScan', value='repair') + log.info('Requesting repair lib sync') + elif mode == 'manualsync': + log.info('Requesting full library scan') + window('plex_runLibScan', value='full') + + elif mode == 'texturecache': + window('plex_runLibScan', value='del_textures') + + elif mode == 'chooseServer': + entrypoint.chooseServer() + + elif mode == 'refreshplaylist': + log.info('Requesting playlist/nodes refresh') + window('plex_runLibScan', value='views') + + elif mode == 'deviceid': + self.deviceid() + + elif mode == 'fanart': log.info('User requested fanarttv refresh') window('plex_runLibScan', value='fanart') + elif '/extrafanart' in argv[0]: + plexpath = argv[2][1:] + plexid = itemid + entrypoint.getExtraFanArt(plexid, plexpath) + entrypoint.getVideoFiles(plexid, plexpath) + # Called by e.g. 3rd party plugin video extras - if ("/Extras" in argv[0] or "/VideoFiles" in argv[0] or - "/Extras" in argv[2]): - plexId = params.get('id', None) + elif ('/Extras' in argv[0] or '/VideoFiles' in argv[0] or + '/Extras' in argv[2]): + plexId = itemid or None entrypoint.getVideoFiles(plexId, params) - if modes.get(mode): - # Simple functions - if mode == "play": - dbid = params.get('dbid') - # modes[mode](itemid, dbid) - modes[mode](itemid, dbid) - - elif mode in ("nextup", "inprogressepisodes"): - limit = int(params['limit']) - modes[mode](params['tagname'], limit) - - elif mode in ("channels","getsubfolders"): - modes[mode](itemid) - - elif mode == "browsecontent": - modes[mode](itemid, params.get('type'), params.get('folderid')) - - elif mode == 'browseplex': - modes[mode]( - itemid, - params.get('type'), - params.get('folderid')) - - elif mode in ('ondeck', 'recentepisodes'): - modes[mode]( - itemid, - params.get('type'), - params.get('tagname'), - int(params.get('limit'))) - - elif mode == "channelsfolder": - folderid = params['folderid'] - modes[mode](itemid, folderid) - elif mode == "companion": - modes[mode](itemid, params=argv[2]) - elif mode == 'Plex_Node': - modes[mode](params.get('id'), - params.get('viewOffset')) - else: - modes[mode]() else: - # Other functions - if mode == "settings": - executebuiltin('Addon.OpenSettings(plugin.video.plexkodiconnect)') - elif mode in ("manualsync", "repair"): - if window('plex_online') != "true": - # Server is not online, do not run the sync - dialog('ok', - heading=lang(29999), - message=lang(39205)) - log.error("Not connected to a PMS.") - else: - if mode == 'repair': - window('plex_runLibScan', value="repair") - log.info("Requesting repair lib sync") - elif mode == 'manualsync': - log.info("Requesting full library scan") - window('plex_runLibScan', value="full") - elif mode == "texturecache": - window('plex_runLibScan', value='del_textures') - else: - entrypoint.doMainListing() + entrypoint.doMainListing() -if __name__ == "__main__": - log.info('plugin.video.plexkodiconnect started') + def play(self): + # Put the request into the 'queue' + while window('plex_play_new_item'): + sleep(50) + window('plex_play_new_item', + value='%s%s' % ('play', argv[2])) + # Wait for the result + while not pickl_window('plex_result'): + sleep(50) + result = unpickle_me() + if result is None: + log.error('Error encountered, aborting') + dialog('notification', + heading='{plex}', + message=lang(30128), + icon='{error}', + time=3000) + setResolvedUrl(HANDLE, False, ListItem()) + elif result.listitem: + listitem = convert_PKC_to_listitem(result.listitem) + setResolvedUrl(HANDLE, True, listitem) + + def deviceid(self): + deviceId_old = window('plex_client_Id') + from clientinfo import getDeviceId + try: + deviceId = getDeviceId(reset=True) + except Exception as e: + log.error('Failed to generate a new device Id: %s' % e) + dialog('ok', lang(29999), lang(33032)) + else: + log.info('Successfully removed old device ID: %s New deviceId:' + '%s' % (deviceId_old, deviceId)) + # 'Kodi will now restart to apply the changes' + dialog('ok', lang(29999), lang(33033)) + executebuiltin('RestartApp') + +if __name__ == '__main__': + log.info('%s started' % v.ADDON_ID) Main() - log.info('plugin.video.plexkodiconnect stopped') + log.info('%s stopped' % v.ADDON_ID) diff --git a/resources/language/Danish/strings.xml b/resources/language/Danish/strings.xml index f03a8d29..0342a57c 100644 --- a/resources/language/Danish/strings.xml +++ b/resources/language/Danish/strings.xml @@ -515,5 +515,5 @@ Nulstil alle indstillinger for PlexKodiConnect Addon? (dette er normalt ikke anbefalet og unødvendigt!) Amazon Alexa (Voice Recognition) - Activate Alexa + Alexa aktivieren \ No newline at end of file diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml index 9eb1aff6..69313894 100644 --- a/resources/language/English/strings.xml +++ b/resources/language/English/strings.xml @@ -516,4 +516,5 @@ Amazon Alexa (Voice Recognition) Activate Alexa - \ No newline at end of file + Browse by folder + diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index 2aa33cd3..123c2283 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -106,7 +106,10 @@ def Plex_Node(url, viewOffset, playdirectly=False, node=True): log.info('Plex_Node called with url: %s, viewOffset: %s' % (url, viewOffset)) # Plex redirect, e.g. watch later. Need to get actual URLs - xml = downloadutils.DownloadUtils().downloadUrl(url) + if url.startswith('http'): + xml = downloadutils.DownloadUtils().downloadUrl(url) + else: + xml = downloadutils.DownloadUtils().downloadUrl('{server}%s' % url) try: xml[0].attrib except: @@ -194,6 +197,9 @@ def doMainListing(): # Plex Watch later addDirectoryItem(lang(39211), "plugin://plugin.video.plexkodiconnect/?mode=watchlater") + # Plex Channels + addDirectoryItem(lang(30173), + "plugin://plugin.video.plexkodiconnect/?mode=channels") # Plex user switch addDirectoryItem(lang(39200) + window('plex_username'), "plugin://plugin.video.plexkodiconnect/" @@ -211,23 +217,6 @@ def doMainListing(): xbmcplugin.endOfDirectory(HANDLE) -##### Generate a new deviceId -def resetDeviceId(): - deviceId_old = window('plex_client_Id') - from clientinfo import getDeviceId - try: - deviceId = getDeviceId(reset=True) - except Exception as e: - log.error("Failed to generate a new device Id: %s" % e) - dialog('ok', lang(29999), lang(33032)) - else: - log.info("Successfully removed old deviceId: %s New deviceId: %s" - % (deviceId_old, deviceId)) - # "Kodi will now restart to apply the changes" - dialog('ok', lang(29999), lang(33033)) - executebuiltin('RestartApp') - - def switchPlexUser(): """ Signs out currently logged in user (if applicable). Triggers sign-in of a @@ -249,12 +238,6 @@ def switchPlexUser(): __LogIn() -##### REFRESH EMBY PLAYLISTS ##### -def refreshPlaylist(): - log.info('Requesting playlist/nodes refresh') - window('plex_runLibScan', value="views") - - #### SHOW SUBFOLDERS FOR NODE ##### def GetSubFolders(nodeindex): nodetypes = ["",".recent",".recentepisodes",".inprogress",".inprogressepisodes",".unwatched",".nextepisodes",".sets",".genres",".random",".recommended"] @@ -661,83 +644,6 @@ def RunLibScan(mode): window('plex_runLibScan', value='full') -def BrowsePlexContent(viewid, mediatype="", folderid=""): - """ - Browse Plex Photos: - viewid: PMS name of the library - mediatype: mediatype, 'photos' - nodetype: e.g. 'ondeck' (TBD!!) - """ - log.debug("BrowsePlexContent called with viewid: %s, mediatype: " - "%s, folderid: %s" % (viewid, mediatype, folderid)) - - if not folderid: - # Top-level navigation, so get the content of this section - # Get all sections - xml = GetPlexSectionResults( - viewid, - containerSize=int(settings('limitindex'))) - try: - xml.attrib - except AttributeError: - log.error("Error download section %s" % viewid) - return xbmcplugin.endOfDirectory(HANDLE, False) - else: - # folderid was passed so we can directly access the folder - xml = downloadutils.DownloadUtils().downloadUrl( - "{server}%s" % folderid) - try: - xml.attrib - except AttributeError: - log.error("Error downloading %s" % folderid) - return xbmcplugin.endOfDirectory(HANDLE, False) - - # Set the folder's name - xbmcplugin.setPluginCategory(HANDLE, - xml.attrib.get('librarySectionTitle')) - - # set the correct params for the content type - if mediatype == "photos": - xbmcplugin.setContent(HANDLE, 'photos') - - # process the listing - for item in xml: - api = API(item) - if item.tag == 'Directory': - li = ListItem(item.attrib.get('title', 'Missing title')) - # for folders we add an additional browse request, passing the - # folderId - li.setProperty('IsFolder', 'true') - li.setProperty('IsPlayable', 'false') - path = "%s?id=%s&mode=browseplex&type=%s&folderid=%s" \ - % (ARGV_0, viewid, mediatype, api.getKey()) - api.set_listitem_artwork(li) - xbmcplugin.addDirectoryItem(handle=HANDLE, - url=path, - listitem=li, - isFolder=True) - else: - li = api.CreateListItemFromPlexItem() - api.set_listitem_artwork(li) - xbmcplugin.addDirectoryItem( - handle=HANDLE, - url=li.getProperty("path"), - listitem=li) - - xbmcplugin.addSortMethod(HANDLE, - xbmcplugin.SORT_METHOD_VIDEO_TITLE) - xbmcplugin.addSortMethod(HANDLE, - xbmcplugin.SORT_METHOD_DATE) - xbmcplugin.addSortMethod(HANDLE, - xbmcplugin.SORT_METHOD_VIDEO_RATING) - xbmcplugin.addSortMethod(HANDLE, - xbmcplugin.SORT_METHOD_VIDEO_RUNTIME) - - xbmcplugin.endOfDirectory( - handle=HANDLE, - cacheToDisc=settings('enableTextureCache') == 'true') - - def getOnDeck(viewid, mediatype, tagname, limit): """ Retrieves Plex On Deck items, currently only for TV shows @@ -905,28 +811,175 @@ def watchlater(): log.info('Displaying watch later plex.tv items') xbmcplugin.setContent(HANDLE, 'movies') - url = "plugin://plugin.video.plexkodiconnect/" - params = { - 'mode': "Plex_Node", - } for item in xml: - api = API(item) - listitem = api.CreateListItemFromPlexItem() - api.AddStreamInfo(listitem) - api.set_listitem_artwork(listitem) - params['id'] = item.attrib.get('key') - params['viewOffset'] = item.attrib.get('viewOffset', '0') - params['plex_type'] = item.attrib.get('type') - xbmcplugin.addDirectoryItem( - handle=HANDLE, - url="%s?%s" % (url, urlencode(params)), - listitem=listitem) + __build_item(item) xbmcplugin.endOfDirectory( handle=HANDLE, cacheToDisc=settings('enableTextureCache') == 'true') +def channels(): + """ + Listing for Plex Channels + """ + if window('plex_restricteduser') == 'true': + log.error('No Plex Channels - restricted user') + return xbmcplugin.endOfDirectory(HANDLE, False) + + xml = downloadutils.DownloadUtils().downloadUrl('{server}/channels/all') + try: + xml[0].attrib + except (ValueError, AttributeError, IndexError): + log.error('Could not download Plex Channels') + return xbmcplugin.endOfDirectory(HANDLE, False) + + log.info('Displaying Plex Channels') + xbmcplugin.setContent(HANDLE, 'files') + for method in v.SORT_METHODS_DIRECTORY: + xbmcplugin.addSortMethod(HANDLE, getattr(xbmcplugin, method)) + for item in xml: + __build_folder(item) + xbmcplugin.endOfDirectory( + handle=HANDLE, + cacheToDisc=settings('enableTextureCache') == 'true') + + +def browse_plex(key=None, plex_section_id=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 plex_section_id + """ + if key: + xml = downloadutils.DownloadUtils().downloadUrl('{server}%s' % key) + else: + xml = GetPlexSectionResults( + plex_section_id, + containerSize=int(settings('limitindex'))) + try: + xml[0].attrib + except (ValueError, AttributeError, IndexError): + log.error('Could not browse to %s' % key) + return xbmcplugin.endOfDirectory(HANDLE, False) + + photos = False + movies = False + clips = False + tvshows = False + episodes = False + songs = False + artists = False + albums = False + musicvideos = False + for item in xml: + typus = item.attrib.get('type') + if item.tag == 'Directory': + __build_folder(item, plex_section_id=plex_section_id) + else: + __build_item(item) + if typus == v.PLEX_TYPE_PHOTO: + photos = True + elif typus == v.PLEX_TYPE_MOVIE: + movies = True + elif typus == v.PLEX_TYPE_CLIP: + clips = True + elif typus in (v.PLEX_TYPE_SHOW, v.PLEX_TYPE_SEASON): + tvshows = True + elif typus == v.PLEX_TYPE_EPISODE: + episodes = True + elif typus == v.PLEX_TYPE_SONG: + songs = True + elif typus == v.PLEX_TYPE_ARTIST: + artists = True + elif typus == v.PLEX_TYPE_ALBUM: + albums = True + elif typus == v.PLEX_TYPE_MUSICVIDEO: + musicvideos = True + + # Set the correct content type + if movies is True: + xbmcplugin.setContent(HANDLE, 'movies') + sort_methods = v.SORT_METHODS_MOVIES + elif clips is True: + xbmcplugin.setContent(HANDLE, 'movies') + sort_methods = v.SORT_METHODS_CLIPS + elif photos is True: + xbmcplugin.setContent(HANDLE, 'files') + sort_methods = v.SORT_METHODS_PHOTOS + elif tvshows is True: + xbmcplugin.setContent(HANDLE, 'tvshows') + sort_methods = v.SORT_METHOD_TVSHOWS + elif episodes is True: + xbmcplugin.setContent(HANDLE, 'episodes') + sort_methods = v.SORT_METHODS_EPISODES + elif songs is True: + xbmcplugin.setContent(HANDLE, 'songs') + sort_methods = v.SORT_METHODS_SONGS + elif artists is True: + xbmcplugin.setContent(HANDLE, 'artists') + sort_methods = v.SORT_METHODS_ARTISTS + elif albums is True: + xbmcplugin.setContent(HANDLE, 'albums') + sort_methods = v.SORT_METHODS_ALBUMS + elif musicvideos is True: + xbmcplugin.setContent(HANDLE, 'musicvideos') + sort_methods = v.SORT_METHODS_MOVIES + else: + xbmcplugin.setContent(HANDLE, 'files') + sort_methods = v.SORT_METHODS_DIRECTORY + + for method in sort_methods: + xbmcplugin.addSortMethod(HANDLE, getattr(xbmcplugin, method)) + + # Set the Kodi title for this view + title = xml.attrib.get('librarySectionTitle', xml.attrib.get('title1')) + xbmcplugin.setPluginCategory(HANDLE, title) + + xbmcplugin.endOfDirectory( + handle=HANDLE, + cacheToDisc=settings('enableTextureCache') == 'true') + + +def __build_folder(xml_element, plex_section_id=None): + url = "plugin://%s/" % v.ADDON_ID + key = xml_element.attrib.get('fastKey', xml_element.attrib.get('key')) + if not key.startswith('/'): + key = '/library/sections/%s/%s' % (plex_section_id, key) + params = { + 'mode': "browseplex", + 'key': key, + 'id': plex_section_id + } + listitem = ListItem(xml_element.attrib.get('title')) + listitem.setArt({'thumb': xml_element.attrib.get('thumb'), + 'poster': xml_element.attrib.get('art')}) + xbmcplugin.addDirectoryItem(handle=HANDLE, + url="%s?%s" % (url, urlencode(params)), + isFolder=True, + listitem=listitem) + + +def __build_item(xml_element): + api = API(xml_element) + listitem = api.CreateListItemFromPlexItem() + api.AddStreamInfo(listitem) + api.set_listitem_artwork(listitem) + if api.getType() != v.PLEX_TYPE_PHOTO: + url = "plugin://%s/" % v.ADDON_ID + params = { + 'mode': "Plex_Node", + 'id': xml_element.attrib.get('key'), + 'viewOffset': xml_element.attrib.get('viewOffset', '0'), + 'plex_type': xml_element.attrib.get('type') + } + url = '%s?%s' % (url, urlencode(params)) + else: + url = listitem.getProperty('path') + xbmcplugin.addDirectoryItem(handle=HANDLE, + url=url, + listitem=listitem) + + def enterPMS(): """ Opens dialogs for the user the plug in the PMS details diff --git a/resources/lib/playqueue.py b/resources/lib/playqueue.py index 7adfca1a..551bd5f3 100644 --- a/resources/lib/playqueue.py +++ b/resources/lib/playqueue.py @@ -53,6 +53,8 @@ class Playqueue(Thread): else: # Currently, only video or audio playqueues available playqueue.kodi_pl = PlayList(PLAYLIST_VIDEO) + # Overwrite 'picture' with 'photo' + playqueue.type = v.KODI_TYPE_PHOTO self.playqueues.append(playqueue) # sort the list by their playlistid, just in case self.playqueues = sorted( diff --git a/resources/lib/playutils.py b/resources/lib/playutils.py index 7c4e1af4..410819c5 100644 --- a/resources/lib/playutils.py +++ b/resources/lib/playutils.py @@ -70,19 +70,6 @@ class PlayUtils(): log.info("The playurl is: %s" % playurl) return playurl - def httpPlay(self): - # Audio, Video, Photo - - itemid = self.item['Id'] - mediatype = self.item['MediaType'] - - if mediatype == "Audio": - playurl = "%s/emby/Audio/%s/stream" % (self.server, itemid) - else: - playurl = "%s/emby/Videos/%s/stream?static=true" % (self.server, itemid) - - return playurl - def isDirectPlay(self): """ Returns the path/playurl if we can direct play, None otherwise diff --git a/resources/lib/variables.py b/resources/lib/variables.py index 1ed3fa9c..cf05a893 100644 --- a/resources/lib/variables.py +++ b/resources/lib/variables.py @@ -113,6 +113,7 @@ PLEX_TYPE_AUDIO = 'music' PLEX_TYPE_SONG = 'track' PLEX_TYPE_ALBUM = 'album' PLEX_TYPE_ARTIST = 'artist' +PLEX_TYPE_MUSICVIDEO = 'musicvideo' PLEX_TYPE_PHOTO = 'photo' @@ -131,6 +132,7 @@ KODI_TYPE_AUDIO = 'audio' KODI_TYPE_SONG = 'song' KODI_TYPE_ALBUM = 'album' KODI_TYPE_ARTIST = 'artist' +KODI_TYPE_MUSICVIDEO = 'musicvideo' KODI_TYPE_PHOTO = 'photo' @@ -195,7 +197,8 @@ KODI_PLAYLIST_TYPE_FROM_PLEX_TYPE = { PLEX_TYPE_ARTIST: KODI_TYPE_AUDIO, PLEX_TYPE_ALBUM: KODI_TYPE_AUDIO, PLEX_TYPE_SONG: KODI_TYPE_AUDIO, - PLEX_TYPE_AUDIO: KODI_TYPE_AUDIO + PLEX_TYPE_AUDIO: KODI_TYPE_AUDIO, + PLEX_TYPE_PHOTO: KODI_TYPE_PHOTO } @@ -266,3 +269,88 @@ ALEXA_TO_COMPANION = { 'queryContainerKey': 'containerKey', 'queryToken': 'token', } + +# Kodi sort methods for xbmcplugin.addSortMethod() +SORT_METHODS_DIRECTORY = ( + 'SORT_METHOD_UNSORTED', # sorted as returned from Plex + 'SORT_METHOD_LABEL', +) + +SORT_METHODS_PHOTOS = ( + 'SORT_METHOD_UNSORTED', + 'SORT_METHOD_LABEL', + 'SORT_METHOD_DATE', + 'SORT_METHOD_DATEADDED', +) + +SORT_METHODS_CLIPS = ( + 'SORT_METHOD_UNSORTED', + 'SORT_METHOD_TITLE', + 'SORT_METHOD_DURATION', +) + +SORT_METHODS_MOVIES = ( + 'SORT_METHOD_UNSORTED', + 'SORT_METHOD_TITLE', + 'SORT_METHOD_DURATION', + 'SORT_METHOD_VIDEO_RATING', + 'SORT_METHOD_VIDEO_USER_RATING', + 'SORT_METHOD_MPAA_RATING', + 'SORT_METHOD_COUNTRY', + 'SORT_METHOD_STUDIO', + 'SORT_METHOD_GENRE', +) + +SORT_METHOD_TVSHOWS = ( + 'SORT_METHOD_UNSORTED', + 'SORT_METHOD_TITLE', + 'SORT_METHOD_VIDEO_RATING', + 'SORT_METHOD_VIDEO_USER_RATING', + 'SORT_METHOD_MPAA_RATING', + 'SORT_METHOD_COUNTRY', + 'SORT_METHOD_GENRE', +) + +SORT_METHODS_EPISODES = ( + 'SORT_METHOD_UNSORTED', + 'SORT_METHOD_TITLE', + 'SORT_METHOD_EPISODE', + 'SORT_METHOD_DURATION', + 'SORT_METHOD_VIDEO_RATING', + 'SORT_METHOD_VIDEO_USER_RATING', + 'SORT_METHOD_MPAA_RATING', + 'SORT_METHOD_FILE', + 'SORT_METHOD_FULLPATH', +) + +SORT_METHODS_SONGS = ( + 'SORT_METHOD_UNSORTED', + 'SORT_METHOD_TITLE', + 'SORT_METHOD_TRACKNUM', + 'SORT_METHOD_DURATION', + 'SORT_METHOD_ARTIST', + 'SORT_METHOD_ARTIST_AND_YEAR', + 'SORT_METHOD_ALBUM', + 'SORT_METHOD_SONG_RATING', + 'SORT_METHOD_SONG_USER_RATING' +) + +SORT_METHODS_ARTISTS = ( + 'SORT_METHOD_UNSORTED', + 'SORT_METHOD_TITLE', + 'SORT_METHOD_TRACKNUM', + 'SORT_METHOD_DURATION', + 'SORT_METHOD_ARTIST', + 'SORT_METHOD_ARTIST_AND_YEAR', + 'SORT_METHOD_ALBUM', +) + +SORT_METHODS_ALBUMS = ( + 'SORT_METHOD_UNSORTED', + 'SORT_METHOD_TITLE', + 'SORT_METHOD_TRACKNUM', + 'SORT_METHOD_DURATION', + 'SORT_METHOD_ARTIST', + 'SORT_METHOD_ARTIST_AND_YEAR', + 'SORT_METHOD_ALBUM', +) diff --git a/resources/lib/videonodes.py b/resources/lib/videonodes.py index da621151..3d16fd91 100644 --- a/resources/lib/videonodes.py +++ b/resources/lib/videonodes.py @@ -102,7 +102,7 @@ class VideoNodes(object): return if mediatype == "photos": - path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=getsubfolders" % indexnumber + path = "plugin://plugin.video.plexkodiconnect?mode=browseplex&key=/library/sections/%s&id=%s" % (viewid, viewid) window('Plex.nodes.%s.index' % indexnumber, value=path) @@ -131,58 +131,64 @@ class VideoNodes(object): '9': "genres", '10': "random", '11': "recommended", - '12': "ondeck" + '12': "ondeck", + '13': 'browsefiles' } mediatypes = { # label according to nodetype per mediatype - 'movies': + 'movies': { - '1': tagname, - '2': 30174, - # '4': 30177, - # '6': 30189, - '8': 39501, - '9': 135, - '10': 30227, - '11': 30230, - '12': 39500, + '1': tagname, + '2': 30174, + # '4': 30177, + # '6': 30189, + '8': 39501, + '9': 135, + '10': 30227, + '11': 30230, + '12': 39500, + '13': 39702 }, - 'tvshows': + 'tvshows': { - '1': tagname, - # '2': 30170, - '3': 30174, - # '4': 30171, - # '5': 30178, - # '7': 30179, - '9': 135, - '10': 30227, - # '11': 30230, - '12': 39500, - }, - - 'homevideos': - { - '1': tagname, - '2': 30251, - '11': 30253 - }, - - 'photos': - { - '1': tagname, - '2': 30252, - '8': 30255, - '11': 30254 + '1': tagname, + # '2': 30170, + '3': 30174, + # '4': 30171, + # '5': 30178, + # '7': 30179, + '9': 135, + '10': 30227, + # '11': 30230, + '12': 39500, + '13': 39702 }, - 'musicvideos': + 'homevideos': { - '1': tagname, - '2': 30256, - '4': 30257, - '6': 30258 + '1': tagname, + '2': 30251, + '11': 30253, + '13': 39702 + }, + + 'photos': + { + '1': tagname, + '2': 30252, + '8': 30255, + '11': 30254, + '13': 39702 + }, + + 'musicvideos': + { + '1': tagname, + '2': 30256, + '4': 30257, + '6': 30258, + '13': 39702 } } @@ -200,6 +206,7 @@ class VideoNodes(object): '10': '8', # "random", '11': '5', # "recommended", '12': '1', # "ondeck" + '13': '9' # browse by folder } nodes = mediatypes[mediatype] @@ -244,6 +251,8 @@ class VideoNodes(object): elif mediatype =="movies": # Reset nodetype; we got the label nodetype = 'inprogress' + elif nodetype == 'browsefiles': + path = 'plugin://plugin.video.plexkodiconnect?mode=browseplex&key=/library/sections/%s/folder' % viewid else: path = "library://video/Plex-%s/%s_%s.xml" % (dirname, viewid, nodetype) @@ -285,7 +294,7 @@ class VideoNodes(object): continue # Create the root - if (nodetype in ("nextepisodes", "ondeck", 'recentepisodes') or mediatype == "homevideos"): + if (nodetype in ("nextepisodes", "ondeck", 'recentepisodes', 'browsefiles') or mediatype == "homevideos"): # Folder type with plugin path root = self.commonRoot(order=sortorder[node], label=label, tagname=tagname, roottype=2) etree.SubElement(root, 'path').text = path