Merge pull request #1485 from croneter/py3-fix-widgets

Fix PKC widgets not working at all in some cases
This commit is contained in:
croneter 2021-05-24 09:43:16 +02:00 committed by GitHub
commit 118693c980
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 122 additions and 129 deletions

View file

@ -8,6 +8,7 @@
<import addon="plugin.video.plexkodiconnect.movies" version="3.0.0" /> <import addon="plugin.video.plexkodiconnect.movies" version="3.0.0" />
<import addon="plugin.video.plexkodiconnect.tvshows" version="3.0.0" /> <import addon="plugin.video.plexkodiconnect.tvshows" version="3.0.0" />
<import addon="metadata.themoviedb.org.python" version="1.3.1+matrix.1" /> <import addon="metadata.themoviedb.org.python" version="1.3.1+matrix.1" />
<import addon="script.module.arrow" version="0.15.5"/>
</requires> </requires>
<extension point="xbmc.python.pluginsource" library="default.py"> <extension point="xbmc.python.pluginsource" library="default.py">
<provides>video audio image</provides> <provides>video audio image</provides>

View file

@ -585,6 +585,15 @@ def item_details(kodi_id, kodi_type):
ret = JsonRPC(json).execute({'%sid' % kodi_type: kodi_id, ret = JsonRPC(json).execute({'%sid' % kodi_type: kodi_id,
'properties': fields}) 'properties': fields})
try: try:
return ret['result']['%sdetails' % kodi_type] ret = ret['result']['%sdetails' % kodi_type]
except (KeyError, TypeError): except (KeyError, TypeError):
return {} return {}
if kodi_type == v.KODI_TYPE_SHOW:
# append watched counts to tvshow details
ret["extraproperties"] = {
"totalseasons": str(ret["season"]),
"totalepisodes": str(ret["episode"]),
"watchedepisodes": str(ret["watchedepisodes"]),
"unwatchedepisodes": str(ret["episode"] - ret["watchedepisodes"])
}
return ret

View file

@ -5,88 +5,45 @@ script.module.metadatautils
kodi_constants.py kodi_constants.py
Several common constants for use with Kodi json api Several common constants for use with Kodi json api
''' '''
FIELDS_BASE = ['dateadded', 'file', 'lastplayed', 'plot', 'title', 'art', FIELDS_BASE = ["dateadded", "file", "lastplayed", "plot", "title", "art", "playcount"]
'playcount'] FIELDS_FILE = FIELDS_BASE + ["streamdetails", "director", "resume", "runtime"]
FIELDS_FILE = FIELDS_BASE + ['streamdetails', 'director', 'resume', 'runtime'] FIELDS_MOVIES = FIELDS_FILE + ["plotoutline", "sorttitle", "cast", "votes", "showlink", "top250", "trailer", "year",
FIELDS_MOVIES = FIELDS_FILE + ['plotoutline', 'sorttitle', 'cast', 'votes', "country", "studio", "set", "genre", "mpaa", "setid", "rating", "tag", "tagline",
'showlink', 'top250', 'trailer', 'year', 'country', 'studio', 'set', "writer", "originaltitle",
'genre', 'mpaa', 'setid', 'rating', 'tag', 'tagline', 'writer', "imdbnumber"]
'originaltitle', 'imdbnumber', 'uniqueid'] FIELDS_MOVIES.append("uniqueid")
FIELDS_TVSHOWS = FIELDS_BASE + ['sorttitle', 'mpaa', 'premiered', 'year', FIELDS_TVSHOWS = FIELDS_BASE + ["sorttitle", "mpaa", "premiered", "year", "episode", "watchedepisodes", "votes",
'episode', 'watchedepisodes', 'votes', 'rating', 'studio', 'season', "rating", "studio", "season", "genre", "cast", "episodeguide", "tag", "originaltitle",
'genre', 'cast', 'episodeguide', 'tag', 'originaltitle', 'imdbnumber'] "imdbnumber"]
FIELDS_SEASON = ['art', 'playcount', 'season', 'showtitle', 'episode', FIELDS_SEASON = ['art', 'playcount', 'season', 'showtitle', 'episode',
'tvshowid', 'watchedepisodes', 'userrating', 'fanart', 'thumbnail'] 'tvshowid', 'watchedepisodes', 'userrating', 'fanart', 'thumbnail']
FIELDS_EPISODES = FIELDS_FILE + ['cast', 'productioncode', 'rating', 'votes', FIELDS_EPISODES = FIELDS_FILE + ["cast", "productioncode", "rating", "votes", "episode", "showtitle", "tvshowid",
'episode', 'showtitle', 'tvshowid', 'season', 'firstaired', 'writer', "season", "firstaired", "writer", "originaltitle"]
'originaltitle'] FIELDS_MUSICVIDEOS = FIELDS_FILE + ["genre", "artist", "tag", "album", "track", "studio", "year"]
FIELDS_MUSICVIDEOS = FIELDS_FILE + ['genre', 'artist', 'tag', 'album', 'track', FIELDS_FILES = FIELDS_FILE + ["plotoutline", "sorttitle", "cast", "votes", "trailer", "year", "country", "studio",
'studio', 'year'] "genre", "mpaa", "rating", "tagline", "writer", "originaltitle", "imdbnumber",
FIELDS_FILES = FIELDS_FILE + ['plotoutline', 'sorttitle', 'cast', 'votes', "premiered", "episode", "showtitle",
'trailer', 'year', 'country', 'studio', 'genre', 'mpaa', 'rating', "firstaired", "watchedepisodes", "duration", "season"]
'tagline', 'writer', 'originaltitle', 'imdbnumber', 'premiered', 'episode', FIELDS_SONGS = ["artist", "displayartist", "title", "rating", "fanart", "thumbnail", "duration", "disc",
'showtitle', 'firstaired', 'watchedepisodes', 'duration', 'season'] "playcount", "comment", "file", "album", "lastplayed", "genre", "musicbrainzartistid", "track",
FIELDS_SONGS = ['artist', 'displayartist', 'title', 'rating', 'fanart', "dateadded"]
'thumbnail', 'duration', 'disc', 'playcount', 'comment', 'file', 'album', FIELDS_ALBUMS = ["title", "fanart", "thumbnail", "genre", "displayartist", "artist",
'lastplayed', 'genre', 'musicbrainzartistid', 'track', 'dateadded'] "musicbrainzalbumartistid", "year", "rating", "artistid", "musicbrainzalbumid", "theme", "description",
FIELDS_ALBUMS = ['title', 'fanart', 'thumbnail', 'genre', 'displayartist', "type", "style", "playcount", "albumlabel", "mood", "dateadded"]
'artist', 'musicbrainzalbumartistid', 'year', 'rating', 'artistid', FIELDS_ARTISTS = ["born", "formed", "died", "style", "yearsactive", "mood", "fanart", "thumbnail",
'musicbrainzalbumid', 'theme', 'description', 'type', 'style', 'playcount', "musicbrainzartistid", "disbanded", "description", "instrument"]
'albumlabel', 'mood', 'dateadded'] FIELDS_RECORDINGS = ["art", "channel", "directory", "endtime", "file", "genre", "icon", "playcount", "plot",
FIELDS_ARTISTS = ['born', 'formed', 'died', 'style', 'yearsactive', 'mood', "plotoutline", "resume", "runtime", "starttime", "streamurl", "title"]
'fanart', 'thumbnail', 'musicbrainzartistid', 'disbanded', 'description', FIELDS_CHANNELS = ["broadcastnow", "channeltype", "hidden", "locked", "lastplayed", "thumbnail", "channel"]
'instrument']
FIELDS_RECORDINGS = ['art', 'channel', 'directory', 'endtime', 'file', 'genre',
'icon', 'playcount', 'plot', 'plotoutline', 'resume', 'runtime',
'starttime', 'streamurl', 'title']
FIELDS_CHANNELS = ['broadcastnow', 'channeltype', 'hidden', 'locked',
'lastplayed', 'thumbnail', 'channel']
FILTER_UNWATCHED = { FILTER_UNWATCHED = {"operator": "lessthan", "field": "playcount", "value": "1"}
'operator': 'lessthan', FILTER_WATCHED = {"operator": "isnot", "field": "playcount", "value": "0"}
'field': 'playcount', FILTER_RATING = {"operator": "greaterthan", "field": "rating", "value": "7"}
'value': '1' FILTER_RATING_MUSIC = {"operator": "greaterthan", "field": "rating", "value": "3"}
} FILTER_INPROGRESS = {"operator": "true", "field": "inprogress", "value": ""}
FILTER_WATCHED = { SORT_RATING = {"method": "rating", "order": "descending"}
'operator': 'isnot', SORT_RANDOM = {"method": "random", "order": "descending"}
'field': 'playcount', SORT_TITLE = {"method": "title", "order": "ascending"}
'value': '0' SORT_DATEADDED = {"method": "dateadded", "order": "descending"}
} SORT_LASTPLAYED = {"method": "lastplayed", "order": "descending"}
FILTER_RATING = { SORT_EPISODE = {"method": "episode"}
'operator': 'greaterthan',
'field': 'rating',
'value': '7'
}
FILTER_RATING_MUSIC = {
'operator': 'greaterthan',
'field': 'rating',
'value': '3'
}
FILTER_INPROGRESS = {
'operator': 'true',
'field': 'inprogress',
'value': ''
}
SORT_RATING = {
'method': 'rating',
'order': 'descending'
}
SORT_RANDOM = {
'method': 'random',
'order': 'descending'
}
SORT_TITLE = {
'method': 'title',
'order': 'ascending'
}
SORT_DATEADDED = {
'method': 'dateadded',
'order': 'descending'
}
SORT_LASTPLAYED = {
'method': 'lastplayed',
'order': 'descending'
}
SORT_EPISODE = {
'method': 'episode'
}

View file

@ -9,6 +9,7 @@ from datetime import datetime
from unicodedata import normalize from unicodedata import normalize
from threading import Lock from threading import Lock
import urllib import urllib
import arrow
# Originally tried faster cElementTree, but does NOT work reliably with Kodi # Originally tried faster cElementTree, but does NOT work reliably with Kodi
# etree parse unsafe; make sure we're always receiving unicode # etree parse unsafe; make sure we're always receiving unicode
from . import defused_etree as etree from . import defused_etree as etree
@ -616,6 +617,21 @@ def indent(elem, level=0):
LOG.info('Indentation failed with: %s', err) LOG.info('Indentation failed with: %s', err)
def localdate_from_utc_string(timestring):
"""helper to convert internal utc time (used in pvr) to local timezone"""
utc_datetime = arrow.get(timestring)
local_datetime = utc_datetime.to('local')
return local_datetime.format("YYYY-MM-DD HH:mm:ss")
def localized_date_time(timestring):
"""returns localized version of the timestring (used in pvr)"""
date_time = arrow.get(timestring)
local_date = date_time.strftime(xbmc.getRegion("dateshort"))
local_time = date_time.strftime(xbmc.getRegion("time").replace(":%S", ""))
return local_date, local_time
class XmlKodiSetting(object): class XmlKodiSetting(object):
""" """
Used to load a Kodi XML settings file from special://profile as an etree Used to load a Kodi XML settings file from special://profile as an etree

View file

@ -7,6 +7,7 @@ Loads of different functions called in SEPARATE Python instances through
e.g. plugin://... calls. Hence be careful to only rely on window variables. e.g. plugin://... calls. Hence be careful to only rely on window variables.
""" """
from logging import getLogger from logging import getLogger
import arrow
import xbmc import xbmc
import xbmcgui import xbmcgui
@ -157,7 +158,6 @@ def _generate_content(api):
'director': api.directors(), # list of [str] 'director': api.directors(), # list of [str]
'duration': api.runtime(), 'duration': api.runtime(),
'episode': api.index(), 'episode': api.index(),
# 'file': '', # e.g. 'videodb://tvshows/titles/20'
'genre': api.genres(), 'genre': api.genres(),
# 'imdbnumber': '', # e.g.'341663' # 'imdbnumber': '', # e.g.'341663'
'label': api.title(), # e.g. '1x05. Category 55 Emergency Doomsday Crisis' 'label': api.title(), # e.g. '1x05. Category 55 Emergency Doomsday Crisis'
@ -246,12 +246,8 @@ def _generate_content(api):
def prepare_listitem(item): def prepare_listitem(item):
""" """helper to convert kodi output from json api to compatible format for
helper to convert kodi output from json api to compatible format for listitems"""
listitems
Code from script.module.metadatautils, kodidb.py
"""
try: try:
# fix values returned from json to be used as listitem values # fix values returned from json to be used as listitem values
properties = item.get("extraproperties", {}) properties = item.get("extraproperties", {})
@ -292,8 +288,8 @@ def prepare_listitem(item):
if item['type'] == "album" and 'album' not in item and 'label' in item: if item['type'] == "album" and 'album' not in item and 'label' in item:
item['album'] = item['label'] item['album'] = item['label']
if "duration" not in item and "runtime" in item: if "duration" not in item and "runtime" in item:
if (item["runtime"] / 60) > 300: if (item["runtime"] // 60) > 300:
item["duration"] = item["runtime"] / 60 item["duration"] = item["runtime"] // 60
else: else:
item["duration"] = item["runtime"] item["duration"] = item["runtime"]
if "plot" not in item and "comment" in item: if "plot" not in item and "comment" in item:
@ -307,7 +303,7 @@ def prepare_listitem(item):
if "imdbnumber" not in properties and "imdbnumber" in item: if "imdbnumber" not in properties and "imdbnumber" in item:
properties["imdbnumber"] = item["imdbnumber"] properties["imdbnumber"] = item["imdbnumber"]
if "imdbnumber" not in properties and "uniqueid" in item: if "imdbnumber" not in properties and "uniqueid" in item:
for value in list(item["uniqueid"].values()): for value in item["uniqueid"].values():
if value.startswith("tt"): if value.startswith("tt"):
properties["imdbnumber"] = value properties["imdbnumber"] = value
@ -391,6 +387,22 @@ def prepare_listitem(item):
properties["Album_Description"] = item.get('album_description') properties["Album_Description"] = item.get('album_description')
# pvr properties # pvr properties
if "starttime" in item:
# convert utc time to local time
item["starttime"] = utils.localdate_from_utc_string(item["starttime"])
item["endtime"] = utils.localdate_from_utc_string(item["endtime"])
# set localized versions of the time and date as additional props
startdate, starttime = utils.localized_date_time(item['starttime'])
enddate, endtime = utils.localized_date_time(item['endtime'])
properties["StartTime"] = starttime
properties["StartDate"] = startdate
properties["EndTime"] = endtime
properties["EndDate"] = enddate
properties["Date"] = "%s %s-%s" % (startdate, starttime, endtime)
properties["StartDateTime"] = "%s %s" % (startdate, starttime)
properties["EndDateTime"] = "%s %s" % (enddate, endtime)
# set date to startdate
item["date"] = arrow.get(item["starttime"]).format("DD.MM.YYYY")
if "channellogo" in item: if "channellogo" in item:
properties["channellogo"] = item["channellogo"] properties["channellogo"] = item["channellogo"]
properties["channelicon"] = item["channellogo"] properties["channelicon"] = item["channellogo"]
@ -441,51 +453,47 @@ def prepare_listitem(item):
item["extraproperties"] = properties item["extraproperties"] = properties
# return the result if "file" not in item or not item['file']:
LOG.warn('No filepath for item: %s', item)
item["file"] = ""
return item return item
except Exception: except Exception as exc:
utils.ERROR(notify=True) LOG.error('item: %s', item)
LOG.error('item that caused crash: %s', item) LOG.exception('Exception encountered: %s', exc)
def create_listitem(item, as_tuple=True, offscreen=True, def create_listitem(item, as_tuple=True, offscreen=True):
listitem=xbmcgui.ListItem): """helper to create a kodi listitem from kodi compatible dict with mediainfo"""
"""
helper to create a kodi listitem from kodi compatible dict with mediainfo
WARNING: paths, so item['file'] for items NOT synched to the Kodi DB
shall NOT occur in the Kodi paths table!
Kodi information screen does not work otherwise
Code from script.module.metadatautils, kodidb.py
"""
try: try:
liz = listitem( liz = xbmcgui.ListItem(
label=item.get("label", ""), label=item.get("label", ""),
label2=item.get("label2", ""), label2=item.get("label2", ""),
path=item['file'], path=item['file'],
offscreen=offscreen) offscreen=offscreen)
# only set isPlayable prop if really needed # only set isPlayable prop if really needed
if item.get("isFolder", False): if item.get("isFolder", False):
liz.setProperty('IsPlayable', 'false') liz.setProperty('IsPlayable', 'false')
elif "plugin://script.skin.helper" not in item['file']: elif "plugin://script.skin.helper" not in item['file']:
liz.setProperty('IsPlayable', 'true') liz.setProperty('IsPlayable', 'true')
nodetype = "Video"
if item["type"] in ["song", "album", "artist"]: if item["type"] in ["song", "album", "artist"]:
nodetype = "music" nodetype = "Music"
elif item['type'] == 'photo': elif item['type'] == 'photo':
nodetype = 'pictures' nodetype = 'Pictures'
else:
nodetype = 'video'
# extra properties # extra properties
for key, value in item["extraproperties"].items(): for key, value in item["extraproperties"].items():
liz.setProperty(key, value) liz.setProperty(key, value)
if nodetype == 'video': # video infolabels
if nodetype == "Video":
infolabels = { infolabels = {
"title": item.get("title"), "title": item.get("title"),
"path": item.get("file"),
"size": item.get("size"), "size": item.get("size"),
"genre": item.get("genre"), "genre": item.get("genre"),
"year": item.get("year"), "year": item.get("year"),
@ -516,8 +524,7 @@ def create_listitem(item, as_tuple=True, offscreen=True,
"album": item.get("album"), "album": item.get("album"),
"artist": item.get("artist"), "artist": item.get("artist"),
"votes": item.get("votes"), "votes": item.get("votes"),
"trailer": item.get("trailer"), "trailer": item.get("trailer")
# "progress": item.get('progresspercentage')
} }
if item["type"] == "episode": if item["type"] == "episode":
infolabels["season"] = item["season"] infolabels["season"] = item["season"]
@ -534,7 +541,8 @@ def create_listitem(item, as_tuple=True, offscreen=True,
if "date" in item: if "date" in item:
infolabels["date"] = item["date"] infolabels["date"] = item["date"]
elif nodetype == 'music': # music infolabels
elif nodetype == 'Music':
infolabels = { infolabels = {
"title": item.get("title"), "title": item.get("title"),
"size": item.get("size"), "size": item.get("size"),
@ -560,12 +568,12 @@ def create_listitem(item, as_tuple=True, offscreen=True,
"title": item.get("title"), "title": item.get("title"),
'picturepath': item['file'] 'picturepath': item['file']
} }
# setting the dbtype and dbid is supported from kodi krypton and up # 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", "genre", "categorie"]:
if item["type"] not in ["recording", "channel", "favourite", ""]:
infolabels["mediatype"] = item["type"] infolabels["mediatype"] = item["type"]
# setting the dbid on music items is not supported ? # 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"] infolabels["dbid"] = item["extraproperties"]["DBID"]
if "lastplayed" in item: if "lastplayed" in item:
@ -575,9 +583,11 @@ def create_listitem(item, as_tuple=True, offscreen=True,
liz.setInfo(type=nodetype, infoLabels=infolabels) liz.setInfo(type=nodetype, infoLabels=infolabels)
# artwork # artwork
if "icon" in item:
item['art']['icon'] = item['icon']
liz.setArt(item.get("art", {})) liz.setArt(item.get("art", {}))
if "icon" in item:
liz.setArt({"icon": item['icon']})
if "thumbnail" in item:
liz.setArt({"thumb": item['thumbnail']})
# contextmenu # contextmenu
if item["type"] in ["episode", "season"] and "season" in item and "tvshowid" in item: if item["type"] in ["episode", "season"] and "season" in item and "tvshowid" in item:
@ -585,20 +595,20 @@ def create_listitem(item, as_tuple=True, offscreen=True,
if "contextmenu" not in item: if "contextmenu" not in item:
item["contextmenu"] = [] item["contextmenu"] = []
item["contextmenu"] += [ item["contextmenu"] += [
(xbmc.getLocalizedString(20364), "ActivateWindow(Video,videodb://tvshows/titles/%s/,return)" (xbmc.getLocalizedString(20364), "ActivateWindow(Videos,videodb://tvshows/titles/%s/,return)"
% (item["tvshowid"])), % (item["tvshowid"])),
(xbmc.getLocalizedString(20373), "ActivateWindow(Video,videodb://tvshows/titles/%s/%s/,return)" (xbmc.getLocalizedString(20373), "ActivateWindow(Videos,videodb://tvshows/titles/%s/%s/,return)"
% (item["tvshowid"], item["season"]))] % (item["tvshowid"], item["season"]))]
if "contextmenu" in item: if "contextmenu" in item:
liz.addContextMenuItems(item["contextmenu"]) liz.addContextMenuItems(item["contextmenu"])
if as_tuple: if as_tuple:
return (item["file"], liz, item.get("isFolder", False)) return item["file"], liz, item.get("isFolder", False)
else: else:
return liz return liz
except Exception: except Exception as exc:
utils.ERROR(notify=True) LOG.error('item: %s', item)
LOG.error('item that should have been turned into a listitem: %s', item) LOG.exception('Exception encountered: %s', exc)
def create_main_entry(item): def create_main_entry(item):