Skip to content

cheneytianx/media-converter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Convertor — Web 音视频格式转换工具

基于 Flask + FFmpeg 的在线音视频格式转换工具,支持 12 种格式(6 音频 + 6 视频)互转,提供拖拽上传、实时进度条、一键下载等功能。

环境依赖

依赖 版本要求 说明
Python >= 3.9 运行时环境
Flask >= 3.0, < 4.0 Web 框架(见 requirements.txt
FFmpeg >= 5.0(建议) 音视频编解码引擎,需包含 ffmpegffprobe

FFmpeg 是系统级依赖,不在 pip install 范围内,需单独安装。

快速部署

1. 安装 FFmpeg

# Ubuntu / Debian
sudo apt update && sudo apt install -y ffmpeg

# CentOS / RHEL
sudo yum install -y epel-release && sudo yum install -y ffmpeg

# macOS (Homebrew)
brew install ffmpeg

# 验证安装
ffmpeg -version
ffprobe -version

2. 安装 Python 依赖

# 建议使用虚拟环境
python3 -m venv venv
source venv/bin/activate   # Windows: venv\Scripts\activate

pip install -r requirements.txt

3. 启动服务

# 开发模式(带热重载)
python app.py
# 服务启动在 http://localhost:5000

# 生产模式(使用 Gunicorn)
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:5000 app:app

4. 访问

浏览器打开 http://localhost:5000,拖拽文件即可开始转换。

配置项

配置文件为 config.py,可按需修改:

配置项 默认值 说明
UPLOAD_FOLDER ./uploads 上传文件存储目录
OUTPUT_FOLDER ./outputs 转换结果存储目录
MAX_CONTENT_LENGTH 500 MB 单文件上传大小限制
ALLOWED_EXTENSIONS 6 音频 + 6 视频 支持的文件格式
FILE_RETENTION_SECONDS 3600(1小时) 文件保留时间

支持的格式

类型 格式
音频 MP3、WAV、AAC、FLAC、OGG、M4A
视频 MP4、AVI、MKV、MOV、WebM、FLV

同类型内可互转(音频↔音频,视频↔视频)。

项目目录结构

convertor/
├── app.py                          # Flask 主应用(路由层)
├── config.py                       # 全局配置
├── requirements.txt                # Python 依赖
├── converter/                      # 核心转换模块
│   ├── __init__.py
│   ├── formats.py                  # 格式定义与校验
│   ├── ffmpeg_wrapper.py           # FFmpeg 进程封装
│   └── task_manager.py             # 任务状态管理
├── templates/
│   └── index.html                  # 页面模板
├── static/
│   ├── css/style.css               # 暗色主题样式
│   └── js/main.js                  # 前端交互逻辑
├── uploads/                        # 上传文件临时存储
└── outputs/                        # 转换结果存储

整体架构图

graph TB
    subgraph Browser["🌐 浏览器 (前端)"]
        direction TB
        HTML["index.html<br/>页面结构 · 4个步骤面板"]
        CSS["style.css<br/>暗色主题 · 响应式布局"]
        JS["main.js<br/>交互逻辑 · 状态机"]
        HTML --- CSS
        HTML --- JS
    end

    subgraph Flask["🐍 Flask 服务端 (app.py)"]
        direction TB
        R1["GET /<br/>渲染首页"]
        R2["POST /api/upload<br/>接收文件 · 返回格式信息"]
        R3["POST /api/convert<br/>创建转换任务 · 启动后台线程"]
        R4["GET /api/task/:id<br/>查询任务进度"]
        R5["GET /api/download/:id<br/>下载转换结果"]
    end

    subgraph Core["⚙️ converter 核心模块"]
        direction TB
        FMT["formats.py<br/>12种格式定义<br/>6音频 + 6视频<br/>格式校验·目标推荐"]
        FFW["ffmpeg_wrapper.py<br/>FFmpeg 进程管理<br/>ffprobe 获取媒体信息<br/>-progress pipe:1 进度解析"]
        TM["task_manager.py<br/>Task 数据类<br/>内存字典存储<br/>线程锁保护读写"]
    end

    subgraph Storage["💾 磁盘存储"]
        UP["uploads/<br/>上传的原始文件"]
        OUT["outputs/<br/>转换后的文件"]
    end

    subgraph External["🔧 外部依赖"]
        FF["FFmpeg / FFprobe<br/>音视频编解码引擎"]
    end

    CFG["config.py<br/>路径 · 大小限制<br/>支持格式 · 保留时间"]

    JS -- "XHR POST multipart/form-data" --> R2
    JS -- "fetch POST JSON" --> R3
    JS -- "fetch GET (500ms轮询)" --> R4
    JS -- "a href 下载" --> R5
    R1 -- "render_template" --> HTML

    R2 -- "校验格式" --> FMT
    R2 -- "ffprobe 获取时长" --> FFW
    R2 -- "file.save()" --> UP

    R3 -- "create_task()" --> TM
    R3 -- "threading.Thread" --> FFW

    R4 -- "get_task()" --> TM

    R5 -- "send_file()" --> OUT

    FFW -- "subprocess.Popen" --> FF
    FFW -- "progress_callback" --> TM
    FFW -- "输出文件" --> OUT
    FFW -- "读取源文件" --> UP
    FFW -- "get_format_info()" --> FMT

    CFG -. "UPLOAD_FOLDER" .-> R2
    CFG -. "OUTPUT_FOLDER" .-> R3
    CFG -. "MAX_CONTENT_LENGTH" .-> Flask

    style Browser fill:#1a1a2e,stroke:#e94560,color:#fff
    style Flask fill:#16213e,stroke:#0f3460,color:#fff
    style Core fill:#0f3460,stroke:#533483,color:#fff
    style Storage fill:#1a1a2e,stroke:#e94560,color:#fff
    style External fill:#533483,stroke:#e94560,color:#fff
Loading

核心流程时序图

sequenceDiagram
    actor User as 👤 用户
    participant B as 🌐 浏览器<br/>main.js
    participant F as 🐍 Flask<br/>app.py
    participant FMT as 📋 formats.py
    participant TM as 📊 task_manager.py
    participant T as 🧵 后台线程
    participant FFW as ⚙️ ffmpeg_wrapper.py
    participant FF as 🔧 FFmpeg 进程

    Note over User,FF: ① 上传阶段
    User->>B: 拖拽/选择文件
    B->>B: new FormData()<br/>append('file', File)
    B->>F: XHR POST /api/upload<br/>multipart/form-data
    Note over B: upload.onprogress 更新上传进度条
    F->>F: 校验文件格式/大小
    F->>F: file.save() → uploads/
    F->>FFW: get_media_info(path)
    FFW->>FF: ffprobe -print_format json
    FF-->>FFW: {duration, streams, format}
    F->>FMT: get_convertible_targets('ogg')
    FMT-->>F: [{ext:'mp3'}, {ext:'wav'}, ...]
    F-->>B: 200 JSON {filename, targets, duration, size}

    Note over User,FF: ② 选择格式
    B->>User: 展示格式按钮 (MP3/WAV/AAC/FLAC/M4A)
    User->>B: 点击 MP3 → 点击"开始转换"

    Note over User,FF: ③ 转换阶段
    B->>F: fetch POST /api/convert<br/>{stored_name, target_ext:'mp3'}
    F->>FMT: 校验目标格式
    F->>TM: create_task() → Task{id, status:pending}
    F->>T: threading.Thread(target=_run_conversion)
    F-->>B: 200 JSON {task_id}
    T->>TM: mark_processing(task_id)
    T->>FFW: convert(input, output, 'mp3', callback)
    FFW->>FF: ffmpeg -y -i input.ogg -progress pipe:1 output.mp3

    loop FFmpeg 持续输出进度
        FF-->>FFW: stdout: out_time_ms=3500000
        FFW->>FFW: progress = 3.5s / 10s × 100 = 35%
        FFW->>TM: progress_callback(35.0)
        TM->>TM: task.progress = 35.0 (加锁)
    end

    loop 前端每500ms轮询
        B->>F: fetch GET /api/task/{id}
        F->>TM: get_task(id)
        TM-->>F: {status:'processing', progress:35.0}
        F-->>B: 200 JSON
        B->>B: progressBar.width = '35%'
    end

    FF-->>FFW: 进程退出 returncode=0
    FFW->>TM: progress_callback(100.0)
    T->>TM: mark_completed(task_id)

    B->>F: fetch GET /api/task/{id}
    F-->>B: {status:'completed', progress:100}
    B->>B: clearInterval(轮询) → 展示完成界面

    Note over User,FF: ④ 下载阶段
    User->>B: 点击"下载文件"
    B->>F: GET /api/download/{task_id}
    F->>F: send_file(outputs/xxx.mp3)
    F-->>B: 二进制文件流
    B->>User: 浏览器触发文件下载
Loading

模块职责一览

模块 文件 核心职责
配置 config.py 路径、大小限制(500MB)、保留时间(1h)
路由层 app.py 5 个 API 端点,请求分发,线程调度
格式定义 formats.py 12 种格式(6音频+6视频)的 MIME、编解码器、互转关系
FFmpeg 封装 ffmpeg_wrapper.py subprocess.Popen 调用 FFmpeg,-progress pipe:1 解析进度
任务管理 task_manager.py 内存字典 + 线程锁,任务生命周期(pending→processing→completed/failed)
前端页面 index.html 4 步骤面板:上传 → 选格式 → 转换中 → 下载
前端样式 style.css 暗色主题、拖拽区域、进度条动画
前端逻辑 main.js XHR 上传(带进度)、格式选择交互、500ms 轮询进度、状态机切换

关键设计决策

决策 选择 原因
上传方式 XHR(非 fetch) 需要 xhr.upload.onprogress 监听上传进度
转换执行 后台线程 避免阻塞 Flask 请求处理
进度通信 轮询(非 WebSocket) MVP 阶段,实现简单,500ms 间隔体验够好
任务存储 内存字典 + 线程锁 单进程足够,无需引入 Redis/数据库
进度来源 FFmpeg -progress pipe:1 原生支持,结构化输出,比解析 stderr 可靠
文件命名 时间戳前缀 防重名,同时保留原始文件名可读性

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages