From 037925a030726883e345658f00c7c66c1fc7bd73 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Mon, 14 Mar 2016 17:47:05 +0100 Subject: [PATCH] Prep On Deck --- default.py | 9 ++- resources/language/English/strings.xml | 4 ++ resources/language/German/strings.xml | 3 + resources/lib/PlexAPI.py | 69 ++++++++++++++++++++++- resources/lib/PlexFunctions.py | 42 ++++++++------ resources/lib/entrypoint.py | 76 +++++++++++++++++++++++++- resources/lib/playbackutils.py | 48 +--------------- resources/lib/videonodes.py | 12 +++- 8 files changed, 192 insertions(+), 71 deletions(-) diff --git a/default.py b/default.py index 2dc5ac6e..2c86d9c9 100644 --- a/default.py +++ b/default.py @@ -68,7 +68,8 @@ class Main: 'switchuser': entrypoint.switchPlexUser, 'deviceid': entrypoint.resetDeviceId, 'reConnect': entrypoint.reConnect, - 'delete': entrypoint.deleteItem + 'delete': entrypoint.deleteItem, + 'browseplex': entrypoint.BrowsePlexContent } if "/extrafanart" in sys.argv[0]: @@ -98,6 +99,12 @@ class Main: elif mode == "browsecontent": modes[mode]( itemid, params.get('type',[""])[0], params.get('folderid',[""])[0] ) + elif mode == 'browseplex': + modes[mode]( + itemid, + params.get('type', [""])[0], + params.get('folderid', [""])[0]) + elif mode == "channelsfolder": folderid = params['folderid'][0] modes[mode](itemid, folderid) diff --git a/resources/language/English/strings.xml b/resources/language/English/strings.xml index 02c28b05..8dcbb467 100644 --- a/resources/language/English/strings.xml +++ b/resources/language/English/strings.xml @@ -411,4 +411,8 @@ Full library sync finished Sync had to skip some items because they could not be processed. Kodi may be instable now!! Please post your Kodi logs to the Plex forum. + + On Deck + + diff --git a/resources/language/German/strings.xml b/resources/language/German/strings.xml index 65446c10..25ed4e97 100644 --- a/resources/language/German/strings.xml +++ b/resources/language/German/strings.xml @@ -343,4 +343,7 @@ Plex Bibliotheken aktualisiert Einige Plex Einträge mussten übersprungen werden, da sie nicht verarbeitet werden konnten. Kodi ist nun möglicherweise instabil!! Bitte teilen Sie Ihr Kodi log im Plex Forum. + + Aktuell + diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py index 11734fb2..b5d6111b 100644 --- a/resources/lib/PlexAPI.py +++ b/resources/lib/PlexAPI.py @@ -2194,8 +2194,75 @@ class API(): mapping[kodiindex] = index externalsubs.append(url) kodiindex += 1 - mapping = json.dumps(mapping) utils.window('emby_%s.indexMapping' % playurl, value=mapping) return externalsubs + + def CreateListItemFromPlexItem(self, listItem=None): + """ + Call on a child level of PMS xml response (e.g. in a for loop) + + listItem: existing xbmcgui.ListItem to work with + otherwise, a new one is created + + Returns XBMC listitem for this PMS library item + """ + people = self.getPeople() + userdata = self.getUserData() + title, sorttitle = self.getTitle() + + if listItem is None: + listItem = xbmcgui.ListItem() + + metadata = { + 'genre': self.joinList(self.getGenres()), + 'year': self.getYear(), + 'rating': self.getAudienceRating(), + 'playcount': userdata['PlayCount'], + 'cast': people['Cast'], + 'director': self.joinList(people.get('Director')), + 'plot': self.getPlot(), + 'title': title, + 'sorttitle': sorttitle, + 'duration': userdata['Runtime'], + 'studio': self.joinList(self.getStudios()), + 'tagline': self.getTagline(), + 'writer': self.joinList(people.get('Writer')), + 'premiered': self.getPremiereDate(), + 'dateadded': self.getDateCreated(), + 'lastplayed': userdata['LastPlayedDate'], + 'mpaa': self.getMpaa(), + 'aired': self.getPremiereDate() + } + + if "episode" in self.getType(): + # Only for tv shows + key, show, season, episode = self.getEpisodeDetails() + metadata['episode'] = episode + metadata['season'] = season + metadata['tvshowtitle'] = show + + listItem.setProperty('IsPlayable', 'true') + listItem.setProperty('IsFolder', 'false') + listItem.setProperty('embyid', self.getRatingKey()) + listItem.setLabel(title) + listItem.setInfo('video', infoLabels=metadata) + return listItem + + def AddStreamInfo(self, listItem): + """ + Add media stream information to xbmcgui.ListItem + """ + mediastreams = self.getMediaStreams() + videostreamFound = False + if mediastreams: + for key, value in mediastreams.iteritems(): + if key == "video" and value: + videostreamFound = True + if value: + listItem.addStreamInfo(key, value) + if not videostreamFound: + # just set empty streamdetails to prevent errors in the logs + listItem.addStreamInfo( + "video", {'duration': self.getRuntime()[1]}) diff --git a/resources/lib/PlexFunctions.py b/resources/lib/PlexFunctions.py index fb96485c..d00da0e7 100644 --- a/resources/lib/PlexFunctions.py +++ b/resources/lib/PlexFunctions.py @@ -307,7 +307,13 @@ def GetAllPlexLeaves(viewId, lastViewedAt=None, updatedAt=None, url += '?' + '&'.join(args) + '&' else: url += '?' + return DownloadChunks(url, containerSize) + +def GetPlexOnDeck(viewId, containerSize=None): + """ + """ + url = "{server}/library/sections/%s/onDeck?" % viewId return DownloadChunks(url, containerSize) @@ -329,31 +335,31 @@ def GetPlexCollections(mediatype): """ collections = [] url = "{server}/library/sections" - jsondata = downloadutils.DownloadUtils().downloadUrl(url) + xml = downloadutils.DownloadUtils().downloadUrl(url) try: - result = jsondata['_children'] - except KeyError: - pass - else: - for item in result: - contentType = item['type'] - if contentType in mediatype: - name = item['title'] - contentId = item['key'] - uuid = item['uuid'] - collections.append({ - 'name': name, - 'type': contentType, - 'id': str(contentId), - 'uuid': uuid - }) + xml.attrib + except AttributeError: + logMsg(title, 'Could not download PMS sections for %s' % url, -1) + return {} + for item in xml: + contentType = item['type'] + if contentType in mediatype: + name = item['title'] + contentId = item['key'] + uuid = item['uuid'] + collections.append({ + 'name': name, + 'type': contentType, + 'id': str(contentId), + 'uuid': uuid + }) return collections def GetPlexPlaylist(itemid, librarySectionUUID, mediatype='movie'): """ Returns raw API metadata XML dump for a playlist with e.g. trailers. - """ + """ trailerNumber = settings('trailerNumber') if not trailerNumber: trailerNumber = '3' diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index c927dd79..9682cbae 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -23,6 +23,7 @@ import playutils import playlist import PlexFunctions +import PlexAPI ############################################################################### @@ -670,13 +671,19 @@ def GetSubFolders(nodeindex): ##### BROWSE EMBY NODES DIRECTLY ##### def BrowseContent(viewname, type="", folderid=""): - + """ + Plex: + viewname: PMS name of the library + type: PMS library section ID + folderid: e.g. 'ondeck' + """ emby = embyserver.Read_EmbyServer() art = artwork.Artwork() doUtils = downloadutils.DownloadUtils() #folderid used as filter ? - if folderid in ["recent","recentepisodes","inprogress","inprogressepisodes","unwatched","nextepisodes","sets","genres","random","recommended"]: + if folderid in ["recent","recentepisodes","inprogress","inprogressepisodes","unwatched","nextepisodes","sets","genres","random","recommended", + "ondeck"]: filter = folderid folderid = "" else: @@ -685,7 +692,7 @@ def BrowseContent(viewname, type="", folderid=""): xbmcplugin.setPluginCategory(int(sys.argv[1]), viewname) #get views for root level if not folderid: - views = emby.getViews(type) + views = PlexFunctions.GetPlexCollections() for view in views: if view.get("name") == viewname.decode('utf-8'): folderid = view.get("id") @@ -1278,3 +1285,66 @@ def RunLibScan(mode): line1=string(39205)) else: utils.window('plex_runLibScan', value='full') + + +def BrowsePlexContent(viewid, mediatype="", nodetype=""): + """ + Plex: + viewid: PMS name of the library + mediatype: mediatype, e.g. 'movies', 'tvshows', 'photos' + nodetype: e.g. 'ondeck' + """ + utils.logMsg(title, "BrowsePlexContent called with viewid: %s, mediatype: %s, nodetype: %s" % (viewid, mediatype, nodetype), 1) + + if nodetype == 'ondeck': + xml = PlexFunctions.GetPlexOnDeck( + viewid, + containerSize=int(utils.settings('limitindex'))) + if not xml: + utils.logMsg(title, "Cannot get view for section %s" % viewid, -1) + return + + viewname = xml.attrib.get('librarySectionTitle') + xbmcplugin.setPluginCategory(int(sys.argv[1]), viewname) + + # set the correct params for the content type + if mediatype.lower() == "homevideos, tvshows": + xbmcplugin.setContent(int(sys.argv[1]), 'episodes') + itemtype = "Video,Folder,PhotoAlbum" + elif mediatype.lower() == "photos": + xbmcplugin.setContent(int(sys.argv[1]), 'files') + itemtype = "Photo,PhotoAlbum,Folder" + else: + itemtype = "" + + # process the listing + for item in xml: + API = PlexAPI.API(item) + li = API.CreateListItemFromPlexItem() + if item.tag == 'Directory': + # for folders we add an additional browse request, passing the + # folderId + li.setProperty('IsFolder', 'true') + li.setProperty('IsPlayable', 'false') + path = "%s?id=%s&mode=browsecontent&type=%s&folderid=%s" % (sys.argv[0].decode('utf-8'), viewname.decode('utf-8'), type.decode('utf-8'), item.get("Id").decode('utf-8')) + xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]), url=path, listitem=li, isFolder=True) + else: + # playable item, set plugin path and mediastreams + path = "%s?id=%s&mode=play" % (sys.argv[0], API.getRatingKey()) + li.setProperty("path", path) + API.AddStreamInfo(li) + pbutils.PlaybackUtils(item).setArtwork(li) + xbmcplugin.addDirectoryItem( + handle=int(sys.argv[1]), + url=li.getProperty("path"), + listitem=li) + + if filter == "recent": + xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE) + else: + xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_TITLE) + xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_DATE) + xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RATING) + xbmcplugin.addSortMethod(int(sys.argv[1]), xbmcplugin.SORT_METHOD_VIDEO_RUNTIME) + + xbmcplugin.endOfDirectory(handle=int(sys.argv[1])) diff --git a/resources/lib/playbackutils.py b/resources/lib/playbackutils.py index 5d6ec4dd..402e3b44 100644 --- a/resources/lib/playbackutils.py +++ b/resources/lib/playbackutils.py @@ -70,7 +70,7 @@ class PlaybackUtils(): window('emby_%s.playmethod' % playurl, "Transcode") listitem.setPath(playurl) self.setArtwork(listitem) - self.setListItem(listitem) + API.CreateListItemFromPlexItem(listitem) self.setProperties(playurl, listitem) return xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem) @@ -159,7 +159,7 @@ class PlaybackUtils(): self.setProperties(additionalPlayurl, additionalListItem) self.setArtwork(additionalListItem) # NEW to Plex - self.setListItem(additionalListItem) + API.CreateListItemFromPlexItem(additionalListItem) playlist.add(additionalPlayurl, additionalListItem, index=self.currentPosition) self.pl.verifyPlaylist() @@ -194,7 +194,7 @@ class PlaybackUtils(): if homeScreen and seektime and window('emby_customPlaylist') != "true": log("Play as a widget item.", 1) - self.setListItem(listitem) + API.CreateListItemFromPlexItem(listitem) xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem) elif ((introsPlaylist and window('emby_customPlaylist') == "true") or @@ -368,45 +368,3 @@ class PlaybackUtils(): listItem.setProperty(arttype, path) else: listItem.setArt({arttype: path}) - - def setListItem(self, listItem): - - API = self.API - mediaType = API.getType() - people = API.getPeople() - - userdata = API.getUserData() - title, sorttitle = API.getTitle() - - metadata = { - 'genre': API.joinList(API.getGenres()), - 'year': API.getYear(), - 'rating': API.getAudienceRating(), - 'playcount': userdata['PlayCount'], - 'cast': people['Cast'], - 'director': API.joinList(people.get('Director')), - 'plot': API.getPlot(), - 'title': title, - 'sorttitle': sorttitle, - 'duration': userdata['Runtime'], - 'studio': API.joinList(API.getStudios()), - 'tagline': API.getTagline(), - 'writer': API.joinList(people.get('Writer')), - 'premiered': API.getPremiereDate(), - 'dateadded': API.getDateCreated(), - 'lastplayed': userdata['LastPlayedDate'], - 'mpaa': API.getMpaa(), - 'aired': API.getPremiereDate() - } - - if "episode" in mediaType: - # Only for tv shows - key, show, season, episode = API.getEpisodeDetails() - metadata['episode'] = episode - metadata['season'] = season - metadata['tvshowtitle'] = show - - listItem.setProperty('IsPlayable', 'true') - listItem.setProperty('IsFolder', 'false') - listItem.setLabel(metadata['title']) - listItem.setInfo('video', infoLabels=metadata) diff --git a/resources/lib/videonodes.py b/resources/lib/videonodes.py index 6e99f3bd..460beb96 100644 --- a/resources/lib/videonodes.py +++ b/resources/lib/videonodes.py @@ -127,6 +127,7 @@ class VideoNodes(object): '9': "genres", '10': "random", '11': "recommended", + '12': "ondeck" } mediatypes = { # label according to nodetype per mediatype @@ -152,10 +153,11 @@ class VideoNodes(object): '7': 30179, '9': 135, '10': 30229, - '11': 30230 + '11': 30230, + '12': 39500 }, - 'homevideoss': + 'homevideos': { '1': tagname, '2': 30251, @@ -211,6 +213,10 @@ class VideoNodes(object): elif kodiversion == 14 and nodetype == "inprogressepisodes": # Custom query path = "plugin://plugin.video.plexkodiconnect/?id=%s&mode=inprogressepisodes&limit=50"% tagname + elif nodetype == 'ondeck': + # PLEX custom query + path = ("plugin://plugin.video.plexkodiconnect/?id=%s&mode=browseplex&type=%s&folderid=%s" + % (viewid, mediatype, nodetype)) else: path = "library://video/Plex-%s/%s_%s.xml" % (dirname, viewid, nodetype) @@ -248,7 +254,7 @@ class VideoNodes(object): continue # Create the root - if (nodetype == "nextepisodes" or mediatype == "homevideos" or + if (nodetype in ("nextepisodes", "ondeck") or mediatype == "homevideos" or (kodiversion == 14 and nodetype in ('recentepisodes', 'inprogressepisodes'))): # Folder type with plugin path root = self.commonRoot(order=node, label=label, tagname=tagname, roottype=2)