# Telegram Media Downloader — 产品需求文档(PRD) **文档版本:** 1.0 **项目版本:** 2.2.5 **撰写日期:** 2026-04-06 **项目地址:** https://github.com/tangyoha/telegram_media_downloader --- ## 目录 1. [产品概述](#1-产品概述) 2. [目标用户](#2-目标用户) 3. [使用场景](#3-使用场景) 4. [核心功能](#4-核心功能) 5. [项目架构](#5-项目架构) 6. [目录结构](#6-目录结构) 7. [核心模块详解](#7-核心模块详解) 8. [配置项完整说明](#8-配置项完整说明) 9. [下载过滤器语法](#9-下载过滤器语法) 10. [Web 界面 API 接口](#10-web-界面-api-接口) 11. [数据结构](#11-数据结构) 12. [下载目录结构](#12-下载目录结构) 13. [安装与运行](#13-安装与运行) 14. [依赖说明](#14-依赖说明) 15. [当前限制与已知问题](#15-当前限制与已知问题) 16. [优化建议与迭代方向](#16-优化建议与迭代方向) --- ## 1. 产品概述 ### 1.1 产品定位 Telegram Media Downloader 是一款基于 Python 的 Telegram 媒体批量下载工具。它通过 Telegram 官方 MTProto API(pyrogram 客户端)连接 Telegram,能够从指定频道、群组或私聊中批量下载多种媒体类型(视频、图片、音频、文档、语音等),并提供配套的 Web 可视化管理界面和 Telegram Bot 控制接口。 ### 1.2 核心价值 - **批量高效**:支持对多个频道并发处理,断点续传,不重复下载已有文件。 - **精细过滤**:通过类 SQL 表达式对消息日期、文件大小、文件名、视频分辨率、标题内容等进行精确筛选。 - **多端控制**:提供 Web 界面(浏览器访问)和 Telegram Bot 两种控制方式,无需每次修改配置文件。 - **可扩展存储**:下载完成后可自动上传到 Telegram 指定聊天,或通过 Rclone 上传到 100+ 种云存储(Google Drive、OneDrive、阿里云盘等)。 - **容器化支持**:完整的 Docker/Docker-Compose 支持,易于部署和隔离运行。 ### 1.3 版本说明 当前版本为 **2.2.5**(见 `utils/__init__.py`)。该版本已经历多次重构,核心下载引擎稳定,Web 界面为近期新增功能,仍处于持续迭代阶段。 --- ## 2. 目标用户 | 用户类型 | 使用场景 | 技术门槛 | |---------|---------|---------| | 内容收藏者 | 批量下载 Telegram 频道的学习资料、影视资源、图片合集 | 低(Web 界面操作) | | 内容创作者 | 从特定群组归档历史素材 | 低 | | 开发者/研究者 | 数据采集、内容存档 | 中(需配置 API) | | 运营人员 | 多频道内容监控、素材备份 | 中 | | 服务器管理员 | 无头服务器 + Docker 自动化归档 | 高 | **前提要求:** 1. 拥有 Telegram 账号 2. 在 [my.telegram.org/apps](https://my.telegram.org/apps) 申请个人 API 凭证(免费,约 1 分钟) 3. 目标频道/群组可访问(公开频道或已加入的私有群组) 4. 中国大陆用户需配置代理(Telegram 在大陆被屏蔽) --- ## 3. 使用场景 ### 场景 A:一次性归档某个频道的历史内容 用户希望把某个影视资源 Telegram 频道自 2023 年以来发布的所有 MP4 视频下载到本地。 **操作流程:** 1. 打开 Web 界面,在顶部表单输入频道名(如 `@example_channel`) 2. 设置开始日期 `2023-01-01`,验证频道有效性 3. 在 `config.yaml` 中配置 `file_formats.video: [mp4]`(或在界面中设置) 4. 点击「保存并重启下载」,程序自动遍历频道历史消息,过滤并下载所有 MP4 文件 5. 下载完成后,文件按 `频道名/年月/` 目录结构整理好 --- ### 场景 B:持续监听新频道更新 用户希望每次运行都只下载上次未下载的新内容(增量同步)。 **机制:** 每次下载完成后,程序自动把当前最大消息 ID 写入 `config.yaml` 的 `last_read_message_id` 字段,下次运行从该 ID 之后继续,不重复下载。 --- ### 场景 C:通过 Telegram Bot 远程控制 用户在服务器上部署程序,配置 `bot_token`,通过向 Bot 发送命令来添加下载任务、查看进度,无需 SSH 进服务器。 --- ### 场景 D:高清视频筛选下载 频道中混合了低清和高清视频,用户只想要 1080p 及以上的内容。 **配置过滤器:** ```yaml download_filter: media_width >= 1920 and media_height >= 1080 ``` --- ### 场景 E:大文件上传至云盘 本地磁盘空间有限,下载后自动通过 Rclone 上传到 Google Drive,并删除本地文件。 --- ## 4. 核心功能 ### 4.1 媒体批量下载 - **支持媒体类型:** audio(音频)、photo(图片)、video(视频)、document(文档/PDF/压缩包等)、voice(语音消息)、video_note(圆形视频消息)、animation(GIF) - **文件格式过滤:** 对每种媒体类型可配置允许的格式列表,`all` 表示不限制 - **并发下载:** 通过 asyncio 队列管理,支持配置最大并发任务数(`max_download_task`)和最大并发传输数(`max_concurrent_transmissions`) - **断点续传:** 通过 `last_read_message_id` 记录上次处理到的消息位置,下次启动自动从该位置继续 - **去重跳过:** 已存在于目标路径且大小一致的文件自动跳过,不重复下载 - **临时目录机制:** 下载先写入 `temp/` 目录,完成后移动到最终位置,避免半完成文件污染 ### 4.2 下载过滤器 基于 PLY(Python Lex-Yacc)实现的表达式过滤引擎,支持: - 按消息日期筛选(最常用) - 按文件大小筛选 - 按视频分辨率筛选 - 按文件名正则匹配 - 按消息标题关键字筛选 - 按视频时长筛选 - 逻辑组合:`and`、`or` 详见 [第9节:下载过滤器语法](#9-下载过滤器语法)。 ### 4.3 Web 管理界面 - **顶部操作栏(横排):** 频道输入+验证、开始/结束日期、保存路径编辑、保存并重启按钮 - **实时统计头部:** 下载速度、正在下载数、已完成数、已跳过数 - **当前任务横幅:** 显示正在处理的频道名及当前状态 - **历史频道网格:** 记录历史使用的频道,点击可快速填入表单 - **下载进度列表:** 实时展示每个文件的下载进度条、速度 - **已完成列表:** 展示已完成下载的文件名和大小 - **暂停/继续控制:** 随时暂停或恢复下载任务 - **新用户引导向导:** 首次使用时显示 3 步引导(API 凭证获取、代理配置、确认),无需手动编辑配置文件 ### 4.4 Telegram Bot 控制 通过配置 `bot_token` 启用机器人模式: - 向 Bot 发送频道链接,Bot 自动添加下载任务 - 发送命令查看当前下载进度 - 转发消息给 Bot,Bot 自动下载其中的媒体 - 权限控制(可限制只有特定用户能使用) ### 4.5 云存储上传 | 功能 | 说明 | |------|------| | 上传到 Telegram 聊天 | 下载完成后转发到指定 Telegram 聊天/频道 | | Rclone 上传 | 支持所有 Rclone 兼容的云存储(Google Drive、OneDrive、S3、阿里云盘等) | | 阿里云盘(Aligo)| 专用适配器,支持阿里云盘 API | | 上传前压缩 | 可配置上传前对文件进行 ZIP 压缩 | | 上传后删除本地 | 上传成功后自动删除本地文件,节省磁盘空间 | ### 4.6 多语言支持 支持界面/日志语言:英文(EN)、中文(ZH)、俄文(RU)、乌克兰文(UA),通过 `config.yaml` 中 `language` 字段配置。 ### 4.7 容器化部署 提供完整 Dockerfile 和 docker-compose.yaml,支持: - 单命令部署 `docker-compose up -d` - 数据卷挂载(配置文件、下载目录) - 端口映射(Web 界面) --- ## 5. 项目架构 ### 5.1 技术栈 | 层级 | 技术 | |------|------| | Telegram 通信 | pyrogram(自定义 fork,支持最新 TDLib) | | 异步框架 | asyncio(Python 原生) | | Web 服务 | Flask 2.2.2 + Flask-Login | | 配置存储 | YAML(ruamel.yaml 保持格式) | | 过滤器引擎 | PLY(Python Lex-Yacc) | | 前端 | 原生 HTML/CSS/JS(无框架,深色主题) | | 加密 | pycryptodome(AES-128-CBC) | | 日志 | loguru + rich | | 云存储 | Rclone(子进程调用)、aligo | ### 5.2 整体架构图 ``` ┌─────────────────────────────────────────────────────────┐ │ media_downloader.py │ │ (主进程入口) │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌───────────────┐ │ │ │ Application │ │ asyncio │ │ Flask Web │ │ │ │ 配置管理 │ │ 事件循环 │ │ Server │ │ │ └──────────────┘ └──────┬───────┘ └───────────────┘ │ │ │ │ │ ┌────────────────┼──────────────────┐ │ │ ▼ ▼ ▼ │ │ ┌─────────────┐ ┌────────────┐ ┌──────────────┐ │ │ │ 下载主流程 │ │ Worker │ │ Bot 处理 │ │ │ │ (每个聊天) │ │ 队列处理 │ │ (可选) │ │ │ └──────┬──────┘ └─────┬──────┘ └──────────────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌─────────────────────────────────┐ │ │ │ pyrogram.Client │ │ │ │ (Telegram MTProto API) │ │ │ └────────────────┬────────────────┘ │ └────────────────────┼────────────────────────────────────┘ │ ┌──────┴──────┐ │ Telegram │ │ 服务器 │ └─────────────┘ ``` ### 5.3 核心数据流 ``` 程序启动 │ ├─ 加载 config.yaml → Application 初始化 │ ├─ 启动 Flask Web Server(端口 5001) │ ├─ 建立 Telegram 连接(pyrogram.Client.start()) │ └─ 首次运行:提示手机号 + 验证码(终端交互) │ └─ download_all_chat() 主循环 │ ├─ 对每个 chat(config.yaml 中的 chat 列表): │ ├─ get_chat_history_v2() → 逐批获取历史消息(每批 100 条) │ ├─ exec_filter() → 应用过滤表达式 │ ├─ 通过时 → add_download_task() → 压入 asyncio.Queue │ └─ 记录 last_read_message_id │ └─ Worker 并发处理队列 ├─ _get_media_meta() → 提取文件名、大小等元数据 ├─ 检查文件是否已存在 → 跳过 ├─ download_media() → 下载到 temp/ 目录 ├─ _check_download_finish() → 校验完整性 ├─ _move_to_download_path() → 移动到最终路径 ├─ [可选] 上传到 Telegram 或云盘 └─ update_download_status() → 更新 Web 统计数据 程序结束 ├─ 等待所有队列任务完成 ├─ 更新 config.yaml 中的 last_read_message_id └─ 如 restart_program=True → 重启进程(os.execv) ``` --- ## 6. 目录结构 ``` telegram_media_downloader/ ├── media_downloader.py # 主入口,程序启动和下载编排 ├── config.yaml # 用户配置文件(核心) ├── requirements.txt # Python 依赖列表 ├── setup.py # 打包配置 ├── Makefile # 快捷命令(install/test/lint) ├── Dockerfile # Docker 镜像构建文件 ├── docker-compose.yaml # Docker 编排配置 ├── gen_filter_cache.py # 过滤器缓存预生成脚本 ├── README.md # 英文文档 ├── README_CN.md # 中文文档 ├── PRD.md # 本文档 │ ├── module/ # 核心业务模块 │ ├── __init__.py │ ├── app.py # Application 类、任务节点、配置管理 │ ├── web.py # Flask Web 服务、所有 HTTP API │ ├── bot.py # Telegram Bot 处理逻辑 │ ├── filter.py # PLY 词法/语法分析过滤引擎 │ ├── download_stat.py # 下载速度统计、任务进度追踪 │ ├── get_chat_history_v2.py # 聊天历史异步生成器(优化版) │ ├── pyrogram_extension.py # pyrogram 扩展(钩子、进度、上传) │ ├── send_media_group_v2.py # 媒体组批量转发 │ ├── cloud_drive.py # 云盘上传适配器(Rclone/Aligo) │ ├── language.py # 多语言翻译字典 │ ├── parsetab.py # PLY 自动生成的解析表(勿手动修改) │ ├── parser.out # PLY 解析调试信息 │ ├── templates/ # Flask HTML 模板 │ │ ├── index.html # 主界面(含向导、下载控制) │ │ ├── login.html # 登录页面 │ │ └── control.html # 旧版控制页面(已由 index.html 统一) │ └── static/ # Web 静态资源 │ ├── css/ │ ├── layui/ # Layui 前端框架(旧版残留) │ ├── request/ # JS 请求库 │ ├── login/ # 登录页样式 │ └── aes/ # AES 加密 JS 库 │ ├── utils/ # 通用工具类库 │ ├── __init__.py # 版本号定义(__version__ = "2.2.5") │ ├── crypto.py # AES-128-CBC 加密解密 │ ├── file_management.py # 文件操作辅助函数 │ ├── format.py # 字节格式化、日期时间处理、URL 解析 │ ├── log.py # werkzeug 日志过滤器 │ ├── meta.py # 元数据打印工具 │ ├── meta_data.py # MetaData 类(过滤器属性集合) │ ├── platform.py # 跨平台(Windows/Linux/macOS)检测 │ └── updates.py # 版本更新检查 │ ├── tests/ # 单元测试 │ ├── test_common.py │ ├── test_media_downloader.py │ ├── module/ │ │ └── test_app.py │ └── utils/ │ ├── test_cypto.py │ ├── test_filter.py │ ├── test_file_management.py │ ├── test_format.py │ ├── test_log.py │ ├── test_meta.py │ └── test_updates.py │ └── temp/ # 下载临时目录(运行时自动创建) ``` --- ## 7. 核心模块详解 ### 7.1 `media_downloader.py` — 主入口 **职责:** 程序生命周期管理、异步任务编排、下载流程控制。 | 函数 | 说明 | |------|------| | `main()` | 程序主入口,创建 pyrogram 客户端,启动 Web/Bot 服务,进入下载主循环 | | `run_with_restart()` | 包装 main(),支持程序自动重启(`_app.restart_program = True` 时触发) | | `download_all_chat()` | 遍历 config 中所有聊天,依次调用 `download_chat_task` | | `download_chat_task()` | 单个聊天的完整下载流程:获取历史 → 过滤 → 入队 | | `download_task()` | 处理单条消息的下载:元数据提取 → 去重 → 下载 → 移动 → 上传 | | `download_media()` | 调用 pyrogram API 执行实际文件下载,写入 temp/ | | `worker()` | asyncio Queue 消费者,循环从队列取任务并调用 `download_task` | | `add_download_task()` | 将消息包装为任务节点,压入 asyncio.Queue | | `_get_media_meta()` | 从 pyrogram Message 对象提取文件名、大小、扩展名等元数据 | | `save_msg_to_file()` | 将纯文本消息保存为 .txt 文件(需配置 `enable_download_txt`) | **重启机制:** ```python # 任意地方设置此标志: _app.restart_program = True # run_with_restart() 检测到此标志后执行: os.execv(sys.executable, [sys.executable] + sys.argv) ``` --- ### 7.2 `module/app.py` — 应用配置管理 **职责:** 配置文件的读取/写入、任务节点数据结构定义、过滤器执行。 #### 核心枚举 ```python class DownloadState(Enum): Downloading = 1 # 正在下载 StopDownload = 2 # 已停止(暂停) class DownloadStatus(Enum): SkipDownload = 1 # 已跳过(文件已存在) SuccessDownload = 2 # 下载成功 FailedDownload = 3 # 下载失败 Downloading = 4 # 下载中 ``` #### 核心类 **`ChatDownloadConfig`** — 单个聊天的下载配置: | 属性 | 类型 | 说明 | |------|------|------| | `chat_id` | str/int | 聊天标识符 | | `last_read_message_id` | int | 最后读取的消息ID(断点续传依据) | | `download_filter` | str | 下载过滤表达式 | | `ids_to_retry` | list | 失败需重试的消息ID列表 | **`TaskNode`** — 单次下载任务的运行时状态: | 属性 | 类型 | 说明 | |------|------|------| | `chat_id` | str/int | 所属聊天 | | `total_task` | int | 本次任务总消息数 | | `download_status` | dict | {message_id: DownloadStatus} | | `is_running` | bool | 是否正在运行 | | `upload_success_count` | int | 上传成功计数 | **`Application`** — 全局应用实例: | 属性 | 说明 | |------|------| | `config` | 完整的 YAML 配置字典 | | `chat_download_config` | {chat_id: ChatDownloadConfig} | | `save_path` | 下载根目录路径 | | `api_id / api_hash` | Telegram API 凭证 | | `media_types` | 要下载的媒体类型列表 | | `file_formats` | 各媒体类型的格式白名单 | | `file_path_prefix` | 目录结构组件列表 | | `file_name_prefix` | 文件名前缀组件列表 | | `proxy` | 代理配置字典 | | `restart_program` | 重启标志(设为 True 触发重启) | | `cloud_drive_config` | 云盘配置 | --- ### 7.3 `module/web.py` — Web 服务 **职责:** Flask HTTP 服务、用户认证、前端 API、配置管理、频道历史。 **初始化方式(依赖注入):** ```python def init_web(app, client, add_download_task_func, download_chat_task_func): """ 由 media_downloader.py 在启动时调用,注入全局依赖: - app: Application 实例 - client: pyrogram.Client 实例 - add_download_task_func: 添加下载任务的回调 - download_chat_task_func: 下载聊天任务的回调 """ ``` **频道历史存储:** 保存在 `module/channel_history.json`,结构如下: ```json [ { "chat_id": "example_channel", "chat_title": "示例频道", "chat_type": "CHANNEL", "last_used": "2026-04-06T12:00:00" } ] ``` **登录认证:** - 密码在 `config.yaml` 的 `web_login_secret` 字段配置(明文,传输时 AES 加密) - 未配置 `web_login_secret` 时自动跳过登录(本地开发模式) - 使用 Flask-Login 管理会话 --- ### 7.4 `module/filter.py` — 过滤引擎 **职责:** 将过滤表达式字符串解析并编译为可执行的过滤函数。 **技术实现:** 使用 PLY(Python Lex-Yacc)构建词法分析器和语法分析器,将表达式 AST 转换为 Python 可调用对象。 **支持的可过滤属性(`utils/meta_data.py` 中 `MetaData` 类定义):** | 属性名 | 数据类型 | 说明 | |--------|---------|------| | `message_date` | datetime | 消息发送时间 | | `message_id` | int | 消息 ID | | `media_file_size` | int | 文件大小(字节) | | `media_width` | int | 图片/视频宽度(像素) | | `media_height` | int | 图片/视频高度(像素) | | `media_file_name` | str | 文件名(含扩展名) | | `message_caption` | str | 消息说明文字 | | `media_duration` | int | 视频/音频时长(秒) | | `sender_id` | int | 发送者用户 ID | | `sender_name` | str | 发送者名称 | | `reply_to_message_id` | int | 被回复的消息 ID | --- ### 7.5 `module/download_stat.py` — 下载统计 **职责:** 全局下载速度计算、任务进度数据的维护(供 Web API 消费)。 **核心全局数据(`_download_result`):** ```python _download_result: dict = { "chat_id_1": { message_id_1: { "down_byte": 已下载字节数(int), "total_size": 文件总字节数(int), "file_name": 完整文件路径(str), "download_speed": 当前速度(bytes/s,int), "start_time": 开始时间戳(float), } } } ``` **任务进度数据(`_task_progress`):** ```python _task_progress: dict = { "current_chat": 当前处理的聊天 ID(str), "current_chat_title": 当前聊天标题(str), "checked_messages": 已检查的消息总数(int), "skipped_files": 已跳过的文件数(int), "downloading_files": 正在下载的文件数(int), "completed_files": 已完成的文件数(int), "failed_files": 失败的文件数(int), "is_checking": 是否正在扫描消息(bool), } ``` --- ### 7.6 `module/pyrogram_extension.py` — pyrogram 扩展 **职责:** 扩展 pyrogram 客户端功能,包括下载进度回调、上传功能、权限检查。 **核心功能:** 1. **`HookClient`** — 继承 `pyrogram.Client`,支持自定义钩子: - `on_download_start` — 下载开始时回调 - `on_download_progress` — 下载进度更新回调 - `on_download_finish` — 下载完成回调 2. **`record_download_status`** — 装饰器,自动将下载进度写入 `download_stat.py` 的统计数据。 3. **上传功能:** - `upload_telegram_chat()` — 上传文件到指定 Telegram 聊天 - 通过 cloud_drive.py 上传到云存储 4. **`get_extension(file_id, mime_type)`** — 根据 Telegram 文件 ID 或 MIME 类型推断文件扩展名。 5. **`check_user_permission(user_id)`** — 检查用户是否在白名单中(Bot 模式权限控制)。 --- ### 7.7 `module/cloud_drive.py` — 云盘上传 **支持适配器:** | 适配器 | 说明 | 配置字段 | |--------|------|---------| | `rclone` | 通用云存储(100+ 服务) | `upload_adapter: rclone` | | `aligo` | 阿里云盘专用 | `upload_adapter: aligo` | **配置示例(config.yaml):** ```yaml cloud_drive_config: enable_upload_file: true upload_adapter: rclone # 或 aligo remote_dir: "gdrive:/telegram" # Rclone 远程路径 before_upload_file_zip: false # 上传前是否压缩 after_upload_file_delete: true # 上传成功后删除本地文件 ``` --- ## 8. 配置项完整说明 配置文件路径:`config.yaml`(项目根目录) ### 8.1 必填项 | 字段 | 类型 | 示例 | 说明 | |------|------|------|------| | `api_id` | int | `12345678` | Telegram API ID,从 my.telegram.org 获取 | | `api_hash` | str | `"abcdef1234..."` | Telegram API Hash,32位字符串 | | `chat` | list | 见下方 | 下载目标配置列表 | | `save_path` | str | `"/Users/xx/Downloads/tg"` | 文件保存根目录(绝对路径) | ### 8.2 聊天配置(`chat` 列表元素) | 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `chat_id` | str/int | — | 频道/群组用户名(`@xxx`或`xxx`)或数字 ID | | `last_read_message_id` | int | `0` | 上次读取到的消息 ID,断点续传依据,程序自动更新 | | `download_filter` | str | 无 | 过滤表达式,见第9节 | ### 8.3 媒体类型和格式 | 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `media_types` | list | `[audio, photo, video, document]` | 要下载的媒体类型 | | `file_formats.audio` | list | `[all]` | 允许的音频格式,`all` 不限制 | | `file_formats.video` | list | `[all]` | 允许的视频格式 | | `file_formats.document` | list | `[all]` | 允许的文档格式 | **有效媒体类型值:** `audio`、`photo`、`video`、`document`、`voice`、`video_note`、`animation` ### 8.4 文件路径配置 | 字段 | 类型 | 示例 | 说明 | |------|------|------|------| | `file_path_prefix` | list | `[chat_title, media_datetime]` | 目录层级结构组件,顺序即目录嵌套顺序 | | `file_name_prefix` | list | `[message_id]` | 文件名前缀组件 | **`file_path_prefix` 可用值:** - `chat_title` — 频道/群组名称 - `media_datetime` — 媒体发布的年月(格式 `2024_01`) - `media_type` — 媒体类型(audio/video/photo/document) **`file_name_prefix` 可用值:** - `message_id` — 消息 ID - `caption` — 消息说明文字(截断处理) - `chat_title` — 聊天标题 ### 8.5 Web 服务配置 | 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `web_host` | str | `"127.0.0.1"` | Web 服务绑定地址,服务器部署改为 `"0.0.0.0"` | | `web_port` | int | `5000` | Web 服务端口 | | `web_login_secret` | str | 无(跳过登录) | Web 界面登录密码,AES 加密传输 | ### 8.6 代理配置 | 字段 | 类型 | 说明 | |------|------|------| | `proxy.scheme` | str | 代理类型:`socks5` 或 `http` | | `proxy.hostname` | str | 代理服务器地址,本地代理填 `"127.0.0.1"` | | `proxy.port` | int | 代理端口(Clash SOCKS5: 7891,HTTP: 7890;V2Ray: 10808/10809) | **常见代理软件对应配置:** | 代理软件 | SOCKS5 端口 | HTTP 端口 | 推荐配置 | |---------|------------|----------|---------| | Clash | 7891 | 7890 | `scheme: socks5, port: 7891` | | 狗子云(VPN模式) | 无 | 7890 | `scheme: http, port: 7890` | | V2Ray | 10808 | 10809 | `scheme: socks5, port: 10808` | | Shadowsocks | 1080 | — | `scheme: socks5, port: 1080` | ### 8.7 性能配置 | 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `max_download_task` | int | `5` | 最大并发下载任务数 | | `max_concurrent_transmissions` | int | `1` | 单个文件的并发分片传输数 | ### 8.8 其他配置 | 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `language` | str | `"EN"` | 界面语言:`EN`/`ZH`/`RU`/`UA` | | `log_level` | str | `"INFO"` | 日志级别:`DEBUG`/`INFO`/`WARNING`/`ERROR` | | `enable_download_txt` | bool | `false` | 是否将纯文本消息保存为 .txt 文件 | | `hide_file_name` | bool | `false` | Web 界面是否隐藏下载文件名(隐私模式) | | `bot_token` | str | 无 | Telegram Bot Token,启用 Bot 模式 | | `allowed_user_ids` | list | 无(不限制) | 允许使用 Bot 的用户 ID 白名单 | --- ## 9. 下载过滤器语法 过滤器配置在 `config.yaml` 的 `chat[i].download_filter` 字段中。 ### 9.1 基本语法 ``` <属性名> <操作符> <值> [and/or <条件2>] ``` ### 9.2 操作符 | 操作符 | 适用类型 | 示例 | |--------|---------|------| | `>=` | 数字、日期 | `media_file_size >= 1024` | | `<=` | 数字、日期 | `message_date <= 2024-12-31 23:59:59` | | `>` | 数字、日期 | `media_width > 1920` | | `<` | 数字、日期 | `media_duration < 3600` | | `==` | 数字、字符串 | `sender_id == 123456` | | `!=` | 数字、字符串 | `sender_id != 123456` | | `matches` | 字符串 | `media_file_name matches ".*\.mp4$"` | | `contains` | 字符串 | `message_caption contains "4K"` | ### 9.3 逻辑组合 ``` # AND:两个条件都满足 message_date >= 2024-01-01 00:00:00 and message_date <= 2024-12-31 23:59:59 # OR:任一条件满足 media_file_name matches ".*\.mp4$" or media_file_name matches ".*\.mkv$" # 复合条件 message_date >= 2024-01-01 00:00:00 and media_file_size >= 10485760 ``` ### 9.4 完整示例 ```yaml # 示例1:只下载 2024 年的内容 download_filter: message_date >= 2024-01-01 00:00:00 and message_date <= 2024-12-31 23:59:59 # 示例2:只下载大于 10MB 的文件 download_filter: media_file_size >= 10485760 # 示例3:只下载 1080p 及以上的视频 download_filter: media_width >= 1920 and media_height >= 1080 # 示例4:只下载文件名包含特定字符串的文件 download_filter: media_file_name matches ".*\\.720p.*" # 示例5:只下载标题含关键字的消息 download_filter: message_caption contains "完整版" # 示例6:下载特定时间段内的大文件视频 download_filter: message_date >= 2023-06-01 00:00:00 and media_file_size >= 52428800 and media_duration >= 600 ``` ### 9.5 日期格式规范 日期必须使用格式:`YYYY-MM-DD HH:MM:SS`,例如: - `2024-01-18 00:00:00`(起始日期,时间设 00:00:00) - `2024-12-31 23:59:59`(结束日期,时间设 23:59:59) --- ## 10. Web 界面 API 接口 Base URL:`http://127.0.0.1:5001`(默认,端口可配置) ### 10.1 无需认证的接口 #### `GET /api/setup_status` 检测当前配置完成度,用于判断是否需要显示新用户引导向导。 **响应:** ```json { "has_api_credentials": true, // api_id 和 api_hash 是否已配置且有效 "has_session": true, // 是否已有 Telegram 登录会话文件 "has_chat": true, // 是否已配置了至少一个聊天 "proxy": { "scheme": "http", "hostname": "127.0.0.1", "port": 7890 }, "save_path": "/Users/xx/Downloads/tg" } ``` #### `POST /api/save_initial_config` 保存初始配置(新用户引导第3步使用),无需登录。 **请求体:** ```json { "api_id": 12345678, "api_hash": "abcdef...", "proxy_enabled": true, "proxy_scheme": "http", "proxy_hostname": "127.0.0.1", "proxy_port": 7890, "save_path": "/Users/xx/Downloads/tg" } ``` **响应:** ```json { "success": true, "message": "配置已保存,程序将重启" } ``` #### `GET /get_app_version` **响应:** `"2.2.5"`(纯文本) --- ### 10.2 需要认证的接口 所有以下接口在设置了 `web_login_secret` 时需要先登录,否则返回 302 重定向到 `/login`。 #### `GET /get_download_status` 获取实时下载速度和状态。 **响应:** ```json { "download_speed": "1.23 MB/s", "upload_speed": "0.00 B/s", "state": "pause" // "pause" = 下载中,"continue" = 已暂停 } ``` --- #### `GET /api/task_progress` 获取当前任务详细进度。 **响应:** ```json { "current_chat": "example_channel", "current_chat_title": "示例频道", "checked_messages": 1234, "skipped_files": 567, "downloading_files": 3, "completed_files": 890, "failed_files": 2, "is_checking": false } ``` --- #### `POST /set_download_state?state=pause` 暂停或继续下载。 | state 参数 | 效果 | |-----------|------| | `pause` | 暂停下载 | | `continue` | 继续下载 | **响应:** `"pause"` 或 `"continue"`(纯文本,表示操作后的新状态) --- #### `GET /api/get_config` 获取当前配置信息(供 Web 界面初始化表单)。 **响应:** ```json { "chat_id": "example_channel", "download_filter": "message_date >= 2024-01-01 00:00:00", "save_path": "/Users/xx/Downloads/tg" } ``` --- #### `POST /api/validate_chat` 验证频道/群组是否可访问(连通 Telegram 服务器查询)。 **请求体:** ```json { "chat_id": "example_channel" } ``` **响应(成功):** ```json { "valid": true, "chat_id": "example_channel", "chat_title": "示例频道", "chat_type": "CHANNEL" // CHANNEL / SUPERGROUP / GROUP / PRIVATE } ``` **响应(失败):** ```json { "valid": false, "error": "Chat not found" } ``` --- #### `POST /api/save_path` 修改文件保存路径(立即生效,无需重启)。 **请求体:** ```json { "save_path": "/new/save/path" } ``` **响应:** ```json { "success": true, "save_path": "/new/save/path" } ``` --- #### `POST /api/save_and_restart` 保存下载配置并重启程序(最常用操作)。 **请求体:** ```json { "chat_id": "example_channel", "start_date": "2024-01-01", "end_date": "2024-12-31", // 可为空 "chat_title": "示例频道", "chat_type": "CHANNEL" } ``` **响应:** ```json { "success": true, "message": "配置已保存,程序正在重启" } ``` --- #### `POST /api/start_download` 动态启动下载任务(**不重启程序**,直接将任务加入当前运行队列)。 **请求体(同 save_and_restart)** **响应:** ```json { "success": true, "message": "下载任务已启动" } ``` --- #### `GET /api/channel_history` 获取频道历史记录列表(按最近使用排序)。 **响应:** ```json { "success": true, "history": [ { "chat_id": "example_channel", "chat_title": "示例频道", "chat_type": "CHANNEL", "last_used": "2026-04-06T12:00:00" } ] } ``` --- #### `DELETE /api/channel_history/` 删除指定频道的历史记录。 **响应:** `{ "success": true, "message": "已删除" }` --- #### `POST /api/channel_history/clear` 清空所有频道历史记录。 **响应:** `{ "success": true, "message": "已清空" }` --- #### `GET /get_download_list?already_down=false` 获取下载文件列表。 | 参数 | 值 | 说明 | |-----|-----|------| | `already_down` | `false` | 返回正在下载的文件列表 | | `already_down` | `true` | 返回已完成下载的文件列表 | **响应(JSON 数组):** ```json [ { "chat": "example_channel", "id": "12345", "filename": "video.mp4", "total_size": "1.23 GB", "download_progress": "45.6", "download_speed": "2.34 MB/s", "save_path": "/Users/xx/Downloads/tg/示例频道/2024_01/video.mp4" } ] ``` --- ## 11. 数据结构 ### 11.1 `config.yaml` 完整示例 ```yaml # ── Telegram API 凭证(必填)── api_id: 12345678 api_hash: abcdef1234567890abcdef1234567890 # ── 下载目标列表(至少一个)── chat: - chat_id: example_channel last_read_message_id: 0 # 程序运行后自动更新,勿手动修改 download_filter: message_date >= 2024-01-01 00:00:00 # ── 媒体类型 ── media_types: - audio - photo - video - document - voice - video_note # ── 文件格式过滤 ── file_formats: audio: - all document: - all video: - all # ── 目录/文件名结构 ── file_path_prefix: - chat_title - media_datetime # ── 保存路径(必填)── save_path: /Users/username/Downloads/telegram_downloads # ── Web 界面 ── web_host: 127.0.0.1 web_port: 5001 # web_login_secret: your_password # 注释掉则不需要密码 # ── 代理(大陆必填)── proxy: scheme: http # 或 socks5 hostname: 127.0.0.1 port: 7890 # ── 可选高级配置 ── language: ZH max_download_task: 5 enable_download_txt: false # ── Bot 模式(可选)── # bot_token: 1234567890:AAXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # allowed_user_ids: [123456789] # ── 云盘上传(可选)── # cloud_drive_config: # enable_upload_file: true # upload_adapter: rclone # remote_dir: "gdrive:/Telegram" # after_upload_file_delete: true ``` ### 11.2 `channel_history.json` 结构 ```json [ { "chat_id": "example_channel", "chat_title": "示例频道", "chat_type": "CHANNEL", "last_used": "2026-04-06T12:34:56.789000" } ] ``` --- ## 12. 下载目录结构 下载文件的存储结构由 `file_path_prefix` 和 `file_name_prefix` 两个配置项控制。 ### 12.1 默认结构(`file_path_prefix: [chat_title, media_datetime]`) ``` save_path/ ├── 示例频道/ ← chat_title │ ├── 2024_01/ ← media_datetime (YYYY_MM) │ │ ├── 1001 - video_001.mp4 ← message_id - 文件名 │ │ ├── 1002 - photo_001.jpg │ │ └── 1003 - document.pdf │ └── 2024_02/ │ └── ... ├── 另一个频道/ │ └── ... └── temp/ ← 下载中的临时文件 └── ...(下载完成后自动清理) ``` ### 12.2 按媒体类型分目录(`file_path_prefix: [chat_title, media_type]`) ``` save_path/ └── 示例频道/ ├── video/ │ ├── video_001.mp4 │ └── video_002.mkv ├── photo/ │ └── photo_001.jpg ├── audio/ └── document/ ``` --- ## 13. 安装与运行 ### 13.1 环境要求 - Python 3.7+ - 网络可访问 Telegram 服务器(大陆需代理) - 已申请 Telegram API 凭证 ### 13.2 本地安装 ```bash # 克隆仓库 git clone https://github.com/tangyoha/telegram_media_downloader.git cd telegram_media_downloader # 安装依赖 pip3 install -r requirements.txt # 或使用 Makefile make install # 编辑配置文件(必须) # 填写 api_id, api_hash, save_path, chat(或使用 Web 向导) nano config.yaml # 启动 python3 media_downloader.py ``` ### 13.3 首次运行登录流程 ``` 程序启动 ↓ 提示输入手机号:Enter phone number: +86138... ↓ 提示输入验证码(Telegram App 内收件箱):Enter OTP: 12345 ↓ (可能需要)输入两步验证密码:Enter password: **** ↓ 登录成功,生成 session 文件(项目根目录,*.session) ↓ 后续运行无需重新登录(session 文件保存登录状态) ``` **注意:** session 文件是登录凭证,请妥善保管,不要提交到代码仓库。 ### 13.4 Docker 部署 ```bash # 方式一:使用 docker-compose(推荐) # 修改 docker-compose.yaml 中的卷挂载路径 docker-compose up -d # 方式二:手动运行容器 docker pull tangyoha/telegram_media_downloader:latest docker run -d \ -p 5000:5000 \ -v $(pwd)/config.yaml:/app/config.yaml \ -v /your/download/path:/downloads \ tangyoha/telegram_media_downloader:latest ``` ### 13.5 访问 Web 界面 启动后访问:`http://localhost:5001`(端口以 config.yaml 中 `web_port` 为准) --- ## 14. 依赖说明 ### 14.1 核心依赖 | 库 | 版本 | 用途 | 说明 | |----|------|------|------| | pyrogram | 自定义 fork | Telegram MTProto 客户端 | 使用 tangyoha 的修补版,修复了若干上游问题 | | PyTgCrypto | 1.2.6 | Telegram 加密 | pyrogram 的加密加速依赖 | | ruamel.yaml | 0.17.21 | YAML 读写 | 保持 YAML 注释和格式,用于写回 config.yaml | | PyYAML | 5.3.1 | YAML 解析 | 兼容性保留 | | flask | 2.2.2 | Web 框架 | 提供 HTTP 服务和模板渲染 | | flask-login | 0.6.2 | 会话认证 | Web 界面登录管理 | | Werkzeug | 2.2.2 | WSGI 工具 | Flask 依赖,HTTP 服务底层 | | pycryptodome | 3.18.0 | AES 加密 | Web 密码加密传输 | | ply | 3.11 | 词法/语法分析 | 下载过滤器表达式引擎 | | loguru | 0.6.0 | 日志 | 结构化彩色日志输出 | | rich | 12.5.1 | 终端输出 | 彩色进度显示 | | requests | 2.32.3 | HTTP 请求 | 版本更新检查 | ### 14.2 可选依赖 | 库 | 用途 | |----|------| | rclone | 云存储上传(外部命令,非 Python 包) | | aligo | 阿里云盘上传 | --- ## 15. 当前限制与已知问题 ### 15.1 功能限制 | 限制 | 描述 | 影响 | |------|------|------| | 终端首次登录 | 首次运行必须在终端手动输入手机号+验证码,不支持纯 Web 引导完成登录 | 服务器无头部署不便 | | 无内置调度 | 没有定时任务功能,需借助外部 cron 实现周期性运行 | 无法自动增量同步 | | 频道列表硬编码 | config.yaml 中的 chat 列表需手动编辑或通过 Web 界面覆盖,不支持多频道并存管理 | 多频道管理不便 | | 单进程架构 | 所有下载在一个进程中,重启会中断当前任务 | 更新配置代价大 | | 无下载队列持久化 | 程序重启后,未完成的队列任务丢失 | 中断恢复能力弱 | | Web 界面无 HTTPS | Web 服务仅 HTTP,暴露在公网有安全风险 | 服务器部署需额外加固 | ### 15.2 已知技术问题 | 问题 | 原因 | 临时解决方案 | |------|------|------------| | 代理配置敏感 | pyrogram 直连 Telegram DC IP,不走系统代理,必须在 config.yaml 中显式配置代理 | 确保代理软件开启了 SOCKS5/HTTP 端口 | | Flask 模板缓存 | 非 debug 模式下 Jinja2 不自动重载模板,修改 HTML 后需重启服务 | 重启服务进程 | | PLY 解析表文件 | `parsetab.py` 是自动生成的,不应手动修改,但提交到了版本库中 | 保持不修改此文件 | | 私有频道下载受限 | 必须以账号身份加入该私有频道才能下载,Bot Token 无法访问普通私有频道 | 用账号加入频道后再配置下载 | ### 15.3 性能特性 - 历史消息获取速度受 Telegram API 频率限制(大频道可能需要数分钟至数小时扫描) - 并发下载数默认 5,可通过 `max_download_task` 调高,但过高可能触发 Telegram 限流 - 临时文件在 `temp/` 目录,建议 `temp/` 和最终保存路径在同一磁盘分区(避免跨分区移动开销) --- ## 16. 优化建议与迭代方向 ### 16.1 高优先级(影响核心体验) #### 1. Web 引导完成 Telegram 登录 **现状:** 首次登录必须在终端交互完成,对服务器部署和非技术用户极不友好。 **建议:** 在 Web 向导第4步中,通过轮询 `/api/login_status` 接口感知登录状态,或通过 WebSocket 推送终端输出,在 Web 界面内完成手机号+验证码输入。 #### 2. 下载任务持久化 **现状:** 程序重启后,正在进行的任务从队列消失(只有 `last_read_message_id` 之前的已读记录)。 **建议:** 将 asyncio Queue 的待处理任务序列化到 SQLite 或 JSON 文件,重启后自动恢复。 #### 3. 多频道独立配置管理 **现状:** `save_and_restart` 接口会覆盖 config.yaml 中的 `chat` 列表为单个频道,不支持同时配置多频道。 **建议:** Web 界面支持频道列表管理,增删改每个频道的配置,而不是每次覆盖。 --- ### 16.2 中优先级(提升使用效率) #### 4. 内置定时任务(增量同步调度器) **建议:** 在 Web 界面添加"定时同步"功能(如每天凌晨 2 点自动运行),内置 APScheduler 或 cron 风格调度,无需依赖外部 cron。 #### 5. 下载统计与历史报告 **建议:** 增加下载历史记录(SQLite 存储),Web 界面展示: - 按日期的下载量图表 - 按频道的下载统计 - 磁盘空间使用情况 #### 6. 实时进度推送(WebSocket) **现状:** Web 界面通过轮询(每 2 秒 fetch 一次)获取进度,有延迟且浪费请求。 **建议:** 改用 WebSocket 或 SSE(Server-Sent Events)推送实时进度,降低延迟和服务器压力。 #### 7. 文件去重优化 **现状:** 通过比较文件路径和大小判断是否已下载。 **建议:** 增加基于消息 ID 的已下载记录(SQLite),即使文件被移动或重命名,也不会重复下载同一消息。 --- ### 16.3 低优先级(长期演进) #### 8. HTTPS 支持 **建议:** Web 服务支持 SSL/TLS 配置(提供证书路径配置项),或提供 Nginx 反向代理配置示例,供服务器部署时使用。 #### 9. 移动端适配 **现状:** Web 界面在小屏幕(480px 以下)有布局问题。 **建议:** 完善响应式布局,优化移动端操作体验(触摸友好的表单控件、大按钮)。 #### 10. 过滤器可视化编辑器 **现状:** 过滤器需手动输入表达式字符串,语法不直观。 **建议:** Web 界面提供图形化过滤条件构建器(类似 Notion filter),自动生成过滤表达式字符串。 #### 11. 多账号支持 **建议:** 支持配置多个 Telegram 账号(多个 `api_id/api_hash`),不同频道使用不同账号下载,规避单账号频率限制。 #### 12. API 文档自动生成 **建议:** 使用 Flask-RESTX 或 flasgger 为 Web API 生成 Swagger 文档,方便二次开发和集成。 --- ### 16.4 代码质量建议 | 类别 | 建议 | |------|------| | 测试覆盖率 | 补充 web.py API 端点的集成测试,当前测试主要覆盖工具函数 | | 类型注解 | 核心函数补充完整的 Python 类型注解(typing),提升 IDE 支持和可读性 | | 配置校验 | 启动时对 config.yaml 进行 Schema 校验(使用 pydantic 或 jsonschema),提前发现配置错误 | | 错误处理 | media_downloader.py 中部分 except 过于宽泛(`except Exception`),建议细化为具体异常类型 | | 日志规范 | 统一使用 loguru,移除部分混用 print 和 logging 的情况 | | 文档字符串 | 核心函数(尤其是 web.py 的路由函数)补充 docstring | --- *文档结束* > 本文档基于项目版本 2.2.5 编写(2026-04-06)。如项目代码有更新,部分细节可能与实际不符,请以代码为准。