This commit is contained in:
lz_db
2026-10-25 11:46:44 +08:00
parent 893dfed0ed
commit 494f667719
63 changed files with 5213 additions and 239 deletions
+35 -2
View File
@@ -14,6 +14,7 @@ logger = logging.getLogger(__name__)
# 内容源配置(域名轮换时只需改这里)
SOURCE_KEY = "mdcmai4"
SOURCE_CODE = "madou" # sources 表中的 code,用于给导入内容打「来源」标
UPSTREAM = "https://mdcmai4.xyz"
PRODUCT_ID = 1
CATEGORY_ID = 12 # 本站分类:AI短剧
@@ -37,6 +38,21 @@ def _proxy_url(path: str) -> str:
return f"{SELF_API_BASE}/api/v1/m3u8/proxy?path={quote(path, safe='')}"
async def default_source_id(db: AsyncSession) -> int:
"""麻豆等内容源的来源 id(sources.code 匹配;缺失时取第一个启用来源)"""
from app.models.video import Source
sid = (
await db.execute(select(Source.id).where(Source.code == SOURCE_CODE))
).scalar_one_or_none()
if sid:
return sid
sid = (
await db.execute(select(Source.id).where(Source.status == 1).order_by(Source.sort, Source.id))
).scalars().first()
return sid or 0
async def _import_drama(client: httpx.AsyncClient, db: AsyncSession, drama_id: int) -> tuple[int, int]:
"""导入单部短剧,返回 (新增集数, 跳过集数)"""
detail = await _fetch_json(client, f"{UPSTREAM}/api/v1/short-dramas/{drama_id}", {"productId": PRODUCT_ID})
@@ -63,6 +79,7 @@ async def _import_drama(client: httpx.AsyncClient, db: AsyncSession, drama_id: i
description=(detail.get("description") or "")[:500],
author_id=AUTHOR_USER_ID,
category_id=CATEGORY_ID,
source_id=await default_source_id(db),
total_episodes=0,
view_count=int(detail.get("heatCount") or 0),
status=1,
@@ -94,6 +111,7 @@ async def _import_drama(client: httpx.AsyncClient, db: AsyncSession, drama_id: i
Video(
user_id=AUTHOR_USER_ID,
category_id=CATEGORY_ID,
source_id=series.source_id,
series_id=series.id,
episode_no=ep_no,
title=(ep.get("titleOverride") or ep.get("title") or f"第{ep_no}集")[:100],
@@ -209,6 +227,7 @@ async def _import_video_item(db: AsyncSession, source: CollectionSource, item: d
v = Video(
user_id=AUTHOR_USER_ID,
category_id=source.category_id,
source_id=source.source_id or await default_source_id(db),
title=(item.get("title") or f"视频{vid}")[:100],
description=(item.get("description") or "")[:500],
cover_url=_abs_cover(item)[:500],
@@ -232,13 +251,23 @@ async def sync_source(
max_pages: int = 0,
stop_after_existing: int = 30,
delay: float = 0.25,
should_cancel=None,
) -> dict:
"""按源的 URL 模板翻页采集;连续 stop_after_existing 条已存在则提前停止(增量)"""
stat = {"created": 0, "skipped": 0, "pages": 0, "stopped_early": False}
"""按源的 URL 模板翻页采集;连续 stop_after_existing 条已存在则提前停止(增量)。
should_cancel 返回 True 时每页/每条边界退出(用于手动取消)"""
stat = {"created": 0, "skipped": 0, "pages": 0, "stopped_early": False, "cancelled": False}
consecutive_existing = 0
def _cancelled() -> bool:
return bool(should_cancel and should_cancel())
async with httpx.AsyncClient(follow_redirects=True) as client:
page = 1
while True:
if _cancelled():
stat["cancelled"] = True
logger.info("通用采集源 %s 同步被取消(第 %s 页前)", source.id, page)
return stat
url = source.list_url.replace("{{page}}", str(page))
data = await _fetch_json(client, url)
items = data.get("items", [])
@@ -246,6 +275,10 @@ async def sync_source(
break
stat["pages"] = page
for item in items:
if _cancelled():
stat["cancelled"] = True
logger.info("通用采集源 %s 同步被取消(处理 item 中)", source.id)
return stat
if await _import_video_item(db, source, item):
stat["created"] += 1
consecutive_existing = 0