diff --git a/app/chain/mediaserver.py b/app/chain/mediaserver.py index ead78f89..50d06c66 100644 --- a/app/chain/mediaserver.py +++ b/app/chain/mediaserver.py @@ -1,5 +1,5 @@ import threading -from typing import List, Union, Optional, Generator +from typing import List, Union, Optional, Generator, Any from app.chain import ChainBase from app.core.cache import cached @@ -27,8 +27,8 @@ class MediaServerChain(ChainBase): """ return self.run_module("mediaserver_librarys", server=server, username=username, hidden=hidden) - def items(self, server: str, library_id: Union[str, int], start_index: int = 0, limit: Optional[int] = -1) \ - -> Optional[Generator]: + def items(self, server: str, library_id: Union[str, int], + start_index: int = 0, limit: Optional[int] = -1) -> Generator[Any, None, None]: """ 获取媒体服务器项目列表,支持分页和不分页逻辑,默认不分页获取所有数据 diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index 63af834d..d63d9f9d 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -1262,7 +1262,7 @@ class SubscribeChain(ChainBase, metaclass=Singleton): 订阅相关的下载和文件信息 """ if not subscribe: - return + return None # 返回订阅数据 subscribe_info = schemas.SubscrbieInfo() diff --git a/app/chain/transfer.py b/app/chain/transfer.py index 67804384..6a369658 100644 --- a/app/chain/transfer.py +++ b/app/chain/transfer.py @@ -606,7 +606,7 @@ class TransferChain(ChainBase, metaclass=Singleton): logger.error(f"整理队列处理出现错误:{e} - {traceback.format_exc()}") def __handle_transfer(self, task: TransferTask, - callback: Optional[Callable] = None) -> Tuple[bool, str]: + callback: Optional[Callable] = None) -> Optional[Tuple[bool, str]]: """ 处理整理任务 """ diff --git a/app/core/module.py b/app/core/module.py index c3543cff..25753d9b 100644 --- a/app/core/module.py +++ b/app/core/module.py @@ -121,7 +121,7 @@ class ModuleManager(metaclass=Singleton): 获取实现了同一方法的模块列表 """ if not self._running_modules: - return [] + return for _, module in self._running_modules.items(): if hasattr(module, method) \ and ObjectUtils.check_method(getattr(module, method)): @@ -132,7 +132,7 @@ class ModuleManager(metaclass=Singleton): 获取指定类型的模块列表 """ if not self._running_modules: - return [] + return for _, module in self._running_modules.items(): if hasattr(module, 'get_type') \ and module.get_type() == module_type: @@ -143,7 +143,7 @@ class ModuleManager(metaclass=Singleton): 获取指定子类型的模块 """ if not self._running_modules: - return [] + return for _, module in self._running_modules.items(): if hasattr(module, 'get_subtype') \ and module.get_subtype() == module_subtype: diff --git a/app/core/security.py b/app/core/security.py index fbde303b..2a7dae40 100644 --- a/app/core/security.py +++ b/app/core/security.py @@ -4,7 +4,8 @@ import hmac import json import os import traceback -from datetime import datetime, timedelta +import datetime +from datetime import timedelta from typing import Any, Union, Annotated, Optional import jwt @@ -69,13 +70,13 @@ def create_access_token( if expires_delta is not None: if expires_delta.total_seconds() <= 0: raise ValueError("过期时间必须为正数") - expire = datetime.utcnow() + expires_delta + expire = datetime.datetime.now(datetime.UTC) + expires_delta else: - expire = datetime.utcnow() + default_expire + expire = datetime.datetime.now(datetime.UTC) + default_expire to_encode = { "exp": expire, - "iat": datetime.utcnow(), + "iat": datetime.datetime.now(datetime.UTC), "sub": str(userid), "username": username, "super_user": super_user, @@ -102,7 +103,7 @@ def __set_or_refresh_resource_token_cookie(request: Request, response: Response, decoded_token = jwt.decode(resource_token, settings.RESOURCE_SECRET_KEY, algorithms=[ALGORITHM]) exp = decoded_token.get("exp") if exp: - remaining_time = datetime.utcfromtimestamp(exp) - datetime.utcnow() + remaining_time = datetime.datetime.fromtimestamp(exp) - datetime.datetime.now(datetime.UTC) # 根据剩余时长提前刷新令牌 if remaining_time < timedelta(seconds=(settings.RESOURCE_ACCESS_TOKEN_EXPIRE_SECONDS / 3)): raise jwt.ExpiredSignatureError diff --git a/app/helper/browser.py b/app/helper/browser.py index 2bbf7d30..67a98192 100644 --- a/app/helper/browser.py +++ b/app/helper/browser.py @@ -1,4 +1,4 @@ -from typing import Callable, Any +from typing import Callable, Any, Optional from playwright.sync_api import sync_playwright, Page from cf_clearance import sync_cf_retry, sync_stealth @@ -61,7 +61,7 @@ class PlaywrightHelper: ua: str = None, proxies: dict = None, headless: bool = False, - timeout: int = 20) -> str: + timeout: int = 20) -> Optional[str]: """ 获取网页源码 :param url: 网页地址 diff --git a/app/modules/emby/emby.py b/app/modules/emby/emby.py index a3bf4508..ad6f3152 100644 --- a/app/modules/emby/emby.py +++ b/app/modules/emby/emby.py @@ -3,7 +3,7 @@ import re import traceback from datetime import datetime from pathlib import Path -from typing import List, Optional, Union, Dict, Generator, Tuple +from typing import List, Optional, Union, Dict, Generator, Tuple, Any from requests import Response @@ -13,6 +13,7 @@ from app.log import logger from app.schemas.types import MediaType from app.utils.http import RequestUtils from app.utils.url import UrlUtils +from schemas import MediaServerItem class Emby: @@ -545,7 +546,7 @@ class Emby: return False return False - def refresh_library_by_items(self, items: List[schemas.RefreshMediaItem]) -> bool: + def refresh_library_by_items(self, items: List[schemas.RefreshMediaItem]) -> Optional[bool]: """ 按类型、名称、年份来刷新媒体库 :param items: 已识别的需要刷新媒体库的媒体信息列表 @@ -668,8 +669,8 @@ class Emby: logger.error(f"连接/Users/{self.user}/Items/{itemid}出错:" + str(e)) return None - def get_items(self, parent: Union[str, int], start_index: int = 0, limit: Optional[int] = -1) \ - -> Optional[Generator]: + def get_items(self, parent: Union[str, int], start_index: int = 0, + limit: Optional[int] = -1) -> Generator[MediaServerItem | None | Any, Any, None]: """ 获取媒体服务器项目列表,支持分页和不分页逻辑,默认不分页获取所有数据 diff --git a/app/modules/filemanager/storages/alist.py b/app/modules/filemanager/storages/alist.py index 7f563db9..0404a3c0 100644 --- a/app/modules/filemanager/storages/alist.py +++ b/app/modules/filemanager/storages/alist.py @@ -201,12 +201,12 @@ class Alist(StorageBase, metaclass=Singleton): if resp is None: logging.warning(f"请求获取目录 {fileitem.path} 的文件列表失败,无法连接alist服务") - return + return None if resp.status_code != 200: logging.warning( f"请求获取目录 {fileitem.path} 的文件列表失败,状态码:{resp.status_code}" ) - return + return None result = resp.json() @@ -214,7 +214,7 @@ class Alist(StorageBase, metaclass=Singleton): logging.warning( f'获取目录 {fileitem.path} 的文件列表失败,错误信息:{result["message"]}' ) - return + return None return [ schemas.FileItem( @@ -259,15 +259,15 @@ class Alist(StorageBase, metaclass=Singleton): """ if resp is None: logging.warning(f"请求创建目录 {path} 失败,无法连接alist服务") - return + return None if resp.status_code != 200: logging.warning(f"请求创建目录 {path} 失败,状态码:{resp.status_code}") - return + return None result = resp.json() if result["code"] != 200: logging.warning(f'创建目录 {path} 失败,错误信息:{result["message"]}') - return + return None return self.get_item(path) @@ -349,15 +349,15 @@ class Alist(StorageBase, metaclass=Singleton): """ if resp is None: logging.warning(f"请求获取文件 {path} 失败,无法连接alist服务") - return + return None if resp.status_code != 200: logging.warning(f"请求获取文件 {path} 失败,状态码:{resp.status_code}") - return + return None result = resp.json() if result["code"] != 200: logging.debug(f'获取文件 {path} 失败,错误信息:{result["message"]}') - return + return None return schemas.FileItem( storage=self.schema.value, @@ -513,15 +513,15 @@ class Alist(StorageBase, metaclass=Singleton): """ if not resp: logging.warning(f"请求获取文件 {path} 失败,无法连接alist服务") - return + return None if resp.status_code != 200: logging.warning(f"请求获取文件 {path} 失败,状态码:{resp.status_code}") - return + return None result = resp.json() if result["code"] != 200: logging.warning(f'获取文件 {path} 失败,错误信息:{result["message"]}') - return + return None if result["data"]["raw_url"]: download_url = result["data"]["raw_url"] @@ -569,7 +569,7 @@ class Alist(StorageBase, metaclass=Singleton): if resp.status_code != 200: logging.warning(f"请求上传文件 {path} 失败,状态码:{resp.status_code}") - return + return None new_item = self.get_item(Path(fileitem.path) / path.name) if new_item and new_name and new_name != path.name: diff --git a/app/modules/filter/__init__.py b/app/modules/filter/__init__.py index 7d6d0270..8b100a10 100644 --- a/app/modules/filter/__init__.py +++ b/app/modules/filter/__init__.py @@ -259,7 +259,7 @@ class FilterModule(_ModuleBase): return None if not matched else torrent - def __match_group(self, torrent: TorrentInfo, rule_group: Union[list, str]) -> bool: + def __match_group(self, torrent: TorrentInfo, rule_group: Union[list, str]) -> Optional[bool]: """ 判断种子是否匹配规则组 """ diff --git a/app/modules/indexer/parser/ipt_project.py b/app/modules/indexer/parser/ipt_project.py index cb268002..643cd0b3 100644 --- a/app/modules/indexer/parser/ipt_project.py +++ b/app/modules/indexer/parser/ipt_project.py @@ -54,7 +54,7 @@ class IptSiteUserInfo(SiteParserBase): def _parse_user_torrent_seeding_info(self, html_text: str, multi_page: bool = False) -> Optional[str]: html = etree.HTML(html_text) if not StringUtils.is_valid_html_element(html): - return + return None # seeding start seeding_end_pos = 3 if html.xpath('//tr/td[text() = "Leechers"]'): diff --git a/app/modules/indexer/parser/tnode.py b/app/modules/indexer/parser/tnode.py index 9b010670..1100a62d 100644 --- a/app/modules/indexer/parser/tnode.py +++ b/app/modules/indexer/parser/tnode.py @@ -65,7 +65,7 @@ class TNodeSiteUserInfo(SiteParserBase): """ seeding_info = json.loads(html_text) if seeding_info.get("status") != 200: - return + return None torrents = seeding_info.get("data", {}).get("torrents", []) diff --git a/app/modules/jellyfin/jellyfin.py b/app/modules/jellyfin/jellyfin.py index 50b5e096..3dc68494 100644 --- a/app/modules/jellyfin/jellyfin.py +++ b/app/modules/jellyfin/jellyfin.py @@ -1,6 +1,6 @@ import json from datetime import datetime -from typing import List, Union, Optional, Dict, Generator, Tuple +from typing import List, Union, Optional, Dict, Generator, Tuple, Any from requests import Response @@ -10,6 +10,7 @@ from app.log import logger from app.schemas import MediaType from app.utils.http import RequestUtils from app.utils.url import UrlUtils +from schemas import MediaServerItem class Jellyfin: @@ -548,7 +549,7 @@ class Jellyfin: logger.error(f"连接Items/Id/Ancestors出错:" + str(e)) return None - def refresh_root_library(self) -> bool: + def refresh_root_library(self) -> Optional[bool]: """ 通知Jellyfin刷新整个媒体库 """ @@ -762,7 +763,7 @@ class Jellyfin: return None def get_items(self, parent: Union[str, int], start_index: int = 0, limit: Optional[int] = -1) \ - -> Optional[Generator]: + -> Generator[MediaServerItem | None | Any, Any, None]: """ 获取媒体服务器项目列表,支持分页和不分页逻辑,默认不分页获取所有数据 diff --git a/app/modules/plex/plex.py b/app/modules/plex/plex.py index 48e4be62..6009d0cf 100644 --- a/app/modules/plex/plex.py +++ b/app/modules/plex/plex.py @@ -14,6 +14,7 @@ from app.log import logger from app.schemas import MediaType from app.utils.http import RequestUtils from app.utils.url import UrlUtils +from schemas import MediaServerItem class Plex: @@ -367,7 +368,7 @@ class Plex: return False return self._plex.library.update() - def refresh_library_by_items(self, items: List[schemas.RefreshMediaItem]) -> bool: + def refresh_library_by_items(self, items: List[schemas.RefreshMediaItem]) -> Optional[bool]: """ 按路径刷新媒体库 item: target_path """ @@ -512,7 +513,7 @@ class Plex: ) def get_items(self, parent: Union[str, int], start_index: int = 0, limit: Optional[int] = -1) \ - -> Optional[Generator]: + -> Generator[MediaServerItem | None, Any, None]: """ 获取媒体服务器项目列表,支持分页和不分页逻辑,默认不分页获取所有数据 @@ -855,7 +856,7 @@ class Plex: :param kwargs: 其他请求参数,如headers, cookies, proxies等 """ if not self._session: - return + return None try: url = UrlUtils.adapt_request_url(host=self._host, endpoint=endpoint) kwargs.setdefault("headers", self.__get_request_headers()) diff --git a/app/schemas/context.py b/app/schemas/context.py index fb103711..5f98ceae 100644 --- a/app/schemas/context.py +++ b/app/schemas/context.py @@ -1,4 +1,4 @@ -from typing import Optional, Dict, List, Union +from typing import Optional, Dict, List, Union, Any from pydantic import BaseModel, Field @@ -235,9 +235,9 @@ class Context(BaseModel): 上下文 """ # 元数据 - meta_info: Optional[MetaInfo] = None + meta_info: Optional[Union[MetaInfo, Any]] = None # 媒体信息 - media_info: Optional[MediaInfo] = None + media_info: Optional[Union[MediaInfo, Any]] = None # 种子信息 torrent_info: Optional[TorrentInfo] = None diff --git a/app/schemas/workflow.py b/app/schemas/workflow.py index 3b695e0e..854d4d9e 100644 --- a/app/schemas/workflow.py +++ b/app/schemas/workflow.py @@ -42,43 +42,43 @@ class Action(BaseModel): """ 动作信息 """ - id: Optional[str] = Field(None, description="动作ID") - type: Optional[str] = Field(None, description="动作类型 (类名)") - name: Optional[str] = Field(None, description="动作名称") - description: Optional[str] = Field(None, description="动作描述") - position: Optional[dict] = Field({}, description="位置") - data: Optional[dict] = Field({}, description="参数") + id: Optional[str] = Field(default=None, description="动作ID") + type: Optional[str] = Field(default=None, description="动作类型 (类名)") + name: Optional[str] = Field(default=None, description="动作名称") + description: Optional[str] = Field(default=None, description="动作描述") + position: Optional[dict] = Field(default={}, description="位置") + data: Optional[dict] = Field(default={}, description="参数") class ActionExecution(BaseModel): """ 动作执行情况 """ - action: Optional[str] = Field(None, description="当前动作(名称)") - result: Optional[bool] = Field(None, description="执行结果") - message: Optional[str] = Field(None, description="执行消息") + action: Optional[str] = Field(default=None, description="当前动作(名称)") + result: Optional[bool] = Field(default=None, description="执行结果") + message: Optional[str] = Field(default=None, description="执行消息") class ActionContext(BaseModel): """ 动作基础上下文,各动作通用数据 """ - content: Optional[str] = Field(None, description="文本类内容") - torrents: Optional[List[Context]] = Field([], description="资源列表") - medias: Optional[List[MediaInfo]] = Field([], description="媒体列表") - fileitems: Optional[List[FileItem]] = Field([], description="文件列表") - downloads: Optional[List[DownloadTask]] = Field([], description="下载任务列表") - sites: Optional[List[Site]] = Field([], description="站点列表") - subscribes: Optional[List[Subscribe]] = Field([], description="订阅列表") - execute_history: Optional[List[ActionExecution]] = Field([], description="执行历史") - progress: Optional[int] = Field(0, description="执行进度(%)") + content: Optional[str] = Field(default=None, description="文本类内容") + torrents: Optional[List[Context]] = Field(default=[], description="资源列表") + medias: Optional[List[MediaInfo]] = Field(default=[], description="媒体列表") + fileitems: Optional[List[FileItem]] = Field(default=[], description="文件列表") + downloads: Optional[List[DownloadTask]] = Field(default=[], description="下载任务列表") + sites: Optional[List[Site]] = Field(default=[], description="站点列表") + subscribes: Optional[List[Subscribe]] = Field(default=[], description="订阅列表") + execute_history: Optional[List[ActionExecution]] = Field(default=[], description="执行历史") + progress: Optional[int] = Field(default=0, description="执行进度(%)") class ActionFlow(BaseModel): """ 工作流流程 """ - id: Optional[str] = Field(None, description="流程ID") - source: Optional[str] = Field(None, description="源动作") - target: Optional[str] = Field(None, description="目标动作") - animated: Optional[bool] = Field(True, description="是否动画流程") + id: Optional[str] = Field(default=None, description="流程ID") + source: Optional[str] = Field(default=None, description="源动作") + target: Optional[str] = Field(default=None, description="目标动作") + animated: Optional[bool] = Field(default=True, description="是否动画流程")