From b2139ce150c43b221cfcf95f92871007d2f47c86 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 24 Jan 2021 14:53:43 +0100 Subject: [PATCH 1/3] Optimize capitalization --- resources/lib/widgets.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/lib/widgets.py b/resources/lib/widgets.py index b9c779a2..fbc6487c 100644 --- a/resources/lib/widgets.py +++ b/resources/lib/widgets.py @@ -474,7 +474,7 @@ def create_listitem(item, as_tuple=True, offscreen=True, nodetype = "Video" if item["type"] in ["song", "album", "artist"]: - nodetype = "Music" + nodetype = "music" # extra properties for key, value in item["extraproperties"].iteritems(): @@ -532,8 +532,7 @@ def create_listitem(item, as_tuple=True, offscreen=True, if "date" in item: infolabels["date"] = item["date"] - # music infolabels - else: + elif nodetype == 'music': infolabels = { "title": item.get("title"), "size": item.get("size"), From e887e7162b3661d69c0cb02b8b9a51a5bc7ba37a Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 24 Jan 2021 15:01:55 +0100 Subject: [PATCH 2/3] Fix pictures from Plex picture libraries not working/displaying --- resources/lib/widgets.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/resources/lib/widgets.py b/resources/lib/widgets.py index fbc6487c..a4880181 100644 --- a/resources/lib/widgets.py +++ b/resources/lib/widgets.py @@ -472,16 +472,18 @@ def create_listitem(item, as_tuple=True, offscreen=True, elif "plugin://script.skin.helper" not in item['file']: liz.setProperty('IsPlayable', 'true') - nodetype = "Video" if item["type"] in ["song", "album", "artist"]: nodetype = "music" + elif item['type'] == 'photo': + nodetype = 'pictures' + else: + nodetype = 'video' # extra properties for key, value in item["extraproperties"].iteritems(): liz.setProperty(key, value) - # video infolabels - if nodetype == "Video": + if nodetype == 'video': infolabels = { "title": item.get("title"), "size": item.get("size"), @@ -552,12 +554,18 @@ def create_listitem(item, as_tuple=True, offscreen=True, if "lastplayed" in item: infolabels["lastplayed"] = item["lastplayed"] + else: + # Pictures + infolabels = { + "title": item.get("title"), + 'picturepath': item['file'] + } # setting the dbtype and dbid is supported from kodi krypton and up # PKC hack: ignore empty type if item["type"] not in ["recording", "channel", "favourite", ""]: infolabels["mediatype"] = item["type"] # setting the dbid on music items is not supported ? - if nodetype == "Video" and "DBID" in item["extraproperties"]: + if nodetype == "video" and "DBID" in item["extraproperties"]: infolabels["dbid"] = item["extraproperties"]["DBID"] if "lastplayed" in item: From b9c1aaac20f61eae93917e9f269fc4fda9378279 Mon Sep 17 00:00:00 2001 From: croneter Date: Sun, 24 Jan 2021 16:35:27 +0100 Subject: [PATCH 3/3] Add some additional exif picture metadata to listitems. But Kodi skins do not seem to be using that info, unfortunately --- resources/lib/plex_api/media.py | 19 +++++ resources/lib/widgets.py | 142 +++++++++++++++++--------------- 2 files changed, 95 insertions(+), 66 deletions(-) diff --git a/resources/lib/plex_api/media.py b/resources/lib/plex_api/media.py index e6eb7a59..7ddb14ca 100644 --- a/resources/lib/plex_api/media.py +++ b/resources/lib/plex_api/media.py @@ -62,6 +62,25 @@ class Media(object): answ['bitDepth'] = None return answ + def picture_codec(self): + """ + Returns the exif metadata of pictures. This does NOT seem to be used + reliably by Kodi skins! (e.g. not at all) + """ + return { + 'exif:CameraMake': self.xml[0].get('make'), # e.g. 'Canon' + 'exif:CameraModel': self.xml[0].get('model'), # e.g. 'Canon XYZ' + 'exif:DateTime': self.xml.get('originallyAvailableAt', '').replace('-', ':') or None, # e.g. '2017-11-05' + 'exif:Height': self.xml[0].get('height'), # e.g. '2160' + 'exif:Width': self.xml[0].get('width'), # e.g. '3240' + 'exif:Orientation': self.xml[0][self.part].get('orientation'), # e.g. '1' + 'exif:FocalLength': self.xml[0].get('focalLength'), # TO BE VALIDATED + 'exif:ExposureTime': self.xml[0].get('exposure'), # e.g. '1/1000' + 'exif:ApertureFNumber': self.xml[0].get('aperture'), # e.g. 'f/5.0' + 'exif:ISOequivalent': self.xml[0].get('iso'), # e.g. '1600' + # missing on Kodi side: lens, e.g. "EF50mm f/1.8 II" + } + def mediastreams(self): """ Returns the media streams for metadata purposes diff --git a/resources/lib/widgets.py b/resources/lib/widgets.py index a4880181..837ba719 100644 --- a/resources/lib/widgets.py +++ b/resources/lib/widgets.py @@ -136,74 +136,84 @@ def _generate_content(api): # other fields - let's use the PMS answer to be safe # See https://github.com/croneter/PlexKodiConnect/issues/1129 if not api.kodi_id or 'title' not in item: - cast = [{ - 'name': x[0], - 'thumbnail': x[1], - 'role': x[2], - 'order': x[3], - } for x in api.people()['actor']] - item = { - 'cast': cast, - 'country': api.countries(), - 'dateadded': api.date_created(), # e.g '2019-01-03 19:40:59' - 'director': api.directors(), # list of [str] - 'duration': api.runtime(), - 'episode': api.index(), - # 'file': '', # e.g. 'videodb://tvshows/titles/20' - 'genre': api.genres(), - # 'imdbnumber': '', # e.g.'341663' - 'label': api.title(), # e.g. '1x05. Category 55 Emergency Doomsday Crisis' - 'lastplayed': api.lastplayed(), # e.g. '2019-01-04 16:05:03' - 'mpaa': api.content_rating(), # e.g. 'TV-MA' - 'originaltitle': '', # e.g. 'Titans (2018)' - 'playcount': api.viewcount(), # [int] - 'plot': api.plot(), # [str] - 'plotoutline': api.tagline(), - 'premiered': api.premiere_date(), # '2018-10-12' - 'rating': api.rating(), # [float] - 'season': api.season_number(), - 'sorttitle': api.sorttitle(), # 'Titans (2018)' - 'studio': api.studios(), - 'tag': [], # List of tags this item belongs to - 'tagline': api.tagline(), - 'thumbnail': '', # e.g. 'image://https%3a%2f%2fassets.tv' - 'title': api.title(), # 'Titans (2018)' - 'type': api.kodi_type, - 'trailer': api.trailer(), - 'tvshowtitle': api.show_title(), - 'uniqueid': { - 'imdbnumber': api.guids.get('imdb') or '', - 'tvdb_id': api.guids.get('tvdb') or '', - 'tmdb_id': api.guids.get('tmdb') or '' - }, - 'votes': '0', # [str]! - 'writer': api.writers(), # list of [str] - 'year': api.year(), # [int] - } - - if plex_type in (v.PLEX_TYPE_EPISODE, v.PLEX_TYPE_SEASON, v.PLEX_TYPE_SHOW): - leaves = api.leave_count() - if leaves: - item['extraproperties'] = leaves + if api.plex_type == v.PLEX_TYPE_PHOTO: + item = { + 'title': api.title(), + 'label': api.title(), + 'type': api.kodi_type, + 'dateadded': api.date_created(), # e.g '2019-01-03 19:40:59' + 'lastplayed': api.lastplayed(), # e.g. '2019-01-04 16:05:03' + 'playcount': api.viewcount(), + } + item.update(api.picture_codec()) + else: + cast = [{ + 'name': x[0], + 'thumbnail': x[1], + 'role': x[2], + 'order': x[3], + } for x in api.people()['actor']] + item = { + 'cast': cast, + 'country': api.countries(), + 'dateadded': api.date_created(), # e.g '2019-01-03 19:40:59' + 'director': api.directors(), # list of [str] + 'duration': api.runtime(), + 'episode': api.index(), + # 'file': '', # e.g. 'videodb://tvshows/titles/20' + 'genre': api.genres(), + # 'imdbnumber': '', # e.g.'341663' + 'label': api.title(), # e.g. '1x05. Category 55 Emergency Doomsday Crisis' + 'lastplayed': api.lastplayed(), # e.g. '2019-01-04 16:05:03' + 'mpaa': api.content_rating(), # e.g. 'TV-MA' + 'originaltitle': '', # e.g. 'Titans (2018)' + 'playcount': api.viewcount(), # [int] + 'plot': api.plot(), # [str] + 'plotoutline': api.tagline(), + 'premiered': api.premiere_date(), # '2018-10-12' + 'rating': api.rating(), # [float] + 'season': api.season_number(), + 'sorttitle': api.sorttitle(), # 'Titans (2018)' + 'studio': api.studios(), + 'tag': [], # List of tags this item belongs to + 'tagline': api.tagline(), + 'thumbnail': '', # e.g. 'image://https%3a%2f%2fassets.tv' + 'title': api.title(), # 'Titans (2018)' + 'type': api.kodi_type, + 'trailer': api.trailer(), + 'tvshowtitle': api.show_title(), + 'uniqueid': { + 'imdbnumber': api.guids.get('imdb') or '', + 'tvdb_id': api.guids.get('tvdb') or '', + 'tmdb_id': api.guids.get('tmdb') or '' + }, + 'votes': '0', # [str]! + 'writer': api.writers(), # list of [str] + 'year': api.year(), # [int] + } + # Add all info for e.g. video and audio streams + item['streamdetails'] = api.mediastreams() + # Cleanup required due to the way metadatautils works + if not item['lastplayed']: + del item['lastplayed'] + for stream in item['streamdetails']['video']: + stream['height'] = utils.cast(int, stream['height']) + stream['width'] = utils.cast(int, stream['width']) + stream['aspect'] = utils.cast(float, stream['aspect']) + item['streamdetails']['subtitle'] = [{'language': x} for x in item['streamdetails']['subtitle']] + # Resume point + resume = api.resume_point() + if resume: + item['resume'] = { + 'position': resume, + 'total': api.runtime() + } + if plex_type in (v.PLEX_TYPE_EPISODE, v.PLEX_TYPE_SEASON, v.PLEX_TYPE_SHOW): + leaves = api.leave_count() + if leaves: + item['extraproperties'] = leaves # Add all the artwork we can item['art'] = api.artwork(full_artwork=True) - # Add all info for e.g. video and audio streams - item['streamdetails'] = api.mediastreams() - # Cleanup required due to the way metadatautils works - if not item['lastplayed']: - del item['lastplayed'] - for stream in item['streamdetails']['video']: - stream['height'] = utils.cast(int, stream['height']) - stream['width'] = utils.cast(int, stream['width']) - stream['aspect'] = utils.cast(float, stream['aspect']) - item['streamdetails']['subtitle'] = [{'language': x} for x in item['streamdetails']['subtitle']] - # Resume point - resume = api.resume_point() - if resume: - item['resume'] = { - 'position': resume, - 'total': api.runtime() - } item['icon'] = v.ICON_FROM_PLEXTYPE[plex_type] # Some customization