No description
  • Go 64.6%
  • Python 28.1%
  • Shell 6%
  • Dockerfile 0.5%
  • Makefile 0.4%
  • Other 0.4%
Find a file
formatjn2019 61834ced04 feat: add subtitle_span support and fix episode numeric sort order
- Add subtitle_span field to match API, allowing one subtitle to cover
  multiple consecutive media files (e.g. BD with 2 episodes per disc)
- Replace string-based episode sort with numeric-aware LessOrder,
  fixing "10 before 2" ordering in all scan/match/group paths
- Add parseOrderNumber helper that handles OVA/PV as non-numeric

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 00:06:25 +08:00
cmd 结构优化 2026-03-15 05:09:35 +08:00
deployments/docker feat: publish matchsubtitles runtime base image 2026-04-12 19:50:37 +08:00
docs refactor: 重构主链路并修正蓝光主剧集识别 2026-03-15 03:13:21 +08:00
internal feat: add subtitle_span support and fix episode numeric sort order 2026-06-03 00:06:25 +08:00
legacy/python refactor: 重构主链路并修正蓝光主剧集识别 2026-03-15 03:13:21 +08:00
scripts refactor: 重构主链路并修正蓝光主剧集识别 2026-03-15 03:13:21 +08:00
.dockerignore 镜像构建 2026-03-23 00:11:02 +08:00
.gitignore refactor: 重构主链路并修正蓝光主剧集识别 2026-03-15 03:13:21 +08:00
.woodpecker.yml feat: publish matchsubtitles runtime base image 2026-04-12 19:50:37 +08:00
AGENTS.md chore: 添加 AGENTS.md 并更新 .gitignore 2026-03-09 23:32:31 +08:00
auto_test.cmd 命令自动测试脚本 2023-05-16 10:12:46 +08:00
go.mod 结构优化 2026-03-15 05:09:35 +08:00
go.sum 结构优化 2026-03-15 05:09:35 +08:00
LICENSE Initial commit 2023-02-01 09:53:07 +08:00
Makefile refactor: 重构主链路并修正蓝光主剧集识别 2026-03-15 03:13:21 +08:00
README.md feat: add subtitle_span support and fix episode numeric sort order 2026-06-03 00:06:25 +08:00

MatchSubtitles (Go Rewrite)

目标:保持核心能力(字幕与媒体自动匹配/重命名),去掉花哨功能,优先代码质量与工程结构。

核心功能

  • 按剧集序号匹配 媒体文件 与 字幕文件
  • 支持两种输入:
    • -m 普通媒体目录
    • -b 蓝光原盘目录(自动读取 BDMV/STREAM/*.m2ts)
  • 蓝光多盘模式按单盘结果逐盘叠加
  • 支持排除整盘或指定 playlist
  • 支持输出到目标目录(-t)或就地处理
  • -b 和 -t 可同时使用,将识别出的原盘媒体文件输出到目标目录
  • 支持执行模式:
    • 普通媒体模式默认 hardlink
    • 蓝光模式默认 hardlink
    • --move 显式改为移动
    • -c 显式改为复制
    • -l 显式改为硬链接
  • 硬链接失败时会打印警告,并自动回退为移动
  • 支持 dry-run(-p)
  • 支持最小权限 HTTP 服务(--port)

构建

go build -o bin/matchsubtitles ./cmd/matchsubtitles

用法

1) 普通媒体 + 字幕匹配

./bin/matchsubtitles -s /path/subtitles -m /path/media

默认会硬链接字幕到媒体目录;如果加 -t,则输出到目标目录。

2) 原盘 + 字幕匹配(自动)

./bin/matchsubtitles -s /path/subtitles -b /path/disc1 -b /path/disc2

默认会硬链接输出;如果加 -t,会把原盘媒体和字幕一起输出到目标目录。

3) 只识别原盘并输出媒体到目标目录

./bin/matchsubtitles -b /path/disc1 -b /path/disc2 -t /path/target

4) 原盘强制分配(每盘固定数量)

./bin/matchsubtitles -s /path/subtitles -b /path/disc1 -b /path/disc2 -f -e 2

5) 原盘排除某一盘

./bin/matchsubtitles -p -s /path/subtitles -b /path/disc1 -b /path/disc2 --exclude-disc 2

6) 原盘排除指定 playlist

./bin/matchsubtitles -p -s /path/subtitles -b /path/disc1 -b /path/disc2 --exclude-playlist 1:00012

也支持不带盘号的全局排除:

./bin/matchsubtitles -p -s /path/subtitles -b /path/disc1 -b /path/disc2 --exclude-playlist 00012

7) 输出到目标目录(只打印计划,不落盘)

./bin/matchsubtitles -s /path/subtitles -m /path/media -t /path/target -p

8) 显式指定文件操作模式

./bin/matchsubtitles -s /path/subtitles -m /path/media --move
./bin/matchsubtitles -s /path/subtitles -m /path/media -c
./bin/matchsubtitles -b /path/disc1 -t /path/target -l

9) 启动 Web 服务

./bin/matchsubtitles --port 8080

不指定 --port 时,不启动 Web 服务。

如果你的数据在当前目录之外,例如上级目录的 ../source,需要显式放宽 Web 根目录:

./bin/matchsubtitles --port 8080 --web-root ..

也支持多个根目录:

./bin/matchsubtitles --port 8080 --web-root .. --web-root /mnt/media

参数

  • -s, --subtitle 字幕目录
  • -m, --media 媒体目录(与 -b 二选一)
  • -b, --bdmv 原盘目录,可重复
  • -t, --target 目标目录
  • -f, --force 强制分配模式(原盘)
  • -e, --each 每盘固定数量(需配合 -f)
  • -r, --reverse 反向分配(原盘平均模式)
  • --exclude-disc 排除指定盘号,可重复
  • --exclude-playlist 排除指定 playlist,可重复,格式为 00012 或 2:00012
  • -c, --copy 复制输出文件
  • -l, --hardlink 硬链接输出文件
  • --move 移动输出文件
  • -p, --print 仅打印计划(dry-run)
  • -pf, --prefix 自定义文件名前缀
  • -sf, --suffix 自定义文件名后缀
  • --bd-strategy 蓝光策略,支持 hybrid|size|parser
  • --port 启动 HTTP 服务端口;不指定则不启动
  • --web-root Web 服务允许访问的根目录,可重复,默认当前工作目录

默认行为

  • -m -s
    • 默认硬链接字幕
    • 不带 -t 时输出到媒体所在目录
    • 带 -t 时输出到目标目录
  • -b
    • 不带 -t 且不带 -s 时,只打印识别到的媒体文件
  • -b -t
    • 默认硬链接输出识别到的原盘媒体文件
  • -b -s -t
    • 默认硬链接输出原盘媒体文件和匹配后的字幕
  • --move
    • 显式改为移动
  • -l
    • 显式要求硬链接
    • 若硬链接失败,会提示并回退为移动
  • --port
    • 启动只读/受限导出 HTTP 服务
    • 不参与普通 CLI 匹配流程
  • --web-root
    • 所有 Web API 的读写边界
    • 接口传入的路径必须位于该根目录下

HTTP 接口

接口设计遵循最小权限原则:

  • 不提供删除、覆盖、移动接口
  • 导出时只允许:
    • 媒体文件使用硬链接
    • 字幕文件使用复制
  • Web 层基于 humi + chi,可直接复用 Huma 的 OpenAPI 输出
  • 所有路径都会限制在 --web-root 下,并拒绝越界路径与越界 symlink

1) 列出指定路径下所有原盘

健康检查:

curl 'http://127.0.0.1:8080/health'

返回:

{"status":"ok"}

1) 列出指定路径下所有原盘

curl 'http://127.0.0.1:8080/api/discs?path=/path/to/root'

2) 查看单个原盘的所有 m2ts 和解析出的主剧集文件

curl 'http://127.0.0.1:8080/api/disc/inspect?path=/path/to/disc'

返回中还会包含:

  • selected_playlist_id
  • excluded
    • 被排除 playlist 的 playlist_id
    • 排除原因 reason

3) 解析字幕并按语言分类

curl 'http://127.0.0.1:8080/api/subtitles?path=/path/to/subtitles'

4) 导出选定的媒体和字幕到目标目录

curl -X POST 'http://127.0.0.1:8080/api/export' \
  -H 'Content-Type: application/json' \
  -d '{
    "target_dir": "/path/to/out",
    "media_paths": ["/path/a/00000.m2ts", "/path/b/00001.m2ts"],
    "subtitle_paths": ["/path/sub/ep1.ass", "/path/sub/ep2.ass"]
 }'

约束:

  • media_paths 必填,可跨盘
  • subtitle_paths 可选
  • 如果提供字幕,数量必须与媒体数量一致
  • 目标目录不存在时会自动创建
  • 已存在目标文件时接口会直接报错,不覆盖

5) 用选定媒体和字幕目录自动配对字幕

curl -X POST 'http://127.0.0.1:8080/api/match' \
  -H 'Content-Type: application/json' \
  -d '{
    "media_paths": [
      "/path/a/00000.m2ts",
      "/path/b/00001.m2ts"
    ],
    "subtitle_dir": "/path/to/subtitles",
    "language": "chs",
    "mode": "manual",
    "subtitle_span": 1
  }'

说明:

  • media_paths 的顺序就是最终配对顺序
  • subtitle_dir 必填
  • language 可选
  • mode 支持:
    • manual
      • 按传入媒体顺序和字幕顺序配对
      • 适合原盘 m2ts
      • subtitle_span 可设置一份字幕覆盖几个连续媒体,默认 1;例如 BD 每张盘两集、字幕为 01-02 时传 2
    • auto
      • 尝试按文件名剧集号自动匹配
      • 适合普通媒体文件
  • 如果字幕目录里存在多种语言且未指定 language,接口会报错要求显式指定

6) 直接导出已配对结果

curl -X POST 'http://127.0.0.1:8080/api/export-matched' \
  -H 'Content-Type: application/json' \
  -d '{
    "target_dir": "/path/to/out",
    "prefix": "EP",
    "suffix": "_chs",
    "mode": "hardlink",
    "naming": "order",
    "include_media": true,
    "pairs": [
      {
        "media_path": "/path/a/00000.m2ts",
        "subtitle_path": "/path/sub/ep1.ass",
        "order": "1"
      }
    ]
  }'

说明:

  • prefix / suffix 与 CLI 的 -pf / -sf 对齐,会用 order 生成输出文件名。
  • mode 与 CLI 的 --hardlink / --copy / --move 对齐,支持 hardlink|copy|move。
  • naming: "order" 可在未设置 prefix/suffix 时也强制按集数命名。
  • include_media 默认为 true,设为 false 时只导出字幕。

当前蓝光规则

  • 单盘识别以 INDX/MPLS 解析结果为基础
  • 先保留可映射到 m2ts 的候选
  • 优先参考 libbluray 风格的主 title 特征,而不是只按最大文件或最长时长
  • 使用时长簇、章节数、preferred playlist 等特征筛选主剧集候选
  • 过滤明显偏短、结构异常、或像附属内容的候选
  • 对已选 playlist 的映射结果再做一次输出收缩,尽量只保留核心媒体文件
  • 多盘按单盘识别结果依盘序叠加
  • 支持排除整盘或指定 playlist

手动回归命令

普通媒体

go test ./...
go build -o bin/matchsubtitles ./cmd/matchsubtitles
./bin/matchsubtitles -p -s /path/subtitles -m /path/media

单盘蓝光

./bin/matchsubtitles -p -s /path/subtitles -b /path/disc1

多盘蓝光

./bin/matchsubtitles -p -s /path/subtitles -b /path/disc1 -b /path/disc2

多盘蓝光 + 排除整盘

./bin/matchsubtitles -p -s /path/subtitles -b /path/disc1 -b /path/disc2 --exclude-disc 2

多盘蓝光 + 排除 playlist

./bin/matchsubtitles -p -s /path/subtitles -b /path/disc1 -b /path/disc2 --exclude-playlist 1:00012

原盘输出到目标目录

./bin/matchsubtitles -p -b /path/disc1 -b /path/disc2 -t /path/target
./bin/matchsubtitles -p -s /path/subtitles -b /path/disc1 -b /path/disc2 -t /path/target

项目结构

cmd/matchsubtitles/      # CLI 入口
internal/app/            # 应用编排层
internal/cli/            # 参数解析
internal/domain/         # 核心数据模型
internal/parse/          # 剧集号和蓝光候选解析
internal/select/         # 蓝光筛选规则
internal/scan/media/     # 普通媒体扫描
internal/scan/subtitle/  # 字幕扫描与语言识别
internal/scan/root/      # 原盘根目录与 STREAM 发现
internal/match/          # 字幕匹配和原盘分配
internal/httpapi/        # humi + chi HTTP 接口层
internal/service/disc/   # CLI 与 Web 共用的原盘选择用例
internal/service/web/    # Web 共享服务层与路径边界控制
internal/plan/           # 输出路径规划
internal/ops/            # 文件操作执行
internal/bd/             # 蓝光兼容入口与 playlist 解析
legacy/python/           # 旧 Python 参考实现

说明:

  • internal/match/disc 和 internal/match/pair 为正式匹配实现
  • internal/plan/rename 和 internal/ops/file 为正式执行链路
  • internal/scan/* 现在按媒体、字幕、原盘发现拆分,主链路直接使用子包
  • internal/service/disc 负责统一原盘发现、inspect 和按策略选媒体