Fix PKC widgets not working at all in some cases

This commit is contained in:
croneter 2021-05-24 09:35:07 +02:00
parent 2cc60a8f70
commit 5a0de0e5f7
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.tvshows" version="3.0.0" />
<import addon="metadata.themoviedb.org.python" version="1.3.1+matrix.1" />
<import addon="script.module.arrow" version="0.15.5"/>
</requires>
<extension point="xbmc.python.pluginsource" library="default.py">
<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,
'properties': fields})
try:
return ret['result']['%sdetails' % kodi_type]
ret = ret['result']['%sdetails' % kodi_type]
except (KeyError, TypeError):
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
Several common constants for use with Kodi json api
'''
FIELDS_BASE = ['dateadded', 'file', 'lastplayed', 'plot', 'title', 'art',
'playcount']
FIELDS_FILE = FIELDS_BASE + ['streamdetails', 'director', 'resume', 'runtime']
FIELDS_MOVIES = FIELDS_FILE + ['plotoutline', 'sorttitle', 'cast', 'votes',
'showlink', 'top250', 'trailer', 'year', 'country', 'studio', 'set',
'genre', 'mpaa', 'setid', 'rating', 'tag', 'tagline', 'writer',
'originaltitle', 'imdbnumber', 'uniqueid']
FIELDS_TVSHOWS = FIELDS_BASE + ['sorttitle', 'mpaa', 'premiered', 'year',
'episode', 'watchedepisodes', 'votes', 'rating', 'studio', 'season',
'genre', 'cast', 'episodeguide', 'tag', 'originaltitle', 'imdbnumber']
FIELDS_BASE = ["dateadded", "file", "lastplayed", "plot", "title", "art", "playcount"]
FIELDS_FILE = FIELDS_BASE + ["streamdetails", "director", "resume", "runtime"]
FIELDS_MOVIES = FIELDS_FILE + ["plotoutline", "sorttitle", "cast", "votes", "showlink", "top250", "trailer", "year",
"country", "studio", "set", "genre", "mpaa", "setid", "rating", "tag", "tagline",
"writer", "originaltitle",
"imdbnumber"]
FIELDS_MOVIES.append("uniqueid")
FIELDS_TVSHOWS = FIELDS_BASE + ["sorttitle", "mpaa", "premiered", "year", "episode", "watchedepisodes", "votes",
"rating", "studio", "season", "genre", "cast", "episodeguide", "tag", "originaltitle",
"imdbnumber"]
FIELDS_SEASON = ['art', 'playcount', 'season', 'showtitle', 'episode',
'tvshowid', 'watchedepisodes', 'userrating', 'fanart', 'thumbnail']
FIELDS_EPISODES = FIELDS_FILE + ['cast', 'productioncode', 'rating', 'votes',
'episode', 'showtitle', 'tvshowid', 'season', 'firstaired', 'writer',
'originaltitle']
FIELDS_MUSICVIDEOS = FIELDS_FILE + ['genre', 'artist', 'tag', 'album', 'track',
'studio', 'year']
FIELDS_FILES = FIELDS_FILE + ['plotoutline', 'sorttitle', 'cast', 'votes',
'trailer', 'year', 'country', 'studio', 'genre', 'mpaa', 'rating',
'tagline', 'writer', 'originaltitle', 'imdbnumber', 'premiered', 'episode',
'showtitle', 'firstaired', 'watchedepisodes', 'duration', 'season']
FIELDS_SONGS = ['artist', 'displayartist', 'title', 'rating', 'fanart',
'thumbnail', 'duration', 'disc', 'playcount', 'comment', 'file', 'album',
'lastplayed', 'genre', 'musicbrainzartistid', 'track', 'dateadded']
FIELDS_ALBUMS = ['title', 'fanart', 'thumbnail', 'genre', 'displayartist',
'artist', 'musicbrainzalbumartistid', 'year', 'rating', 'artistid',
'musicbrainzalbumid', 'theme', 'description', 'type', 'style', 'playcount',
'albumlabel', 'mood', 'dateadded']
FIELDS_ARTISTS = ['born', 'formed', 'died', 'style', 'yearsactive', 'mood',
'fanart', 'thumbnail', 'musicbrainzartistid', 'disbanded', 'description',
'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']
'tvshowid', 'watchedepisodes', 'userrating', 'fanart', 'thumbnail']
FIELDS_EPISODES = FIELDS_FILE + ["cast", "productioncode", "rating", "votes", "episode", "showtitle", "tvshowid",
"season", "firstaired", "writer", "originaltitle"]
FIELDS_MUSICVIDEOS = FIELDS_FILE + ["genre", "artist", "tag", "album", "track", "studio", "year"]
FIELDS_FILES = FIELDS_FILE + ["plotoutline", "sorttitle", "cast", "votes", "trailer", "year", "country", "studio",
"genre", "mpaa", "rating", "tagline", "writer", "originaltitle", "imdbnumber",
"premiered", "episode", "showtitle",
"firstaired", "watchedepisodes", "duration", "season"]
FIELDS_SONGS = ["artist", "displayartist", "title", "rating", "fanart", "thumbnail", "duration", "disc",
"playcount", "comment", "file", "album", "lastplayed", "genre", "musicbrainzartistid", "track",
"dateadded"]
FIELDS_ALBUMS = ["title", "fanart", "thumbnail", "genre", "displayartist", "artist",
"musicbrainzalbumartistid", "year", "rating", "artistid", "musicbrainzalbumid", "theme", "description",
"type", "style", "playcount", "albumlabel", "mood", "dateadded"]
FIELDS_ARTISTS = ["born", "formed", "died", "style", "yearsactive", "mood", "fanart", "thumbnail",
"musicbrainzartistid", "disbanded", "description", "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 = {
'operator': 'lessthan',
'field': 'playcount',
'value': '1'
}
FILTER_WATCHED = {
'operator': 'isnot',
'field': 'playcount',
'value': '0'
}
FILTER_RATING = {
'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'
}
FILTER_UNWATCHED = {"operator": "lessthan", "field": "playcount", "value": "1"}
FILTER_WATCHED = {"operator": "isnot", "field": "playcount", "value": "0"}
FILTER_RATING = {"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 threading import Lock
import urllib
import arrow
# Originally tried faster cElementTree, but does NOT work reliably with Kodi
# etree parse unsafe; make sure we're always receiving unicode
from . import defused_etree as etree
@ -616,6 +617,21 @@ def indent(elem, level=0):
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):
"""
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.
"""
from logging import getLogger
import arrow
import xbmc
import xbmcgui
@ -157,7 +158,6 @@ def _generate_content(api):
'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'
@ -246,12 +246,8 @@ def _generate_content(api):
def prepare_listitem(item):
"""
helper to convert kodi output from json api to compatible format for
listitems
Code from script.module.metadatautils, kodidb.py
"""
"""helper to convert kodi output from json api to compatible format for
listitems"""
try:
# fix values returned from json to be used as listitem values
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:
item['album'] = item['label']
if "duration" not in item and "runtime" in item:
if (item["runtime"] / 60) > 300:
item["duration"] = item["runtime"] / 60
if (item["runtime"] // 60) > 300:
item["duration"] = item["runtime"] // 60
else:
item["duration"] = item["runtime"]
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:
properties["imdbnumber"] = item["imdbnumber"]
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"):
properties["imdbnumber"] = value
@ -391,6 +387,22 @@ def prepare_listitem(item):
properties["Album_Description"] = item.get('album_description')
# 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:
properties["channellogo"] = item["channellogo"]
properties["channelicon"] = item["channellogo"]
@ -441,51 +453,47 @@ def prepare_listitem(item):
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
except Exception:
utils.ERROR(notify=True)
LOG.error('item that caused crash: %s', item)
except Exception as exc:
LOG.error('item: %s', item)
LOG.exception('Exception encountered: %s', exc)
def create_listitem(item, as_tuple=True, offscreen=True,
listitem=xbmcgui.ListItem):
"""
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
"""
def create_listitem(item, as_tuple=True, offscreen=True):
"""helper to create a kodi listitem from kodi compatible dict with mediainfo"""
try:
liz = listitem(
liz = xbmcgui.ListItem(
label=item.get("label", ""),
label2=item.get("label2", ""),
path=item['file'],
offscreen=offscreen)
# only set isPlayable prop if really needed
if item.get("isFolder", False):
liz.setProperty('IsPlayable', 'false')
elif "plugin://script.skin.helper" not in item['file']:
liz.setProperty('IsPlayable', 'true')
nodetype = "Video"
if item["type"] in ["song", "album", "artist"]:
nodetype = "music"
nodetype = "Music"
elif item['type'] == 'photo':
nodetype = 'pictures'
else:
nodetype = 'video'
nodetype = 'Pictures'
# extra properties
for key, value in item["extraproperties"].items():
liz.setProperty(key, value)
if nodetype == 'video':
# video infolabels
if nodetype == "Video":
infolabels = {
"title": item.get("title"),
"path": item.get("file"),
"size": item.get("size"),
"genre": item.get("genre"),
"year": item.get("year"),
@ -516,8 +524,7 @@ def create_listitem(item, as_tuple=True, offscreen=True,
"album": item.get("album"),
"artist": item.get("artist"),
"votes": item.get("votes"),
"trailer": item.get("trailer"),
# "progress": item.get('progresspercentage')
"trailer": item.get("trailer")
}
if item["type"] == "episode":
infolabels["season"] = item["season"]
@ -534,7 +541,8 @@ def create_listitem(item, as_tuple=True, offscreen=True,
if "date" in item:
infolabels["date"] = item["date"]
elif nodetype == 'music':
# music infolabels
elif nodetype == 'Music':
infolabels = {
"title": item.get("title"),
"size": item.get("size"),
@ -560,12 +568,12 @@ def create_listitem(item, as_tuple=True, offscreen=True,
"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", ""]:
if item["type"] not in ["recording", "channel", "favourite", "genre", "categorie"]:
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:
@ -575,9 +583,11 @@ def create_listitem(item, as_tuple=True, offscreen=True,
liz.setInfo(type=nodetype, infoLabels=infolabels)
# artwork
if "icon" in item:
item['art']['icon'] = item['icon']
liz.setArt(item.get("art", {}))
if "icon" in item:
liz.setArt({"icon": item['icon']})
if "thumbnail" in item:
liz.setArt({"thumb": item['thumbnail']})
# contextmenu
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:
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"])),
(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"]))]
if "contextmenu" in item:
liz.addContextMenuItems(item["contextmenu"])
if as_tuple:
return (item["file"], liz, item.get("isFolder", False))
return item["file"], liz, item.get("isFolder", False)
else:
return liz
except Exception:
utils.ERROR(notify=True)
LOG.error('item that should have been turned into a listitem: %s', item)
except Exception as exc:
LOG.error('item: %s', item)
LOG.exception('Exception encountered: %s', exc)
def create_main_entry(item):