diff --git a/.gitattributes b/.gitattributes index ad58e791..d7ed8434 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,4 +5,5 @@ changelog.txt export-ignore tests export-ignore .travis.yml export-ignore .gitlab-ci.yml export-ignore -HOW_TO_RELEASE.md export-ignore \ No newline at end of file +HOW_TO_RELEASE.md export-ignore +ruff.toml export-ignore \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7fccb9f3..b671adbf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,10 +15,18 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install python-dateutil pytest mock pytest-cov coverage + pip install python-dateutil pytest mock pytest-cov coverage ruff + + - name: Check code formatting + run: ruff format . + + - name: Check for code errors + run: ruff check . + - name: Run Test run: | py.test --cov coveralls --cov-report term-missing --cov-report=lcov --cov=resources tests/ + - name: Coveralls GitHub Action uses: coverallsapp/github-action@1.1.3 with: diff --git a/resources/lib/kodiUtilities.py b/resources/lib/kodiUtilities.py index 52bebc5f..6f55806b 100644 --- a/resources/lib/kodiUtilities.py +++ b/resources/lib/kodiUtilities.py @@ -6,20 +6,21 @@ import xbmcaddon import json import re -import sys import logging from resources.lib import utilities # read settings -__addon__ = xbmcaddon.Addon('script.trakt') +__addon__ = xbmcaddon.Addon("script.trakt") logger = logging.getLogger(__name__) -REGEX_URL = '(^https?://)(.+)' +REGEX_URL = "(^https?://)(.+)" -def notification(header: str, message: str, time=5000, icon=__addon__.getAddonInfo('icon')): +def notification( + header: str, message: str, time=5000, icon=__addon__.getAddonInfo("icon") +): xbmcgui.Dialog().notification(header, message, icon, time) @@ -64,197 +65,220 @@ def kodiJsonRequest(params): response = json.loads(request) try: - if 'result' in response: - return response['result'] + if "result" in response: + return response["result"] return None except KeyError: - logger.warn("[%s] %s" % - (params['method'], response['error']['message'])) + logger.warn("[%s] %s" % (params["method"], response["error"]["message"])) return None + # check exclusion settings for filename passed as argument def checkExclusion(fullpath: str) -> bool: - if not fullpath: return True # Live TV exclusion - if fullpath.startswith("pvr://") and getSettingAsBool('ExcludeLiveTV'): + if fullpath.startswith("pvr://") and getSettingAsBool("ExcludeLiveTV"): logger.debug( - "checkExclusion(): Video is playing via Live TV, which is currently set as excluded location.") + "checkExclusion(): Video is playing via Live TV, which is currently set as excluded location." + ) return True # HTTP exclusion - if fullpath.startswith(("http://", "https://")) and getSettingAsBool('ExcludeHTTP'): + if fullpath.startswith(("http://", "https://")) and getSettingAsBool("ExcludeHTTP"): logger.debug( - "checkExclusion(): Video is playing via HTTP source, which is currently set as excluded location.") + "checkExclusion(): Video is playing via HTTP source, which is currently set as excluded location." + ) return True # Plugin exclusion - if fullpath.startswith("plugin://") and getSettingAsBool('ExcludePlugin'): + if fullpath.startswith("plugin://") and getSettingAsBool("ExcludePlugin"): logger.debug( - "checkExclusion(): Video is playing via Plugin source, which is currently set as excluded location.") + "checkExclusion(): Video is playing via Plugin source, which is currently set as excluded location." + ) return True # Script exclusion - if xbmcgui.Window(10000).getProperty('script.trakt.paused') == 'true' and getSettingAsBool('ExcludeScript'): + if xbmcgui.Window(10000).getProperty( + "script.trakt.paused" + ) == "true" and getSettingAsBool("ExcludeScript"): logger.debug( - "checkExclusion(): Video is playing via Script source, which is currently set as excluded location.") + "checkExclusion(): Video is playing via Script source, which is currently set as excluded location." + ) return True # Path exclusions - ExcludePath = getSetting('ExcludePath') - if ExcludePath != "" and getSettingAsBool('ExcludePathOption'): + ExcludePath = getSetting("ExcludePath") + if ExcludePath != "" and getSettingAsBool("ExcludePathOption"): if fullpath.startswith(ExcludePath): logger.debug( - "checkExclusion(): Video is from location, which is currently set as excluded path 1.") + "checkExclusion(): Video is from location, which is currently set as excluded path 1." + ) return True found = False for x in range(2, 13): - found |= utilities.checkExcludePath(getSetting( - 'ExcludePath%i' % x), getSettingAsBool('ExcludePathOption%i' % x), fullpath, x) + found |= utilities.checkExcludePath( + getSetting("ExcludePath%i" % x), + getSettingAsBool("ExcludePathOption%i" % x), + fullpath, + x, + ) return found -def kodiRpcToTraktMediaObject(type, data, mode='collected'): - if type == 'show': - if 'uniqueid' in data: - data['ids'] = data.pop('uniqueid') - elif 'imdbnumber' in data: - id = data.pop('imdbnumber') - data['ids'] = utilities.guessBestTraktId(id, type)[0] +def kodiRpcToTraktMediaObject(type, data, mode="collected"): + if type == "show": + if "uniqueid" in data: + data["ids"] = data.pop("uniqueid") + elif "imdbnumber" in data: + id = data.pop("imdbnumber") + data["ids"] = utilities.guessBestTraktId(id, type)[0] else: - logger.debug('kodiRpcToTraktMediaObject(): No uniqueid found') - data['rating'] = data['userrating'] if 'userrating' in data and data['userrating'] > 0 else 0 - del data['label'] + logger.debug("kodiRpcToTraktMediaObject(): No uniqueid found") + data["rating"] = ( + data["userrating"] if "userrating" in data and data["userrating"] > 0 else 0 + ) + del data["label"] return data - elif type == 'episode': - if checkExclusion(data['file']): + elif type == "episode": + if checkExclusion(data["file"]): return - if data['playcount'] is None: + if data["playcount"] is None: plays = 0 else: - plays = data.pop('playcount') + plays = data.pop("playcount") if plays > 0: watched = 1 else: watched = 0 - episode = {'season': data['season'], 'number': data['episode'], 'title': data['label'], - 'ids': {'episodeid': data['episodeid']}, 'watched': watched, - 'plays': plays, 'collected': 1} - - if 'uniqueid' in data: - if 'tmdb' in data['uniqueid']: - episode['ids']['tmdb'] = data['uniqueid']['tmdb'] - if 'imdb' in data['uniqueid']: - episode['ids']['imdb'] = data['uniqueid']['imdb'] - if 'tvdb' in data['uniqueid']: - episode['ids']['tvdb'] = data['uniqueid']['tvdb'] - elif 'unknown' in data['uniqueid'] and data['uniqueid']['unknown'] != '': - episode['ids'].update(utilities.guessBestTraktId( - data['uniqueid']['unknown'], type)[0]) - elif 'imdbnumber' in data: - id = data.pop('imdbnumber') - data['ids'] = utilities.guessBestTraktId(id, type)[0] - - if 'lastplayed' in data: - episode['watched_at'] = utilities.convertDateTimeToUTC( - data['lastplayed']) - if 'dateadded' in data: - episode['collected_at'] = utilities.convertDateTimeToUTC( - data['dateadded']) - if 'runtime' in data: - episode['runtime'] = data['runtime'] - episode['rating'] = data['userrating'] if 'userrating' in data and data['userrating'] > 0 else 0 - if mode == 'watched' and episode['watched']: + episode = { + "season": data["season"], + "number": data["episode"], + "title": data["label"], + "ids": {"episodeid": data["episodeid"]}, + "watched": watched, + "plays": plays, + "collected": 1, + } + + if "uniqueid" in data: + if "tmdb" in data["uniqueid"]: + episode["ids"]["tmdb"] = data["uniqueid"]["tmdb"] + if "imdb" in data["uniqueid"]: + episode["ids"]["imdb"] = data["uniqueid"]["imdb"] + if "tvdb" in data["uniqueid"]: + episode["ids"]["tvdb"] = data["uniqueid"]["tvdb"] + elif "unknown" in data["uniqueid"] and data["uniqueid"]["unknown"] != "": + episode["ids"].update( + utilities.guessBestTraktId(data["uniqueid"]["unknown"], type)[0] + ) + elif "imdbnumber" in data: + id = data.pop("imdbnumber") + data["ids"] = utilities.guessBestTraktId(id, type)[0] + + if "lastplayed" in data: + episode["watched_at"] = utilities.convertDateTimeToUTC(data["lastplayed"]) + if "dateadded" in data: + episode["collected_at"] = utilities.convertDateTimeToUTC(data["dateadded"]) + if "runtime" in data: + episode["runtime"] = data["runtime"] + episode["rating"] = ( + data["userrating"] if "userrating" in data and data["userrating"] > 0 else 0 + ) + if mode == "watched" and episode["watched"]: return episode - elif mode == 'collected' and episode['collected']: + elif mode == "collected" and episode["collected"]: return episode else: return - elif type == 'movie': - if checkExclusion(data.pop('file')): + elif type == "movie": + if checkExclusion(data.pop("file")): return - if 'lastplayed' in data: - data['watched_at'] = utilities.convertDateTimeToUTC( - data.pop('lastplayed')) - if 'dateadded' in data: - data['collected_at'] = utilities.convertDateTimeToUTC( - data.pop('dateadded')) - if data['playcount'] is None: - data['plays'] = 0 + if "lastplayed" in data: + data["watched_at"] = utilities.convertDateTimeToUTC(data.pop("lastplayed")) + if "dateadded" in data: + data["collected_at"] = utilities.convertDateTimeToUTC(data.pop("dateadded")) + if data["playcount"] is None: + data["plays"] = 0 else: - data['plays'] = data.pop('playcount') - data['rating'] = data['userrating'] if 'userrating' in data and data['userrating'] > 0 else 0 - data['collected'] = 1 # this is in our kodi so it should be collected - data['watched'] = 1 if data['plays'] > 0 else 0 - if 'uniqueid' in data: - data['ids'] = data.pop('uniqueid') - elif 'imdbnumber' in data: - id = data.pop('imdbnumber') - data['ids'] = utilities.guessBestTraktId(id, type)[0] + data["plays"] = data.pop("playcount") + data["rating"] = ( + data["userrating"] if "userrating" in data and data["userrating"] > 0 else 0 + ) + data["collected"] = 1 # this is in our kodi so it should be collected + data["watched"] = 1 if data["plays"] > 0 else 0 + if "uniqueid" in data: + data["ids"] = data.pop("uniqueid") + elif "imdbnumber" in data: + id = data.pop("imdbnumber") + data["ids"] = utilities.guessBestTraktId(id, type)[0] else: - logger.debug('kodiRpcToTraktMediaObject(): No uniqueid found') - del data['label'] + logger.debug("kodiRpcToTraktMediaObject(): No uniqueid found") + del data["label"] return data else: - logger.debug('kodiRpcToTraktMediaObject() No valid type') + logger.debug("kodiRpcToTraktMediaObject() No valid type") return -def kodiRpcToTraktMediaObjects(data, mode='collected'): - if 'tvshows' in data: - shows = data['tvshows'] +def kodiRpcToTraktMediaObjects(data, mode="collected"): + if "tvshows" in data: + shows = data["tvshows"] # reformat show array for show in shows: - kodiRpcToTraktMediaObject('show', show, mode) + kodiRpcToTraktMediaObject("show", show, mode) return shows - elif 'episodes' in data: + elif "episodes" in data: a_episodes = {} seasons = [] - for episode in data['episodes']: - while not episode['season'] in a_episodes: - s_no = episode['season'] + for episode in data["episodes"]: + while episode["season"] not in a_episodes: + s_no = episode["season"] a_episodes[s_no] = [] - s_no = episode['season'] - episodeObject = kodiRpcToTraktMediaObject('episode', episode, mode) + s_no = episode["season"] + episodeObject = kodiRpcToTraktMediaObject("episode", episode, mode) if episodeObject: a_episodes[s_no].append(episodeObject) for episode in a_episodes: - seasons.append( - {'number': episode, 'episodes': a_episodes[episode]}) + seasons.append({"number": episode, "episodes": a_episodes[episode]}) return seasons - elif 'movies' in data: - movies = data['movies'] + elif "movies" in data: + movies = data["movies"] kodi_movies = [] # reformat movie array for movie in movies: - movieObject = kodiRpcToTraktMediaObject('movie', movie, mode) + movieObject = kodiRpcToTraktMediaObject("movie", movie, mode) if movieObject: kodi_movies.append(movieObject) return kodi_movies else: - logger.debug( - 'kodiRpcToTraktMediaObjects() No valid key found in rpc data') + logger.debug("kodiRpcToTraktMediaObjects() No valid key found in rpc data") return def getShowDetailsFromKodi(showID, fields): - result = kodiJsonRequest({'jsonrpc': '2.0', 'method': 'VideoLibrary.GetTVShowDetails', 'params': { - 'tvshowid': showID, 'properties': fields}, 'id': 1}) + result = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetTVShowDetails", + "params": {"tvshowid": showID, "properties": fields}, + "id": 1, + } + ) logger.debug("getShowDetailsFromKodi(): %s" % str(result)) if not result: @@ -262,16 +286,21 @@ def getShowDetailsFromKodi(showID, fields): return None try: - return result['tvshowdetails'] + return result["tvshowdetails"] except KeyError: - logger.debug( - "getShowDetailsFromKodi(): KeyError: result['tvshowdetails']") + logger.debug("getShowDetailsFromKodi(): KeyError: result['tvshowdetails']") return None def getSeasonDetailsFromKodi(seasonID, fields): - result = kodiJsonRequest({'jsonrpc': '2.0', 'method': 'VideoLibrary.GetSeasonDetails', 'params': { - 'seasonid': seasonID, 'properties': fields}, 'id': 1}) + result = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetSeasonDetails", + "params": {"seasonid": seasonID, "properties": fields}, + "id": 1, + } + ) logger.debug("getSeasonDetailsFromKodi(): %s" % str(result)) if not result: @@ -279,52 +308,65 @@ def getSeasonDetailsFromKodi(seasonID, fields): return None try: - return result['seasondetails'] + return result["seasondetails"] except KeyError: - logger.debug( - "getSeasonDetailsFromKodi(): KeyError: result['seasondetails']") + logger.debug("getSeasonDetailsFromKodi(): KeyError: result['seasondetails']") return None + # get a single episode from kodi given the id def getEpisodeDetailsFromKodi(libraryId, fields): - result = kodiJsonRequest({'jsonrpc': '2.0', 'method': 'VideoLibrary.GetEpisodeDetails', 'params': { - 'episodeid': libraryId, 'properties': fields}, 'id': 1}) + result = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetEpisodeDetails", + "params": {"episodeid": libraryId, "properties": fields}, + "id": 1, + } + ) logger.debug("getEpisodeDetailsFromKodi(): %s" % str(result)) if not result: - logger.debug( - "getEpisodeDetailsFromKodi(): Result from Kodi was empty.") + logger.debug("getEpisodeDetailsFromKodi(): Result from Kodi was empty.") return None show_data = getShowDetailsFromKodi( - result['episodedetails']['tvshowid'], ['year', 'uniqueid', 'imdbnumber']) + result["episodedetails"]["tvshowid"], ["year", "uniqueid", "imdbnumber"] + ) if not show_data: logger.debug( - "getEpisodeDetailsFromKodi(): Result from getShowDetailsFromKodi() was empty.") + "getEpisodeDetailsFromKodi(): Result from getShowDetailsFromKodi() was empty." + ) return None - if 'uniqueid' in show_data: - result['episodedetails']['show_ids'] = show_data['uniqueid'] - elif 'imdbnumber' in show_data: - result['episodedetails']['show_ids'] = show_data['imdbnumber'] - result['episodedetails']['year'] = show_data['year'] + if "uniqueid" in show_data: + result["episodedetails"]["show_ids"] = show_data["uniqueid"] + elif "imdbnumber" in show_data: + result["episodedetails"]["show_ids"] = show_data["imdbnumber"] + result["episodedetails"]["year"] = show_data["year"] try: - return result['episodedetails'] + return result["episodedetails"] except KeyError: - logger.debug( - "getEpisodeDetailsFromKodi(): KeyError: result['episodedetails']") + logger.debug("getEpisodeDetailsFromKodi(): KeyError: result['episodedetails']") return None + # get a single movie from kodi given the id def getMovieDetailsFromKodi(libraryId, fields): - result = kodiJsonRequest({'jsonrpc': '2.0', 'method': 'VideoLibrary.GetMovieDetails', 'params': { - 'movieid': libraryId, 'properties': fields}, 'id': 1}) + result = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetMovieDetails", + "params": {"movieid": libraryId, "properties": fields}, + "id": 1, + } + ) logger.debug("getMovieDetailsFromKodi(): %s" % str(result)) if not result: @@ -332,61 +374,114 @@ def getMovieDetailsFromKodi(libraryId, fields): return None try: - return result['moviedetails'] + return result["moviedetails"] except KeyError: - logger.debug( - "getMovieDetailsFromKodi(): KeyError: result['moviedetails']") + logger.debug("getMovieDetailsFromKodi(): KeyError: result['moviedetails']") return None def checkAndConfigureProxy(): - proxyActive = kodiJsonRequest({'jsonrpc': '2.0', "method": "Settings.GetSettingValue", "params": { - "setting": "network.usehttpproxy"}, 'id': 1})['value'] - proxyType = kodiJsonRequest({'jsonrpc': '2.0', "method": "Settings.GetSettingValue", "params": { - "setting": "network.httpproxytype"}, 'id': 1})['value'] - proxyOverride = getSettingAsBool('proxy_override') + proxyActive = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "Settings.GetSettingValue", + "params": {"setting": "network.usehttpproxy"}, + "id": 1, + } + )["value"] + proxyType = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "Settings.GetSettingValue", + "params": {"setting": "network.httpproxytype"}, + "id": 1, + } + )["value"] + proxyOverride = getSettingAsBool("proxy_override") if proxyOverride: - proxyURL = getSetting('proxy_uri') - proxyPort = getSetting('proxy_port') - proxyUsername = getSetting('proxy_username') - proxyPassword = getSetting('proxy_password') + proxyURL = getSetting("proxy_uri") + proxyPort = getSetting("proxy_port") + proxyUsername = getSetting("proxy_username") + proxyPassword = getSetting("proxy_password") elif proxyActive and (proxyType == 0): - proxyURL = kodiJsonRequest({'jsonrpc': '2.0', "method": "Settings.GetSettingValue", "params": { - "setting": "network.httpproxyserver"}, 'id': 1})['value'] - proxyPort = str(kodiJsonRequest({'jsonrpc': '2.0', "method": "Settings.GetSettingValue", "params": { - "setting": "network.httpproxyport"}, 'id': 1})['value']) - proxyUsername = kodiJsonRequest({'jsonrpc': '2.0', "method": "Settings.GetSettingValue", "params": { - "setting": "network.httpproxyusername"}, 'id': 1})['value'] - proxyPassword = kodiJsonRequest({'jsonrpc': '2.0', "method": "Settings.GetSettingValue", "params": { - "setting": "network.httpproxypassword"}, 'id': 1})['value'] + proxyURL = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "Settings.GetSettingValue", + "params": {"setting": "network.httpproxyserver"}, + "id": 1, + } + )["value"] + proxyPort = str( + kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "Settings.GetSettingValue", + "params": {"setting": "network.httpproxyport"}, + "id": 1, + } + )["value"] + ) + proxyUsername = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "Settings.GetSettingValue", + "params": {"setting": "network.httpproxyusername"}, + "id": 1, + } + )["value"] + proxyPassword = kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "Settings.GetSettingValue", + "params": {"setting": "network.httpproxypassword"}, + "id": 1, + } + )["value"] else: return None - if proxyUsername and proxyPassword and proxyURL and proxyPort: regexUrl = re.compile(REGEX_URL) matchURL = regexUrl.search(proxyURL) if matchURL: - return matchURL.group(1) + proxyUsername + ':' + proxyPassword + '@' + matchURL.group(2) + ':' + proxyPort + return ( + matchURL.group(1) + + proxyUsername + + ":" + + proxyPassword + + "@" + + matchURL.group(2) + + ":" + + proxyPort + ) else: - return 'http://' + proxyUsername + ':' + proxyPassword + '@' + proxyURL + ':' + proxyPort + return ( + "http://" + + proxyUsername + + ":" + + proxyPassword + + "@" + + proxyURL + + ":" + + proxyPort + ) elif proxyURL and proxyPort: regexUrl = re.compile(REGEX_URL) hasScheme = regexUrl.search(proxyURL) if hasScheme: - return proxyURL + ':' + proxyPort + return proxyURL + ":" + proxyPort else: - return 'http://' + proxyURL + ':' + proxyPort + return "http://" + proxyURL + ":" + proxyPort else: return None - return None def getMediaType(): - listType = xbmc.getInfoLabel('ListItem.DBTYPE') + listType = xbmc.getInfoLabel("ListItem.DBTYPE") xbmc.log("list item type: %s" % listType, xbmc.LOGINFO) @@ -403,59 +498,71 @@ def getMediaType(): def getInfoLabelDetails(result): - type = result['item']['type'] - data = {'action': 'started'} + type = result["item"]["type"] + data = {"action": "started"} # check type of item - if 'id' not in result['item'] or type == 'channel': + if "id" not in result["item"] or type == "channel": # do a deeper check to see if we have enough data to perform scrobbles logger.debug( - "getInfoLabelDetails - Started playing a non-library file, checking available data.") - season = int(xbmc.getInfoLabel('VideoPlayer.Season') or '-1') - episode = int(xbmc.getInfoLabel('VideoPlayer.Episode') or '-1') - showtitle = (xbmc.getInfoLabel('VideoPlayer.TVShowTitle') - or xbmc.getInfoLabel('VideoPlayer.Title')) - title = xbmc.getInfoLabel('VideoPlayer.EpisodeName') - year = (xbmc.getInfoLabel('VideoPlayer.Year') - or utilities.regex_year(showtitle)[1]) - video_ids = xbmcgui.Window(10000).getProperty('script.trakt.ids') + "getInfoLabelDetails - Started playing a non-library file, checking available data." + ) + season = int(xbmc.getInfoLabel("VideoPlayer.Season") or "-1") + episode = int(xbmc.getInfoLabel("VideoPlayer.Episode") or "-1") + showtitle = xbmc.getInfoLabel("VideoPlayer.TVShowTitle") or xbmc.getInfoLabel( + "VideoPlayer.Title" + ) + title = xbmc.getInfoLabel("VideoPlayer.EpisodeName") + year = ( + xbmc.getInfoLabel("VideoPlayer.Year") or utilities.regex_year(showtitle)[1] + ) + video_ids = xbmcgui.Window(10000).getProperty("script.trakt.ids") if video_ids: - data['video_ids'] = json.loads(video_ids) - logger.debug("getInfoLabelDetails info - ids: %s, showtitle: %s, Year: %s, Season: %s, Episode: %s" % - (video_ids, showtitle, year, season, episode)) + data["video_ids"] = json.loads(video_ids) + logger.debug( + "getInfoLabelDetails info - ids: %s, showtitle: %s, Year: %s, Season: %s, Episode: %s" + % (video_ids, showtitle, year, season, episode) + ) if season >= 0 and episode > 0 and (showtitle or video_ids): # we have season, episode and either a show title or video_ids, can scrobble this as an episode - type = 'episode' - data['type'] = 'episode' - data['season'] = season - data['episode'] = episode - data['showtitle'] = showtitle - data['title'] = (title or showtitle) + type = "episode" + data["type"] = "episode" + data["season"] = season + data["episode"] = episode + data["showtitle"] = showtitle + data["title"] = title or showtitle if year.isdigit(): - data['year'] = int(year) - logger.debug("getInfoLabelDetails - Playing a non-library 'episode' - %s - S%02dE%02d - %s." % - (data['showtitle'], data['season'], data['episode'], data['title'])) + data["year"] = int(year) + logger.debug( + "getInfoLabelDetails - Playing a non-library 'episode' - %s - S%02dE%02d - %s." + % (data["showtitle"], data["season"], data["episode"], data["title"]) + ) elif (year or video_ids) and season < 0 and not title: # we have a year or video_id and no season/showtitle info, enough for a movie - type = 'movie' - data['type'] = 'movie' + type = "movie" + data["type"] = "movie" if year.isdigit(): - data['year'] = int(year) - data['title'] = utilities.regex_year(showtitle)[0] or showtitle - logger.debug("getInfoLabelDetails - Playing a non-library 'movie' - %s (%s)." % - (data['title'], data.get('year', 'NaN'))) - elif (showtitle or title): + data["year"] = int(year) + data["title"] = utilities.regex_year(showtitle)[0] or showtitle + logger.debug( + "getInfoLabelDetails - Playing a non-library 'movie' - %s (%s)." + % (data["title"], data.get("year", "NaN")) + ) + elif showtitle or title: title, season, episode = utilities.regex_tvshow(title) if season < 0 and episode < 0: title, season, episode = utilities.regex_tvshow(showtitle) - data['type'] = 'episode' - data['season'] = season - data['episode'] = episode - data['title'] = data['showtitle'] = (title or showtitle) - logger.debug("getInfoLabelDetails - Title: %s, showtitle: %s, season: %d, episode: %d" % - (title, showtitle, season, episode)) + data["type"] = "episode" + data["season"] = season + data["episode"] = episode + data["title"] = data["showtitle"] = title or showtitle + logger.debug( + "getInfoLabelDetails - Title: %s, showtitle: %s, season: %d, episode: %d" + % (title, showtitle, season, episode) + ) else: logger.debug( - "getInfoLabelDetails - Non-library file, not enough data for scrobbling, skipping.") + "getInfoLabelDetails - Non-library file, not enough data for scrobbling, skipping." + ) return {}, {} return type, data diff --git a/resources/lib/rating.py b/resources/lib/rating.py index 8b8fab09..ac067e5f 100644 --- a/resources/lib/rating.py +++ b/resources/lib/rating.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- """Module used to launch rating dialogues and send ratings to Trakt""" -import xbmc import xbmcaddon import xbmcgui from resources.lib import utilities @@ -51,7 +50,7 @@ def rateMedia(media_type, itemsToRate, unrate=False, rating=None): if summary_info['user']['ratings']['rating'] > 0: rating = 0 - if not rating is None: + if rating is not None: logger.debug("'%s' is being unrated." % s) __rateOnTrakt(rating, media_type, summary_info, unrate=True) else: diff --git a/resources/lib/script.py b/resources/lib/script.py index 4e56464d..5b6c1443 100644 --- a/resources/lib/script.py +++ b/resources/lib/script.py @@ -264,7 +264,7 @@ def run(): for e in result['episodes']: data['title'] = e['showtitle'] season = str(e['season']) - if not season in s: + if season not in s: s[season] = [] if e['playcount'] == 0: s[season].append(e['episode']) @@ -375,7 +375,7 @@ def run(): for e in result['episodes']: data['title'] = e['showtitle'] season = str(e['season']) - if not season in s: + if season not in s: s[season] = [] if e['playcount'] == 0: s[season].append(e['episode']) diff --git a/resources/lib/scrobbler.py b/resources/lib/scrobbler.py index 4951b19e..72792e6d 100644 --- a/resources/lib/scrobbler.py +++ b/resources/lib/scrobbler.py @@ -12,7 +12,7 @@ logger = logging.getLogger(__name__) -class Scrobbler(): +class Scrobbler: traktapi = None isPlaying = False isPaused = False @@ -34,7 +34,7 @@ def __init__(self, api): self.traktapi = api def _currentEpisode(self, watchedPercent, episodeCount): - split = (100 / episodeCount) + split = 100 / episodeCount for i in range(episodeCount - 1, 0, -1): if watchedPercent >= (i * split): return i @@ -46,106 +46,157 @@ def transitionCheck(self, isSeek=False): if self.isPlaying: t = xbmc.Player().getTime() - l = xbmc.PlayList(xbmc.PLAYLIST_VIDEO).getposition() + position = xbmc.PlayList(xbmc.PLAYLIST_VIDEO).getposition() if self.isPVR: if self.stopScrobbler: self.stopScrobbler = False self.lastMPCheck = time.time() + 600 # 10min transition sleep - self.__scrobble('stop') + self.__scrobble("stop") return - self.watchedTime = (utilities._to_sec( - xbmc.getInfoLabel('PVR.EpgEventElapsedTime(hh:mm:ss)'))) - self.videoDuration = int(utilities._to_sec( - xbmc.getInfoLabel('PVR.EpgEventDuration(hh:mm:ss)'))) - elif self.playlistIndex == l: + self.watchedTime = utilities._to_sec( + xbmc.getInfoLabel("PVR.EpgEventElapsedTime(hh:mm:ss)") + ) + self.videoDuration = int( + utilities._to_sec( + xbmc.getInfoLabel("PVR.EpgEventDuration(hh:mm:ss)") + ) + ) + elif self.playlistIndex == position: self.watchedTime = t else: logger.debug( - "Current playlist item changed! Not updating time! (%d -> %d)" % (self.playlistIndex, l)) + "Current playlist item changed! Not updating time! (%d -> %d)" + % (self.playlistIndex, position) + ) # do transition check every minute if (time.time() > (self.lastMPCheck + 60)) or isSeek: self.lastMPCheck = time.time() watchedPercent = self.__calculateWatchedPercent() - if 'id' in self.curVideo and self.isMultiPartEpisode: + if "id" in self.curVideo and self.isMultiPartEpisode: epIndex = self._currentEpisode( - watchedPercent, self.curVideo['multi_episode_count']) + watchedPercent, self.curVideo["multi_episode_count"] + ) if self.curMPEpisode != epIndex: - response = self.__scrobble('stop') + response = self.__scrobble("stop") if response is not None: - logger.debug("Scrobble response: %s" % - str(response)) + logger.debug("Scrobble response: %s" % str(response)) self.videosToRate.append(self.curVideoInfo) # update current information self.curMPEpisode = epIndex - self.curVideoInfo = kodiUtilities.kodiRpcToTraktMediaObject('episode', kodiUtilities.getEpisodeDetailsFromKodi( - self.curVideo['multi_episode_data'][self.curMPEpisode], ['showtitle', 'season', 'episode', 'tvshowid', 'uniqueid', 'file', 'playcount'])) + self.curVideoInfo = kodiUtilities.kodiRpcToTraktMediaObject( + "episode", + kodiUtilities.getEpisodeDetailsFromKodi( + self.curVideo["multi_episode_data"][ + self.curMPEpisode + ], + [ + "showtitle", + "season", + "episode", + "tvshowid", + "uniqueid", + "file", + "playcount", + ], + ), + ) logger.debug( - "Multi episode transition - call start for next episode") - response = self.__scrobble('start') + "Multi episode transition - call start for next episode" + ) + response = self.__scrobble("start") self.__preFetchUserRatings(response) elif self.isPVR: activePlayers = kodiUtilities.kodiJsonRequest( - {"jsonrpc": "2.0", "method": "Player.GetActivePlayers", "id": 1}) - logger.debug("Scrobble - activePlayers: %s" % - activePlayers) - playerId = int(activePlayers[0]['playerid']) - logger.debug( - "Scrobble - Doing Player.GetItem kodiJsonRequest") + {"jsonrpc": "2.0", "method": "Player.GetActivePlayers", "id": 1} + ) + logger.debug("Scrobble - activePlayers: %s" % activePlayers) + playerId = int(activePlayers[0]["playerid"]) + logger.debug("Scrobble - Doing Player.GetItem kodiJsonRequest") result = kodiUtilities.kodiJsonRequest( - {'jsonrpc': '2.0', 'method': 'Player.GetItem', 'params': {'playerid': playerId}, 'id': 1}) + { + "jsonrpc": "2.0", + "method": "Player.GetItem", + "params": {"playerid": playerId}, + "id": 1, + } + ) if result: logger.debug("Scrobble - %s" % result) - type, curVideo = kodiUtilities.getInfoLabelDetails( - result) + type, curVideo = kodiUtilities.getInfoLabelDetails(result) if curVideo != self.curVideo: - response = self.__scrobble('stop') + response = self.__scrobble("stop") if response is not None: - logger.debug("Scrobble response: %s" % - str(response)) + logger.debug("Scrobble response: %s" % str(response)) logger.debug("Scrobble PVR transition") # update current information self.curVideo = curVideo - if utilities.isMovie(self.curVideo['type']): - if 'title' in self.curVideo and 'year' in self.curVideo: + if utilities.isMovie(self.curVideo["type"]): + if ( + "title" in self.curVideo + and "year" in self.curVideo + ): self.curVideoInfo = { - 'title': self.curVideo['title'], 'year': self.curVideo['year']} + "title": self.curVideo["title"], + "year": self.curVideo["year"], + } else: logger.debug( - "Scrobble Couldn't set curVideoInfo for movie type") + "Scrobble Couldn't set curVideoInfo for movie type" + ) logger.debug( - "Scrobble Movie type, curVideoInfo: %s" % self.curVideoInfo) - - elif utilities.isEpisode(self.curVideo['type']): - if 'title' in self.curVideo and 'season' in self.curVideo and 'episode' in self.curVideo: - self.curVideoInfo = {'title': self.curVideo['title'], 'season': self.curVideo['season'], - 'number': self.curVideo['episode']} + "Scrobble Movie type, curVideoInfo: %s" + % self.curVideoInfo + ) + + elif utilities.isEpisode(self.curVideo["type"]): + if ( + "title" in self.curVideo + and "season" in self.curVideo + and "episode" in self.curVideo + ): + self.curVideoInfo = { + "title": self.curVideo["title"], + "season": self.curVideo["season"], + "number": self.curVideo["episode"], + } title, year = utilities.regex_year( - self.curVideo['showtitle']) + self.curVideo["showtitle"] + ) if not year: self.traktShowSummary = { - 'title': self.curVideo['showtitle']} + "title": self.curVideo["showtitle"] + } else: self.traktShowSummary = { - 'title': title, 'year': year} - - if 'year' in self.curVideo: - self.traktShowSummary['year'] = self.curVideo['year'] + "title": title, + "year": year, + } + + if "year" in self.curVideo: + self.traktShowSummary[ + "year" + ] = self.curVideo["year"] else: logger.debug( - "Scrobble Couldn't set curVideoInfo/traktShowSummary for episode type") + "Scrobble Couldn't set curVideoInfo/traktShowSummary for episode type" + ) logger.debug( - "Scrobble Episode type, curVideoInfo: %s" % self.curVideoInfo) + "Scrobble Episode type, curVideoInfo: %s" + % self.curVideoInfo + ) logger.debug( - "Scrobble Episode type, traktShowSummary: %s" % self.traktShowSummary) - response = self.__scrobble('start') + "Scrobble Episode type, traktShowSummary: %s" + % self.traktShowSummary + ) + response = self.__scrobble("start") elif isSeek: - self.__scrobble('start') + self.__scrobble("start") def playbackStarted(self, data): logger.debug("playbackStarted(data: %s)" % data) @@ -155,13 +206,16 @@ def playbackStarted(self, data): self.curVideoInfo = None self.videosToRate = [] - if not kodiUtilities.getSettingAsBool('scrobble_fallback') and 'id' not in self.curVideo and 'video_ids' not in self.curVideo: - logger.debug('Aborting scrobble to avoid fallback: %s' % - (self.curVideo)) + if ( + not kodiUtilities.getSettingAsBool("scrobble_fallback") + and "id" not in self.curVideo + and "video_ids" not in self.curVideo + ): + logger.debug("Aborting scrobble to avoid fallback: %s" % (self.curVideo)) return - if 'type' in self.curVideo: - logger.debug("Watching: %s" % self.curVideo['type']) + if "type" in self.curVideo: + logger.debug("Watching: %s" % self.curVideo["type"]) if not xbmc.Player().isPlayingVideo(): logger.debug("Suddenly stopped watching item") return @@ -169,14 +223,19 @@ def playbackStarted(self, data): xbmc.sleep(1000) try: self.isPVR = xbmc.getCondVisibility( - 'Pvr.IsPlayingTv') | xbmc.Player().getPlayingFile().startswith('pvr://') + "Pvr.IsPlayingTv" + ) | xbmc.Player().getPlayingFile().startswith("pvr://") self.watchedTime = xbmc.Player().getTime() self.videoDuration = 0 if self.isPVR: - self.watchedTime = (utilities._to_sec( - xbmc.getInfoLabel('PVR.EpgEventElapsedTime(hh:mm:ss)'))) - self.videoDuration = int(utilities._to_sec( - xbmc.getInfoLabel('PVR.EpgEventDuration(hh:mm:ss)'))) + self.watchedTime = utilities._to_sec( + xbmc.getInfoLabel("PVR.EpgEventElapsedTime(hh:mm:ss)") + ) + self.videoDuration = int( + utilities._to_sec( + xbmc.getInfoLabel("PVR.EpgEventDuration(hh:mm:ss)") + ) + ) else: self.videoDuration = xbmc.Player().getTotalTime() except Exception as e: @@ -185,128 +244,200 @@ def playbackStarted(self, data): return if self.videoDuration == 0: - if utilities.isMovie(self.curVideo['type']): + if utilities.isMovie(self.curVideo["type"]): self.videoDuration = 90 - elif utilities.isEpisode(self.curVideo['type']): + elif utilities.isEpisode(self.curVideo["type"]): self.videoDuration = 30 else: self.videoDuration = 1 - self.playlistIndex = xbmc.PlayList( - xbmc.PLAYLIST_VIDEO).getposition() + self.playlistIndex = xbmc.PlayList(xbmc.PLAYLIST_VIDEO).getposition() self.isMultiPartEpisode = False - if utilities.isMovie(self.curVideo['type']): - if 'id' in self.curVideo: - self.curVideoInfo = kodiUtilities.kodiRpcToTraktMediaObject('movie', kodiUtilities.getMovieDetailsFromKodi( - self.curVideo['id'], ['uniqueid', 'imdbnumber', 'title', 'year', 'file', 'lastplayed', 'playcount'])) - elif 'video_ids' in self.curVideo: - self.curVideoInfo = {'ids': self.curVideo['video_ids']} - elif 'title' in self.curVideo and 'year' in self.curVideo: + if utilities.isMovie(self.curVideo["type"]): + if "id" in self.curVideo: + self.curVideoInfo = kodiUtilities.kodiRpcToTraktMediaObject( + "movie", + kodiUtilities.getMovieDetailsFromKodi( + self.curVideo["id"], + [ + "uniqueid", + "imdbnumber", + "title", + "year", + "file", + "lastplayed", + "playcount", + ], + ), + ) + elif "video_ids" in self.curVideo: + self.curVideoInfo = {"ids": self.curVideo["video_ids"]} + elif "title" in self.curVideo and "year" in self.curVideo: self.curVideoInfo = { - 'title': self.curVideo['title'], 'year': self.curVideo['year']} + "title": self.curVideo["title"], + "year": self.curVideo["year"], + } else: logger.debug("Couldn't set curVideoInfo for movie type") - logger.debug("Movie type, curVideoInfo: %s" % - self.curVideoInfo) - - elif utilities.isEpisode(self.curVideo['type']): - if 'id' in self.curVideo: - episodeDetailsKodi = kodiUtilities.getEpisodeDetailsFromKodi(self.curVideo['id'], [ - 'showtitle', 'season', 'episode', 'tvshowid', 'uniqueid', 'file', 'playcount']) - title, year = utilities.regex_year( - episodeDetailsKodi['showtitle']) + logger.debug("Movie type, curVideoInfo: %s" % self.curVideoInfo) + + elif utilities.isEpisode(self.curVideo["type"]): + if "id" in self.curVideo: + episodeDetailsKodi = kodiUtilities.getEpisodeDetailsFromKodi( + self.curVideo["id"], + [ + "showtitle", + "season", + "episode", + "tvshowid", + "uniqueid", + "file", + "playcount", + ], + ) + title, year = utilities.regex_year(episodeDetailsKodi["showtitle"]) if not year: self.traktShowSummary = { - 'title': episodeDetailsKodi['showtitle'], 'year': episodeDetailsKodi['year']} + "title": episodeDetailsKodi["showtitle"], + "year": episodeDetailsKodi["year"], + } else: - self.traktShowSummary = {'title': title, 'year': year} - if 'show_ids' in episodeDetailsKodi: - self.traktShowSummary['ids'] = episodeDetailsKodi['show_ids'] + self.traktShowSummary = {"title": title, "year": year} + if "show_ids" in episodeDetailsKodi: + self.traktShowSummary["ids"] = episodeDetailsKodi["show_ids"] self.curVideoInfo = kodiUtilities.kodiRpcToTraktMediaObject( - 'episode', episodeDetailsKodi) + "episode", episodeDetailsKodi + ) if not self.curVideoInfo: # getEpisodeDetailsFromKodi was empty logger.debug( - "Episode details from Kodi was empty, ID (%d) seems invalid, aborting further scrobbling of this episode." % self.curVideo['id']) + "Episode details from Kodi was empty, ID (%d) seems invalid, aborting further scrobbling of this episode." + % self.curVideo["id"] + ) self.curVideo = None self.isPlaying = False self.watchedTime = 0 return - elif 'video_ids' in self.curVideo and 'season' in self.curVideo and 'episode' in self.curVideo: + elif ( + "video_ids" in self.curVideo + and "season" in self.curVideo + and "episode" in self.curVideo + ): + self.curVideoInfo = { + "season": self.curVideo["season"], + "number": self.curVideo["episode"], + } + self.traktShowSummary = {"ids": self.curVideo["video_ids"]} + elif ( + "title" in self.curVideo + and "season" in self.curVideo + and "episode" in self.curVideo + ): self.curVideoInfo = { - 'season': self.curVideo['season'], 'number': self.curVideo['episode']} - self.traktShowSummary = {'ids': self.curVideo['video_ids']} - elif 'title' in self.curVideo and 'season' in self.curVideo and 'episode' in self.curVideo: - self.curVideoInfo = {'title': self.curVideo['title'], 'season': self.curVideo['season'], - 'number': self.curVideo['episode']} - - title, year = utilities.regex_year( - self.curVideo['showtitle']) + "title": self.curVideo["title"], + "season": self.curVideo["season"], + "number": self.curVideo["episode"], + } + + title, year = utilities.regex_year(self.curVideo["showtitle"]) if not year: - self.traktShowSummary = { - 'title': self.curVideo['showtitle']} + self.traktShowSummary = {"title": self.curVideo["showtitle"]} else: - self.traktShowSummary = {'title': title, 'year': year} + self.traktShowSummary = {"title": title, "year": year} - if 'year' in self.curVideo: - self.traktShowSummary['year'] = self.curVideo['year'] + if "year" in self.curVideo: + self.traktShowSummary["year"] = self.curVideo["year"] else: logger.debug( - "Couldn't set curVideoInfo/traktShowSummary for episode type") + "Couldn't set curVideoInfo/traktShowSummary for episode type" + ) - if 'multi_episode_count' in self.curVideo and self.curVideo['multi_episode_count'] > 1: + if ( + "multi_episode_count" in self.curVideo + and self.curVideo["multi_episode_count"] > 1 + ): self.isMultiPartEpisode = True - logger.debug("Episode type, curVideoInfo: %s" % - self.curVideoInfo) - logger.debug("Episode type, traktShowSummary: %s" % - self.traktShowSummary) + logger.debug("Episode type, curVideoInfo: %s" % self.curVideoInfo) + logger.debug( + "Episode type, traktShowSummary: %s" % self.traktShowSummary + ) self.isPlaying = True self.isPaused = False result = {} - if kodiUtilities.getSettingAsBool('scrobble_movie') or kodiUtilities.getSettingAsBool('scrobble_episode'): - result = self.__scrobble('start') - elif kodiUtilities.getSettingAsBool('rate_movie') and utilities.isMovie(self.curVideo['type']) and 'ids' in self.curVideoInfo: + if kodiUtilities.getSettingAsBool( + "scrobble_movie" + ) or kodiUtilities.getSettingAsBool("scrobble_episode"): + result = self.__scrobble("start") + elif ( + kodiUtilities.getSettingAsBool("rate_movie") + and utilities.isMovie(self.curVideo["type"]) + and "ids" in self.curVideoInfo + ): best_id, id_type = utilities.best_id( - self.curVideoInfo['ids'], self.curVideo['type']) - result = {'movie': self.traktapi.getMovieSummary( - best_id).to_dict()} - elif kodiUtilities.getSettingAsBool('rate_episode') and utilities.isEpisode(self.curVideo['type']) and 'ids' in self.traktShowSummary: + self.curVideoInfo["ids"], self.curVideo["type"] + ) + result = {"movie": self.traktapi.getMovieSummary(best_id).to_dict()} + elif ( + kodiUtilities.getSettingAsBool("rate_episode") + and utilities.isEpisode(self.curVideo["type"]) + and "ids" in self.traktShowSummary + ): best_id, id_type = utilities.best_id( - self.traktShowSummary['ids'], self.curVideo['type']) - result = {'show': self.traktapi.getShowSummary(best_id).to_dict(), - 'episode': self.traktapi.getEpisodeSummary(best_id, self.curVideoInfo['season'], - self.curVideoInfo['number']).to_dict()} - result['episode']['season'] = self.curVideoInfo['season'] - - if 'id' in self.curVideo: - if utilities.isMovie(self.curVideo['type']): - result['movie']['movieid'] = self.curVideo['id'] - elif utilities.isEpisode(self.curVideo['type']): - result['episode']['episodeid'] = self.curVideo['id'] + self.traktShowSummary["ids"], self.curVideo["type"] + ) + result = { + "show": self.traktapi.getShowSummary(best_id).to_dict(), + "episode": self.traktapi.getEpisodeSummary( + best_id, + self.curVideoInfo["season"], + self.curVideoInfo["number"], + ).to_dict(), + } + result["episode"]["season"] = self.curVideoInfo["season"] + + if "id" in self.curVideo: + if utilities.isMovie(self.curVideo["type"]): + result["movie"]["movieid"] = self.curVideo["id"] + elif utilities.isEpisode(self.curVideo["type"]): + result["episode"]["episodeid"] = self.curVideo["id"] self.__preFetchUserRatings(result) def __preFetchUserRatings(self, result): if result: - if utilities.isMovie(self.curVideo['type']) and kodiUtilities.getSettingAsBool('rate_movie'): + if utilities.isMovie( + self.curVideo["type"] + ) and kodiUtilities.getSettingAsBool("rate_movie"): # pre-get summary information, for faster rating dialog. logger.debug( - "Movie rating is enabled, pre-fetching summary information.") - self.curVideoInfo = result['movie'] - self.curVideoInfo['user'] = {'ratings': self.traktapi.getMovieRatingForUser( - result['movie']['ids']['trakt'], 'trakt')} - elif utilities.isEpisode(self.curVideo['type']) and kodiUtilities.getSettingAsBool('rate_episode'): + "Movie rating is enabled, pre-fetching summary information." + ) + self.curVideoInfo = result["movie"] + self.curVideoInfo["user"] = { + "ratings": self.traktapi.getMovieRatingForUser( + result["movie"]["ids"]["trakt"], "trakt" + ) + } + elif utilities.isEpisode( + self.curVideo["type"] + ) and kodiUtilities.getSettingAsBool("rate_episode"): # pre-get summary information, for faster rating dialog. logger.debug( - "Episode rating is enabled, pre-fetching summary information.") - self.curVideoInfo = result['episode'] - self.curVideoInfo['user'] = {'ratings': self.traktapi.getEpisodeRatingForUser(result['show']['ids']['trakt'], - self.curVideoInfo['season'], self.curVideoInfo['number'], 'trakt')} - logger.debug('Pre-Fetch result: %s; Info: %s' % - (result, self.curVideoInfo)) + "Episode rating is enabled, pre-fetching summary information." + ) + self.curVideoInfo = result["episode"] + self.curVideoInfo["user"] = { + "ratings": self.traktapi.getEpisodeRatingForUser( + result["show"]["ids"]["trakt"], + self.curVideoInfo["season"], + self.curVideoInfo["number"], + "trakt", + ) + } + logger.debug("Pre-Fetch result: %s; Info: %s" % (result, self.curVideoInfo)) def playbackResumed(self): if not self.isPlaying or self.isPVR: @@ -318,7 +449,7 @@ def playbackResumed(self): logger.debug("Resumed after: %s" % str(p)) self.pausedAt = 0 self.isPaused = False - self.__scrobble('start') + self.__scrobble("start") def playbackPaused(self): if not self.isPlaying or self.isPVR: @@ -328,7 +459,7 @@ def playbackPaused(self): logger.debug("Paused after: %s" % str(self.watchedTime)) self.isPaused = True self.pausedAt = time.time() - self.__scrobble('pause') + self.__scrobble("pause") def playbackSeek(self): if not self.isPlaying: @@ -350,10 +481,14 @@ def playbackEnded(self): self.isPlaying = False self.stopScrobbler = False if self.watchedTime != 0: - if 'type' in self.curVideo: - self.__scrobble('stop') + if "type" in self.curVideo: + self.__scrobble("stop") ratingCheck( - self.curVideo['type'], self.videosToRate, self.watchedTime, self.videoDuration) + self.curVideo["type"], + self.videosToRate, + self.watchedTime, + self.videoDuration, + ) self.watchedTime = 0 self.isPVR = False self.isMultiPartEpisode = False @@ -375,81 +510,104 @@ def __scrobble(self, status): return logger.debug("scrobble()") - scrobbleMovieOption = kodiUtilities.getSettingAsBool('scrobble_movie') - scrobbleEpisodeOption = kodiUtilities.getSettingAsBool( - 'scrobble_episode') + scrobbleMovieOption = kodiUtilities.getSettingAsBool("scrobble_movie") + scrobbleEpisodeOption = kodiUtilities.getSettingAsBool("scrobble_episode") watchedPercent = self.__calculateWatchedPercent() - if utilities.isMovie(self.curVideo['type']) and scrobbleMovieOption: + if utilities.isMovie(self.curVideo["type"]) and scrobbleMovieOption: response = self.traktapi.scrobbleMovie( - self.curVideoInfo, watchedPercent, status) + self.curVideoInfo, watchedPercent, status + ) if response is not None: self.__scrobbleNotification(response) logger.debug("Scrobble response: %s" % str(response)) return response else: - logger.debug("Failed to scrobble movie: %s | %s | %s" % - (self.curVideoInfo, watchedPercent, status)) + logger.debug( + "Failed to scrobble movie: %s | %s | %s" + % (self.curVideoInfo, watchedPercent, status) + ) - elif utilities.isEpisode(self.curVideo['type']) and scrobbleEpisodeOption: + elif utilities.isEpisode(self.curVideo["type"]) and scrobbleEpisodeOption: if self.isMultiPartEpisode: - logger.debug("Multi-part episode, scrobbling part %d of %d." % - (self.curMPEpisode + 1, self.curVideo['multi_episode_count'])) + logger.debug( + "Multi-part episode, scrobbling part %d of %d." + % (self.curMPEpisode + 1, self.curVideo["multi_episode_count"]) + ) adjustedDuration = int( - self.videoDuration / self.curVideo['multi_episode_count']) + self.videoDuration / self.curVideo["multi_episode_count"] + ) watchedPercent = ( - (self.watchedTime - (adjustedDuration * self.curMPEpisode)) / adjustedDuration) * 100 - - logger.debug("scrobble sending show object: %s" % - str(self.traktShowSummary)) - logger.debug("scrobble sending episode object: %s" % - str(self.curVideoInfo)) + (self.watchedTime - (adjustedDuration * self.curMPEpisode)) + / adjustedDuration + ) * 100 + + logger.debug( + "scrobble sending show object: %s" % str(self.traktShowSummary) + ) + logger.debug("scrobble sending episode object: %s" % str(self.curVideoInfo)) response = self.traktapi.scrobbleEpisode( - self.traktShowSummary, self.curVideoInfo, watchedPercent, status) + self.traktShowSummary, self.curVideoInfo, watchedPercent, status + ) - if (kodiUtilities.getSettingAsBool('scrobble_secondary_title')): + if kodiUtilities.getSettingAsBool("scrobble_secondary_title"): logger.debug( - '[traktPlayer] Setting is enabled to try secondary show title, if necessary.') + "[traktPlayer] Setting is enabled to try secondary show title, if necessary." + ) # If there is an empty response, the reason might be that the title we have isn't the actual show title, # but rather an alternative title. To handle this case, call the Trakt search function. if response is None: - logger.debug("Searching for show title: %s" % - self.traktShowSummary['title']) + logger.debug( + "Searching for show title: %s" % self.traktShowSummary["title"] + ) # This text query API is basically the same as searching on the website. Works with alternative # titles, unlike the scrobble function. newResp = self.traktapi.getTextQuery( - self.traktShowSummary['title'], "show", None) + self.traktShowSummary["title"], "show", None + ) if not newResp: - logger.debug( - "Empty Response from getTextQuery, giving up") + logger.debug("Empty Response from getTextQuery, giving up") else: logger.debug( - "Got Response from getTextQuery: %s" % str(newResp)) + "Got Response from getTextQuery: %s" % str(newResp) + ) # We got something back. Have to assume the first show found is the right one; if there's more than # one, there's no way to know which to use. Pull the primary title from the response (and the year, # just because it's there). - showObj = { - 'title': newResp[0].title, 'year': newResp[0].year} + showObj = {"title": newResp[0].title, "year": newResp[0].year} logger.debug( - "scrobble sending getTextQuery first show object: %s" % str(showObj)) + "scrobble sending getTextQuery first show object: %s" + % str(showObj) + ) # Now we can attempt the scrobble again, using the primary title this time. response = self.traktapi.scrobbleEpisode( - showObj, self.curVideoInfo, watchedPercent, status) + showObj, self.curVideoInfo, watchedPercent, status + ) if response is not None: # Don't scrobble incorrect episode, episode numbers can differ from database. ie Aired vs. DVD order. Use fuzzy logic to match episode title. - if self.isPVR and not utilities._fuzzyMatch(self.curVideoInfo['title'], response['episode']['title'], 50.0): - logger.debug("scrobble sending incorrect scrobbleEpisode stopping: %sx%s - %s != %s" % ( - self.curVideoInfo['season'], self.curVideoInfo['number'], self.curVideoInfo['title'], response['episode']['title'])) + if self.isPVR and not utilities._fuzzyMatch( + self.curVideoInfo["title"], response["episode"]["title"], 50.0 + ): + logger.debug( + "scrobble sending incorrect scrobbleEpisode stopping: %sx%s - %s != %s" + % ( + self.curVideoInfo["season"], + self.curVideoInfo["number"], + self.curVideoInfo["title"], + response["episode"]["title"], + ) + ) self.stopScrobbler = True self.__scrobbleNotification(response) logger.debug("Scrobble response: %s" % str(response)) return response else: - logger.debug("Failed to scrobble episode: %s | %s | %s | %s" % (self.traktShowSummary, - self.curVideoInfo, watchedPercent, - status)) + logger.debug( + "Failed to scrobble episode: %s | %s | %s | %s" + % (self.traktShowSummary, self.curVideoInfo, watchedPercent, status) + ) def __scrobbleNotification(self, info): if not self.curVideoInfo: @@ -457,5 +615,6 @@ def __scrobbleNotification(self, info): if kodiUtilities.getSettingAsBool("scrobble_notification"): s = utilities.getFormattedItemName( - self.curVideo['type'], info[self.curVideo['type']]) + self.curVideo["type"], info[self.curVideo["type"]] + ) kodiUtilities.notification(kodiUtilities.getString(32015), s) diff --git a/resources/lib/service.py b/resources/lib/service.py index 62627851..cf9e34d6 100644 --- a/resources/lib/service.py +++ b/resources/lib/service.py @@ -4,7 +4,6 @@ import xbmc import time import xbmcgui -import json import re import urllib.request import urllib.parse @@ -29,7 +28,7 @@ class traktService: dispatchQueue = sqlitequeue.SqliteQueue() def __init__(self): - threading.Thread.name = 'trakt' + threading.Thread.name = "trakt" def _dispatchQueue(self, data): logger.debug("Queuing for dispatch: %s" % data) @@ -38,46 +37,50 @@ def _dispatchQueue(self, data): def _dispatch(self, data): try: logger.debug("Dispatch: %s" % data) - action = data['action'] - if action == 'started': - del data['action'] + action = data["action"] + if action == "started": + del data["action"] self.scrobbler.playbackStarted(data) - elif action == 'ended' or action == 'stopped': + elif action == "ended" or action == "stopped": self.scrobbler.playbackEnded() - elif action == 'paused': + elif action == "paused": self.scrobbler.playbackPaused() - elif action == 'resumed': + elif action == "resumed": self.scrobbler.playbackResumed() - elif action == 'seek' or action == 'seekchapter': + elif action == "seek" or action == "seekchapter": self.scrobbler.playbackSeek() - elif action == 'scanFinished': - if kodiUtilities.getSettingAsBool('sync_on_update'): + elif action == "scanFinished": + if kodiUtilities.getSettingAsBool("sync_on_update"): logger.debug("Performing sync after library update.") self.doSync() - elif action == 'databaseCleaned': - if kodiUtilities.getSettingAsBool('sync_on_update') and (kodiUtilities.getSettingAsBool('clean_trakt_movies') or kodiUtilities.getSettingAsBool('clean_trakt_episodes')): + elif action == "databaseCleaned": + if kodiUtilities.getSettingAsBool("sync_on_update") and ( + kodiUtilities.getSettingAsBool("clean_trakt_movies") + or kodiUtilities.getSettingAsBool("clean_trakt_episodes") + ): logger.debug("Performing sync after library clean.") self.doSync() - elif action == 'markWatched': - del data['action'] + elif action == "markWatched": + del data["action"] self.doMarkWatched(data) - elif action == 'manualRating': - ratingData = data['ratingData'] + elif action == "manualRating": + ratingData = data["ratingData"] self.doManualRating(ratingData) - elif action == 'addtowatchlist': # add to watchlist - del data['action'] + elif action == "addtowatchlist": # add to watchlist + del data["action"] self.doAddToWatchlist(data) - elif action == 'manualSync': + elif action == "manualSync": if not self.syncThread.is_alive(): logger.debug("Performing a manual sync.") self.doSync( - manual=True, silent=data['silent'], library=data['library']) + manual=True, silent=data["silent"], library=data["library"] + ) else: logger.debug("There already is a sync in progress.") - elif action == 'settings': + elif action == "settings": kodiUtilities.showSettings() - elif action == 'auth_info': - xbmc.executebuiltin('Dialog.Close(all, true)') + elif action == "auth_info": + xbmc.executebuiltin("Dialog.Close(all, true)") # init traktapi class globals.traktapi = traktAPI(True) else: @@ -87,7 +90,7 @@ def _dispatch(self, data): logger.fatal(message) def run(self): - startup_delay = kodiUtilities.getSettingAsInt('startup_delay') + startup_delay = kodiUtilities.getSettingAsInt("startup_delay") if startup_delay: logger.debug("Delaying startup by %d seconds." % startup_delay) xbmc.sleep(startup_delay * 1000) @@ -136,113 +139,135 @@ def run(self): self.syncThread.join() def doManualRating(self, data): - action = data['action'] - media_type = data['media_type'] + action = data["action"] + media_type = data["media_type"] summaryInfo = None if not utilities.isValidMediaType(media_type): - logger.debug("doManualRating(): Invalid media type '%s' passed for manual %s." % ( - media_type, action)) + logger.debug( + "doManualRating(): Invalid media type '%s' passed for manual %s." + % (media_type, action) + ) return - if not data['action'] in ['rate', 'unrate']: + if data["action"] not in ["rate", "unrate"]: logger.debug("doManualRating(): Unknown action passed.") return - if 'video_ids' in data: - logger.debug("Getting data for manual %s of %s: video_ids: |%s| dbid: |%s|" % ( - action, media_type, data.get('video_ids'), data.get('dbid'))) + if "video_ids" in data: + logger.debug( + "Getting data for manual %s of %s: video_ids: |%s| dbid: |%s|" + % (action, media_type, data.get("video_ids"), data.get("dbid")) + ) - best_id, id_type = utilities.best_id(data['video_ids'], media_type) + best_id, id_type = utilities.best_id(data["video_ids"], media_type) else: - logger.debug("Getting data for manual %s of %s: video_id: |%s| dbid: |%s|" % ( - action, media_type, data.get('video_id'), data.get('dbid'))) + logger.debug( + "Getting data for manual %s of %s: video_id: |%s| dbid: |%s|" + % (action, media_type, data.get("video_id"), data.get("dbid")) + ) temp_ids, id_type = utilities.guessBestTraktId( - str(data['video_id']), media_type) + str(data["video_id"]), media_type + ) best_id = temp_ids[id_type] if not id_type: - logger.debug("doManualRating(): Unrecognized id_type: |%s|-|%s|." % - (media_type, best_id)) + logger.debug( + "doManualRating(): Unrecognized id_type: |%s|-|%s|." + % (media_type, best_id) + ) return ids = globals.traktapi.getIdLookup(best_id, id_type) if not ids: - logger.debug("doManualRating(): No Results for: |%s|-|%s|." % - (media_type, best_id)) + logger.debug( + "doManualRating(): No Results for: |%s|-|%s|." % (media_type, best_id) + ) return - trakt_id = dict(ids[0].keys)['trakt'] + trakt_id = dict(ids[0].keys)["trakt"] if utilities.isEpisode(media_type): summaryInfo = globals.traktapi.getEpisodeSummary( - trakt_id, data['season'], data['episode']) + trakt_id, data["season"], data["episode"] + ) userInfo = globals.traktapi.getEpisodeRatingForUser( - trakt_id, data['season'], data['episode'], 'trakt') + trakt_id, data["season"], data["episode"], "trakt" + ) elif utilities.isSeason(media_type): summaryInfo = globals.traktapi.getShowSummary(trakt_id) userInfo = globals.traktapi.getSeasonRatingForUser( - trakt_id, data['season'], 'trakt') + trakt_id, data["season"], "trakt" + ) elif utilities.isShow(media_type): summaryInfo = globals.traktapi.getShowSummary(trakt_id) - userInfo = globals.traktapi.getShowRatingForUser(trakt_id, 'trakt') + userInfo = globals.traktapi.getShowRatingForUser(trakt_id, "trakt") elif utilities.isMovie(media_type): summaryInfo = globals.traktapi.getMovieSummary(trakt_id) - userInfo = globals.traktapi.getMovieRatingForUser( - trakt_id, 'trakt') + userInfo = globals.traktapi.getMovieRatingForUser(trakt_id, "trakt") if summaryInfo is not None: summaryInfo = summaryInfo.to_dict() - summaryInfo['user'] = {'ratings': userInfo} + summaryInfo["user"] = {"ratings": userInfo} if utilities.isEpisode(media_type): - summaryInfo['season'] = data['season'] - summaryInfo['number'] = data['episode'] - summaryInfo['episodeid'] = data.get('dbid') + summaryInfo["season"] = data["season"] + summaryInfo["number"] = data["episode"] + summaryInfo["episodeid"] = data.get("dbid") elif utilities.isSeason(media_type): - summaryInfo['season'] = data['season'] + summaryInfo["season"] = data["season"] elif utilities.isMovie(media_type): - summaryInfo['movieid'] = data.get('dbid') + summaryInfo["movieid"] = data.get("dbid") elif utilities.isShow(media_type): - summaryInfo['tvshowid'] = data.get('dbid') + summaryInfo["tvshowid"] = data.get("dbid") - if action == 'rate': - if not 'rating' in data: + if action == "rate": + if "rating" not in data: rateMedia(media_type, [summaryInfo]) else: - rateMedia(media_type, [summaryInfo], rating=data['rating']) - elif action == 'unrate': + rateMedia(media_type, [summaryInfo], rating=data["rating"]) + elif action == "unrate": rateMedia(media_type, [summaryInfo], unrate=True) else: logger.debug( - "doManualRating(): Summary info was empty, possible problem retrieving data from Trakt.tv") + "doManualRating(): Summary info was empty, possible problem retrieving data from Trakt.tv" + ) def doAddToWatchlist(self, data): - media_type = data['media_type'] + media_type = data["media_type"] if utilities.isMovie(media_type): - best_id, id_type = utilities.best_id(data['ids'], media_type) + best_id, id_type = utilities.best_id(data["ids"], media_type) - summaryInfo = globals.traktapi.getMovieSummary( - best_id).to_dict() + summaryInfo = globals.traktapi.getMovieSummary(best_id).to_dict() if summaryInfo: s = utilities.getFormattedItemName(media_type, summaryInfo) logger.debug( - "doAddToWatchlist(): '%s' trying to add to users watchlist." % s) - params = {'movies': [summaryInfo]} + "doAddToWatchlist(): '%s' trying to add to users watchlist." % s + ) + params = {"movies": [summaryInfo]} logger.debug("doAddToWatchlist(): %s" % str(params)) result = globals.traktapi.addToWatchlist(params) if result: - kodiUtilities.notification( - kodiUtilities.getString(32165), s) + kodiUtilities.notification(kodiUtilities.getString(32165), s) else: - kodiUtilities.notification( - kodiUtilities.getString(32166), s) + kodiUtilities.notification(kodiUtilities.getString(32166), s) elif utilities.isEpisode(media_type): - summaryInfo = {'shows': [{'ids': data['ids'], - 'seasons': [{'number': data['season'], 'episodes': [{'number': data['number']}]}]}]} + summaryInfo = { + "shows": [ + { + "ids": data["ids"], + "seasons": [ + { + "number": data["season"], + "episodes": [{"number": data["number"]}], + } + ], + } + ] + } logger.debug("doAddToWatchlist(): %s" % str(summaryInfo)) s = utilities.getFormattedItemName(media_type, data) @@ -252,12 +277,15 @@ def doAddToWatchlist(self, data): else: kodiUtilities.notification(kodiUtilities.getString(32166), s) elif utilities.isSeason(media_type): - summaryInfo = {'shows': [{'ids': data['ids'], - 'seasons': [{'number': data['season']}]}]} + summaryInfo = { + "shows": [{"ids": data["ids"], "seasons": [{"number": data["season"]}]}] + } s = utilities.getFormattedItemName(media_type, data) - logger.debug("doAddToWatchlist(): '%s - Season %d' trying to add to users watchlist." - % (data['ids'], data['season'])) + logger.debug( + "doAddToWatchlist(): '%s - Season %d' trying to add to users watchlist." + % (data["ids"], data["season"]) + ) result = globals.traktapi.addToWatchlist(summaryInfo) if result: @@ -265,8 +293,7 @@ def doAddToWatchlist(self, data): else: kodiUtilities.notification(kodiUtilities.getString(32166), s) elif utilities.isShow(media_type): - summaryInfo = { - 'shows': [{'ids': data['ids']}]} + summaryInfo = {"shows": [{"ids": data["ids"]}]} s = utilities.getFormattedItemName(media_type, data) logger.debug("doAddToWatchlist(): %s" % str(summaryInfo)) @@ -277,32 +304,41 @@ def doAddToWatchlist(self, data): kodiUtilities.notification(kodiUtilities.getString(32166), s) def doMarkWatched(self, data): - - media_type = data['media_type'] + media_type = data["media_type"] if utilities.isMovie(media_type): - best_id, id_type = utilities.best_id(data['ids'], media_type) + best_id, id_type = utilities.best_id(data["ids"], media_type) - summaryInfo = globals.traktapi.getMovieSummary( - best_id).to_dict() + summaryInfo = globals.traktapi.getMovieSummary(best_id).to_dict() if summaryInfo: - if not summaryInfo['watched']: + if not summaryInfo["watched"]: s = utilities.getFormattedItemName(media_type, summaryInfo) logger.debug( - "doMarkWatched(): '%s' is not watched on Trakt, marking it as watched." % s) - params = {'movies': [summaryInfo]} + "doMarkWatched(): '%s' is not watched on Trakt, marking it as watched." + % s + ) + params = {"movies": [summaryInfo]} logger.debug("doMarkWatched(): %s" % str(params)) result = globals.traktapi.addToHistory(params) if result: - kodiUtilities.notification( - kodiUtilities.getString(32113), s) + kodiUtilities.notification(kodiUtilities.getString(32113), s) else: - kodiUtilities.notification( - kodiUtilities.getString(32114), s) + kodiUtilities.notification(kodiUtilities.getString(32114), s) elif utilities.isEpisode(media_type): - summaryInfo = {'shows': [{'ids': data['ids'], 'seasons': [ - {'number': data['season'], 'episodes': [{'number': data['number']}]}]}]} + summaryInfo = { + "shows": [ + { + "ids": data["ids"], + "seasons": [ + { + "number": data["season"], + "episodes": [{"number": data["number"]}], + } + ], + } + ] + } logger.debug("doMarkWatched(): %s" % str(summaryInfo)) s = utilities.getFormattedItemName(media_type, data) @@ -312,40 +348,54 @@ def doMarkWatched(self, data): else: kodiUtilities.notification(kodiUtilities.getString(32114), s) elif utilities.isSeason(media_type): - summaryInfo = {'shows': [{'ids': data['ids'], 'seasons': [ - {'number': data['season'], 'episodes': []}]}]} + summaryInfo = { + "shows": [ + { + "ids": data["ids"], + "seasons": [{"number": data["season"], "episodes": []}], + } + ] + } s = utilities.getFormattedItemName(media_type, data) - for ep in data['episodes']: - summaryInfo['shows'][0]['seasons'][0]['episodes'].append({ - 'number': ep}) + for ep in data["episodes"]: + summaryInfo["shows"][0]["seasons"][0]["episodes"].append({"number": ep}) - logger.debug("doMarkWatched(): '%s - Season %d' has %d episode(s) that are going to be marked as watched." % - (data['id'], data['season'], len(summaryInfo['shows'][0]['seasons'][0]['episodes']))) + logger.debug( + "doMarkWatched(): '%s - Season %d' has %d episode(s) that are going to be marked as watched." + % ( + data["id"], + data["season"], + len(summaryInfo["shows"][0]["seasons"][0]["episodes"]), + ) + ) self.addEpisodesToHistory(summaryInfo, s) elif utilities.isShow(media_type): - summaryInfo = {'shows': [{'ids': data['ids'], 'seasons': []}]} + summaryInfo = {"shows": [{"ids": data["ids"], "seasons": []}]} if summaryInfo: s = utilities.getFormattedItemName(media_type, data) - logger.debug('data: %s' % data) - for season in data['seasons']: + logger.debug("data: %s" % data) + for season in data["seasons"]: episodeJson = [] - for episode in data['seasons'][season]: - episodeJson.append({'number': episode}) - summaryInfo['shows'][0]['seasons'].append( - {'number': season, 'episodes': episodeJson}) + for episode in data["seasons"][season]: + episodeJson.append({"number": episode}) + summaryInfo["shows"][0]["seasons"].append( + {"number": season, "episodes": episodeJson} + ) self.addEpisodesToHistory(summaryInfo, s) def addEpisodesToHistory(self, summaryInfo, s): - if len(summaryInfo['shows'][0]['seasons'][0]['episodes']) > 0: + if len(summaryInfo["shows"][0]["seasons"][0]["episodes"]) > 0: logger.debug("doMarkWatched(): %s" % str(summaryInfo)) result = globals.traktapi.addToHistory(summaryInfo) if result: - kodiUtilities.notification(kodiUtilities.getString( - 32113), kodiUtilities.getString(32115) % (result['added']['episodes'], s)) + kodiUtilities.notification( + kodiUtilities.getString(32113), + kodiUtilities.getString(32115) % (result["added"]["episodes"], s), + ) else: kodiUtilities.notification(kodiUtilities.getString(32114), s) @@ -355,7 +405,6 @@ def doSync(self, manual=False, silent=False, library="all"): class syncThread(threading.Thread): - _isManual = False def __init__(self, isManual=False, runSilent=False, library="all"): @@ -366,34 +415,37 @@ def __init__(self, isManual=False, runSilent=False, library="all"): self._library = library def run(self): - sync = Sync(show_progress=self._isManual, run_silent=self._runSilent, - library=self._library, api=globals.traktapi) + sync = Sync( + show_progress=self._isManual, + run_silent=self._runSilent, + library=self._library, + api=globals.traktapi, + ) sync.sync() class traktMonitor(xbmc.Monitor): def __init__(self, *args, **kwargs): - self.action = kwargs['action'] + self.action = kwargs["action"] # xbmc.getCondVisibility('Library.IsScanningVideo') returns false when cleaning during update... self.scanning_video = False logger.debug("[traktMonitor] Initalized.") def onNotification(self, sender, method, data): # method looks like Other.NEXTUPWATCHEDSIGNAL - if method.split('.')[1].upper() != 'NEXTUPWATCHEDSIGNAL': + if method.split(".")[1].upper() != "NEXTUPWATCHEDSIGNAL": return - logger.debug('Callback received - Upnext skipped to the next episode') - data = {'action': 'ended'} + logger.debug("Callback received - Upnext skipped to the next episode") + data = {"action": "ended"} self.action(data) # called when database gets updated and return video or music to indicate which DB has been changed def onScanFinished(self, database): - if database == 'video': + if database == "video": self.scanning_video = False - logger.debug( - "[traktMonitor] onScanFinished(database: %s)" % database) - data = {'action': 'scanFinished'} + logger.debug("[traktMonitor] onScanFinished(database: %s)" % database) + data = {"action": "scanFinished"} self.action(data) # called when database update starts and return video or music to indicate which DB is being updated @@ -401,21 +453,21 @@ def onDatabaseScanStarted(self, database): if database == "video": self.scanning_video = True logger.debug( - "[traktMonitor] onDatabaseScanStarted(database: %s)" % database) + "[traktMonitor] onDatabaseScanStarted(database: %s)" % database + ) def onCleanFinished(self, database): if database == "video" and not self.scanning_video: # Ignore clean on update. - data = {'action': 'databaseCleaned'} + data = {"action": "databaseCleaned"} self.action(data) class traktPlayer(xbmc.Player): - _playing = False plIndex = None def __init__(self, *args, **kwargs): - self.action = kwargs['action'] + self.action = kwargs["action"] logger.debug("[traktPlayer] Initalized.") # called when kodi starts playing a file @@ -425,8 +477,9 @@ def onAVStarted(self): self.id = None # take the user start scrobble offset into account - scrobbleStartOffset = kodiUtilities.getSettingAsInt( - 'scrobble_start_offset')*60 + scrobbleStartOffset = ( + kodiUtilities.getSettingAsInt("scrobble_start_offset") * 60 + ) if scrobbleStartOffset > 0: waitFor = 10 waitedFor = 0 @@ -436,88 +489,136 @@ def onAVStarted(self): time.sleep(waitFor) if not self.isPlayingVideo(): logger.debug( - '[traktPlayer] Playback stopped before reaching the scrobble offset') + "[traktPlayer] Playback stopped before reaching the scrobble offset" + ) return # only do anything if we're playing a video if self.isPlayingVideo(): # get item data from json rpc activePlayers = kodiUtilities.kodiJsonRequest( - {"jsonrpc": "2.0", "method": "Player.GetActivePlayers", "id": 1}) + {"jsonrpc": "2.0", "method": "Player.GetActivePlayers", "id": 1} + ) logger.debug( - "[traktPlayer] onAVStarted() - activePlayers: %s" % activePlayers) - playerId = int(activePlayers[0]['playerid']) + "[traktPlayer] onAVStarted() - activePlayers: %s" % activePlayers + ) + playerId = int(activePlayers[0]["playerid"]) logger.debug( - "[traktPlayer] onAVStarted() - Doing Player.GetItem kodiJsonRequest") + "[traktPlayer] onAVStarted() - Doing Player.GetItem kodiJsonRequest" + ) result = kodiUtilities.kodiJsonRequest( - {'jsonrpc': '2.0', 'method': 'Player.GetItem', 'params': {'playerid': playerId}, 'id': 1}) + { + "jsonrpc": "2.0", + "method": "Player.GetItem", + "params": {"playerid": playerId}, + "id": 1, + } + ) if result: logger.debug("[traktPlayer] onAVStarted() - %s" % result) # check for exclusion _filename = None try: _filename = self.getPlayingFile() - except: + except: # noqa: E722 logger.debug( - "[traktPlayer] onAVStarted() - Exception trying to get playing filename, player suddenly stopped.") + "[traktPlayer] onAVStarted() - Exception trying to get playing filename, player suddenly stopped." + ) return if kodiUtilities.checkExclusion(_filename): logger.debug( - "[traktPlayer] onAVStarted() - '%s' is in exclusion settings, ignoring." % _filename) + "[traktPlayer] onAVStarted() - '%s' is in exclusion settings, ignoring." + % _filename + ) return - if (kodiUtilities.getSettingAsBool('scrobble_mythtv_pvr')): + if kodiUtilities.getSettingAsBool("scrobble_mythtv_pvr"): logger.debug( - '[traktPlayer] Setting is enabled to try scrobbling mythtv pvr recording, if necessary.') + "[traktPlayer] Setting is enabled to try scrobbling mythtv pvr recording, if necessary." + ) - self.type = result['item']['type'] - data = {'action': 'started'} + self.type = result["item"]["type"] + data = {"action": "started"} # check type of item - if 'id' not in result['item'] or self.type == 'channel': + if "id" not in result["item"] or self.type == "channel": # get non-library details by infolabel (ie. PVR, plugins, etc.) self.type, data = kodiUtilities.getInfoLabelDetails(result) - elif self.type == 'episode' or self.type == 'movie': + elif self.type == "episode" or self.type == "movie": # get library id - self.id = result['item']['id'] - data['id'] = self.id - data['type'] = self.type + self.id = result["item"]["id"] + data["id"] = self.id + data["type"] = self.type - if self.type == 'episode': + if self.type == "episode": logger.debug( - "[traktPlayer] onAVStarted() - Doing multi-part episode check.") - result = kodiUtilities.kodiJsonRequest({'jsonrpc': '2.0', 'method': 'VideoLibrary.GetEpisodeDetails', 'params': { - 'episodeid': self.id, 'properties': ['tvshowid', 'season', 'episode', 'file']}, 'id': 1}) + "[traktPlayer] onAVStarted() - Doing multi-part episode check." + ) + result = kodiUtilities.kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetEpisodeDetails", + "params": { + "episodeid": self.id, + "properties": [ + "tvshowid", + "season", + "episode", + "file", + ], + }, + "id": 1, + } + ) if result: - logger.debug( - "[traktPlayer] onAVStarted() - %s" % result) - tvshowid = int( - result['episodedetails']['tvshowid']) - season = int(result['episodedetails']['season']) - currentfile = result['episodedetails']['file'] - - result = kodiUtilities.kodiJsonRequest({'jsonrpc': '2.0', 'method': 'VideoLibrary.GetEpisodes', 'params': { - 'tvshowid': tvshowid, 'season': season, 'properties': ['episode', 'file'], 'sort': {'method': 'episode'}}, 'id': 1}) + logger.debug("[traktPlayer] onAVStarted() - %s" % result) + tvshowid = int(result["episodedetails"]["tvshowid"]) + season = int(result["episodedetails"]["season"]) + currentfile = result["episodedetails"]["file"] + + result = kodiUtilities.kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetEpisodes", + "params": { + "tvshowid": tvshowid, + "season": season, + "properties": ["episode", "file"], + "sort": {"method": "episode"}, + }, + "id": 1, + } + ) if result: logger.debug( - "[traktPlayer] onAVStarted() - %s" % result) + "[traktPlayer] onAVStarted() - %s" % result + ) # make sure episodes array exists in results - if 'episodes' in result: + if "episodes" in result: multi = [] - for i in range(result['limits']['start'], result['limits']['total']): - if currentfile == result['episodes'][i]['file']: + for i in range( + result["limits"]["start"], + result["limits"]["total"], + ): + if currentfile == result["episodes"][i]["file"]: multi.append( - result['episodes'][i]['episodeid']) + result["episodes"][i]["episodeid"] + ) if len(multi) > 1: - data['multi_episode_data'] = multi - data['multi_episode_count'] = len( - multi) + data["multi_episode_data"] = multi + data["multi_episode_count"] = len(multi) logger.debug( - "[traktPlayer] onAVStarted() - This episode is part of a multi-part episode.") + "[traktPlayer] onAVStarted() - This episode is part of a multi-part episode." + ) else: logger.debug( - "[traktPlayer] onAVStarted() - This is a single episode.") - elif (kodiUtilities.getSettingAsBool('scrobble_mythtv_pvr') and self.type == 'unknown' and result['item']['label']): + "[traktPlayer] onAVStarted() - This is a single episode." + ) + elif ( + kodiUtilities.getSettingAsBool("scrobble_mythtv_pvr") + and self.type == "unknown" + and result["item"]["label"] + ): # If we have label/id but no show type, then this might be a PVR recording. # DEBUG INFO: This code is useful when trying to figure out what info is available. Many of the fields @@ -536,30 +637,41 @@ def onAVStarted(self): # As of Kodi v17, many of the VideoPlayer labels are populated by the MythTV PVR addon, though sadly this # does not include IMDB number. That means we're still stuck using the show title/episode name to look up # IDs to feed to the scrobbler. Still, much easier than previous versions! - foundShowName = xbmc.getInfoLabel('VideoPlayer.Title') + foundShowName = xbmc.getInfoLabel("VideoPlayer.Title") logger.debug( - "[traktPlayer] onAVStarted() - Found VideoPlayer.Title: %s" % foundShowName) - foundEpisodeName = xbmc.getInfoLabel( - 'VideoPlayer.EpisodeName') + "[traktPlayer] onAVStarted() - Found VideoPlayer.Title: %s" + % foundShowName + ) + foundEpisodeName = xbmc.getInfoLabel("VideoPlayer.EpisodeName") logger.debug( - "[traktPlayer] onAVStarted() - Found VideoPlayer.EpisodeName: %s" % foundEpisodeName) - foundEpisodeYear = xbmc.getInfoLabel('VideoPlayer.Year') + "[traktPlayer] onAVStarted() - Found VideoPlayer.EpisodeName: %s" + % foundEpisodeName + ) + foundEpisodeYear = xbmc.getInfoLabel("VideoPlayer.Year") logger.debug( - "[traktPlayer] onAVStarted() - Found VideoPlayer.Year: %s" % foundEpisodeYear) - foundSeason = xbmc.getInfoLabel('VideoPlayer.Season') + "[traktPlayer] onAVStarted() - Found VideoPlayer.Year: %s" + % foundEpisodeYear + ) + foundSeason = xbmc.getInfoLabel("VideoPlayer.Season") logger.debug( - "[traktPlayer] onAVStarted() - Found VideoPlayer.Season: %s" % foundSeason) - foundEpisode = xbmc.getInfoLabel('VideoPlayer.Episode') + "[traktPlayer] onAVStarted() - Found VideoPlayer.Season: %s" + % foundSeason + ) + foundEpisode = xbmc.getInfoLabel("VideoPlayer.Episode") logger.debug( - "[traktPlayer] onAVStarted() - Found VideoPlayer.Episode: %s" % foundEpisode) - if (foundShowName and foundEpisodeName and foundEpisodeYear): + "[traktPlayer] onAVStarted() - Found VideoPlayer.Episode: %s" + % foundEpisode + ) + if foundShowName and foundEpisodeName and foundEpisodeYear: # If the show/episode/year are populated, we can skip all the mess of trying to extract the info from the # Player.Filename infolabel. logger.debug( - "[traktPlayer] onAVStarted() - Got info from VideoPlayer labels") + "[traktPlayer] onAVStarted() - Got info from VideoPlayer labels" + ) else: logger.debug( - "[traktPlayer] onAVStarted() - No love from VideoPlayer labels, trying Player.Filename infolabel") + "[traktPlayer] onAVStarted() - No love from VideoPlayer labels, trying Player.Filename infolabel" + ) # If that didn't work, we can fall back on the Player.Filename infolabel. It shows up like this: # (v16) ShowName [sXXeYY ](year) EpisodeName, channel, PVRFileName # (v17) ShowName [sXXeYY ](year) EpisodeName, channel, date, PVRFileName @@ -568,184 +680,248 @@ def onAVStarted(self): # Powerless s01e08 (2017)%20Green%20Furious, TV%20(WOOD%20TV), 20170414_003000, 1081_1492129800_4e1.pvr # DC's Legends of Tomorrow (2016) Pilot, Part 2, TV (CW W MI), 20160129_010000, 1081_1492129800_4e1.pvr foundLabel = urllib.parse.unquote( - xbmc.getInfoLabel('Player.Filename')) + xbmc.getInfoLabel("Player.Filename") + ) logger.debug( - "[traktPlayer] onAVStarted() - Found unknown video type with label: %s. Might be a PVR episode, searching Trakt for it." % foundLabel) + "[traktPlayer] onAVStarted() - Found unknown video type with label: %s. Might be a PVR episode, searching Trakt for it." + % foundLabel + ) logger.debug( - "[traktPlayer] onAVStarted() - After urllib.unquote: %s." % foundLabel) + "[traktPlayer] onAVStarted() - After urllib.unquote: %s." + % foundLabel + ) splitLabel = foundLabel.rsplit(", ", 3) logger.debug( - "[traktPlayer] onAVStarted() - Post-split of label: %s " % splitLabel) + "[traktPlayer] onAVStarted() - Post-split of label: %s " + % splitLabel + ) if len(splitLabel) != 4: logger.debug( - "[traktPlayer] onAVStarted() - Label doesn't have the ShowName sXXeYY (year) EpisodeName, channel, date, PVRFileName format that was expected. Might be the v16 version with no date instead.") + "[traktPlayer] onAVStarted() - Label doesn't have the ShowName sXXeYY (year) EpisodeName, channel, date, PVRFileName format that was expected. Might be the v16 version with no date instead." + ) splitLabel = foundLabel.rsplit(", ", 2) logger.debug( - "[traktPlayer] onAVStarted() - Post-split of label: %s " % splitLabel) + "[traktPlayer] onAVStarted() - Post-split of label: %s " + % splitLabel + ) if len(splitLabel) != 3: logger.debug( - "[traktPlayer] onAVStarted() - Label doesn't have the ShowName sXXeYY (year) EpisodeName, channel, PVRFileName format that was expected. Giving up.") + "[traktPlayer] onAVStarted() - Label doesn't have the ShowName sXXeYY (year) EpisodeName, channel, PVRFileName format that was expected. Giving up." + ) return foundShowAndEpInfo = splitLabel[0] logger.debug( - "[traktPlayer] onAVStarted() - show plus episode info: %s" % foundShowAndEpInfo) + "[traktPlayer] onAVStarted() - show plus episode info: %s" + % foundShowAndEpInfo + ) splitShowAndEpInfo = re.split( - r' (s\d\de\d\d)? ?\((\d\d\d\d)\) ', foundShowAndEpInfo, 1) + r" (s\d\de\d\d)? ?\((\d\d\d\d)\) ", foundShowAndEpInfo, 1 + ) logger.debug( - "[traktPlayer] onAVStarted() - Post-split of show plus episode info: %s " % splitShowAndEpInfo) + "[traktPlayer] onAVStarted() - Post-split of show plus episode info: %s " + % splitShowAndEpInfo + ) if len(splitShowAndEpInfo) != 4: logger.debug( - "[traktPlayer] onAVStarted() - Show plus episode info doesn't have the ShowName sXXeYY (year) EpisodeName format that was expected. Giving up.") + "[traktPlayer] onAVStarted() - Show plus episode info doesn't have the ShowName sXXeYY (year) EpisodeName format that was expected. Giving up." + ) return foundShowName = splitShowAndEpInfo[0] logger.debug( - "[traktPlayer] onAVStarted() - using show name: %s" % foundShowName) + "[traktPlayer] onAVStarted() - using show name: %s" + % foundShowName + ) foundEpisodeName = splitShowAndEpInfo[3] logger.debug( - "[traktPlayer] onAVStarted() - using episode name: %s" % foundEpisodeName) + "[traktPlayer] onAVStarted() - using episode name: %s" + % foundEpisodeName + ) foundEpisodeYear = splitShowAndEpInfo[2] logger.debug( - "[traktPlayer] onAVStarted() - using episode year: %s" % foundEpisodeYear) + "[traktPlayer] onAVStarted() - using episode year: %s" + % foundEpisodeYear + ) epYear = None try: epYear = int(foundEpisodeYear) except ValueError: epYear = None logger.debug( - "[traktPlayer] onAVStarted() - verified episode year: %d" % epYear) + "[traktPlayer] onAVStarted() - verified episode year: %d" + % epYear + ) # All right, now we have the show name, episode name, and (maybe) episode year. All good, but useless for # scrobbling since Trakt only understands IDs, not names. - data['video_ids'] = None - data['season'] = None - data['episode'] = None - data['episodeTitle'] = None + data["video_ids"] = None + data["season"] = None + data["episode"] = None + data["episodeTitle"] = None # First thing to try, a text query to the Trakt DB looking for this episode. Note # that we can't search for show and episode together, because the Trakt function gets confused and returns nothing. newResp = globals.traktapi.getTextQuery( - foundEpisodeName, "episode", epYear) + foundEpisodeName, "episode", epYear + ) if not newResp: logger.debug( - "[traktPlayer] onAVStarted() - Empty Response from getTextQuery, giving up") + "[traktPlayer] onAVStarted() - Empty Response from getTextQuery, giving up" + ) else: logger.debug( - "[traktPlayer] onAVStarted() - Got Response from getTextQuery: %s" % str(newResp)) + "[traktPlayer] onAVStarted() - Got Response from getTextQuery: %s" + % str(newResp) + ) # We got something back. See if one of the returned values is for the show we're looking for. Often it's # not, but since there's no way to tell the search which show we want, this is all we can do. rightResp = None for thisResp in newResp: compareShowName = thisResp.show.title logger.debug( - "[traktPlayer] onAVStarted() - comparing show name: %s" % compareShowName) + "[traktPlayer] onAVStarted() - comparing show name: %s" + % compareShowName + ) if thisResp.show.title == foundShowName: logger.debug( - "[traktPlayer] onAVStarted() - found the right show, using this response") + "[traktPlayer] onAVStarted() - found the right show, using this response" + ) rightResp = thisResp break if rightResp is None: logger.debug( - "[traktPlayer] onAVStarted() - Failed to find matching episode/show via text search.") + "[traktPlayer] onAVStarted() - Failed to find matching episode/show via text search." + ) else: # OK, now we have a episode object to work with. - self.type = 'episode' - data['type'] = 'episode' + self.type = "episode" + data["type"] = "episode" # You'd think we could just use the episode key that Trakt just returned to us, but the scrobbler # function (see scrobber.py) only understands the show key plus season/episode values. showKeys = {} for eachKey in rightResp.show.keys: showKeys[eachKey[0]] = eachKey[1] - data['video_ids'] = showKeys + data["video_ids"] = showKeys # For some reason, the Trakt search call returns the season and episode as an array in the pk field. # You'd think individual episode and season fields would be better, but whatever. - data['season'] = rightResp.pk[0] - data['episode'] = rightResp.pk[1] + data["season"] = rightResp.pk[0] + data["episode"] = rightResp.pk[1] # At this point if we haven't found the episode data yet, the episode-title-text-search method # didn't work. - if (not data['season']): + if not data["season"]: # This text query API is basically the same as searching on the website. Works with alternative # titles, unlike the scrobble function. Though we can't use the episode year since that would only # match the show if we're dealing with season 1. logger.debug( - "[traktPlayer] onAVStarted() - Searching for show title via getTextQuery: %s" % foundShowName) + "[traktPlayer] onAVStarted() - Searching for show title via getTextQuery: %s" + % foundShowName + ) newResp = globals.traktapi.getTextQuery( - foundShowName, "show", None) + foundShowName, "show", None + ) if not newResp: logger.debug( - "[traktPlayer] onAVStarted() - Empty Show Response from getTextQuery, falling back on episode text query") + "[traktPlayer] onAVStarted() - Empty Show Response from getTextQuery, falling back on episode text query" + ) else: logger.debug( - "[traktPlayer] onAVStarted() - Got Show Response from getTextQuery: %s" % str(newResp)) + "[traktPlayer] onAVStarted() - Got Show Response from getTextQuery: %s" + % str(newResp) + ) # We got something back. Have to assume the first show found is the right one; if there's more than # one, there's no way to know which to use. Pull the ids from the show data, and store 'em for scrobbling. showKeys = {} for eachKey in newResp[0].keys: showKeys[eachKey[0]] = eachKey[1] - data['video_ids'] = showKeys + data["video_ids"] = showKeys # Now to find the episode. There's no search function to look for an episode within a show, but # we can get all the episodes and look for the title. - while (not data['season']): + while not data["season"]: logger.debug( - "[traktPlayer] onAVStarted() - Querying for all seasons/episodes of this show") - epQueryResp = globals.traktapi.getShowWithAllEpisodesList( - data['video_ids']['trakt']) + "[traktPlayer] onAVStarted() - Querying for all seasons/episodes of this show" + ) + epQueryResp = ( + globals.traktapi.getShowWithAllEpisodesList( + data["video_ids"]["trakt"] + ) + ) if not epQueryResp: # Nothing returned. Giving up. logger.debug( - "[traktPlayer] onAVStarted() - No response received") + "[traktPlayer] onAVStarted() - No response received" + ) break else: # Got the list back. Go through each season. logger.debug( - "[traktPlayer] onAVStarted() - Got response with seasons: %s" % str(epQueryResp)) + "[traktPlayer] onAVStarted() - Got response with seasons: %s" + % str(epQueryResp) + ) for eachSeason in epQueryResp: # For each season, check each episode. logger.debug( - "[traktPlayer] onAVStarted() - Processing season: %s" % str(eachSeason)) + "[traktPlayer] onAVStarted() - Processing season: %s" + % str(eachSeason) + ) for eachEpisodeNumber in eachSeason.episodes: thisEpTitle = None # Get the title. The try block is here in case the title doesn't exist for some entries. try: - thisEpTitle = eachSeason.episodes[eachEpisodeNumber].title - except: + thisEpTitle = eachSeason.episodes[ + eachEpisodeNumber + ].title + except: # noqa: E722 thisEpTitle = None - logger.debug("[traktPlayer] onAVStarted() - Checking episode number %d with title %s" % ( - eachEpisodeNumber, thisEpTitle)) - if (foundEpisodeName == thisEpTitle): + logger.debug( + "[traktPlayer] onAVStarted() - Checking episode number %d with title %s" + % (eachEpisodeNumber, thisEpTitle) + ) + if foundEpisodeName == thisEpTitle: # Found it! Save the data. The scrobbler wants season and episode number. Which for some # reason is stored as a pair in the first item in the keys array. - data['season'] = eachSeason.episodes[eachEpisodeNumber].keys[0][0] - data['episode'] = eachSeason.episodes[eachEpisodeNumber].keys[0][1] + data["season"] = eachSeason.episodes[ + eachEpisodeNumber + ].keys[0][0] + data["episode"] = eachSeason.episodes[ + eachEpisodeNumber + ].keys[0][1] # Title too, just for the heck of it. Though it's not actually used. - data['episodeTitle'] = thisEpTitle + data["episodeTitle"] = thisEpTitle break # If we already found our data, no need to go through the rest of the seasons. - if (data['season']): + if data["season"]: break # Now we've done all we can. - if (data['season']): + if data["season"]: # OK, that's everything. Data should be all set for scrobbling. - logger.debug("[traktPlayer] onAVStarted() - Playing a non-library 'episode' : show trakt key %s, season: %d, episode: %d" % ( - data['video_ids'], data['season'], data['episode'])) + logger.debug( + "[traktPlayer] onAVStarted() - Playing a non-library 'episode' : show trakt key %s, season: %d, episode: %d" + % (data["video_ids"], data["season"], data["episode"]) + ) else: # Still no data? Too bad, have to give up. logger.debug( - "[traktPlayer] onAVStarted() - Did our best, but couldn't get info for this show and episode. Skipping.") + "[traktPlayer] onAVStarted() - Did our best, but couldn't get info for this show and episode. Skipping." + ) return else: logger.debug( - "[traktPlayer] onAVStarted() - Video type '%s' unrecognized, skipping." % self.type) + "[traktPlayer] onAVStarted() - Video type '%s' unrecognized, skipping." + % self.type + ) return pl = xbmc.PlayList(xbmc.PLAYLIST_VIDEO) plSize = len(pl) if plSize > 1: pos = pl.getposition() - if not self.plIndex is None: + if self.plIndex is not None: logger.debug( - "[traktPlayer] onAVStarted() - User manually skipped to next (or previous) video, forcing playback ended event.") + "[traktPlayer] onAVStarted() - User manually skipped to next (or previous) video, forcing playback ended event." + ) self.onPlayBackEnded() self.plIndex = pos logger.debug( - "[traktPlayer] onAVStarted() - Playlist contains %d item(s), and is currently on item %d" % (plSize, (pos + 1))) + "[traktPlayer] onAVStarted() - Playlist contains %d item(s), and is currently on item %d" + % (plSize, (pos + 1)) + ) self._playing = True @@ -754,68 +930,75 @@ def onAVStarted(self): # called when kodi stops playing a file def onPlayBackEnded(self): - xbmcgui.Window(10000).clearProperty('script.trakt.ids') - xbmcgui.Window(10000).clearProperty('script.trakt.paused') + xbmcgui.Window(10000).clearProperty("script.trakt.ids") + xbmcgui.Window(10000).clearProperty("script.trakt.paused") if self._playing: - logger.debug("[traktPlayer] onPlayBackEnded() - %s" % - self.isPlayingVideo()) + logger.debug("[traktPlayer] onPlayBackEnded() - %s" % self.isPlayingVideo()) self._playing = False self.plIndex = None - data = {'action': 'ended'} + data = {"action": "ended"} self.action(data) # called when user stops kodi playing a file def onPlayBackStopped(self): - xbmcgui.Window(10000).clearProperty('script.trakt.ids') - xbmcgui.Window(10000).clearProperty('script.trakt.paused') + xbmcgui.Window(10000).clearProperty("script.trakt.ids") + xbmcgui.Window(10000).clearProperty("script.trakt.paused") if self._playing: - logger.debug("[traktPlayer] onPlayBackStopped() - %s" % - self.isPlayingVideo()) + logger.debug( + "[traktPlayer] onPlayBackStopped() - %s" % self.isPlayingVideo() + ) self._playing = False self.plIndex = None - data = {'action': 'stopped'} + data = {"action": "stopped"} self.action(data) # called when user pauses a playing file def onPlayBackPaused(self): if self._playing: - logger.debug("[traktPlayer] onPlayBackPaused() - %s" % - self.isPlayingVideo()) - data = {'action': 'paused'} + logger.debug( + "[traktPlayer] onPlayBackPaused() - %s" % self.isPlayingVideo() + ) + data = {"action": "paused"} self.action(data) # called when user resumes a paused file def onPlayBackResumed(self): if self._playing: - logger.debug("[traktPlayer] onPlayBackResumed() - %s" % - self.isPlayingVideo()) - data = {'action': 'resumed'} + logger.debug( + "[traktPlayer] onPlayBackResumed() - %s" % self.isPlayingVideo() + ) + data = {"action": "resumed"} self.action(data) # called when user queues the next item def onQueueNextItem(self): if self._playing: - logger.debug("[traktPlayer] onQueueNextItem() - %s" % - self.isPlayingVideo()) + logger.debug("[traktPlayer] onQueueNextItem() - %s" % self.isPlayingVideo()) # called when players speed changes. (eg. user FF/RW) def onPlayBackSpeedChanged(self, speed): if self._playing: - logger.debug("[traktPlayer] onPlayBackSpeedChanged(speed: %s) - %s" % - (str(speed), self.isPlayingVideo())) + logger.debug( + "[traktPlayer] onPlayBackSpeedChanged(speed: %s) - %s" + % (str(speed), self.isPlayingVideo()) + ) # called when user seeks to a time def onPlayBackSeek(self, time, offset): if self._playing: - logger.debug("[traktPlayer] onPlayBackSeek(time: %s, offset: %s) - %s" % - (str(time), str(offset), self.isPlayingVideo())) - data = {'action': 'seek', 'time': time, 'offset': offset} + logger.debug( + "[traktPlayer] onPlayBackSeek(time: %s, offset: %s) - %s" + % (str(time), str(offset), self.isPlayingVideo()) + ) + data = {"action": "seek", "time": time, "offset": offset} self.action(data) # called when user performs a chapter seek def onPlayBackSeekChapter(self, chapter): if self._playing: - logger.debug("[traktPlayer] onPlayBackSeekChapter(chapter: %s) - %s" % - (str(chapter), self.isPlayingVideo())) - data = {'action': 'seekchapter', 'chapter': chapter} + logger.debug( + "[traktPlayer] onPlayBackSeekChapter(chapter: %s) - %s" + % (str(chapter), self.isPlayingVideo()) + ) + data = {"action": "seekchapter", "chapter": chapter} self.action(data) diff --git a/resources/lib/sqlitequeue.py b/resources/lib/sqlitequeue.py index 17a29ada..41503d60 100644 --- a/resources/lib/sqlitequeue.py +++ b/resources/lib/sqlitequeue.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import os -import sys import sqlite3 from json import loads, dumps @@ -57,8 +56,8 @@ def __init__(self): def __len__(self) -> int: with self._get_conn() as conn: - l = conn.execute(self._count).fetchone()[0] - return l + executed = conn.execute(self._count).fetchone()[0] + return executed def __iter__(self): with self._get_conn() as conn: diff --git a/resources/lib/syncEpisodes.py b/resources/lib/syncEpisodes.py index 70fbc848..09be8f42 100644 --- a/resources/lib/syncEpisodes.py +++ b/resources/lib/syncEpisodes.py @@ -12,36 +12,51 @@ class SyncEpisodes: def __init__(self, sync, progress): self.sync = sync if self.sync.show_notification: - kodiUtilities.notification('%s %s' % (kodiUtilities.getString( - 32045), kodiUtilities.getString(32050)), kodiUtilities.getString(32061)) # Sync started + kodiUtilities.notification( + "%s %s" + % (kodiUtilities.getString(32045), kodiUtilities.getString(32050)), + kodiUtilities.getString(32061), + ) # Sync started if self.sync.show_progress and not self.sync.run_silent: - progress.create("%s %s" % (kodiUtilities.getString( - 32045), kodiUtilities.getString(32050)), "") + progress.create( + "%s %s" + % (kodiUtilities.getString(32045), kodiUtilities.getString(32050)), + "", + ) kodiShowsCollected, kodiShowsWatched = self.__kodiLoadShows() if not isinstance(kodiShowsCollected, list) and not kodiShowsCollected: logger.debug( - "[Episodes Sync] Kodi collected show list is empty, aborting tv show Sync.") + "[Episodes Sync] Kodi collected show list is empty, aborting tv show Sync." + ) if self.sync.show_progress and not self.sync.run_silent: progress.close() return if not isinstance(kodiShowsWatched, list) and not kodiShowsWatched: logger.debug( - "[Episodes Sync] Kodi watched show list is empty, aborting tv show Sync.") + "[Episodes Sync] Kodi watched show list is empty, aborting tv show Sync." + ) if self.sync.show_progress and not self.sync.run_silent: progress.close() return - traktShowsCollected, traktShowsWatched, traktShowsRated, traktEpisodesRated = self.__traktLoadShows() + ( + traktShowsCollected, + traktShowsWatched, + traktShowsRated, + traktEpisodesRated, + ) = self.__traktLoadShows() if not traktShowsCollected: logger.debug( - "[Episodes Sync] Error getting Trakt.tv collected show list, aborting tv show sync.") + "[Episodes Sync] Error getting Trakt.tv collected show list, aborting tv show sync." + ) if self.sync.show_progress and not self.sync.run_silent: progress.close() return if not traktShowsWatched: logger.debug( - "[Episodes Sync] Error getting Trakt.tv watched show list, aborting tv show sync.") + "[Episodes Sync] Error getting Trakt.tv watched show list, aborting tv show sync." + ) if self.sync.show_progress and not self.sync.run_silent: progress.close() return @@ -49,62 +64,81 @@ def __init__(self, sync, progress): traktShowsProgress = self.__traktLoadShowsPlaybackProgress(25, 36) self.__addEpisodesToTraktCollection( - kodiShowsCollected, traktShowsCollected, 37, 47) + kodiShowsCollected, traktShowsCollected, 37, 47 + ) self.__deleteEpisodesFromTraktCollection( - traktShowsCollected, kodiShowsCollected, 48, 58) + traktShowsCollected, kodiShowsCollected, 48, 58 + ) - self.__addEpisodesToTraktWatched( - kodiShowsWatched, traktShowsWatched, 59, 69) + self.__addEpisodesToTraktWatched(kodiShowsWatched, traktShowsWatched, 59, 69) self.__addEpisodesToKodiWatched( - traktShowsWatched, kodiShowsWatched, kodiShowsCollected, 70, 80) + traktShowsWatched, kodiShowsWatched, kodiShowsCollected, 70, 80 + ) - self.__addEpisodeProgressToKodi( - traktShowsProgress, kodiShowsCollected, 81, 91) + self.__addEpisodeProgressToKodi(traktShowsProgress, kodiShowsCollected, 81, 91) self.__syncShowsRatings(traktShowsRated, kodiShowsCollected, 92, 95) - self.__syncEpisodeRatings( - traktEpisodesRated, kodiShowsCollected, 96, 99) + self.__syncEpisodeRatings(traktEpisodesRated, kodiShowsCollected, 96, 99) if self.sync.show_notification: - kodiUtilities.notification('%s %s' % (kodiUtilities.getString( - 32045), kodiUtilities.getString(32050)), kodiUtilities.getString(32062)) # Sync complete + kodiUtilities.notification( + "%s %s" + % (kodiUtilities.getString(32045), kodiUtilities.getString(32050)), + kodiUtilities.getString(32062), + ) # Sync complete if self.sync.show_progress and not self.sync.run_silent: self.sync.UpdateProgress( - 100, line1=" ", line2=kodiUtilities.getString(32075), line3=" ") + 100, line1=" ", line2=kodiUtilities.getString(32075), line3=" " + ) progress.close() - logger.debug("[Episodes Sync] Shows on Trakt.tv (%d), shows in Kodi (%d)." % ( - len(traktShowsCollected['shows']), len(kodiShowsCollected['shows']))) + logger.debug( + "[Episodes Sync] Shows on Trakt.tv (%d), shows in Kodi (%d)." + % (len(traktShowsCollected["shows"]), len(kodiShowsCollected["shows"])) + ) - logger.debug("[Episodes Sync] Episodes on Trakt.tv (%d), episodes in Kodi (%d)." % ( - utilities.countEpisodes(traktShowsCollected), utilities.countEpisodes(kodiShowsCollected))) + logger.debug( + "[Episodes Sync] Episodes on Trakt.tv (%d), episodes in Kodi (%d)." + % ( + utilities.countEpisodes(traktShowsCollected), + utilities.countEpisodes(kodiShowsCollected), + ) + ) logger.debug("[Episodes Sync] Complete.") - ''' begin code for episode sync ''' + """ begin code for episode sync """ def __kodiLoadShows(self): - self.sync.UpdateProgress(1, line1=kodiUtilities.getString( - 32094), line2=kodiUtilities.getString(32095)) + self.sync.UpdateProgress( + 1, + line1=kodiUtilities.getString(32094), + line2=kodiUtilities.getString(32095), + ) logger.debug("[Episodes Sync] Getting show data from Kodi") - data = kodiUtilities.kodiJsonRequest({'jsonrpc': '2.0', 'method': 'VideoLibrary.GetTVShows', 'params': { - 'properties': ['title', 'uniqueid', 'year', 'userrating']}, 'id': 0}) - if data['limits']['total'] == 0: + data = kodiUtilities.kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetTVShows", + "params": {"properties": ["title", "uniqueid", "year", "userrating"]}, + "id": 0, + } + ) + if data["limits"]["total"] == 0: logger.debug("[Episodes Sync] Kodi json request was empty.") return None, None tvshows = kodiUtilities.kodiRpcToTraktMediaObjects(data) - logger.debug( - "[Episode Sync] Getting shows from kodi finished %s" % tvshows) + logger.debug("[Episode Sync] Getting shows from kodi finished %s" % tvshows) if tvshows is None: return None, None self.sync.UpdateProgress(2, line2=kodiUtilities.getString(32096)) - resultCollected = {'shows': []} - resultWatched = {'shows': []} + resultCollected = {"shows": []} + resultWatched = {"shows": []} i = 0 x = float(len(tvshows)) logger.debug("[Episodes Sync] Getting episode data from Kodi") @@ -112,213 +146,282 @@ def __kodiLoadShows(self): i += 1 y = ((i / x) * 8) + 2 self.sync.UpdateProgress( - int(y), line2=kodiUtilities.getString(32097) % (i, x)) + int(y), line2=kodiUtilities.getString(32097) % (i, x) + ) - if 'ids' not in show_col1: + if "ids" not in show_col1: logger.debug( - "[Episodes Sync] Tvshow %s has no imdbnumber or uniqueid" % show_col1['tvshowid']) + "[Episodes Sync] Tvshow %s has no imdbnumber or uniqueid" + % show_col1["tvshowid"] + ) continue - show = {'title': show_col1['title'], 'ids': show_col1['ids'], 'year': show_col1['year'], 'rating': show_col1['rating'], - 'tvshowid': show_col1['tvshowid'], 'seasons': []} - - data = kodiUtilities.kodiJsonRequest({'jsonrpc': '2.0', 'method': 'VideoLibrary.GetEpisodes', 'params': {'tvshowid': show_col1['tvshowid'], 'properties': [ - 'season', 'episode', 'playcount', 'uniqueid', 'lastplayed', 'file', 'dateadded', 'runtime', 'userrating']}, 'id': 0}) + show = { + "title": show_col1["title"], + "ids": show_col1["ids"], + "year": show_col1["year"], + "rating": show_col1["rating"], + "tvshowid": show_col1["tvshowid"], + "seasons": [], + } + + data = kodiUtilities.kodiJsonRequest( + { + "jsonrpc": "2.0", + "method": "VideoLibrary.GetEpisodes", + "params": { + "tvshowid": show_col1["tvshowid"], + "properties": [ + "season", + "episode", + "playcount", + "uniqueid", + "lastplayed", + "file", + "dateadded", + "runtime", + "userrating", + ], + }, + "id": 0, + } + ) if not data: logger.debug( - "[Episodes Sync] There was a problem getting episode data for '%s', aborting sync." % show['title']) + "[Episodes Sync] There was a problem getting episode data for '%s', aborting sync." + % show["title"] + ) return None, None - elif 'episodes' not in data: + elif "episodes" not in data: logger.debug( - "[Episodes Sync] '%s' has no episodes in Kodi." % show['title']) + "[Episodes Sync] '%s' has no episodes in Kodi." % show["title"] + ) continue - if 'tvshowid' in show_col1: - del(show_col1['tvshowid']) + if "tvshowid" in show_col1: + del show_col1["tvshowid"] showWatched = copy.deepcopy(show) data2 = copy.deepcopy(data) - show['seasons'] = kodiUtilities.kodiRpcToTraktMediaObjects(data) + show["seasons"] = kodiUtilities.kodiRpcToTraktMediaObjects(data) - showWatched['seasons'] = kodiUtilities.kodiRpcToTraktMediaObjects( - data2, 'watched') + showWatched["seasons"] = kodiUtilities.kodiRpcToTraktMediaObjects( + data2, "watched" + ) - resultCollected['shows'].append(show) - resultWatched['shows'].append(showWatched) + resultCollected["shows"].append(show) + resultWatched["shows"].append(showWatched) self.sync.UpdateProgress(10, line2=kodiUtilities.getString(32098)) return resultCollected, resultWatched def __traktLoadShows(self): - self.sync.UpdateProgress(10, line1=kodiUtilities.getString( - 32099), line2=kodiUtilities.getString(32100)) + self.sync.UpdateProgress( + 10, + line1=kodiUtilities.getString(32099), + line2=kodiUtilities.getString(32100), + ) logger.debug( - '[Episodes Sync] Getting episode collection/watched/rated from Trakt.tv') + "[Episodes Sync] Getting episode collection/watched/rated from Trakt.tv" + ) try: traktShowsCollected = {} traktShowsCollected = self.sync.traktapi.getShowsCollected( - traktShowsCollected) + traktShowsCollected + ) traktShowsCollected = list(traktShowsCollected.items()) self.sync.UpdateProgress(12, line2=kodiUtilities.getString(32101)) traktShowsWatched = {} - traktShowsWatched = self.sync.traktapi.getShowsWatched( - traktShowsWatched) + traktShowsWatched = self.sync.traktapi.getShowsWatched(traktShowsWatched) traktShowsWatched = list(traktShowsWatched.items()) traktShowsRated = {} traktEpisodesRated = {} - if kodiUtilities.getSettingAsBool('trakt_sync_ratings'): - traktShowsRated = self.sync.traktapi.getShowsRated( - traktShowsRated) + if kodiUtilities.getSettingAsBool("trakt_sync_ratings"): + traktShowsRated = self.sync.traktapi.getShowsRated(traktShowsRated) traktShowsRated = list(traktShowsRated.items()) traktEpisodesRated = self.sync.traktapi.getEpisodesRated( - traktEpisodesRated) + traktEpisodesRated + ) traktEpisodesRated = list(traktEpisodesRated.items()) except Exception: logger.debug( - "[Episodes Sync] Invalid Trakt.tv show list, possible error getting data from Trakt, aborting Trakt.tv collection/watched/rated update.") + "[Episodes Sync] Invalid Trakt.tv show list, possible error getting data from Trakt, aborting Trakt.tv collection/watched/rated update." + ) return False, False, False, False i = 0 x = float(len(traktShowsCollected)) - showsCollected = {'shows': []} + showsCollected = {"shows": []} for _, show in traktShowsCollected: i += 1 y = ((i / x) * 4) + 12 self.sync.UpdateProgress( - int(y), line2=kodiUtilities.getString(32102) % (i, x)) + int(y), line2=kodiUtilities.getString(32102) % (i, x) + ) # will keep the data in python structures - just like the KODI response show = show.to_dict() - showsCollected['shows'].append(show) + showsCollected["shows"].append(show) i = 0 x = float(len(traktShowsWatched)) - showsWatched = {'shows': []} + showsWatched = {"shows": []} for _, show in traktShowsWatched: i += 1 y = ((i / x) * 4) + 16 self.sync.UpdateProgress( - int(y), line2=kodiUtilities.getString(32102) % (i, x)) + int(y), line2=kodiUtilities.getString(32102) % (i, x) + ) # will keep the data in python structures - just like the KODI response show = show.to_dict() - showsWatched['shows'].append(show) + showsWatched["shows"].append(show) i = 0 x = float(len(traktShowsRated)) - showsRated = {'shows': []} + showsRated = {"shows": []} for _, show in traktShowsRated: i += 1 y = ((i / x) * 4) + 20 self.sync.UpdateProgress( - int(y), line2=kodiUtilities.getString(32102) % (i, x)) + int(y), line2=kodiUtilities.getString(32102) % (i, x) + ) # will keep the data in python structures - just like the KODI response show = show.to_dict() - showsRated['shows'].append(show) + showsRated["shows"].append(show) i = 0 x = float(len(traktEpisodesRated)) - episodesRated = {'shows': []} + episodesRated = {"shows": []} for _, show in traktEpisodesRated: i += 1 y = ((i / x) * 4) + 20 self.sync.UpdateProgress( - int(y), line2=kodiUtilities.getString(32102) % (i, x)) + int(y), line2=kodiUtilities.getString(32102) % (i, x) + ) # will keep the data in python structures - just like the KODI response show = show.to_dict() - episodesRated['shows'].append(show) + episodesRated["shows"].append(show) self.sync.UpdateProgress(25, line2=kodiUtilities.getString(32103)) return showsCollected, showsWatched, showsRated, episodesRated def __traktLoadShowsPlaybackProgress(self, fromPercent, toPercent): - if kodiUtilities.getSettingAsBool('trakt_episode_playback') and not self.sync.IsCanceled(): - self.sync.UpdateProgress(fromPercent, line1=kodiUtilities.getString( - 1485), line2=kodiUtilities.getString(32119)) + if ( + kodiUtilities.getSettingAsBool("trakt_episode_playback") + and not self.sync.IsCanceled() + ): + self.sync.UpdateProgress( + fromPercent, + line1=kodiUtilities.getString(1485), + line2=kodiUtilities.getString(32119), + ) - logger.debug( - '[Playback Sync] Getting playback progress from Trakt.tv') + logger.debug("[Playback Sync] Getting playback progress from Trakt.tv") try: traktProgressShows = self.sync.traktapi.getEpisodePlaybackProgress() except Exception as ex: logger.debug( - "[Playback Sync] Invalid Trakt.tv progress list, possible error getting data from Trakt, aborting Trakt.tv playback update. Error: %s" % ex) + "[Playback Sync] Invalid Trakt.tv progress list, possible error getting data from Trakt, aborting Trakt.tv playback update. Error: %s" + % ex + ) return False i = 0 x = float(len(traktProgressShows)) - showsProgress = {'shows': []} + showsProgress = {"shows": []} for show in traktProgressShows: i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent + y = ((i / x) * (toPercent - fromPercent)) + fromPercent self.sync.UpdateProgress( - int(y), line2=kodiUtilities.getString(32120) % (i, x)) + int(y), line2=kodiUtilities.getString(32120) % (i, x) + ) # will keep the data in python structures - just like the KODI response show = show.to_dict() - showsProgress['shows'].append(show) + showsProgress["shows"].append(show) - self.sync.UpdateProgress( - toPercent, line2=kodiUtilities.getString(32121)) + self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32121)) return showsProgress - def __addEpisodesToTraktCollection(self, kodiShows, traktShows, fromPercent, toPercent): - if kodiUtilities.getSettingAsBool('add_episodes_to_trakt') and not self.sync.IsCanceled(): + def __addEpisodesToTraktCollection( + self, kodiShows, traktShows, fromPercent, toPercent + ): + if ( + kodiUtilities.getSettingAsBool("add_episodes_to_trakt") + and not self.sync.IsCanceled() + ): addTraktShows = copy.deepcopy(traktShows) addKodiShows = copy.deepcopy(kodiShows) tmpTraktShowsAdd = utilities.compareEpisodes( - addKodiShows, addTraktShows, kodiUtilities.getSettingAsBool("scrobble_fallback")) + addKodiShows, + addTraktShows, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + ) traktShowsAdd = copy.deepcopy(tmpTraktShowsAdd) utilities.sanitizeShows(traktShowsAdd) # logger.debug("traktShowsAdd %s" % traktShowsAdd) - if len(traktShowsAdd['shows']) == 0: - self.sync.UpdateProgress(toPercent, line1=kodiUtilities.getString( - 32068), line2=kodiUtilities.getString(32104)) + if len(traktShowsAdd["shows"]) == 0: + self.sync.UpdateProgress( + toPercent, + line1=kodiUtilities.getString(32068), + line2=kodiUtilities.getString(32104), + ) logger.debug( - "[Episodes Sync] Trakt.tv episode collection is up to date.") + "[Episodes Sync] Trakt.tv episode collection is up to date." + ) return - logger.debug("[Episodes Sync] %i show(s) have episodes (%d) to be added to your Trakt.tv collection." % ( - len(traktShowsAdd['shows']), utilities.countEpisodes(traktShowsAdd))) - for show in traktShowsAdd['shows']: - logger.debug("[Episodes Sync] Episodes added: %s" % - self.__getShowAsString(show, short=True)) + logger.debug( + "[Episodes Sync] %i show(s) have episodes (%d) to be added to your Trakt.tv collection." + % (len(traktShowsAdd["shows"]), utilities.countEpisodes(traktShowsAdd)) + ) + for show in traktShowsAdd["shows"]: + logger.debug( + "[Episodes Sync] Episodes added: %s" + % self.__getShowAsString(show, short=True) + ) - self.sync.UpdateProgress(fromPercent, line1=kodiUtilities.getString( - 32068), line2=kodiUtilities.getString(32067) % (len(traktShowsAdd['shows']))) + self.sync.UpdateProgress( + fromPercent, + line1=kodiUtilities.getString(32068), + line2=kodiUtilities.getString(32067) % (len(traktShowsAdd["shows"])), + ) # split episode list into chunks of 50 chunksize = 50 - chunked_episodes = utilities.chunks( - traktShowsAdd['shows'], chunksize) + chunked_episodes = utilities.chunks(traktShowsAdd["shows"], chunksize) errorcount = 0 i = 0 - x = float(len(traktShowsAdd['shows'])) + x = float(len(traktShowsAdd["shows"])) for chunk in chunked_episodes: if self.sync.IsCanceled(): return i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent - self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString( - 32069) % ((i) * chunksize if (i) * chunksize < x else x, x)) + y = ((i / x) * (toPercent - fromPercent)) + fromPercent + self.sync.UpdateProgress( + int(y), + line2=kodiUtilities.getString(32069) + % ((i) * chunksize if (i) * chunksize < x else x, x), + ) - request = {'shows': chunk} + request = {"shows": chunk} logger.debug("[traktAddEpisodes] Shows to add %s" % request) try: self.sync.traktapi.addToCollection(request) @@ -327,86 +430,131 @@ def __addEpisodesToTraktCollection(self, kodiShows, traktShows, fromPercent, toP logging.fatal(message) errorcount += 1 - logger.debug( - "[traktAddEpisodes] Finished with %d error(s)" % errorcount) - self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString( - 32105) % utilities.countEpisodes(traktShowsAdd)) - - def __deleteEpisodesFromTraktCollection(self, traktShows, kodiShows, fromPercent, toPercent): - if kodiUtilities.getSettingAsBool('clean_trakt_episodes') and not self.sync.IsCanceled(): + logger.debug("[traktAddEpisodes] Finished with %d error(s)" % errorcount) + self.sync.UpdateProgress( + toPercent, + line2=kodiUtilities.getString(32105) + % utilities.countEpisodes(traktShowsAdd), + ) + + def __deleteEpisodesFromTraktCollection( + self, traktShows, kodiShows, fromPercent, toPercent + ): + if ( + kodiUtilities.getSettingAsBool("clean_trakt_episodes") + and not self.sync.IsCanceled() + ): removeTraktShows = copy.deepcopy(traktShows) removeKodiShows = copy.deepcopy(kodiShows) traktShowsRemove = utilities.compareEpisodes( - removeTraktShows, removeKodiShows, kodiUtilities.getSettingAsBool("scrobble_fallback")) + removeTraktShows, + removeKodiShows, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + ) utilities.sanitizeShows(traktShowsRemove) - if len(traktShowsRemove['shows']) == 0: - self.sync.UpdateProgress(toPercent, line1=kodiUtilities.getString( - 32077), line2=kodiUtilities.getString(32110)) + if len(traktShowsRemove["shows"]) == 0: + self.sync.UpdateProgress( + toPercent, + line1=kodiUtilities.getString(32077), + line2=kodiUtilities.getString(32110), + ) logger.debug( - '[Episodes Sync] Trakt.tv episode collection is clean, no episodes to remove.') + "[Episodes Sync] Trakt.tv episode collection is clean, no episodes to remove." + ) return - logger.debug("[Episodes Sync] %i show(s) will have episodes removed from Trakt.tv collection." % len( - traktShowsRemove['shows'])) - for show in traktShowsRemove['shows']: - logger.debug("[Episodes Sync] Episodes removed: %s" % - self.__getShowAsString(show, short=True)) + logger.debug( + "[Episodes Sync] %i show(s) will have episodes removed from Trakt.tv collection." + % len(traktShowsRemove["shows"]) + ) + for show in traktShowsRemove["shows"]: + logger.debug( + "[Episodes Sync] Episodes removed: %s" + % self.__getShowAsString(show, short=True) + ) - self.sync.UpdateProgress(fromPercent, line1=kodiUtilities.getString( - 32077), line2=kodiUtilities.getString(32111) % utilities.countEpisodes(traktShowsRemove)) + self.sync.UpdateProgress( + fromPercent, + line1=kodiUtilities.getString(32077), + line2=kodiUtilities.getString(32111) + % utilities.countEpisodes(traktShowsRemove), + ) - logger.debug("[traktRemoveEpisodes] Shows to remove %s" % - traktShowsRemove) + logger.debug("[traktRemoveEpisodes] Shows to remove %s" % traktShowsRemove) try: self.sync.traktapi.removeFromCollection(traktShowsRemove) except Exception as ex: message = utilities.createError(ex) logging.fatal(message) - self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString( - 32112) % utilities.countEpisodes(traktShowsRemove)) - - def __addEpisodesToTraktWatched(self, kodiShows, traktShows, fromPercent, toPercent): - if kodiUtilities.getSettingAsBool('trakt_episode_playcount') and not self.sync.IsCanceled(): + self.sync.UpdateProgress( + toPercent, + line2=kodiUtilities.getString(32112) + % utilities.countEpisodes(traktShowsRemove), + ) + + def __addEpisodesToTraktWatched( + self, kodiShows, traktShows, fromPercent, toPercent + ): + if ( + kodiUtilities.getSettingAsBool("trakt_episode_playcount") + and not self.sync.IsCanceled() + ): updateTraktTraktShows = copy.deepcopy(traktShows) updateTraktKodiShows = copy.deepcopy(kodiShows) traktShowsUpdate = utilities.compareEpisodes( - updateTraktKodiShows, updateTraktTraktShows, kodiUtilities.getSettingAsBool("scrobble_fallback"), watched=True) + updateTraktKodiShows, + updateTraktTraktShows, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + watched=True, + ) utilities.sanitizeShows(traktShowsUpdate) # logger.debug("traktShowsUpdate %s" % traktShowsUpdate) - if len(traktShowsUpdate['shows']) == 0: - self.sync.UpdateProgress(toPercent, line1=kodiUtilities.getString( - 32071), line2=kodiUtilities.getString(32106)) + if len(traktShowsUpdate["shows"]) == 0: + self.sync.UpdateProgress( + toPercent, + line1=kodiUtilities.getString(32071), + line2=kodiUtilities.getString(32106), + ) logger.debug( - "[Episodes Sync] Trakt.tv episode playcounts are up to date.") + "[Episodes Sync] Trakt.tv episode playcounts are up to date." + ) return - logger.debug("[Episodes Sync] %i show(s) are missing playcounts on Trakt.tv" % len( - traktShowsUpdate['shows'])) - for show in traktShowsUpdate['shows']: - logger.debug("[Episodes Sync] Episodes updated: %s" % - self.__getShowAsString(show, short=True)) + logger.debug( + "[Episodes Sync] %i show(s) are missing playcounts on Trakt.tv" + % len(traktShowsUpdate["shows"]) + ) + for show in traktShowsUpdate["shows"]: + logger.debug( + "[Episodes Sync] Episodes updated: %s" + % self.__getShowAsString(show, short=True) + ) - self.sync.UpdateProgress(fromPercent, line1=kodiUtilities.getString( - 32071), line2=kodiUtilities.getString(32070) % (len(traktShowsUpdate['shows']))) + self.sync.UpdateProgress( + fromPercent, + line1=kodiUtilities.getString(32071), + line2=kodiUtilities.getString(32070) % (len(traktShowsUpdate["shows"])), + ) errorcount = 0 i = 0 - x = float(len(traktShowsUpdate['shows'])) - for show in traktShowsUpdate['shows']: + x = float(len(traktShowsUpdate["shows"])) + for show in traktShowsUpdate["shows"]: if self.sync.IsCanceled(): return epCount = utilities.countEpisodes([show]) - title = show['title'] + title = show["title"] i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent + y = ((i / x) * (toPercent - fromPercent)) + fromPercent self.sync.UpdateProgress( - int(y), line2=title, line3=kodiUtilities.getString(32073) % epCount) + int(y), line2=title, line3=kodiUtilities.getString(32073) % epCount + ) - s = {'shows': [show]} + s = {"shows": [show]} logger.debug("[traktUpdateEpisodes] Shows to update %s" % s) try: self.sync.traktapi.addToHistory(s) @@ -415,248 +563,431 @@ def __addEpisodesToTraktWatched(self, kodiShows, traktShows, fromPercent, toPerc logging.fatal(message) errorcount += 1 - logger.debug( - "[traktUpdateEpisodes] Finished with %d error(s)" % errorcount) - self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString( - 32072) % (len(traktShowsUpdate['shows'])), line3=" ") - - def __addEpisodesToKodiWatched(self, traktShows, kodiShows, kodiShowsCollected, fromPercent, toPercent): - if kodiUtilities.getSettingAsBool('kodi_episode_playcount') and not self.sync.IsCanceled(): + logger.debug("[traktUpdateEpisodes] Finished with %d error(s)" % errorcount) + self.sync.UpdateProgress( + toPercent, + line2=kodiUtilities.getString(32072) % (len(traktShowsUpdate["shows"])), + line3=" ", + ) + + def __addEpisodesToKodiWatched( + self, traktShows, kodiShows, kodiShowsCollected, fromPercent, toPercent + ): + if ( + kodiUtilities.getSettingAsBool("kodi_episode_playcount") + and not self.sync.IsCanceled() + ): updateKodiTraktShows = copy.deepcopy(traktShows) updateKodiKodiShows = copy.deepcopy(kodiShows) - kodiShowsUpdate = utilities.compareEpisodes(updateKodiTraktShows, updateKodiKodiShows, kodiUtilities.getSettingAsBool( - "scrobble_fallback"), watched=True, restrict=True, collected=kodiShowsCollected) + kodiShowsUpdate = utilities.compareEpisodes( + updateKodiTraktShows, + updateKodiKodiShows, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + watched=True, + restrict=True, + collected=kodiShowsCollected, + ) - if len(kodiShowsUpdate['shows']) == 0: - self.sync.UpdateProgress(toPercent, line1=kodiUtilities.getString( - 32074), line2=kodiUtilities.getString(32107)) - logger.debug( - "[Episodes Sync] Kodi episode playcounts are up to date.") + if len(kodiShowsUpdate["shows"]) == 0: + self.sync.UpdateProgress( + toPercent, + line1=kodiUtilities.getString(32074), + line2=kodiUtilities.getString(32107), + ) + logger.debug("[Episodes Sync] Kodi episode playcounts are up to date.") return - logger.debug("[Episodes Sync] %i show(s) shows are missing playcounts on Kodi" % len( - kodiShowsUpdate['shows'])) - for s in ["%s" % self.__getShowAsString(s, short=True) for s in kodiShowsUpdate['shows']]: + logger.debug( + "[Episodes Sync] %i show(s) shows are missing playcounts on Kodi" + % len(kodiShowsUpdate["shows"]) + ) + for s in [ + "%s" % self.__getShowAsString(s, short=True) + for s in kodiShowsUpdate["shows"] + ]: logger.debug("[Episodes Sync] Episodes updated: %s" % s) # logger.debug("kodiShowsUpdate: %s" % kodiShowsUpdate) episodes = [] - for show in kodiShowsUpdate['shows']: - for season in show['seasons']: - for episode in season['episodes']: - episodes.append({'episodeid': episode['ids']['episodeid'], 'playcount': episode['plays'], - "lastplayed": utilities.convertUtcToDateTime(episode['last_watched_at'])}) + for show in kodiShowsUpdate["shows"]: + for season in show["seasons"]: + for episode in season["episodes"]: + episodes.append( + { + "episodeid": episode["ids"]["episodeid"], + "playcount": episode["plays"], + "lastplayed": utilities.convertUtcToDateTime( + episode["last_watched_at"] + ), + } + ) # split episode list into chunks of 50 chunksize = 50 chunked_episodes = utilities.chunks( - [{"jsonrpc": "2.0", "method": "VideoLibrary.SetEpisodeDetails", "params": episodes[i], "id": i} for i in range(len(episodes))], chunksize) + [ + { + "jsonrpc": "2.0", + "method": "VideoLibrary.SetEpisodeDetails", + "params": episodes[i], + "id": i, + } + for i in range(len(episodes)) + ], + chunksize, + ) i = 0 x = float(len(episodes)) for chunk in chunked_episodes: if self.sync.IsCanceled(): return i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent - self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString( - 32108) % ((i) * chunksize if (i) * chunksize < x else x, x)) + y = ((i / x) * (toPercent - fromPercent)) + fromPercent + self.sync.UpdateProgress( + int(y), + line2=kodiUtilities.getString(32108) + % ((i) * chunksize if (i) * chunksize < x else x, x), + ) logger.debug("[Episodes Sync] chunk %s" % str(chunk)) result = kodiUtilities.kodiJsonRequest(chunk) logger.debug("[Episodes Sync] result %s" % str(result)) self.sync.UpdateProgress( - toPercent, line2=kodiUtilities.getString(32109) % len(episodes)) + toPercent, line2=kodiUtilities.getString(32109) % len(episodes) + ) def __addEpisodeProgressToKodi(self, traktShows, kodiShows, fromPercent, toPercent): - if kodiUtilities.getSettingAsBool('trakt_episode_playback') and traktShows and not self.sync.IsCanceled(): + if ( + kodiUtilities.getSettingAsBool("trakt_episode_playback") + and traktShows + and not self.sync.IsCanceled() + ): updateKodiTraktShows = copy.deepcopy(traktShows) updateKodiKodiShows = copy.deepcopy(kodiShows) - kodiShowsUpdate = utilities.compareEpisodes(updateKodiTraktShows, updateKodiKodiShows, kodiUtilities.getSettingAsBool( - "scrobble_fallback"), restrict=True, playback=True) - - if len(kodiShowsUpdate['shows']) == 0: - self.sync.UpdateProgress(toPercent, line1=kodiUtilities.getString( - 1441), line2=kodiUtilities.getString(32129)) - logger.debug( - "[Episodes Sync] Kodi episode progress is up to date.") + kodiShowsUpdate = utilities.compareEpisodes( + updateKodiTraktShows, + updateKodiKodiShows, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + restrict=True, + playback=True, + ) + + if len(kodiShowsUpdate["shows"]) == 0: + self.sync.UpdateProgress( + toPercent, + line1=kodiUtilities.getString(1441), + line2=kodiUtilities.getString(32129), + ) + logger.debug("[Episodes Sync] Kodi episode progress is up to date.") return - logger.debug("[Episodes Sync] %i show(s) shows are missing progress in Kodi" % len( - kodiShowsUpdate['shows'])) - for s in ["%s" % self.__getShowAsString(s, short=True) for s in kodiShowsUpdate['shows']]: + logger.debug( + "[Episodes Sync] %i show(s) shows are missing progress in Kodi" + % len(kodiShowsUpdate["shows"]) + ) + for s in [ + "%s" % self.__getShowAsString(s, short=True) + for s in kodiShowsUpdate["shows"] + ]: logger.debug("[Episodes Sync] Episodes updated: %s" % s) episodes = [] - for show in kodiShowsUpdate['shows']: - for season in show['seasons']: - for episode in season['episodes']: + for show in kodiShowsUpdate["shows"]: + for season in show["seasons"]: + for episode in season["episodes"]: # If library item doesn't have a runtime set get it from # Trakt to avoid later using 0 in runtime * progress_pct. - if not episode['runtime']: - episode['runtime'] = self.sync.traktapi.getEpisodeSummary( - show['ids']['trakt'], season['number'], episode['number'], extended='full').runtime * 60 + if not episode["runtime"]: + episode["runtime"] = ( + self.sync.traktapi.getEpisodeSummary( + show["ids"]["trakt"], + season["number"], + episode["number"], + extended="full", + ).runtime + * 60 + ) episodes.append( - {'episodeid': episode['ids']['episodeid'], 'progress': episode['progress'], 'runtime': episode['runtime']}) + { + "episodeid": episode["ids"]["episodeid"], + "progress": episode["progress"], + "runtime": episode["runtime"], + } + ) # need to calculate the progress in int from progress in percent from Trakt # split episode list into chunks of 50 chunksize = 50 - chunked_episodes = utilities.chunks([{"jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetEpisodeDetails", "params": {"episodeid": episodes[i]['episodeid'], "resume": { - "position": episodes[i]['runtime'] / 100.0 * episodes[i]['progress'], "total": episodes[i]['runtime']}}} for i in range(len(episodes)) if episodes[i]['runtime'] > 0], chunksize) + chunked_episodes = utilities.chunks( + [ + { + "jsonrpc": "2.0", + "id": i, + "method": "VideoLibrary.SetEpisodeDetails", + "params": { + "episodeid": episodes[i]["episodeid"], + "resume": { + "position": episodes[i]["runtime"] + / 100.0 + * episodes[i]["progress"], + "total": episodes[i]["runtime"], + }, + }, + } + for i in range(len(episodes)) + if episodes[i]["runtime"] > 0 + ], + chunksize, + ) i = 0 x = float(len(episodes)) for chunk in chunked_episodes: if self.sync.IsCanceled(): return i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent - self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString( - 32130) % ((i) * chunksize if (i) * chunksize < x else x, x)) + y = ((i / x) * (toPercent - fromPercent)) + fromPercent + self.sync.UpdateProgress( + int(y), + line2=kodiUtilities.getString(32130) + % ((i) * chunksize if (i) * chunksize < x else x, x), + ) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress( - toPercent, line2=kodiUtilities.getString(32131) % len(episodes)) + toPercent, line2=kodiUtilities.getString(32131) % len(episodes) + ) def __syncShowsRatings(self, traktShows, kodiShows, fromPercent, toPercent): - if kodiUtilities.getSettingAsBool('trakt_sync_ratings') and traktShows and not self.sync.IsCanceled(): + if ( + kodiUtilities.getSettingAsBool("trakt_sync_ratings") + and traktShows + and not self.sync.IsCanceled() + ): updateKodiTraktShows = copy.deepcopy(traktShows) updateKodiKodiShows = copy.deepcopy(kodiShows) traktShowsToUpdate = utilities.compareShows( - updateKodiKodiShows, updateKodiTraktShows, kodiUtilities.getSettingAsBool("scrobble_fallback"), rating=True) - if len(traktShowsToUpdate['shows']) == 0: + updateKodiKodiShows, + updateKodiTraktShows, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + rating=True, + ) + if len(traktShowsToUpdate["shows"]) == 0: self.sync.UpdateProgress( - toPercent, line1='', line2=kodiUtilities.getString(32181)) - logger.debug( - "[Episodes Sync] Trakt show ratings are up to date.") + toPercent, line1="", line2=kodiUtilities.getString(32181) + ) + logger.debug("[Episodes Sync] Trakt show ratings are up to date.") else: - logger.debug("[Episodes Sync] %i show(s) will have show ratings added on Trakt" % len( - traktShowsToUpdate['shows'])) + logger.debug( + "[Episodes Sync] %i show(s) will have show ratings added on Trakt" + % len(traktShowsToUpdate["shows"]) + ) - self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString( - 32182) % len(traktShowsToUpdate['shows'])) + self.sync.UpdateProgress( + fromPercent, + line1="", + line2=kodiUtilities.getString(32182) + % len(traktShowsToUpdate["shows"]), + ) self.sync.traktapi.addRating(traktShowsToUpdate) # needs to be restricted, because we can't add a rating to an episode which is not in our Kodi collection - kodiShowsUpdate = utilities.compareShows(updateKodiTraktShows, updateKodiKodiShows, kodiUtilities.getSettingAsBool( - "scrobble_fallback"), rating=True, restrict=True) - - if len(kodiShowsUpdate['shows']) == 0: + kodiShowsUpdate = utilities.compareShows( + updateKodiTraktShows, + updateKodiKodiShows, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + rating=True, + restrict=True, + ) + + if len(kodiShowsUpdate["shows"]) == 0: self.sync.UpdateProgress( - toPercent, line1='', line2=kodiUtilities.getString(32176)) - logger.debug( - "[Episodes Sync] Kodi show ratings are up to date.") + toPercent, line1="", line2=kodiUtilities.getString(32176) + ) + logger.debug("[Episodes Sync] Kodi show ratings are up to date.") else: - logger.debug("[Episodes Sync] %i show(s) will have show ratings added in Kodi" % len( - kodiShowsUpdate['shows'])) + logger.debug( + "[Episodes Sync] %i show(s) will have show ratings added in Kodi" + % len(kodiShowsUpdate["shows"]) + ) shows = [] - for show in kodiShowsUpdate['shows']: + for show in kodiShowsUpdate["shows"]: shows.append( - {'tvshowid': show['tvshowid'], 'rating': show['rating']}) + {"tvshowid": show["tvshowid"], "rating": show["rating"]} + ) # split episode list into chunks of 50 chunksize = 50 - chunked_episodes = utilities.chunks([{"jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetTVShowDetails", - "params": {"tvshowid": shows[i]['tvshowid'], - "userrating": shows[i]['rating']}} for i in range(len(shows))], - chunksize) + chunked_episodes = utilities.chunks( + [ + { + "jsonrpc": "2.0", + "id": i, + "method": "VideoLibrary.SetTVShowDetails", + "params": { + "tvshowid": shows[i]["tvshowid"], + "userrating": shows[i]["rating"], + }, + } + for i in range(len(shows)) + ], + chunksize, + ) i = 0 x = float(len(shows)) for chunk in chunked_episodes: if self.sync.IsCanceled(): return i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent - self.sync.UpdateProgress(int(y), line1='', line2=kodiUtilities.getString( - 32177) % ((i) * chunksize if (i) * chunksize < x else x, x)) + y = ((i / x) * (toPercent - fromPercent)) + fromPercent + self.sync.UpdateProgress( + int(y), + line1="", + line2=kodiUtilities.getString(32177) + % ((i) * chunksize if (i) * chunksize < x else x, x), + ) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress( - toPercent, line2=kodiUtilities.getString(32178) % len(shows)) + toPercent, line2=kodiUtilities.getString(32178) % len(shows) + ) def __syncEpisodeRatings(self, traktShows, kodiShows, fromPercent, toPercent): - if kodiUtilities.getSettingAsBool('trakt_sync_ratings') and traktShows and not self.sync.IsCanceled(): + if ( + kodiUtilities.getSettingAsBool("trakt_sync_ratings") + and traktShows + and not self.sync.IsCanceled() + ): updateKodiTraktShows = copy.deepcopy(traktShows) updateKodiKodiShows = copy.deepcopy(kodiShows) traktShowsToUpdate = utilities.compareEpisodes( - updateKodiKodiShows, updateKodiTraktShows, kodiUtilities.getSettingAsBool("scrobble_fallback"), rating=True) - if len(traktShowsToUpdate['shows']) == 0: + updateKodiKodiShows, + updateKodiTraktShows, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + rating=True, + ) + if len(traktShowsToUpdate["shows"]) == 0: self.sync.UpdateProgress( - toPercent, line1='', line2=kodiUtilities.getString(32181)) - logger.debug( - "[Episodes Sync] Trakt episode ratings are up to date.") + toPercent, line1="", line2=kodiUtilities.getString(32181) + ) + logger.debug("[Episodes Sync] Trakt episode ratings are up to date.") else: - logger.debug("[Episodes Sync] %i show(s) will have episode ratings added on Trakt" % len( - traktShowsToUpdate['shows'])) + logger.debug( + "[Episodes Sync] %i show(s) will have episode ratings added on Trakt" + % len(traktShowsToUpdate["shows"]) + ) - self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString( - 32182) % len(traktShowsToUpdate['shows'])) + self.sync.UpdateProgress( + fromPercent, + line1="", + line2=kodiUtilities.getString(32182) + % len(traktShowsToUpdate["shows"]), + ) self.sync.traktapi.addRating(traktShowsToUpdate) - kodiShowsUpdate = utilities.compareEpisodes(updateKodiTraktShows, updateKodiKodiShows, kodiUtilities.getSettingAsBool( - "scrobble_fallback"), restrict=True, rating=True) - if len(kodiShowsUpdate['shows']) == 0: + kodiShowsUpdate = utilities.compareEpisodes( + updateKodiTraktShows, + updateKodiKodiShows, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + restrict=True, + rating=True, + ) + if len(kodiShowsUpdate["shows"]) == 0: self.sync.UpdateProgress( - toPercent, line1='', line2=kodiUtilities.getString(32173)) - logger.debug( - "[Episodes Sync] Kodi episode ratings are up to date.") + toPercent, line1="", line2=kodiUtilities.getString(32173) + ) + logger.debug("[Episodes Sync] Kodi episode ratings are up to date.") else: - logger.debug("[Episodes Sync] %i show(s) will have episode ratings added in Kodi" % len( - kodiShowsUpdate['shows'])) - for s in ["%s" % self.__getShowAsString(s, short=True) for s in kodiShowsUpdate['shows']]: + logger.debug( + "[Episodes Sync] %i show(s) will have episode ratings added in Kodi" + % len(kodiShowsUpdate["shows"]) + ) + for s in [ + "%s" % self.__getShowAsString(s, short=True) + for s in kodiShowsUpdate["shows"] + ]: logger.debug("[Episodes Sync] Episodes updated: %s" % s) episodes = [] - for show in kodiShowsUpdate['shows']: - for season in show['seasons']: - for episode in season['episodes']: + for show in kodiShowsUpdate["shows"]: + for season in show["seasons"]: + for episode in season["episodes"]: episodes.append( - {'episodeid': episode['ids']['episodeid'], 'rating': episode['rating']}) + { + "episodeid": episode["ids"]["episodeid"], + "rating": episode["rating"], + } + ) # split episode list into chunks of 50 chunksize = 50 - chunked_episodes = utilities.chunks([{"jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetEpisodeDetails", - "params": {"episodeid": episodes[i]['episodeid'], - "userrating": episodes[i]['rating']}} for i in range(len(episodes))], - chunksize) + chunked_episodes = utilities.chunks( + [ + { + "jsonrpc": "2.0", + "id": i, + "method": "VideoLibrary.SetEpisodeDetails", + "params": { + "episodeid": episodes[i]["episodeid"], + "userrating": episodes[i]["rating"], + }, + } + for i in range(len(episodes)) + ], + chunksize, + ) i = 0 x = float(len(episodes)) for chunk in chunked_episodes: if self.sync.IsCanceled(): return i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent - self.sync.UpdateProgress(int(y), line1='', line2=kodiUtilities.getString( - 32174) % ((i) * chunksize if (i) * chunksize < x else x, x)) + y = ((i / x) * (toPercent - fromPercent)) + fromPercent + self.sync.UpdateProgress( + int(y), + line1="", + line2=kodiUtilities.getString(32174) + % ((i) * chunksize if (i) * chunksize < x else x, x), + ) kodiUtilities.kodiJsonRequest(chunk) self.sync.UpdateProgress( - toPercent, line2=kodiUtilities.getString(32175) % len(episodes)) + toPercent, line2=kodiUtilities.getString(32175) % len(episodes) + ) def __getShowAsString(self, show, short=False): p = [] - if 'seasons' in show: - for season in show['seasons']: + if "seasons" in show: + for season in show["seasons"]: s = "" if short: s = ", ".join( - ["S%02dE%02d" % (season['number'], i['number']) for i in season['episodes']]) + [ + "S%02dE%02d" % (season["number"], i["number"]) + for i in season["episodes"] + ] + ) else: episodes = ", ".join( - [str(i) for i in show['shows']['seasons'][season]]) + [str(i) for i in show["shows"]["seasons"][season]] + ) s = "Season: %d, Episodes: %s" % (season, episodes) p.append(s) else: p = ["All"] - if 'tvdb' in show['ids']: - return "%s [tvdb: %s] - %s" % (show['title'], show['ids']['tvdb'], ", ".join(p)) + if "tvdb" in show["ids"]: + return "%s [tvdb: %s] - %s" % ( + show["title"], + show["ids"]["tvdb"], + ", ".join(p), + ) else: - return "%s [tvdb: No id] - %s" % (show['title'], ", ".join(p)) + return "%s [tvdb: No id] - %s" % (show["title"], ", ".join(p)) diff --git a/resources/lib/syncMovies.py b/resources/lib/syncMovies.py index 89bf500e..56af70ca 100644 --- a/resources/lib/syncMovies.py +++ b/resources/lib/syncMovies.py @@ -8,20 +8,25 @@ logger = logging.getLogger(__name__) -class SyncMovies(): +class SyncMovies: def __init__(self, sync, progress): self.sync = sync if self.sync.show_notification: - kodiUtilities.notification('%s %s' % (kodiUtilities.getString( - 32045), kodiUtilities.getString(32046)), kodiUtilities.getString(32061)) # Sync started + kodiUtilities.notification( + "%s %s" + % (kodiUtilities.getString(32045), kodiUtilities.getString(32046)), + kodiUtilities.getString(32061), + ) # Sync started if sync.show_progress and not sync.run_silent: - progress.create("%s %s" % (kodiUtilities.getString( - 32045), kodiUtilities.getString(32046)), "") + progress.create( + "%s %s" + % (kodiUtilities.getString(32045), kodiUtilities.getString(32046)), + "", + ) kodiMovies = self.__kodiLoadMovies() if not isinstance(kodiMovies, list) and not kodiMovies: - logger.debug( - "[Movies Sync] Kodi movie list is empty, aborting movie Sync.") + logger.debug("[Movies Sync] Kodi movie list is empty, aborting movie Sync.") if sync.show_progress and not sync.run_silent: progress.close() return @@ -29,7 +34,8 @@ def __init__(self, sync, progress): traktMovies = self.__traktLoadMovies() except Exception: logger.debug( - "[Movies Sync] Error getting Trakt.tv movie list, aborting movie Sync.") + "[Movies Sync] Error getting Trakt.tv movie list, aborting movie Sync." + ) if sync.show_progress and not sync.run_silent: progress.close() return @@ -50,24 +56,49 @@ def __init__(self, sync, progress): if self.sync.show_progress and not self.sync.run_silent: self.sync.UpdateProgress( - 100, line1=kodiUtilities.getString(32066), line2=" ", line3=" ") + 100, line1=kodiUtilities.getString(32066), line2=" ", line3=" " + ) progress.close() if self.sync.show_notification: - kodiUtilities.notification('%s %s' % (kodiUtilities.getString( - 32045), kodiUtilities.getString(32046)), kodiUtilities.getString(32062)) # Sync complete - - logger.debug("[Movies Sync] Movies on Trakt.tv (%d), movies in Kodi (%d)." % ( - len(traktMovies), len(kodiMovies))) + kodiUtilities.notification( + "%s %s" + % (kodiUtilities.getString(32045), kodiUtilities.getString(32046)), + kodiUtilities.getString(32062), + ) # Sync complete + + logger.debug( + "[Movies Sync] Movies on Trakt.tv (%d), movies in Kodi (%d)." + % (len(traktMovies), len(kodiMovies)) + ) logger.debug("[Movies Sync] Complete.") def __kodiLoadMovies(self): self.sync.UpdateProgress(1, line2=kodiUtilities.getString(32079)) logger.debug("[Movies Sync] Getting movie data from Kodi") - data = kodiUtilities.kodiJsonRequest({'jsonrpc': '2.0', 'id': 0, 'method': 'VideoLibrary.GetMovies', 'params': {'properties': [ - 'title', 'imdbnumber', 'uniqueid', 'year', 'playcount', 'lastplayed', 'file', 'dateadded', 'runtime', 'userrating']}}) - if data['limits']['total'] == 0: + data = kodiUtilities.kodiJsonRequest( + { + "jsonrpc": "2.0", + "id": 0, + "method": "VideoLibrary.GetMovies", + "params": { + "properties": [ + "title", + "imdbnumber", + "uniqueid", + "year", + "playcount", + "lastplayed", + "file", + "dateadded", + "runtime", + "userrating", + ] + }, + } + ) + if data["limits"]["total"] == 0: logger.debug("[Movies Sync] Kodi JSON request was empty.") return @@ -78,8 +109,11 @@ def __kodiLoadMovies(self): return kodi_movies def __traktLoadMovies(self): - self.sync.UpdateProgress(10, line1=kodiUtilities.getString( - 32079), line2=kodiUtilities.getString(32081)) + self.sync.UpdateProgress( + 10, + line1=kodiUtilities.getString(32079), + line2=kodiUtilities.getString(32081), + ) logger.debug("[Movies Sync] Getting movie collection from Trakt.tv") @@ -89,7 +123,7 @@ def __traktLoadMovies(self): self.sync.UpdateProgress(17, line2=kodiUtilities.getString(32082)) traktMovies = self.sync.traktapi.getMoviesWatched(traktMovies) - if kodiUtilities.getSettingAsBool('trakt_sync_ratings'): + if kodiUtilities.getSettingAsBool("trakt_sync_ratings"): traktMovies = self.sync.traktapi.getMoviesRated(traktMovies) traktMovies = list(traktMovies.items()) @@ -104,65 +138,81 @@ def __traktLoadMovies(self): return movies def __traktLoadMoviesPlaybackProgress(self, fromPercent, toPercent): - if kodiUtilities.getSettingAsBool('trakt_movie_playback') and not self.sync.IsCanceled(): - self.sync.UpdateProgress( - fromPercent, line2=kodiUtilities.getString(32122)) + if ( + kodiUtilities.getSettingAsBool("trakt_movie_playback") + and not self.sync.IsCanceled() + ): + self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString(32122)) - logger.debug( - '[Movies Sync] Getting playback progress from Trakt.tv') + logger.debug("[Movies Sync] Getting playback progress from Trakt.tv") try: traktProgressMovies = self.sync.traktapi.getMoviePlaybackProgress() except Exception: logger.debug( - "[Movies Sync] Invalid Trakt.tv playback progress list, possible error getting data from Trakt, aborting Trakt.tv playback update.") + "[Movies Sync] Invalid Trakt.tv playback progress list, possible error getting data from Trakt, aborting Trakt.tv playback update." + ) return False i = 0 x = float(len(traktProgressMovies)) - moviesProgress = {'movies': []} + moviesProgress = {"movies": []} for movie in traktProgressMovies: i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent + y = ((i / x) * (toPercent - fromPercent)) + fromPercent self.sync.UpdateProgress( - int(y), line2=kodiUtilities.getString(32123) % (i, x)) + int(y), line2=kodiUtilities.getString(32123) % (i, x) + ) # will keep the data in python structures - just like the KODI response movie = movie.to_dict() - moviesProgress['movies'].append(movie) + moviesProgress["movies"].append(movie) - self.sync.UpdateProgress( - toPercent, line2=kodiUtilities.getString(32124)) + self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString(32124)) return moviesProgress - def __addMoviesToTraktCollection(self, kodiMovies, traktMovies, fromPercent, toPercent): - if kodiUtilities.getSettingAsBool('add_movies_to_trakt') and not self.sync.IsCanceled(): + def __addMoviesToTraktCollection( + self, kodiMovies, traktMovies, fromPercent, toPercent + ): + if ( + kodiUtilities.getSettingAsBool("add_movies_to_trakt") + and not self.sync.IsCanceled() + ): addTraktMovies = copy.deepcopy(traktMovies) addKodiMovies = copy.deepcopy(kodiMovies) traktMoviesToAdd = utilities.compareMovies( - addKodiMovies, addTraktMovies, kodiUtilities.getSettingAsBool("scrobble_fallback")) + addKodiMovies, + addTraktMovies, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + ) utilities.sanitizeMovies(traktMoviesToAdd) logger.debug( - "[Movies Sync] Compared movies, found %s to add." % len(traktMoviesToAdd)) + "[Movies Sync] Compared movies, found %s to add." + % len(traktMoviesToAdd) + ) if len(traktMoviesToAdd) == 0: self.sync.UpdateProgress( - toPercent, line2=kodiUtilities.getString(32084)) - logger.debug( - "[Movies Sync] Trakt.tv movie collection is up to date.") + toPercent, line2=kodiUtilities.getString(32084) + ) + logger.debug("[Movies Sync] Trakt.tv movie collection is up to date.") return - titles = ", ".join(["%s" % (m['title']) for m in traktMoviesToAdd]) - logger.debug("[Movies Sync] %i movie(s) will be added to Trakt.tv collection." % len( - traktMoviesToAdd)) + titles = ", ".join(["%s" % (m["title"]) for m in traktMoviesToAdd]) + logger.debug( + "[Movies Sync] %i movie(s) will be added to Trakt.tv collection." + % len(traktMoviesToAdd) + ) logger.debug("[Movies Sync] Movies to add : %s" % titles) self.sync.UpdateProgress( - fromPercent, line2=kodiUtilities.getString(32063) % len(traktMoviesToAdd)) + fromPercent, + line2=kodiUtilities.getString(32063) % len(traktMoviesToAdd), + ) - moviesToAdd = {'movies': traktMoviesToAdd} + moviesToAdd = {"movies": traktMoviesToAdd} # logger.debug("Movies to add: %s" % moviesToAdd) try: self.sync.traktapi.addToCollection(moviesToAdd) @@ -171,75 +221,105 @@ def __addMoviesToTraktCollection(self, kodiMovies, traktMovies, fromPercent, toP logging.fatal(message) self.sync.UpdateProgress( - toPercent, line2=kodiUtilities.getString(32085) % len(traktMoviesToAdd)) - - def __deleteMoviesFromTraktCollection(self, traktMovies, kodiMovies, fromPercent, toPercent): - - if kodiUtilities.getSettingAsBool('clean_trakt_movies') and not self.sync.IsCanceled(): + toPercent, line2=kodiUtilities.getString(32085) % len(traktMoviesToAdd) + ) + + def __deleteMoviesFromTraktCollection( + self, traktMovies, kodiMovies, fromPercent, toPercent + ): + if ( + kodiUtilities.getSettingAsBool("clean_trakt_movies") + and not self.sync.IsCanceled() + ): removeTraktMovies = copy.deepcopy(traktMovies) removeKodiMovies = copy.deepcopy(kodiMovies) logger.debug("[Movies Sync] Starting to remove.") traktMoviesToRemove = utilities.compareMovies( - removeTraktMovies, removeKodiMovies, kodiUtilities.getSettingAsBool("scrobble_fallback")) + removeTraktMovies, + removeKodiMovies, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + ) utilities.sanitizeMovies(traktMoviesToRemove) - logger.debug("[Movies Sync] Compared movies, found %s to remove." % len( - traktMoviesToRemove)) + logger.debug( + "[Movies Sync] Compared movies, found %s to remove." + % len(traktMoviesToRemove) + ) if len(traktMoviesToRemove) == 0: self.sync.UpdateProgress( - toPercent, line2=kodiUtilities.getString(32091)) + toPercent, line2=kodiUtilities.getString(32091) + ) logger.debug( - "[Movies Sync] Trakt.tv movie collection is clean, no movies to remove.") + "[Movies Sync] Trakt.tv movie collection is clean, no movies to remove." + ) return - titles = ", ".join(["%s" % (m['title']) - for m in traktMoviesToRemove]) - logger.debug("[Movies Sync] %i movie(s) will be removed from Trakt.tv collection." % len( - traktMoviesToRemove)) + titles = ", ".join(["%s" % (m["title"]) for m in traktMoviesToRemove]) + logger.debug( + "[Movies Sync] %i movie(s) will be removed from Trakt.tv collection." + % len(traktMoviesToRemove) + ) logger.debug("[Movies Sync] Movies removed: %s" % titles) - self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString( - 32076) % len(traktMoviesToRemove)) + self.sync.UpdateProgress( + fromPercent, + line2=kodiUtilities.getString(32076) % len(traktMoviesToRemove), + ) - moviesToRemove = {'movies': traktMoviesToRemove} + moviesToRemove = {"movies": traktMoviesToRemove} try: self.sync.traktapi.removeFromCollection(moviesToRemove) except Exception as ex: message = utilities.createError(ex) logging.fatal(message) - self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString( - 32092) % len(traktMoviesToRemove)) - - def __addMoviesToTraktWatched(self, kodiMovies, traktMovies, fromPercent, toPercent): - if kodiUtilities.getSettingAsBool('trakt_movie_playcount') and not self.sync.IsCanceled(): + self.sync.UpdateProgress( + toPercent, + line2=kodiUtilities.getString(32092) % len(traktMoviesToRemove), + ) + + def __addMoviesToTraktWatched( + self, kodiMovies, traktMovies, fromPercent, toPercent + ): + if ( + kodiUtilities.getSettingAsBool("trakt_movie_playcount") + and not self.sync.IsCanceled() + ): updateTraktTraktMovies = copy.deepcopy(traktMovies) updateTraktKodiMovies = copy.deepcopy(kodiMovies) traktMoviesToUpdate = utilities.compareMovies( - updateTraktKodiMovies, updateTraktTraktMovies, kodiUtilities.getSettingAsBool("scrobble_fallback"), watched=True) + updateTraktKodiMovies, + updateTraktTraktMovies, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + watched=True, + ) utilities.sanitizeMovies(traktMoviesToUpdate) if len(traktMoviesToUpdate) == 0: self.sync.UpdateProgress( - toPercent, line2=kodiUtilities.getString(32086)) - logger.debug( - "[Movies Sync] Trakt.tv movie playcount is up to date") + toPercent, line2=kodiUtilities.getString(32086) + ) + logger.debug("[Movies Sync] Trakt.tv movie playcount is up to date") return - titles = ", ".join(["%s" % (m['title']) - for m in traktMoviesToUpdate]) - logger.debug("[Movies Sync] %i movie(s) playcount will be updated on Trakt.tv" % len( - traktMoviesToUpdate)) + titles = ", ".join(["%s" % (m["title"]) for m in traktMoviesToUpdate]) + logger.debug( + "[Movies Sync] %i movie(s) playcount will be updated on Trakt.tv" + % len(traktMoviesToUpdate) + ) logger.debug("[Movies Sync] Movies updated: %s" % titles) - self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString( - 32064) % len(traktMoviesToUpdate)) + self.sync.UpdateProgress( + fromPercent, + line2=kodiUtilities.getString(32064) % len(traktMoviesToUpdate), + ) # Send request to update playcounts on Trakt.tv chunksize = 200 chunked_movies = utilities.chunks( - [movie for movie in traktMoviesToUpdate], chunksize) + [movie for movie in traktMoviesToUpdate], chunksize + ) errorcount = 0 i = 0 x = float(len(traktMoviesToUpdate)) @@ -247,11 +327,14 @@ def __addMoviesToTraktWatched(self, kodiMovies, traktMovies, fromPercent, toPerc if self.sync.IsCanceled(): return i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent - self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString( - 32093) % ((i) * chunksize if (i) * chunksize < x else x, x)) + y = ((i / x) * (toPercent - fromPercent)) + fromPercent + self.sync.UpdateProgress( + int(y), + line2=kodiUtilities.getString(32093) + % ((i) * chunksize if (i) * chunksize < x else x, x), + ) - params = {'movies': chunk} + params = {"movies": chunk} # logger.debug("moviechunk: %s" % params) try: self.sync.traktapi.addToHistory(params) @@ -260,153 +343,264 @@ def __addMoviesToTraktWatched(self, kodiMovies, traktMovies, fromPercent, toPerc logging.fatal(message) errorcount += 1 - logger.debug( - "[Movies Sync] Movies updated: %d error(s)" % errorcount) - self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString( - 32087) % len(traktMoviesToUpdate)) + logger.debug("[Movies Sync] Movies updated: %d error(s)" % errorcount) + self.sync.UpdateProgress( + toPercent, + line2=kodiUtilities.getString(32087) % len(traktMoviesToUpdate), + ) def __addMoviesToKodiWatched(self, traktMovies, kodiMovies, fromPercent, toPercent): - - if kodiUtilities.getSettingAsBool('kodi_movie_playcount') and not self.sync.IsCanceled(): + if ( + kodiUtilities.getSettingAsBool("kodi_movie_playcount") + and not self.sync.IsCanceled() + ): updateKodiTraktMovies = copy.deepcopy(traktMovies) updateKodiKodiMovies = copy.deepcopy(kodiMovies) - kodiMoviesToUpdate = utilities.compareMovies(updateKodiTraktMovies, updateKodiKodiMovies, kodiUtilities.getSettingAsBool( - "scrobble_fallback"), watched=True, restrict=True) + kodiMoviesToUpdate = utilities.compareMovies( + updateKodiTraktMovies, + updateKodiKodiMovies, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + watched=True, + restrict=True, + ) if len(kodiMoviesToUpdate) == 0: self.sync.UpdateProgress( - toPercent, line2=kodiUtilities.getString(32088)) - logger.debug( - "[Movies Sync] Kodi movie playcount is up to date.") + toPercent, line2=kodiUtilities.getString(32088) + ) + logger.debug("[Movies Sync] Kodi movie playcount is up to date.") return - titles = ", ".join(["%s" % (m['title']) - for m in kodiMoviesToUpdate]) - logger.debug("[Movies Sync] %i movie(s) playcount will be updated in Kodi" % len( - kodiMoviesToUpdate)) + titles = ", ".join(["%s" % (m["title"]) for m in kodiMoviesToUpdate]) + logger.debug( + "[Movies Sync] %i movie(s) playcount will be updated in Kodi" + % len(kodiMoviesToUpdate) + ) logger.debug("[Movies Sync] Movies to add: %s" % titles) - self.sync.UpdateProgress(fromPercent, line2=kodiUtilities.getString( - 32065) % len(kodiMoviesToUpdate)) + self.sync.UpdateProgress( + fromPercent, + line2=kodiUtilities.getString(32065) % len(kodiMoviesToUpdate), + ) # split movie list into chunks of 50 chunksize = 50 - chunked_movies = utilities.chunks([{"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": {"movieid": kodiMoviesToUpdate[i]['movieid'], "playcount": kodiMoviesToUpdate[i] - ['plays'], "lastplayed": utilities.convertUtcToDateTime(kodiMoviesToUpdate[i]['last_watched_at'])}, "id": i} for i in range(len(kodiMoviesToUpdate))], chunksize) + chunked_movies = utilities.chunks( + [ + { + "jsonrpc": "2.0", + "method": "VideoLibrary.SetMovieDetails", + "params": { + "movieid": kodiMoviesToUpdate[i]["movieid"], + "playcount": kodiMoviesToUpdate[i]["plays"], + "lastplayed": utilities.convertUtcToDateTime( + kodiMoviesToUpdate[i]["last_watched_at"] + ), + }, + "id": i, + } + for i in range(len(kodiMoviesToUpdate)) + ], + chunksize, + ) i = 0 x = float(len(kodiMoviesToUpdate)) for chunk in chunked_movies: if self.sync.IsCanceled(): return i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent - self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString( - 32089) % ((i) * chunksize if (i) * chunksize < x else x, x)) + y = ((i / x) * (toPercent - fromPercent)) + fromPercent + self.sync.UpdateProgress( + int(y), + line2=kodiUtilities.getString(32089) + % ((i) * chunksize if (i) * chunksize < x else x, x), + ) kodiUtilities.kodiJsonRequest(chunk) - self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString( - 32090) % len(kodiMoviesToUpdate)) + self.sync.UpdateProgress( + toPercent, + line2=kodiUtilities.getString(32090) % len(kodiMoviesToUpdate), + ) def __addMovieProgressToKodi(self, traktMovies, kodiMovies, fromPercent, toPercent): - - if kodiUtilities.getSettingAsBool('trakt_movie_playback') and traktMovies and not self.sync.IsCanceled(): + if ( + kodiUtilities.getSettingAsBool("trakt_movie_playback") + and traktMovies + and not self.sync.IsCanceled() + ): updateKodiTraktMovies = copy.deepcopy(traktMovies) updateKodiKodiMovies = copy.deepcopy(kodiMovies) - kodiMoviesToUpdate = utilities.compareMovies(updateKodiTraktMovies['movies'], updateKodiKodiMovies, kodiUtilities.getSettingAsBool( - "scrobble_fallback"), restrict=True, playback=True) + kodiMoviesToUpdate = utilities.compareMovies( + updateKodiTraktMovies["movies"], + updateKodiKodiMovies, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + restrict=True, + playback=True, + ) if len(kodiMoviesToUpdate) == 0: self.sync.UpdateProgress( - toPercent, line1='', line2=kodiUtilities.getString(32125)) - logger.debug( - "[Movies Sync] Kodi movie progress is up to date.") + toPercent, line1="", line2=kodiUtilities.getString(32125) + ) + logger.debug("[Movies Sync] Kodi movie progress is up to date.") return - logger.debug("[Movies Sync] %i movie(s) progress will be updated in Kodi" % len( - kodiMoviesToUpdate)) + logger.debug( + "[Movies Sync] %i movie(s) progress will be updated in Kodi" + % len(kodiMoviesToUpdate) + ) - self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString( - 32126) % len(kodiMoviesToUpdate)) + self.sync.UpdateProgress( + fromPercent, + line1="", + line2=kodiUtilities.getString(32126) % len(kodiMoviesToUpdate), + ) # If library item doesn't have a runtime set get it from # Trakt to avoid later using 0 in runtime * progress_pct. for movie in kodiMoviesToUpdate: - if not movie['runtime']: - movie['runtime'] = self.sync.traktapi.getMovieSummary( - movie['ids']['trakt'], extended='full').runtime * 60 + if not movie["runtime"]: + movie["runtime"] = ( + self.sync.traktapi.getMovieSummary( + movie["ids"]["trakt"], extended="full" + ).runtime + * 60 + ) # need to calculate the progress in int from progress in percent from Trakt # split movie list into chunks of 50 chunksize = 50 - chunked_movies = utilities.chunks([{"jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetMovieDetails", "params": {"movieid": kodiMoviesToUpdate[i]['movieid'], "resume": { - "position": kodiMoviesToUpdate[i]['runtime'] / 100.0 * kodiMoviesToUpdate[i]['progress'], "total": kodiMoviesToUpdate[i]['runtime']}}} for i in range(len(kodiMoviesToUpdate)) if kodiMoviesToUpdate[i]['runtime'] > 0], chunksize) + chunked_movies = utilities.chunks( + [ + { + "jsonrpc": "2.0", + "id": i, + "method": "VideoLibrary.SetMovieDetails", + "params": { + "movieid": kodiMoviesToUpdate[i]["movieid"], + "resume": { + "position": kodiMoviesToUpdate[i]["runtime"] + / 100.0 + * kodiMoviesToUpdate[i]["progress"], + "total": kodiMoviesToUpdate[i]["runtime"], + }, + }, + } + for i in range(len(kodiMoviesToUpdate)) + if kodiMoviesToUpdate[i]["runtime"] > 0 + ], + chunksize, + ) i = 0 x = float(len(kodiMoviesToUpdate)) for chunk in chunked_movies: if self.sync.IsCanceled(): return i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent - self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString( - 32127) % ((i) * chunksize if (i) * chunksize < x else x, x)) + y = ((i / x) * (toPercent - fromPercent)) + fromPercent + self.sync.UpdateProgress( + int(y), + line2=kodiUtilities.getString(32127) + % ((i) * chunksize if (i) * chunksize < x else x, x), + ) kodiUtilities.kodiJsonRequest(chunk) - self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString( - 32128) % len(kodiMoviesToUpdate)) + self.sync.UpdateProgress( + toPercent, + line2=kodiUtilities.getString(32128) % len(kodiMoviesToUpdate), + ) def __syncMovieRatings(self, traktMovies, kodiMovies, fromPercent, toPercent): - - if kodiUtilities.getSettingAsBool('trakt_sync_ratings') and traktMovies and not self.sync.IsCanceled(): + if ( + kodiUtilities.getSettingAsBool("trakt_sync_ratings") + and traktMovies + and not self.sync.IsCanceled() + ): updateKodiTraktMovies = copy.deepcopy(traktMovies) updateKodiKodiMovies = copy.deepcopy(kodiMovies) traktMoviesToUpdate = utilities.compareMovies( - updateKodiKodiMovies, updateKodiTraktMovies, kodiUtilities.getSettingAsBool("scrobble_fallback"), rating=True) + updateKodiKodiMovies, + updateKodiTraktMovies, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + rating=True, + ) if len(traktMoviesToUpdate) == 0: self.sync.UpdateProgress( - toPercent, line1='', line2=kodiUtilities.getString(32179)) - logger.debug( - "[Movies Sync] Trakt movie ratings are up to date.") + toPercent, line1="", line2=kodiUtilities.getString(32179) + ) + logger.debug("[Movies Sync] Trakt movie ratings are up to date.") else: - logger.debug("[Movies Sync] %i movie(s) ratings will be updated on Trakt" % len( - traktMoviesToUpdate)) + logger.debug( + "[Movies Sync] %i movie(s) ratings will be updated on Trakt" + % len(traktMoviesToUpdate) + ) - self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString( - 32180) % len(traktMoviesToUpdate)) + self.sync.UpdateProgress( + fromPercent, + line1="", + line2=kodiUtilities.getString(32180) % len(traktMoviesToUpdate), + ) - moviesRatings = {'movies': traktMoviesToUpdate} + moviesRatings = {"movies": traktMoviesToUpdate} self.sync.traktapi.addRating(moviesRatings) - kodiMoviesToUpdate = utilities.compareMovies(updateKodiTraktMovies, updateKodiKodiMovies, kodiUtilities.getSettingAsBool( - "scrobble_fallback"), restrict=True, rating=True) + kodiMoviesToUpdate = utilities.compareMovies( + updateKodiTraktMovies, + updateKodiKodiMovies, + kodiUtilities.getSettingAsBool("scrobble_fallback"), + restrict=True, + rating=True, + ) if len(kodiMoviesToUpdate) == 0: self.sync.UpdateProgress( - toPercent, line1='', line2=kodiUtilities.getString(32169)) - logger.debug( - "[Movies Sync] Kodi movie ratings are up to date.") + toPercent, line1="", line2=kodiUtilities.getString(32169) + ) + logger.debug("[Movies Sync] Kodi movie ratings are up to date.") else: - logger.debug("[Movies Sync] %i movie(s) ratings will be updated in Kodi" % len( - kodiMoviesToUpdate)) + logger.debug( + "[Movies Sync] %i movie(s) ratings will be updated in Kodi" + % len(kodiMoviesToUpdate) + ) - self.sync.UpdateProgress(fromPercent, line1='', line2=kodiUtilities.getString( - 32170) % len(kodiMoviesToUpdate)) + self.sync.UpdateProgress( + fromPercent, + line1="", + line2=kodiUtilities.getString(32170) % len(kodiMoviesToUpdate), + ) # split movie list into chunks of 50 chunksize = 50 - chunked_movies = utilities.chunks([{"jsonrpc": "2.0", "id": i, "method": "VideoLibrary.SetMovieDetails", - "params": {"movieid": kodiMoviesToUpdate[i]['movieid'], - "userrating": kodiMoviesToUpdate[i]['rating']}} for i in range(len(kodiMoviesToUpdate))], - chunksize) + chunked_movies = utilities.chunks( + [ + { + "jsonrpc": "2.0", + "id": i, + "method": "VideoLibrary.SetMovieDetails", + "params": { + "movieid": kodiMoviesToUpdate[i]["movieid"], + "userrating": kodiMoviesToUpdate[i]["rating"], + }, + } + for i in range(len(kodiMoviesToUpdate)) + ], + chunksize, + ) i = 0 x = float(len(kodiMoviesToUpdate)) for chunk in chunked_movies: if self.sync.IsCanceled(): return i += 1 - y = ((i / x) * (toPercent-fromPercent)) + fromPercent - self.sync.UpdateProgress(int(y), line2=kodiUtilities.getString( - 32171) % ((i) * chunksize if (i) * chunksize < x else x, x)) + y = ((i / x) * (toPercent - fromPercent)) + fromPercent + self.sync.UpdateProgress( + int(y), + line2=kodiUtilities.getString(32171) + % ((i) * chunksize if (i) * chunksize < x else x, x), + ) kodiUtilities.kodiJsonRequest(chunk) - self.sync.UpdateProgress(toPercent, line2=kodiUtilities.getString( - 32172) % len(kodiMoviesToUpdate)) + self.sync.UpdateProgress( + toPercent, + line2=kodiUtilities.getString(32172) % len(kodiMoviesToUpdate), + ) diff --git a/resources/lib/traktContextMenu.py b/resources/lib/traktContextMenu.py index 5ff9b5b1..00ce5c36 100644 --- a/resources/lib/traktContextMenu.py +++ b/resources/lib/traktContextMenu.py @@ -22,16 +22,23 @@ class traktContextMenu(xbmcgui.WindowXMLDialog): action = None def __new__(cls, media_type=None, buttons=None): - return super(traktContextMenu, cls).__new__(cls, "script-trakt-ContextMenu.xml", __addon__.getAddonInfo('path'), - media_type=media_type, buttons=None) + return super(traktContextMenu, cls).__new__( + cls, + "script-trakt-ContextMenu.xml", + __addon__.getAddonInfo("path"), + media_type=media_type, + buttons=None, + ) def __init__(self, *args, **kwargs): - self.buttons = kwargs['buttons'] - self.media_type = kwargs['media_type'] + self.buttons = kwargs["buttons"] + self.media_type = kwargs["media_type"] super(traktContextMenu, self).__init__() def onInit(self): - mange_string = getString(32133) if isMovie(self.media_type) else getString(32134) + mange_string = ( + getString(32133) if isMovie(self.media_type) else getString(32134) + ) rate_string = getString(32137) if isShow(self.media_type): rate_string = getString(32138) @@ -40,16 +47,33 @@ def onInit(self): elif isEpisode(self.media_type): rate_string = getString(32139) - actions = [mange_string, getString(32135), getString(32136), rate_string, getString(32140), getString(32141), getString(32142), getString(32143)] - keys = ["itemlists", "removefromlist", "addtowatchlist", "rate", "togglewatched", "managelists", "updatetags", - "sync"] + actions = [ + mange_string, + getString(32135), + getString(32136), + rate_string, + getString(32140), + getString(32141), + getString(32142), + getString(32143), + ] + keys = [ + "itemlists", + "removefromlist", + "addtowatchlist", + "rate", + "togglewatched", + "managelists", + "updatetags", + "sync", + ] - l = self.getControl(ACTION_LIST) + actionList = self.getControl(ACTION_LIST) for i in range(len(actions)): if keys[i] in self.buttons: - l.addItem(self.newListItem(actions[i], id=keys[i])) + actionList.addItem(self.newListItem(actions[i], id=keys[i])) - self.setFocus(l) + self.setFocus(actionList) def newListItem(self, label, selected=False, *args, **kwargs): item = xbmcgui.ListItem(label) @@ -59,14 +83,14 @@ def newListItem(self, label, selected=False, *args, **kwargs): return item def onAction(self, action): - if not action.getId() in ACTION_ITEM_SELECT: + if action.getId() not in ACTION_ITEM_SELECT: if action in ACTION_CLOSE_LIST: self.close() if action in ACTION_ITEM_SELECT: cID = self.getFocusId() if cID == ACTION_LIST: - l = self.getControl(cID) - item = l.getSelectedItem() - self.action = item.getProperty('id') + control = self.getControl(cID) + item = control.getSelectedItem() + self.action = item.getProperty("id") self.close() diff --git a/resources/lib/traktapi.py b/resources/lib/traktapi.py index 8c823aaf..30b265ec 100644 --- a/resources/lib/traktapi.py +++ b/resources/lib/traktapi.py @@ -3,23 +3,29 @@ import logging import time from json import dumps, loads -from sys import version_info import xbmcaddon from resources.lib import deviceAuthDialog -from resources.lib.kodiUtilities import (checkAndConfigureProxy, getSetting, - getSettingAsInt, getString, - notification, setSetting) -from resources.lib.utilities import (createError, findEpisodeMatchInList, - findMovieMatchInList, - findSeasonMatchInList, - findShowMatchInList) +from resources.lib.kodiUtilities import ( + checkAndConfigureProxy, + getSetting, + getSettingAsInt, + getString, + notification, + setSetting, +) +from resources.lib.utilities import ( + findEpisodeMatchInList, + findMovieMatchInList, + findSeasonMatchInList, + findShowMatchInList, +) from trakt import Trakt from trakt.objects import Movie, Show # read settings -__addon__ = xbmcaddon.Addon('script.trakt') -__addonversion__ = __addon__.getAddonInfo('version') +__addon__ = xbmcaddon.Addon("script.trakt") +__addonversion__ = __addon__.getAddonInfo("version") logger = logging.getLogger(__name__) @@ -33,28 +39,22 @@ def __init__(self, force=False): proxyURL = checkAndConfigureProxy() if proxyURL: - Trakt.http.proxies = { - 'http': proxyURL, - 'https': proxyURL - } + Trakt.http.proxies = {"http": proxyURL, "https": proxyURL} # Configure Trakt.configuration.defaults.client( - id=self.__client_id, - secret=self.__client_secret + id=self.__client_id, secret=self.__client_secret ) # Bind event - Trakt.on('oauth.token_refreshed', self.on_token_refreshed) + Trakt.on("oauth.token_refreshed", self.on_token_refreshed) - Trakt.configuration.defaults.oauth( - refresh=True - ) + Trakt.configuration.defaults.oauth(refresh=True) - if getSetting('authorization') and not force: - self.authorization = loads(getSetting('authorization')) + if getSetting("authorization") and not force: + self.authorization = loads(getSetting("authorization")) else: - last_reminder = getSettingAsInt('last_reminder') + last_reminder = getSettingAsInt("last_reminder") now = int(time.time()) if last_reminder >= 0 and last_reminder < now - (24 * 60 * 60) or force: self.login() @@ -62,38 +62,45 @@ def __init__(self, force=False): def login(self): # Request new device code with Trakt.configuration.http(timeout=90): - code = Trakt['oauth/device'].code() + code = Trakt["oauth/device"].code() if not code: - logger.debug('Error can not reach trakt') + logger.debug("Error can not reach trakt") notification(getString(32024), getString(32023)) else: # Construct device authentication poller - poller = Trakt['oauth/device'].poll(**code)\ - .on('aborted', self.on_aborted)\ - .on('authenticated', self.on_authenticated)\ - .on('expired', self.on_expired)\ - .on('poll', self.on_poll) + poller = ( + Trakt["oauth/device"] + .poll(**code) + .on("aborted", self.on_aborted) + .on("authenticated", self.on_authenticated) + .on("expired", self.on_expired) + .on("poll", self.on_poll) + ) # Start polling for authentication token poller.start(daemon=False) - logger.debug('Enter the code "%s" at %s to authenticate your account' % ( - code.get('user_code'), - code.get('verification_url') - )) - - self.authDialog = deviceAuthDialog.DeviceAuthDialog('script-trakt-DeviceAuthDialog.xml', __addon__.getAddonInfo('path'), - code=code.get('user_code'), url=code.get('verification_url')) + logger.debug( + 'Enter the code "%s" at %s to authenticate your account' + % (code.get("user_code"), code.get("verification_url")) + ) + + self.authDialog = deviceAuthDialog.DeviceAuthDialog( + "script-trakt-DeviceAuthDialog.xml", + __addon__.getAddonInfo("path"), + code=code.get("user_code"), + url=code.get("verification_url"), + ) self.authDialog.doModal() del self.authDialog def on_aborted(self): """Triggered when device authentication was aborted (either with `DeviceOAuthPoller.stop()` - or via the "poll" event)""" + or via the "poll" event)""" - logger.debug('Authentication aborted') + logger.debug("Authentication aborted") self.authDialog.close() def on_authenticated(self, token): @@ -103,8 +110,8 @@ def on_authenticated(self, token): :type token: dict """ self.authorization = token - setSetting('authorization', dumps(self.authorization)) - logger.debug('Authentication complete: %r' % token) + setSetting("authorization", dumps(self.authorization)) + logger.debug("Authentication complete: %r" % token) self.authDialog.close() notification(getString(32157), getString(32152), 3000) self.updateUser() @@ -112,7 +119,7 @@ def on_authenticated(self, token): def on_expired(self): """Triggered when the device authentication code has expired""" - logger.debug('Authentication expired') + logger.debug("Authentication expired") self.authDialog.close() def on_poll(self, callback): @@ -128,37 +135,34 @@ def on_poll(self, callback): def on_token_refreshed(self, response): # OAuth token refreshed, save token for future calls self.authorization = response - setSetting('authorization', dumps(self.authorization)) + setSetting("authorization", dumps(self.authorization)) - logger.debug('Token refreshed') + logger.debug("Token refreshed") def updateUser(self): user = self.getUser() - if user and 'user' in user: - setSetting('user', user['user']['username']) + if user and "user" in user: + setSetting("user", user["user"]["username"]) else: - setSetting('user', '') + setSetting("user", "") def scrobbleEpisode(self, show, episode, percent, status): result = None with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - if status == 'start': - result = Trakt['scrobble'].start( - show=show, - episode=episode, - progress=percent) - elif status == 'pause': - result = Trakt['scrobble'].pause( - show=show, - episode=episode, - progress=percent) - elif status == 'stop': - result = Trakt['scrobble'].stop( - show=show, - episode=episode, - progress=percent) + if status == "start": + result = Trakt["scrobble"].start( + show=show, episode=episode, progress=percent + ) + elif status == "pause": + result = Trakt["scrobble"].pause( + show=show, episode=episode, progress=percent + ) + elif status == "stop": + result = Trakt["scrobble"].stop( + show=show, episode=episode, progress=percent + ) else: logger.debug("scrobble() Bad scrobble status") return result @@ -168,18 +172,12 @@ def scrobbleMovie(self, movie, percent, status): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - if status == 'start': - result = Trakt['scrobble'].start( - movie=movie, - progress=percent) - elif status == 'pause': - result = Trakt['scrobble'].pause( - movie=movie, - progress=percent) - elif status == 'stop': - result = Trakt['scrobble'].stop( - movie=movie, - progress=percent) + if status == "start": + result = Trakt["scrobble"].start(movie=movie, progress=percent) + elif status == "pause": + result = Trakt["scrobble"].pause(movie=movie, progress=percent) + elif status == "stop": + result = Trakt["scrobble"].stop(movie=movie, progress=percent) else: logger.debug("scrobble() Bad scrobble status") return result @@ -187,109 +185,109 @@ def scrobbleMovie(self, movie, percent, status): def getShowsCollected(self, shows): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): - Trakt['sync/collection'].shows(shows, exceptions=True) + Trakt["sync/collection"].shows(shows, exceptions=True) return shows def getMoviesCollected(self, movies): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): - Trakt['sync/collection'].movies(movies, exceptions=True) + Trakt["sync/collection"].movies(movies, exceptions=True) return movies def getShowsWatched(self, shows): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): - Trakt['sync/watched'].shows(shows, exceptions=True) + Trakt["sync/watched"].shows(shows, exceptions=True) return shows def getMoviesWatched(self, movies): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): - Trakt['sync/watched'].movies(movies, exceptions=True) + Trakt["sync/watched"].movies(movies, exceptions=True) return movies def getShowsRated(self, shows): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): - Trakt['sync/ratings'].shows(store=shows, exceptions=True) + Trakt["sync/ratings"].shows(store=shows, exceptions=True) return shows def getEpisodesRated(self, shows): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): - Trakt['sync/ratings'].episodes(store=shows, exceptions=True) + Trakt["sync/ratings"].episodes(store=shows, exceptions=True) return shows def getMoviesRated(self, movies): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): - Trakt['sync/ratings'].movies(store=movies, exceptions=True) + Trakt["sync/ratings"].movies(store=movies, exceptions=True) return movies def addToCollection(self, mediaObject): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - result = Trakt['sync/collection'].add(mediaObject) + result = Trakt["sync/collection"].add(mediaObject) return result def removeFromCollection(self, mediaObject): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - result = Trakt['sync/collection'].remove(mediaObject) + result = Trakt["sync/collection"].remove(mediaObject) return result def addToHistory(self, mediaObject): with Trakt.configuration.oauth.from_response(self.authorization): # don't try this call it may cause multiple watches - result = Trakt['sync/history'].add(mediaObject) + result = Trakt["sync/history"].add(mediaObject) return result def addToWatchlist(self, mediaObject): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - result = Trakt['sync/watchlist'].add(mediaObject) + result = Trakt["sync/watchlist"].add(mediaObject) return result - def getShowRatingForUser(self, showId, idType='tvdb'): + def getShowRatingForUser(self, showId, idType="tvdb"): ratings = {} with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - Trakt['sync/ratings'].shows(store=ratings) + Trakt["sync/ratings"].shows(store=ratings) return findShowMatchInList(showId, ratings, idType) - def getSeasonRatingForUser(self, showId, season, idType='tvdb'): + def getSeasonRatingForUser(self, showId, season, idType="tvdb"): ratings = {} with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - Trakt['sync/ratings'].seasons(store=ratings) + Trakt["sync/ratings"].seasons(store=ratings) return findSeasonMatchInList(showId, season, ratings, idType) - def getEpisodeRatingForUser(self, showId, season, episode, idType='tvdb'): + def getEpisodeRatingForUser(self, showId, season, episode, idType="tvdb"): ratings = {} with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - Trakt['sync/ratings'].episodes(store=ratings) + Trakt["sync/ratings"].episodes(store=ratings) return findEpisodeMatchInList(showId, season, episode, ratings, idType) - def getMovieRatingForUser(self, movieId, idType='imdb'): + def getMovieRatingForUser(self, movieId, idType="imdb"): ratings = {} with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - Trakt['sync/ratings'].movies(store=ratings) + Trakt["sync/ratings"].movies(store=ratings) return findMovieMatchInList(movieId, ratings, idType) # Send a rating to Trakt as mediaObject so we can add the rating def addRating(self, mediaObject): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - result = Trakt['sync/ratings'].add(mediaObject) + result = Trakt["sync/ratings"].add(mediaObject) return result # Send a rating to Trakt as mediaObject so we can remove the rating def removeRating(self, mediaObject): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - result = Trakt['sync/ratings'].remove(mediaObject) + result = Trakt["sync/ratings"].remove(mediaObject) return result def getMoviePlaybackProgress(self): @@ -298,7 +296,7 @@ def getMoviePlaybackProgress(self): # Fetch playback with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - playback = Trakt['sync/playback'].movies(exceptions=True) + playback = Trakt["sync/playback"].movies(exceptions=True) for _, item in list(playback.items()): if type(item) is Movie: @@ -312,7 +310,7 @@ def getEpisodePlaybackProgress(self): # Fetch playback with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - playback = Trakt['sync/playback'].episodes(exceptions=True) + playback = Trakt["sync/playback"].episodes(exceptions=True) for _, item in list(playback.items()): if type(item) is Show: @@ -322,31 +320,30 @@ def getEpisodePlaybackProgress(self): def getMovieSummary(self, movieId, extended=None): with Trakt.configuration.http(retry=True): - return Trakt['movies'].get(movieId, extended=extended) + return Trakt["movies"].get(movieId, extended=extended) def getShowSummary(self, showId): with Trakt.configuration.http(retry=True): - return Trakt['shows'].get(showId) + return Trakt["shows"].get(showId) def getShowWithAllEpisodesList(self, showId): with Trakt.configuration.http(retry=True, timeout=90): - return Trakt['shows'].seasons(showId, extended='episodes') + return Trakt["shows"].seasons(showId, extended="episodes") def getEpisodeSummary(self, showId, season, episode, extended=None): with Trakt.configuration.http(retry=True): - return Trakt['shows'].episode(showId, season, episode, - extended=extended) + return Trakt["shows"].episode(showId, season, episode, extended=extended) def getIdLookup(self, id, id_type): with Trakt.configuration.http(retry=True): - result = Trakt['search'].lookup(id, id_type) + result = Trakt["search"].lookup(id, id_type) if result and not isinstance(result, list): result = [result] return result def getTextQuery(self, query, type, year): with Trakt.configuration.http(retry=True, timeout=90): - result = Trakt['search'].query(query, type, year) + result = Trakt["search"].query(query, type, year) if result and not isinstance(result, list): result = [result] return result @@ -354,5 +351,5 @@ def getTextQuery(self, query, type, year): def getUser(self): with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): - result = Trakt['users/settings'].get() + result = Trakt["users/settings"].get() return result diff --git a/resources/lib/utilities.py b/resources/lib/utilities.py index e322ba33..d625e9e5 100644 --- a/resources/lib/utilities.py +++ b/resources/lib/utilities.py @@ -19,48 +19,47 @@ def isMovie(type): - return type == 'movie' + return type == "movie" def isEpisode(type): - return type == 'episode' + return type == "episode" def isShow(type): - return type == 'show' + return type == "show" def isSeason(type): - return type == 'season' + return type == "season" def isValidMediaType(type): - return type in ['movie', 'show', 'season', 'episode'] + return type in ["movie", "show", "season", "episode"] -def chunks(l, n): - return [l[i:i + n] for i in range(0, len(l), n)] +def chunks(list, n): + return [list[i : i + n] for i in range(0, len(list), n)] def getFormattedItemName(type, info): - s = '' + s = "" try: if isShow(type): - s = info['title'] + s = info["title"] elif isEpisode(type): - s = "S%02dE%02d - %s" % (info['season'], - info['number'], info['title']) + s = "S%02dE%02d - %s" % (info["season"], info["number"], info["title"]) elif isSeason(type): if isinstance(info, list): info = info[0] - if info['season'] > 0: - s = "%s - Season %d" % (info['title'], info['season']) + if info["season"] > 0: + s = "%s - Season %d" % (info["title"], info["season"]) else: - s = "%s - Specials" % info['title'] + s = "%s - Specials" % info["title"] elif isMovie(type): - s = "%s (%s)" % (info['title'], info['year']) + s = "%s (%s)" % (info["title"], info["year"]) except KeyError: - s = '' + s = "" return s @@ -73,8 +72,8 @@ def __findInList(list, case_sensitive=True, **kwargs): if key in item: key_val = item[key] else: - if 'ids' in item and key in item['ids']: - key_val = item['ids'][key] + if "ids" in item and key in item["ids"]: + key_val = item["ids"][key] else: continue if not case_sensitive and isinstance(key_val, str): @@ -91,28 +90,46 @@ def __findInList(list, case_sensitive=True, **kwargs): def findMediaObject(mediaObjectToMatch, listToSearch, matchByTitleAndYear): result = None - if result is None and 'ids' in mediaObjectToMatch and 'imdb' in mediaObjectToMatch['ids'] and str(mediaObjectToMatch['ids']['imdb']).startswith("tt"): - result = __findInList( - listToSearch, imdb=mediaObjectToMatch['ids']['imdb']) + if ( + result is None + and "ids" in mediaObjectToMatch + and "imdb" in mediaObjectToMatch["ids"] + and str(mediaObjectToMatch["ids"]["imdb"]).startswith("tt") + ): + result = __findInList(listToSearch, imdb=mediaObjectToMatch["ids"]["imdb"]) # we don't want to give up if we don't find a match based on the first # field so we use if instead of elif - if result is None and 'ids' in mediaObjectToMatch and 'tmdb' in mediaObjectToMatch['ids'] and mediaObjectToMatch['ids']['tmdb']: - result = __findInList( - listToSearch, tmdb=mediaObjectToMatch['ids']['tmdb']) - if result is None and 'ids' in mediaObjectToMatch and 'tvdb' in mediaObjectToMatch['ids'] and mediaObjectToMatch['ids']['tvdb']: - result = __findInList( - listToSearch, tvdb=mediaObjectToMatch['ids']['tvdb']) - - if(matchByTitleAndYear): + if ( + result is None + and "ids" in mediaObjectToMatch + and "tmdb" in mediaObjectToMatch["ids"] + and mediaObjectToMatch["ids"]["tmdb"] + ): + result = __findInList(listToSearch, tmdb=mediaObjectToMatch["ids"]["tmdb"]) + if ( + result is None + and "ids" in mediaObjectToMatch + and "tvdb" in mediaObjectToMatch["ids"] + and mediaObjectToMatch["ids"]["tvdb"] + ): + result = __findInList(listToSearch, tvdb=mediaObjectToMatch["ids"]["tvdb"]) + + if matchByTitleAndYear: # match by title and year it will result in movies with the same title and # year to mismatch - but what should we do instead? - if result is None and 'title' in mediaObjectToMatch and 'year' in mediaObjectToMatch: + if ( + result is None + and "title" in mediaObjectToMatch + and "year" in mediaObjectToMatch + ): result = __findInList( - listToSearch, title=mediaObjectToMatch['title'], year=mediaObjectToMatch['year']) + listToSearch, + title=mediaObjectToMatch["title"], + year=mediaObjectToMatch["year"], + ) # match only by title, as some items don't have a year on trakt - if result is None and 'title' in mediaObjectToMatch: - result = __findInList( - listToSearch, title=mediaObjectToMatch['title']) + if result is None and "title" in mediaObjectToMatch: + result = __findInList(listToSearch, title=mediaObjectToMatch["title"]) return result @@ -120,30 +137,31 @@ def findMediaObject(mediaObjectToMatch, listToSearch, matchByTitleAndYear): def regex_tvshow(label): regexes = [ # ShowTitle.S01E09; s01e09, s01.e09, s01-e09 - r'(.*?)[._ -]s([0-9]+)[._ -]*e([0-9]+)', - r'(.*?)[._ -]([0-9]+)x([0-9]+)', # Showtitle.1x09 - r'(.*?)[._ -]([0-9]+)([0-9][0-9])', # ShowTitle.109 + r"(.*?)[._ -]s([0-9]+)[._ -]*e([0-9]+)", + r"(.*?)[._ -]([0-9]+)x([0-9]+)", # Showtitle.1x09 + r"(.*?)[._ -]([0-9]+)([0-9][0-9])", # ShowTitle.109 # ShowTitle.Season 01 - Episode 02, Season 01 Episode 02 - '(.*?)[._ -]?season[._ -]*([0-9]+)[._ -]*-?[._ -]*episode[._ -]*([0-9]+)', + "(.*?)[._ -]?season[._ -]*([0-9]+)[._ -]*-?[._ -]*episode[._ -]*([0-9]+)", # ShowTitle_[s01]_[e01] - r'(.*?)[._ -]\[s([0-9]+)\][._ -]*\[[e]([0-9]+)', - r'(.*?)[._ -]s([0-9]+)[._ -]*ep([0-9]+)'] # ShowTitle - s01ep03, ShowTitle - s1ep03 + r"(.*?)[._ -]\[s([0-9]+)\][._ -]*\[[e]([0-9]+)", + r"(.*?)[._ -]s([0-9]+)[._ -]*ep([0-9]+)", + ] # ShowTitle - s01ep03, ShowTitle - s1ep03 for regex in regexes: match = re.search(regex, label, re.I) if match: show_title, season, episode = match.groups() if show_title: - show_title = re.sub(r'[\[\]_\(\).-]', ' ', show_title) - show_title = re.sub(r'\s\s+', ' ', show_title) + show_title = re.sub(r"[\[\]_\(\).-]", " ", show_title) + show_title = re.sub(r"\s\s+", " ", show_title) show_title = show_title.strip() return show_title, int(season), int(episode) - return '', -1, -1 + return "", -1, -1 def regex_year(title): - prog = re.compile(r'^(.+) \((\d{4})\)$') + prog = re.compile(r"^(.+) \((\d{4})\)$") result = prog.match(title) if result: @@ -153,19 +171,33 @@ def regex_year(title): def findMovieMatchInList(id, listToMatch, idType): - return next((item.to_dict() for key, item in list(listToMatch.items()) if any(idType in key for key, value in item.keys if str(value) == str(id))), {}) + return next( + ( + item.to_dict() + for key, item in list(listToMatch.items()) + if any(idType in key for key, value in item.keys if str(value) == str(id)) + ), + {}, + ) def findShowMatchInList(id, listToMatch, idType): - return next((item.to_dict() for key, item in list(listToMatch.items()) if any(idType in key for key, value in item.keys if str(value) == str(id))), {}) + return next( + ( + item.to_dict() + for key, item in list(listToMatch.items()) + if any(idType in key for key, value in item.keys if str(value) == str(id)) + ), + {}, + ) def findSeasonMatchInList(id, seasonNumber, listToMatch, idType): show = findShowMatchInList(id, listToMatch, idType) logger.debug("findSeasonMatchInList %s" % show) - if 'seasons' in show: - for season in show['seasons']: - if season['number'] == seasonNumber: + if "seasons" in show: + for season in show["seasons"]: + if season["number"] == seasonNumber: return season return {} @@ -174,8 +206,8 @@ def findSeasonMatchInList(id, seasonNumber, listToMatch, idType): def findEpisodeMatchInList(id, seasonNumber, episodeNumber, list, idType): season = findSeasonMatchInList(id, seasonNumber, list, idType) if season: - for episode in season['episodes']: - if episode['number'] == episodeNumber: + for episode in season["episodes"]: + if episode["number"] == episodeNumber: return episode return {} @@ -194,7 +226,8 @@ def convertDateTimeToUTC(toConvert): utc = local.astimezone(tzutc()) except ValueError: logger.debug( - 'convertDateTimeToUTC() ValueError: movie/show was collected/watched outside of the unix timespan. Fallback to datetime utcnow') + "convertDateTimeToUTC() ValueError: movie/show was collected/watched outside of the unix timespan. Fallback to datetime utcnow" + ) utc = datetime.utcnow() return str(utc) else: @@ -210,7 +243,8 @@ def convertUtcToDateTime(toConvert): local = utc.astimezone(tzlocal()) except ValueError: logger.debug( - 'convertUtcToDateTime() ValueError: movie/show was collected/watched outside of the unix timespan. Fallback to datetime now') + "convertUtcToDateTime() ValueError: movie/show was collected/watched outside of the unix timespan. Fallback to datetime now" + ) local = datetime.now() return local.strftime(dateFormat) else: @@ -231,41 +265,43 @@ def createError(ex): def guessBestTraktId(id, type) -> Tuple[dict, str]: data = {} - id_type = '' + id_type = "" if id.startswith("tt"): - data['imdb'] = id - id_type = 'imdb' + data["imdb"] = id + id_type = "imdb" elif id.isdigit() and isMovie(type): - data['tmdb'] = id - id_type = 'tmdb' + data["tmdb"] = id + id_type = "tmdb" elif id.isdigit() and (isEpisode(type) or isSeason(type) or isShow(type)): - data['tvdb'] = id - id_type = 'tvdb' + data["tvdb"] = id + id_type = "tvdb" else: - data['slug'] = id - id_type = 'slug' + data["slug"] = id + id_type = "slug" return data, id_type def best_id(ids, type) -> Tuple[str, str]: - if 'trakt' in ids: - return ids['trakt'], 'trakt' - elif 'imdb' in ids and isMovie(type): - return ids['imdb'], 'imdb' - elif 'tmdb' in ids: - return ids['tmdb'], 'tmdb' - elif 'tvdb' in ids: - return ids['tvdb'], 'tvdb' - elif 'tvrage' in ids: - return ids['tvrage'], 'tvrage' - elif 'slug' in ids: - return ids['slug'], 'slug' + if "trakt" in ids: + return ids["trakt"], "trakt" + elif "imdb" in ids and isMovie(type): + return ids["imdb"], "imdb" + elif "tmdb" in ids: + return ids["tmdb"], "tmdb" + elif "tvdb" in ids: + return ids["tvdb"], "tvdb" + elif "tvrage" in ids: + return ids["tvrage"], "tvrage" + elif "slug" in ids: + return ids["slug"], "slug" def checkExcludePath(excludePath, excludePathEnabled, fullpath, x): if excludePath != "" and excludePathEnabled and fullpath.startswith(excludePath): logger.debug( - "checkExclusion(): Video is from location, which is currently set as excluded path %i." % x) + "checkExclusion(): Video is from location, which is currently set as excluded path %i." + % x + ) return True else: return False @@ -275,16 +311,17 @@ def sanitizeMovies(movies): # do not remove watched_at and collected_at may cause problems between the # 4 sync types (would probably have to deepcopy etc) for movie in movies: - if 'collected' in movie: - del movie['collected'] - if 'watched' in movie: - del movie['watched'] - if 'movieid' in movie: - del movie['movieid'] - if 'plays' in movie: - del movie['plays'] - if 'userrating' in movie: - del movie['userrating'] + if "collected" in movie: + del movie["collected"] + if "watched" in movie: + del movie["watched"] + if "movieid" in movie: + del movie["movieid"] + if "plays" in movie: + del movie["plays"] + if "userrating" in movie: + del movie["userrating"] + # todo add tests @@ -292,121 +329,156 @@ def sanitizeMovies(movies): def sanitizeShows(shows): # do not remove watched_at and collected_at may cause problems between the # 4 sync types (would probably have to deepcopy etc) - for show in shows['shows']: - for season in show['seasons']: - for episode in season['episodes']: - if 'collected' in episode: - del episode['collected'] - if 'watched' in episode: - del episode['watched'] - if 'season' in episode: - del episode['season'] - if 'plays' in episode: - del episode['plays'] - if 'ids' in episode and 'episodeid' in episode['ids']: - del episode['ids']['episodeid'] - - -def compareMovies(movies_col1, movies_col2, matchByTitleAndYear, watched=False, restrict=False, playback=False, rating=False): + for show in shows["shows"]: + for season in show["seasons"]: + for episode in season["episodes"]: + if "collected" in episode: + del episode["collected"] + if "watched" in episode: + del episode["watched"] + if "season" in episode: + del episode["season"] + if "plays" in episode: + del episode["plays"] + if "ids" in episode and "episodeid" in episode["ids"]: + del episode["ids"]["episodeid"] + + +def compareMovies( + movies_col1, + movies_col2, + matchByTitleAndYear, + watched=False, + restrict=False, + playback=False, + rating=False, +): movies = [] for movie_col1 in movies_col1: if movie_col1: - movie_col2 = findMediaObject( - movie_col1, movies_col2, matchByTitleAndYear) + movie_col2 = findMediaObject(movie_col1, movies_col2, matchByTitleAndYear) # logger.debug("movie_col1 %s" % movie_col1) # logger.debug("movie_col2 %s" % movie_col2) if movie_col2: # match found if watched: # are we looking for watched items - if movie_col2['watched'] == 0 and movie_col1['watched'] == 1: - if 'movieid' not in movie_col1: - movie_col1['movieid'] = movie_col2['movieid'] + if movie_col2["watched"] == 0 and movie_col1["watched"] == 1: + if "movieid" not in movie_col1: + movie_col1["movieid"] = movie_col2["movieid"] movies.append(movie_col1) elif playback: - if 'movieid' not in movie_col1: - movie_col1['movieid'] = movie_col2['movieid'] - movie_col1['runtime'] = movie_col2['runtime'] + if "movieid" not in movie_col1: + movie_col1["movieid"] = movie_col2["movieid"] + movie_col1["runtime"] = movie_col2["runtime"] movies.append(movie_col1) elif rating: - if 'rating' in movie_col1 and movie_col1['rating'] != 0 and ('rating' not in movie_col2 or movie_col2['rating'] == 0): - if 'movieid' not in movie_col1: - movie_col1['movieid'] = movie_col2['movieid'] + if ( + "rating" in movie_col1 + and movie_col1["rating"] != 0 + and ("rating" not in movie_col2 or movie_col2["rating"] == 0) + ): + if "movieid" not in movie_col1: + movie_col1["movieid"] = movie_col2["movieid"] movies.append(movie_col1) else: - if 'collected' in movie_col2 and not movie_col2['collected']: + if "collected" in movie_col2 and not movie_col2["collected"]: movies.append(movie_col1) else: # no match found if not restrict: - if 'collected' in movie_col1 and movie_col1['collected']: - if watched and (movie_col1['watched'] == 1): + if "collected" in movie_col1 and movie_col1["collected"]: + if watched and (movie_col1["watched"] == 1): movies.append(movie_col1) - elif rating and movie_col1['rating'] != 0: + elif rating and movie_col1["rating"] != 0: movies.append(movie_col1) elif not watched and not rating: movies.append(movie_col1) return movies -def compareShows(shows_col1, shows_col2, matchByTitleAndYear, rating=False, restrict=False): +def compareShows( + shows_col1, shows_col2, matchByTitleAndYear, rating=False, restrict=False +): shows = [] # logger.debug("shows_col1 %s" % shows_col1) # logger.debug("shows_col2 %s" % shows_col2) - for show_col1 in shows_col1['shows']: + for show_col1 in shows_col1["shows"]: if show_col1: show_col2 = findMediaObject( - show_col1, shows_col2['shows'], matchByTitleAndYear) + show_col1, shows_col2["shows"], matchByTitleAndYear + ) # logger.debug("show_col1 %s" % show_col1) # logger.debug("show_col2 %s" % show_col2) if show_col2: - show = {'title': show_col1['title'], - 'ids': {}, 'year': show_col1['year']} - if show_col1['ids']: - show['ids'].update(show_col1['ids']) - if show_col2['ids']: - show['ids'].update(show_col2['ids']) - if 'tvshowid' in show_col2: - show['tvshowid'] = show_col2['tvshowid'] - - if rating and 'rating' in show_col1 and show_col1['rating'] != 0 and ('rating' not in show_col2 or show_col2['rating'] == 0): - show['rating'] = show_col1['rating'] + show = { + "title": show_col1["title"], + "ids": {}, + "year": show_col1["year"], + } + if show_col1["ids"]: + show["ids"].update(show_col1["ids"]) + if show_col2["ids"]: + show["ids"].update(show_col2["ids"]) + if "tvshowid" in show_col2: + show["tvshowid"] = show_col2["tvshowid"] + + if ( + rating + and "rating" in show_col1 + and show_col1["rating"] != 0 + and ("rating" not in show_col2 or show_col2["rating"] == 0) + ): + show["rating"] = show_col1["rating"] shows.append(show) elif not rating: shows.append(show) else: if not restrict: - show = {'title': show_col1['title'], - 'ids': {}, 'year': show_col1['year']} - if show_col1['ids']: - show['ids'].update(show_col1['ids']) - - if rating and 'rating' in show_col1 and show_col1['rating'] != 0: - show['rating'] = show_col1['rating'] + show = { + "title": show_col1["title"], + "ids": {}, + "year": show_col1["year"], + } + if show_col1["ids"]: + show["ids"].update(show_col1["ids"]) + + if rating and "rating" in show_col1 and show_col1["rating"] != 0: + show["rating"] = show_col1["rating"] shows.append(show) elif not rating: shows.append(show) - result = {'shows': shows} + result = {"shows": shows} return result # always return shows_col1 if you have enrich it, but don't return shows_col2 -def compareEpisodes(shows_col1, shows_col2, matchByTitleAndYear, watched=False, restrict=False, collected=False, playback=False, rating=False): +def compareEpisodes( + shows_col1, + shows_col2, + matchByTitleAndYear, + watched=False, + restrict=False, + collected=False, + playback=False, + rating=False, +): shows = [] # logger.debug("epi shows_col1 %s" % shows_col1) # logger.debug("epi shows_col2 %s" % shows_col2) - for show_col1 in shows_col1['shows']: + for show_col1 in shows_col1["shows"]: if show_col1: show_col2 = findMediaObject( - show_col1, shows_col2['shows'], matchByTitleAndYear) + show_col1, shows_col2["shows"], matchByTitleAndYear + ) # logger.debug("show_col1 %s" % show_col1) # logger.debug("show_col2 %s" % show_col2) if show_col2: season_diff = {} # format the data to be easy to compare Trakt and KODI data - season_col1 = __getEpisodes(show_col1['seasons']) - season_col2 = __getEpisodes(show_col2['seasons']) + season_col1 = __getEpisodes(show_col1["seasons"]) + season_col2 = __getEpisodes(show_col2["seasons"]) for season in season_col1: a = season_col1[season] if season in season_col2: @@ -418,49 +490,85 @@ def compareEpisodes(shows_col1, shows_col2, matchByTitleAndYear, watched=False, eps = {} for ep in t: eps[ep] = a[ep] - if 'episodeid' in season_col2[season][ep]['ids']: - if 'ids' in eps: - eps[ep]['ids']['episodeid'] = season_col2[season][ep]['ids']['episodeid'] + if "episodeid" in season_col2[season][ep]["ids"]: + if "ids" in eps: + eps[ep]["ids"]["episodeid"] = season_col2[ + season + ][ep]["ids"]["episodeid"] else: - eps[ep]['ids'] = { - 'episodeid': season_col2[season][ep]['ids']['episodeid']} - eps[ep]['runtime'] = season_col2[season][ep]['runtime'] + eps[ep]["ids"] = { + "episodeid": season_col2[season][ep][ + "ids" + ]["episodeid"] + } + eps[ep]["runtime"] = season_col2[season][ep][ + "runtime" + ] season_diff[season] = eps elif rating: t = list(set(a).intersection(set(b))) if len(t) > 0: eps = {} for ep in t: - if 'rating' in a[ep] and a[ep]['rating'] != 0 and season_col2[season][ep]['rating'] == 0: + if ( + "rating" in a[ep] + and a[ep]["rating"] != 0 + and season_col2[season][ep]["rating"] == 0 + ): eps[ep] = a[ep] - if 'episodeid' in season_col2[season][ep]['ids']: - if 'ids' in eps: - eps[ep]['ids']['episodeid'] = season_col2[season][ep]['ids']['episodeid'] + if ( + "episodeid" + in season_col2[season][ep]["ids"] + ): + if "ids" in eps: + eps[ep]["ids"][ + "episodeid" + ] = season_col2[season][ep]["ids"][ + "episodeid" + ] else: - eps[ep]['ids'] = { - 'episodeid': season_col2[season][ep]['ids']['episodeid']} + eps[ep]["ids"] = { + "episodeid": season_col2[season][ + ep + ]["ids"]["episodeid"] + } if len(eps) > 0: season_diff[season] = eps elif len(diff) > 0: if restrict: # get all the episodes that we have in Kodi, watched or not - update kodi collectedShow = findMediaObject( - show_col1, collected['shows'], matchByTitleAndYear) + show_col1, collected["shows"], matchByTitleAndYear + ) # logger.debug("collected %s" % collectedShow) collectedSeasons = __getEpisodes( - collectedShow['seasons']) + collectedShow["seasons"] + ) t = list( - set(collectedSeasons[season]).intersection(set(diff))) + set(collectedSeasons[season]).intersection( + set(diff) + ) + ) if len(t) > 0: eps = {} for ep in t: eps[ep] = a[ep] - if 'episodeid' in collectedSeasons[season][ep]['ids']: - if 'ids' in eps: - eps[ep]['ids']['episodeid'] = collectedSeasons[season][ep]['ids']['episodeid'] + if ( + "episodeid" + in collectedSeasons[season][ep]["ids"] + ): + if "ids" in eps: + eps[ep]["ids"][ + "episodeid" + ] = collectedSeasons[season][ep]["ids"][ + "episodeid" + ] else: - eps[ep]['ids'] = { - 'episodeid': collectedSeasons[season][ep]['ids']['episodeid']} + eps[ep]["ids"] = { + "episodeid": collectedSeasons[ + season + ][ep]["ids"]["episodeid"] + } season_diff[season] = eps else: eps = {} @@ -475,62 +583,78 @@ def compareEpisodes(shows_col1, shows_col2, matchByTitleAndYear, watched=False, # logger.debug("season_diff %s" % season_diff) if len(season_diff) > 0: # logger.debug("Season_diff") - show = {'title': show_col1['title'], 'ids': { - }, 'year': show_col1['year'], 'seasons': []} - if show_col1['ids']: - show['ids'].update(show_col1['ids']) - if show_col2['ids']: - show['ids'].update(show_col2['ids']) + show = { + "title": show_col1["title"], + "ids": {}, + "year": show_col1["year"], + "seasons": [], + } + if show_col1["ids"]: + show["ids"].update(show_col1["ids"]) + if show_col2["ids"]: + show["ids"].update(show_col2["ids"]) for seasonKey in season_diff: episodes = [] for episodeKey in season_diff[seasonKey]: episodes.append(season_diff[seasonKey][episodeKey]) - show['seasons'].append( - {'number': seasonKey, 'episodes': episodes}) - if 'tvshowid' in show_col2: - show['tvshowid'] = show_col2['tvshowid'] + show["seasons"].append( + {"number": seasonKey, "episodes": episodes} + ) + if "tvshowid" in show_col2: + show["tvshowid"] = show_col2["tvshowid"] # logger.debug("show %s" % show) shows.append(show) else: if not restrict: if countEpisodes([show_col1]) > 0: - show = {'title': show_col1['title'], 'ids': { - }, 'year': show_col1['year'], 'seasons': []} - if show_col1['ids']: - show['ids'].update(show_col1['ids']) - for seasonKey in show_col1['seasons']: + show = { + "title": show_col1["title"], + "ids": {}, + "year": show_col1["year"], + "seasons": [], + } + if show_col1["ids"]: + show["ids"].update(show_col1["ids"]) + for seasonKey in show_col1["seasons"]: episodes = [] - for episodeKey in seasonKey['episodes']: - if watched and (episodeKey['watched'] == 1): + for episodeKey in seasonKey["episodes"]: + if watched and (episodeKey["watched"] == 1): episodes.append(episodeKey) - elif rating and episodeKey['rating'] != 0: + elif rating and episodeKey["rating"] != 0: episodes.append(episodeKey) elif not watched and not rating: episodes.append(episodeKey) if len(episodes) > 0: - show['seasons'].append( - {'number': seasonKey['number'], 'episodes': episodes}) - - if 'tvshowid' in show_col1: - del(show_col1['tvshowid']) + show["seasons"].append( + { + "number": seasonKey["number"], + "episodes": episodes, + } + ) + + if "tvshowid" in show_col1: + del show_col1["tvshowid"] if countEpisodes([show]) > 0: shows.append(show) - result = {'shows': shows} + result = {"shows": shows} return result def countEpisodes(shows, collection=True): count = 0 - if 'shows' in shows: - shows = shows['shows'] + if "shows" in shows: + shows = shows["shows"] for show in shows: - for seasonKey in show['seasons']: - if seasonKey is not None and 'episodes' in seasonKey: - for episodeKey in seasonKey['episodes']: + for seasonKey in show["seasons"]: + if seasonKey is not None and "episodes" in seasonKey: + for episodeKey in seasonKey["episodes"]: if episodeKey is not None: - if 'collected' in episodeKey and not episodeKey['collected'] == collection: + if ( + "collected" in episodeKey + and not episodeKey["collected"] == collection + ): continue - if 'number' in episodeKey and episodeKey['number']: + if "number" in episodeKey and episodeKey["number"]: count += 1 return count @@ -539,16 +663,16 @@ def __getEpisodes(seasons): data = {} for season in seasons: episodes = {} - for episode in season['episodes']: - episodes[episode['number']] = episode - data[season['number']] = episodes + for episode in season["episodes"]: + episodes[episode["number"]] = episode + data[season["number"]] = episodes return data def checkIfNewVersion(old, new): # Check if old is empty, it might be the first time we check - if old == '': + if old == "": return True # Major if old[0] < new[0]: @@ -564,10 +688,15 @@ def checkIfNewVersion(old, new): def _to_sec(timedelta_string, factors=(1, 60, 3600, 86400)): """[[[days:]hours:]minutes:]seconds -> seconds""" - return sum(x*y for x, y in zip(list(map(float, timedelta_string.split(':')[::-1])), factors)) + return sum( + x * y + for x, y in zip(list(map(float, timedelta_string.split(":")[::-1])), factors) + ) def _fuzzyMatch(string1, string2, match_percent=55.0): s = difflib.SequenceMatcher(None, string1, string2) s.find_longest_match(0, len(string1), 0, len(string2)) - return (difflib.SequenceMatcher(None, string1, string2).ratio() * 100) >= match_percent + return ( + difflib.SequenceMatcher(None, string1, string2).ratio() * 100 + ) >= match_percent diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 00000000..d8f26b51 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,2 @@ +# Ignore line length for now +ignore = ["E501"] \ No newline at end of file diff --git a/tests/test_kodiUtilities.py b/tests/test_kodiUtilities.py index 5c45e536..e5aac41c 100644 --- a/tests/test_kodiUtilities.py +++ b/tests/test_kodiUtilities.py @@ -1,22 +1,21 @@ # -*- coding: utf-8 -*- # -import pytest import mock import sys xbmc_mock = mock.Mock() -sys.modules['xbmc'] = xbmc_mock +sys.modules["xbmc"] = xbmc_mock xbmcgui_mock = mock.Mock() -sys.modules['xbmcgui'] = xbmcgui_mock +sys.modules["xbmcgui"] = xbmcgui_mock xbmcaddon_mock = mock.Mock() -sys.modules['xbmcaddon'] = xbmcaddon_mock -from resources.lib import kodiUtilities +sys.modules["xbmcaddon"] = xbmcaddon_mock +from resources.lib import kodiUtilities # noqa: E402 def test_notification(): assert not xbmcgui_mock.Dialog().notification.called - kodiUtilities.notification('header', 'message') + kodiUtilities.notification("header", "message") assert xbmcgui_mock.Dialog().notification.called diff --git a/tests/test_utilities.py b/tests/test_utilities.py index 85c3e308..75974fbe 100644 --- a/tests/test_utilities.py +++ b/tests/test_utilities.py @@ -2,8 +2,6 @@ # import json -from typing import Tuple -import pytest from resources.lib import utilities @@ -13,212 +11,212 @@ def load_params_from_json(json_path): def test_isMovie(): - assert utilities.isMovie('movie') + assert utilities.isMovie("movie") def test_isEpisode(): - assert utilities.isEpisode('episode') + assert utilities.isEpisode("episode") def test_isShow(): - assert utilities.isShow('show') + assert utilities.isShow("show") def test_isSeason(): - assert utilities.isSeason('season') + assert utilities.isSeason("season") def test_isValidMediaType_Movie(): - assert utilities.isValidMediaType('movie') + assert utilities.isValidMediaType("movie") def test_isValidMediaType_Episode(): - assert utilities.isValidMediaType('episode') + assert utilities.isValidMediaType("episode") def test_isValidMediaType_Show(): - assert utilities.isValidMediaType('show') + assert utilities.isValidMediaType("show") def test_isValidMediaType_Season(): - assert utilities.isValidMediaType('season') + assert utilities.isValidMediaType("season") def test_chunks(): - movies = load_params_from_json('tests/fixtures/movies.json') + movies = load_params_from_json("tests/fixtures/movies.json") assert len(utilities.chunks(movies, 1)) == 3 def test_getFormattedItemName_Show(): - data = load_params_from_json('tests/fixtures/show.json') - assert utilities.getFormattedItemName('show', data) == 'Game of Thrones' + data = load_params_from_json("tests/fixtures/show.json") + assert utilities.getFormattedItemName("show", data) == "Game of Thrones" def test_getFormattedItemName_Season(): - data = load_params_from_json('tests/fixtures/season.json') - assert utilities.getFormattedItemName( - 'season', data) == 'Winter Is Coming - Season 1' + data = load_params_from_json("tests/fixtures/season.json") + assert ( + utilities.getFormattedItemName("season", data) == "Winter Is Coming - Season 1" + ) def test_getFormattedItemName_Season2(): - data = load_params_from_json('tests/fixtures/season_no_list.json') - assert utilities.getFormattedItemName( - 'season', data) == 'Regular Show - Season 8' + data = load_params_from_json("tests/fixtures/season_no_list.json") + assert utilities.getFormattedItemName("season", data) == "Regular Show - Season 8" def test_getFormattedItemName_Episode(): - data = load_params_from_json('tests/fixtures/episode.json') - assert utilities.getFormattedItemName( - 'episode', data) == 'S01E01 - Winter Is Coming' + data = load_params_from_json("tests/fixtures/episode.json") + assert ( + utilities.getFormattedItemName("episode", data) == "S01E01 - Winter Is Coming" + ) def test_getFormattedItemName_Movie(): - data = load_params_from_json('tests/fixtures/movie.json') - assert utilities.getFormattedItemName( - 'movie', data) == 'TRON: Legacy (2010)' + data = load_params_from_json("tests/fixtures/movie.json") + assert utilities.getFormattedItemName("movie", data) == "TRON: Legacy (2010)" + # Testing the tilte def test_regex_tvshow_title_1(): - assert utilities.regex_tvshow('ShowTitle.S01E09')[0] == 'ShowTitle' + assert utilities.regex_tvshow("ShowTitle.S01E09")[0] == "ShowTitle" def test_regex_tvshow_title_2(): - assert utilities.regex_tvshow('ShowTitle.1x09')[0] == 'ShowTitle' + assert utilities.regex_tvshow("ShowTitle.1x09")[0] == "ShowTitle" def test_regex_tvshow_title_3(): - assert utilities.regex_tvshow('ShowTitle.109')[0] == 'ShowTitle' + assert utilities.regex_tvshow("ShowTitle.109")[0] == "ShowTitle" def test_regex_tvshow_title_4(): - assert utilities.regex_tvshow( - 'ShowTitle.Season 01 - Episode 02')[0] == 'ShowTitle' + assert utilities.regex_tvshow("ShowTitle.Season 01 - Episode 02")[0] == "ShowTitle" def test_regex_tvshow_title_5(): - assert utilities.regex_tvshow('ShowTitle_[s01]_[e01]')[0] == 'ShowTitle' + assert utilities.regex_tvshow("ShowTitle_[s01]_[e01]")[0] == "ShowTitle" def test_regex_tvshow_title_6(): - assert utilities.regex_tvshow('ShowTitle - s01ep03')[0] == 'ShowTitle' + assert utilities.regex_tvshow("ShowTitle - s01ep03")[0] == "ShowTitle" + # Testing the season def test_regex_tvshow_season_1(): - assert utilities.regex_tvshow('ShowTitle.S01E09')[1] == 1 + assert utilities.regex_tvshow("ShowTitle.S01E09")[1] == 1 def test_regex_tvshow_season_2(): - assert utilities.regex_tvshow('ShowTitle.1x09')[1] == 1 + assert utilities.regex_tvshow("ShowTitle.1x09")[1] == 1 def test_regex_tvshow_season_3(): - assert utilities.regex_tvshow('ShowTitle.109')[1] == 1 + assert utilities.regex_tvshow("ShowTitle.109")[1] == 1 def test_regex_tvshow_season_4(): - assert utilities.regex_tvshow('ShowTitle.Season 01 - Episode 02')[1] == 1 + assert utilities.regex_tvshow("ShowTitle.Season 01 - Episode 02")[1] == 1 def test_regex_tvshow_season_5(): - assert utilities.regex_tvshow('ShowTitle_[s01]_[e01]')[1] == 1 + assert utilities.regex_tvshow("ShowTitle_[s01]_[e01]")[1] == 1 def test_regex_tvshow_season_6(): - assert utilities.regex_tvshow('ShowTitle - s01ep03')[1] == 1 + assert utilities.regex_tvshow("ShowTitle - s01ep03")[1] == 1 + # Testing the episode def test_regex_tvshow_episode_1(): - assert utilities.regex_tvshow('ShowTitle.S01E09')[2] == 9 + assert utilities.regex_tvshow("ShowTitle.S01E09")[2] == 9 def test_regex_tvshow_episode_2(): - assert utilities.regex_tvshow('ShowTitle.1x09')[2] == 9 + assert utilities.regex_tvshow("ShowTitle.1x09")[2] == 9 def test_regex_tvshow_episode_3(): - assert utilities.regex_tvshow('ShowTitle.109')[2] == 9 + assert utilities.regex_tvshow("ShowTitle.109")[2] == 9 def test_regex_tvshow_episode_4(): - assert utilities.regex_tvshow('ShowTitle.Season 01 - Episode 09')[2] == 9 + assert utilities.regex_tvshow("ShowTitle.Season 01 - Episode 09")[2] == 9 def test_regex_tvshow_episode_5(): - assert utilities.regex_tvshow('ShowTitle_[s01]_[e09]')[2] == 9 + assert utilities.regex_tvshow("ShowTitle_[s01]_[e09]")[2] == 9 def test_regex_tvshow_episode_6(): - assert utilities.regex_tvshow('ShowTitle - s01ep09')[2] == 9 + assert utilities.regex_tvshow("ShowTitle - s01ep09")[2] == 9 def test_regex_year_title_1(): - assert utilities.regex_year('ShowTitle (2014)')[0] == 'ShowTitle' + assert utilities.regex_year("ShowTitle (2014)")[0] == "ShowTitle" def test_regex_year_title_2(): - assert utilities.regex_year('ShowTitle')[0] == '' + assert utilities.regex_year("ShowTitle")[0] == "" def test_regex_year_year_1(): - assert utilities.regex_year('ShowTitle (2014)')[1] == '2014' + assert utilities.regex_year("ShowTitle (2014)")[1] == "2014" def test_regex_year_year_2(): - assert utilities.regex_year('ShowTitle')[1] == '' + assert utilities.regex_year("ShowTitle")[1] == "" def test_guessBestTraktId_IMDB(): - assert utilities.guessBestTraktId('tt1431045', 'movie')[ - 0] == {'imdb': 'tt1431045'} + assert utilities.guessBestTraktId("tt1431045", "movie")[0] == {"imdb": "tt1431045"} def test_guessBestTraktId_TMDB(): - assert utilities.guessBestTraktId('20077', 'movie')[ - 0] == {'tmdb': '20077'} + assert utilities.guessBestTraktId("20077", "movie")[0] == {"tmdb": "20077"} def test_guessBestTraktId_Tvdb(): - assert utilities.guessBestTraktId('4346770', 'show')[ - 0] == {'tvdb': '4346770'} + assert utilities.guessBestTraktId("4346770", "show")[0] == {"tvdb": "4346770"} def test_best_id_trakt(): - data = load_params_from_json('tests/fixtures/shows.json') - assert utilities.best_id( - data[1]['show']['ids'], 'show') == (1395, 'trakt') + data = load_params_from_json("tests/fixtures/shows.json") + assert utilities.best_id(data[1]["show"]["ids"], "show") == (1395, "trakt") def test_checkExcludePath_Path_Excluded(): - assert utilities.checkExcludePath( - 'C:/excludes/', True, 'C:/excludes/video.mkv', 2) + assert utilities.checkExcludePath("C:/excludes/", True, "C:/excludes/video.mkv", 2) def test_checkExcludePath_Path_Excluded_Special_Chars(): - assert utilities.checkExcludePath( - 'C:/öäüß%6/', True, 'C:/öäüß%6/video.mkv', 2) + assert utilities.checkExcludePath("C:/öäüß%6/", True, "C:/öäüß%6/video.mkv", 2) def test_checkExcludePath_Path_NotExcluded(): - assert utilities.checkExcludePath( - 'C:/excludes/', True, 'C:/notexcluded/video.mkv', 2) is False + assert ( + utilities.checkExcludePath("C:/excludes/", True, "C:/notexcluded/video.mkv", 2) + is False + ) def test_checkExcludePath_Path_Disabled(): - assert utilities.checkExcludePath( - 'C:/excludes/', False, 'C:/excludes/video.mkv', 2) is False + assert ( + utilities.checkExcludePath("C:/excludes/", False, "C:/excludes/video.mkv", 2) + is False + ) def test_sanitizeMovies_collected(): - data = load_params_from_json('tests/fixtures/movies_unsanatized.json') + data = load_params_from_json("tests/fixtures/movies_unsanatized.json") utilities.sanitizeMovies(data) for movie in data: - result = 'collected' in movie + result = "collected" in movie if result: break @@ -226,10 +224,10 @@ def test_sanitizeMovies_collected(): def test_sanitizeMovies_watched(): - data = load_params_from_json('tests/fixtures/movies_unsanatized.json') + data = load_params_from_json("tests/fixtures/movies_unsanatized.json") utilities.sanitizeMovies(data) for movie in data: - result = 'watched' in movie + result = "watched" in movie if result: break @@ -237,10 +235,10 @@ def test_sanitizeMovies_watched(): def test_sanitizeMovies_movieid(): - data = load_params_from_json('tests/fixtures/movies_unsanatized.json') + data = load_params_from_json("tests/fixtures/movies_unsanatized.json") utilities.sanitizeMovies(data) for movie in data: - result = 'movieid' in movie + result = "movieid" in movie if result: break @@ -248,10 +246,10 @@ def test_sanitizeMovies_movieid(): def test_sanitizeMovies_plays(): - data = load_params_from_json('tests/fixtures/movies_unsanatized.json') + data = load_params_from_json("tests/fixtures/movies_unsanatized.json") utilities.sanitizeMovies(data) for movie in data: - result = 'plays' in movie + result = "plays" in movie if result: break @@ -259,10 +257,10 @@ def test_sanitizeMovies_plays(): def test_sanitizeMovies_userrating(): - data = load_params_from_json('tests/fixtures/movies_unsanatized.json') + data = load_params_from_json("tests/fixtures/movies_unsanatized.json") utilities.sanitizeMovies(data) for movie in data: - result = 'userrating' in movie + result = "userrating" in movie if result: break @@ -270,302 +268,286 @@ def test_sanitizeMovies_userrating(): def test_compareMovies_matchByTitleAndYear_collected_match(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') - data2 = load_params_from_json('tests/fixtures/movies_remote.json') - data3 = load_params_from_json('tests/fixtures/movies_watched.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") + data2 = load_params_from_json("tests/fixtures/movies_remote.json") + data3 = load_params_from_json("tests/fixtures/movies_watched.json") assert utilities.compareMovies(data1, data2, True) == data3 def test_compareMovies_matchByTitleAndYear_watched_match(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') - data2 = load_params_from_json('tests/fixtures/movies_remote.json') - data3 = load_params_from_json('tests/fixtures/movies_watched.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") + data2 = load_params_from_json("tests/fixtures/movies_remote.json") + data3 = load_params_from_json("tests/fixtures/movies_watched.json") assert utilities.compareMovies(data1, data2, True, watched=True) == data3 def test_compareMovies_matchByTitleAndYear_playback_match(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') - data2 = load_params_from_json('tests/fixtures/movies_remote.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") + data2 = load_params_from_json("tests/fixtures/movies_remote.json") assert utilities.compareMovies(data1, data2, True, playback=True) == data1 def test_compareMovies_matchByTitleAndYear_rating_match(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') - data2 = load_params_from_json('tests/fixtures/movies_remote.json') - data3 = load_params_from_json('tests/fixtures/movies_watched.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") + data2 = load_params_from_json("tests/fixtures/movies_remote.json") + data3 = load_params_from_json("tests/fixtures/movies_watched.json") assert utilities.compareMovies(data1, data2, True, rating=True) == data3 def test_compareMovies_matchByTitleAndYear_collected_nomatch(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") assert utilities.compareMovies(data1, "", True) == data1 def test_compareMovies_matchByTitleAndYear_watched_nomatch(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') - data3 = load_params_from_json('tests/fixtures/movies_watched.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") + data3 = load_params_from_json("tests/fixtures/movies_watched.json") assert utilities.compareMovies(data1, "", True, watched=True) == data3 def test_compareMovies_matchByTitleAndYear_playback_nomatch(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") assert utilities.compareMovies(data1, "", True, playback=True) == data1 def test_compareMovies_matchByTitleAndYear_rating_nomatch(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") assert utilities.compareMovies(data1, "", True, rating=True) == data1 def test_compareMovies_not_matchByTitleAndYear_collected_match(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') - data2 = load_params_from_json('tests/fixtures/movies_remote.json') - data3 = load_params_from_json('tests/fixtures/movies_watched.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") + data2 = load_params_from_json("tests/fixtures/movies_remote.json") + data3 = load_params_from_json("tests/fixtures/movies_watched.json") assert utilities.compareMovies(data1, data2, False) == data3 def test_compareMovies_not_matchByTitleAndYear_watched_match(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') - data2 = load_params_from_json('tests/fixtures/movies_remote.json') - data3 = load_params_from_json('tests/fixtures/movies_watched.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") + data2 = load_params_from_json("tests/fixtures/movies_remote.json") + data3 = load_params_from_json("tests/fixtures/movies_watched.json") assert utilities.compareMovies(data1, data2, False, watched=True) == data3 def test_compareMovies_not_matchByTitleAndYear_playback_match(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') - data2 = load_params_from_json('tests/fixtures/movies_remote.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") + data2 = load_params_from_json("tests/fixtures/movies_remote.json") assert utilities.compareMovies(data1, data2, False, playback=True) == data1 def test_compareMovies_not_matchByTitleAndYear_rating_match(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') - data2 = load_params_from_json('tests/fixtures/movies_remote.json') - data3 = load_params_from_json('tests/fixtures/movies_watched.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") + data2 = load_params_from_json("tests/fixtures/movies_remote.json") + data3 = load_params_from_json("tests/fixtures/movies_watched.json") assert utilities.compareMovies(data1, data2, False, rating=True) == data3 def test_compareMovies_not_matchByTitleAndYear_collected_nomatch(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") assert utilities.compareMovies(data1, "", False) == data1 def test_compareMovies_not_matchByTitleAndYear_watched_nomatch(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') - data3 = load_params_from_json('tests/fixtures/movies_watched.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") + data3 = load_params_from_json("tests/fixtures/movies_watched.json") assert utilities.compareMovies(data1, "", False, watched=True) == data3 def test_compareMovies_not_matchByTitleAndYear_playback_nomatch(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") assert utilities.compareMovies(data1, "", False, playback=True) == data1 def test_compareMovies_not_matchByTitleAndYear_rating_nomatch(): - data1 = load_params_from_json('tests/fixtures/movies_local.json') + data1 = load_params_from_json("tests/fixtures/movies_local.json") assert utilities.compareMovies(data1, "", False, rating=True) == data1 def test_checkIfNewVersion_unchanged(): - assert utilities.checkIfNewVersion('3.1.3', '3.1.3') == False + assert utilities.checkIfNewVersion("3.1.3", "3.1.3") is False def test_checkIfNewVersion_major_new(): - assert utilities.checkIfNewVersion('2.1.3', '3.1.3') == True + assert utilities.checkIfNewVersion("2.1.3", "3.1.3") is True def test_checkIfNewVersion_major_old(): - assert utilities.checkIfNewVersion('2.1.3', '1.1.3') == False + assert utilities.checkIfNewVersion("2.1.3", "1.1.3") is False def test_checkIfNewVersion_minor_new(): - assert utilities.checkIfNewVersion('2.1.3', '2.4.3') == True + assert utilities.checkIfNewVersion("2.1.3", "2.4.3") is True def test_checkIfNewVersion_minor_old(): - assert utilities.checkIfNewVersion('2.6.3', '1.1.3') == False + assert utilities.checkIfNewVersion("2.6.3", "1.1.3") is False def test_checkIfNewVersion_revision_new(): - assert utilities.checkIfNewVersion('2.1.510', '3.1.513') == True + assert utilities.checkIfNewVersion("2.1.510", "3.1.513") is True def test_checkIfNewVersion_revision_old(): - assert utilities.checkIfNewVersion('2.1.3', '1.1.5') == False + assert utilities.checkIfNewVersion("2.1.3", "1.1.5") is False def test_checkIfNewVersion_old_version_empty(): - assert utilities.checkIfNewVersion('', '1.1.5') == True + assert utilities.checkIfNewVersion("", "1.1.5") is True def test_compareShows_matchByTitleAndYear_no_rating(): - data1 = load_params_from_json( - 'tests/fixtures/compare_shows_local_batman.json') - data2 = load_params_from_json( - 'tests/fixtures/compare_shows_remote_batman.json') + data1 = load_params_from_json("tests/fixtures/compare_shows_local_batman.json") + data2 = load_params_from_json("tests/fixtures/compare_shows_remote_batman.json") - assert utilities.compareShows( - data1, data2, True, rating=True) == {"shows": []} + assert utilities.compareShows(data1, data2, True, rating=True) == {"shows": []} def test_compareShows_matchByTitleAndYear_rating_changed(): data1 = load_params_from_json( - 'tests/fixtures/compare_shows_local_batman_rating.json') - data2 = load_params_from_json( - 'tests/fixtures/compare_shows_remote_batman.json') - fixture = load_params_from_json( - 'tests/fixtures/compare_shows_compared_batman.json') + "tests/fixtures/compare_shows_local_batman_rating.json" + ) + data2 = load_params_from_json("tests/fixtures/compare_shows_remote_batman.json") + fixture = load_params_from_json("tests/fixtures/compare_shows_compared_batman.json") assert utilities.compareShows(data1, data2, True, rating=True) == fixture def test_compareShows_not_matchByTitleAndYear_no_rating(): - data1 = load_params_from_json( - 'tests/fixtures/compare_shows_local_batman.json') - data2 = load_params_from_json( - 'tests/fixtures/compare_shows_remote_batman.json') + data1 = load_params_from_json("tests/fixtures/compare_shows_local_batman.json") + data2 = load_params_from_json("tests/fixtures/compare_shows_remote_batman.json") - assert utilities.compareShows( - data1, data2, False, rating=True) == {"shows": []} + assert utilities.compareShows(data1, data2, False, rating=True) == {"shows": []} def test_compareShows_not_matchByTitleAndYear_rating_changed(): data1 = load_params_from_json( - 'tests/fixtures/compare_shows_local_batman_rating.json') - data2 = load_params_from_json( - 'tests/fixtures/compare_shows_remote_batman.json') - fixture = load_params_from_json( - 'tests/fixtures/compare_shows_compared_batman.json') + "tests/fixtures/compare_shows_local_batman_rating.json" + ) + data2 = load_params_from_json("tests/fixtures/compare_shows_remote_batman.json") + fixture = load_params_from_json("tests/fixtures/compare_shows_compared_batman.json") assert utilities.compareShows(data1, data2, False, rating=True) == fixture def test_compareEpisodes_matchByTitleAndYear_no_matches(): - data1 = load_params_from_json( - 'tests/fixtures/compare_shows_local_batman.json') - data2 = load_params_from_json( - 'tests/fixtures/compare_shows_remote_batman.json') + data1 = load_params_from_json("tests/fixtures/compare_shows_local_batman.json") + data2 = load_params_from_json("tests/fixtures/compare_shows_remote_batman.json") assert utilities.compareEpisodes(data1, data2, True) == {"shows": []} def test_compareEpisodes_matchByTitleAndYear_local_episode_added(): - data1 = load_params_from_json( - 'tests/fixtures/compare_shows_local_batman.json') + data1 = load_params_from_json("tests/fixtures/compare_shows_local_batman.json") data2 = load_params_from_json( - 'tests/fixtures/compare_shows_remote_batman_episode.json') + "tests/fixtures/compare_shows_remote_batman_episode.json" + ) fixture = load_params_from_json( - 'tests/fixtures/compare_shows_batman_episode_to_add.json') + "tests/fixtures/compare_shows_batman_episode_to_add.json" + ) assert utilities.compareEpisodes(data1, data2, True) == fixture def test_compareEpisodes_not_matchByTitleAndYear_no_matches(): - data1 = load_params_from_json( - 'tests/fixtures/compare_shows_local_batman.json') - data2 = load_params_from_json( - 'tests/fixtures/compare_shows_remote_batman.json') + data1 = load_params_from_json("tests/fixtures/compare_shows_local_batman.json") + data2 = load_params_from_json("tests/fixtures/compare_shows_remote_batman.json") assert utilities.compareEpisodes(data1, data2, False) == {"shows": []} def test_compareEpisodes_not_matchByTitleAndYear_local_episode_added(): - data1 = load_params_from_json( - 'tests/fixtures/compare_shows_local_batman.json') + data1 = load_params_from_json("tests/fixtures/compare_shows_local_batman.json") data2 = load_params_from_json( - 'tests/fixtures/compare_shows_remote_batman_episode.json') + "tests/fixtures/compare_shows_remote_batman_episode.json" + ) fixture = load_params_from_json( - 'tests/fixtures/compare_shows_batman_episode_to_add.json') + "tests/fixtures/compare_shows_batman_episode_to_add.json" + ) assert utilities.compareEpisodes(data1, data2, False) == fixture def test_findMediaObject_not_matchByTitleAndYear_should_not_match(): - data1 = load_params_from_json('tests/fixtures/movies_local_blind.json') - data2 = load_params_from_json( - 'tests/fixtures/movies_remote_blind_no_match.json') + data1 = load_params_from_json("tests/fixtures/movies_local_blind.json") + data2 = load_params_from_json("tests/fixtures/movies_remote_blind_no_match.json") - assert utilities.findMediaObject(data1, data2, False) == None + assert utilities.findMediaObject(data1, data2, False) is None def test_findMediaObject_not_matchByTitleAndYear_should_match(): - data1 = load_params_from_json('tests/fixtures/movies_local_blind.json') - data2 = load_params_from_json( - 'tests/fixtures/movies_remote_blind_match.json') + data1 = load_params_from_json("tests/fixtures/movies_local_blind.json") + data2 = load_params_from_json("tests/fixtures/movies_remote_blind_match.json") assert utilities.findMediaObject(data1, data2, False) == data1 def test_findMediaObject_not_matchByTitleAndYear_add_collection(): - data1 = load_params_from_json('tests/fixtures/movies_local_chaos.json') + data1 = load_params_from_json("tests/fixtures/movies_local_chaos.json") data2 = [] - assert utilities.findMediaObject(data1, data2, False) == None + assert utilities.findMediaObject(data1, data2, False) is None def test_findMediaObject_not_matchByTitleAndYear_add_collection_same_year_title_movie_in_collection(): - data1 = load_params_from_json('tests/fixtures/movies_local_chaos.json') - data2 = load_params_from_json( - 'tests/fixtures/movies_remote_chaos_match.json') + data1 = load_params_from_json("tests/fixtures/movies_local_chaos.json") + data2 = load_params_from_json("tests/fixtures/movies_remote_chaos_match.json") - assert utilities.findMediaObject(data1, data2, False) == None + assert utilities.findMediaObject(data1, data2, False) is None def test_findMediaObject_match_by_title_should_match(): - data1 = load_params_from_json('tests/fixtures/movies_local_blind.json') - data2 = load_params_from_json( - 'tests/fixtures/movies_remote_blind_no_match.json') + data1 = load_params_from_json("tests/fixtures/movies_local_blind.json") + data2 = load_params_from_json("tests/fixtures/movies_remote_blind_no_match.json") assert utilities.findMediaObject(data1, data2, True) == data2[0] def test_findMediaObject_matchByTitleAndYear_should_match(): - data1 = load_params_from_json('tests/fixtures/movies_local_blind.json') - data2 = load_params_from_json( - 'tests/fixtures/movies_remote_blind_match.json') + data1 = load_params_from_json("tests/fixtures/movies_local_blind.json") + data2 = load_params_from_json("tests/fixtures/movies_remote_blind_match.json") assert utilities.findMediaObject(data1, data2, True) == data1 def test_findMediaObject_matchByTitleAndYear_add_collection(): - data1 = load_params_from_json('tests/fixtures/movies_local_chaos.json') + data1 = load_params_from_json("tests/fixtures/movies_local_chaos.json") data2 = [] - assert utilities.findMediaObject(data1, data2, True) == None + assert utilities.findMediaObject(data1, data2, True) is None def test_findMediaObject_matchByTitleAndYear_add_collection_same_year_title_movie_in_collection(): - data1 = load_params_from_json('tests/fixtures/movies_local_chaos.json') - data2 = load_params_from_json( - 'tests/fixtures/movies_remote_chaos_match.json') + data1 = load_params_from_json("tests/fixtures/movies_local_chaos.json") + data2 = load_params_from_json("tests/fixtures/movies_remote_chaos_match.json") assert utilities.findMediaObject(data1, data2, True) == data2[0] def test_countEpisodes1(): - data1 = load_params_from_json( - 'tests/fixtures/compare_shows_local_batman.json') + data1 = load_params_from_json("tests/fixtures/compare_shows_local_batman.json") assert utilities.countEpisodes(data1) == 6 def test_countEpisodes2(): data1 = load_params_from_json( - 'tests/fixtures/compare_shows_remote_batman_episode.json') + "tests/fixtures/compare_shows_remote_batman_episode.json" + ) assert utilities.countEpisodes(data1) == 5