fix: clean typing issues and refresh runtime dependencies

Align endpoint/module type hints and config reload handling while updating base Python image and package pins to improve build/runtime compatibility.

Made-with: Cursor
This commit is contained in:
DDSRem
2026-03-24 19:21:04 +08:00
parent aae50004b1
commit 517300afe9
14 changed files with 34 additions and 33 deletions
+1 -3
View File
@@ -5,15 +5,13 @@ from fastapi import APIRouter, Depends, Body
from app import schemas from app import schemas
from app.chain.download import DownloadChain from app.chain.download import DownloadChain
from app.chain.media import MediaChain from app.chain.media import MediaChain
from app.core.config import settings
from app.core.context import MediaInfo, Context, TorrentInfo from app.core.context import MediaInfo, Context, TorrentInfo
from app.core.event import eventmanager
from app.core.metainfo import MetaInfo from app.core.metainfo import MetaInfo
from app.core.security import verify_token from app.core.security import verify_token
from app.db.models.user import User from app.db.models.user import User
from app.db.systemconfig_oper import SystemConfigOper from app.db.systemconfig_oper import SystemConfigOper
from app.db.user_oper import get_current_active_user from app.db.user_oper import get_current_active_user
from app.schemas.types import ChainEventType, SystemConfigKey from app.schemas.types import SystemConfigKey
router = APIRouter() router = APIRouter()
-1
View File
@@ -12,7 +12,6 @@ from app.db.models.user import User
from app.db.user_oper import get_current_active_superuser_async, \ from app.db.user_oper import get_current_active_superuser_async, \
get_current_active_user_async, get_current_active_user get_current_active_user_async, get_current_active_user
from app.db.userconfig_oper import UserConfigOper from app.db.userconfig_oper import UserConfigOper
from app.utils.otp import OtpUtils
router = APIRouter() router = APIRouter()
-1
View File
@@ -253,7 +253,6 @@ class MediaChain(ChainBase, ConfigReloadMixin, metaclass=Singleton):
""" """
流式下载图片并保存到文件 流式下载图片并保存到文件
:param storagechain: StorageChain实例
:param fileitem: 关联的媒体文件项 :param fileitem: 关联的媒体文件项
:param path: 图片文件路径 :param path: 图片文件路径
:param url: 图片下载URL :param url: 图片下载URL
-1
View File
@@ -22,7 +22,6 @@ from webauthn.helpers.structs import (
PublicKeyCredentialDescriptor, PublicKeyCredentialDescriptor,
AuthenticatorTransport, AuthenticatorTransport,
UserVerificationRequirement, UserVerificationRequirement,
AuthenticatorAttachment,
ResidentKeyRequirement, ResidentKeyRequirement,
AuthenticatorSelectionCriteria AuthenticatorSelectionCriteria
) )
+1 -1
View File
@@ -714,7 +714,7 @@ class Emby:
logger.error(f"连接Users/Items出错:" + str(e)) logger.error(f"连接Users/Items出错:" + str(e))
return None return None
def get_webhook_message(self, form: any, args: dict) -> Optional[schemas.WebhookEventInfo]: def get_webhook_message(self, form: Any, args: dict) -> Optional[schemas.WebhookEventInfo]:
""" """
解析Emby Webhook报文 解析Emby Webhook报文
电影: 电影:
+1 -1
View File
@@ -111,7 +111,7 @@ class BitptSiteUserInfo(SiteParserBase):
def _parse_message_content(self, html_text) -> Tuple[Optional[str], Optional[str], Optional[str]]: def _parse_message_content(self, html_text) -> Tuple[Optional[str], Optional[str], Optional[str]]:
pass pass
def _parse_user_torrent_seeding_info(self, html_text: str): def _parse_user_torrent_seeding_info(self, html_text: str, **kwargs):
pass pass
def parse(self): def parse(self):
+1 -1
View File
@@ -117,7 +117,7 @@ class ZhixingSiteUserInfo(SiteParserBase):
def _parse_message_content(self, html_text) -> Tuple[Optional[str], Optional[str], Optional[str]]: def _parse_message_content(self, html_text) -> Tuple[Optional[str], Optional[str], Optional[str]]:
pass pass
def _parse_user_torrent_seeding_info(self, html_text: str): def _parse_user_torrent_seeding_info(self, html_text: str, multi_page: bool = False):
""" """
占位,避免抽象类报错 占位,避免抽象类报错
""" """
+1 -1
View File
@@ -569,7 +569,7 @@ class Jellyfin:
logger.error(f"连接Library/Refresh出错:" + str(e)) logger.error(f"连接Library/Refresh出错:" + str(e))
return False return False
def get_webhook_message(self, body: any) -> Optional[schemas.WebhookEventInfo]: def get_webhook_message(self, body: Any) -> Optional[schemas.WebhookEventInfo]:
""" """
解析Jellyfin报文 解析Jellyfin报文
{ {
+1 -1
View File
@@ -549,7 +549,7 @@ class Plex:
logger.error(f"获取媒体库列表出错:{str(err)}") logger.error(f"获取媒体库列表出错:{str(err)}")
return None return None
def get_webhook_message(self, form: any) -> Optional[schemas.WebhookEventInfo]: def get_webhook_message(self, form: Any) -> Optional[schemas.WebhookEventInfo]:
""" """
解析Plex报文 解析Plex报文
eventItem 字段的含义 eventItem 字段的含义
+1 -1
View File
@@ -409,7 +409,7 @@ class TrimeMedia:
return lib return lib
return None return None
def get_webhook_message(self, body: any) -> Optional[schemas.WebhookEventInfo]: def get_webhook_message(self, body: Any) -> Optional[schemas.WebhookEventInfo]:
pass pass
def get_iteminfo(self, itemid: str) -> Optional[schemas.MediaServerItem]: def get_iteminfo(self, itemid: str) -> Optional[schemas.MediaServerItem]:
+18 -8
View File
@@ -17,34 +17,44 @@ class ConfigReloadMixin:
def __init_subclass__(cls, **kwargs): def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs) super().__init_subclass__(**kwargs)
config_watch = getattr(cls, 'CONFIG_WATCH', None) config_watch = getattr(cls, "CONFIG_WATCH", None)
if not config_watch: if not config_watch:
return return
# 检查 on_config_changed 方法是否为异步 # 检查 on_config_changed 方法是否为异步
is_async = inspect.iscoroutinefunction(cls.on_config_changed) is_async = inspect.iscoroutinefunction(cls.on_config_changed)
method_name = 'handle_config_changed' method_name = "handle_config_changed"
# 创建事件处理函数 # 创建事件处理函数
def create_handler(is_async): def create_handler(is_async):
if is_async: if is_async:
async def wrapper(self: ConfigReloadMixin, event: Event): async def wrapper(self: ConfigReloadMixin, event: Event):
if not event: if not event:
return return
changed_keys = getattr(event.event_data, "key", set()) & config_watch changed_keys = (
getattr(event.event_data, "key", set()) & config_watch
)
if not changed_keys: if not changed_keys:
return return
logger.info(f"配置 {', '.join(changed_keys)} 变更,重载 {self.get_reload_name()}...") logger.info(
await self.on_config_changed() f"配置 {', '.join(changed_keys)} 变更,重载 {self.get_reload_name()}..."
)
self.on_config_changed()
else: else:
def wrapper(self: ConfigReloadMixin, event: Event): def wrapper(self: ConfigReloadMixin, event: Event):
if not event: if not event:
return return
changed_keys = getattr(event.event_data, "key", set()) & config_watch changed_keys = (
getattr(event.event_data, "key", set()) & config_watch
)
if not changed_keys: if not changed_keys:
return return
logger.info(f"配置 {', '.join(changed_keys)} 变更,重载 {self.get_reload_name()}...") logger.info(
f"配置 {', '.join(changed_keys)} 变更,重载 {self.get_reload_name()}..."
)
self.on_config_changed() self.on_config_changed()
return wrapper return wrapper
@@ -52,7 +62,7 @@ class ConfigReloadMixin:
# 创建并设置处理函数 # 创建并设置处理函数
handler = create_handler(is_async) handler = create_handler(is_async)
handler.__module__ = cls.__module__ handler.__module__ = cls.__module__
handler.__qualname__ = f'{cls.__name__}.{method_name}' handler.__qualname__ = f"{cls.__name__}.{method_name}"
setattr(cls, method_name, handler) setattr(cls, method_name, handler)
# 添加为事件处理器 # 添加为事件处理器
eventmanager.add_event_listener(EventType.ConfigChanged, handler) eventmanager.add_event_listener(EventType.ConfigChanged, handler)
+3 -1
View File
@@ -1,9 +1,11 @@
from typing import Tuple
import pyotp import pyotp
class OtpUtils: class OtpUtils:
@staticmethod @staticmethod
def generate_secret_key(username: str) -> (str, str): def generate_secret_key(username: str) -> Tuple[str, str]:
try: try:
secret = pyotp.random_base32() secret = pyotp.random_base32()
uri = pyotp.totp.TOTP(secret).provisioning_uri(name='MoviePilot', uri = pyotp.totp.TOTP(secret).provisioning_uri(name='MoviePilot',
+2 -2
View File
@@ -1,4 +1,4 @@
FROM python:3.12.8-slim-bookworm AS base FROM python:3.12.13-slim-bookworm AS base
# 准备软件包 # 准备软件包
@@ -68,7 +68,7 @@ WORKDIR /app
COPY requirements.in requirements.in COPY requirements.in requirements.in
RUN python3 -m venv ${VENV_PATH} \ RUN python3 -m venv ${VENV_PATH} \
&& pip install --upgrade "pip<25.0" \ && pip install --upgrade "pip<25.0" \
&& pip install "Cython" "pip-tools<7.5" \ && pip install "Cython~=3.1.2" "pip-tools<7.5" \
&& pip-compile requirements.in \ && pip-compile requirements.in \
&& pip install -r requirements.txt && pip install -r requirements.txt
+4 -10
View File
@@ -10,6 +10,7 @@ python-multipart~=0.0.9
aiofiles~=24.1.0 aiofiles~=24.1.0
aioshutil~=1.5 aioshutil~=1.5
alembic~=1.16.2 alembic~=1.16.2
anyio~=4.10.0
bcrypt~=4.0.1 bcrypt~=4.0.1
regex~=2024.11.6 regex~=2024.11.6
cn2an~=0.5.19 cn2an~=0.5.19
@@ -22,6 +23,7 @@ urllib3~=2.5.0
lxml~=6.0.0 lxml~=6.0.0
pyquery~=2.0.1 pyquery~=2.0.1
ruamel.yaml~=0.18.14 ruamel.yaml~=0.18.14
PyYAML~=6.0.2
APScheduler~=3.11.0 APScheduler~=3.11.0
cryptography~=45.0.4 cryptography~=45.0.4
pytz~=2025.2 pytz~=2025.2
@@ -31,10 +33,8 @@ plexapi~=4.17.0
transmission-rpc~=4.3.0 transmission-rpc~=4.3.0
Jinja2~=3.1.6 Jinja2~=3.1.6
pyparsing~=3.2.3 pyparsing~=3.2.3
func_timeout==4.3.5
bs4~=0.0.2
beautifulsoup4~=4.13.4 beautifulsoup4~=4.13.4
pillow~=11.2.1 pillow~=12.1.1
pillow-avif-plugin~=1.5.2 pillow-avif-plugin~=1.5.2
pyTelegramBotAPI~=4.27.0 pyTelegramBotAPI~=4.27.0
telegramify-markdown~=0.5.2 telegramify-markdown~=0.5.2
@@ -49,24 +49,18 @@ starlette~=0.46.2
PyVirtualDisplay~=3.0 PyVirtualDisplay~=3.0
psutil~=7.0.0 psutil~=7.0.0
python-dotenv~=1.1.1 python-dotenv~=1.1.1
python-hosts~=1.1.2
watchdog~=6.0.0 watchdog~=6.0.0
watchfiles~=1.1.0 watchfiles~=1.1.0
cacheout~=0.16.0
click~=8.2.1 click~=8.2.1
requests-cache~=1.2.1
parse~=1.20.2 parse~=1.20.2
docker~=7.1.0 docker~=7.1.0
pywin32==310; platform_system == "Windows" pywin32==310; platform_system == "Windows"
cachetools~=6.1.0 cachetools~=6.1.0
fast-bencode~=1.1.7
pystray~=0.19.5 pystray~=0.19.5
pyotp~=2.9.0 pyotp~=2.9.0
webauthn~=2.7.0 webauthn~=2.7.0
Pinyin2Hanzi~=0.1.1 Pinyin2Hanzi~=0.1.1
pywebpush~=2.0.3 pywebpush~=2.0.3
aiopathlib~=0.6.0
asynctempfile~=0.5.0
aiosqlite~=0.21.0 aiosqlite~=0.21.0
psycopg2-binary~=2.9.10 psycopg2-binary~=2.9.10
asyncpg~=0.30.0 asyncpg~=0.30.0
@@ -88,8 +82,8 @@ langchain-community~=0.4.1
langchain-openai~=1.1.11 langchain-openai~=1.1.11
langchain-google-genai~=4.2.1 langchain-google-genai~=4.2.1
langchain-deepseek~=1.0.1 langchain-deepseek~=1.0.1
langchain-experimental~=0.4.1
langgraph~=1.1.3 langgraph~=1.1.3
openai~=2.29.0 openai~=2.29.0
google-genai~=1.68.0
ddgs~=9.10.0 ddgs~=9.10.0
websocket-client~=1.8.0 websocket-client~=1.8.0