Skip to content

Commit 4c6d56c

Browse files
author
“pokelu”
committed
perf: prompt tuning
- ai模式prompt调优、增加匹配失败后的retry机制 - 更新readme
1 parent 022eac2 commit 4c6d56c

7 files changed

Lines changed: 178 additions & 95 deletions

File tree

README.md

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@
44
<img title="SubtitleRenamer" src="docs/preview/config_editor.png" alt="" width=50%>
55
</p>
66

7-
该项目的功能是根据给定视频文件匹配本地字幕并重命名字幕文件有一个勉强能用的GUI界面。
7+
该项目的功能是根据给定视频文件匹配本地字幕并重命名字幕文件,亮点在于可以使用**大模型**自动识别文件中的集数信息,通用性比一般正则表达式强很多!有一个勉强能用的GUI界面。
88

99
## 初衷
1010

1111
在使用TinyMediaManager刮削并重命名动画文件之后,视频文件和下载的字幕文件将无法匹配;人工重命名大量文件过于麻烦,所以搭建了一个自动化流程来处理这件事。
1212
本项目的默认集数匹配规则(ab)来源于[AutoBangumi](https://github.com/EstrellaXD/Auto_Bangumi),因此可能不适用于非动画文件、非动画字幕组的重命名工作。
13+
若想实现更加通用的字幕匹配,推荐使用**ai**模式并配置大模型的`Base URL``API Key``Model Name`,实现基于大模型的文件匹配。
1314

1415
## Anime Subtitle Renamer功能简介
1516

1617
- 根据给定文件夹地址中视频名自动匹配对应字幕文件并进行重命名,输出至指定文件夹
17-
- 支持修改自定义字幕语言后缀
18+
- 支持自定义字幕语言后缀的映射
1819
- 支持自定义匹配的视频、字幕格式
1920
- 支持三种视频-字幕匹配方法
2021
- 可自动清除视频文件夹中的旧字幕文件
@@ -30,7 +31,7 @@ Anime Subtitle Renamer可以读取的视频所在目录需满足以下要求:
3031
- 不支持合集,如`A E01-04.mp4`
3132
- 示例视频所在目录结构如下:
3233

33-
```
34+
```file
3435
video_dir
3536
├── A S01E01.mp4
3637
├── A S01E02.mp4
@@ -50,7 +51,7 @@ Anime Subtitle Renamer对字幕文件的读取会遍历路径内所有字幕文
5051
- 字幕文件有集数标识,如`孤独摇滚! - S01E01 - 孤独的转机.ass``[DMG&VCB-Studio] BOCCHI THE ROCK! [06][Ma10p_1080p][x265_flac].srt`
5152
- 示例字幕所在目录结构如下:
5253

53-
```
54+
```file
5455
sub_src_dir
5556
│   ├── tc
5657
│   │   ├── A[01].tc.ass
@@ -66,7 +67,7 @@ Anime Subtitle Renamer对字幕文件的读取会遍历路径内所有字幕文
6667

6768
若将字幕目标目录设置与视频文件所在目录一致,则输出示例应为:
6869

69-
```
70+
```file
7071
video_dir(sub_src_dir)
7172
├── A S01E01.mp4
7273
├── A S01E02.mp4
@@ -92,37 +93,46 @@ Anime Subtitle Renamer对字幕文件的读取会遍历路径内所有字幕文
9293

9394
1. 调整视频所在目录、字幕所在目录文件结构,见[视频和字幕文件目录说明](#视频和字幕文件目录说明)
9495
2. 按照实际情况修改基本配置中的视频所在目录、字幕所在目录、字幕目标目录(视频所在目录和相同则可以实现播放器中字幕自动识别)
95-
3. 设置是否清除原文件为False、Debug模式为True,运行程序,弹出窗口预览当前配置下字幕文件重命名的预计结果
96+
3. 选取匹配模式(`ai``raw``ab`),若选择`ai`,则需要配置大模型服务商的`Base URL``API Key``Model Name`;若选择`raw`,则需要配置匹配位置和匹配正则
97+
4. 设置是否清除原文件为False、Debug模式为True,运行程序,弹出窗口预览当前配置下字幕文件重命名的预计结果
9698

9799
<p align="center">
98100
<img title="RenamingPreview" src="docs/preview/renaming_preview.png" alt="" width=50%>
99101
</p>
100102

101-
4. 确认重命名无误,设置是否清除原文件为True、Debug模式为False,运行程序,完成重命名字幕文件的生成
103+
5. 确认重命名无误,设置是否清除原文件为True、Debug模式为False,运行程序,完成重命名字幕文件的生成
102104

103105
## 配置说明
104106

105107
### 基本配置
106108

109+
- 匹配模式(match_method):可选ab、raw、 ai
107110
- 是否清除原文件(clear_files):如果是True,则在生成新字幕文件前删除字幕目标目录下所有字幕文件
108111
- Debug模式(debug_mode):如果是True,则不会生成新字幕文件,而是弹窗显示当前配置下字幕文件重命名的预计结果
109112
- 视频所在目录(video_dir)
110113
- 字幕所在目录(sub_src_dir)
111-
- 字幕目标目录(sub_tar_dir):设置为与视频所在目录相同,则实现Jellyfin(或其他播放器)对外部字幕文件的自动读取
114+
- 字幕目标目录(sub_tar_dir):设置为与视频所在目录相同,则实现Jellyfin(或其他播放器)对外部字幕文件的自动读取;暂不支持设定为与字幕所在目录相同
112115

113-
### 高级配置(若无匹配失败情况不建议修改)
116+
### 高级配置
114117

115118
- 匹配视频格式(video_ext):python列表格式,默认为`['mp4', 'mkv']`,确定哪些后缀的文件将被视为视频
116119
- 匹配字幕格式(subtitle_ext):python列表格式,默认为`['ass', 'srt']`,确定哪些后缀的文件将被视为字幕
117-
- 匹配模式(match_method):可选ab、raw<del>、 ai(开发中,暂时不要选择)</del>
118-
- 匹配位置(match_pos):仅在`raw`模式下有效,输入应为大于等于-1的整数,用于确定匹配字幕文件集数在文件名中对应的位置(可能存在匹配出多个数字的情况),默认为-1,即匹配出的最后一个数字,0表示匹配出的第一个数字,1表示匹配出的第二个数字,以此类推,一般选择0或-1即可
119-
- 匹配正则表达式(pattern):仅在`raw`模式下有效,输入应为字符串形式正则表达式,用于匹配字幕文件名中的集数信息,默认为`(?:[A-Za-z]{1,2})?\b\d{1,2}\b`
120+
- 视频集数位置(video_match_pos):仅在`raw`模式下有效,输入应为大于等于-1的整数,用于确定匹配视频文件集数在文件名中对应的位置(可能存在匹配出多个数字的情况),默认为-1,即匹配出的最后一个数字,0表示匹配出的第一个数字,1表示匹配出的第二个数字,以此类推,一般选择0或-1即可
121+
- 视频匹配正则(video_pattern):仅在`raw`模式下有效,输入应为字符串形式正则表达式,用于匹配视频文件名中的集数信息,默认为`(?:[A-Za-z]{1,2})?\b\d{1,2}\b`
122+
- 字幕集数位置(sub_match_pos):仅在`raw`模式下有效,输入应为大于等于-1的整数,用于确定匹配字幕文件集数在文件名中对应的位置(可能存在匹配出多个数字的情况),默认为-1,即匹配出的最后一个数字,0表示匹配出的第一个数字,1表示匹配出的第二个数字,以此类推,一般选择0或-1即可
123+
- 字幕匹配正则(sub_pattern):仅在`raw`模式下有效,输入应为字符串形式正则表达式,用于匹配字幕文件名中的集数信息,默认为`(?:[A-Za-z]{1,2})?\b\d{1,2}\b`
124+
125+
### AI配置
126+
127+
- Base URL(base_url):大模型服务商的`Base URL`,如`https://api.openai.com/v1`,具体详见您所使用的大模型服务商官网
128+
- API Key(api_key):大模型服务商的`API Key`,如`sk-xxxxxxxxxxxxxxxxxxxxxxxxx`,具体详见您所使用的大模型服务商官网
129+
- Model Name(model):选择使用的`Model Name`,如`gpt-3.5-turbo`,具体详见您所使用的大模型服务商官网
120130

121131
### 隐藏配置
122132

123133
- language_ext:原字幕语言后缀与自定义字幕语言后缀映射,json格式,用于生成新字幕语言名,如`tc``sc`将被转换为`繁體中文.chi``简体中文.chi`,默认为:
124134

125-
```
135+
```json
126136
{
127137
"chs": "简体中文.chi",
128138
"cht": "繁體中文.chi",
@@ -139,16 +149,16 @@ Anime Subtitle Renamer对字幕文件的读取会遍历路径内所有字幕文
139149

140150
### ab模式
141151

142-
- 默认和推荐的匹配模式
152+
- 默认的匹配模式
143153
- 使用[AutoBangumi](https://github.com/EstrellaXD/Auto_Bangumi)[raw_parser.py](https://github.com/EstrellaXD/Auto_Bangumi/blob/main/backend/src/module/parser/analyser/raw_parser.py)的正则表达式匹配视频和字幕文件中的集数信息
144154

145-
### raw模式(个人调试用)
155+
### raw模式
146156

147-
- 仅支持TinyMediaManager中默认重命名规则(`${showTitle} - S${seasonNr2}E${episodeNr2} - ${title}`)命名的视频集数识别
148157
- 使用自定义的正则表达式匹配字幕文件中的集数信息
149-
- 需配合匹配位置(match_pos)、匹配正则表达式(pattern)两参数使用
150-
- 一般工作流程为:使用pattern提取字幕文件中候选的数字信息,再使用match_pos选择的正确的集数信息
158+
- 一般工作流程为:使用pattern提取视频/字幕文件中候选的数字信息,再使用match_pos选择提取出正确集数信息的位置
151159

152-
### <del>ai模式</del>
160+
### ai模式(推荐)
153161

154-
- 开发中,请勿选用
162+
- 更智能、泛用性更强的匹配模式
163+
- 使用大模型自动识别视频和字幕文件中的集数信息,实现文件匹配
164+
- 使用经验:Qwen2.5-32B-Instruct可稳定完成正确匹配,14B、7B模型可能存在个别文件匹配失败的情况
101 KB
Loading
148 KB
Loading

docs/preview/config_editor.png

35.5 KB
Loading

docs/preview/renaming_preview.png

11.2 KB
Loading

front_end/config_editor.py

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -42,29 +42,34 @@ def init_ui(self):
4242
# basic_frame.grid_columnconfigure(4, weight=1, minsize=150) # 其他内容列
4343

4444
# 基本设置标签页控件布局
45-
ttk.Label(basic_frame, text="是否清除原文件:", justify='left').grid(column=0, row=0, padx=5, pady=5, sticky='w')
45+
ttk.Label(basic_frame, text="匹配模式:", justify='left').grid(column=0, row=0, padx=5, pady=5, sticky='w')
46+
self.match_method_combo = ttk.Combobox(basic_frame, values=["ab", "ai", "raw"])
47+
self.match_method_combo.grid(column=1, row=0, padx=5, pady=5, sticky='ew')
48+
self.entry_maping_dict[self.match_method_combo] = "match_method"
49+
50+
ttk.Label(basic_frame, text="是否清除原文件:", justify='left').grid(column=0, row=1, padx=5, pady=5, sticky='w')
4651
self.clear_files_combo = ttk.Combobox(basic_frame, values=[True, False])
47-
self.clear_files_combo.grid(column=1, row=0, padx=5, pady=5, sticky='ew')
52+
self.clear_files_combo.grid(column=1, row=1, padx=5, pady=5, sticky='ew')
4853
self.entry_maping_dict[self.clear_files_combo] = "clear_files"
4954

50-
ttk.Label(basic_frame, text="Debug模式:", justify='left').grid(column=2, row=0, padx=5, pady=5, sticky='w')
55+
ttk.Label(basic_frame, text="Debug模式:", justify='left').grid(column=2, row=1, padx=5, pady=5, sticky='w')
5156
self.debug_mode_combo = ttk.Combobox(basic_frame, values=[True, False])
52-
self.debug_mode_combo.grid(column=3, row=0, padx=5, pady=5, sticky='ew')
57+
self.debug_mode_combo.grid(column=3, row=1, padx=5, pady=5, sticky='ew')
5358
self.entry_maping_dict[self.debug_mode_combo] = "debug_mode"
5459

55-
ttk.Label(basic_frame, text="视频所在目录:", justify='left').grid(column=0, row=1, padx=5, pady=5, sticky='w')
60+
ttk.Label(basic_frame, text="视频所在目录:", justify='left').grid(column=0, row=2, padx=5, pady=5, sticky='w')
5661
self.video_dir_entry = ttk.Entry(basic_frame)
57-
self.video_dir_entry.grid(column=1, columnspan=3, row=1, padx=5, pady=5, sticky='ew')
62+
self.video_dir_entry.grid(column=1, columnspan=3, row=2, padx=5, pady=5, sticky='ew')
5863
self.entry_maping_dict[self.video_dir_entry] = "video_dir"
5964

60-
ttk.Label(basic_frame, text="字幕所在目录:", justify='left').grid(column=0, row=2, padx=5, pady=5, sticky='w')
65+
ttk.Label(basic_frame, text="字幕所在目录:", justify='left').grid(column=0, row=3, padx=5, pady=5, sticky='w')
6166
self.sub_src_dir_entry = ttk.Entry(basic_frame)
62-
self.sub_src_dir_entry.grid(column=1, columnspan=3, row=2, padx=5, pady=5, sticky='ew')
67+
self.sub_src_dir_entry.grid(column=1, columnspan=3, row=3, padx=5, pady=5, sticky='ew')
6368
self.entry_maping_dict[self.sub_src_dir_entry] = "sub_src_dir"
6469

65-
ttk.Label(basic_frame, text="字幕目标目录:", justify='left').grid(column=0, row=3, padx=5, pady=5, sticky='w')
70+
ttk.Label(basic_frame, text="字幕目标目录:", justify='left').grid(column=0, row=4, padx=5, pady=5, sticky='w')
6671
self.sub_tar_dir_entry = ttk.Entry(basic_frame)
67-
self.sub_tar_dir_entry.grid(column=1, columnspan=3, row=3, padx=5, pady=5, sticky='ew')
72+
self.sub_tar_dir_entry.grid(column=1, columnspan=3, row=4, padx=5, pady=5, sticky='ew')
6873
self.entry_maping_dict[self.sub_tar_dir_entry] = "sub_tar_dir"
6974

7075
# 创建一个框架来容纳高级设置
@@ -89,29 +94,24 @@ def init_ui(self):
8994
self.subtitle_ext_entry.grid(column=1, columnspan=3, row=1, padx=5, pady=5, sticky='ew')
9095
self.entry_maping_dict[self.subtitle_ext_entry] = "subtitle_ext"
9196

92-
ttk.Label(advanced_frame, text="匹配模式:", justify='left').grid(column=0, row=2, padx=5, pady=5, sticky='w')
93-
self.match_method_combo = ttk.Combobox(advanced_frame, values=["ab", "ai", "raw"])
94-
self.match_method_combo.grid(column=1, row=2, padx=5, pady=5, sticky='ew')
95-
self.entry_maping_dict[self.match_method_combo] = "match_method"
96-
97-
ttk.Label(advanced_frame, text="视频集数位置:", justify='left').grid(column=0, row=3, padx=5, pady=5, sticky='w')
97+
ttk.Label(advanced_frame, text="视频集数位置:", justify='left').grid(column=0, row=2, padx=5, pady=5, sticky='w')
9898
self.video_match_pos_entry = ttk.Entry(advanced_frame)
99-
self.video_match_pos_entry.grid(column=1, row=3, padx=5, pady=5, sticky='ew')
99+
self.video_match_pos_entry.grid(column=1, row=2, padx=5, pady=5, sticky='ew')
100100
self.entry_maping_dict[self.video_match_pos_entry] = "video_match_pos"
101101

102-
ttk.Label(advanced_frame, text="视频匹配正则:", justify='left').grid(column=2, row=3, padx=5, pady=5, sticky='w')
102+
ttk.Label(advanced_frame, text="视频匹配正则:", justify='left').grid(column=2, row=2, padx=5, pady=5, sticky='w')
103103
self.video_pattern_entry = ttk.Entry(advanced_frame)
104-
self.video_pattern_entry.grid(column=3, row=3, padx=5, pady=5, sticky='ew')
104+
self.video_pattern_entry.grid(column=3, row=2, padx=5, pady=5, sticky='ew')
105105
self.entry_maping_dict[self.video_pattern_entry] = "video_pattern"
106106

107-
ttk.Label(advanced_frame, text="字幕集数位置:", justify='left').grid(column=0, row=4, padx=5, pady=5, sticky='w')
107+
ttk.Label(advanced_frame, text="字幕集数位置:", justify='left').grid(column=0, row=3, padx=5, pady=5, sticky='w')
108108
self.sub_match_pos_entry = ttk.Entry(advanced_frame)
109-
self.sub_match_pos_entry.grid(column=1, row=4, padx=5, pady=5, sticky='ew')
109+
self.sub_match_pos_entry.grid(column=1, row=3, padx=5, pady=5, sticky='ew')
110110
self.entry_maping_dict[self.sub_match_pos_entry] = "sub_match_pos"
111111

112-
ttk.Label(advanced_frame, text="字幕匹配正则:", justify='left').grid(column=2, row=4, padx=5, pady=5, sticky='w')
112+
ttk.Label(advanced_frame, text="字幕匹配正则:", justify='left').grid(column=2, row=3, padx=5, pady=5, sticky='w')
113113
self.sub_pattern_entry = ttk.Entry(advanced_frame)
114-
self.sub_pattern_entry.grid(column=3, row=4, padx=5, pady=5, sticky='ew')
114+
self.sub_pattern_entry.grid(column=3, row=3, padx=5, pady=5, sticky='ew')
115115
self.entry_maping_dict[self.sub_pattern_entry] = "sub_pattern"
116116

117117
# 创建一个框架来容纳AI设置

0 commit comments

Comments
 (0)