feat: 增加视频清单搜索,并在开发期缓存剧目清单

This commit is contained in:
Nixevol
2026-09-27 04:21:00 +08:00
parent cc930cd1ed
commit f010426f94
7 changed files with 268 additions and 21 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ async def load_catalog(body: SessionBody):
async def generate():
try:
async for event in catalog.stream(token, raw_device):
async for event in catalog.stream(token, raw_device, body.refresh):
yield json.dumps(event, ensure_ascii=False) + "\n"
except Exception as exc:
message = str(exc) or "获取清单失败"
+1
View File
@@ -34,6 +34,7 @@ class AdbBody(BaseModel):
class SessionBody(BaseModel):
token: str
device: DeviceBody | None = None
refresh: bool = False
class ProxyBody(BaseModel):
+31 -4
View File
@@ -4,6 +4,7 @@ from server.services.covers import covers
from server.services.device import DeviceProfile
from server.services.http import http
from server.services.rapt import API_BASE
from server.services import catalog_cache
from server.services.sessions import sessions
CLASSIFY_MAX_PAGES = 50
@@ -39,7 +40,27 @@ class CatalogService:
raise RuntimeError("没有获取到分类")
return rows
async def stream(self, token: str, raw_device: dict | None):
async def stream(self, token: str, raw_device: dict | None, refresh: bool = False):
if catalog_cache.enabled():
if refresh:
catalog_cache.clear()
else:
cached = catalog_cache.read()
if cached:
for event in cached:
yield event
return
collected: list[dict] = []
failed = False
async for event in self._fetch(token, raw_device):
if event.get("failed"):
failed = True
collected.append(event)
yield event
if catalog_cache.enabled() and not failed and collected and collected[-1].get("type") == "done":
catalog_cache.write(collected)
async def _fetch(self, token: str, raw_device: dict | None):
token = token.strip()
if not token:
raise RuntimeError("请先登录")
@@ -79,7 +100,10 @@ class CatalogService:
for event in self._iter_paged(key, name, token, device, path, method, body, max_pages, note):
loop.call_soon_threadsafe(queue.put_nowait, event)
except Exception as exc:
loop.call_soon_threadsafe(queue.put_nowait, self._event(key, name, [], str(exc) or "获取清单失败", True))
loop.call_soon_threadsafe(
queue.put_nowait,
self._event(key, name, [], str(exc) or "获取清单失败", done=True, failed=True),
)
finally:
loop.call_soon_threadsafe(queue.put_nowait, None)
@@ -101,8 +125,11 @@ class CatalogService:
task.cancel()
await asyncio.gather(*tasks, return_exceptions=True)
def _event(self, key: str, name: str, items: list[dict], note: str = "", done: bool = False) -> dict:
return {"type": "page", "key": key, "name": name, "note": note, "items": items, "done": done}
def _event(self, key: str, name: str, items: list[dict], note: str = "", done: bool = False, failed: bool = False) -> dict:
event = {"type": "page", "key": key, "name": name, "note": note, "items": items, "done": done}
if failed:
event["failed"] = True
return event
def _drama(self, item: dict) -> dict | None:
vid = item.get("id")
+41
View File
@@ -0,0 +1,41 @@
"""开发期剧目清单缓存。
只缓存分类和剧目清单,不缓存封面。封面仍由 cache/covers 负责。
手动刷新会删掉这份缓存并重新请求。
正式打包时 Tauri 会把 RAPTD_CATALOG_CACHE 设为 0,从而关闭这里。
"""
import json
import os
from pathlib import Path
PATH = Path(__file__).resolve().parents[2] / "cache" / "catalog.json"
def enabled() -> bool:
flag = os.environ.get("RAPTD_CATALOG_CACHE", "1").strip().lower()
return flag not in ("0", "false", "no")
def read() -> list[dict] | None:
if not PATH.is_file():
return None
try:
data = json.loads(PATH.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError):
return None
events = data.get("events") if isinstance(data, dict) else None
if not isinstance(events, list) or not events:
return None
return events
def write(events: list[dict]) -> None:
PATH.parent.mkdir(parents=True, exist_ok=True)
temporary = PATH.with_suffix(".json.tmp")
temporary.write_text(json.dumps({"events": events}, ensure_ascii=False), encoding="utf-8")
temporary.replace(PATH)
def clear() -> None:
PATH.unlink(missing_ok=True)