Files
yuming cf40343c51
部署到群晖 / deploy (push) Failing after 10m45s
初始化 telegram-downloader 并接入群晖 CI/CD
2026-04-22 21:29:03 +08:00

45 KiB
Raw Permalink Blame History

Telegram Media Downloader — 产品需求文档(PRD

文档版本: 1.0
项目版本: 2.2.5
撰写日期: 2026-04-06
项目地址: https://github.com/tangyoha/telegram_media_downloader


目录

  1. 产品概述
  2. 目标用户
  3. 使用场景
  4. 核心功能
  5. 项目架构
  6. 目录结构
  7. 核心模块详解
  8. 配置项完整说明
  9. 下载过滤器语法
  10. Web 界面 API 接口
  11. 数据结构
  12. 下载目录结构
  13. 安装与运行
  14. 依赖说明
  15. 当前限制与已知问题
  16. 优化建议与迭代方向

1. 产品概述

1.1 产品定位

Telegram Media Downloader 是一款基于 Python 的 Telegram 媒体批量下载工具。它通过 Telegram 官方 MTProto APIpyrogram 客户端)连接 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 申请个人 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.yamllast_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(圆形视频消息)、animationGIF
  • 文件格式过滤: 对每种媒体类型可配置允许的格式列表,all 表示不限制
  • 并发下载: 通过 asyncio 队列管理,支持配置最大并发任务数(max_download_task)和最大并发传输数(max_concurrent_transmissions
  • 断点续传: 通过 last_read_message_id 记录上次处理到的消息位置,下次启动自动从该位置继续
  • 去重跳过: 已存在于目标路径且大小一致的文件自动跳过,不重复下载
  • 临时目录机制: 下载先写入 temp/ 目录,完成后移动到最终位置,避免半完成文件污染

4.2 下载过滤器

基于 PLYPython Lex-Yacc)实现的表达式过滤引擎,支持:

  • 按消息日期筛选(最常用)
  • 按文件大小筛选
  • 按视频分辨率筛选
  • 按文件名正则匹配
  • 按消息标题关键字筛选
  • 按视频时长筛选
  • 逻辑组合:andor

详见 第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.yamllanguage 字段配置。

4.7 容器化部署

提供完整 Dockerfile 和 docker-compose.yaml,支持:

  • 单命令部署 docker-compose up -d
  • 数据卷挂载(配置文件、下载目录)
  • 端口映射(Web 界面)

5. 项目架构

5.1 技术栈

层级 技术
Telegram 通信 pyrogram(自定义 fork,支持最新 TDLib)
异步框架 asyncioPython 原生)
Web 服务 Flask 2.2.2 + Flask-Login
配置存储 YAMLruamel.yaml 保持格式)
过滤器引擎 PLYPython Lex-Yacc
前端 原生 HTML/CSS/JS(无框架,深色主题)
加密 pycryptodomeAES-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() 主循环
       │
       ├─ 对每个 chatconfig.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.yamlweb_login_secret 字段配置(明文,传输时 AES 加密)
  • 未配置 web_login_secret 时自动跳过登录(本地开发模式)
  • 使用 Flask-Login 管理会话

7.4 module/filter.py — 过滤引擎

职责: 将过滤表达式字符串解析并编译为可执行的过滤函数。

技术实现: 使用 PLYPython Lex-Yacc)构建词法分析器和语法分析器,将表达式 AST 转换为 Python 可调用对象。

支持的可过滤属性(utils/meta_data.pyMetaData 类定义):

属性名 数据类型 说明
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/sint,
            "start_time":     开始时间戳float,
        }
    }
}

任务进度数据(_task_progress):

_task_progress: dict = {
    "current_chat":       当前处理的聊天 IDstr,
    "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):

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 Hash32位字符串
chat list 见下方 下载目标配置列表
save_path str "/Users/xx/Downloads/tg" 文件保存根目录(绝对路径)

8.2 聊天配置(chat 列表元素)

字段 类型 默认值 说明
chat_id str/int 频道/群组用户名(@xxxxxx)或数字 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] 允许的文档格式

有效媒体类型值: audiophotovideodocumentvoicevideo_noteanimation

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 代理类型:socks5http
proxy.hostname str 代理服务器地址,本地代理填 "127.0.0.1"
proxy.port int 代理端口(Clash SOCKS5: 7891HTTP: 7890V2Ray: 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.yamlchat[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 URLhttp://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_prefixfile_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 或 SSEServer-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)。如项目代码有更新,部分细节可能与实际不符,请以代码为准。