This commit is contained in:
Odd Stråbø 2025-09-17 20:51:29 -03:00 committed by GitHub
commit e9ee4989c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 163 additions and 50 deletions

1
.gitignore vendored
View file

@ -70,6 +70,7 @@ Thumbs.db
!.vscode/extensions.json
!.vscode/settings.json
pyinstrument/
.venv/
# Now managed by templates
addon.xml

View file

@ -1,6 +1,7 @@
{
"recommendations": [
"ms-vscode-remote.remote-containers",
"ms-python.black-formatter"
"ms-python.black-formatter",
"ms-python.mypy-type-checker"
]
}

View file

@ -10,6 +10,7 @@ from importlib import reload
# Workaround for threads using datetime: _striptime is locked
import _strptime # noqa:F401
import xbmc
import xbmcgui

View file

@ -13,7 +13,12 @@ from .objects import Movies, TVShows, MusicVideos, Music
from .database import Database, get_sync, save_sync, jellyfin_db
from .helper import translate, settings, window, progress, dialog, LazyLogger, xmls
from .helper.utils import get_screensaver, set_screensaver
from .helper.exceptions import LibraryException, PathValidationException
from .helper.exceptions import (
LibraryException,
LibraryExitException,
LibrarySyncLaterException,
PathValidationException,
)
##################################################################################################
@ -169,11 +174,13 @@ class FullSync(object):
selection = dialog("multi", translate(33120), choices)
if selection is None:
raise LibraryException("LibrarySelection")
# TODO: Why are we handling these two differently?
# presumably one means the dialog got aborted, the other means that we just pressed ok without selecting any libraries
raise LibraryException("Library selection dialog returned None.")
elif not selection:
LOG.info("Nothing was selected.")
raise LibraryException("SyncLibraryLater")
raise LibrarySyncLaterException("No libraries where selected, sync later.")
if 0 in selection:
selection = list(range(1, len(libraries) + 1))
@ -275,11 +282,13 @@ class FullSync(object):
media[library["CollectionType"]](library)
except LibraryException as error:
if error.status == "StopCalled":
# TODO: Fixme; We're catching all LibraryException here,
# but silently ignoring any that isn't the exit condition.
# Investigate what would be appropriate behavior here.
if isinstance(error, LibraryExitException):
save_sync(self.sync)
raise
LOG.warning("Ignoring exception %s", error)
except PathValidationException:
raise

View file

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
from __future__ import division, absolute_import, print_function, unicode_literals
import json
##################################################################################################
from . import settings, LazyLogger
from .utils import translate_path
import json
##################################################################################################

View file

@ -19,9 +19,15 @@ class HTTPException(Exception):
class LibraryException(Exception):
# Jellyfin library sync exception
def __init__(self, status):
self.status = status
pass
class LibraryExitException(LibraryException):
"Exception raised to propagate application exit."
class LibrarySyncLaterException(LibraryException):
"Raised when no libraries are selected for sync."
class PathValidationException(Exception):
@ -30,5 +36,3 @@ class PathValidationException(Exception):
TODO: Investigate the usage of this to see if it can be done better.
"""
pass

View file

@ -3,7 +3,8 @@ from __future__ import division, absolute_import, print_function, unicode_litera
class LazyLogger(object):
"""`helper.loghandler.getLogger()` is used everywhere.
"""
`helper.loghandler.getLogger()` is used everywhere.
This class helps to avoid import errors.
"""
@ -19,3 +20,86 @@ class LazyLogger(object):
self.__logger = getLogger(self.__logger_name)
return getattr(self.__logger, name)
#####################################################################
# Following are stubs of methods provided by `logging.Logger`. #
# Please ensure any actually functional code is above this comment. #
#####################################################################
def setLevel(self, level):
"""
Set the logging level of this logger. level must be an int or a str.
"""
...
def debug(self, msg, *args, **kwargs):
"""
Log 'msg % args' with severity 'DEBUG'.
To pass exception information, use the keyword argument exc_info with
a true value, e.g.
logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
"""
...
def info(self, msg, *args, **kwargs):
"""
Log 'msg % args' with severity 'INFO'.
To pass exception information, use the keyword argument exc_info with
a true value, e.g.
logger.info("Houston, we have a %s", "interesting problem", exc_info=1)
"""
...
def warning(self, msg, *args, **kwargs):
"""
Log 'msg % args' with severity 'WARNING'.
To pass exception information, use the keyword argument exc_info with
a true value, e.g.
logger.warning("Houston, we have a %s", "bit of a problem", exc_info=1)
"""
...
def error(self, msg, *args, **kwargs):
"""
Log 'msg % args' with severity 'ERROR'.
To pass exception information, use the keyword argument exc_info with
a true value, e.g.
logger.error("Houston, we have a %s", "major problem", exc_info=1)
"""
...
def exception(self, msg, *args, exc_info=True, **kwargs):
"""
Convenience method for logging an ERROR with exception information.
"""
...
def critical(self, msg, *args, **kwargs):
"""
Log 'msg % args' with severity 'CRITICAL'.
To pass exception information, use the keyword argument exc_info with
a true value, e.g.
logger.critical("Houston, we have a %s", "major disaster", exc_info=1)
"""
...
def log(self, level, msg, *args, **kwargs):
"""
Log 'msg % args' with the integer severity 'level'.
To pass exception information, use the keyword argument exc_info with
a true value, e.g.
logger.log(level, "We have a %s", "mysterious problem", exc_info=1)
"""
...

View file

@ -162,18 +162,6 @@ def dialog(dialog_type, *args, **kwargs):
return types[dialog_type](*args, **kwargs)
def should_stop():
"""Checkpoint during the sync process."""
if xbmc.Monitor().waitForAbort(0.00001):
return True
if window("jellyfin_should_stop.bool"):
LOG.info("exiiiiitttinggg")
return True
return not window("jellyfin_online.bool")
def get_screensaver():
"""Get the current screensaver value."""
result = JSONRPC("Settings.getSettingValue").execute(

View file

@ -4,11 +4,12 @@ from __future__ import division, absolute_import, print_function, unicode_litera
#################################################################################################
import xbmcgui
import xbmc
from . import LazyLogger
from .utils import should_stop
from .exceptions import LibraryException
from .utils import window
from .exceptions import LibraryExitException
from .translate import translate
#################################################################################################
@ -55,14 +56,15 @@ def stop(func):
def wrapper(*args, **kwargs):
try:
if should_stop(): # ??? TODO: Fixme
raise Exception
if xbmc.Monitor().waitForAbort(0.00001):
raise LibraryExitException("Kodi aborted, exiting...")
except Exception as error:
LOG.exception(error)
if window("jellyfin_should_stop.bool"):
LOG.info("exiiiiitttinggg")
raise LibraryExitException("Should stop flag raised, exiting...")
raise LibraryException("StopCalled")
if not window("jellyfin_online.bool"):
raise LibraryExitException("Jellyfin not online, exiting...")
return func(*args, **kwargs)

View file

@ -19,7 +19,11 @@ from .views import Views
from .downloader import GetItemWorker
from .helper import translate, api, stop, settings, window, dialog, event, LazyLogger
from .helper.utils import split_list, set_screensaver, get_screensaver
from .helper.exceptions import LibraryException
from .helper.exceptions import (
LibraryException,
LibraryExitException,
LibrarySyncLaterException,
)
from .jellyfin import Jellyfin
##################################################################################################
@ -93,7 +97,8 @@ class Library(threading.Thread):
try:
self.service()
except LibraryException:
except LibraryException as error:
LOG.warning(error)
break
except Exception as error:
LOG.exception(error)
@ -438,19 +443,20 @@ class Library(threading.Thread):
return True
return True
except LibrarySyncLaterException as error:
LOG.error(error.status)
dialog("ok", "{jellyfin}", translate(33129))
settings("SyncInstallRunDone.bool", True)
sync = get_sync()
sync["Libraries"] = []
save_sync(sync)
return True
except LibraryException as error:
LOG.error(error.status)
if error.status in "SyncLibraryLater":
dialog("ok", "{jellyfin}", translate(33129))
settings("SyncInstallRunDone.bool", True)
sync = get_sync()
sync["Libraries"] = []
save_sync(sync)
return True
except Exception as error:
LOG.exception(error)
@ -758,8 +764,12 @@ class UpdateWorker(threading.Thread):
(item["Type"], api.API(item).get_naming())
)
except LibraryException as error:
if error.status == "StopCalled":
# TODO: Fixme; We're catching all LibraryException here,
# but silently ignoring any that isn't the exit condition.
# Investigate what would be appropriate behavior here.
if isinstance(error, LibraryExitException):
break
LOG.warning("Ignoring exception %s", error)
except Exception as error:
LOG.exception(error)
@ -823,8 +833,12 @@ class UserDataWorker(threading.Thread):
elif item["Type"] == "Audio":
music.userdata(item)
except LibraryException as error:
if error.status == "StopCalled":
# TODO: Fixme; We're catching all LibraryException here,
# but silently ignoring any that isn't the exit condition.
# Investigate what would be appropriate behavior here.
if isinstance(error, LibraryExitException):
break
LOG.warning("Ignoring exception %s", error)
except Exception as error:
LOG.exception(error)
@ -943,8 +957,12 @@ class RemovedWorker(threading.Thread):
try:
obj(item["Id"])
except LibraryException as error:
if error.status == "StopCalled":
# TODO: Fixme; We're catching all LibraryException here,
# but silently ignoring any that isn't the exit condition.
# Investigate what would be appropriate behavior here.
if isinstance(error, LibraryExitException):
break
LOG.warning("Ignoring exception %s", error)
except Exception as error:
LOG.exception(error)
finally:

4
mypy.ini Normal file
View file

@ -0,0 +1,4 @@
[mypy]
check_untyped_defs = True
warn_unused_configs = True
files = .

View file

@ -1,7 +1,7 @@
[flake8]
max-line-length = 9999
import-order-style = pep8
exclude = .git,.vscode
exclude = .git,.vscode,.venv
extend-ignore =
I202
E203