From decb09ecca1c808f396358ae2bfc5651fddf17c3 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Mon, 12 May 2025 13:03:36 +0200 Subject: [PATCH] Small fixes & error handling --- scanner/scanner/client.py | 4 +- scanner/scanner/fsscan.py | 87 ++++++++++--------- scanner/scanner/providers/themoviedatabase.py | 2 +- 3 files changed, 50 insertions(+), 43 deletions(-) diff --git a/scanner/scanner/client.py b/scanner/scanner/client.py index 9478fd2a..f6d630f9 100644 --- a/scanner/scanner/client.py +++ b/scanner/scanner/client.py @@ -18,7 +18,7 @@ class KyooClient: print("Missing environment variable 'KYOO_APIKEY'.") exit(2) self._client = ClientSession( - base_url=os.environ.get("KYOO_URL", "http://api:3567/api"), + base_url=os.environ.get("KYOO_URL", "http://api:3567/api") + "/", headers={ "User-Agent": "kyoo scanner v5", "X-API-KEY": api_key, @@ -37,7 +37,7 @@ class KyooClient: await self._client.close() async def get_videos_info(self) -> VideoInfo: - async with self._client.get("/videos") as r: + async with self._client.get("videos") as r: r.raise_for_status() return VideoInfo(**await r.json()) diff --git a/scanner/scanner/fsscan.py b/scanner/scanner/fsscan.py index 8818b686..c2e4c289 100644 --- a/scanner/scanner/fsscan.py +++ b/scanner/scanner/fsscan.py @@ -39,56 +39,63 @@ class Scanner: logger.info("Starting scan at %s. This may take some time...", path) if self._ignore_pattern: logger.info(f"Applying ignore pattern: {self._ignore_pattern}") - videos = self.walk_fs(path) + try: + videos = self.walk_fs(path) - self._info = await self._client.get_videos_info() + self._info = await self._client.get_videos_info() - # TODO: handle unmatched - to_register = videos - self._info.paths - to_delete = self._info.paths - videos if remove_deleted else set() + # TODO: handle unmatched + to_register = videos - self._info.paths + to_delete = self._info.paths - videos if remove_deleted else set() - if ( - not any(to_register) - and any(to_delete) - and len(to_delete) == len(self._info.paths) - ): - logger.warning("All video files are unavailable. Check your disks.") - return + if ( + not any(to_register) + and any(to_delete) + and len(to_delete) == len(self._info.paths) + ): + logger.warning("All video files are unavailable. Check your disks.") + return - # delete stale files before creating new ones to prevent potential conflicts - if to_delete: - logger.info("Removing %d stale files.", len(to_delete)) - await self._client.delete_videos(to_delete) + # delete stale files before creating new ones to prevent potential conflicts + if to_delete: + logger.info("Removing %d stale files.", len(to_delete)) + await self._client.delete_videos(to_delete) - if to_register: - logger.info("Found %d new files to register.", len(to_register)) - await self._register(to_register) + if to_register: + logger.info("Found %d new files to register.", len(to_register)) + await self._register(to_register) - logger.info("Scan finished for %s.", path) + logger.info("Scan finished for %s.", path) + except Exception as e: + logger.error("Unexpected error while running scan.", exc_info=e) async def monitor(self): + logger.info(f"Watching for new files in {self._root_path}") async for changes in awatch(self._root_path, ignore_permission_denied=True): - for event, file in changes: - if not isdir(file) and not is_video(file): - continue - if ( - self._ignore_pattern and self._ignore_pattern.match(file) - ) or is_ignored_path(file): - logger.info("Ignoring event %s for file %s", event, file) - continue + try: + for event, file in changes: + if not isdir(file) and not is_video(file): + continue + if ( + self._ignore_pattern and self._ignore_pattern.match(file) + ) or is_ignored_path(file): + logger.info("Ignoring event %s for file %s", event, file) + continue - match event: - case Change.added if isdir(file): - logger.info("New dir found: %s", file) - await self.scan(file) - case Change.added: - logger.info("New video found: %s", file) - await self._register([file]) - case Change.deleted: - logger.info("Delete video at: %s", file) - await self._client.delete_videos([file]) - case Change.modified: - pass + match event: + case Change.added if isdir(file): + logger.info("New dir found: %s", file) + await self.scan(file) + case Change.added: + logger.info("New video found: %s", file) + await self._register([file]) + case Change.deleted: + logger.info("Delete video at: %s", file) + await self._client.delete_videos([file]) + case Change.modified: + pass + except Exception as e: + logger.error("Unexpected error while monitoring files.", exc_info=e) async def _register(self, videos: list[str] | set[str]): # TODO: we should probably chunk those diff --git a/scanner/scanner/providers/themoviedatabase.py b/scanner/scanner/providers/themoviedatabase.py index 66a1467f..6c7a71f9 100644 --- a/scanner/scanner/providers/themoviedatabase.py +++ b/scanner/scanner/providers/themoviedatabase.py @@ -31,7 +31,7 @@ class TheMovieDatabase(Provider): def __init__(self) -> None: super().__init__() self._client = ClientSession( - base_url="https://api.themoviedb.org/3", + base_url="https://api.themoviedb.org/3/", headers={ "User-Agent": "kyoo scanner v5", "X-API-KEY": (