Jianwu Huang
e5c6b89be4
Merge pull request #428 from pumpkinperson996/fix/ytdlp-zero-retries
...
fix(downloader): yt-dlp 未设 retries 时零重试,一次网络抖动就让任务失败
2026-08-25 13:57:33 +08:00
pumpkinperson996
58ec74f2a2
fix(downloader): yt-dlp 未设 retries 时零重试,网络抖动一次任务即失败
...
## 问题
任意一次瞬时网络故障都会让整个笔记任务失败,即便立刻重试同一个链接就能成功。
实际遇到的报错:
ERROR: [download] Got error: HTTPSConnectionPool(
host='upos-sz-mirrorcosov.bilivideo.com', port=443): Read timed out.
同一个视频一分钟后重新提交,10.18 MiB 四秒下完。
## 原因
yt-dlp 文档里 `retries` 默认 10,但那个默认值是**命令行参数解析器**给的,
Python API 不套用。项目所有 `ydl_opts` 都没设 `retries`,于是:
# yt_dlp/downloader/http.py
for retry in RetryManager(self.params.get('retries'), ...) # -> None
# yt_dlp/utils/_utils.py
self.retries = _retries or 0 # -> 0
也就是**每次下载只尝试一次,零重试**。这不是 B 站独有的问题,
YouTube 以及所有走 yt-dlp 的路径都一样。
## 改动
- `base.py`:新增 `YDL_RETRY_OPTS`(`retries` / `fragment_retries` /
`socket_timeout`),两个 downloader 本来就从 base 导入,不额外引入模块。
- `youtube_downloader.py`(2 处)、`bilibili_downloader.py`(3 处):
所有 `ydl_opts` 都展开该常量。只修报错的那一处会把其余四处继续留在零重试。
- 用的是 yt-dlp 自带的重试机制,没有自写重试循环。
取值偏保守(3 次而非 CLI 的 10):笔记任务是用户在前台等的,
重试太久不如早点失败让用户重来。
## 测试
新增 `tests/test_ydl_retry_opts.py`:
- 行为:常量给出的 RetryManager 预算 > 0;并显式钉住
`RetryManager(None).retries == 0` 这个被规避的坑。
- 结构:用 AST 断言两个 downloader 里**每一个** `ydl_opts` 字面量都展开了
`YDL_RETRY_OPTS`,防止以后新增下载路径时又悄悄回到零重试。
结构用例确认过 red-green:去掉任一处展开即失败,并指出具体文件行号。
无 yt-dlp 的环境下两个行为用例自动 skip,与仓库既有测试风格一致。
2026-07-25 12:00:06 -05:00
pumpkinperson996
3e579b1434
fix(youtube): 修复 YouTube 笔记生成失败 "Requested format is not available"
...
## 问题
输入 YouTube 链接生成笔记时任务必定失败:
ERROR: [youtube] <id>: Requested format is not available
即使字幕已经抓取成功(日志里能看到"成功获取 YouTube 字幕,共 N 段"),
任务仍然在下载阶段崩掉。
## 原因
两个独立的问题叠加:
1. `requirements.txt` 把 yt-dlp 钉在 `2025.3.31`。YouTube 之后轮换过
player,旧版 yt-dlp 解不出 nsig 签名,所有音视频格式都被丢弃,只剩下
storyboard 图片(日志:`Only images are available for download`),
格式选择随即抛错。
2. 有字幕时 `NoteGenerator` 走的是"只取元信息"的路径
(`download(skip_download=True)`,只要 title/duration/cover),但
`YoutubeDownloader.download` 无条件设置了
`format='bestaudio[ext=m4a]/bestaudio/best'`。于是一个根本不需要媒体流的
调用,也会因为选不出格式而失败——把一个字幕已经到手的任务整个带崩。
## 改动
- `youtube_downloader.py`:`skip_download` 时设置
`ignore_no_formats_error=True`。yt-dlp 落后于 YouTube 时,只取元信息的路径
降级为"没有音频",而不是让整个笔记失败。
- `youtube_downloader.py`:`ext = info.get("ext") or "m4a"`。跳过下载时
yt-dlp 返回 `ext=None`,`dict.get` 的默认值对显式 None 不生效,
会拼出 `xxx.None` 这样的路径。
- `requirements.txt`:`yt-dlp==2025.3.31` → `>=2026.7.4`。yt-dlp 是对抗
YouTube 变化的滚动依赖,精确钉版本本身就是这个 bug 的成因;用 `>=` 与同文件
的 `youtube-transcript-api>=1.0.0` 保持一致。
- `bilibili_dm_patch.py`:wrapper 改为透传 `**kwargs`。升级 yt-dlp 后
`_real_extract` 会以 `fatal=False` 调用 `_download_playinfo`,而 wrapper
钉死了签名,导致 **所有 B 站下载** 抛
`TypeError: ... got an unexpected keyword argument 'fatal'`。
- 测试:新增 `test_youtube_metadata_only.py` 覆盖上面两条 YouTube 保证;
`test_bilibili_dm_patch.py` 新增未知 kwargs 透传用例,并让 fake 响应带上
`code` 字段(yt-dlp 2026.x 会先校验信封再返回 data)。
## 验证
- 真实跑通:YouTube(有字幕,走元信息路径)与 B 站(无字幕,走完整下载 +
转写)均能生成笔记。
- `pytest tests/` → 46 passed。唯一失败的
`test_task_serial_executor` 在升级前后表现一致,与本次改动无关,未作改动。
2026-07-25 07:37:10 -05:00
Jianwu Huang
54714faa73
Merge pull request #410 from JefferyHcool/fix/bilibili-412-dm-img-params
...
fix(bilibili): 注入 dm_img 风控参数修复 wbi/playurl 412
2026-06-17 09:58:37 +08:00
huangjianwu and Claude Opus 4.8
f79dc612fb
fix(bilibili): 注入 dm_img 风控参数修复 wbi/playurl 412
...
B 站 wbi/playurl 网关新增 dm_img_list/dm_img_str/dm_cover_img_str/
dm_img_inter + web_location 风控校验,缺失即返回 HTTP 412。对于网页不内嵌
playinfo、必须走 API 的视频(如 BV1X9L16oEgB),yt-dlp(含最新版)尚未适配,
导致下载失败,且刷新 cookie 无效。
通过猴补丁在 BilibiliBaseIE._download_playinfo 的 wbi 签名前注入哑值 dm_img
参数(取值形态对齐 yt-dlp 自身在 arc/search 端点的用法),即可恢复 200。
已验证补丁对固定版 2025.03.31 与最新 2026.06.09 签名一致、向前兼容;新增 4 个单元测试。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-06-17 09:55:54 +08:00
wmsdsb137
ab9ca6a026
fix: address Copilot review suggestions
...
- Remove unused Tuple import
- Validate /pN suffix p >= 1
- Reuse resolve_bilibili_short_url in fetch_subtitles
- Deduplicate short URL resolution
2026-06-16 21:01:27 +08:00
wmsdsb137
2ba409880e
fix(bilibili): 修正分P视频字幕优先链路未传p参数导致取错集
...
问题:B站分P视频(如62集课程),提交?p=36链接时,
字幕优先链路通过x/web-interface/view API拿cid时未传p参数,
默认取第1集cid,导致生成的是第1集的笔记。
同时yt-dlp正确下载了p36音频,但被跳过。
修复:
- url_parser新增extract_bilibili_p_number()提取URL中的p参数
- bilibili_subtitle的_get_cid()接收p参数,从data.pages[p-1]取对应分P的cid
- fetch_subtitles()调用extract_bilibili_p_number()透传p
2026-06-16 20:54:36 +08:00
huangjianwu and Claude Opus 4.7
41f17592c2
fix(backend): 部署韧性——模型自愈/就绪门禁/全局代理/启动诊断
...
- whisper: model.bin 截断/损坏时删目录重下重试一次,修「Unable to
open file model.bin」死循环;mlx 同样按 config.json 判完整性
- /generate_note 加就绪门禁:本地转写引擎模型没下好直接拦截,返回
reason=transcriber_model_not_ready,不让任务静默卡在首次下载
- 全局代理:新增 ProxyConfigManager(JSON 配置 + HTTP_PROXY env 兜底)
+ build_openai_client,统一注入代理到 LLM/Groq 客户端;yt-dlp 与
youtube-transcript-api 也走代理
- build_openai_client 校验 api_key 非空,空 key 给「xxx 的 API Key
未配置」而不是天书般的 Illegal header value b'Bearer '
- universal_gpt: 模型拒绝自定义 temperature(o1/o3/gpt-5 系列)时
就地去掉参数重试,不消耗重试预算
- connect_test 改用真实 chat completion 而非 /v1/models 探测
- main.py: lifespan 拆 [startup 1/5..5/5] 分段日志 + 异常清晰定位
- /sys_health 重构为结构化返回 {backend,ffmpeg,db,whisper_model}
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-14 19:01:14 +08:00
huangjianwu and Claude Opus 4.7
702b57c165
feat(bilibili): 优先走官方 player API 直拉字幕
...
之前 BilibiliDownloader.download_subtitles 走的是 yt-dlp 的 writesubtitles 路径,对 B 站签名/Cookie 的兼容性差,常常空手而归,落到音频下载 + Whisper 转写的慢路径。
新增 bilibili_subtitle.BilibiliSubtitleFetcher:
- /x/web-interface/view?bvid=... → 拿 cid
- /x/player/wbi/v2?bvid=...&cid=... → 拿 subtitle 列表(subtitle_url 已带 auth_key)
- 优先级:人工中文 > AI 中文 > 任意中文 > 任意非空
- fetch JSON body 解析为 TranscriptResult
- 通过 CookieConfigManager 自动注入 SESSDATA cookie(AI 字幕必需)
bilibili_downloader.download_subtitles 顺序改为:先试新 fetcher,失败再回退到原 yt-dlp 路径。NoteGenerator 的字幕优先逻辑无需改动——它本来就调 download_subtitles。
效果:
- B 站视频如果有字幕(人工或 AI),直接秒拿,跳过音频下载 + 转写
- 完全绕开 MLX Whisper 不可用 / 模型未下载 等转写器问题
- 拿不到字幕时仍可走原音频转写路径
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-07 11:55:50 +08:00
Lizhilin
c5e08e1ec6
fix: BilibiliDownloader 从 CookieConfigManager 读取 cookie 并注入 yt-dlp cookiefile
2026-04-28 23:20:34 +08:00
huangjianwu and Claude Opus 4.6
f4801d5be7
feat(youtube): 使用 youtube-transcript-api 优先获取字幕,有字幕时跳过音频下载
...
- 新增 YouTubeSubtitleFetcher 模块,通过 youtube-transcript-api 获取字幕
- 重构笔记生成流程:缓存 → 平台字幕 → 按需下载 → 转写 fallback
- 有字幕时仅提取视频元信息,不下载音视频文件
- 添加 youtube-transcript-api 依赖
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com >
2026-03-23 17:31:30 +08:00
sunnyclubcn and Claude Opus 4.5
85b24dee40
feat: Add subtitle priority fetching and enhance mindmap export
...
## Subtitle Priority (Backend)
- Add download_subtitles() method to base downloader
- Implement Bilibili subtitle fetching with cookies support
- Implement YouTube subtitle fetching
- Support SRT and JSON3 format parsing
- Prioritize platform subtitles over Whisper transcription
## Mindmap Export Enhancements (Frontend)
- Add SVG vector export with proper viewBox handling
- Add XMind format export with Chinese character encoding fix
- Fix PNG/SVG export to capture full content by calling fit() before export
- Add JSZip dependency for XMind export
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com >
2026-01-24 17:12:14 +08:00
JefferyHcool
d92cc4a977
feat(NoteForm): 增加文件上传状态反馈
2025-06-19 14:54:51 +08:00
JefferyHcool
8b1bc54f2d
refactor(backend): 重构后端异常处理和模型管理
...
- 新增自定义异常类 BizException、NoteError 和 ProviderError
- 优化了模型管理相关的逻辑,包括加载、删除和测试连接等功能
- 改进了 Douyin 下载器的错误处理
- 调整了任务重试逻辑和笔记生成的异常处理- 更新了相关组件和页面以适应新的异常处理机制
2025-06-06 21:30:23 +08:00
黄建武
21c9d47495
feat(download): 添加快手下载器并优化下载配置功能
...
- 新增快手下载器,支持快手视频下载
- 添加下载配置页面,可设置各平台Cookies
- 优化后端接口,增加获取和更新Cookies的功能
- 前端新增Downloader组件和相关表单组件
- 更新路由配置,增加下载配置相关路由
2025-05-08 18:15:59 +08:00
黄建武
97f153646f
feat(frontend): 新增多版本笔记功能,并做了向下兼容。
...
- 新增关于页面组件,介绍项目背景、功能和使用方法
- 重构笔记生成逻辑,支持多版本笔记
- 新增笔记版本选择、复制和导出功能
-优化笔记界面布局和交互
- 调整部分组件样式,提升用户体验
2025-05-04 11:00:54 +08:00
黄建武
04dad3b72a
feat(downloaders): 添加抖音视频识别功能
...
- 新增 abogus.py 文件,实现 a_bogus 参数的生成逻辑
- 代码源自 JoeanAmier/TikTokDownloader 项目,并进行了适配和优化
- 功能包括生成用户代理字符串、加密 URL 参数和生成最终的 a_bogus值
- 提供了详细的注释和函数说明,便于理解和维护
2025-05-02 14:00:29 +08:00
思诺特
c65de4654f
feat(local): 添加本地视频处理功能
...
- 实现本地视频上传和处理功能
- 新增 LocalDownloader 类处理本地视频
- 更新前端界面支持本地视频选择
- 添加视频封面提取和保存功能
- 优化后端路由支持本地视频上传
2025-04-28 13:34:09 +08:00
思诺特
84cd345b9f
feat(frontend): 重构首页布局并添加生成历史组件
...
- 新增 History 组件用于展示生成历史记录
- 调整 HomeLayout 布局,增加 History 侧边栏
- 优化 NoteHistory 组件样式和布局- 更新首页样式,调整各个组件的位置和样式
2025-04-27 16:57:03 +08:00
思诺特
489fa78946
fix(env): 修复 Youtube 转义失败问题
2025-04-27 14:45:57 +08:00
Jefferyhcool
bb974b0b89
:feat 新增模型配置页面和相关功能
...
- 新增模型配置页面组件和路由
- 实现模型配置表单和相关逻辑- 添加全局配置入口和功能- 优化首页布局和样式- 新增 404 页面组件
- 更新部分组件样式和结构
2025-04-22 17:01:02 +08:00
Jefferyhcool
6c21aa2055
feat(bilibili_downloader): 更新视频下载格式选择
...
- 将音频下载格式更改为视频+音频组合格式
- 新格式优先选择bv*+ba,然后是bestvideo+bestaudio,最后是best
- 这个改动可以提高下载视频的质量,并确保视频和音频同步
2025-04-15 22:07:09 +08:00
Jefferyhcool
61a0c08df8
fix(bilibili_downloader): 修改音频下载格式为 mp4
...
- 将 BilibiliDownloader 类中的音频下载格式从 m4a改为 mp4
- 此修改解决了因 m4a 格式导致的文件名错误问题
2025-04-15 22:01:10 +08:00
Jefferyhcool
a966614dd2
feat(downloaders): 更新 Bilibili 和 YouTube 下载器格式选择- 将 Bilibili 和 YouTube 下载器的格式选择从视频格式更改为音频格式
...
-优先选择 m4a 格式的最佳音频,如果没有可用的 m4a 格式,则选择最佳音频格式
- 如果没有可用的音频格式,则选择最佳格式
2025-04-15 20:19:46 +08:00
思诺特
a395f8e1c1
feat(downloaders): 更新 Bilibili 和 YouTube 下载器的视频质量选择
...
- Bilibili 下载器:将视频格式选择更改为优先下载 MP4 格式,且分辨率不超过 480p
- YouTube 下载器:将视频格式选择更改为优先下载 MP4 格式,且分辨率不超过 480p
2025-04-15 11:39:38 +08:00
思诺特
ea565b8a9f
fix(bilibili_downloader): 移除音频转码步骤并更改输出格式
...
- 移除了 ydl_opts 中的 postprocessors 设置,取消了将音频转码为 mp3 的步骤
- 将输出音频文件的扩展名从 mp3 改为 m4a
2025-04-15 10:44:00 +08:00
Jefferyhcool
7bf31ad16e
fix(downloaders): 修复 Youtube 下载报错问题理
...
- 优化了临时文件清理逻辑,删除以指定文件名开头的所有相关文件
2025-04-15 00:16:03 +08:00
Jefferyhcool
1ebf236f4f
feat(app): 添加日志记录功能
...
- 新增 logger 模块,用于全局日志记录
- 在关键位置添加日志输出,包括事件处理、文件清理、FFmpeg 检查等
- 优化数据库操作,增加日志记录
- 在主程序启动时添加日志输出
2025-04-14 19:50:44 +08:00
Jefferyhcool
0e0b8da317
first commit
2025-04-13 17:44:54 +08:00