diff --git a/addon.xml b/addon.xml index 7c699048..da8e127b 100644 --- a/addon.xml +++ b/addon.xml @@ -1,14 +1,10 @@ - + - + video audio image @@ -21,6 +17,13 @@ + all + + GNU GENERAL PUBLIC LICENSE. Version 2, June 1991 + https://forums.plex.tv + https://github.com/croneter/PlexKodiConnect + + https://github.com/croneter/PlexKodiConnect Native Integration of Plex into Kodi Native Integration of Plex into Kodi Native Integration of Plex into Kodi @@ -37,11 +40,5 @@ Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk! Tilslut Kodi til din Plex Media Server. Dette plugin forudsætter, at du administrere alle dine videoer med Plex (og ikke med Kodi). Du kan miste data som allerede er gemt i Kodi video og musik-databaser (dette plugin ændrer direkte i dem). Brug på eget ansvar! Verbind Kodi met je Plex Media Server. Deze plugin gaat ervan uit dat je al je video's met Plex (en niet met Kodi) beheerd. Je kunt gegevens reeds opgeslagen in de databases voor video en muziek van Kodi (deze plugin wijzigt deze gegevens direct) verliezen. Gebruik op eigen risico! - all - GPL v2.0 - https://forums.plex.tv - https://github.com/croneter/PlexKodiConnect - - https://github.com/croneter/PlexKodiConnect - \ No newline at end of file + 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 707329c4..0342a57c 100644 --- a/resources/language/Danish/strings.xml +++ b/resources/language/Danish/strings.xml @@ -515,6 +515,5 @@ Nulstil alle indstillinger for PlexKodiConnect Addon? (dette er normalt ikke anbefalet og unødvendigt!) Amazon Alexa (Voice Recognition) - Activate Alexa - Browse by folder + Alexa aktivieren \ No newline at end of file diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index 2aa33cd3..7cce004b 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: @@ -114,8 +117,7 @@ def Plex_Node(url, viewOffset, playdirectly=False, node=True): return if viewOffset != '0': try: - viewOffset = int(v.PLEX_TO_KODI_TIMEFACTOR * - float(viewOffset)) + viewOffset = int(v.PLEX_TO_KODI_TIMEFACTOR * float(viewOffset)) except: pass else: @@ -194,6 +196,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 +216,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 +237,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 +643,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 +810,179 @@ 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_CLIP: + params = { + 'mode': "Plex_Node", + 'id': xml_element.attrib.get('key'), + 'viewOffset': xml_element.attrib.get('viewOffset', '0'), + 'plex_type': xml_element.attrib.get('type') + } + else: + params = { + 'filename': api.getKey(), + 'id': api.getRatingKey(), + 'dbid': listitem.getProperty('dbid') or '', + 'mode': "play" + } + url = "plugin://%s?%s" % (v.ADDON_ID, urlencode(params)) + 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/playlist_func.py b/resources/lib/playlist_func.py index 5ab80b6c..61d28b7c 100644 --- a/resources/lib/playlist_func.py +++ b/resources/lib/playlist_func.py @@ -260,7 +260,6 @@ def init_Plex_playlist(playlist, plex_id=None, kodi_item=None): except KeyError: log.error('Could not init Plex playlist') return - item.ID = xml[-1].attrib['%sItemID' % playlist.kind] playlist.items.append(item) log.debug('Initialized the playlist on the Plex side: %s' % playlist) 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 diff --git a/resources/settings.xml b/resources/settings.xml index fd50830b..429c7173 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -96,7 +96,7 @@ - +