| 1 | # 项目实现总结(dy-downloader) |
| 2 | |
| 3 | ## 1. 项目概览 |
| 4 | |
| 5 | - **项目名称**: Douyin Downloader (`dy-downloader`) |
| 6 | - **版本**: `2.0.0` |
| 7 | - **更新时间**: `2026-02-18` |
| 8 | - **当前状态**: ✅ 核心功能可用,自动化测试通过 |
| 9 | |
| 10 | |
| 11 | ## 2. 当前实现能力(按代码现状) |
| 12 | |
| 13 | ### 2.1 已支持 |
| 14 | |
| 15 | - 单个视频下载(`/video/{aweme_id}`) |
| 16 | - 单个图文下载(`/note/{note_id}`) |
| 17 | - 抖音短链下载(`https://v.douyin.com/...`,会先解析后下载) |
| 18 | - 用户主页发布作品批量下载(`/user/{sec_uid}` + `mode: [post]`) |
| 19 | - 无水印优先下载,支持封面/音乐/头像/原始 JSON |
| 20 | - 并发下载、重试、速率限制 |
| 21 | - 基于作品发布时间(`create_time`)生成文件名/目录日期前缀(`YYYY-MM-DD_...`) |
| 22 | - 生成独立下载清单文件 `download_manifest.jsonl` |
| 23 | - 时间过滤(`start_time` / `end_time`) |
| 24 | - 数量限制(当前对 `number.post` 生效) |
| 25 | - SQLite 去重与增量下载(当前对 `increase.post` 生效) |
| 26 | - 翻页受限时的浏览器兜底(采集 `aweme_id` 并补全详情) |
| 27 | |
| 28 | ### 2.2 已新增(本次实现) |
| 29 | |
| 30 | - 用户点赞下载(`mode: [like]`) |
| 31 | - 用户合集下载(`mode: [mix]`)与单合集链接(`/collection/{mix_id}`、`/mix/{mix_id}`) |
| 32 | - 用户音乐模式下载(`mode: [music]`)与单音乐链接(`/music/{music_id}`) |
| 33 | - `number.like` / `number.mix` / `number.music` 与 `increase.like` / `increase.mix` / `increase.music` 生效 |
| 34 | - `number.allmix` / `increase.allmix` 兼容保留,并在加载时归一化到 `mix` |
| 35 | |
| 36 | |
| 37 | ## 3. 架构与模块 |
| 38 | |
| 39 | ```text |
| 40 | dy-downloader/ |
| 41 | ├── cli/ # CLI 入口与展示 |
| 42 | ├── core/ # 下载主流程、URL解析、API客户端 |
| 43 | ├── storage/ # 文件、元数据、数据库 |
| 44 | ├── auth/ # Cookie / token 管理 |
| 45 | ├── control/ # 限速、重试、并发队列 |
| 46 | ├── config/ # 配置加载与默认配置 |
| 47 | └── utils/ # 日志与通用工具 |
| 48 | ``` |
| 49 | |
| 50 | |
| 51 | ## 4. 下载数据落盘策略 |
| 52 | |
| 53 | ### 4.1 文件系统(主数据) |
| 54 | |
| 55 | 默认目录结构(`folderstyle: true`): |
| 56 | |
| 57 | ```text |
| 58 | Downloaded/ |
| 59 | ├── download_manifest.jsonl |
| 60 | └── 作者名/ |
| 61 | └── post/ |
| 62 | └── 2024-02-07_作品标题_aweme_id/ |
| 63 | ├── 2024-02-07_作品标题_aweme_id.mp4 |
| 64 | ├── 2024-02-07_作品标题_aweme_id_cover.jpg |
| 65 | ├── 2024-02-07_作品标题_aweme_id_music.mp3 |
| 66 | ├── 2024-02-07_作品标题_aweme_id_avatar.jpg |
| 67 | └── 2024-02-07_作品标题_aweme_id_data.json |
| 68 | ``` |
| 69 | |
| 70 | 命名日期优先使用作品发布时间 `create_time`;若缺失或非法,会回退到当前日期并记录告警。 |
| 71 | |
| 72 | ### 4.2 独立下载清单(新增) |
| 73 | |
| 74 | - 文件:`{path}/download_manifest.jsonl` |
| 75 | - 形式:每行一条 JSON(append-only) |
| 76 | - 典型字段: |
| 77 | - `date`(作品发布日期) |
| 78 | - `aweme_id` |
| 79 | - `author_name` |
| 80 | - `desc` |
| 81 | - `media_type` |
| 82 | - `tags`(来自 `text_extra`、`cha_list`、`desc` 中 `#`) |
| 83 | - `file_names` |
| 84 | - `file_paths` |
| 85 | - `publish_timestamp`(若可解析) |
| 86 | - `recorded_at`(写入时间) |
| 87 | |
| 88 | ### 4.3 SQLite 数据库(可开关) |
| 89 | |
| 90 | - 默认开关:`database: true` |
| 91 | - 默认库文件:`dy_downloader.db` |
| 92 | - 表结构: |
| 93 | - `aweme`:作品明细、作者、发布时间、下载时间、保存路径、原始 metadata |
| 94 | - `download_history`:每次任务 URL、类型、总数、成功数、配置快照 |
| 95 | |
| 96 | > 当 `database: false` 时,不写 SQLite,但**仍会写**媒体文件和 `download_manifest.jsonl`。 |
| 97 | |
| 98 | |
| 99 | ## 5. 关键流程(简版) |
| 100 | |
| 101 | 1. 读取配置(命令行 > 环境变量 > 配置文件 > 默认配置) |
| 102 | 2. 初始化 Cookie 与 API 客户端 |
| 103 | 3. 解析链接类型(视频 / 图文 / 用户) |
| 104 | 4. 拉取作品数据并应用时间/数量/增量规则 |
| 105 | 5. 并发下载媒体文件 |
| 106 | 6. 写入可选 JSON 元数据 |
| 107 | 7. 追加写入 `download_manifest.jsonl` |
| 108 | 8. 若开启数据库,写入 `aweme` 与 `download_history` |
| 109 | |
| 110 | |
| 111 | ## 6. 近期更新(2026-02-18) |
| 112 | |
| 113 | - ✅ 文件名和目录日期从“下载时间”改为“作品发布时间(`create_time`)” |
| 114 | - ✅ 新增独立下载清单 `download_manifest.jsonl` |
| 115 | - ✅ 清单中补充 `date/file_names/tags` 等可追溯字段 |
| 116 | - ✅ 增加对应测试,确保发布时间命名与清单写入行为 |
| 117 | |
| 118 | |
| 119 | ## 7. 测试与验证 |
| 120 | |
| 121 | 执行命令: |
| 122 | |
| 123 | ```bash |
| 124 | PYTHONPATH=. pytest -q |
| 125 | ``` |
| 126 | |
| 127 | 结果: |
| 128 | |
| 129 | ```text |
| 130 | 71 passed |
| 131 | ``` |
| 132 | |
| 133 | 说明:当前有 `pytest-asyncio` 的 deprecation warning(事件循环 scope 配置),不影响功能正确性。 |
| 134 | |
| 135 | |
| 136 | ## 8. 后续建议 |
| 137 | |
| 138 | 1. 为 `like/mix/music` 增加浏览器兜底,降低 API 分页受限影响。 |
| 139 | 2. 为 `download_manifest.jsonl` 增加轮转或归档策略(长期运行场景)。 |
| 140 | 3. 补充数据库查询 CLI(例如按作者/日期/标签检索)。 |
| 141 |