From 62883e946c3a048bc5c095c5304b48060ab7b77d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:46:30 +0000 Subject: [PATCH 1/2] feat: add comprehensive type hints to resources/lib Added type hints to class attributes and methods across all core logic files in resources/lib/ to improve type safety and maintainability. Ensure Kodi Python 3 compatibility by using the 'typing' module and string forward references for external library types. Co-authored-by: razzeee <5943908+razzeee@users.noreply.github.com> --- resources/lib/deviceAuthDialog.py | 14 ++--- resources/lib/globals.py | 3 +- resources/lib/kodilogging.py | 8 +-- resources/lib/obfuscation.py | 6 ++- resources/lib/rating.py | 23 +++++--- resources/lib/script.py | 5 +- resources/lib/scrobbler.py | 59 +++++++++++---------- resources/lib/service.py | 75 ++++++++++++++------------ resources/lib/sqlitequeue.py | 7 ++- resources/lib/sync.py | 29 ++++++---- resources/lib/syncEpisodes.py | 35 ++++++------ resources/lib/syncMovies.py | 29 +++++----- resources/lib/traktContextMenu.py | 15 +++--- resources/lib/traktapi.py | 88 +++++++++++++++++-------------- 14 files changed, 223 insertions(+), 173 deletions(-) diff --git a/resources/lib/deviceAuthDialog.py b/resources/lib/deviceAuthDialog.py index 422815f9..91f61398 100644 --- a/resources/lib/deviceAuthDialog.py +++ b/resources/lib/deviceAuthDialog.py @@ -20,12 +20,14 @@ class DeviceAuthDialog(xbmcgui.WindowXMLDialog): + code: str + url: str - def __init__(self, xmlFile, resourcePath, code, url): + def __init__(self, xmlFile: str, resourcePath: str, code: str, url: str) -> None: self.code = code self.url = url - def onInit(self): + def onInit(self) -> None: instuction = self.getControl(INSTRUCTION_LABEL) authcode = self.getControl(AUTHCODE_LABEL) warning = self.getControl(WARNING_LABEL) @@ -34,17 +36,17 @@ def onInit(self): authcode.setLabel(self.code) warning.setLabel(getString(32162)) - def onAction(self, action): + def onAction(self, action: xbmcgui.Action) -> None: if action == ACTION_PREVIOUS_MENU or action == ACTION_BACK: self.close() - def onControl(self, control): + def onControl(self, control: xbmcgui.Control) -> None: pass - def onFocus(self, control): + def onFocus(self, control: xbmcgui.Control) -> None: pass - def onClick(self, control): + def onClick(self, control: xbmcgui.Control) -> None: logger.debug('onClick: %s' % (control)) if control == LATER_BUTTON: diff --git a/resources/lib/globals.py b/resources/lib/globals.py index 4f27428a..3f38c44f 100644 --- a/resources/lib/globals.py +++ b/resources/lib/globals.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- # +from typing import Any -traktapi = None +traktapi: Any = None diff --git a/resources/lib/kodilogging.py b/resources/lib/kodilogging.py index bc28ea9a..186d0066 100644 --- a/resources/lib/kodilogging.py +++ b/resources/lib/kodilogging.py @@ -25,14 +25,14 @@ class KodiLogHandler(logging.StreamHandler): - def __init__(self): + def __init__(self) -> None: logging.StreamHandler.__init__(self) addon_id = xbmcaddon.Addon().getAddonInfo('id') prefix = "[%s] " % addon_id formatter = logging.Formatter(prefix + '%(name)s: %(message)s') self.setFormatter(formatter) - def emit(self, record): + def emit(self, record: logging.LogRecord) -> None: levels = { logging.CRITICAL: xbmc.LOGFATAL, logging.ERROR: xbmc.LOGERROR, @@ -44,10 +44,10 @@ def emit(self, record): if getSettingAsBool('debug'): xbmc.log(self.format(record), levels[record.levelno]) - def flush(self): + def flush(self) -> None: pass -def config(): +def config() -> None: logger = logging.getLogger() logger.addHandler(KodiLogHandler()) logger.setLevel(logging.DEBUG) diff --git a/resources/lib/obfuscation.py b/resources/lib/obfuscation.py index 99b54eaa..dd8b2129 100644 --- a/resources/lib/obfuscation.py +++ b/resources/lib/obfuscation.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- +from typing import List, Union -def deobfuscate(data): + +def deobfuscate(data: Union[List[int], str]) -> str: if not data or not isinstance(data, list): return "" return "".join(chr(b ^ 0x42) for b in data) -def obfuscate(data): +def obfuscate(data: str) -> List[int]: if not data: return [] return [ord(c) ^ 0x42 for c in data] diff --git a/resources/lib/rating.py b/resources/lib/rating.py index ac067e5f..db7e749d 100644 --- a/resources/lib/rating.py +++ b/resources/lib/rating.py @@ -5,6 +5,7 @@ import xbmcgui from resources.lib import utilities from resources.lib import kodiUtilities +from typing import Dict, List, Optional, Any, Union from resources.lib import globals import logging @@ -13,7 +14,7 @@ __addon__ = xbmcaddon.Addon("script.trakt") -def ratingCheck(media_type, items_to_rate, watched_time, total_time): +def ratingCheck(media_type: str, items_to_rate: List[Dict], watched_time: float, total_time: float) -> None: """Check if a video should be rated and if so launches the rating dialog""" logger.debug("Rating Check called for '%s'" % media_type) if not kodiUtilities.getSettingAsBool("rate_%s" % media_type): @@ -30,7 +31,7 @@ def ratingCheck(media_type, items_to_rate, watched_time, total_time): media_type, watched, kodiUtilities.getSettingAsFloat("rate_min_view_time"))) -def rateMedia(media_type, itemsToRate, unrate=False, rating=None): +def rateMedia(media_type: str, itemsToRate: List[Dict], unrate: bool = False, rating: Optional[Union[int, str]] = None) -> None: """Launches the rating dialog""" for summary_info in itemsToRate: if not utilities.isValidMediaType(media_type): @@ -117,7 +118,7 @@ def rateMedia(media_type, itemsToRate, unrate=False, rating=None): rating = None -def __rateOnTrakt(rating, media_type, media, unrate=False): +def __rateOnTrakt(rating: Union[int, str], media_type: str, media: Dict, unrate: bool = False) -> None: logger.debug("Sending rating (%s) to Trakt.tv" % rating) params = media @@ -166,6 +167,12 @@ def __rateOnTrakt(rating, media_type, media, unrate=False): class RatingDialog(xbmcgui.WindowXMLDialog): + media_type: str + media: Dict + rating: Optional[Union[int, str]] + rerate: bool + default_rating: int + buttons = { 11030: 1, 11031: 2, @@ -192,17 +199,17 @@ class RatingDialog(xbmcgui.WindowXMLDialog): 11039: 32027 } - def __init__(self, xmlFile, resourcePath, media_type, media, rerate): + def __init__(self, xmlFile: str, resourcePath: str, media_type: str, media: Dict, rerate: bool) -> None: self.media_type = media_type self.media = media self.rating = None self.rerate = rerate self.default_rating = kodiUtilities.getSettingAsInt('rating_default') - def __new__(cls, xmlFile, resourcePath, media_type, media, rerate): + def __new__(cls, xmlFile: str, resourcePath: str, media_type: str, media: Dict, rerate: bool) -> Any: return super(RatingDialog, cls).__new__(cls, xmlFile, resourcePath) - def onInit(self): + def onInit(self) -> None: s = utilities.getFormattedItemName(self.media_type, self.media) self.getControl(10012).setLabel(s) @@ -211,12 +218,12 @@ def onInit(self): rateID = 11029 + int(self.media['user']['ratings']['rating']) self.setFocus(self.getControl(rateID)) - def onClick(self, controlID): + def onClick(self, controlID: int) -> None: if controlID in self.buttons: self.rating = self.buttons[controlID] self.close() - def onFocus(self, controlID): + def onFocus(self, controlID: int) -> None: if controlID in self.focus_labels: s = kodiUtilities.getString(self.focus_labels[controlID]) diff --git a/resources/lib/script.py b/resources/lib/script.py index 5b6c1443..e1de6e70 100644 --- a/resources/lib/script.py +++ b/resources/lib/script.py @@ -5,12 +5,13 @@ from resources.lib import utilities from resources.lib import kodiUtilities from resources.lib import sqlitequeue +from typing import Dict, Optional from resources.lib.traktContextMenu import traktContextMenu logger = logging.getLogger(__name__) -def __getArguments(): +def __getArguments() -> Dict: data = None default_actions = {0: "sync"} if len(sys.argv) == 1: @@ -26,7 +27,7 @@ def __getArguments(): return data -def run(): +def run() -> None: args = __getArguments() data = {} diff --git a/resources/lib/scrobbler.py b/resources/lib/scrobbler.py index 72792e6d..10f0e630 100644 --- a/resources/lib/scrobbler.py +++ b/resources/lib/scrobbler.py @@ -4,6 +4,7 @@ import xbmc import time import logging +from typing import Dict, List, Optional, Any from resources.lib import utilities from resources.lib import kodiUtilities import math @@ -13,34 +14,34 @@ class Scrobbler: - traktapi = None - isPlaying = False - isPaused = False - stopScrobbler = False - isPVR = False - isMultiPartEpisode = False - lastMPCheck = 0 - curMPEpisode = 0 - videoDuration = 1 - watchedTime = 0 - pausedAt = 0 - curVideo = None - curVideoInfo = None - playlistIndex = 0 - traktShowSummary = None - videosToRate = [] - - def __init__(self, api): + traktapi: Any = None + isPlaying: bool = False + isPaused: bool = False + stopScrobbler: bool = False + isPVR: bool = False + isMultiPartEpisode: bool = False + lastMPCheck: float = 0 + curMPEpisode: int = 0 + videoDuration: float = 1 + watchedTime: float = 0 + pausedAt: float = 0 + curVideo: Optional[Dict] = None + curVideoInfo: Optional[Dict] = None + playlistIndex: int = 0 + traktShowSummary: Optional[Dict] = None + videosToRate: List[Dict] = [] + + def __init__(self, api: Any) -> None: self.traktapi = api - def _currentEpisode(self, watchedPercent, episodeCount): + def _currentEpisode(self, watchedPercent: float, episodeCount: int) -> int: split = 100 / episodeCount for i in range(episodeCount - 1, 0, -1): if watchedPercent >= (i * split): return i return 0 - def transitionCheck(self, isSeek=False): + def transitionCheck(self, isSeek: bool = False) -> None: if not xbmc.Player().isPlayingVideo(): return @@ -198,7 +199,7 @@ def transitionCheck(self, isSeek=False): elif isSeek: self.__scrobble("start") - def playbackStarted(self, data): + def playbackStarted(self, data: Dict) -> None: logger.debug("playbackStarted(data: %s)" % data) if not data: return @@ -406,7 +407,7 @@ def playbackStarted(self, data): self.__preFetchUserRatings(result) - def __preFetchUserRatings(self, result): + def __preFetchUserRatings(self, result: Dict) -> None: if result: if utilities.isMovie( self.curVideo["type"] @@ -439,7 +440,7 @@ def __preFetchUserRatings(self, result): } logger.debug("Pre-Fetch result: %s; Info: %s" % (result, self.curVideoInfo)) - def playbackResumed(self): + def playbackResumed(self) -> None: if not self.isPlaying or self.isPVR: return @@ -451,7 +452,7 @@ def playbackResumed(self): self.isPaused = False self.__scrobble("start") - def playbackPaused(self): + def playbackPaused(self) -> None: if not self.isPlaying or self.isPVR: return @@ -461,14 +462,14 @@ def playbackPaused(self): self.pausedAt = time.time() self.__scrobble("pause") - def playbackSeek(self): + def playbackSeek(self) -> None: if not self.isPlaying: return logger.debug("playbackSeek()") self.transitionCheck(isSeek=True) - def playbackEnded(self): + def playbackEnded(self) -> None: if not self.isPVR: self.videosToRate.append(self.curVideoInfo) if not self.isPlaying: @@ -497,7 +498,7 @@ def playbackEnded(self): self.curVideo = None self.playlistIndex = 0 - def __calculateWatchedPercent(self): + def __calculateWatchedPercent(self) -> float: # we need to floor this, so this calculation yields the same result as the playback progress calculation floored = math.floor(self.videoDuration) if floored != 0: @@ -505,7 +506,7 @@ def __calculateWatchedPercent(self): else: return 0 - def __scrobble(self, status): + def __scrobble(self, status: str) -> Optional[Dict]: if not self.curVideoInfo: return @@ -609,7 +610,7 @@ def __scrobble(self, status): % (self.traktShowSummary, self.curVideoInfo, watchedPercent, status) ) - def __scrobbleNotification(self, info): + def __scrobbleNotification(self, info: Dict) -> None: if not self.curVideoInfo: return diff --git a/resources/lib/service.py b/resources/lib/service.py index 6be72c57..9d28f4ff 100644 --- a/resources/lib/service.py +++ b/resources/lib/service.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import threading import logging +from typing import Any, Dict, Optional, Callable import xbmc import time import xbmcgui @@ -22,19 +23,19 @@ class traktService: - scrobbler = None - updateTagsThread = None - syncThread = None - dispatchQueue = sqlitequeue.SqliteQueue() + scrobbler: Optional[Scrobbler] = None + updateTagsThread: Optional[threading.Thread] = None + syncThread: Optional[threading.Thread] = None + dispatchQueue: sqlitequeue.SqliteQueue = sqlitequeue.SqliteQueue() - def __init__(self): + def __init__(self) -> None: threading.Thread.name = "trakt" - def _dispatchQueue(self, data): + def _dispatchQueue(self, data: Dict) -> None: logger.debug("Queuing for dispatch: %s" % data) self.dispatchQueue.append(data) - def _dispatch(self, data): + def _dispatch(self, data: Dict) -> None: try: logger.debug("Dispatch: %s" % data) action = data["action"] @@ -89,7 +90,7 @@ def _dispatch(self, data): message = utilities.createError(ex) logger.fatal(message) - def run(self): + def run(self) -> None: startup_delay = kodiUtilities.getSettingAsInt("startup_delay") if startup_delay: logger.debug("Delaying startup by %d seconds." % startup_delay) @@ -138,7 +139,7 @@ def run(self): if self.syncThread.is_alive(): self.syncThread.join() - def doManualRating(self, data): + def doManualRating(self, data: Dict) -> None: action = data["action"] media_type = data["media_type"] summaryInfo = None @@ -234,7 +235,7 @@ def doManualRating(self, data): "doManualRating(): Summary info was empty, possible problem retrieving data from Trakt.tv" ) - def doAddToWatchlist(self, data): + def doAddToWatchlist(self, data: Dict) -> None: media_type = data["media_type"] if utilities.isMovie(media_type): @@ -303,7 +304,7 @@ def doAddToWatchlist(self, data): else: kodiUtilities.notification(kodiUtilities.getString(32166), s) - def doMarkWatched(self, data): + def doMarkWatched(self, data: Dict) -> None: media_type = data["media_type"] if utilities.isMovie(media_type): @@ -386,7 +387,7 @@ def doMarkWatched(self, data): self.addEpisodesToHistory(summaryInfo, s) - def addEpisodesToHistory(self, summaryInfo, s): + def addEpisodesToHistory(self, summaryInfo: Dict, s: str) -> None: if len(summaryInfo["shows"][0]["seasons"][0]["episodes"]) > 0: logger.debug("doMarkWatched(): %s" % str(summaryInfo)) @@ -399,22 +400,24 @@ def addEpisodesToHistory(self, summaryInfo, s): else: kodiUtilities.notification(kodiUtilities.getString(32114), s) - def doSync(self, manual=False, silent=False, library="all"): + def doSync(self, manual: bool = False, silent: bool = False, library: str = "all") -> None: self.syncThread = syncThread(manual, silent, library) self.syncThread.start() class syncThread(threading.Thread): - _isManual = False + _isManual: bool = False + _runSilent: bool = False + _library: str = "all" - def __init__(self, isManual=False, runSilent=False, library="all"): + def __init__(self, isManual: bool = False, runSilent: bool = False, library: str = "all") -> None: threading.Thread.__init__(self) self.name = "trakt-sync" self._isManual = isManual self._runSilent = runSilent self._library = library - def run(self): + def run(self) -> None: sync = Sync( show_progress=self._isManual, run_silent=self._runSilent, @@ -425,13 +428,16 @@ def run(self): class traktMonitor(xbmc.Monitor): - def __init__(self, *args, **kwargs): + action: Callable[[Dict], None] + scanning_video: bool = False + + def __init__(self, *args: Any, **kwargs: Any) -> None: 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): + def onNotification(self, sender: str, method: str, data: str) -> None: # method looks like Other.NEXTUPWATCHEDSIGNAL if "." not in method or method.split(".")[1].upper() != "NEXTUPWATCHEDSIGNAL": return @@ -441,7 +447,7 @@ def onNotification(self, sender, method, data): self.action(data) # called when database gets updated and return video or music to indicate which DB has been changed - def onScanFinished(self, database): + def onScanFinished(self, database: str) -> None: if database == "video": self.scanning_video = False logger.debug("[traktMonitor] onScanFinished(database: %s)" % database) @@ -449,29 +455,32 @@ def onScanFinished(self, database): self.action(data) # called when database update starts and return video or music to indicate which DB is being updated - def onDatabaseScanStarted(self, database): + def onDatabaseScanStarted(self, database: str) -> None: if database == "video": self.scanning_video = True logger.debug( "[traktMonitor] onDatabaseScanStarted(database: %s)" % database ) - def onCleanFinished(self, database): + def onCleanFinished(self, database: str) -> None: if database == "video" and not self.scanning_video: # Ignore clean on update. data = {"action": "databaseCleaned"} self.action(data) class traktPlayer(xbmc.Player): - _playing = False - plIndex = None + _playing: bool = False + plIndex: Optional[int] = None + action: Callable[[Dict], None] + type: Optional[str] = None + id: Optional[int] = None - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: self.action = kwargs["action"] logger.debug("[traktPlayer] Initalized.") # called when kodi starts playing a file - def onAVStarted(self): + def onAVStarted(self) -> None: xbmc.sleep(1000) self.type = None self.id = None @@ -940,7 +949,7 @@ def onAVStarted(self): self.action(data) # called when kodi stops playing a file - def onPlayBackEnded(self): + def onPlayBackEnded(self) -> None: xbmcgui.Window(10000).clearProperty("script.trakt.ids") xbmcgui.Window(10000).clearProperty("script.trakt.paused") if self._playing: @@ -951,7 +960,7 @@ def onPlayBackEnded(self): self.action(data) # called when user stops kodi playing a file - def onPlayBackStopped(self): + def onPlayBackStopped(self) -> None: xbmcgui.Window(10000).clearProperty("script.trakt.ids") xbmcgui.Window(10000).clearProperty("script.trakt.paused") if self._playing: @@ -964,7 +973,7 @@ def onPlayBackStopped(self): self.action(data) # called when user pauses a playing file - def onPlayBackPaused(self): + def onPlayBackPaused(self) -> None: if self._playing: logger.debug( "[traktPlayer] onPlayBackPaused() - %s" % self.isPlayingVideo() @@ -973,7 +982,7 @@ def onPlayBackPaused(self): self.action(data) # called when user resumes a paused file - def onPlayBackResumed(self): + def onPlayBackResumed(self) -> None: if self._playing: logger.debug( "[traktPlayer] onPlayBackResumed() - %s" % self.isPlayingVideo() @@ -982,12 +991,12 @@ def onPlayBackResumed(self): self.action(data) # called when user queues the next item - def onQueueNextItem(self): + def onQueueNextItem(self) -> None: if self._playing: logger.debug("[traktPlayer] onQueueNextItem() - %s" % self.isPlayingVideo()) # called when players speed changes. (eg. user FF/RW) - def onPlayBackSpeedChanged(self, speed): + def onPlayBackSpeedChanged(self, speed: int) -> None: if self._playing: logger.debug( "[traktPlayer] onPlayBackSpeedChanged(speed: %s) - %s" @@ -995,7 +1004,7 @@ def onPlayBackSpeedChanged(self, speed): ) # called when user seeks to a time - def onPlayBackSeek(self, time, offset): + def onPlayBackSeek(self, time: int, offset: int) -> None: if self._playing: logger.debug( "[traktPlayer] onPlayBackSeek(time: %s, offset: %s) - %s" @@ -1005,7 +1014,7 @@ def onPlayBackSeek(self, time, offset): self.action(data) # called when user performs a chapter seek - def onPlayBackSeekChapter(self, chapter): + def onPlayBackSeekChapter(self, chapter: int) -> None: if self._playing: logger.debug( "[traktPlayer] onPlayBackSeekChapter(chapter: %s) - %s" diff --git a/resources/lib/sqlitequeue.py b/resources/lib/sqlitequeue.py index a45da780..443c9988 100644 --- a/resources/lib/sqlitequeue.py +++ b/resources/lib/sqlitequeue.py @@ -14,7 +14,7 @@ import xbmcvfs import xbmcaddon import logging -from typing import Any, Iterator, Optional +from typing import Any, Iterator, Optional, Dict logger = logging.getLogger(__name__) @@ -45,7 +45,10 @@ class SqliteQueue(object): ) _purge = 'DELETE FROM queue' - def __init__(self): + path: str + _connection_cache: Dict[int, sqlite3.Connection] + + def __init__(self) -> None: self.path = xbmcvfs.translatePath(__addon__.getAddonInfo("profile")) if not xbmcvfs.exists(self.path): logger.debug("Making path structure: %s" % repr(self.path)) diff --git a/resources/lib/sync.py b/resources/lib/sync.py index 7c17af82..69beb4bd 100644 --- a/resources/lib/sync.py +++ b/resources/lib/sync.py @@ -2,6 +2,7 @@ import logging +from typing import Optional, Any import xbmc import xbmcgui @@ -13,7 +14,15 @@ class Sync(): - def __init__(self, show_progress=False, run_silent=False, library="all", api=None): + traktapi: Any = None + show_progress: bool = False + run_silent: bool = False + library: str = "all" + sync_on_update: bool = False + notify: bool = False + notify_during_playback: bool = False + + def __init__(self, show_progress: bool = False, run_silent: bool = False, library: str = "all", api: Any = None) -> None: self.traktapi = api self.show_progress = show_progress self.run_silent = run_silent @@ -24,35 +33,35 @@ def __init__(self, show_progress=False, run_silent=False, library="all", api=Non self.notify = getSettingAsBool('show_sync_notifications') self.notify_during_playback = not getSettingAsBool("hide_notifications_playback") - def __syncCheck(self, media_type): + def __syncCheck(self, media_type: str) -> bool: return self.__syncCollectionCheck(media_type) or self.__syncWatchedCheck(media_type) or self.__syncPlaybackCheck(media_type) or self.__syncRatingsCheck() - def __syncPlaybackCheck(self, media_type): + def __syncPlaybackCheck(self, media_type: str) -> bool: if media_type == 'movies': return getSettingAsBool('trakt_movie_playback') else: return getSettingAsBool('trakt_episode_playback') - def __syncCollectionCheck(self, media_type): + def __syncCollectionCheck(self, media_type: str) -> bool: if media_type == 'movies': return getSettingAsBool('add_movies_to_trakt') or getSettingAsBool('clean_trakt_movies') else: return getSettingAsBool('add_episodes_to_trakt') or getSettingAsBool('clean_trakt_episodes') - def __syncRatingsCheck(self): + def __syncRatingsCheck(self) -> bool: return getSettingAsBool('trakt_sync_ratings') - def __syncWatchedCheck(self, media_type): + def __syncWatchedCheck(self, media_type: str) -> bool: if media_type == 'movies': return getSettingAsBool('trakt_movie_playcount') or getSettingAsBool('kodi_movie_playcount') else: return getSettingAsBool('trakt_episode_playcount') or getSettingAsBool('kodi_episode_playcount') @property - def show_notification(self): + def show_notification(self) -> bool: return not self.show_progress and self.sync_on_update and self.notify and (self.notify_during_playback or not xbmc.Player().isPlayingVideo()) - def sync(self): + def sync(self) -> None: logger.debug("Starting synchronization with Trakt.tv") if self.__syncCheck('movies'): @@ -79,14 +88,14 @@ def sync(self): logger.debug("[Sync] Finished synchronization with Trakt.tv") - def IsCanceled(self): + def IsCanceled(self) -> bool: if self.show_progress and not self.run_silent and progress.iscanceled(): logger.debug("Sync was canceled by user.") return True else: return False - def UpdateProgress(self, *args, **kwargs): + def UpdateProgress(self, *args: Any, **kwargs: Any) -> None: if self.show_progress and not self.run_silent: line1 = "" diff --git a/resources/lib/syncEpisodes.py b/resources/lib/syncEpisodes.py index 09be8f42..fa40398e 100644 --- a/resources/lib/syncEpisodes.py +++ b/resources/lib/syncEpisodes.py @@ -2,6 +2,7 @@ import copy import logging +from typing import Dict, List, Tuple, Union, Optional, Any from resources.lib import kodiUtilities, utilities @@ -9,7 +10,9 @@ class SyncEpisodes: - def __init__(self, sync, progress): + sync: Any + + def __init__(self, sync: Any, progress: Any) -> None: self.sync = sync if self.sync.show_notification: kodiUtilities.notification( @@ -111,7 +114,7 @@ def __init__(self, sync, progress): """ begin code for episode sync """ - def __kodiLoadShows(self): + def __kodiLoadShows(self) -> Tuple[Optional[Dict], Optional[Dict]]: self.sync.UpdateProgress( 1, line1=kodiUtilities.getString(32094), @@ -215,7 +218,7 @@ def __kodiLoadShows(self): self.sync.UpdateProgress(10, line2=kodiUtilities.getString(32098)) return resultCollected, resultWatched - def __traktLoadShows(self): + def __traktLoadShows(self) -> Tuple[Union[Dict, bool], Union[Dict, bool], Union[Dict, bool], Union[Dict, bool]]: self.sync.UpdateProgress( 10, line1=kodiUtilities.getString(32099), @@ -319,7 +322,7 @@ def __traktLoadShows(self): return showsCollected, showsWatched, showsRated, episodesRated - def __traktLoadShowsPlaybackProgress(self, fromPercent, toPercent): + def __traktLoadShowsPlaybackProgress(self, fromPercent: int, toPercent: int) -> Union[Dict, bool, None]: if ( kodiUtilities.getSettingAsBool("trakt_episode_playback") and not self.sync.IsCanceled() @@ -360,8 +363,8 @@ def __traktLoadShowsPlaybackProgress(self, fromPercent, toPercent): return showsProgress def __addEpisodesToTraktCollection( - self, kodiShows, traktShows, fromPercent, toPercent - ): + self, kodiShows: Dict, traktShows: Dict, fromPercent: int, toPercent: int + ) -> None: if ( kodiUtilities.getSettingAsBool("add_episodes_to_trakt") and not self.sync.IsCanceled() @@ -438,8 +441,8 @@ def __addEpisodesToTraktCollection( ) def __deleteEpisodesFromTraktCollection( - self, traktShows, kodiShows, fromPercent, toPercent - ): + self, traktShows: Dict, kodiShows: Dict, fromPercent: int, toPercent: int + ) -> None: if ( kodiUtilities.getSettingAsBool("clean_trakt_episodes") and not self.sync.IsCanceled() @@ -496,8 +499,8 @@ def __deleteEpisodesFromTraktCollection( ) def __addEpisodesToTraktWatched( - self, kodiShows, traktShows, fromPercent, toPercent - ): + self, kodiShows: Dict, traktShows: Dict, fromPercent: int, toPercent: int + ) -> None: if ( kodiUtilities.getSettingAsBool("trakt_episode_playcount") and not self.sync.IsCanceled() @@ -571,8 +574,8 @@ def __addEpisodesToTraktWatched( ) def __addEpisodesToKodiWatched( - self, traktShows, kodiShows, kodiShowsCollected, fromPercent, toPercent - ): + self, traktShows: Dict, kodiShows: Dict, kodiShowsCollected: Dict, fromPercent: int, toPercent: int + ) -> None: if ( kodiUtilities.getSettingAsBool("kodi_episode_playcount") and not self.sync.IsCanceled() @@ -658,7 +661,7 @@ def __addEpisodesToKodiWatched( toPercent, line2=kodiUtilities.getString(32109) % len(episodes) ) - def __addEpisodeProgressToKodi(self, traktShows, kodiShows, fromPercent, toPercent): + def __addEpisodeProgressToKodi(self, traktShows: Dict, kodiShows: Dict, fromPercent: int, toPercent: int) -> None: if ( kodiUtilities.getSettingAsBool("trakt_episode_playback") and traktShows @@ -760,7 +763,7 @@ def __addEpisodeProgressToKodi(self, traktShows, kodiShows, fromPercent, toPerce toPercent, line2=kodiUtilities.getString(32131) % len(episodes) ) - def __syncShowsRatings(self, traktShows, kodiShows, fromPercent, toPercent): + def __syncShowsRatings(self, traktShows: Dict, kodiShows: Dict, fromPercent: int, toPercent: int) -> None: if ( kodiUtilities.getSettingAsBool("trakt_sync_ratings") and traktShows @@ -858,7 +861,7 @@ def __syncShowsRatings(self, traktShows, kodiShows, fromPercent, toPercent): toPercent, line2=kodiUtilities.getString(32178) % len(shows) ) - def __syncEpisodeRatings(self, traktShows, kodiShows, fromPercent, toPercent): + def __syncEpisodeRatings(self, traktShows: Dict, kodiShows: Dict, fromPercent: int, toPercent: int) -> None: if ( kodiUtilities.getSettingAsBool("trakt_sync_ratings") and traktShows @@ -963,7 +966,7 @@ def __syncEpisodeRatings(self, traktShows, kodiShows, fromPercent, toPercent): toPercent, line2=kodiUtilities.getString(32175) % len(episodes) ) - def __getShowAsString(self, show, short=False): + def __getShowAsString(self, show: Dict, short: bool = False) -> str: p = [] if "seasons" in show: for season in show["seasons"]: diff --git a/resources/lib/syncMovies.py b/resources/lib/syncMovies.py index 56af70ca..c0323a96 100644 --- a/resources/lib/syncMovies.py +++ b/resources/lib/syncMovies.py @@ -2,6 +2,7 @@ import copy import logging +from typing import Dict, List, Optional, Any, Union from resources.lib import kodiUtilities, utilities @@ -9,7 +10,9 @@ class SyncMovies: - def __init__(self, sync, progress): + sync: Any + + def __init__(self, sync: Any, progress: Any) -> None: self.sync = sync if self.sync.show_notification: kodiUtilities.notification( @@ -73,7 +76,7 @@ def __init__(self, sync, progress): ) logger.debug("[Movies Sync] Complete.") - def __kodiLoadMovies(self): + def __kodiLoadMovies(self) -> Optional[List[Dict]]: self.sync.UpdateProgress(1, line2=kodiUtilities.getString(32079)) logger.debug("[Movies Sync] Getting movie data from Kodi") @@ -108,7 +111,7 @@ def __kodiLoadMovies(self): return kodi_movies - def __traktLoadMovies(self): + def __traktLoadMovies(self) -> List[Dict]: self.sync.UpdateProgress( 10, line1=kodiUtilities.getString(32079), @@ -137,7 +140,7 @@ def __traktLoadMovies(self): return movies - def __traktLoadMoviesPlaybackProgress(self, fromPercent, toPercent): + def __traktLoadMoviesPlaybackProgress(self, fromPercent: int, toPercent: int) -> Union[Dict, bool]: if ( kodiUtilities.getSettingAsBool("trakt_movie_playback") and not self.sync.IsCanceled() @@ -173,8 +176,8 @@ def __traktLoadMoviesPlaybackProgress(self, fromPercent, toPercent): return moviesProgress def __addMoviesToTraktCollection( - self, kodiMovies, traktMovies, fromPercent, toPercent - ): + self, kodiMovies: List[Dict], traktMovies: List[Dict], fromPercent: int, toPercent: int + ) -> None: if ( kodiUtilities.getSettingAsBool("add_movies_to_trakt") and not self.sync.IsCanceled() @@ -225,8 +228,8 @@ def __addMoviesToTraktCollection( ) def __deleteMoviesFromTraktCollection( - self, traktMovies, kodiMovies, fromPercent, toPercent - ): + self, traktMovies: List[Dict], kodiMovies: List[Dict], fromPercent: int, toPercent: int + ) -> None: if ( kodiUtilities.getSettingAsBool("clean_trakt_movies") and not self.sync.IsCanceled() @@ -280,8 +283,8 @@ def __deleteMoviesFromTraktCollection( ) def __addMoviesToTraktWatched( - self, kodiMovies, traktMovies, fromPercent, toPercent - ): + self, kodiMovies: List[Dict], traktMovies: List[Dict], fromPercent: int, toPercent: int + ) -> None: if ( kodiUtilities.getSettingAsBool("trakt_movie_playcount") and not self.sync.IsCanceled() @@ -349,7 +352,7 @@ def __addMoviesToTraktWatched( line2=kodiUtilities.getString(32087) % len(traktMoviesToUpdate), ) - def __addMoviesToKodiWatched(self, traktMovies, kodiMovies, fromPercent, toPercent): + def __addMoviesToKodiWatched(self, traktMovies: List[Dict], kodiMovies: List[Dict], fromPercent: int, toPercent: int) -> None: if ( kodiUtilities.getSettingAsBool("kodi_movie_playcount") and not self.sync.IsCanceled() @@ -424,7 +427,7 @@ def __addMoviesToKodiWatched(self, traktMovies, kodiMovies, fromPercent, toPerce line2=kodiUtilities.getString(32090) % len(kodiMoviesToUpdate), ) - def __addMovieProgressToKodi(self, traktMovies, kodiMovies, fromPercent, toPercent): + def __addMovieProgressToKodi(self, traktMovies: Dict, kodiMovies: List[Dict], fromPercent: int, toPercent: int) -> None: if ( kodiUtilities.getSettingAsBool("trakt_movie_playback") and traktMovies @@ -510,7 +513,7 @@ def __addMovieProgressToKodi(self, traktMovies, kodiMovies, fromPercent, toPerce line2=kodiUtilities.getString(32128) % len(kodiMoviesToUpdate), ) - def __syncMovieRatings(self, traktMovies, kodiMovies, fromPercent, toPercent): + def __syncMovieRatings(self, traktMovies: List[Dict], kodiMovies: List[Dict], fromPercent: int, toPercent: int) -> None: if ( kodiUtilities.getSettingAsBool("trakt_sync_ratings") and traktMovies diff --git a/resources/lib/traktContextMenu.py b/resources/lib/traktContextMenu.py index 00ce5c36..792a0b2e 100644 --- a/resources/lib/traktContextMenu.py +++ b/resources/lib/traktContextMenu.py @@ -2,6 +2,7 @@ import xbmcaddon import xbmcgui +from typing import List, Optional, Any from resources.lib.utilities import isMovie, isShow, isSeason, isEpisode from resources.lib.kodiUtilities import getString @@ -19,9 +20,11 @@ class traktContextMenu(xbmcgui.WindowXMLDialog): - action = None + action: Optional[str] = None + buttons: List[str] + media_type: str - def __new__(cls, media_type=None, buttons=None): + def __new__(cls, media_type: Optional[str] = None, buttons: Optional[List[str]] = None) -> Any: return super(traktContextMenu, cls).__new__( cls, "script-trakt-ContextMenu.xml", @@ -30,12 +33,12 @@ def __new__(cls, media_type=None, buttons=None): buttons=None, ) - def __init__(self, *args, **kwargs): + def __init__(self, *args: Any, **kwargs: Any) -> None: self.buttons = kwargs["buttons"] self.media_type = kwargs["media_type"] super(traktContextMenu, self).__init__() - def onInit(self): + def onInit(self) -> None: mange_string = ( getString(32133) if isMovie(self.media_type) else getString(32134) ) @@ -75,14 +78,14 @@ def onInit(self): self.setFocus(actionList) - def newListItem(self, label, selected=False, *args, **kwargs): + def newListItem(self, label: str, selected: bool = False, *args: Any, **kwargs: Any) -> xbmcgui.ListItem: item = xbmcgui.ListItem(label) item.select(selected) for key in kwargs: item.setProperty(key, str(kwargs[key])) return item - def onAction(self, action): + def onAction(self, action: xbmcgui.Action) -> None: if action.getId() not in ACTION_ITEM_SELECT: if action in ACTION_CLOSE_LIST: self.close() diff --git a/resources/lib/traktapi.py b/resources/lib/traktapi.py index cd30d6de..9d8ae3c8 100644 --- a/resources/lib/traktapi.py +++ b/resources/lib/traktapi.py @@ -4,6 +4,7 @@ import os import time from json import dumps, loads +from typing import Any, Dict, List, Optional import xbmcaddon from resources.lib import deviceAuthDialog @@ -34,10 +35,12 @@ class traktAPI(object): # Placeholders for build-time injection - __client_id = "TRAKT_CLIENT_ID_PLACEHOLDER" - __client_secret = "TRAKT_CLIENT_SECRET_PLACEHOLDER" + __client_id: str = "TRAKT_CLIENT_ID_PLACEHOLDER" + __client_secret: str = "TRAKT_CLIENT_SECRET_PLACEHOLDER" + authorization: Optional[Dict] = None + authDialog: Optional[deviceAuthDialog.DeviceAuthDialog] = None - def __init__(self, force=False): + def __init__(self, force: bool = False) -> None: logger.debug("Initializing.") proxyURL = checkAndConfigureProxy() @@ -76,7 +79,7 @@ def __init__(self, force=False): if last_reminder >= 0 and last_reminder < now - (24 * 60 * 60) or force: self.login() - def login(self): + def login(self) -> None: # Request new device code with Trakt.configuration.http(timeout=90): code = Trakt["oauth/device"].code() @@ -113,14 +116,15 @@ def login(self): del self.authDialog - def on_aborted(self): + def on_aborted(self) -> None: """Triggered when device authentication was aborted (either with `DeviceOAuthPoller.stop()` or via the "poll" event)""" logger.debug("Authentication aborted") - self.authDialog.close() + if self.authDialog: + self.authDialog.close() - def on_authenticated(self, token): + def on_authenticated(self, token: Dict) -> None: """Triggered when device authentication has been completed :param token: Authentication token details @@ -129,17 +133,19 @@ def on_authenticated(self, token): self.authorization = token setSetting("authorization", dumps(self.authorization)) logger.debug("Authentication complete: %r" % token) - self.authDialog.close() + if self.authDialog: + self.authDialog.close() notification(getString(32157), getString(32152), 3000) self.updateUser() - def on_expired(self): + def on_expired(self) -> None: """Triggered when the device authentication code has expired""" logger.debug("Authentication expired") - self.authDialog.close() + if self.authDialog: + self.authDialog.close() - def on_poll(self, callback): + def on_poll(self, callback: Any) -> None: """Triggered before each poll :param callback: Call with `True` to continue polling, or `False` to abort polling @@ -149,21 +155,21 @@ def on_poll(self, callback): # Continue polling callback(True) - def on_token_refreshed(self, response): + def on_token_refreshed(self, response: Dict) -> None: # OAuth token refreshed, save token for future calls self.authorization = response setSetting("authorization", dumps(self.authorization)) logger.debug("Token refreshed") - def updateUser(self): + def updateUser(self) -> None: user = self.getUser() if user and "user" in user: setSetting("user", user["user"]["username"]) else: setSetting("user", "") - def scrobbleEpisode(self, show, episode, percent, status): + def scrobbleEpisode(self, show: Dict, episode: Dict, percent: float, status: str) -> Optional[Dict]: result = None with Trakt.configuration.oauth.from_response(self.authorization): @@ -184,7 +190,7 @@ def scrobbleEpisode(self, show, episode, percent, status): logger.debug("scrobble() Bad scrobble status") return result - def scrobbleMovie(self, movie, percent, status): + def scrobbleMovie(self, movie: Dict, percent: float, status: str) -> Optional[Dict]: result = None with Trakt.configuration.oauth.from_response(self.authorization): @@ -199,94 +205,94 @@ def scrobbleMovie(self, movie, percent, status): logger.debug("scrobble() Bad scrobble status") return result - def getShowsCollected(self, shows): + def getShowsCollected(self, shows: Dict) -> Dict: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): Trakt["sync/collection"].shows(shows, exceptions=True) return shows - def getMoviesCollected(self, movies): + def getMoviesCollected(self, movies: Dict) -> Dict: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): Trakt["sync/collection"].movies(movies, exceptions=True) return movies - def getShowsWatched(self, shows): + def getShowsWatched(self, shows: Dict) -> Dict: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): Trakt["sync/watched"].shows(shows, exceptions=True) return shows - def getMoviesWatched(self, movies): + def getMoviesWatched(self, movies: Dict) -> Dict: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): Trakt["sync/watched"].movies(movies, exceptions=True) return movies - def getShowsRated(self, shows): + def getShowsRated(self, shows: Dict) -> Dict: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): Trakt["sync/ratings"].shows(store=shows, exceptions=True) return shows - def getEpisodesRated(self, shows): + def getEpisodesRated(self, shows: Dict) -> Dict: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): Trakt["sync/ratings"].episodes(store=shows, exceptions=True) return shows - def getMoviesRated(self, movies): + def getMoviesRated(self, movies: Dict) -> Dict: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True, timeout=90): Trakt["sync/ratings"].movies(store=movies, exceptions=True) return movies - def addToCollection(self, mediaObject): + def addToCollection(self, mediaObject: Dict) -> Optional[Dict]: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): result = Trakt["sync/collection"].add(mediaObject) return result - def removeFromCollection(self, mediaObject): + def removeFromCollection(self, mediaObject: Dict) -> Optional[Dict]: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): result = Trakt["sync/collection"].remove(mediaObject) return result - def addToHistory(self, mediaObject): + def addToHistory(self, mediaObject: Dict) -> Optional[Dict]: with Trakt.configuration.oauth.from_response(self.authorization): # don't try this call it may cause multiple watches result = Trakt["sync/history"].add(mediaObject) return result - def addToWatchlist(self, mediaObject): + def addToWatchlist(self, mediaObject: Dict) -> Optional[Dict]: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): result = Trakt["sync/watchlist"].add(mediaObject) return result - def getShowRatingForUser(self, showId, idType="tvdb"): + def getShowRatingForUser(self, showId: str, idType: str = "tvdb") -> Dict: ratings = {} with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): Trakt["sync/ratings"].shows(store=ratings) return findShowMatchInList(showId, ratings, idType) - def getSeasonRatingForUser(self, showId, season, idType="tvdb"): + def getSeasonRatingForUser(self, showId: str, season: int, idType: str = "tvdb") -> Dict: ratings = {} with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): Trakt["sync/ratings"].seasons(store=ratings) return findSeasonMatchInList(showId, season, ratings, idType) - def getEpisodeRatingForUser(self, showId, season, episode, idType="tvdb"): + def getEpisodeRatingForUser(self, showId: str, season: int, episode: int, idType: str = "tvdb") -> Dict: ratings = {} with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): Trakt["sync/ratings"].episodes(store=ratings) return findEpisodeMatchInList(showId, season, episode, ratings, idType) - def getMovieRatingForUser(self, movieId, idType="imdb"): + def getMovieRatingForUser(self, movieId: str, idType: str = "imdb") -> Dict: ratings = {} with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): @@ -294,20 +300,20 @@ def getMovieRatingForUser(self, movieId, idType="imdb"): return findMovieMatchInList(movieId, ratings, idType) # Send a rating to Trakt as mediaObject so we can add the rating - def addRating(self, mediaObject): + def addRating(self, mediaObject: Dict) -> Optional[Dict]: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): 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): + def removeRating(self, mediaObject: Dict) -> Optional[Dict]: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): result = Trakt["sync/ratings"].remove(mediaObject) return result - def getMoviePlaybackProgress(self): + def getMoviePlaybackProgress(self) -> List["Movie"]: progressMovies = [] # Fetch playback @@ -321,7 +327,7 @@ def getMoviePlaybackProgress(self): return progressMovies - def getEpisodePlaybackProgress(self): + def getEpisodePlaybackProgress(self) -> List["Show"]: progressEpisodes = [] # Fetch playback @@ -335,37 +341,37 @@ def getEpisodePlaybackProgress(self): return progressEpisodes - def getMovieSummary(self, movieId, extended=None): + def getMovieSummary(self, movieId: str, extended: Optional[str] = None) -> "Movie": with Trakt.configuration.http(retry=True): return Trakt["movies"].get(movieId, extended=extended) - def getShowSummary(self, showId): + def getShowSummary(self, showId: str) -> "Show": with Trakt.configuration.http(retry=True): return Trakt["shows"].get(showId) - def getShowWithAllEpisodesList(self, showId): + def getShowWithAllEpisodesList(self, showId: str) -> List: with Trakt.configuration.http(retry=True, timeout=90): return Trakt["shows"].seasons(showId, extended="episodes") - def getEpisodeSummary(self, showId, season, episode, extended=None): + def getEpisodeSummary(self, showId: str, season: int, episode: int, extended: Optional[str] = None) -> Any: with Trakt.configuration.http(retry=True): return Trakt["shows"].episode(showId, season, episode, extended=extended) - def getIdLookup(self, id, id_type): + def getIdLookup(self, id: str, id_type: str) -> Optional[List]: with Trakt.configuration.http(retry=True): result = Trakt["search"].lookup(id, id_type) if result and not isinstance(result, list): result = [result] return result - def getTextQuery(self, query, type, year): + def getTextQuery(self, query: str, type: str, year: Optional[int]) -> Optional[List]: with Trakt.configuration.http(retry=True, timeout=90): result = Trakt["search"].query(query, type, year) if result and not isinstance(result, list): result = [result] return result - def getUser(self): + def getUser(self) -> Optional[Dict]: with Trakt.configuration.oauth.from_response(self.authorization): with Trakt.configuration.http(retry=True): result = Trakt["users/settings"].get() From b5ad4747ba2dbf846c255f50fa8ee39cad8b52da Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:49:08 +0000 Subject: [PATCH 2/2] feat: fix unused imports and improve type hints Removed unused Optional and List imports from script.py, sync.py, and syncEpisodes.py that were causing CI failures. Finalized comprehensive type hints across resources/lib/. Co-authored-by: razzeee <5943908+razzeee@users.noreply.github.com> --- resources/lib/script.py | 2 +- resources/lib/sync.py | 2 +- resources/lib/syncEpisodes.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/lib/script.py b/resources/lib/script.py index e1de6e70..aa3d3478 100644 --- a/resources/lib/script.py +++ b/resources/lib/script.py @@ -5,7 +5,7 @@ from resources.lib import utilities from resources.lib import kodiUtilities from resources.lib import sqlitequeue -from typing import Dict, Optional +from typing import Dict from resources.lib.traktContextMenu import traktContextMenu logger = logging.getLogger(__name__) diff --git a/resources/lib/sync.py b/resources/lib/sync.py index 69beb4bd..536fdca9 100644 --- a/resources/lib/sync.py +++ b/resources/lib/sync.py @@ -2,7 +2,7 @@ import logging -from typing import Optional, Any +from typing import Any import xbmc import xbmcgui diff --git a/resources/lib/syncEpisodes.py b/resources/lib/syncEpisodes.py index fa40398e..860abcbe 100644 --- a/resources/lib/syncEpisodes.py +++ b/resources/lib/syncEpisodes.py @@ -2,7 +2,7 @@ import copy import logging -from typing import Dict, List, Tuple, Union, Optional, Any +from typing import Dict, Tuple, Union, Optional, Any from resources.lib import kodiUtilities, utilities