MCP SubMatcher
一个基于统计分词匹配算法的 MCP 服务器,能够自动将本地字幕文件重命名为与对应视频同名,解决压制组、分辨率等干扰信息导致的无法自动加载字幕的问题。
特性
🎯 智能匹配算法:基于文件名分词和权重系统,自动匹配视频和字幕
🌍 多语言支持:支持简英双语、繁英双语、简体中文、繁体中文、纯英文
📊 格式识别:ASS格式优先于SRT格式
🔧 压制组识别:识别相同的压制组给予额外加分
🛡️ 安全机制:演习模式、确认机制、备份功能
⚙️ 可配置性:完全可定制的权重和匹配规则
🔌 MCP 协议:通过 MCP 协议与 Claude Desktop 等 AI 客户端集成
📝 审计日志:自动记录所有配置变更
安装
环境要求
快速安装
1. 安装 UV(如果还未安装)
curl -LsSf https://astral.sh/uv/install.sh | sh # macOS/Linux
# 或
brew install uv # macOS
2. 配置 Claude Desktop(推荐方式)
直接在 Claude Desktop 配置文件中使用 uvx 从 GitHub 安装,无需克隆项目。
配置 MCP 客户端
Claude Desktop 配置
在 Claude Desktop 的配置文件中添加 MCP 服务器:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
添加以下配置:
{
"mcpServers": {
"submatcher": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/sienyaa/mcp-submatcher",
"mcp-submatcher"
]
}
}
}
说明:
高级配置(可选)
如果需要使用特定版本或本地开发版本,可以使用以下配置:
{
"mcpServers": {
"submatcher": {
"command": "uv",
"args": [
"--directory",
"/path/to/mcp-submatcher",
"run",
"mcp-submatcher"
]
}
}
}
说明:
适用于本地开发或需要使用特定版本的情况
需要先克隆项目并运行 uv sync
重启 Claude Desktop
配置完成后,重启 Claude Desktop 以加载 MCP 服务器。
快速开始
配置完成后,你可以直接在 Claude Desktop 中与对话:
你:请扫描 /Users/username/Videos/TVShows 目录中的视频和字幕文件
Claude:[自动调用 scan_media_files 工具]
扫描完成,找到 5 个视频文件和 6 个字幕文件。
你:请预览这个目录中的字幕匹配结果
Claude:[自动调用 preview_matching 工具]
匹配结果预览:
1. Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.mkv
- 匹配字幕: Breaking.Bad.S01E01.chs&eng.ass (评分: 280.0)
2. Breaking.Bad.S01E02.720p.WEB-DL.DDP5.1.H.264-NTb.mkv
- 匹配字幕: Breaking.Bad.S01E02.cht&eng.srt (评分: 270.0)
你:看起来不错,请执行实际重命名
Claude:[自动调用 rename_subtitles 工具]
重命名完成:
- Breaking.Bad.S01E01.chs&eng.ass → Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.ass
- Breaking.Bad.S01E02.cht&eng.srt → Breaking.Bad.S01E02.720p.WEB-DL.DDP5.1.H.264-NTb.srt
就这么简单!无需手动运行任何命令,只需与 Claude 对话即可。
工具详解
MCP SubMatcher 提供以下 6 个工具:
1. scan_media_files
扫描指定目录中的视频和字幕文件。
参数:
对话示例:
你:请扫描 /Users/username/Videos/TVShows 目录中的视频和字幕文件
Claude:[调用 scan_media_files 工具]
扫描完成,找到 5 个视频文件和 6 个字幕文件。
输出示例:
{
"success": true,
"directory": "/Users/username/Videos/TVShows",
"video_files": [
{
"path": "/Users/username/Videos/TVShows/Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.mkv",
"file_type": "video",
"name": "Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.mkv",
"stem": "Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS",
"extension": ".mkv",
"tokens": ["breaking", "bad", "s01e01", "1080p", "bluray", "x264", "sparks"],
"season": 1,
"episode": 1
}
],
"subtitle_files": [
{
"path": "/Users/username/Videos/TVShows/Breaking.Bad.S01E01.chs&eng.ass",
"file_type": "subtitle",
"name": "Breaking.Bad.S01E01.chs&eng.ass",
"stem": "Breaking.Bad.S01E01.chs&eng",
"extension": ".ass",
"tokens": ["breaking", "bad", "s01e01", "chs&eng"],
"season": 1,
"episode": 1
}
],
"video_count": 5,
"subtitle_count": 6
}
2. preview_matching
预览字幕匹配结果(演习模式,不会实际修改文件)。
参数:
对话示例:
你:请预览 /Users/username/Videos/TVShows 目录中的字幕匹配结果
Claude:[调用 preview_matching 工具]
匹配结果预览:
1. Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.mkv
- 匹配字幕: Breaking.Bad.S01E01.chs&eng.ass (评分: 280.0)
- 语言权重: 120.0
- 格式权重: 100.0
- 血统加分: 0.0
输出示例:
[
{
"video": {
"path": "/Users/username/Videos/TVShows/Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.mkv",
"file_type": "video",
"name": "Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.mkv",
"stem": "Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS",
"extension": ".mkv",
"tokens": ["breaking", "bad", "s01e01", "1080p", "bluray", "x264", "sparks"],
"season": 1,
"episode": 1
},
"subtitle": {
"path": "/Users/username/Videos/TVShows/Breaking.Bad.S01E01.chs&eng.ass",
"file_type": "subtitle",
"name": "Breaking.Bad.S01E01.chs&eng.ass",
"stem": "Breaking.Bad.S01E01.chs&eng",
"extension": ".ass",
"tokens": ["breaking", "bad", "s01e01", "chs&eng"],
"season": 1,
"episode": 1
},
"score": 280.0,
"language_weight": 120.0,
"format_weight": 100.0,
"lineage_bonus": 0.0
}
]
3. rename_subtitles
执行字幕重命名操作。
参数:
directory (string, 必需): 要处理的目录路径
confirm (boolean, 可选): 是否确认执行实际重命名(默认为 false,仅演习模式)
对话示例:
你:请重命名 /Users/username/Videos/TVShows 目录中的字幕文件
Claude:[调用 rename_subtitles 工具,confirm=false]
演习模式完成,将重命名 3 个字幕文件。
你:确认执行实际重命名
Claude:[调用 rename_subtitles 工具,confirm=true]
重命名完成:
- Breaking.Bad.S01E01.chs&eng.ass → Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.ass
- Breaking.Bad.S01E02.cht&eng.srt → Breaking.Bad.S01E02.720p.WEB-DL.DDP5.1.H.264-NTb.srt
输出示例:
{
"success": true,
"directory": "/Users/username/Videos/TVShows",
"dry_run": false,
"renamed_count": 3,
"failed_count": 0,
"skipped_count": 0,
"renamed_files": [
{
"old_name": "Breaking.Bad.S01E01.chs&eng.ass",
"new_name": "Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.ass",
"video_name": "Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.mkv",
"score": 280.0
}
],
"failed_files": [],
"skipped_files": []
}
4. get_config_value
获取配置文件中指定路径的值。
参数:
对话示例:
你:请获取配置中语言权重的第一个值
Claude:[调用 get_config_value 工具]
配置值:120
你:请获取配置中安全设置的 dry_run 值
Claude:[调用 get_config_value 工具]
配置值:false
输出示例:
5. set_config_value
设置配置文件中指定路径的值。
参数:
path (string, 必需): 配置路径,支持点号路径和数组索引
value (any, 必需): 要设置的值,可以是字符串、数字、布尔值等
对话示例:
你:请将配置中安全设置的 dry_run 设置为 true
Claude:[调用 set_config_value 工具]
配置已更新:
- 配置路径: safety.dry_run
- 旧值: false
- 新值: true
- 备份文件: /path/to/.config_backups/config_backup_20260201_165517.yaml
你:请将语言权重中简英双语的权重设置为 150
Claude:[调用 set_config_value 工具]
配置已更新:
- 配置路径: language_weights[0].weight
- 旧值: 120
- 新值: 150
输出示例:
{
"success": true,
"path": "safety.dry_run",
"config_path": "/path/to/core/config.yaml",
"backup_path": "/path/to/.config_backups/config_backup_20260201_165517.yaml",
"old_value": false,
"new_value": true,
"error": null,
"timestamp": "2026-02-01 16:55"
}
6. get_config_summary
获取当前配置摘要。
参数:无
对话示例:
你:请获取当前配置摘要
Claude:[调用 get_config_summary 工具]
当前配置摘要:
- 语言权重: 5 种语言类型
- 格式权重: 2 种格式
- 压制组加分: 已启用
- 安全设置: dry_run=false, require_confirm=true
输出示例:
{
"language_weights": [
{"name": "简英双语", "weight": 120, "keywords": ["chs&eng", "双语"]},
{"name": "繁英双语", "weight": 90, "keywords": ["cht&eng", "繁英"]},
{"name": "简体中文", "weight": 80, "keywords": ["chs", "sc", "简体"]},
{"name": "繁体中文", "weight": 70, "keywords": ["cht", "tc", "繁体"]},
{"name": "纯英文", "weight": 60, "keywords": ["eng", "en", "english"]}
],
"format_weights": [
{"name": "ass", "weight": 100, "description": "特效字幕格式"},
{"name": "srt", "weight": 80, "description": "通用字幕格式"}
],
"lineage_bonus": {
"enabled": true,
"weight": 20,
"common_release_groups": ["eztv", "rarbg", "sparks"]
},
"safety": {
"dry_run": false,
"require_confirm": true,
"backup_enabled": false,
"backup_dir": ".backup"
},
"matching": {
"min_common_tokens": 1,
"min_score_threshold": 50,
"skip_on_conflict": true,
"log_unmatched": true
}
}
配置管理
配置文件位置
配置文件位于 core/config.yaml。
主要配置项
1. 语言权重配置
控制字幕语言的优先级,权重越高越优先。
language_weights:
- name: "简英双语"
weight: 120
keywords:
- "chs&eng"
- "cht&eng"
- "简体&英文"
- "双语"
- "中英"
- "zh&en"
说明:
2. 格式权重配置
控制字幕格式的优先级。
format_weights:
- name: "ass"
weight: 100
description: "特效字幕格式,支持样式和特效"
- name: "srt"
weight: 80
description: "通用字幕格式"
3. 血统匹配加分
如果字幕文件名包含与视频相同的压制组标识,给予额外加分。
lineage_bonus:
enabled: true
weight: 20
description: "如果字幕文件名包含与视频相同的压制组标识,给予额外加分"
common_release_groups:
- "eztv"
- "rarbg"
- "vxt"
- "yify"
- "sparks"
4. 文件扩展名配置
定义支持的视频和字幕格式。
file_extensions:
video:
- ".mp4"
- ".mkv"
- ".avi"
subtitle:
- ".ass"
- ".srt"
5. 分词配置
控制文件名分词的行为。
tokenization:
separators:
- "."
- "_"
- "-"
- "["
- "]"
- "("
- ")"
- " "
min_token_length: 2
ignore_tokens:
- "the"
- "a"
- "an"
- "of"
- "in"
- "on"
6. 集数提取配置
定义识别季号和集号的正则表达式模式。
episode_patterns:
- pattern: "S(\\d{1,2})E(\\d{1,2})"
season_group: 1
episode_group: 2
- pattern: "(\\d{1,2})x(\\d{1,2})"
season_group: 1
episode_group: 2
7. 安全配置
控制程序的安全行为。
safety:
dry_run: false
require_confirm: true
backup_enabled: false
backup_dir: ".backup"
动态配置
通过 MCP 工具可以动态修改配置:
用户:请将配置中安全设置的 dry_run 设置为 true
Claude:[调用 set_config_value 工具]
配置已更新:safety.dry_run = true
安全功能
1. 演习模式 (Dry Run)
在演习模式下,工具只显示将要执行的操作,不实际修改文件。
启用演习模式:
你:请将配置中安全设置的 dry_run 设置为 true
Claude:[调用 set_config_value 工具]
配置已更新:safety.dry_run = true
使用演习模式:
你:请预览 /Users/username/Videos/TVShows 目录中的字幕匹配结果
Claude:[调用 preview_matching 工具]
演习模式完成,将重命名 3 个字幕文件(不会实际修改文件)。
2. 确认机制
要求用户确认后才执行重命名操作。
配置确认机制:
你:请启用确认机制
Claude:[调用 set_config_value 工具,path="safety.require_confirm", value=true]
配置已更新:safety.require_confirm = true
禁用确认机制(自动执行):
你:请禁用确认机制
Claude:[调用 set_config_value 工具,path="safety.require_confirm", value=false]
配置已更新:safety.require_confirm = false
3. 备份功能
在执行重命名前备份原始文件。
启用备份功能:
你:请启用备份功能
Claude:[调用 set_config_value 工具,path="safety.backup_enabled", value=true]
配置已更新:safety.backup_enabled = true
设置备份目录:
你:请将备份目录设置为 subtitle_backups
Claude:[调用 set_config_value 工具,path="safety.backup_dir", value="subtitle_backups"]
配置已更新:safety.backup_dir = subtitle_backups
备份文件结构:
当前目录/
├── .backup/ # 或自定义备份目录
│ ├── Breaking.Bad.S01E01.chs&eng.ass
│ ├── Breaking.Bad.S01E02.cht&eng.srt
│ └── Breaking.Bad.S01E03.chs&eng.ass
└── ... # 其他文件
4. 配置文件自动备份
每次修改配置时自动备份配置文件。
备份位置:
.config_backups/
├── config_backup_20260201_165517.yaml
├── config_backup_20260201_165520.yaml
└── config_backup_20260201_165525.yaml
审计日志:
所有配置变更都会记录到 SYSTEM_MOD_LOG.md 文件中,包括:
工作原理
1. 文件扫描
程序递归扫描指定目录,识别所有视频文件和字幕文件。
2. 分词处理
使用配置的分隔符将文件名切碎为单词元(Tokens)。
示例:
Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.mkv
↓ 分词后
['breaking', 'bad', 's01e01', '1080p', 'bluray', 'x264', 'sparks']
3. 聚类分析
统计所有文件中每个Token出现的频率,识别"全局Token"(高频词)。
目的:区分文件夹内混杂的多部不同美剧。
4. 集数提取
从文件名中提取季号和集号,支持多种格式:
S01E01 → Season 1, Episode 1
1x01 → Season 1, Episode 1
101 → Season 1, Episode 01
5. 匹配算法
基于"全局Token"和"集数锚点"双重验证建立对应关系。
匹配条件:
存在共同的全局Token
季号和集号匹配(或至少集号匹配)
6. 评分系统
当一个视频对应多个候选字幕时,按以下权重自动筛选最优解:
评分公式:
总分 = 基础分 + 语言权重 + 格式权重 + 血统加分
权重说明:
基础分:基于共同Token和集数匹配
语言权重(最高优先级):
简英双语:120分
繁英双语:90分
简体中文:80分
繁体中文:70分
纯英文:60分
格式权重:
.ass:100分(支持特效)
.srt:80分(通用格式)
血统加分:
故障排除
常见问题
1. 没有找到匹配
问题:工具没有找到任何匹配的字幕文件。
可能原因:
字幕文件名与视频文件名差异太大
匹配分数低于阈值(默认50分)
文件编码问题
解决方案:
你:请降低匹配阈值
Claude:[调用 set_config_value 工具,path="matching.min_score_threshold", value=30]
配置已更新:matching.min_score_threshold = 30
你:请检查文件编码
Claude:建议确保文件名使用 UTF-8 编码
2. 匹配错误
问题:工具匹配了错误的字幕文件。
可能原因:
解决方案:
你:请提高匹配精度
Claude:[调用 set_config_value 工具,path="matching.min_common_tokens", value=2]
配置已更新:matching.min_common_tokens = 2
你:请调整语言权重
Claude:[调用 set_config_value 工具,path="language_weights[0].weight", value=200]
配置已更新:language_weights[0].weight = 200
3. 权限问题
问题:无法重命名文件。
可能原因:
解决方案:
# 检查文件权限
ls -la 文件名
# 修改权限
chmod +w 文件名
调试模式
启用详细日志
你:请记录未匹配的文件
Claude:[调用 set_config_value 工具,path="matching.log_unmatched", value=true]
配置已更新:matching.log_unmatched = true
你:请在冲突时跳过
Claude:[调用 set_config_value 工具,path="matching.skip_on_conflict", value=true]
配置已更新:matching.skip_on_conflict = true
检查匹配分数
你:请获取详细的匹配分析
Claude:[调用 preview_matching 工具]
匹配结果预览:
1. Breaking.Bad.S01E01.1080p.BluRay.x264-SPARKS.mkv
- 匹配字幕: Breaking.Bad.S01E01.chs&eng.ass (评分: 280.0)
- 语言权重: 120.0
- 格式权重: 100.0
- 血统加分: 0.0
最佳实践
1. 新用户建议
你:请帮我设置新用户环境
Claude:[执行以下步骤]
1. 先启用演习模式
[调用 set_config_value 工具,path="safety.dry_run", value=true]
2. 预览匹配结果
[调用 preview_matching 工具]
3. 确认无误后关闭演习模式
[调用 set_config_value 工具,path="safety.dry_run", value=false]
4. 执行重命名
[调用 rename_subtitles 工具,confirm=true]
2. 批量处理建议
你:请帮我批量处理多个目录
Claude:[执行以下步骤]
1. 启用备份功能
[调用 set_config_value 工具,path="safety.backup_enabled", value=true]
2. 设置合理的备份目录
[调用 set_config_value 工具,path="safety.backup_dir", value="字幕备份_$(date +%Y%m%d)"]
3. 逐个处理目录
- 扫描目录
- 预览匹配
- 执行重命名
3. 性能优化
避免在根目录运行,指定具体目录
定期清理备份文件
对于大量文件,分批处理
附录
A. 默认配置值
语言权重
语言类型 | 权重 | 关键词 |
简英双语 | 120 | chs&eng, cht&eng, 简体&英文, 双语, 中英, zh&en |
繁英双语 | 90 | cht&eng, 繁体&英文, 繁英 |
简体中文 | 80 | chs, sc, 简体, 简中, zh |
繁体中文 | 70 | cht, tc, 繁体, 繁中 |
纯英文 | 60 | eng, en, english |
格式权重
格式 | 权重 | 描述 |
ASS | 100 | 特效字幕格式,支持样式和特效 |
SRT | 80 | 通用字幕格式 |
压制组加分
启用: true
权重: 20
常见压制组: eztv, rarbg, vxt, yify, yts, sparks, fgt, mgb, dimension, deflate, x264, x265, h264, h265, 1080p, 720p, 2160p, 4k, web-dl, webrip, bluray, bdrip, dvdrip
安全设置
dry_run: false
require_confirm: true
backup_enabled: false
backup_dir: ".backup"
匹配设置
min_common_tokens: 1
min_score_threshold: 50
skip_on_conflict: true
log_unmatched: true
B. 支持的视频格式
.mkv
.mp4
.avi
.mov
.wmv
.flv
.webm
C. 支持的字幕格式
D. 文件名匹配规则
分词处理:将文件名按分隔符(. _ - 空格)分割成单词
剧集识别:识别 S01E01、S01E02 等剧集格式
质量识别:识别 1080p、720p、4K 等质量标识
编码识别:识别 x264、x265、H.264、H.265 等编码格式
压制组识别:识别 HiQVE、YIFY、RARBG 等压制组
E. 评分算法
总分 = 基础分 + 语言权重 + 格式权重 + 压制组加分
示例:
简英双语ASS字幕 + 相同压制组
= 120(语言) + 100(格式) + 20(压制组)
= 240分
常见问题
Q1: 如何在 Claude Desktop 中使用 MCP SubMatcher?
确保已安装 uv
在 Claude Desktop 配置文件中添加 MCP 服务器配置(见上方"配置 MCP 客户端"章节)
重启 Claude Desktop
直接与 Claude 对话,例如:"请扫描 /path/to/videos 目录中的视频和字幕文件"
Q2: 使用 uvx 安装有什么优势?
使用 uvx --from git+https://github.com/sienyaa/mcp-submatcher 的优势:
无需克隆项目:直接从 GitHub 下载运行
自动更新:每次启动时自动使用最新版本
零依赖管理:uvx 会自动处理所有依赖
沙盒化:每个运行实例都是独立的,不会污染系统环境
快速启动:uvx 使用缓存机制,第二次启动更快
Q3: 为什么有些字幕没有匹配成功?
可能原因:
字幕文件名中缺少集号信息(如 S01E01)
字幕和视频的集号不匹配
存在多个相同评分的候选字幕(冲突)
字幕文件名与视频文件名没有共同的全局Token
解决方法:
Q4: 如何调整语言优先级?
通过 MCP 工具动态修改配置:
用户:请获取当前配置摘要
Claude:[调用 get_config_summary 工具]
用户:请将语言权重中简英双语的权重设置为 150
Claude:[调用 set_config_value 工具]
Q5: 程序会修改视频文件吗?
不会。程序只会重命名字幕文件,不会修改或删除视频文件。
Q6: 如何撤销重命名操作?
目前程序不支持自动撤销。建议:
首次使用时先使用 preview_matching 工具预览结果
确认结果无误后再使用 rename_subtitles 工具执行重命名
可以手动备份字幕文件
Q7: 支持哪些视频和字幕格式?
默认支持:
视频:.mkv, .mp4, .avi, .mov, .wmv, .flv, .webm
字幕:.ass, .srt, .sub, .ssa
可以通过 MCP 工具动态添加更多格式。
Q8: 程序能处理多个剧集混合的目录吗?
可以。程序通过聚类分析自动识别不同的剧集,即使多个剧集混合在同一目录也能正确匹配。
Q9: 如何使用特定版本或本地开发版本?
如果需要使用特定版本或本地开发版本,可以在 Claude Desktop 配置文件中使用 uv --directory 方式:
{
"mcpServers": {
"submatcher": {
"command": "uv",
"args": [
"--directory",
"/path/to/mcp-submatcher",
"run",
"mcp-submatcher"
]
}
}
}
适用场景:
本地开发和测试
需要使用特定版本
需要修改代码并立即测试
注意:使用此方式前需要先克隆项目并运行 uv sync。
技术架构
核心模块
mcp_server.py: MCP 服务器入口,定义工具和调用逻辑
mcp_adapter.py: SubMatcher 的 MCP 适配器,提供 MCP 友好的接口
config_nlp.py: 配置文件的 MCP 包装器,支持动态配置
core/submatcher.py: 核心匹配算法实现
core/config.yaml: 默认配置文件
技术栈
语言: Python 3.10+
MCP 协议: Model Context Protocol
核心库: pathlib, re, collections.Counter
配置管理: PyYAML
包管理: uv
性能特点
轻量级: 仅使用标准库和少量依赖
高效: 基于统计和正则表达式,处理速度快
安全: 默认演习模式,防止误操作
灵活: 完全可配置,适应不同需求
开发
本地开发
如果需要本地开发或修改代码,可以克隆项目:
git clone https://github.com/sienyaa/mcp-submatcher.git
cd mcp-submatcher
uv sync
然后在 Claude Desktop 配置文件中使用本地版本:
{
"mcpServers": {
"submatcher": {
"command": "uv",
"args": [
"--directory",
"/path/to/mcp-submatcher",
"run",
"mcp-submatcher"
]
}
}
}
运行测试
代码格式化
类型检查
贡献
欢迎提交问题报告和改进建议!
许可证
MIT License
更新日志
v1.0.0 (2026-02-01)
✨ 初始版本发布
✅ 实现所有核心功能
✅ 支持 MCP 协议
✅ 完善的安全机制
✅ 详细的文档
相关链接
享受 AI 辅助的自动化字幕管理体验! 🎬🤖📝