# 视频预览格式扩展：AVI 等 18 种容器现在可以预览了

- 日期：2026-09-19
- 对象：下载中心 8899 预览/转码模块（`~/Downloads/download-server.js`）
- 起因：用户提问「下载平台不支持 AVI 的预览播放吗」

---

## 一、结论：改前确实不支持

改前 `previewKind()` 只认两类：

| 类别 | 改前支持的扩展名 |
|---|---|
| 浏览器原生播放（`PREVIEW_MIME`） | `.mp4` `.m4v` `.webm` `.ogv` |
| 服务端转码后预览（`TRANSCODE_EXTS`） | `.wmv` `.asf` `.rmvb` `.rm` |

**AVI（以及 MKV/MOV/FLV/MPG/TS/VOB/3GP…）不在任何一类里** → `previewKind()` 返回 `null` → 列表页不给预览链接，**点文件名直接下载**。
本机 dl-hub 里现有 **20 个 AVI、16 个 MKV、2 个 MOV** 属于这种情况。

### 为什么 AVI 不能直接播放
不是分辨率或编码单一问题，而是**容器**就不支持：Chrome/Firefox/Edge/Safari 都不解析 AVI 容器（AVI 常见搭配 Xvid/DivX/MPEG-4 Part2 视频 + MP3/AC3 音频，浏览器对这些编码组合也没有解码路径）。所以只能服务端转码成 MP4(H.264/AAC) 再播。

## 二、本次改动

### 1. AVI 等 18 种容器接入既有「ffmpeg→MP4」转码流水线
新增到 `TRANSCODE_EXTS`：
`.avi .divx .flv .f4v .mpg .mpeg .mpe .m2v .m1v .ts .m2ts .mts .vob .3gp .3g2 .ogm .dv`（加上原有 `.wmv .asf .rmvb .rm`）

复用现成能力（无需新写）：转码队列（同时 1 个）、进度百分比、缓存 7 天/上限 6GB、MP4 支持 Range 拖动播放、失败重试窗口。

### 2. MKV / MOV 改为「逐文件按编码判定」（新增能力）
MKV/MOV 是**容器可变**：H.264+AAC 的 MKV 浏览器能直接播（秒开），而 HEVC/AC3/DTS/ProRes 的就不行。所以：

- 列表页照常给出预览入口；
- 打开预览时用 `ffprobe` 看真实编码，判定规则：
  **视频 ∈ {h264, vp8, vp9, av1}** 且 **音频 ∈ {aac, mp3, opus, vorbis, flac, pcm}**（或无音频）→ 用原生 `<video>` 秒开；
  否则 → 自动切到服务端转码播放器。
- 判定结果按「路径 + size + mtime」缓存，避免每次打开都探测。
- 顺带给 `.mkv`/`.mov` 补了正确的内联 `Content-Type`（`video/x-matroska` / `video/quicktime`），原生播放更可靠。

### 3. 转码线程上限（`DL_TRANSCODE_THREADS`，默认 4）
改造过程中实测：不受限时 ffmpeg 吃满 **6 个核（~620% CPU）**，会拖慢同机其它服务。现固定 `-threads 4`（实测 ~372%），可用环境变量调整。

## 三、改后的格式支持矩阵

| 扩展名 | 预览方式 |
|---|---|
| `.mp4` `.m4v` `.webm` `.ogv` | ✅ 浏览器原生播放 |
| `.mkv` `.mov` | 🔍 打开时 ffprobe 判定编码：h264/vp8/vp9/av1 → 秒开；否则转码 |
| `.avi` `.divx` `.wmv` `.asf` `.rmvb` `.rm` `.flv` `.f4v` | 🔄 服务端转码后预览 |
| `.mpg` `.mpeg` `.mpe` `.m2v` `.m1v` `.ts` `.m2ts` `.mts` | 🔄 服务端转码后预览 |
| `.vob` `.3gp` `.3g2` `.ogm` `.dv` | 🔄 服务端转码后预览 |

## 四、验证（16/16 通过 + 真实文件验证）

用 ffmpeg 现造素材（已清理）：

| 用例 | 结果 |
|---|---|
| 列表页：AVI / MKV / 异常编码 MKV 均出现预览链接 | ✅ |
| AVI（mpeg4+mp3）→ 预览页使用转码播放器 iframe | ✅ |
| **实际跑完一次 AVI 转码**：状态 `done`、进度 100%、产出 MP4 可 `Range` 流式播放且 `Content-Type: video/mp4` | ✅ |
| MKV（h264+aac）→ 使用原生 `<video>`、不走转码、内联 `Content-Type: video/x-matroska`、支持 Range(206) | ✅ |
| MKV（mpeg4+ac3）→ 自动改用转码播放器 | ✅ |
| MOV（h264）→ 原生；MOV（mpeg4）→ 转码 | ✅ |
| 回归：既有 `.wmv` 仍走转码播放器 | ✅ |
| 回归：上传功能 23 项 | ✅ 23/23 通过 |
| 移动端列表页（同一套预览路由） | ✅ 200 且含 AVI 预览链接 |
| **真实文件**：`09-迅雷下载/ADN-027/[HD]ADN-027.avi`（1.8GB）与另一个 1GB AVI 均正确进入转码队列 | ✅ |
| 线程上限生效：`-threads 4` 出现在 ffmpeg 命令行，CPU 由 ~620% 降到 ~372% | ✅ |

---

# 追加：转码成本优化（同日 #2）——「先探测编码 + 换封装 + 边转边播」

用户追问：「AVI 不能实现原生播放吗？这些文件都很大，转码会占用大量时间和资源」。

## 1. 事实：AVI 容器无法原生播放，但**不等于必须重编码**

- **容器层面**：Chrome/Firefox/Edge/Safari 都不解析 AVI 容器 —— 这条无解。
- **编码层面**：才是成本的关键。实测用户两个真实 AVI：

| 文件 | 编码 | 时长 | 处理方式 | 耗时 |
|---|---|---|---|---|
| `[HD]ADN-027.avi` 1.69GB | **h264** + mp3 | 1:57:23 | **换封装（`-c copy`）** | **33 秒** |
| `ABS-167.avi` 0.95GB | **mpeg4(Xvid)** + mp3 | 1:59:19 | 视频重编码（音频直拷） | 约 25~40 分钟 |

> h264 的 AVI 只需把码流重新封装进 MP4 容器，**不解码不编码**：无画质损失、几乎不耗 CPU、秒级完成。此前无差别地一律重编码，等于白等 78 分钟。

## 2. 改动

### (1) 转码前先探测编码，自动选择处理方式
新增 `probeStreams()` / `planTranscode()` / `buildTrArgs()`：

| 判定 | 处理 |
|---|---|
| 视频 = h264 且 音频 = aac/mp3（或无音频） | **remux**：`-c copy` 换封装（秒级、零损失） |
| 视频 ≠ h264 但音频 = aac/mp3 | **reencode**：视频转 H.264，**音频直拷**（省一道音频编码） |
| 音频也不兼容 | reencode：视频 + 音频都重编码 |

- remux 万一失败（容器/码流不兼容）**自动回退重编码**，保证一定能出可播 MP4。
- 状态接口新增 `mode`（`remux`/`reencode`）与 `codecs`，播放器页据此显示「换封装中…（通常几十秒）」或「转码中…」。

### (2) 边转边播（live）：不再等整片处理完
- 新增路由 `?transcode=1&ta=live`：ffmpeg 输出 **fragmented MP4 直接推流**给浏览器（`-movflags frag_keyframe+empty_moov+default_base_moof -f mp4 pipe:1`）。
- 源可换封装时用 `-c copy` 边封装边推；否则实时重编码推送。
- 播放器页新增按钮：**「▶ 不想等？点这里『边转边播』立即观看」**。
- 限制：`DL_LIVE_LIMIT`（默认 2）个并发；每次观看各起一个 ffmpeg；进度条只能拖到已缓冲位置。

## 3. 实测（真实文件）

| 项 | 结果 |
|---|---|
| **换封装** `ADN-027.avi`（1.69GB） | **33 秒完成**（对比重编码约 78 分钟，**约 140 倍**）；产出 1722MB、`h264+mp3`、**时长 7043.076s vs 源 7043.109s（一致，零损失）** |
| 边转边播（h264 AVI，`-c copy` 推流） | **首字节 0.48 秒**；8 秒内推流 1286MB（磁盘速度） |
| 边转边播（Xvid AVI，实时重编码） | **首字节 0.58 秒** → 立刻可看，边转边看；编码速度约 5 倍实时，供片无压力 |
| 播放器页含「边转边播」按钮 | ✅ |
| 回归：视频预览 16 项 | ✅ 16/16 |
| 回归：mpeg4 AVI 正确判定 `mode=reencode` 并返回源编码 | ✅ |
| 回归：上传功能 23 项 | ✅ 23/23 |

## 4. 给大文件的建议（按省资源排序）

1. **先看编码**：页面会显示「换封装」还是「转码」。h264 的 AVI/WMV → 点开几十秒后即可流畅播放并缓存 7 天。
2. **必须重编码的大文件**：用播放器页的 **「边转边播」** —— 秒级起播，看到哪转到哪，不必等整片；缺点是进度条拖动受限。
3. **想要流畅拖动/回看**：让后台把它转码完成（缓存 7 天后秒开），或先做别的、稍后再看。
4. **最省服务器资源**：点下载到本地用本地播放器看（AVI 本地播放完全没问题，平台 CPU 零占用）。

## 5. 硬件编码说明（本机不可用）
本机是 i7-4720HQ（Haswell 核显），实测 `h264_vaapi` / `h264_qsv` **均初始化失败**（`iHD_drv_video.so` 不支持该代核显），`h264_nvenc` 无 N 卡 → **只能用 CPU 的 libx264**。若将来想大幅提速，可考虑把大文件转码任务丢给 GPU 机（192.168.31.31，RTX 5060 Ti 有 NVENC）。


## 五、使用说明

- 打开 AVI 等格式的预览：页面显示转码进度百分比，**首次转码**（1.8GB 约 15~30 分钟，4 线程）完成后自动开始播放；**之后 7 天内再打开秒开**（缓存 `<DL_ROOT>/../.dl-transcode-cache`，上限 6GB，自动淘汰最旧）。
- 同一时刻只跑 1 个转码任务，多个请求排队（页面显示队列长度）。
- 可调参数（写在 `dl-server.service` 的 `Environment=`）：`DL_TRANSCODE_THREADS`（默认 4）。
- 注意：转码是 CPU 密集型。若同时要用 ComfyUI/LLM 等重活，建议错峰点击预览。

## 六、相关文件
- 服务端：`~/Downloads/download-server.js`
  （`PREVIEW_MIME`/`TRANSCODE_EXTS`/`PROBE_VIDEO_EXTS`、`previewKind`、`needsTranscodeByProbe`、`videoElFor`、`sendPreview`/`sendFragment`、转码路由与 `TRANSCODE_THREADS`）
- 测试脚本：`/tmp/video-preview-test.mjs`（16 项视频预览测试，已存 `dl-hub/05-下载服务维护/tests/`）
