feat(video-analysis): 抖音免浏览器解析链路 + 快手支持 + 小红书加固 + 发送防重复
- 抖音(2026-10-13):删除 SSR + playwright 旧链路(douyin_ssr.py 移除, douyin_api.py 精简为下载工具),新增 douyin_web.py(x-tt-argus 网关头 + web detail 接口 + 最高码率梯/多 CDN 回退下载)与 douyin_sign.py (纯 Python a_bogus 签名,vendor 自 f2(Apache-2.0),依赖 gmssl SM3); 实测 10/10 成功率、单作品 <1s(旧浏览器链路 5-15s 且随机撞验证码) - 快手(2026-10-08):kuaishou_content.py 分享页 INIT_STATE 静态解析(无登录态), 清晰度梯最佳档 + 图集 URL 组装,入口/策略/Web 面板同步接入 - 小红书:按链接来源定国际/国内站优先序、视频 backupUrls 逐候选回退、 图片 urlDefault→infoList WB_DFT→url 三档回退 - 长视频防重复发送(2026-10-13):媒体发送带 per-call _timeout(默认 300s), 超时=结果未知不再降级重试;auto/active 两条 handler 互斥 - Web 解析封面抽帧 ffmpeg 加 CREATE_NO_WINDOW(pythonw 下不再弹 cmd 黑窗) - 依赖:+gmssl;测试:新增 test_douyin_web / test_kuaishou_parse / test_video_cleanup, 扩充 test_video_policy / test_video_sender_link / test_video_group_file / test_video_media_naming Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
a31aab00e8
commit
a789a44f5d
25 files changed
+3536
-895
No files matched your search
@@ -66,6 +66,10 @@ POSTER_WIDTH = 480
|
||||
POSTER_SUFFIX = "_封面.jpg"
|
||||
POSTER_TIMEOUT = 20
|
||||
|
||||
#: bot 跑在 pythonw.exe(无控制台)下,spawn 控制台程序(ffmpeg)不带这个标志
|
||||
#: 会弹出一个 cmd 黑窗口;只在 Windows 有该常量,其它平台取 0 不影响调用
|
||||
CREATE_NO_WINDOW = getattr(subprocess, "CREATE_NO_WINDOW", 0)
|
||||
|
||||
#: 状态机:queued → running → done / failed
|
||||
ACTIVE_STATUSES = ("queued", "running")
|
||||
|
||||
@@ -136,9 +140,14 @@ async def make_poster(video: Path) -> Optional[Path]:
|
||||
str(out),
|
||||
]
|
||||
try:
|
||||
# 同步子进程必须丢线程池,否则抽帧期间整个事件循环都停摆
|
||||
# 同步子进程必须丢线程池,否则抽帧期间整个事件循环都停摆;
|
||||
# CREATE_NO_WINDOW 防止 pythonw 下弹 cmd 黑窗口
|
||||
await asyncio.to_thread(
|
||||
subprocess.run, cmd, capture_output=True, timeout=POSTER_TIMEOUT
|
||||
subprocess.run,
|
||||
cmd,
|
||||
capture_output=True,
|
||||
timeout=POSTER_TIMEOUT,
|
||||
creationflags=CREATE_NO_WINDOW,
|
||||
)
|
||||
except Exception: # noqa: BLE001 — 封面失败不该拖累任务
|
||||
logger.warning(f"抽关键帧失败:{video.name}")
|
||||
@@ -167,12 +176,14 @@ async def fetch_media(url: str) -> FetchResult:
|
||||
|
||||
分派规则与 `handlers/entry.py::dispatch_url` 一致:抖音 → parse_douyin;
|
||||
B站动态/专栏 → fetch_bilibili_content;其余 B站链接 → download_video(yt-dlp);
|
||||
小红书 → fetch_rednote_content;其它平台 → download_video。
|
||||
b23 / xhslink / 抖音短链的重定向在各 fetcher 内部自己处理。
|
||||
小红书 → fetch_rednote_content;快手 → fetch_kuaishou_content;
|
||||
其它平台 → download_video。
|
||||
b23 / xhslink / 快手短链的重定向在各 fetcher 内部自己处理。
|
||||
"""
|
||||
from ..handlers.douyin import parse_douyin
|
||||
from ..policy import BILIBILI, DOUYIN, XHS
|
||||
from ..policy import BILIBILI, DOUYIN, KUAISHOU, XHS
|
||||
from .fetchers.bilibili_content import fetch_bilibili_content
|
||||
from .fetchers.kuaishou_content import fetch_kuaishou_content
|
||||
from .fetchers.rednote_content import fetch_rednote_content
|
||||
from .fetchers.video_downloader import download_video
|
||||
|
||||
@@ -183,6 +194,9 @@ async def fetch_media(url: str) -> FetchResult:
|
||||
|
||||
if platform == DOUYIN:
|
||||
title, parsed, image_post = await parse_douyin(url)
|
||||
elif platform == KUAISHOU:
|
||||
title, parsed = await fetch_kuaishou_content(url)
|
||||
image_post = isinstance(parsed, list)
|
||||
elif platform == XHS:
|
||||
title, parsed = await fetch_rednote_content(url)
|
||||
image_post = isinstance(parsed, list)
|
||||
@@ -344,7 +358,7 @@ class JobManager:
|
||||
# ── 清理 ──────────────────────────────────────────────
|
||||
|
||||
def remove(self, job_id: str) -> bool:
|
||||
"""删掉一条任务;还在跑的一并取消(Chrome 那头由 playwright 自己收尾)。"""
|
||||
"""删掉一条任务;还在跑的一并取消(进行中的下载协程由取消信号收尾)。"""
|
||||
job = self._jobs.pop(job_id, None)
|
||||
if job is None:
|
||||
return False
|
||||
|
||||
Reference in new issue
Block a user