Updated kodi settings on Lenovo
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
|
||||
########################
|
||||
|
||||
from resources.lib.helper import *
|
||||
from resources.lib.json_map import *
|
||||
|
||||
########################
|
||||
|
||||
class Database(object):
|
||||
def __init__(self,dbid=None,dbtype=None,append=''):
|
||||
self.dbid = dbid
|
||||
self.append = append
|
||||
self.data = {}
|
||||
|
||||
if dbtype:
|
||||
if dbtype in ['movie', 'tvshow', 'season', 'episode', 'musicvideo']:
|
||||
library = 'Video'
|
||||
self.data['nfo'] = True
|
||||
|
||||
elif dbtype in ['set']:
|
||||
library = 'Video'
|
||||
self.data['nfo'] = False
|
||||
|
||||
else:
|
||||
library = 'Audio'
|
||||
self.data['nfo'] = False
|
||||
|
||||
self.set_details = '%sLibrary.Set%sDetails' % (library, dbtype.replace('set', 'movieset'))
|
||||
self.param = '%sid' % dbtype
|
||||
|
||||
def result(self):
|
||||
return self.data
|
||||
|
||||
def write(self,key,value):
|
||||
if not isinstance(key, list):
|
||||
key = [key]
|
||||
value = [value]
|
||||
|
||||
for k in key:
|
||||
json_call(self.set_details,
|
||||
params={'%s' % k: value[key.index(k)], self.param: int(self.dbid)},
|
||||
debug=LOG_JSON
|
||||
)
|
||||
|
||||
def movies(self):
|
||||
self._items('video', 'movie')
|
||||
|
||||
def movie(self):
|
||||
self._item('video', 'movie')
|
||||
|
||||
def sets(self):
|
||||
self._items('video', 'set')
|
||||
|
||||
def set(self):
|
||||
self._item('video', 'set')
|
||||
|
||||
def tvshows(self):
|
||||
self._items('video', 'tvshow')
|
||||
|
||||
def tvshow(self):
|
||||
self._item('video', 'tvshow')
|
||||
|
||||
if self.data['tvshow'] and 'episodes' in self.append:
|
||||
tvshowid = self.data['tvshow'][0].get('tvshowid')
|
||||
self._items('video', 'episode', {'tvshowid': int(tvshowid)})
|
||||
|
||||
def episode(self):
|
||||
self._item('video', 'episode')
|
||||
|
||||
def episodes(self):
|
||||
self._items('video', 'episode')
|
||||
|
||||
def musicvideo(self):
|
||||
self._item('video', 'musicvideo')
|
||||
|
||||
def musicvideos(self):
|
||||
self._items('video', 'musicvideo')
|
||||
|
||||
def artist(self):
|
||||
self._item('audio', 'artist')
|
||||
|
||||
def artists(self):
|
||||
self._items('audio', 'artist')
|
||||
|
||||
def album(self):
|
||||
self._item('audio', 'album')
|
||||
|
||||
def albums(self):
|
||||
self._items('audio', 'album')
|
||||
|
||||
def song(self):
|
||||
self._item('audio', 'song')
|
||||
|
||||
def songs(self):
|
||||
self._items('audio', 'song')
|
||||
|
||||
def genre(self):
|
||||
movie = []
|
||||
tvshow = []
|
||||
musicvideo = []
|
||||
music = []
|
||||
video = []
|
||||
audio = []
|
||||
|
||||
# video db
|
||||
for i in ['movie', 'tvshow', 'musicvideo']:
|
||||
genres = json_call('VideoLibrary.GetGenres',
|
||||
properties=['title'],
|
||||
params={'type': i}
|
||||
)
|
||||
genres = genres.get('result', {}).get('genres', [])
|
||||
|
||||
for genre in genres:
|
||||
eval(i).append(genre.get('label'))
|
||||
|
||||
# audio db
|
||||
genres = json_call('AudioLibrary.GetGenres',
|
||||
properties=['title']
|
||||
)
|
||||
genres = genres.get('result', {}).get('genres', [])
|
||||
|
||||
for genre in genres:
|
||||
music.append(genre.get('label'))
|
||||
|
||||
self.data['moviegenres'] = movie
|
||||
self.data['tvshowgenres'] = tvshow
|
||||
self.data['musicvideogenres'] = musicvideo
|
||||
self.data['musicgenres'] = music
|
||||
self.data['videogenres'] = list(set(movie + tvshow + musicvideo))
|
||||
self.data['audiogenres'] = list(set(music + musicvideo))
|
||||
|
||||
def tags(self):
|
||||
tags = json_call('VideoLibrary.GetTags',
|
||||
properties=['title']
|
||||
)
|
||||
self.data['tags'] = tags.get('result', {}).get('tags', [])
|
||||
|
||||
def _item(self,library,dbtype):
|
||||
item = json_call('%sLibrary.Get%sDetails' % (library, dbtype.replace('set', 'movieset')),
|
||||
properties=JSON_MAP.get('%s_properties' % dbtype),
|
||||
params={'%sid' % dbtype: int(self.dbid)}
|
||||
)
|
||||
self.data[dbtype] = [item.get('result', {}).get('%sdetails' % dbtype)]
|
||||
|
||||
def _items(self,library,dbtype,params=None,query_filter=None):
|
||||
items = json_call('%sLibrary.Get%ss' % (library, dbtype.replace('set', 'movieset')),
|
||||
properties=JSON_MAP.get('%ss_properties' % dbtype),
|
||||
params=params,
|
||||
query_filter=query_filter
|
||||
)
|
||||
self.data[dbtype] = items.get('result', {}).get('%ss' % dbtype, [])
|
||||
@@ -0,0 +1,338 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
|
||||
########################
|
||||
|
||||
from resources.lib.helper import *
|
||||
from resources.lib.json_map import *
|
||||
from resources.lib.functions import *
|
||||
from resources.lib.database import *
|
||||
from resources.lib.nfo_updater import *
|
||||
|
||||
########################
|
||||
|
||||
class EditDialog(object):
|
||||
def __init__(self,dbid,dbtype):
|
||||
winprop('SelectDialogPreselect', clear=True)
|
||||
self.dbid = dbid
|
||||
self.dbtype = dbtype
|
||||
|
||||
self.db = Database(dbid=self.dbid, dbtype=self.dbtype)
|
||||
self.nfo_support = self.db.result().get('nfo')
|
||||
self.status = None
|
||||
self.get_details()
|
||||
|
||||
def get_details(self):
|
||||
getattr(self.db, self.dbtype)()
|
||||
self.details = self.db.result().get(self.dbtype)[0]
|
||||
self.file = self.details.get('file') if self.nfo_support else False
|
||||
|
||||
def editor(self):
|
||||
self.modeselect = []
|
||||
self.keylist = []
|
||||
self.presetlist = []
|
||||
self.typelist = []
|
||||
self.optionlist = []
|
||||
self.generate_list()
|
||||
self.dialog()
|
||||
|
||||
def set(self,key,type):
|
||||
preset = self.details.get(key)
|
||||
|
||||
if isinstance(preset, list):
|
||||
preset = get_joined_items(preset)
|
||||
elif isinstance(preset, float):
|
||||
preset = str(get_rounded_value(preset))
|
||||
elif isinstance(preset, int):
|
||||
preset = str(preset)
|
||||
|
||||
self._handle_dbitem(value_type=type,
|
||||
key=key,
|
||||
preset=preset
|
||||
)
|
||||
self.get_details()
|
||||
self.quit()
|
||||
|
||||
def quit(self):
|
||||
if self.file:
|
||||
|
||||
if self.status:
|
||||
self.details['status'] = self.status
|
||||
|
||||
update_nfo(file=self.file,
|
||||
dbtype=self.dbtype,
|
||||
dbid=self.dbid,
|
||||
details=self.details
|
||||
)
|
||||
|
||||
reload_widgets()
|
||||
|
||||
def dialog(self):
|
||||
preselect = winprop('SelectDialogPreselect')
|
||||
if not preselect:
|
||||
preselect = -1
|
||||
|
||||
if self.details.get('title'):
|
||||
headline = ADDON.getLocalizedString(32040) + ' "' + self.details.get('title') + '"'
|
||||
elif self.details.get('artist'):
|
||||
headline = ADDON.getLocalizedString(32040) + ' "' + self.details.get('artist') + '"'
|
||||
else:
|
||||
headline = ADDON.getLocalizedString(32000)
|
||||
|
||||
self.editdialog = DIALOG.select(headline, self.modeselect, preselect=int(preselect), useDetails=True)
|
||||
|
||||
# Dialog closed -> write changes to nfo and exit
|
||||
if self.editdialog == -1:
|
||||
winprop('SelectDialogPreselect', clear=True)
|
||||
self.quit()
|
||||
|
||||
else:
|
||||
# Edit value based on the type
|
||||
winprop('SelectDialogPreselect', str(self.editdialog))
|
||||
|
||||
self._handle_dbitem(value_type=self.typelist[self.editdialog],
|
||||
key=self.keylist[self.editdialog],
|
||||
preset=self.presetlist[self.editdialog],
|
||||
option=self.optionlist[self.editdialog]
|
||||
)
|
||||
|
||||
# Refetch updated data and return to entry_point to populate the changes in the dialog
|
||||
self.get_details()
|
||||
self.editor()
|
||||
|
||||
def generate_list(self):
|
||||
details = self.details
|
||||
uniqueid = details.get('uniqueid', {})
|
||||
ratings = details.get('ratings')
|
||||
votes = details.get('votes', 0)
|
||||
|
||||
if not votes or votes == -1:
|
||||
votes = 0
|
||||
|
||||
# Fallback rule. Create own ratings dict if it's missing in the database.
|
||||
if not ratings:
|
||||
ratings = {'default': {'default': True,
|
||||
'rating': details.get('rating', 0.0),
|
||||
'votes': details.get('votes', 0)}
|
||||
}
|
||||
|
||||
ratings_default = None
|
||||
for item in ratings:
|
||||
if ratings[item].get('default'):
|
||||
ratings_value = str(get_rounded_value(ratings[item].get('rating', 0.0)))
|
||||
votes_value = str(ratings[item].get('votes', '0'))
|
||||
ratings_default = ratings_value + ' / ' + votes_value + ' (' + xbmc.getLocalizedString(21870) + ': ' + item + ')'
|
||||
break
|
||||
|
||||
if not ratings_default and len(ratings) > 0:
|
||||
ratings_default = ADDON.getLocalizedString(32047)
|
||||
|
||||
if self.dbtype == 'movie':
|
||||
self._create_list(xbmc.getLocalizedString(369), 'title', value=details.get('title'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(20376), 'originaltitle', value=details.get('originaltitle'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(171), 'sorttitle', value=details.get('sorttitle'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(345) + ' / ' + xbmc.getLocalizedString(172), 'premiered', value=details.get('premiered'), type='date')
|
||||
self._create_list(xbmc.getLocalizedString(515), 'genre', value=get_joined_items(details.get('genre')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(202), 'tagline', value=details.get('tagline'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(207), 'plot', value=details.get('plot'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(203), 'plotoutline', value=details.get('plotoutline'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(20457), 'set', value=details.get('set'), type='movieset')
|
||||
self._create_list(xbmc.getLocalizedString(563) + ' / ' + xbmc.getLocalizedString(205), 'ratings', value=ratings_default, type='ratings', option=ratings)
|
||||
self._create_list(ADDON.getLocalizedString(32001), 'userrating', value=str(details.get('userrating')), type='userrating')
|
||||
self._create_list(xbmc.getLocalizedString(20074), 'mpaa', value=details.get('mpaa'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(20339), 'director', value=get_joined_items(details.get('director')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(20417), 'writer', value=get_joined_items(details.get('writer')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(21875), 'country', value=get_joined_items(details.get('country')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(572), 'studio', value=get_joined_items(details.get('studio')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(20459), 'tag', value=get_joined_items(details.get('tag')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(20410), 'trailer', value=details.get('trailer'), type='string')
|
||||
self._create_list('IMDb ID', 'uniqueid', value=uniqueid.get('imdb'), type='uniqueid', option={'type': 'imdb', 'uniqueids': uniqueid})
|
||||
self._create_list('TMDb ID', 'uniqueid', value=uniqueid.get('tmdb'), type='uniqueid', option={'type': 'tmdb', 'uniqueids': uniqueid})
|
||||
self._create_list(xbmc.getLocalizedString(13409), 'top250', value=str(details.get('top250')), type='integer')
|
||||
self._create_list(xbmc.getLocalizedString(570), 'dateadded', value=details.get('dateadded'), type='datetime')
|
||||
self._create_list(xbmc.getLocalizedString(568), 'lastplayed', value=details.get('lastplayed'), type='datetime')
|
||||
self._create_list(xbmc.getLocalizedString(567), 'playcount', value=str(details.get('playcount', 0)), type='integer')
|
||||
|
||||
elif self.dbtype == 'tvshow':
|
||||
if KODI_VERSION < 19:
|
||||
status = ADDON.getLocalizedString(32022)
|
||||
else:
|
||||
status = details.get('status', '')
|
||||
|
||||
self._create_list(xbmc.getLocalizedString(369), 'title', value=details.get('title'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(20376),'originaltitle', value=details.get('originaltitle'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(171), 'sorttitle', value=details.get('sorttitle'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(345) + ' / ' + xbmc.getLocalizedString(172), 'premiered', value=details.get('premiered'), type='date')
|
||||
self._create_list(xbmc.getLocalizedString(515), 'genre', value=get_joined_items(details.get('genre')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(207), 'plot', value=details.get('plot'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(563) + ' / ' + xbmc.getLocalizedString(205), 'ratings', value=ratings_default, type='ratings', option=ratings)
|
||||
self._create_list(ADDON.getLocalizedString(32001), 'userrating', value=str(details.get('userrating')), type='userrating')
|
||||
self._create_list(xbmc.getLocalizedString(20074), 'mpaa', value=details.get('mpaa'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(572), 'studio', value=get_joined_items(details.get('studio')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(20459), 'tag', value=get_joined_items(details.get('tag')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(126), 'status', value=status, type='status')
|
||||
self._create_list('IMDb ID', 'uniqueid', value=uniqueid.get('imdb'), type='uniqueid', option={'type': 'imdb', 'uniqueids': uniqueid, 'episodeguide': details.get('episodeguide')})
|
||||
self._create_list('TMDb ID', 'uniqueid', value=uniqueid.get('tmdb'), type='uniqueid', option={'type': 'tmdb', 'uniqueids': uniqueid, 'episodeguide': details.get('episodeguide')})
|
||||
self._create_list('TVDb ID', 'uniqueid', value=uniqueid.get('tvdb'), type='uniqueid', option={'type': 'tvdb', 'uniqueids': uniqueid, 'episodeguide': details.get('episodeguide')})
|
||||
self._create_list('aniDB ID', 'uniqueid', value=uniqueid.get('anidb'), type='uniqueid', option={'type': 'anidb', 'uniqueids': uniqueid, 'episodeguide': details.get('episodeguide')})
|
||||
|
||||
elif self.dbtype == 'episode':
|
||||
self._create_list(xbmc.getLocalizedString(369), 'title', value=details.get('title'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(20376), 'originaltitle', value=details.get('originaltitle'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(20416), 'firstaired', value=details.get('firstaired'), type='date')
|
||||
self._create_list(xbmc.getLocalizedString(207), 'plot', value=details.get('plot'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(563) + ' / ' + xbmc.getLocalizedString(205), 'ratings', value=ratings_default, type='ratings', option=ratings)
|
||||
self._create_list(ADDON.getLocalizedString(32001), 'userrating', value=str(details.get('userrating')), type='userrating')
|
||||
self._create_list(xbmc.getLocalizedString(20339), 'director', value=get_joined_items(details.get('director')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(20417), 'writer', value=get_joined_items(details.get('writer')), type='array')
|
||||
self._create_list('IMDb ID', 'uniqueid', value=uniqueid.get('imdb'), type='uniqueid', option={'type': 'imdb', 'uniqueids': uniqueid})
|
||||
self._create_list('TMDb ID', 'uniqueid', value=uniqueid.get('tmdb'), type='uniqueid', option={'type': 'tmdb', 'uniqueids': uniqueid})
|
||||
self._create_list('TVDb ID', 'uniqueid', value=uniqueid.get('tvdb'), type='uniqueid', option={'type': 'tvdb', 'uniqueids': uniqueid})
|
||||
self._create_list('aniDB ID', 'uniqueid', value=uniqueid.get('anidb'), type='uniqueid', option={'type': 'anidb', 'uniqueids': uniqueid})
|
||||
self._create_list(xbmc.getLocalizedString(570), 'dateadded', value=details.get('dateadded'), type='datetime')
|
||||
self._create_list(xbmc.getLocalizedString(568), 'lastplayed', value=details.get('lastplayed'), type='datetime')
|
||||
self._create_list(xbmc.getLocalizedString(567), 'playcount', value=str(details.get('playcount', 0)), type='integer')
|
||||
|
||||
elif self.dbtype == 'set':
|
||||
self._create_list(xbmc.getLocalizedString(369), 'title', value=details.get('title'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(207), 'plot', value=details.get('plot'), type='string')
|
||||
|
||||
elif self.dbtype == 'musicvideo':
|
||||
self._create_list(xbmc.getLocalizedString(369), 'title', value=details.get('title'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(557), 'artist', value=get_joined_items(details.get('artist')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(558), 'album', value=details.get('album'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(345) + ' / ' + xbmc.getLocalizedString(172), 'premiered', value=details.get('premiered'), type='date')
|
||||
self._create_list(xbmc.getLocalizedString(554), 'track', value=str(details.get('track')), type='integer')
|
||||
self._create_list(xbmc.getLocalizedString(207), 'plot', value=details.get('plot'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(515), 'genre', value=get_joined_items(details.get('genre')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(20339), 'director', value=get_joined_items(details.get('director')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(572), 'studio', value=get_joined_items(details.get('studio')), type='array')
|
||||
# self._create_list(xbmc.getLocalizedString(563), 'rating', value=str(get_rounded_value(details.get('rating'))), type='integer') broken in kodi? cannot be set
|
||||
self._create_list(ADDON.getLocalizedString(32001), 'userrating', value=str(details.get('userrating')), type='userrating')
|
||||
self._create_list(xbmc.getLocalizedString(20459), 'tag', value=get_joined_items(details.get('tag')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(570), 'dateadded', value=details.get('dateadded'), type='datetime')
|
||||
self._create_list(xbmc.getLocalizedString(568), 'lastplayed', value=details.get('lastplayed'), type='datetime')
|
||||
self._create_list(xbmc.getLocalizedString(567), 'playcount', value=str(details.get('playcount', 0)), type='integer')
|
||||
|
||||
elif self.dbtype == 'artist':
|
||||
self._create_list(xbmc.getLocalizedString(515), 'genre', value=get_joined_items(details.get('genre')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(21821), 'description', value=details.get('description'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(39026), 'disambiguation', value=details.get('disambiguation'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(736), 'style', value=get_joined_items(details.get('style')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(175), 'mood', value=get_joined_items(details.get('mood')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(21892), 'instrument', value=get_joined_items(details.get('instrument')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(21893), 'born', value=details.get('born'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(21897), 'died', value=details.get('died'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(21894), 'formed', value=details.get('formed'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(21896), 'disbanded', value=details.get('disbanded'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(21898), 'yearsactive', value=get_joined_items(details.get('yearsactive')), type='array')
|
||||
|
||||
elif self.dbtype == 'album':
|
||||
self._create_list(ADDON.getLocalizedString(32023), 'albumlabel', value=details.get('albumlabel'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(21821), 'description', value=details.get('description'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(345), 'year', value=str(details.get('year')), type='integer')
|
||||
self._create_list(xbmc.getLocalizedString(467), 'type', value=details.get('type'), type='string')
|
||||
self._create_list(xbmc.getLocalizedString(515), 'genre', value=get_joined_items(details.get('genre')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(15111), 'theme', value=get_joined_items(details.get('theme')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(175), 'mood', value=get_joined_items(details.get('mood')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(736), 'style', value=get_joined_items(details.get('style')), type='array')
|
||||
self._create_list(xbmc.getLocalizedString(563), 'rating', value=str(get_rounded_value(details.get('rating'))), type='float')
|
||||
self._create_list(xbmc.getLocalizedString(205), 'votes', value=str(votes), type='integer')
|
||||
self._create_list(ADDON.getLocalizedString(32001), 'userrating', value=str(details.get('userrating')), type='userrating')
|
||||
|
||||
elif self.dbtype == 'song':
|
||||
self._create_list(xbmc.getLocalizedString(563), 'rating', value=str(get_rounded_value(details.get('rating'))), type='float')
|
||||
#self._create_list(xbmc.getLocalizedString(205), 'votes', value=str(details.get('votes')), type='integer') not available in methods.json? DaveBlake will fix it.
|
||||
self._create_list(ADDON.getLocalizedString(32001), 'userrating', value=str(details.get('userrating')), type='userrating')
|
||||
self._create_list(xbmc.getLocalizedString(568), 'lastplayed', value=details.get('lastplayed'), type='datetime')
|
||||
self._create_list(xbmc.getLocalizedString(567), 'playcount', value=str(details.get('playcount', 0)), type='integer')
|
||||
|
||||
def _create_list(self,label,key,type,value,option=None):
|
||||
if type in ['uniqueid', 'status', 'movieset']:
|
||||
icon = 'string'
|
||||
elif type == ('userrating'):
|
||||
icon = 'integer'
|
||||
elif type.startswith('date'):
|
||||
icon = 'date'
|
||||
elif type.startswith('rating'):
|
||||
icon = 'float'
|
||||
else:
|
||||
icon = type
|
||||
|
||||
li_item = xbmcgui.ListItem(label=label, label2='n/a' if not value else value)
|
||||
li_item.setArt({'icon': 'special://home/addons/script.metadata.editor/resources/media/icon_%s.png' % icon})
|
||||
|
||||
self.modeselect.append(li_item)
|
||||
self.keylist.append(key)
|
||||
self.typelist.append(type)
|
||||
self.optionlist.append(option)
|
||||
self.presetlist.append('' if not value else value)
|
||||
|
||||
def _handle_dbitem(self,key,value_type,preset=None,option=None):
|
||||
if preset:
|
||||
preset = preset.replace('n/a','')
|
||||
|
||||
if value_type == 'array':
|
||||
value = set_array(self.dbtype, key, preset)
|
||||
|
||||
elif value_type == 'select':
|
||||
value = modify_array(self.dbtype, key, preset)
|
||||
|
||||
elif value_type == 'string':
|
||||
value = set_string(preset)
|
||||
|
||||
elif value_type == 'integer':
|
||||
value = set_integer(preset)
|
||||
|
||||
elif value_type == 'float':
|
||||
value = set_float(preset)
|
||||
|
||||
elif value_type == 'date':
|
||||
value = set_date(preset)
|
||||
|
||||
elif value_type == 'datetime':
|
||||
preset = preset.split(' ') if preset else ['', '']
|
||||
date = set_date(preset[0])
|
||||
time = set_time(preset[1][:-3])
|
||||
value = date + ' ' + time + ':00'
|
||||
|
||||
elif value_type == 'userrating':
|
||||
value = set_integer_range(preset, 11)
|
||||
|
||||
elif value_type == 'ratings':
|
||||
value = set_ratings(option)
|
||||
|
||||
elif value_type == 'status':
|
||||
value = set_status(preset)
|
||||
self.status = value
|
||||
|
||||
elif value_type == 'watchlist':
|
||||
value = toggle_tag(preset)
|
||||
|
||||
elif value_type == 'movieset':
|
||||
value = set_movieset(preset)
|
||||
|
||||
elif value_type == ('uniqueid'):
|
||||
returned_value = set_string(preset)
|
||||
returned_value_json = returned_value if returned_value else None
|
||||
returned_value_str = returned_value if returned_value else ''
|
||||
|
||||
uniqueid_key = option.get('type')
|
||||
uniqueids = option.get('uniqueids')
|
||||
|
||||
value = {uniqueid_key: returned_value_json}
|
||||
|
||||
# build nfo info
|
||||
updated_dict = {}
|
||||
for item in uniqueids:
|
||||
if item == uniqueid_key:
|
||||
updated_dict[item] = returned_value_json
|
||||
else:
|
||||
updated_dict[item] = uniqueids.get(item)
|
||||
|
||||
if uniqueid_key not in updated_dict:
|
||||
updated_dict[uniqueid_key] = returned_value_str
|
||||
|
||||
nfo_value = [updated_dict, option.get('episodeguide')]
|
||||
|
||||
self.db.write(key=key, value=value)
|
||||
@@ -0,0 +1,321 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
|
||||
########################
|
||||
|
||||
from resources.lib.helper import *
|
||||
from resources.lib.database import *
|
||||
|
||||
########################
|
||||
|
||||
def set_ratings(ratings):
|
||||
providerlist = []
|
||||
for item in ratings:
|
||||
providerlist.append(str(item))
|
||||
|
||||
preselect = -1
|
||||
for item in providerlist:
|
||||
if ratings[item].get('default'):
|
||||
preselect = providerlist.index(item)
|
||||
|
||||
menu = DIALOG.select(xbmc.getLocalizedString(424), [ADDON.getLocalizedString(32015), ADDON.getLocalizedString(32016), ADDON.getLocalizedString(32017)])
|
||||
|
||||
if menu == 0: # set default provider
|
||||
providerdefault = DIALOG.select(ADDON.getLocalizedString(32014), providerlist, preselect=preselect)
|
||||
|
||||
if providerdefault >= 0:
|
||||
name = providerlist[providerdefault]
|
||||
|
||||
for item in ratings:
|
||||
default = True if item == name else False
|
||||
ratings[item] = {'default': default,
|
||||
'rating': ratings[item].get('rating'),
|
||||
'votes': ratings[item].get('votes')}
|
||||
|
||||
elif menu == 1: # edit votes/rating
|
||||
providerratings = DIALOG.select(ADDON.getLocalizedString(32012), providerlist, preselect=preselect)
|
||||
|
||||
if providerratings >= 0:
|
||||
name = providerlist[providerratings]
|
||||
cur_rating = round(ratings[name].get('rating', 0.0), 1)
|
||||
cur_votes = ratings[name].get('votes', 0)
|
||||
|
||||
rating = set_float(cur_rating)
|
||||
votes = set_integer(cur_votes)
|
||||
|
||||
if not rating:
|
||||
rating = 0.0
|
||||
|
||||
if not votes:
|
||||
votes = 0
|
||||
|
||||
ratings[name] = {'default': ratings[name].get('default'),
|
||||
'rating': rating,
|
||||
'votes': votes}
|
||||
|
||||
elif menu == 2: # add new rating provider
|
||||
supportedlist = ['imdb', 'themoviedb', 'tomatometerallcritics', 'tomatometeravgcritics', 'tomatometerallaudience', 'tomatometeravgaudience', 'metacritic']
|
||||
|
||||
for item in supportedlist:
|
||||
if item in providerlist:
|
||||
supportedlist.remove(item)
|
||||
|
||||
newprovider = DIALOG.select(ADDON.getLocalizedString(32013), supportedlist)
|
||||
|
||||
if newprovider >= 0:
|
||||
name = supportedlist[newprovider]
|
||||
rating = set_float(heading='Enter rating (floating number - min 0.1 / max 10.0)')
|
||||
|
||||
if not rating or float(rating) > 10:
|
||||
DIALOG.ok(xbmc.getLocalizedString(257), ADDON.getLocalizedString(32018))
|
||||
|
||||
else:
|
||||
votes = set_integer()
|
||||
if not votes:
|
||||
votes = 0
|
||||
|
||||
if not DIALOG.yesno(ADDON.getLocalizedString(32019), ADDON.getLocalizedString(32020)):
|
||||
default = False
|
||||
else:
|
||||
default = True
|
||||
|
||||
for item in ratings:
|
||||
ratings[item] = {'default': False,
|
||||
'rating': ratings[item].get('rating'),
|
||||
'votes': ratings[item].get('votes')}
|
||||
|
||||
ratings[name] = {'default': default,
|
||||
'rating': rating,
|
||||
'votes': votes}
|
||||
|
||||
return ratings
|
||||
|
||||
def set_movieset(preset):
|
||||
db = Database()
|
||||
db.sets()
|
||||
sets = db.result().get('set', [])
|
||||
|
||||
selectlist = []
|
||||
for item in sets:
|
||||
selectlist.append(item.get('title'))
|
||||
selectlist.sort()
|
||||
selectlist.insert(0, xbmc.getLocalizedString(231))
|
||||
selectlist.insert(1, ADDON.getLocalizedString(32005))
|
||||
|
||||
preselect = selectlist.index(preset) if preset in selectlist else -1
|
||||
|
||||
selectdialog = DIALOG.select(xbmc.getLocalizedString(20466), selectlist, preselect=preselect)
|
||||
|
||||
if selectdialog == 0:
|
||||
return ''
|
||||
|
||||
elif selectdialog == 1:
|
||||
value = set_string()
|
||||
return value
|
||||
|
||||
elif selectdialog > 1:
|
||||
return selectlist[selectdialog]
|
||||
|
||||
return preset
|
||||
|
||||
def set_array(dbtype,key,preset):
|
||||
actionlist = [ADDON.getLocalizedString(32005), ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32006)]
|
||||
array_action = DIALOG.select(xbmc.getLocalizedString(14241), actionlist)
|
||||
array_list = get_list_items(preset)
|
||||
|
||||
if array_action == 0:
|
||||
keyboard = xbmc.Keyboard()
|
||||
keyboard.doModal()
|
||||
|
||||
if keyboard.isConfirmed():
|
||||
new_item = keyboard.getText()
|
||||
|
||||
if new_item not in array_list:
|
||||
array_list.append(new_item)
|
||||
|
||||
return remove_empty(array_list)
|
||||
|
||||
elif array_action == 1:
|
||||
array = modify_array(dbtype, key, array_list)
|
||||
return array
|
||||
|
||||
elif array_action == 2:
|
||||
keyboard = xbmc.Keyboard(preset)
|
||||
keyboard.doModal()
|
||||
|
||||
if keyboard.isConfirmed():
|
||||
array = keyboard.getText()
|
||||
else:
|
||||
array = preset
|
||||
|
||||
return get_list_items(array)
|
||||
|
||||
else:
|
||||
return array_list
|
||||
|
||||
|
||||
def modify_array(dbtype,key,values):
|
||||
modified = []
|
||||
all_values = []
|
||||
|
||||
if not isinstance(values, list):
|
||||
values = get_list_items(values)
|
||||
|
||||
if key in ['genre', 'tags']:
|
||||
db = Database()
|
||||
getattr(db, key)()
|
||||
result = db.result()
|
||||
|
||||
if key == 'genre':
|
||||
if dbtype in ['musicvideo', 'artist', 'album']:
|
||||
for genre in result.get('audiogenres'):
|
||||
all_values.append(genre)
|
||||
else:
|
||||
for genre in result.get('videogenres'):
|
||||
all_values.append(genre)
|
||||
|
||||
elif key == 'tags':
|
||||
for tag in result.get('tags'):
|
||||
if tag not in values:
|
||||
all_values.append(tag)
|
||||
|
||||
all_values = list(set(values + all_values))
|
||||
all_values.sort()
|
||||
values.sort()
|
||||
|
||||
# open common array dialog if all_values are empty
|
||||
if not all_values:
|
||||
notification(ADDON.getLocalizedString(32000), ADDON.getLocalizedString(32048))
|
||||
value = set_array(dbtype, key, '')
|
||||
return value
|
||||
|
||||
preselectlist = []
|
||||
for item in values:
|
||||
preselectlist.append(all_values.index(item))
|
||||
|
||||
selectdialog = DIALOG.multiselect(ADDON.getLocalizedString(32002), all_values, preselect=preselectlist)
|
||||
|
||||
if selectdialog == -1 or selectdialog is None:
|
||||
return values
|
||||
|
||||
for index in selectdialog:
|
||||
modified.append(all_values[index])
|
||||
|
||||
return modified
|
||||
|
||||
|
||||
def set_integer(preset=''):
|
||||
preset = str(preset)
|
||||
if preset == '0':
|
||||
preset = ''
|
||||
|
||||
value = xbmcgui.Dialog().numeric(0, xbmc.getLocalizedString(16028), preset)
|
||||
|
||||
if not value:
|
||||
return None
|
||||
|
||||
return int(value)
|
||||
|
||||
|
||||
def set_float(preset='',heading=ADDON.getLocalizedString(32011)):
|
||||
try:
|
||||
preset = float(preset)
|
||||
preset = round(preset,1)
|
||||
|
||||
except Exception:
|
||||
preset = ''
|
||||
|
||||
keyboard = xbmc.Keyboard(str(preset))
|
||||
keyboard.setHeading(heading)
|
||||
keyboard.doModal()
|
||||
|
||||
if keyboard.isConfirmed():
|
||||
try:
|
||||
value = float(keyboard.getText())
|
||||
value = round(value,1)
|
||||
return value
|
||||
|
||||
except Exception:
|
||||
set_float(preset)
|
||||
|
||||
return preset
|
||||
|
||||
|
||||
def set_date(preset):
|
||||
try:
|
||||
conv = time.strptime(preset,'%Y-%m-%d')
|
||||
conv = time.strftime('%d/%m/%Y', conv)
|
||||
|
||||
except Exception:
|
||||
conv = '01/01/1900'
|
||||
|
||||
value = xbmcgui.Dialog().numeric(1, xbmc.getLocalizedString(16028), conv)
|
||||
|
||||
if value:
|
||||
value = value.replace(' ','0')
|
||||
value = time.strptime(value,'%d/%m/%Y')
|
||||
value = time.strftime('%Y-%m-%d',value)
|
||||
return value
|
||||
|
||||
return preset
|
||||
|
||||
|
||||
def set_time(preset):
|
||||
value = xbmcgui.Dialog().numeric(2, xbmc.getLocalizedString(16028), preset)
|
||||
|
||||
if value:
|
||||
return value
|
||||
|
||||
return preset
|
||||
|
||||
|
||||
def set_string(preset=''):
|
||||
value = preset.replace('\n', '[CR]')
|
||||
keyboard = xbmc.Keyboard(value)
|
||||
keyboard.doModal()
|
||||
|
||||
if keyboard.isConfirmed():
|
||||
value = keyboard.getText()
|
||||
|
||||
return value.replace('[CR]', '\n')
|
||||
|
||||
|
||||
def set_integer_range(preset, maximum):
|
||||
preset = int(preset) if preset else 0
|
||||
rangelist = []
|
||||
|
||||
for i in range(0, maximum):
|
||||
rangelist.append(str(i))
|
||||
|
||||
value = DIALOG.select(xbmc.getLocalizedString(424), rangelist, preselect=preset)
|
||||
|
||||
if value >= 0:
|
||||
return value
|
||||
|
||||
return preset
|
||||
|
||||
|
||||
def set_status(preset):
|
||||
statuslist = ['Returning series', 'In production', 'Planned', 'Cancelled', 'Ended']
|
||||
|
||||
if preset == ADDON.getLocalizedString(32022):
|
||||
preset = ''
|
||||
|
||||
value = DIALOG.select(xbmc.getLocalizedString(126), statuslist)
|
||||
|
||||
if value >= 0:
|
||||
return statuslist[value]
|
||||
|
||||
return preset
|
||||
|
||||
|
||||
def toggle_tag(preset):
|
||||
tag = 'Watchlist'
|
||||
tags = get_list_items(preset)
|
||||
|
||||
if tag in tags:
|
||||
tags.remove(tag)
|
||||
else:
|
||||
tags.append(tag)
|
||||
|
||||
return tags
|
||||
@@ -0,0 +1,250 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
|
||||
########################
|
||||
|
||||
import sys
|
||||
import xbmc
|
||||
import xbmcaddon
|
||||
import xbmcgui
|
||||
import xbmcvfs
|
||||
import xbmcplugin
|
||||
import json
|
||||
import time
|
||||
import datetime
|
||||
import os
|
||||
import hashlib
|
||||
import xml.etree.ElementTree as ET
|
||||
import requests
|
||||
import urllib.request as urllib
|
||||
from urllib.parse import urlencode
|
||||
from contextlib import contextmanager
|
||||
|
||||
########################
|
||||
|
||||
ADDON = xbmcaddon.Addon()
|
||||
ADDON_ID = ADDON.getAddonInfo('id')
|
||||
ADDON_DATA_PATH = os.path.join(xbmcvfs.translatePath("special://profile/addon_data/%s" % ADDON_ID))
|
||||
|
||||
NOTICE = xbmc.LOGINFO
|
||||
WARNING = xbmc.LOGWARNING
|
||||
DEBUG = xbmc.LOGDEBUG
|
||||
ERROR = xbmc.LOGERROR
|
||||
LOG_JSON = ADDON.getSettingBool('json_log')
|
||||
KODI_VERSION = int(xbmc.getInfoLabel('System.BuildVersion')[:2])
|
||||
|
||||
DIALOG = xbmcgui.Dialog()
|
||||
|
||||
########################
|
||||
|
||||
|
||||
def log(txt,loglevel=DEBUG,json=False,force=False):
|
||||
if loglevel in [DEBUG, WARNING, ERROR] or force:
|
||||
if force:
|
||||
loglevel = NOTICE
|
||||
|
||||
if json:
|
||||
txt = json_prettyprint(txt)
|
||||
|
||||
message = u'[ %s ] %s' % (ADDON_ID,txt)
|
||||
xbmc.log(msg=message, level=loglevel)
|
||||
|
||||
|
||||
def unicode_string(string):
|
||||
string = u'%s' % string
|
||||
return string
|
||||
|
||||
|
||||
def remove_quotes(label):
|
||||
if not label:
|
||||
return ''
|
||||
|
||||
if label.startswith("'") and label.endswith("'") and len(label) > 2:
|
||||
label = label[1:-1]
|
||||
if label.startswith('"') and label.endswith('"') and len(label) > 2:
|
||||
label = label[1:-1]
|
||||
elif label.startswith('"') and label.endswith('"'):
|
||||
label = label[6:-6]
|
||||
|
||||
return label
|
||||
|
||||
|
||||
def get_joined_items(item):
|
||||
if len(item) > 0 and item is not None:
|
||||
item = '; '.join(item)
|
||||
item = item + ';'
|
||||
else:
|
||||
item = ''
|
||||
|
||||
return item
|
||||
|
||||
|
||||
def get_list_items(string):
|
||||
return remove_empty(string.replace('; ',';').split(';'))
|
||||
|
||||
|
||||
def get_key_item(items,key):
|
||||
try:
|
||||
return items.get(key)
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
def get_rounded_value(value):
|
||||
try:
|
||||
if not isinstance(value, str) and not isinstance(value, float):
|
||||
value = str(value)
|
||||
if not isinstance(value, float):
|
||||
value = float(value)
|
||||
|
||||
return round(value,1)
|
||||
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
def remove_empty(array):
|
||||
cleaned_array = []
|
||||
|
||||
for item in array:
|
||||
if not item or item in ['', ';']:
|
||||
continue
|
||||
cleaned_array.append(item)
|
||||
|
||||
return cleaned_array
|
||||
|
||||
|
||||
def execute(cmd):
|
||||
xbmc.executebuiltin(cmd)
|
||||
|
||||
|
||||
def condition(condition):
|
||||
return xbmc.getCondVisibility(condition)
|
||||
|
||||
|
||||
def winprop(key, value=None, clear=False, window_id=10000):
|
||||
window = xbmcgui.Window(window_id)
|
||||
|
||||
if clear:
|
||||
window.clearProperty(key.replace('.json', '').replace('.bool', '').replace('.str', ''))
|
||||
|
||||
elif value is not None:
|
||||
|
||||
if key.endswith('.json'):
|
||||
key = key.replace('.json', '')
|
||||
value = json.dumps(value)
|
||||
|
||||
elif key.endswith('.bool'):
|
||||
key = key.replace('.bool', '')
|
||||
value = 'true' if value else 'false'
|
||||
|
||||
elif key.endswith('.str'):
|
||||
key = key.replace('.str', '')
|
||||
value = str(value)
|
||||
|
||||
window.setProperty(key, value)
|
||||
|
||||
else:
|
||||
result = window.getProperty(key.replace('.json', '').replace('.bool', '').replace('.str', ''))
|
||||
|
||||
if result:
|
||||
if key.endswith('.json'):
|
||||
result = json.loads(result)
|
||||
|
||||
elif key.endswith('.bool'):
|
||||
result = result in ('true', '1')
|
||||
|
||||
elif key.endswith('.str'):
|
||||
result = eval(result)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def json_call(method,properties=None,sort=None,query_filter=None,limit=None,params=None,item=None,options=None,limits=None,debug=False):
|
||||
json_string = {'jsonrpc': '2.0', 'id': 1, 'method': method, 'params': {}}
|
||||
|
||||
if properties is not None:
|
||||
json_string['params']['properties'] = properties
|
||||
|
||||
if limit is not None:
|
||||
json_string['params']['limits'] = {'start': 0, 'end': int(limit)}
|
||||
|
||||
if sort is not None:
|
||||
json_string['params']['sort'] = sort
|
||||
|
||||
if query_filter is not None:
|
||||
json_string['params']['filter'] = query_filter
|
||||
|
||||
if options is not None:
|
||||
json_string['params']['options'] = options
|
||||
|
||||
if limits is not None:
|
||||
json_string['params']['limits'] = limits
|
||||
|
||||
if item is not None:
|
||||
json_string['params']['item'] = item
|
||||
|
||||
if params is not None:
|
||||
json_string['params'].update(params)
|
||||
|
||||
jsonrpc_call = json.dumps(json_string)
|
||||
|
||||
result = xbmc.executeJSONRPC(jsonrpc_call)
|
||||
result = json.loads(result)
|
||||
|
||||
if debug:
|
||||
log('--> JSON CALL: ' + json_prettyprint(json_string), force=True)
|
||||
log('--> JSON RESULT: ' + json_prettyprint(result), force=True)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def json_prettyprint(string):
|
||||
return json.dumps(string, sort_keys=True, indent=4, separators=(',', ': '))
|
||||
|
||||
|
||||
def xml_prettyprint(root,level=0):
|
||||
i = '\n' + level * ' '
|
||||
|
||||
if len(root):
|
||||
if not root.text or not root.text.strip():
|
||||
root.text = i + ' '
|
||||
|
||||
if not root.tail or not root.tail.strip():
|
||||
root.tail = i
|
||||
|
||||
for root in root:
|
||||
xml_prettyprint(root, level+1)
|
||||
|
||||
if not root.tail or not root.tail.strip():
|
||||
root.tail = i
|
||||
|
||||
else:
|
||||
if level and (not root.tail or not root.tail.strip()):
|
||||
root.tail = i
|
||||
|
||||
|
||||
def notification(header=ADDON.getLocalizedString(32000),message=''):
|
||||
DIALOG.notification(header, message, icon='special://home/addons/script.metadata.editor/resources/icon.png')
|
||||
|
||||
|
||||
def reload_widgets():
|
||||
# Notifies script.embuary.helper to reload widgets
|
||||
execute('NotifyAll(%s,Finished)' % ADDON_ID)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def busy_dialog(force=False):
|
||||
if force:
|
||||
execute('ActivateWindow(busydialognocancel)')
|
||||
|
||||
elif not winprop('UpdatingRatings.bool'):
|
||||
# NFO writing usually only takes < 1s. Just show BusyDialog if it takes longer for whatever reason.
|
||||
execute('AlarmClock(BusyAlarmDelay,ActivateWindow(busydialognocancel),00:02,silent)')
|
||||
|
||||
try:
|
||||
yield
|
||||
|
||||
finally:
|
||||
execute('CancelAlarm(BusyAlarmDelay,silent)')
|
||||
execute('Dialog.Close(busydialognocancel)')
|
||||
@@ -0,0 +1,267 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
JSON_MAP = {
|
||||
'movie_properties': [
|
||||
'title',
|
||||
'originaltitle',
|
||||
'sorttitle',
|
||||
'votes',
|
||||
'playcount',
|
||||
'year',
|
||||
'genre',
|
||||
'studio',
|
||||
'country',
|
||||
'tagline',
|
||||
'tag',
|
||||
'plot',
|
||||
'runtime',
|
||||
'premiered',
|
||||
'file',
|
||||
'plotoutline',
|
||||
'lastplayed',
|
||||
'trailer',
|
||||
'rating',
|
||||
'ratings',
|
||||
'userrating',
|
||||
'resume',
|
||||
'art',
|
||||
'mpaa',
|
||||
'director',
|
||||
'writer',
|
||||
'cast',
|
||||
'set',
|
||||
'setid',
|
||||
'top250',
|
||||
'dateadded',
|
||||
'imdbnumber',
|
||||
'uniqueid'
|
||||
],
|
||||
|
||||
'movies_properties': [
|
||||
'title',
|
||||
'year'
|
||||
],
|
||||
|
||||
'set_properties': [
|
||||
'title',
|
||||
'plot'
|
||||
],
|
||||
|
||||
'sets_properties': [
|
||||
'title'
|
||||
],
|
||||
|
||||
'episode_properties': [
|
||||
'title',
|
||||
'playcount',
|
||||
'season',
|
||||
'episode',
|
||||
'showtitle',
|
||||
'originaltitle',
|
||||
'plot',
|
||||
'votes',
|
||||
'file',
|
||||
'rating',
|
||||
'ratings',
|
||||
'userrating',
|
||||
'resume',
|
||||
'tvshowid',
|
||||
'firstaired',
|
||||
'art',
|
||||
'runtime',
|
||||
'director',
|
||||
'writer',
|
||||
'cast',
|
||||
'dateadded',
|
||||
'lastplayed',
|
||||
'uniqueid'
|
||||
],
|
||||
|
||||
'episodes_properties': [
|
||||
'title',
|
||||
'showtitle'
|
||||
],
|
||||
|
||||
'season_properties': [
|
||||
'season',
|
||||
'episode',
|
||||
'art',
|
||||
'userrating',
|
||||
'watchedepisodes',
|
||||
'showtitle',
|
||||
'playcount',
|
||||
'tvshowid'
|
||||
],
|
||||
|
||||
'seasons_properties': [
|
||||
'season',
|
||||
'showtitle',
|
||||
'tvshowid'
|
||||
],
|
||||
|
||||
'tvshow_properties': [
|
||||
'title',
|
||||
'studio',
|
||||
'year',
|
||||
'plot',
|
||||
'cast',
|
||||
'rating',
|
||||
'ratings',
|
||||
'userrating',
|
||||
'votes',
|
||||
'file',
|
||||
'genre',
|
||||
'episode',
|
||||
'season',
|
||||
'runtime',
|
||||
'mpaa',
|
||||
'premiered',
|
||||
'playcount',
|
||||
'lastplayed',
|
||||
'sorttitle',
|
||||
'originaltitle',
|
||||
'episodeguide',
|
||||
'art',
|
||||
'tag',
|
||||
'dateadded',
|
||||
'watchedepisodes',
|
||||
'imdbnumber',
|
||||
'uniqueid'
|
||||
],
|
||||
|
||||
'tvshows_properties': [
|
||||
'title',
|
||||
'year'
|
||||
],
|
||||
|
||||
'musicvideo_properties': [
|
||||
'title',
|
||||
'playcount',
|
||||
'runtime',
|
||||
'director',
|
||||
'studio',
|
||||
'year',
|
||||
'plot',
|
||||
'album',
|
||||
'artist',
|
||||
'genre',
|
||||
'track',
|
||||
'lastplayed',
|
||||
'fanart',
|
||||
'thumbnail',
|
||||
'file',
|
||||
'resume',
|
||||
'dateadded',
|
||||
'tag',
|
||||
'art',
|
||||
'rating',
|
||||
'userrating',
|
||||
'premiered'
|
||||
],
|
||||
|
||||
'musicvideos_properties': [
|
||||
'title',
|
||||
'year'
|
||||
],
|
||||
|
||||
'artist_properties': [
|
||||
'instrument',
|
||||
'style',
|
||||
'mood',
|
||||
'born',
|
||||
'formed',
|
||||
'description',
|
||||
'genre',
|
||||
'died',
|
||||
'disbanded',
|
||||
'yearsactive',
|
||||
'musicbrainzartistid',
|
||||
'fanart',
|
||||
'thumbnail',
|
||||
'compilationartist',
|
||||
'dateadded',
|
||||
'roles',
|
||||
'songgenres',
|
||||
'isalbumartist',
|
||||
'disambiguation'
|
||||
],
|
||||
|
||||
'artists_properties': [
|
||||
'dateadded'
|
||||
],
|
||||
|
||||
'album_properties': [
|
||||
'title',
|
||||
'description',
|
||||
'artist',
|
||||
'genre',
|
||||
'theme',
|
||||
'mood',
|
||||
'style',
|
||||
'type',
|
||||
'albumlabel',
|
||||
'rating',
|
||||
'votes',
|
||||
'userrating',
|
||||
'year',
|
||||
'musicbrainzalbumid',
|
||||
'musicbrainzalbumartistid',
|
||||
'fanart',
|
||||
'thumbnail',
|
||||
'playcount',
|
||||
'artistid',
|
||||
'displayartist',
|
||||
'compilation',
|
||||
'releasetype',
|
||||
'dateadded'
|
||||
],
|
||||
|
||||
'albums_properties': [
|
||||
'title',
|
||||
'year'
|
||||
],
|
||||
|
||||
'song_properties': [
|
||||
'title',
|
||||
'artist',
|
||||
'albumartist',
|
||||
'genre',
|
||||
'year',
|
||||
'rating',
|
||||
'album',
|
||||
'track',
|
||||
'duration',
|
||||
'comment',
|
||||
'lyrics',
|
||||
'musicbrainztrackid',
|
||||
'musicbrainzartistid',
|
||||
'musicbrainzalbumid',
|
||||
'musicbrainzalbumartistid',
|
||||
'playcount',
|
||||
'fanart',
|
||||
'thumbnail',
|
||||
'file',
|
||||
'albumid',
|
||||
'lastplayed',
|
||||
'disc',
|
||||
'genreid',
|
||||
'artistid',
|
||||
'displayartist',
|
||||
'albumartistid',
|
||||
'albumreleasetype',
|
||||
'dateadded',
|
||||
'votes',
|
||||
'userrating',
|
||||
'mood',
|
||||
'contributors',
|
||||
'displaycomposer',
|
||||
'displayconductor',
|
||||
'displayorchestra',
|
||||
'displaylyricist'
|
||||
],
|
||||
|
||||
'songs_properties': [
|
||||
'title',
|
||||
'artist'
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
|
||||
########################
|
||||
|
||||
from resources.lib.helper import *
|
||||
from resources.lib.database import *
|
||||
|
||||
########################
|
||||
|
||||
def update_nfo(dbtype,dbid,details=None,file=None, forced=False):
|
||||
if not forced and not ADDON.getSettingBool('nfo_updating'):
|
||||
winprop('updatenfo', clear=True)
|
||||
return
|
||||
|
||||
if not details:
|
||||
db = Database(dbid, dbtype)
|
||||
getattr(db, dbtype)()
|
||||
details = db.result().get(dbtype)[0]
|
||||
|
||||
if not details:
|
||||
log('NFO updater: No item details found or provided --> ID: %s Type: %s' % (dbid, dbtype), ERROR)
|
||||
return
|
||||
|
||||
if not file:
|
||||
file = details.get('file')
|
||||
if not file:
|
||||
log('NFO updater: No item path available --> ID: %s Type: %s' % (dbid, dbtype), ERROR)
|
||||
return
|
||||
|
||||
if dbtype == 'tvshow':
|
||||
path = os.path.join(file,'tvshow.nfo')
|
||||
else:
|
||||
path = file.replace(os.path.splitext(file)[1], '.nfo')
|
||||
|
||||
UpdateNFO(file=path,
|
||||
dbtype=dbtype,
|
||||
dbid=dbid,
|
||||
details=details)
|
||||
|
||||
# support for additional movie.nfo
|
||||
if dbtype == 'movie':
|
||||
path = file.replace(os.path.basename(file), 'movie.nfo')
|
||||
|
||||
if xbmcvfs.exists(path):
|
||||
UpdateNFO(file=path,
|
||||
dbtype=dbtype,
|
||||
dbid=dbid,
|
||||
details=details)
|
||||
|
||||
|
||||
class UpdateNFO():
|
||||
def __init__(self,file,dbtype,dbid,details):
|
||||
self.targetfile = file
|
||||
self.dbtype = dbtype
|
||||
self.dbid = dbid
|
||||
self.details = details
|
||||
self.root = None
|
||||
self.sortlist = []
|
||||
self.run()
|
||||
|
||||
def run(self):
|
||||
with busy_dialog():
|
||||
try:
|
||||
if xbmcvfs.exists(self.targetfile):
|
||||
self.root = self.read_file()
|
||||
self.existing_nfo = True
|
||||
|
||||
elif ADDON.getSettingBool('create_nfo'):
|
||||
self.root = ET.Element(self.dbtype.replace('episode', 'episodedetails'))
|
||||
self.existing_nfo = False
|
||||
|
||||
else:
|
||||
raise Exception('File not found')
|
||||
|
||||
self.handle_details()
|
||||
self.write_file()
|
||||
success = True
|
||||
|
||||
except Exception as error:
|
||||
log('Cannot process .nfo file: %s --> %s' % (self.targetfile, error), ERROR)
|
||||
success = False
|
||||
|
||||
if winprop('updatenfo.bool'):
|
||||
msg = xbmc.getLocalizedString(20177) if success else xbmc.getLocalizedString(257)
|
||||
notification(ADDON.getLocalizedString(32046), msg)
|
||||
winprop('updatenfo', clear=True)
|
||||
|
||||
def read_file(self):
|
||||
file = xbmcvfs.File(self.targetfile)
|
||||
content = file.read()
|
||||
file.close()
|
||||
|
||||
if content:
|
||||
tree = ET.ElementTree(ET.fromstring(content))
|
||||
root = tree.getroot()
|
||||
return root
|
||||
|
||||
def write_file(self):
|
||||
# sort nfo
|
||||
if self.existing_nfo:
|
||||
index = 0
|
||||
for key in self.sortlist:
|
||||
for elem in self.root.findall(key):
|
||||
self.root.remove(elem)
|
||||
self.root.insert(index, elem)
|
||||
index += 1
|
||||
|
||||
xml_prettyprint(self.root)
|
||||
|
||||
content = ET.tostring(self.root, encoding='UTF8', method='xml').decode()
|
||||
|
||||
with xbmcvfs.File(self.targetfile, 'w') as f:
|
||||
result = f.write(content)
|
||||
|
||||
def handle_details(self):
|
||||
li = [{'key': 'title', 'value': self.details.get('title')},
|
||||
{'key': 'originaltitle', 'value': self.details.get('originaltitle')},
|
||||
{'key': 'showtitle', 'value': self.details.get('showtitle')},
|
||||
{'key': 'sorttitle', 'value': self.details.get('sorttitle')},
|
||||
{'key': 'userrating', 'value': self.details.get('userrating')},
|
||||
{'key': 'outline', 'value': self.details.get('plotoutline')},
|
||||
{'key': 'plot', 'value': self.details.get('plot')},
|
||||
{'key': 'tagline', 'value': self.details.get('tagline')},
|
||||
{'key': 'mpaa', 'value': self.details.get('mpaa')},
|
||||
{'key': 'premiered', 'value': self.details.get('premiered')},
|
||||
{'key': 'releasedate', 'value': self.details.get('releasedate')}, #emby
|
||||
{'key': 'year', 'value': self.details.get('premiered', '')[:4]}, #emby
|
||||
{'key': 'country', 'value': self.details.get('country')},
|
||||
{'key': 'studio', 'value': self.details.get('studio')},
|
||||
{'key': 'director', 'value': self.details.get('director')},
|
||||
{'key': 'credits', 'value': self.details.get('writer')},
|
||||
{'key': 'writer', 'value': self.details.get('writer')}, #emby
|
||||
{'key': 'tag', 'value': self.details.get('tag')},
|
||||
{'key': 'isuserfavorite', 'value': 'true' if 'Favorite movies' in self.details.get('tag', []) or 'Favorite tvshows' in self.details.get('tag', []) else 'false'}, #emby
|
||||
{'key': 'genre', 'value': self.details.get('genre')},
|
||||
{'key': 'top250', 'value': self.details.get('top250')},
|
||||
{'key': 'ratings', 'value': self.details.get('ratings')},
|
||||
{'key': 'uniqueid', 'value': self.details.get('uniqueid')},
|
||||
{'key': 'status', 'value': self.details.get('status')},
|
||||
{'key': 'aired', 'value': self.details.get('firstaired')},
|
||||
{'key': 'dateadded', 'value': self.details.get('dateadded')}
|
||||
]
|
||||
|
||||
if ADDON.getSettingBool('write_watched_stated'):
|
||||
li.append({'key': 'playcount', 'value': self.details.get('playcount')})
|
||||
li.append({'key': 'watched', 'value': 'true' if self.details.get('playcount', 0) > 0 else 'false'}) #emby
|
||||
li.append({'key': 'lastplayed', 'value': self.details.get('lastplayed')})
|
||||
|
||||
for item in li:
|
||||
key = item.get('key')
|
||||
value = item.get('value')
|
||||
self.sortlist.append(key)
|
||||
|
||||
if key == 'ratings':
|
||||
self.handle_ratings(value)
|
||||
|
||||
elif key == 'uniqueid':
|
||||
self.handle_uniqueid(value, self.details.get('episodeguide', ''))
|
||||
|
||||
else:
|
||||
self.handle_elem(key, value)
|
||||
|
||||
def handle_elem(self,key,value):
|
||||
if key != 'status' or (key == 'status' and value): # Keep status value. Required for leia because status isn't returned thru json.
|
||||
for elem in self.root.findall(key):
|
||||
self.root.remove(elem)
|
||||
|
||||
if isinstance(value, list):
|
||||
for i in value:
|
||||
if i:
|
||||
elem = ET.SubElement(self.root, key)
|
||||
elem.text = unicode_string(i)
|
||||
|
||||
elif value:
|
||||
elem = ET.SubElement(self.root, key)
|
||||
elem.text = unicode_string(value)
|
||||
|
||||
def handle_ratings(self,value):
|
||||
for elem in self.root.findall('ratings'):
|
||||
self.root.remove(elem)
|
||||
|
||||
elem = ET.SubElement(self.root, 'ratings')
|
||||
for item in value:
|
||||
rating = float(value[item].get('rating', 0.0))
|
||||
rating = str(round(rating, 1))
|
||||
votes = str(value[item].get('votes', 0))
|
||||
|
||||
subelem = ET.SubElement(elem, 'rating')
|
||||
subelem.set('name', item)
|
||||
subelem.set('max', '10')
|
||||
|
||||
if value[item].get('default'):
|
||||
subelem.set('default', 'true')
|
||||
|
||||
# Emby <votes>, <rating>
|
||||
for key in ['rating', 'votes']:
|
||||
for defaultelem in self.root.findall(key):
|
||||
self.root.remove(defaultelem)
|
||||
|
||||
defaultelem = ET.SubElement(self.root, key)
|
||||
defaultelem.text = eval(key)
|
||||
|
||||
self.sortlist.append(key)
|
||||
|
||||
else:
|
||||
subelem.set('default', 'false')
|
||||
|
||||
rating_elem = ET.SubElement(subelem, 'value')
|
||||
rating_elem.text = rating
|
||||
|
||||
votes_elem = ET.SubElement(subelem, 'votes')
|
||||
votes_elem.text = votes
|
||||
|
||||
# Emby <criticrating> Rotten ratings
|
||||
if item == 'tomatometerallcritics':
|
||||
normalized_rating = int(float(rating) * 10)
|
||||
if normalized_rating > 100:
|
||||
normalized_rating = ''
|
||||
|
||||
for emby_elem in self.root.findall('criticrating'):
|
||||
self.root.remove(emby_elem)
|
||||
|
||||
emby_rotten = ET.SubElement(self.root, 'criticrating')
|
||||
emby_rotten.text = str(normalized_rating)
|
||||
|
||||
self.sortlist.append('criticrating')
|
||||
|
||||
def handle_uniqueid(self,uniqueids,episodeguide):
|
||||
# find default uniqueid
|
||||
default = ''
|
||||
if 'tvdb' in episodeguide:
|
||||
default = 'tvdb'
|
||||
elif 'tmdb' in episodeguide:
|
||||
default = 'tmdb'
|
||||
else:
|
||||
for elem in self.root.findall('uniqueid'):
|
||||
if elem.get('default'):
|
||||
default = elem.get('type')
|
||||
break
|
||||
|
||||
# set fallback default uniqueid
|
||||
if not default:
|
||||
if self.dbtype == 'movie':
|
||||
if uniqueids.get('tmdb'):
|
||||
default = 'tmdb'
|
||||
elif uniqueids.get('imdb'):
|
||||
default = 'imdb'
|
||||
|
||||
elif self.dbtype == 'tvshow':
|
||||
scraper_default = ADDON.getSetting('tv_scraper_base')
|
||||
|
||||
if (scraper_default == 'TVDb' and uniqueids.get('tvdb')):
|
||||
default = 'tvdb'
|
||||
elif scraper_default == 'TMDb' and uniqueids.get('tmdb'):
|
||||
default = 'tmdb'
|
||||
|
||||
# <uniqueid> fields
|
||||
for elem in self.root.findall('uniqueid'):
|
||||
self.root.remove(elem)
|
||||
|
||||
for item in uniqueids:
|
||||
value = uniqueids.get(item, '')
|
||||
|
||||
if value:
|
||||
elem = ET.SubElement(self.root, 'uniqueid')
|
||||
elem.set('type', item)
|
||||
elem.text = value
|
||||
|
||||
if default == item:
|
||||
elem.set('default', 'true')
|
||||
if self.dbtype == 'tvshow':
|
||||
self._set_episodeguide(item, value)
|
||||
|
||||
self.sortlist.append(item)
|
||||
|
||||
# Emby <imdbid>, <tmdbid>, etc.
|
||||
emby_uniqueids = {}
|
||||
for item in uniqueids:
|
||||
if item == 'imdb':
|
||||
emby_uniqueids['imdbid'] = uniqueids.get(item)
|
||||
emby_uniqueids['imdb_id'] = uniqueids.get(item) # emby is using a underscore for tvshows Oo
|
||||
|
||||
elif item == 'tmdbcollection':
|
||||
emby_uniqueids['collectionnumber'] = uniqueids.get(item)
|
||||
|
||||
elif item.lower() in ['zap2it', 'tvrage', 'tvdb', 'tmdb']:
|
||||
emby_uniqueids['%sid' % item] = uniqueids.get(item)
|
||||
|
||||
for item in emby_uniqueids:
|
||||
for elem in self.root.findall(item):
|
||||
self.root.remove(elem)
|
||||
|
||||
value = emby_uniqueids.get(item)
|
||||
|
||||
if value:
|
||||
elem = ET.SubElement(self.root, item)
|
||||
elem.text = value
|
||||
|
||||
self.sortlist.append(item)
|
||||
|
||||
def _set_episodeguide(self,type,value):
|
||||
post = False
|
||||
cache = ''
|
||||
|
||||
if type == 'tvdb':
|
||||
post = 'yes'
|
||||
cache = 'auth.json'
|
||||
url = 'https://api.thetvdb.com/login?{"apikey":"439DFEBA9D3059C6","id":%s}|Content-Type=application/json' % str(value)
|
||||
json_value = '<episodeguide><url post="%s" cache="%s"><url>%s</url></episodeguide>' % (post, cache, url)
|
||||
|
||||
elif type == 'tmdb':
|
||||
language = ADDON.getSetting('tmdb_language')
|
||||
cache = 'tmdb-%s-%s.json' % (str(value), language)
|
||||
url = 'http://api.themoviedb.org/3/tv/%s?api_key=6a5be4999abf74eba1f9a8311294c267&language=%s' % (str(value), language)
|
||||
json_value = '<episodeguide><url cache="%s"><url>%s</url></episodeguide>' % (cache, url)
|
||||
|
||||
else:
|
||||
url = ''
|
||||
json_value = '<episodeguide><url cache=""><url></url></episodeguide>'
|
||||
|
||||
for elem in self.root.findall('episodeguide'):
|
||||
self.root.remove(elem)
|
||||
|
||||
episodeguide_elem = ET.SubElement(self.root, 'episodeguide')
|
||||
url_elem = ET.SubElement(episodeguide_elem, 'url')
|
||||
if post:
|
||||
url_elem.set('post', post)
|
||||
url_elem.set('cache', cache)
|
||||
url_elem.text = url
|
||||
|
||||
self.sortlist.append('episodeguide')
|
||||
|
||||
json_call('VideoLibrary.SetTVShowDetails',
|
||||
params={'episodeguide': json_value, 'tvshowid': int(self.dbid)},
|
||||
debug=LOG_JSON
|
||||
)
|
||||
@@ -0,0 +1,601 @@
|
||||
#!/usr/bin/python
|
||||
# coding: utf-8
|
||||
|
||||
########################
|
||||
|
||||
from __future__ import division
|
||||
|
||||
from resources.lib.helper import *
|
||||
from resources.lib.functions import *
|
||||
from resources.lib.database import *
|
||||
from resources.lib.nfo_updater import *
|
||||
|
||||
########################
|
||||
|
||||
RUN_IN_BACKGROUND = ADDON.getSettingBool('update_background')
|
||||
BUSYDIALOG = False if RUN_IN_BACKGROUND else True
|
||||
OMDB_FALLBACK = ADDON.getSettingBool('omdb_fallback_search')
|
||||
OMDB_API = ADDON.getSetting('omdb_api_key')
|
||||
COUNTRY_CODE = ADDON.getSetting('country_code')
|
||||
SKIP_MPAA = ADDON.getSettingBool('mpaa_skip')
|
||||
SKIP_NOT_RATED = ADDON.getSettingBool('mpaa_skip_nr')
|
||||
MPAA_FALLBACK = ADDON.getSettingBool('mpaa_fallback')
|
||||
TMDB_LANGUAGE = ADDON.getSetting('tmdb_language')
|
||||
RATING_DEBUG = ADDON.getSetting('debug_rating_updater')
|
||||
|
||||
########################
|
||||
|
||||
def update_ratings(dbid=None,dbtype=None,content=None):
|
||||
# no omdb API key message
|
||||
if not OMDB_API:
|
||||
if not DIALOG.yesno(xbmc.getLocalizedString(14117), ADDON.getLocalizedString(32035)):
|
||||
return
|
||||
|
||||
winprop('UpdatingRatings.bool', True)
|
||||
msg_text = xbmc.getLocalizedString(19256)
|
||||
|
||||
# get database ids
|
||||
if isinstance(dbtype, str):
|
||||
dbtype = dbtype.split('+')
|
||||
|
||||
with busy_dialog(force=BUSYDIALOG):
|
||||
db = Database(dbid=dbid, append=['episodes'])
|
||||
for i in dbtype:
|
||||
getattr(db, i)()
|
||||
result = db.result()
|
||||
|
||||
# calc total items to process
|
||||
total_items = 0
|
||||
for i in result:
|
||||
if result.get(i):
|
||||
total_items = total_items + len(result[i])
|
||||
|
||||
if total_items > 1:
|
||||
# show progress if 1< will be processed
|
||||
progressdialog = ProgressDialog(total_items)
|
||||
|
||||
for i in result:
|
||||
if i == 'movie':
|
||||
cat = xbmc.getLocalizedString(20338)
|
||||
elif i == 'tvshow':
|
||||
cat = xbmc.getLocalizedString(20364)
|
||||
elif i == 'episode':
|
||||
cat = xbmc.getLocalizedString(20359)
|
||||
|
||||
for item in result[i]:
|
||||
if progressdialog.canceled():
|
||||
break
|
||||
|
||||
if item.get('showtitle') and item.get('label'):
|
||||
label = item.get('showtitle') + ' - ' + item.get('label')
|
||||
else:
|
||||
label = item.get('title')
|
||||
|
||||
if item.get('year'):
|
||||
label = label + ' (' + str(item.get('year')) + ')'
|
||||
|
||||
progressdialog.update(cat, label)
|
||||
|
||||
UpdateRating({'dbid': item.get('%sid' % i),
|
||||
'type': i})
|
||||
#xbmc.sleep(50)
|
||||
|
||||
if progressdialog.canceled():
|
||||
msg_text = ADDON.getLocalizedString(32042)
|
||||
break
|
||||
|
||||
progressdialog.close()
|
||||
|
||||
elif total_items == 1:
|
||||
# process single item
|
||||
for i in result:
|
||||
UpdateRating({'dbid': result[i][0].get('%sid' % i),
|
||||
'type': i})
|
||||
|
||||
else:
|
||||
# error message
|
||||
msg_text = ADDON.getLocalizedString(32048)
|
||||
|
||||
winprop('UpdatingRatings', clear=True)
|
||||
notification(ADDON.getLocalizedString(32030), msg_text)
|
||||
|
||||
|
||||
class ProgressDialog(object):
|
||||
def __init__(self,total_items):
|
||||
if RUN_IN_BACKGROUND:
|
||||
self.progressdialog = xbmcgui.DialogProgressBG()
|
||||
else:
|
||||
self.progressdialog = xbmcgui.DialogProgress()
|
||||
|
||||
self.progressdialog.create('Updating', '')
|
||||
self.total_items = total_items
|
||||
self.processed_items = 0
|
||||
self.progress = 0
|
||||
|
||||
def canceled(self):
|
||||
if RUN_IN_BACKGROUND:
|
||||
return True if winprop('CancelRatingUpdater.bool') else False
|
||||
else:
|
||||
return True if self.progressdialog.iscanceled() or winprop('CancelRatingUpdater.bool') else False
|
||||
|
||||
def update(self,cat,label):
|
||||
self.processed_items += 1
|
||||
progress = int(100 / self.total_items * self.processed_items)
|
||||
processed = str(self.processed_items) + ' / ' + str(self.total_items)
|
||||
|
||||
if RUN_IN_BACKGROUND:
|
||||
self.progressdialog.update(progress, processed, cat + ':' + label)
|
||||
else:
|
||||
self.progressdialog.update(progress, cat + ':[CR]' + label + '[CR]' + processed)
|
||||
|
||||
def close(self):
|
||||
self.progressdialog.close()
|
||||
self.progressdialog = None
|
||||
winprop('CancelRatingUpdater', clear=True)
|
||||
|
||||
|
||||
class UpdateRating(object):
|
||||
def __init__(self,params):
|
||||
self.dbid = params.get('dbid')
|
||||
self.dbtype = params.get('type')
|
||||
self.tmdb_type = 'movie' if self.dbtype == 'movie' else 'tv'
|
||||
self.tmdb_tv_status = None
|
||||
self.tmdb_mpaa = None
|
||||
self.tmdb_mpaa_fallback = None
|
||||
self.tmdb_rating = None
|
||||
self.imdb_rating = None
|
||||
self.omdb_limit = False
|
||||
self.update_uniqueid = False
|
||||
self.episodeguide = None
|
||||
|
||||
# collect db data
|
||||
self.db = Database(dbid=self.dbid, dbtype=self.dbtype)
|
||||
self.get_details()
|
||||
|
||||
self.uniqueid = self.details.get('uniqueid', {})
|
||||
self.ratings = self.details.get('ratings', {})
|
||||
self.file = self.details.get('file')
|
||||
self.year = self.details.get('year')
|
||||
self.premiered = self.details.get('premiered') or self.details.get('firstaired')
|
||||
self.title = self.details.get('title')
|
||||
self.original_title = self.details.get('originaltitle') or self.title
|
||||
self.tags = self.details.get('tag')
|
||||
|
||||
if any(string in self.details.get('episodeguide', '') for string in ['tvdb', 'tmdb']):
|
||||
self.episodeguide = self.details.get('episodeguide')
|
||||
else:
|
||||
self.episodeguide = None
|
||||
|
||||
if self.uniqueid:
|
||||
self.run()
|
||||
|
||||
def get_details(self):
|
||||
getattr(self.db, self.dbtype)()
|
||||
self.details = self.db.result().get(self.dbtype)[0]
|
||||
|
||||
def run(self):
|
||||
log('Run rating updater - %s: %s - ID: %s' % (self.dbtype, self.title, str(self.dbid)), force=RATING_DEBUG)
|
||||
self.imdb = self.uniqueid.get('imdb')
|
||||
self.tmdb = self.uniqueid.get('tmdb')
|
||||
self.tvdb = self.uniqueid.get('tvdb')
|
||||
|
||||
# don't proceed for episodes if no IMDb is available
|
||||
if self.dbtype == 'episode' and not self.imdb:
|
||||
log('Episode with no IMDb. Skip.', force=RATING_DEBUG)
|
||||
return
|
||||
|
||||
# get the default used rating
|
||||
self.default_rating = None
|
||||
for rating in self.ratings:
|
||||
if self.ratings[rating].get('default'):
|
||||
self.default_rating = rating
|
||||
break
|
||||
|
||||
if self.dbtype != 'episode':
|
||||
# get TMDb ID (if not available) by using the ID of IMDb or TVDb
|
||||
if not self.tmdb and self.imdb:
|
||||
log('No TMDb. Try to get by IMDb ID %s' % self.imdb, force=RATING_DEBUG)
|
||||
self.get_tmdb_externalid(self.imdb)
|
||||
|
||||
elif not self.tmdb and self.tvdb:
|
||||
log('No TMDb. Try to get by TVDb ID %s' % str(self.tvdb), force=RATING_DEBUG)
|
||||
self.get_tmdb_externalid(self.tvdb)
|
||||
|
||||
# get TMDb rating and IMDb number if not available
|
||||
if self.tmdb:
|
||||
log('Fetch data by TMDb ID %s' % str(self.tmdb), force=RATING_DEBUG)
|
||||
self.get_tmdb()
|
||||
|
||||
# get Rotten, Metacritic and IMDb ratings of OMDb
|
||||
if not self.omdb_limit:
|
||||
log('Fetch OMDb data', force=RATING_DEBUG)
|
||||
self.get_omdb()
|
||||
|
||||
# if no TMDb ID was known before but OMDb return the IMDb ID -> try to get TMDb data again
|
||||
if self.dbtype != 'episode' and not self.tmdb and self.imdb:
|
||||
log('Try to get TMDb ID by returned IMDb ID %s of OMDb' % self.imdb, force=RATING_DEBUG)
|
||||
self.get_tmdb_externalid(self.imdb)
|
||||
|
||||
if self.tmdb:
|
||||
log('Fetch data by TMDb ID %s' % str(self.tmdb), force=RATING_DEBUG)
|
||||
self.get_tmdb()
|
||||
|
||||
# emby <ratings> and <votes>
|
||||
if 'default' in self.ratings:
|
||||
self.emby_ratings()
|
||||
|
||||
# update db + nfo
|
||||
log('Updating info', force=RATING_DEBUG)
|
||||
self.update_info()
|
||||
|
||||
def emby_ratings(self):
|
||||
# Emby For Kodi is storing the rating as 'default'
|
||||
if self.imdb_rating:
|
||||
self._update_ratings_dict(key='default',
|
||||
rating=float(self.imdb_rating),
|
||||
votes=int(self.imdb_votes)
|
||||
)
|
||||
|
||||
elif self.tmdb_rating:
|
||||
self._update_ratings_dict(key='default',
|
||||
rating=float(self.tmdb_rating),
|
||||
votes=int(self.tmdb_votes)
|
||||
)
|
||||
|
||||
def get_tmdb(self):
|
||||
result = self._tmdb(action=self.tmdb_type,
|
||||
call=str(self.tmdb),
|
||||
params={'append_to_response': 'release_dates,content_ratings,external_ids'}
|
||||
)
|
||||
|
||||
if not result:
|
||||
return
|
||||
|
||||
self.tmdb_rating = result.get('vote_average')
|
||||
self.tmdb_votes = result.get('vote_count')
|
||||
|
||||
if not self.original_title:
|
||||
self.original_title = result.get('original_title') or resultresult.get('original_name')
|
||||
|
||||
# update original title if missing
|
||||
if self.original_title:
|
||||
self._set_value('originaltitle', self.original_title)
|
||||
|
||||
if self.tmdb_type == 'tv':
|
||||
premiered = result.get('first_air_date')
|
||||
self.tmdb_tv_status = result.get('status')
|
||||
|
||||
# update TV status as well
|
||||
if self.tmdb_tv_status:
|
||||
self._set_value('status', self.tmdb_tv_status)
|
||||
|
||||
else:
|
||||
premiered = result.get('release_date')
|
||||
|
||||
# update the year if not correct
|
||||
if premiered and self.premiered != premiered:
|
||||
self.year = premiered[:4]
|
||||
|
||||
if ADDON.getSettingBool('update_premiered') or not self.premiered:
|
||||
self._set_value('premiered', premiered)
|
||||
|
||||
if self.tmdb_rating:
|
||||
self._update_ratings_dict(key='themoviedb',
|
||||
rating=self.tmdb_rating,
|
||||
votes=self.tmdb_votes
|
||||
)
|
||||
|
||||
# set MPAA based on setting
|
||||
if not SKIP_MPAA:
|
||||
if self.tmdb_type == 'movie':
|
||||
release_dates = result['release_dates']['results']
|
||||
|
||||
for country in release_dates:
|
||||
if country.get('iso_3166_1') == COUNTRY_CODE:
|
||||
for item in country['release_dates']:
|
||||
if item.get('certification'):
|
||||
self.tmdb_mpaa = item.get('certification')
|
||||
break
|
||||
break
|
||||
|
||||
elif country.get('iso_3166_1') == 'US':
|
||||
for item in country['release_dates']:
|
||||
if item.get('certification'):
|
||||
self.tmdb_mpaa_fallback = item.get('certification')
|
||||
break
|
||||
|
||||
if self.tmdb_type == 'tv':
|
||||
content_ratings = result['content_ratings']['results']
|
||||
|
||||
for country in content_ratings:
|
||||
if country.get('iso_3166_1') == COUNTRY_CODE:
|
||||
self.tmdb_mpaa = country.get('rating')
|
||||
break
|
||||
|
||||
elif country.get('iso_3166_1') == 'US':
|
||||
self.tmdb_mpaa_fallback = country.get('rating')
|
||||
|
||||
if SKIP_NOT_RATED:
|
||||
if self.tmdb_mpaa == 'NR':
|
||||
self.tmdb_mpaa = None
|
||||
|
||||
if self.tmdb_mpaa_fallback == 'NR':
|
||||
self.tmdb_mpaa_fallback = None
|
||||
|
||||
if self.tmdb_mpaa:
|
||||
if COUNTRY_CODE == 'DE':
|
||||
self.tmdb_mpaa = 'FSK ' + self.tmdb_mpaa
|
||||
|
||||
self._set_value('mpaa', self.tmdb_mpaa)
|
||||
|
||||
elif self.tmdb_mpaa_fallback and MPAA_FALLBACK:
|
||||
self._set_value('mpaa', self.tmdb_mpaa_fallback)
|
||||
|
||||
else:
|
||||
self._set_value('mpaa', '')
|
||||
|
||||
# set IMDb ID if not available in the library
|
||||
if not self.imdb:
|
||||
if self.tmdb_type == 'movie':
|
||||
self.imdb = result.get('imdb_id')
|
||||
|
||||
elif self.tmdb_type == 'tv':
|
||||
self.imdb = result['external_ids'].get('imdb_id')
|
||||
|
||||
if self.imdb:
|
||||
self._update_uniqueid_dict('imdb', self.imdb)
|
||||
|
||||
# add TVDb ID to uniqueid if missing
|
||||
if not self.tvdb and self.tmdb_type == 'tv':
|
||||
self.tvdb = result['external_ids'].get('tvdb_id')
|
||||
|
||||
if self.tvdb:
|
||||
self._update_uniqueid_dict('tvdb', self.tvdb)
|
||||
|
||||
def get_tmdb_externalid(self,external_id):
|
||||
result = self._tmdb(action='find',
|
||||
call=str(external_id),
|
||||
params={'external_source': 'imdb_id' if external_id.startswith('tt') else 'tvdb_id'}
|
||||
)
|
||||
|
||||
if self.dbtype == 'movie' and result.get('movie_results'):
|
||||
self.tmdb = result['movie_results'][0].get('id')
|
||||
|
||||
elif self.dbtype == 'tvshow' and result.get('tv_results'):
|
||||
self.tmdb = result['tv_results'][0].get('id')
|
||||
|
||||
if self.tmdb:
|
||||
self._update_uniqueid_dict('tmdb', self.tmdb)
|
||||
|
||||
def get_omdb(self):
|
||||
omdb = self._omdb()
|
||||
|
||||
if not omdb:
|
||||
return
|
||||
|
||||
tree = ET.ElementTree(ET.fromstring(omdb))
|
||||
root = tree.getroot()
|
||||
|
||||
for child in root:
|
||||
# imdb ratings
|
||||
self.imdb_rating = child.get('imdbRating', '').replace('N/A', '')
|
||||
self.imdb_votes = child.get('imdbVotes', '0').replace('N/A', '0').replace(',', '')
|
||||
if self.imdb_rating:
|
||||
self._update_ratings_dict(key='imdb',
|
||||
rating=float(self.imdb_rating),
|
||||
votes=int(self.imdb_votes)
|
||||
)
|
||||
|
||||
# regular rotten rating
|
||||
tomatometerallcritics = child.get('tomatoMeter', '').replace('N/A', '')
|
||||
tomatometerallcritics_avg = child.get('tomatoRating', '').replace('N/A', '')
|
||||
tomatometerallcritics_votes = child.get('tomatoReviews', '0').replace('N/A', '0').replace(',', '')
|
||||
|
||||
if tomatometerallcritics:
|
||||
self._update_ratings_dict(key='tomatometerallcritics',
|
||||
rating=int(tomatometerallcritics) / 10,
|
||||
votes=int(tomatometerallcritics_votes))
|
||||
|
||||
if tomatometerallcritics_avg:
|
||||
self._update_ratings_dict(key='tomatometeravgcritics',
|
||||
rating=float(tomatometerallcritics_avg),
|
||||
votes=int(tomatometerallcritics_votes))
|
||||
|
||||
# user rotten rating
|
||||
tomatometerallaudience = child.get('tomatoUserMeter', '').replace('N/A', '')
|
||||
tomatometerallaudience_avg = child.get('tomatoUserRating', '').replace('N/A', '')
|
||||
tomatometerallaudience_votes = child.get('tomatoUserReviews', '0').replace('N/A', '0').replace(',', '')
|
||||
|
||||
if tomatometerallaudience:
|
||||
self._update_ratings_dict(key='tomatometerallaudience',
|
||||
rating=int(tomatometerallaudience) / 10,
|
||||
votes=int(tomatometerallaudience_votes))
|
||||
|
||||
if tomatometerallaudience_avg:
|
||||
self._update_ratings_dict(key='tomatometeravgaudience',
|
||||
rating=float(tomatometerallaudience_avg),
|
||||
votes=int(tomatometerallaudience_votes))
|
||||
|
||||
# metacritic
|
||||
metacritic = child.get('metascore', '').replace('N/A', '')
|
||||
if metacritic:
|
||||
metacritic = int(metacritic) / 10
|
||||
self._update_ratings_dict(key='metacritic',
|
||||
rating=metacritic,
|
||||
votes=0)
|
||||
|
||||
# set imdb if not set before
|
||||
if not self.imdb and child.get('imdbID') and child.get('imdbID') != 'N/A':
|
||||
self.imdb = child.get('imdbID')
|
||||
self._update_uniqueid_dict('imdb', child.get('imdbID'))
|
||||
|
||||
break
|
||||
|
||||
def update_info(self):
|
||||
# set at least one default rating if none is set in the library
|
||||
if not self.default_rating and self.ratings:
|
||||
for item in ['imdb', 'themoviedb', 'tomatometerallcritics', 'tomatometeravgcritics', 'metacritic']:
|
||||
if item in self.ratings:
|
||||
self.default_rating = item
|
||||
break
|
||||
|
||||
# unkown rating source is stored -> use the first one
|
||||
if not self.default_rating:
|
||||
for item in self.ratings:
|
||||
self.default_rating = item
|
||||
break
|
||||
|
||||
# update to library
|
||||
self._set_value(key='ratings', value=self.ratings)
|
||||
|
||||
if self.update_uniqueid:
|
||||
self._set_value(key='uniqueid', value=self.uniqueid)
|
||||
|
||||
# episode guide verification
|
||||
if self.episodeguide:
|
||||
if 'thetvdb' in self.episodeguide and 'tvdb' not in self.uniqueid:
|
||||
self.episodeguide = None
|
||||
elif 'themoviedb' in self.episodeguide and 'tmdb' not in self.uniqueid:
|
||||
self.episodeguide = None
|
||||
|
||||
if self.dbtype == 'tvshow' and not self.episodeguide:
|
||||
if 'tvdb' in self.uniqueid:
|
||||
value = self.uniqueid.get('tvdb')
|
||||
url = 'https://api.thetvdb.com/login?{"apikey":"439DFEBA9D3059C6","id":%s}|Content-Type=application/json' % str(value)
|
||||
json_value = '<episodeguide><url post="yes" cache="auth.json"><url>%s</url></episodeguide>' % url
|
||||
|
||||
elif 'tmdb' in self.uniqueid:
|
||||
value = self.uniqueid.get('tmdb')
|
||||
cache = 'tmdb-%s-%s.json' % (str(value), TMDB_LANGUAGE)
|
||||
url = 'http://api.themoviedb.org/3/tv/%s?api_key=6a5be4999abf74eba1f9a8311294c267&language=%s' % (str(value), TMDB_LANGUAGE)
|
||||
json_value = '<episodeguide><url cache="%s"><url>%s</url></episodeguide>' % (cache, url)
|
||||
|
||||
else:
|
||||
json_value = '<episodeguide><url cache=""><url></url></episodeguide>'
|
||||
|
||||
self.episodeguide = json_value
|
||||
self._set_value('episodeguide', json_value)
|
||||
|
||||
# nfo updating
|
||||
if self.file:
|
||||
# get updated data
|
||||
self.get_details()
|
||||
|
||||
# TV status cannot be fetched in Leia
|
||||
if self.tmdb_tv_status and not self.details.get('status'):
|
||||
self.details['status'] = self.tmdb_tv_status
|
||||
|
||||
update_nfo(file=self.file,
|
||||
dbtype=self.dbtype,
|
||||
dbid=self.dbid,
|
||||
details=self.details
|
||||
)
|
||||
|
||||
def _update_ratings_dict(self,key,rating,votes):
|
||||
self.ratings[key] = {'default': True if key == self.default_rating else False,
|
||||
'rating': rating,
|
||||
'votes': votes}
|
||||
|
||||
def _update_uniqueid_dict(self,key,value):
|
||||
self.uniqueid[key] = str(value)
|
||||
self.update_uniqueid = True
|
||||
|
||||
def _set_value(self,key,value):
|
||||
self.db.write(key=key, value=value)
|
||||
|
||||
def _omdb(self):
|
||||
if not OMDB_API:
|
||||
log('No OMDb API key configured. Skip.', force=RATING_DEBUG)
|
||||
return
|
||||
|
||||
if self.imdb:
|
||||
url = 'http://www.omdbapi.com/?apikey=%s&i=%s&plot=short&r=xml&tomatoes=true' % (OMDB_API, self.imdb)
|
||||
|
||||
elif OMDB_FALLBACK and self.dbtype != 'episode' and self.original_title and self.year:
|
||||
# urllib has issues with some asian letters
|
||||
try:
|
||||
title = urllib.quote(self.original_title)
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
url = 'http://www.omdbapi.com/?apikey=%s&t=%s&year=%s&plot=short&r=xml&tomatoes=true' % (OMDB_API, title, self.year)
|
||||
|
||||
else:
|
||||
return
|
||||
|
||||
error_msg = 'OMDb error for "%s (%s)" IMDBd "%s". Error --> ' % (self.original_title, self.year, self.imdb)
|
||||
|
||||
for i in range(1,4): # loop if heavy server load
|
||||
log('OMDb call try %s/3' % str(i), force=RATING_DEBUG)
|
||||
try:
|
||||
request = requests.get(url, timeout=5)
|
||||
if not str(request.status_code).startswith('5'):
|
||||
break
|
||||
elif i == 1:
|
||||
notification('OMDb', ADDON.getLocalizedString(32024))
|
||||
|
||||
except Exception:
|
||||
if i < 3:
|
||||
xbmc.sleep(500)
|
||||
else:
|
||||
log(error_msg + '408', WARNING)
|
||||
return
|
||||
|
||||
if request.status_code == 401:
|
||||
log('OMDb error --> API limit reached', WARNING)
|
||||
if DIALOG.yesno(xbmc.getLocalizedString(257), ADDON.getLocalizedString(32033)):
|
||||
log('OMDb limit reached and disabled for next calls', force=RATING_DEBUG)
|
||||
self.omdb_limit = True
|
||||
else:
|
||||
log('OMDb limit reached and rating updater canceled', force=RATING_DEBUG)
|
||||
winprop('CancelRatingUpdater.bool', True)
|
||||
return
|
||||
|
||||
elif not request.ok:
|
||||
log(error_msg + str(request.status_code), WARNING)
|
||||
return
|
||||
|
||||
result = request.text
|
||||
|
||||
if not result or '<root response="False">' in result:
|
||||
log(error_msg + 'Result = ' + str(result), WARNING)
|
||||
return
|
||||
|
||||
return result
|
||||
|
||||
def _tmdb(self,action,call=None,get=None,params=None):
|
||||
result = {}
|
||||
args = {}
|
||||
args['api_key'] = 'fc168650632c6597038cf7072a7c20da'
|
||||
|
||||
if params:
|
||||
args.update(params)
|
||||
|
||||
call = '/' + str(call) if call else ''
|
||||
get = '/' + get if get else ''
|
||||
|
||||
url = 'https://api.themoviedb.org/3/' + action + call + get
|
||||
url = '{0}?{1}'.format(url, urlencode(args))
|
||||
|
||||
for i in range(1,4): # loop if heavy server load
|
||||
log('TMDb call try %s/3' % str(i), force=RATING_DEBUG)
|
||||
try:
|
||||
request = requests.get(url, timeout=5)
|
||||
if not str(request.status_code).startswith('5'):
|
||||
break
|
||||
elif i == 1:
|
||||
notification('TMDb', ADDON.getLocalizedString(32024))
|
||||
|
||||
except Exception:
|
||||
if i < 3:
|
||||
xbmc.sleep(500)
|
||||
else:
|
||||
log('TMDb connection error', force=RATING_DEBUG)
|
||||
return result
|
||||
|
||||
if request.ok:
|
||||
result = request.json()
|
||||
else:
|
||||
log('TMDb returned nothing', force=RATING_DEBUG)
|
||||
|
||||
return result
|
||||
Reference in New Issue
Block a user