copyparty bin/mtag 详解:用独立脚本扩展媒体标签(BPM、调性、哈希、EXIF 剥离)的 MTP 插件机制
bin/mtag/ 目录下的程序是 copyparty 媒体索引系统(up2k/mtag)的自定义标签插件集合:每个脚本以"接收一个文件绝对路径、向 stdout 输出标签值"的极简契约运行,通过 -mtp 选项挂接后,copyparty 会在索引时调用它们,把 BPM、音乐调性(Camelot)、音视频流哈希、PE 可执行文件信息等结果写进元数据库。读完后你将掌握如何把第三方检测工具接入 copyparty 的标签流水线、如何用 volflags 限定插件作用域,以及首次启用 mtp 时必须执行的清库重扫步骤。
机制前提:MTP 插件是什么
copyparty 的媒体索引有两类"解析器":内置的后端(Mutagen 或 FFprobe,见 mtag 后端选择逻辑),以及用户自供的外部程序。-mtp(Media Tag Parser)选项注册的正是后者。
从 MParser 的解析逻辑看,一条 -mtp key=f,audio-key.py 的声明会被拆解为:
=号左侧是标签名(可用逗号声明多个标签,此时脚本必须输出 JSON,见下文);=号右侧逐项解析修饰符,其中:f:force,检测到的值覆盖已有标签值;t<n>:超时秒数(默认 60);e<ext>:只对指定扩展名的文件运行;a[y|n|r|d]:音频要求——y仅音频、n仅非音频、r要求、d不关心;c0-3:捕获 stdout/stderr 的数量;k[t|m|n]:超时时杀主进程/进程树/不杀;p<n>:优先级,值越大越晚执行(高优先级插件还能通过 stdin 拿到前序插件的 JSON 结果);- 最后剩余部分即脚本路径。
执行发生在 MTag.get_bin:脚本以 [python3, 脚本路径, 文件绝对路径] 方式调用(bin 以 .py 结尾时自动补上 Python 解释器),stdout 去除首尾空白后写入标签;若标签名含逗号,则对 stdout 做 json.loads 并按标签名取字段——这就是 cksum.py 和 exe.py 输出 JSON 的原因,一个解析器可以为多个标签同时供值。
批量扫描的调度在 up2k._run_all_mtp / _run_one_mtp:启动扫描时按卷(volume)逐一执行 mtp,并在 mt 表里用 t:mtp 标记记录进度(mtpq 计数)。一个关键行为在源码中有据可查:若某文件在 mt 表中已有任何标签,该文件的 mtp 会被跳过——这正是 README 强调"首次带新 mtp 选项启动时必须先用 -e2tsr 清掉标签"的原因。
功能前提:-e2ts 与后端依赖
bin/mtag/README.md 明确指出:这些插件要工作,copyparty 的 -e2ts(扫描已有文件的标签)必须可用,也就是至少安装 apt install ffmpeg 或 pip3 install mutagen 之一。对照 cfg.py 中的 e2 系列选项定义:
| 选项 | 含义 |
|---|---|
e2d |
启用数据库;文件可搜索 + 上传可撤销 |
e2ds |
启动时扫描可写目录中的新文件(隐含 -e2d) |
e2dsa |
启动时扫描所有目录(隐含 -e2d) |
e2ts |
启动时扫描已有文件的标签(隐含 -e2t) |
e2tsr |
删除数据库中全部元数据(完整重扫,隐含 -e2ts) |
README 同时提醒:如果你的目标只是"上传后跑个脚本",可以完全绕开这套复杂机制,改用更简单的 event hooks(不需要 -e2ts 也不需要 ffmpeg)。
插件清单:按许可证与实现方式分类
README 把插件按"依赖的许可证兼容性"分成三档,这一分法对在生产环境选用插件时判断法律风险很实用。
引入 GPL 代码的插件(import GPL)
- audio-bpm.py:检测音乐 BPM。实现流程为:先调
ffmpeg把音频重采样为 22050 Hz 单声道 f32le 裸数据(只取前 360 秒)写入临时文件,再用vamp.collect(d, 22050, "beatroot-vamp:beatroot")(BeatRoot Vamp 插件,GPL2)拿 beat 时间戳,然后去掉前 20%、保留到 75% 的间隔序列,用60 * (len(bds) / sum(bds))估算 BPM 并打印两位小数;若 BeatRoot 失败则降级到vamp-example-plugins:fixedtempo。代码注释里标注了主路径约 98%、降级路径约 73% 的测试准确率(针对 jcore 曲库)。 - audio-key.py:检测音乐调性。同样先用
ffmpeg截取前 300 秒转 s16 格式,优先用 PyPI 的keyfinder(Mixxx 的 libkeyfinder 分支,GPL3)输出 Camelot 表示(如8B),否则回退调用keyfinder-cli -n camelot。
调用 GPL 外部程序的插件(法律上对多数用途无碍)
-
media-hash.py:生成音视频流内容的校验和。原理是
ffmpeg -f framemd5 -逐帧计算 md5,再按流序号对帧哈希做 SHA-512,取前 12 字节转 URL-safe base64,输出{"ahash": ..., "vhash": ...}。它的价值在于内容级指纹:文件改名、改封装、改文件名不影响流哈希,可配合 up2k 的 dedup 思路做重复媒体识别。 -
image-noexif.py:上传照片时剥离 EXIF/IPTC/XMP 元数据。它调用
exiftool -exif:all= -iptc:all= -xmp:all= -P -o noexif/,把去元数据副本写入noexif/子目录,再与原文件逐字节比较,输出clean(原本无元数据)、exif(已剥离)或failed。README 中解释过为何写子目录而不是原地修改:原地改动会导致 up2k.db 里保留旧哈希直到下次重扫,重复上传会被改名保留为副本;换成"原图备份到 exif/ 子目录 + 原地修改"的方案则会导致重扫前 db 与新文件不同步、后续相同图片被 dedup 链接到修改后的副本。该脚本自带一条完整示例配置:-v/mnt/nas/pics:pics:rwmd,ed:c,e2ts,mte=+noexif:c,mtp=noexif=ejpg,ejpeg,ad,bin/mtag/image-noexif.py其中
mte=+noexif:c把noexif追加为已知标签,ejpg,ejpeg限定扩展名,ad表示"不管 FFmpeg 是否认为它是音频都解析"。
无任何许可证问题的插件
- cksum.py:计算多种校验和,配置串
crc32 md5 md5b sha1 sha1b sha256 sha256b sha512/240 sha512b/240中b后缀表示 base64 编码、/n表示截断到 n 位,输出 JSON。文件自身的 docstring 给出了用法示例:-mtp crc32,md5,sha1,sha256b=ad,bin/mtag/cksum.py(多标签 +ad不关心音频类型)。 - exe.py:抓取
.exe/.dll的 PE 信息,是"单解析器多标签"的官方示例。用pefile解析(自定义 PE2 子类 关掉了一堆昂贵的目录解析以提速),输出arch(x86/x64)、built(编译时间戳)、ui(GUI/cmdline)、cksum(可选头校验和),若存在版本资源还会追加ver(FileVersion/ProductVersion)和orig(OriginalFilename/InternalName)。 - wget.py:把 copyparty 变成文件下载器——向网页的 message/pager 表单 POST 一个 URL,即以
put-xxx.bin(内容为 urlencoded 的 URL,msg=前缀)落盘,mtp 触发后脚本校验协议(仅 http/https/ftp/ftps)、wget --trust-server-names下载,期间留下-- DOWNLOADING name占位文件,成功后删除 .bin、失败留下-- FAILED TO DOWNLOAD name。注意该文件头部已标注 DEPRECATED,README 指明其替代品是 event hook 版本。
危险插件:very-bad-idea.py
very-bad-idea.py 与 meadup.js 插件配合,可以把 copyparty 变成一个粗糙但极灵活的"Chromecast 克隆":
- 任何通过 Android 端应用上传的文件或链接都会在服务器上被执行——这意味着任何拿到上传通道的人都可能投递恶意代码,README 的原话是"protect this with a password and keep it on a LAN!"(务必加密码、且只留在内网);
- 它会顺带给 basic-upload 标签页加一个虚拟键盘,方便"沙发派"遥控操作;
- README 同时推荐了 kamelåså 作为更成熟且安全得多的替代方案(该段为原文档的外部项目推荐,此处仅转述,不保证当前状态)。
依赖安装
两条路线:
- 跑
install-deps.sh:支持 Windows(msys2-mingw64)/Linux/macOS(macOS 需 macports)。脚本会pip install --user keyfinder vamp,然后按需从源码构建 mixxxdj/libkeyfinder(装到~/pe/keyfinder)、vamp-plugin-sdk(装到~/pe/vamp-sdk)、beatroot-vamp 插件(装到~/vamp),并用patchelf --set-rpath修 keyfinder 的.so加载路径,甚至会在 Python 侧的 keyfinder 模块顶部注入一行ctypes.cdll.LoadLibrary(...)。也支持install-deps.sh keyfinder|vamp|soundtouch单装。 - 直接用发行版包(README 认为更优):发行版包
numpy vamp-plugin-sdk beatroot-vamp mixxx-keyfinder ffmpeg,加 pip 的keyfinder vamp。
从 copyparty 接入
全卷生效
启动时带上扫描选项,再任意组合 mtp:
copyparty -e2dsa -e2ts \
-mtp key=f,audio-key.py \
-mtp .bpm=f,audio-bpm.py \
-mtp ahash,vhash=f,media-hash.py
要点(与源码行为一一对应):
f使新值覆盖已有标签(对应 MParser.force);- 标签名里的
.前缀表示数值型标签——内置 tagmap 中.bpm、.tn、.dur等都是这类,见 MTag 的 tagmap 定义,.bpm会同时匹配原始标签bpm/tbpm/tmpo/tbp,key会匹配initial-key/tkey/key; - 脚本路径按启动 copyparty 的当前目录解析,不在该目录就写相对/绝对路径(如
bin/mtag/audio-key.py); - 首次启用新 mtp 时,用
-e2tsr代替-e2ts:-e2tsr会删库重扫(见 cfg.py 选项表),否则已有标签的文件会整卷跳过 mtp。
仅对单个卷生效(volflags)
把选项挂在 -v 的卷条目后面即可,:c 前缀表示"仅对本卷生效":
copyparty -v /mnt/nas/music:/music:r:c,e2dsa:c,e2ts \
:c,mtp=key=f,audio-key.py \
:c,mtp=.bpm=f,audio-bpm.py \
:c,mtp=ahash,vhash=f,media-hash.py
实用技巧:定向删除标签并重扫
README 给了一个 sqlite 技巧:删除 blog* 前缀下所有文件的标签并让它们重新进入扫描队列(mt 表存标签,up 表存文件,w 是 16 进制路径 id):
sqlite3 .hist/up2k.db "delete from mt where w in (select substr(w,1,16) from up where rd like 'blog%')";
删除后这些文件不再满足"已有标签即跳过"的条件,下一次 mtp 扫描就会重新为它们跑全部解析器。
小结与选型建议
- 只想"上传后触发脚本"(如 image-noexif 的用途):优先考虑 event hooks,免去
-e2ts/ffmpeg 门槛; - 想让整个存量曲库获得 BPM/Camelot 标签:
-e2dsa -e2ts(首次-e2tsr)+ 两个audio-*.py,BPM 插件需要 vamp 栈,调性插件需要 keyfinder; - 想做内容级查重:
media-hash.py的流哈希优于文件哈希,能穿透改名与重封装; - 需要多标签一次性写入:参照
cksum.py/exe.py的 JSON 契约写自己的脚本,用逗号标签名注册。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00