diff --git a/backend/src/module/database/user.py b/backend/src/module/database/user.py index efba0618..f3a69069 100644 --- a/backend/src/module/database/user.py +++ b/backend/src/module/database/user.py @@ -73,10 +73,15 @@ class UserDatabase: try: result = self.session.exec(statement) users = list(result.all()) - except Exception: + except Exception as e: + # Table may not exist yet during initial setup + logger.debug( + f"[Database] Could not query users table (may not exist yet): {e}" + ) users = [] if len(users) != 0: return user = User(username="admin", password=get_password_hash("adminadmin")) self.session.add(user) self.session.commit() + logger.info("[Database] Created default admin user") diff --git a/backend/src/module/downloader/client/qb_downloader.py b/backend/src/module/downloader/client/qb_downloader.py index b053c957..90efe007 100644 --- a/backend/src/module/downloader/client/qb_downloader.py +++ b/backend/src/module/downloader/client/qb_downloader.py @@ -26,9 +26,7 @@ class QbDownloader: async def auth(self, retry=3): times = 0 timeout = httpx.Timeout(connect=3.1, read=10.0, write=10.0, pool=10.0) - self._client = httpx.AsyncClient( - timeout=timeout, verify=self.ssl - ) + self._client = httpx.AsyncClient(timeout=timeout, verify=self.ssl) while times < retry: try: resp = await self._client.post( @@ -61,8 +59,12 @@ class QbDownloader: if self._client: try: await self._client.post(self._url("auth/logout")) - except Exception: - pass + except ( + httpx.ConnectError, + httpx.RequestError, + httpx.TimeoutException, + ) as e: + logger.debug(f"[Downloader] Logout request failed (non-critical): {e}") await self._client.aclose() self._client = None @@ -114,7 +116,9 @@ class QbDownloader: ) return resp.json() - async def add_torrents(self, torrent_urls, torrent_files, save_path, category, tags=None): + async def add_torrents( + self, torrent_urls, torrent_files, save_path, category, tags=None + ): data = { "savepath": save_path, "category": category, @@ -133,9 +137,17 @@ class QbDownloader: if torrent_files: if isinstance(torrent_files, list): for i, f in enumerate(torrent_files): - files[f"torrents_{i}"] = (f"torrent_{i}.torrent", f, "application/x-bittorrent") + files[f"torrents_{i}"] = ( + f"torrent_{i}.torrent", + f, + "application/x-bittorrent", + ) else: - files["torrents"] = ("torrent.torrent", torrent_files, "application/x-bittorrent") + files["torrents"] = ( + "torrent.torrent", + torrent_files, + "application/x-bittorrent", + ) max_retries = 3 for attempt in range(max_retries): @@ -153,14 +165,14 @@ class QbDownloader: ) await asyncio.sleep(2) else: - logger.error(f"[Downloader] Failed to add torrent after {max_retries} attempts: {e}") + logger.error( + f"[Downloader] Failed to add torrent after {max_retries} attempts: {e}" + ) raise async def get_torrents_by_tag(self, tag: str) -> list[dict]: """Get all torrents with a specific tag.""" - resp = await self._client.get( - self._url("torrents/info"), params={"tag": tag} - ) + resp = await self._client.get(self._url("torrents/info"), params={"tag": tag}) return resp.json() async def torrents_delete(self, hash, delete_files: bool = True): @@ -191,7 +203,8 @@ class QbDownloader: logger.debug(f"Conflict409Error: {old_path} >> {new_path}") return False return resp.status_code == 200 - except Exception: + except (httpx.ConnectError, httpx.RequestError, httpx.TimeoutException) as e: + logger.warning(f"[Downloader] Failed to rename file {old_path}: {e}") return False async def rss_add_feed(self, url, item_path): @@ -216,6 +229,7 @@ class QbDownloader: async def rss_set_rule(self, rule_name, rule_def): import json + await self._client.post( self._url("rss/setRule"), data={"ruleName": rule_name, "ruleDef": json.dumps(rule_def)}, diff --git a/backend/src/module/downloader/download_client.py b/backend/src/module/downloader/download_client.py index 77261c87..8b2a8e9a 100644 --- a/backend/src/module/downloader/download_client.py +++ b/backend/src/module/downloader/download_client.py @@ -70,10 +70,13 @@ class DownloadClient(TorrentPath): "rss_refresh_interval": 30, } await self.client.prefs_init(prefs=prefs) + # Category creation may fail if it already exists (HTTP 409) or network issues try: await self.client.add_category("BangumiCollection") - except Exception: - logger.debug("[Downloader] Cannot add new category, maybe already exists.") + except Exception as e: + logger.debug( + f"[Downloader] Could not add category (may already exist): {e}" + ) if settings.downloader.path == "": prefs = await self.client.get_app_prefs() settings.downloader.path = self._join_path(prefs["save_path"], "Bangumi") @@ -107,7 +110,9 @@ class DownloadClient(TorrentPath): await asyncio.gather(*[self.set_rule(info) for info in bangumi_info]) logger.debug("[Downloader] Finished.") - async def get_torrent_info(self, category="Bangumi", status_filter="completed", tag=None): + async def get_torrent_info( + self, category="Bangumi", status_filter="completed", tag=None + ): return await self.client.torrents_info( status_filter=status_filter, category=category, tag=tag ) @@ -137,7 +142,9 @@ class DownloadClient(TorrentPath): async with RequestContent() as req: if isinstance(torrent, list): if len(torrent) == 0: - logger.debug(f"[Downloader] No torrent found: {bangumi.official_title}") + logger.debug( + f"[Downloader] No torrent found: {bangumi.official_title}" + ) return False if "magnet" in torrent[0].url: torrent_url = [t.url for t in torrent] @@ -149,7 +156,9 @@ class DownloadClient(TorrentPath): # Filter out None values (failed fetches) torrent_file = [f for f in torrent_file if f is not None] if not torrent_file: - logger.warning(f"[Downloader] Failed to fetch torrent files for: {bangumi.official_title}") + logger.warning( + f"[Downloader] Failed to fetch torrent files for: {bangumi.official_title}" + ) return False torrent_url = None else: @@ -159,7 +168,9 @@ class DownloadClient(TorrentPath): else: torrent_file = await req.get_content(torrent.url) if torrent_file is None: - logger.warning(f"[Downloader] Failed to fetch torrent file for: {bangumi.official_title}") + logger.warning( + f"[Downloader] Failed to fetch torrent file for: {bangumi.official_title}" + ) return False torrent_url = None # Create tag with bangumi_id for offset lookup during rename @@ -175,10 +186,14 @@ class DownloadClient(TorrentPath): logger.debug(f"[Downloader] Add torrent: {bangumi.official_title}") return True else: - logger.debug(f"[Downloader] Torrent added before: {bangumi.official_title}") + logger.debug( + f"[Downloader] Torrent added before: {bangumi.official_title}" + ) return False except Exception as e: - logger.error(f"[Downloader] Failed to add torrent for {bangumi.official_title}: {e}") + logger.error( + f"[Downloader] Failed to add torrent for {bangumi.official_title}: {e}" + ) return False async def move_torrent(self, hashes, location): diff --git a/backend/src/module/parser/title_parser.py b/backend/src/module/parser/title_parser.py index 96f757ef..78a84271 100644 --- a/backend/src/module/parser/title_parser.py +++ b/backend/src/module/parser/title_parser.py @@ -44,7 +44,9 @@ class TitleParser: @staticmethod async def tmdb_poster_parser(bangumi: Bangumi): - tmdb_info = await tmdb_parser(bangumi.official_title, settings.rss_parser.language) + tmdb_info = await tmdb_parser( + bangumi.official_title, settings.rss_parser.language + ) if tmdb_info: logger.debug(f"TMDB Matched, official title is {tmdb_info.title}") bangumi.poster_link = tmdb_info.poster_link @@ -98,9 +100,8 @@ class TitleParser: offset=0, filter=",".join(settings.rss_parser.filter), ) - except Exception as e: - logger.debug(e) - logger.warning(f"Cannot parse {raw}.") + except (ValueError, AttributeError, TypeError) as e: + logger.warning(f"Cannot parse '{raw}': {type(e).__name__}: {e}") return None @staticmethod