45 KiB
Telegram Media Downloader — 产品需求文档(PRD)
文档版本: 1.0
项目版本: 2.2.5
撰写日期: 2026-04-06
项目地址: https://github.com/tangyoha/telegram_media_downloader
目录
- 产品概述
- 目标用户
- 使用场景
- 核心功能
- 项目架构
- 目录结构
- 核心模块详解
- 配置项完整说明
- 下载过滤器语法
- Web 界面 API 接口
- 数据结构
- 下载目录结构
- 安装与运行
- 依赖说明
- 当前限制与已知问题
- 优化建议与迭代方向
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 自动化归档 | 高 |
前提要求:
- 拥有 Telegram 账号
- 在 my.telegram.org/apps 申请个人 API 凭证(免费,约 1 分钟)
- 目标频道/群组可访问(公开频道或已加入的私有群组)
- 中国大陆用户需配置代理(Telegram 在大陆被屏蔽)
3. 使用场景
场景 A:一次性归档某个频道的历史内容
用户希望把某个影视资源 Telegram 频道自 2023 年以来发布的所有 MP4 视频下载到本地。
操作流程:
- 打开 Web 界面,在顶部表单输入频道名(如
@example_channel) - 设置开始日期
2023-01-01,验证频道有效性 - 在
config.yaml中配置file_formats.video: [mp4](或在界面中设置) - 点击「保存并重启下载」,程序自动遍历频道历史消息,过滤并下载所有 MP4 文件
- 下载完成后,文件按
频道名/年月/目录结构整理好
场景 B:持续监听新频道更新
用户希望每次运行都只下载上次未下载的新内容(增量同步)。
机制: 每次下载完成后,程序自动把当前最大消息 ID 写入 config.yaml 的 last_read_message_id 字段,下次运行从该 ID 之后继续,不重复下载。
场景 C:通过 Telegram Bot 远程控制
用户在服务器上部署程序,配置 bot_token,通过向 Bot 发送命令来添加下载任务、查看进度,无需 SSH 进服务器。
场景 D:高清视频筛选下载
频道中混合了低清和高清视频,用户只想要 1080p 及以上的内容。
配置过滤器:
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节:下载过滤器语法。
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) |
重启机制:
# 任意地方设置此标志:
_app.restart_program = True
# run_with_restart() 检测到此标志后执行:
os.execv(sys.executable, [sys.executable] + sys.argv)
7.2 module/app.py — 应用配置管理
职责: 配置文件的读取/写入、任务节点数据结构定义、过滤器执行。
核心枚举
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、配置管理、频道历史。
初始化方式(依赖注入):
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,结构如下:
[
{
"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):
_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):
_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 客户端功能,包括下载进度回调、上传功能、权限检查。
核心功能:
-
HookClient— 继承pyrogram.Client,支持自定义钩子:on_download_start— 下载开始时回调on_download_progress— 下载进度更新回调on_download_finish— 下载完成回调
-
record_download_status— 装饰器,自动将下载进度写入download_stat.py的统计数据。 -
上传功能:
upload_telegram_chat()— 上传文件到指定 Telegram 聊天- 通过 cloud_drive.py 上传到云存储
-
get_extension(file_id, mime_type)— 根据 Telegram 文件 ID 或 MIME 类型推断文件扩展名。 -
check_user_permission(user_id)— 检查用户是否在白名单中(Bot 模式权限控制)。
7.7 module/cloud_drive.py — 云盘上传
支持适配器:
| 适配器 | 说明 | 配置字段 |
|---|---|---|
rclone |
通用云存储(100+ 服务) | upload_adapter: rclone |
aligo |
阿里云盘专用 | upload_adapter: aligo |
配置示例(config.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— 消息 IDcaption— 消息说明文字(截断处理)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 完整示例
# 示例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
检测当前配置完成度,用于判断是否需要显示新用户引导向导。
响应:
{
"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步使用),无需登录。
请求体:
{
"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"
}
响应:
{ "success": true, "message": "配置已保存,程序将重启" }
GET /get_app_version
响应: "2.2.5"(纯文本)
10.2 需要认证的接口
所有以下接口在设置了 web_login_secret 时需要先登录,否则返回 302 重定向到 /login。
GET /get_download_status
获取实时下载速度和状态。
响应:
{
"download_speed": "1.23 MB/s",
"upload_speed": "0.00 B/s",
"state": "pause" // "pause" = 下载中,"continue" = 已暂停
}
GET /api/task_progress
获取当前任务详细进度。
响应:
{
"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 界面初始化表单)。
响应:
{
"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 服务器查询)。
请求体:
{ "chat_id": "example_channel" }
响应(成功):
{
"valid": true,
"chat_id": "example_channel",
"chat_title": "示例频道",
"chat_type": "CHANNEL" // CHANNEL / SUPERGROUP / GROUP / PRIVATE
}
响应(失败):
{
"valid": false,
"error": "Chat not found"
}
POST /api/save_path
修改文件保存路径(立即生效,无需重启)。
请求体:
{ "save_path": "/new/save/path" }
响应:
{ "success": true, "save_path": "/new/save/path" }
POST /api/save_and_restart
保存下载配置并重启程序(最常用操作)。
请求体:
{
"chat_id": "example_channel",
"start_date": "2024-01-01",
"end_date": "2024-12-31", // 可为空
"chat_title": "示例频道",
"chat_type": "CHANNEL"
}
响应:
{ "success": true, "message": "配置已保存,程序正在重启" }
POST /api/start_download
动态启动下载任务(不重启程序,直接将任务加入当前运行队列)。
请求体(同 save_and_restart)
响应:
{ "success": true, "message": "下载任务已启动" }
GET /api/channel_history
获取频道历史记录列表(按最近使用排序)。
响应:
{
"success": true,
"history": [
{
"chat_id": "example_channel",
"chat_title": "示例频道",
"chat_type": "CHANNEL",
"last_used": "2026-04-06T12:00:00"
}
]
}
DELETE /api/channel_history/<chat_id>
删除指定频道的历史记录。
响应: { "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 数组):
[
{
"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 完整示例
# ── 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 结构
[
{
"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 本地安装
# 克隆仓库
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 部署
# 方式一:使用 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)。如项目代码有更新,部分细节可能与实际不符,请以代码为准。