feat: 增加视频清单搜索,并在开发期缓存剧目清单
This commit is contained in:
@@ -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 "获取清单失败"
|
||||
|
||||
@@ -34,6 +34,7 @@ class AdbBody(BaseModel):
|
||||
class SessionBody(BaseModel):
|
||||
token: str
|
||||
device: DeviceBody | None = None
|
||||
refresh: bool = False
|
||||
|
||||
|
||||
class ProxyBody(BaseModel):
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user