Sign inSign up

xdxy/media-shrink

By xdxy

•Updated about 10 hours ago

Image
0

325

xdxy/media-shrink repository overview

基于 ffmpeg 的媒体体积压缩服务。扫描媒体库 → 人工确认 → 批量转码,支持 Intel / NVIDIA / AMD 硬件编码。

网页操作,无需命令行。

English — MediaShrink is a self-hosted web app that scans your media library, shows each file's codec, bitrate and estimated output size, and batch-transcodes only the files you tick. It ships AV1 / HEVC / H.264 presets (plus a fully manual mode), probes hardware encoders by actually encoding a test clip at startup (Intel QSV / VAAPI, NVIDIA NVENC, AMD VAAPI, or CPU), injects sidecar subtitles as soft tracks without burning them in, lets you pick audio tracks per file, and outputs MKV by default. Source files are never touched unless you explicitly rename (.old) or delete them afterwards. Documentation is in Chinese. MIT licensed.


⁠功能对照

需求实现
用 ffmpeg 压缩媒体体积完整转码流水线,保留章节与元数据
扫描出来让人确认再压扫描后列表展示原体积 / 分辨率 / 码率 / 预估体积 / 建议,逐项勾选,开始前二次确认
识别是否已是 AV1 / HEVC扫描时按 codec_name 分组:非 AV1/HEVC 的进「需要转码」表;已是 AV1/HEVC 的单独进另一张表,默认不勾选
支持 Intel 核显硬编码QSV(av1_qsv / hevc_qsv / h264_qsv)+ VAAPI(*_vaapi)双通道
也支持 N 卡 / A 卡NVENC(av1_nvenc / hevc_nvenc / h264_nvenc)、AMD VAAPI(*_vaapi)
同文件夹字幕自动加上自动匹配 影片名.*.srt/ass/vtt/sup,默认全部勾选,可逐条取消
字幕不烧录只做软字幕轨(mkv copy / mp4 mov_text),画面始终原样重编码,不做任何字幕叠加
默认输出 MKV无论源文件是 mp4 还是 avi,统一封装成 .mkv:不限视频/音频编码,PGS/ASS 图形与特效字幕都能装,不会为了塞进 mp4 去重编码音轨
音轨可选逐条勾选要保留的音轨(按语言 / 声道 / 码率 / 默认标记),可选「保留全部 / 仅默认轨 / 仅最高规格 / 全部去掉」
压缩完可处理源文件每个完成的任务都能选「保留 / 改名加 .old 后缀 / 删除」,删除需二次勾选确认;只有输出校验通过才允许操作
较多预设可选16 个预设:AV1 / HEVC / H.264 各有极致压缩→视觉无损多档,另有动画优化、HDR 保留、仅重封装
保留手动配置预设之外还有一个「手动配置」:编码格式、CRF 或目标码率、编码速度、分辨率上限、位深、音频编码都能自己定,可全局生效也可只改某一行
优先 AV1 / HEVC全部预设基于 AV1 与 HEVC,H.264 仅作兼容兜底
优先原分辨率默认完全不缩放;只有明确选「最高 1080p / 720p」预设才会缩小
画质优先默认预设是 HEVC · 高画质(CRF 21);字幕不烧录;音轨默认直接复制;默认保留 10bit / HDR

硬件编码采用「实测」而非「看 ffmpeg 支持列表」:启动时对每个候选编码器真跑一次 0.3 秒编码,跑通才算可用。所以不会出现"界面显示支持、一压就报错"。


⁠快速开始

镜像已发布在 Docker Hub:xdxy/media-shrink:latest(linux/amd64,约 1.0 GB)。

⁠方式一:直接拉镜像(推荐)
docker run -d --name media-shrink --restart unless-stopped \
  -p 8420:8000 \
  -v /你的/影视目录:/media \
  -v /你的/appdata/media-shrink:/data \
  --device=/dev/dri:/dev/dri \
  xdxy/media-shrink:latest

打开 http://localhost:8420⁠ ;也可以直接 http://localhost:8420/?scan=/media/movies 一步进到某个目录的扫描结果(适合收藏成书签)。

参数说明
-p 8420:8000WebUI 端口,左边随便改,容器内固定 8000
-v ...:/media媒体目录,容器内路径自己定,但必须出现在 MEDIA_ROOTS 里
-v ...:/data任务记录,删掉只会丢历史,不影响压缩
--device=/dev/driIntel 核显 / AMD 的硬编入口。没有核显就整行删掉,Docker 找不到设备会拒绝启动

默认输出写回源文件同目录,不用单独挂输出目录;想统一输出再加 -v /你的/输出:/output 并设 OUTPUT_DIR=/output。

⁠方式二:从源码构建
git clone https://github.com/apadn/media-shrink.git
cd media-shrink

docker compose up -d --build      # compose 里已经配好路径和参数

首次构建要下载 ffmpeg 静态构建(约 160 MB),成品镜像约 1.0 GB(静态 ffmpeg 二进制占 340 MB,换来零依赖、编码器齐全)。构建默认用中科大 apt 源(实测比官方源快一个数量级),海外网络改 APT_MIRROR=deb.debian.org。

⁠界面

扫描结果

逐项确认

预设之外还有「手动配置」,全参数自己定(编码 / CRF 或码率 / 速度 / 分辨率 / 位深 / 音频):

手动配置

也可以只在某一行展开、单独改这一个文件,不影响其他行:

单行手动配置

压缩完成后可以在任务卡片上直接处理源文件(保留 / 改名加 .old / 删除):

完成后处理源文件

删除需要勾选确认,弹窗会列出完整路径:

删除确认


⁠扫描结果怎么读

扫描后是两个独立的表:

① 需要转码 —— 编码不是 AV1 / HEVC 的文件(H.264、MPEG-2、VC-1、WMV…)。 默认勾选其中「建议压缩」的项。这一组是压缩收益的主要来源。

② 已是 AV1 / HEVC —— 已经是目标编码的文件。 默认全部不勾选。仍然列出来是因为三种情况值得重新处理:

  • 码率虚高(比如老片子用 HEVC 但码率 20 Mbps),重压还能再省一半
  • 容器里没有字幕,而同目录有匹配的外挂字幕 → 用「仅重封装」秒级加进去
  • 音轨太多(国配 + 原声 + 导评),只留需要的能省不少空间

每行前面的 ▸ 展开可以单独改字幕、音轨、CRF、预设。

预估列

⁠预估体积是怎么算的

预估列直接显示「预计省 x%」,因为它只是经验估算,不是精确值——真实结果只有转码完才知道。

模型由两部分合成:

  1. 理论码率 —— 按 bpp × 宽 × 高 × 帧率 算,bpp 由编码器和质量档决定(例如 HEVC 高画质 0.056、AV1 均衡 0.032)。它回答的是"这个编码器在这个质量档下大概需要多少码率"。
  2. 源码率锚定 —— CRF 编码的实际码率由画面复杂度决定,而同分辨率下源码率正是复杂度最直接的代理。所以再算一个 源码率 × 编码器效率比(H.264→HEVC 取 0.72,→AV1 取 0.58),与理论值取几何平均。

只做第 1 步会两头失真:

  • 源是 3 Mbps 的 1080p H.264 → 理论值算出 3.2 Mbps,界面显示"体积还变大",其实这种已经被压过的片源用 HEVC CRF 21 通常还能省 10~25%
  • 源是 20 Mbps 的高码率母带 → 理论值同样算出 3.2 Mbps,严重低估

另外源码率是硬上限(留 2% 封装余量):重编码不会凭空造出码率,所以估算值不会超过源文件太多。±3% 以内显示「体积基本持平」——这个区间属于估算误差,说成"增加"只会让人以为估算坏了。

选「目标码率」模式时不受以上影响,体积是直接算出来的(码率 × 时长),此时 confidence 为 high。


⁠硬件加速配置

⁠Intel 核显(最常见)
devices:
  - /dev/dri:/dev/dri

宿主机确认:

ls -l /dev/dri            # 应该有 renderD128
sudo apt install vainfo   # 然后
vainfo                    # 能看到 VAProfileXXX / VAEntrypointEncSlice 即可

容器起来后,界面顶部会显示 Intel 核显 · Intel iHD driver ...,且 AV1/HEVC: hevc_qsv 之类的徽章为绿色。

  • 11 代及更早:支持 HEVC / H.264 硬编
  • 11~13 代部分型号:支持 AV1 解码
  • Arc / 14 代+(含核显 Xe2):支持 AV1 编码(av1_qsv)
⁠NVIDIA

宿主机装 nvidia-container-toolkit,然后把 compose 里 deploy: 段取消注释:

deploy:
  resources:
    reservations:
      devices:
        - driver: nvidia
          count: all
          capabilities: [gpu, video, compute, utility]

capabilities 里的 video 必须有,否则 NVENC 不可用。

⁠AMD

同样是 /dev/dri:

devices:
  - /dev/dri:/dev/dri

Linux 下 ffmpeg 走 VAAPI(hevc_vaapi)。AV1 编码需要 RDNA3 及以上(RX 7000 系)。

注意:*_amf 编码器只在 Windows 版 ffmpeg 里存在,Linux 容器内不会出现,这是驱动层限制,不是本项目的问题。

⁠强制用 CPU

界面上「编码器」选 仅 CPU,或对单个文件在展开行里指定。CPU 编码(libsvtav1 / libx265)同码率画质更好,但慢 5~20 倍。


⁠使用流程

  1. 填写目录 —— 点「浏览」可视化选,或直接输入容器内路径(如 /media/movies)
  2. 选默认预设 —— 默认是 HEVC · 高画质(画质优先);扫描时按这个预设估算体积
  3. 点扫描 —— 结果分成「需要转码」和「已是 AV1 / HEVC」两张表,各自有全选 / 只选建议 / 清空
  4. 逐项确认(可选) —— 点文件名前的 ▸ 展开:
    • 音轨:逐条勾选,或用「仅默认轨 / 仅最高规格 / 全部去掉」快速筛选;音频编码可选 复制 / AAC / Opus
    • 字幕:勾选要注入的外挂字幕,选「作为软字幕轨」或「不要字幕」(没有烧录选项)
    • 手动配置:把这一行的预设换成「手动配置」,就能单独给这个文件调编码 / CRF / 码率 / 速度 / 分辨率 / 位深
    • 单独换预设
  5. 点「开始压缩」 —— 弹出汇总(体积变化、编码器、音轨数量、字幕数量、输出格式),确认后入队
  6. 底部任务队列实时显示进度、速度、剩余时间,可随时取消

默认输出到源文件同目录,文件名加 _compressed 后缀,绝不会覆盖源文件;删不删源文件由你在压缩完成后逐个决定。

⁠输出容器

默认一律封装成 MKV,源文件是 mp4 / avi / ts 都一样。原因是 MKV 没有编码和字幕类型的限制:

  • 视频:AV1 / HEVC / H.264 都能装,-c:v copy 时不需要换容器就重编码
  • 音频:AC3、DTS、TrueHD、FLAC、Opus 全部直接 copy;mp4 遇到 DTS 就只能转成 AAC,白损失一次
  • 字幕:PGS / VobSub 图形字幕、ASS 特效字幕都保留原样;mp4 只能存文本字幕,图形字幕只能丢弃

想改回跟随源文件或输出 mp4,改环境变量即可(装不下时会自动退回 mkv,不会让任务失败):

OUTPUT_CONTAINER: "auto"   # 跟随源文件:mp4→mp4、webm→webm、其余→mkv
OUTPUT_CONTAINER: "mp4"    # 强制 mp4(遇 PGS/DVD 图形字幕的文件自动用 mkv)
⁠压缩完怎么处理源文件

不会自动删任何东西。每个压缩成功的任务下方会出现三个选项:

选项结果什么时候用
保留什么都不做,只标记成已确认想留着原片
改名加 .oldmovie.mkv → movie.old.mkv推荐。文件只是多个后缀,随时能改回来;播放一圈确认没问题再删
删除…永久删除,不进回收站已经确认过压缩结果,要立刻腾空间

任务队列右上角还有一批:源文件批量改名 .old / 批量删除源文件…,适合压完几十个文件后一次性收拾。

删除的按钮是「删除…」(带省略号),点了会弹出确认框列出完整路径,还要勾一个「我确认这些源文件可以永久删除」才会真正执行。

服务端还有一层兜底,下面任何一条不满足都拒绝操作源文件(宁可不动手):

  • 任务状态必须是 done
  • 输出文件必须存在、非空
  • 输出体积不能小于 1MB,也不能小于源文件的 0.5%(基本可以断定是截断的失败产物)——这种情况会提示先播放确认
  • 源文件与输出文件不能是同一个文件
  • 同一个任务不能处理两次

想彻底禁用删除,设 ALLOW_SOURCE_DELETE: "0",界面上的删除按钮直接变灰,API 也会返回 403。

改名只动视频文件本身,不会碰同目录的外挂字幕。movie.old.mkv 下次扫描会被识别成 .old 归档并标为「跳过」,不会被当成新片子再压一遍。


⁠关于画质

三个默认行为都是为了不损画质:

做法原因
字幕只做软轨烧录(hardsub)要把字幕合成到画面再重新编码,等于在压缩损失之上再叠一次损失。软字幕轨由播放器实时叠加,画面保持纯净,还能开关
音轨默认 copy音轨重新编码会损失细节,而音频体积通常只占 5~10%,省不了多少
默认保留 10bit / HDR源是 10bit 就输出 10bit,HDR10 / HLG 元数据与色彩标记原样传递

需要更极致的画质可以在展开行里把 CRF 调低(HEVC 18~20 已经很接近视觉无损)。


⁠预设一览

预设适用
HEVC · 高画质(推荐)默认。画质优先,兼容性最好,硬编码支持最广
AV1 · 高画质 / 视觉无损收藏级片源,同画质比 HEVC 再小 20~30%
AV1 · 均衡通用归档
AV1 · 极致压缩空间极度紧张,画质有可见损失
AV1 · 动画/番剧关胶片颗粒合成,再省 15~25%
AV1 · 4K/HDR 保留10bit + HDR 元数据原样保留
HEVC · 均衡兼容性与压缩率平衡
HEVC · 极致压缩激进档
HEVC 10bit · HDR 原样保留强制 10bit,HDR10/HLG 完整保留
HEVC · 动画/番剧平涂色块优化
H.264 · 兼容优先 / 小体积老设备、网页播放
HEVC · 最高 1080p / AV1 · 最高 720p主动降分辨率(唯一会缩放的预设)
仅重封装只换容器 + 加字幕 + 裁音轨,画质零损失,秒级完成
手动配置上面都不合适时的出口:编码、CRF/码率、速度、分辨率、位深、音频全自己定

CRF 参考:HEVC 1824,AV1 2330(画质优先取小值),数值越小画质越好、体积越大。


⁠手动配置

预设覆盖的是常见组合,剩下的特殊情况走「手动配置」——它在预设下拉框里,位置和普通预设一样,选中后下方会展开参数区。

参数可选值说明
编码格式AV1 / HEVC / H.264选完自动按你的硬件匹配编码器(QSV / NVENC / VAAPI / CPU)
码率控制恒定质量(CRF) / 目标码率CRF 画质稳定、体积浮动;目标码率体积可控
CRF 质量滑杆 + 数字输入,0~63切换编码格式时上限自动跟着变
视频码率200~200000 kbps仅「目标码率」模式显示,实际编码会按 1.5× 峰值 / 2× 缓冲做 VBR
编码速度极速 / 快 / 均衡 / 慢 / 最慢映射到各编码器自己的参数:x265 ultrafast…veryslow、SVT-AV1 12…2、NVENC p2…p7、QSV veryfast…veryslow
分辨率上限保持原分辨率 / 2160p / 1440p / 1080p / 720p只在源分辨率更高时缩小,不会放大
位深跟随源文件 / 强制 8bit / 强制 10bit强制 10bit 会走 yuv420p10le / p010le
音频直接复制 / 转 AAC / 转 Opus和展开行里的「音频编码」是同一套,那里选过就以那里为准

作用范围有两种:

  • 全局:把顶部的「默认预设」选成「手动配置」,参数区出现在扫描面板里,所有文件都按它压
  • 单文件:把某一行的预设换成「手动配置」,展开该行单独调;改过之后这行就脱离全局,点「恢复跟随全局」能退回去

配置会存在浏览器里(localStorage),下次打开还是你上次调的值;「恢复默认」按钮回到出厂参数(HEVC / CRF 21 / 均衡 / 原分辨率 / 音频复制)。

体积预估对两种模式都准:CRF 模式走经验公式(±20% 量级),目标码率模式直接按 码率 × 时长 算,基本就是实际大小。

注意:手动配置里选了什么不改变扫描分组——分组看的是源文件编码,不是目标编码。


⁠环境变量

变量默认说明
MEDIA_ROOTS/media,/data,/output允许浏览/扫描的根目录(安全边界)
OUTPUT_DIR空留空=输出到源文件旁;填 /output=统一输出目录
OUTPUT_SUFFIX_compressed输出文件后缀
OUTPUT_CONTAINERmkv输出容器:mkv / mp4 / webm / auto(跟随源文件)
SOURCE_ACTIONkeep源文件默认处理:keep / rename / delete(界面仍逐个确认)
SOURCE_OLD_SUFFIX.old归档后缀,movie.mkv → movie.old.mkv
ALLOW_SOURCE_DELETE1设 0 禁用删除源文件,只留「保留 / 改名」
CONCURRENT_JOBS1并行任务数。硬件编码建议 1~2
VAAPI_DEVICE/dev/dri/renderD128VAAPI 渲染节点
MIN_SIZE_MB1扫描时忽略小于此体积的文件
APT_MIRRORdeb.debian.org构建期 apt 源
FFMPEG_TAGlatest构建期 ffmpeg 版本(BtbN 构建的 tag)

⁠常见问题

界面显示「未挂载 /dev/dri」 compose 里的 devices 没生效,或宿主机没有 /dev/dri(虚拟机 / 群晖需开启核显直通)。

编码器测试全部失败,但 /dev/dri 存在 容器内缺 VAAPI 驱动。进容器跑 vainfo 看报错;群晖/Unraid 常需要额外映射 /dev/dri 的权限。

AV1 硬编不可用 只有 Arc 独显、部分 14 代+ 酷睿、RDNA3 的 A 卡支持 AV1 硬件编码。其余平台会回落到 libsvtav1(CPU),速度慢属正常。

已经是 HEVC 的片源,勾了压缩反而变大了 原片码率已经很低(第二张表里会标「不建议」)。这种情况选「仅重封装」或直接跳过。

保留的音轨在播放器里显示「未知语言」 转码时会显式把源语言标签(chi / eng / jpn…)写回输出流。如果源文件本身就没有语言标签,界面上也只能显示编码格式,需要先用 mkvtoolnix 打标签。

能不能自动删源文件 不会自动删。压缩完成后每个任务会出现「保留 / 改名加 .old / 删除…」三个选项,必须你手动点;删除还要勾确认框,并且输出文件得先通过完整性检查。觉得太危险就设 ALLOW_SOURCE_DELETE=0 直接关掉删除,只留改名归档。


⁠目录结构

media-shrink/
├── Dockerfile            # 多阶段:BtbN ffmpeg 静态构建 + Debian slim 运行时
├── docker-compose.yml
├── requirements.txt
├── LICENSE
├── tools/                # make_favicon.py 生成站点图标、github-actions-ci.yml 可选的 CI 配置
├── docs/                 # 界面截图
├── test/e2e.sh           # 端到端自检
└── app/
    ├── main.py           # FastAPI 路由
    ├── config.py         # 环境变量配置
    ├── probe.py          # ffprobe 解析(含音轨/字幕流元数据)
    ├── presets.py        # 预设矩阵 + 编码参数映射 + 体积估算
    ├── hwdetect.py       # 硬件实测探测
    ├── scanner.py        # 目录扫描 / 编码分组 / 字幕匹配 / 建议
    ├── transcoder.py     # 命令构建(音轨精确映射)+ 队列 + 进度
    └── static/           # 前端(原生 JS,无构建)

⁠构建与推送镜像

USER=xdxy          # 你的 Docker Hub 账号
VERSION=1.0.2      # 跟 app/config.py 里的 APP_VERSION 保持一致

docker build -t $USER/media-shrink:$VERSION -t $USER/media-shrink:latest .

docker login
docker push $USER/media-shrink:$VERSION
docker push $USER/media-shrink:latest

latest 和版本号指向同一个镜像 ID,别人 docker pull $USER/media-shrink:latest 就能用;想回滚旧版也有具名标签可拉。

本机是 x86_64,docker build 出来的就是 linux/amd64,普通 NAS / 服务器都能直接用。要同时出 arm64(树莓派、ARM 版 NAS)就换成 buildx:

docker buildx build --platform linux/amd64,linux/arm64 \
  -t $USER/media-shrink:latest -t $USER/media-shrink:$VERSION --push .

1 GB 的镜像推一次几分钟,取决于上行带宽。改完代码只重建最后几层,docker push 也只会推变化的分层。

⁠API

方法路径说明
GET/api/hw硬件与编码器探测结果
POST/api/scan扫描目录,返回带 codec_family 分组信息的 items
POST/api/estimate重算体积。可传 items: [{path, preset_id, manual, tracks}] 逐条指定;也兼容旧的 paths + preset_id + tracks
POST/api/jobs创建转码任务(audio_tracks 为流 index 数组,null 表示全部)
GET/api/jobs任务列表与进度
POST/api/jobs/{id}/cancel取消任务
POST/api/jobs/source处理源文件:{job_ids, action: keep/rename/delete, confirm};删除必须 confirm=true

⁠自检

bash test/e2e.sh

脚本会自动走完一整条链路:造测试素材(H.264 单音轨 / H.264 无音轨 / H.264 双音轨 / 已是 HEVC,含中英文外挂字幕)→ 起容器 → 扫描并断言编码分组 → 重封装注入字幕(断言视频流未被重编码)→ 只保留一条音轨(断言输出音轨数)→ HEVC 压缩(断言编码与分辨率)→ 手动配置(断言编码器选择、码率预估、输出 10bit / 720p / 900kbps)→ 源文件保留 / 改名 / 删除的安全校验。需要 Docker 与 python3(解析 JSON),改动代码后跑一遍即可回归。


⁠参与贡献

提 Issue 或 PR 都欢迎。改动前建议先跑一遍 bash test/e2e.sh;涉及前端改动的,说明一下改的是哪个交互。

本项目只做「体积压缩 + 元数据整理」,不追求成为完整的转码工作台,功能建议请围绕这条主线。


⁠许可证

MIT⁠ © 2026 apadn

镜像里打包的 ffmpeg⁠ 是 GPL 构建(含 libx265 / libsvtav1 / x264 等), 分发镜像时需同时遵守其 GPL 条款;本项目自身的代码仍是 MIT。

Tag summary

Content type

Image

Digest

sha256:7ff2db6cf…

Size

277.3 MB

Last updated

about 10 hours ago

docker pull xdxy/media-shrink