diff --git a/backend/src/dev_server.py b/backend/src/dev_server.py deleted file mode 100644 index dda12bbd..00000000 --- a/backend/src/dev_server.py +++ /dev/null @@ -1,47 +0,0 @@ -"""Minimal dev server that skips downloader check for UI testing.""" -import uvicorn -from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware -from fastapi import APIRouter - -from module.database.combine import Database -from module.database.engine import engine - -# Initialize DB + migrations + default user -with Database(engine) as db: - db.create_table() - db.user.add_default_user() - -# Build v1 router without program router (which blocks on downloader check) -from module.api.auth import router as auth_router -from module.api.bangumi import router as bangumi_router -from module.api.config import router as config_router -from module.api.log import router as log_router -from module.api.rss import router as rss_router -from module.api.search import router as search_router - -v1 = APIRouter(prefix="/v1") -v1.include_router(auth_router) -v1.include_router(bangumi_router) -v1.include_router(config_router) -v1.include_router(log_router) -v1.include_router(rss_router) -v1.include_router(search_router) - -# Stub status endpoint (real one lives in program router which blocks on downloader) -@v1.get("/status") -async def stub_status(): - return {"status": True, "version": "dev"} - -app = FastAPI() -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) -app.include_router(v1, prefix="/api") - -if __name__ == "__main__": - uvicorn.run(app, host="127.0.0.1", port=7892) diff --git a/backend/src/module/ab_decorator/__init__.py b/backend/src/module/ab_decorator/__init__.py index 3897a8e9..671e8029 100644 --- a/backend/src/module/ab_decorator/__init__.py +++ b/backend/src/module/ab_decorator/__init__.py @@ -4,8 +4,6 @@ import logging import httpx -from .timeout import timeout - logger = logging.getLogger(__name__) _lock = asyncio.Lock() diff --git a/backend/src/module/ab_decorator/timeout.py b/backend/src/module/ab_decorator/timeout.py deleted file mode 100644 index 32f3a985..00000000 --- a/backend/src/module/ab_decorator/timeout.py +++ /dev/null @@ -1,23 +0,0 @@ -import signal - - -def timeout(seconds): - def decorator(func): - def handler(signum, frame): - raise TimeoutError("Function timed out.") - - def wrapper(*args, **kwargs): - # 设置信号处理程序,当超时时触发TimeoutError异常 - signal.signal(signal.SIGALRM, handler) - signal.alarm(seconds) # 设置alarm定时器 - - try: - result = func(*args, **kwargs) - finally: - signal.alarm(0) # 取消alarm定时器 - - return result - - return wrapper - - return decorator diff --git a/backend/src/module/core/status.py b/backend/src/module/core/status.py index 442b8eea..7df2a2d9 100644 --- a/backend/src/module/core/status.py +++ b/backend/src/module/core/status.py @@ -1,4 +1,3 @@ -import asyncio import time from module.checker import Checker @@ -10,13 +9,9 @@ DOWNLOADER_STATUS_TTL = 60 class ProgramStatus(Checker): def __init__(self): super().__init__() - self.stop_event = asyncio.Event() - self.lock = asyncio.Lock() self._downloader_status = False self._downloader_last_check: float = 0 - self._torrents_status = False self._tasks_started = False - self.event = asyncio.Event() @property def is_running(self): diff --git a/backend/src/module/downloader/client/tr_downloader.py b/backend/src/module/downloader/client/tr_downloader.py deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/src/module/downloader/exceptions.py b/backend/src/module/downloader/exceptions.py deleted file mode 100644 index 7ec28c73..00000000 --- a/backend/src/module/downloader/exceptions.py +++ /dev/null @@ -1,2 +0,0 @@ -class ConflictError(Exception): - pass diff --git a/backend/src/module/notification/__init__.py b/backend/src/module/notification/__init__.py index b8d1b55c..23aa4937 100644 --- a/backend/src/module/notification/__init__.py +++ b/backend/src/module/notification/__init__.py @@ -1,10 +1,8 @@ -from .notification import PostNotification -from .manager import NotificationManager from .base import NotificationProvider +from .manager import NotificationManager from .providers import PROVIDER_REGISTRY __all__ = [ - "PostNotification", "NotificationManager", "NotificationProvider", "PROVIDER_REGISTRY", diff --git a/backend/src/module/notification/notification.py b/backend/src/module/notification/notification.py deleted file mode 100644 index 7ddef43b..00000000 --- a/backend/src/module/notification/notification.py +++ /dev/null @@ -1,58 +0,0 @@ -import asyncio -import logging - -from module.conf import settings -from module.database import Database -from module.models import Notification - -from .plugin import ( - BarkNotification, - ServerChanNotification, - TelegramNotification, - WecomNotification, -) - -logger = logging.getLogger(__name__) - - -def getClient(type: str): - if type.lower() == "telegram": - return TelegramNotification - elif type.lower() == "server-chan": - return ServerChanNotification - elif type.lower() == "bark": - return BarkNotification - elif type.lower() == "wecom": - return WecomNotification - else: - return None - - -class PostNotification: - def __init__(self): - Notifier = getClient(settings.notification.type) - self.notifier = Notifier( - token=settings.notification.token, chat_id=settings.notification.chat_id - ) - - @staticmethod - def _get_poster_sync(notify: Notification): - with Database() as db: - poster_path = db.bangumi.match_poster(notify.official_title) - notify.poster_path = poster_path - - async def send_msg(self, notify: Notification) -> bool: - await asyncio.to_thread(self._get_poster_sync, notify) - try: - await self.notifier.post_msg(notify) - logger.debug("Send notification: %s", notify.official_title) - except Exception as e: - logger.warning(f"Failed to send notification: {e}") - return False - - async def __aenter__(self): - await self.notifier.__aenter__() - return self - - async def __aexit__(self, exc_type, exc_val, exc_tb): - await self.notifier.__aexit__(exc_type, exc_val, exc_tb) diff --git a/backend/src/module/notification/plugin/__init__.py b/backend/src/module/notification/plugin/__init__.py deleted file mode 100644 index e9acda8f..00000000 --- a/backend/src/module/notification/plugin/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .bark import BarkNotification -from .server_chan import ServerChanNotification -from .telegram import TelegramNotification -from .wecom import WecomNotification diff --git a/backend/src/module/notification/plugin/bark.py b/backend/src/module/notification/plugin/bark.py deleted file mode 100644 index dc905f52..00000000 --- a/backend/src/module/notification/plugin/bark.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging - -from module.models import Notification -from module.network import RequestContent - -logger = logging.getLogger(__name__) - - -class BarkNotification(RequestContent): - def __init__(self, token, **kwargs): - super().__init__() - self.token = token - self.notification_url = "https://api.day.app/push" - - @staticmethod - def gen_message(notify: Notification) -> str: - text = f""" - 番剧名称:{notify.official_title}\n季度: 第{notify.season}季\n更新集数: 第{notify.episode}集\n{notify.poster_path}\n - """ - return text.strip() - - async def post_msg(self, notify: Notification) -> bool: - text = self.gen_message(notify) - data = {"title": notify.official_title, "body": text, "icon": notify.poster_path, "device_key": self.token} - resp = await self.post_data(self.notification_url, data) - logger.debug("Bark notification: %s", resp.status_code) - return resp.status_code == 200 diff --git a/backend/src/module/notification/plugin/server_chan.py b/backend/src/module/notification/plugin/server_chan.py deleted file mode 100644 index 3a84a081..00000000 --- a/backend/src/module/notification/plugin/server_chan.py +++ /dev/null @@ -1,31 +0,0 @@ -import logging - -from module.models import Notification -from module.network import RequestContent - -logger = logging.getLogger(__name__) - - -class ServerChanNotification(RequestContent): - """Server酱推送""" - - def __init__(self, token, **kwargs): - super().__init__() - self.notification_url = f"https://sctapi.ftqq.com/{token}.send" - - @staticmethod - def gen_message(notify: Notification) -> str: - text = f""" - 番剧名称:{notify.official_title}\n季度: 第{notify.season}季\n更新集数: 第{notify.episode}集\n{notify.poster_path}\n - """ - return text.strip() - - async def post_msg(self, notify: Notification) -> bool: - text = self.gen_message(notify) - data = { - "title": notify.official_title, - "desp": text, - } - resp = await self.post_data(self.notification_url, data) - logger.debug("ServerChan notification: %s", resp.status_code) - return resp.status_code == 200 diff --git a/backend/src/module/notification/plugin/slack.py b/backend/src/module/notification/plugin/slack.py deleted file mode 100644 index 7aa87511..00000000 --- a/backend/src/module/notification/plugin/slack.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging - -from module.models import Notification -from module.network import RequestContent - -logger = logging.getLogger(__name__) - - -class SlackNotification(RequestContent): - def __init__(self, token, **kwargs): - super().__init__() - self.token = token - self.notification_url = "https://api.day.app/push" - - @staticmethod - def gen_message(notify: Notification) -> str: - text = f""" - 番剧名称:{notify.official_title}\n季度: 第{notify.season}季\n更新集数: 第{notify.episode}集\n{notify.poster_path}\n - """ - return text.strip() - - async def post_msg(self, notify: Notification) -> bool: - text = self.gen_message(notify) - data = {"title": notify.official_title, "body": text, "device_key": self.token} - resp = await self.post_data(self.notification_url, data) - logger.debug("Bark notification: %s", resp.status_code) - return resp.status_code == 200 diff --git a/backend/src/module/notification/plugin/telegram.py b/backend/src/module/notification/plugin/telegram.py deleted file mode 100644 index 561f22b1..00000000 --- a/backend/src/module/notification/plugin/telegram.py +++ /dev/null @@ -1,38 +0,0 @@ -import logging - -from module.models import Notification -from module.network import RequestContent -from module.utils import load_image - -logger = logging.getLogger(__name__) - - -class TelegramNotification(RequestContent): - def __init__(self, token, chat_id): - super().__init__() - self.photo_url = f"https://api.telegram.org/bot{token}/sendPhoto" - self.message_url = f"https://api.telegram.org/bot{token}/sendMessage" - self.chat_id = chat_id - - @staticmethod - def gen_message(notify: Notification) -> str: - text = f""" - 番剧名称:{notify.official_title}\n季度: 第{notify.season}季\n更新集数: 第{notify.episode}集 - """ - return text.strip() - - async def post_msg(self, notify: Notification) -> bool: - text = self.gen_message(notify) - data = { - "chat_id": self.chat_id, - "caption": text, - "text": text, - "disable_notification": True, - } - photo = load_image(notify.poster_path) - if photo: - resp = await self.post_files(self.photo_url, data, files={"photo": photo}) - else: - resp = await self.post_data(self.message_url, data) - logger.debug("Telegram notification: %s", resp.status_code) - return resp.status_code == 200 diff --git a/backend/src/module/notification/plugin/wecom.py b/backend/src/module/notification/plugin/wecom.py deleted file mode 100644 index ed1564b5..00000000 --- a/backend/src/module/notification/plugin/wecom.py +++ /dev/null @@ -1,42 +0,0 @@ -import logging - -from module.models import Notification -from module.network import RequestContent - -logger = logging.getLogger(__name__) - - -class WecomNotification(RequestContent): - """企业微信推送 基于图文消息""" - - def __init__(self, token, chat_id, **kwargs): - super().__init__() - # Chat_id is used as noti_url in this push tunnel - self.notification_url = f"{chat_id}" - self.token = token - - @staticmethod - def gen_message(notify: Notification) -> str: - text = f""" - 番剧名称:{notify.official_title}\n季度: 第{notify.season}季\n更新集数: 第{notify.episode}集\n{notify.poster_path}\n - """ - return text.strip() - - async def post_msg(self, notify: Notification) -> bool: - ##Change message format to match Wecom push better - title = "【番剧更新】" + notify.official_title - msg = self.gen_message(notify) - picurl = notify.poster_path - # Default pic to avoid blank in message. Resolution:1068*455 - if picurl == "https://mikanani.me": - picurl = "https://article.biliimg.com/bfs/article/d8bcd0408bf32594fd82f27de7d2c685829d1b2e.png" - data = { - "key": self.token, - "type": "news", - "title": title, - "msg": msg, - "picurl": picurl, - } - resp = await self.post_data(self.notification_url, data) - logger.debug("Wecom notification: %s", resp.status_code) - return resp.status_code == 200 diff --git a/backend/src/module/parser/analyser/bgm_parser.py b/backend/src/module/parser/analyser/bgm_parser.py deleted file mode 100644 index 4ab96eb3..00000000 --- a/backend/src/module/parser/analyser/bgm_parser.py +++ /dev/null @@ -1,15 +0,0 @@ -from module.network import RequestContent - - -def search_url(e): - return f"https://api.bgm.tv/search/subject/{e}?responseGroup=large" - - -async def bgm_parser(title): - url = search_url(title) - async with RequestContent() as req: - contents = await req.get_json(url) - if contents: - return contents[0] - else: - return None diff --git a/backend/src/test/test_rss_engine.py b/backend/src/test/test_rss_engine.py deleted file mode 100644 index f8dbf796..00000000 --- a/backend/src/test/test_rss_engine.py +++ /dev/null @@ -1,17 +0,0 @@ -import pytest - -# Skip the entire module as it requires network access and complex setup -pytestmark = pytest.mark.skip(reason="RSS engine tests require network access and complex async setup") - - -@pytest.mark.asyncio -async def test_rss_engine(): - """ - This test requires: - 1. Network access to mikanani.me - 2. A properly configured async database - 3. The RSS feed to be available - - To run this test, you need to set up a proper test environment. - """ - pass diff --git a/backend/src/test_passkey_server.py b/backend/src/test_passkey_server.py deleted file mode 100644 index 83a4080e..00000000 --- a/backend/src/test_passkey_server.py +++ /dev/null @@ -1,35 +0,0 @@ -""" -Minimal test server for passkey development. -Uses the real auth and passkey API routes without the downloader check. -Run with: uv run python test_passkey_server.py -""" -import uvicorn -from fastapi import FastAPI - -from module.api.auth import router as auth_router -from module.api.passkey import router as passkey_router -from module.database import Database -from module.update.startup import first_run - -app = FastAPI(title="AutoBangumi Passkey Test") - -# Mount real routers -app.include_router(auth_router, prefix="/api/v1") -app.include_router(passkey_router, prefix="/api/v1") - - -@app.on_event("startup") -async def startup(): - """Create tables and default user (no downloader check)""" - with Database() as db: - db.create_table() - db.user.add_default_user() - - -@app.get("/") -def index(): - return {"status": "Passkey test server running"} - - -if __name__ == "__main__": - uvicorn.run(app, host="0.0.0.0", port=7892)