easyrip 3.13.2__py3-none-any.whl → 4.9.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. easyrip/__init__.py +5 -1
  2. easyrip/__main__.py +124 -15
  3. easyrip/easyrip_command.py +457 -148
  4. easyrip/easyrip_config/config.py +269 -0
  5. easyrip/easyrip_config/config_key.py +28 -0
  6. easyrip/easyrip_log.py +120 -42
  7. easyrip/easyrip_main.py +509 -259
  8. easyrip/easyrip_mlang/__init__.py +20 -45
  9. easyrip/easyrip_mlang/global_lang_val.py +18 -16
  10. easyrip/easyrip_mlang/lang_en.py +1 -1
  11. easyrip/easyrip_mlang/lang_zh_Hans_CN.py +101 -77
  12. easyrip/easyrip_mlang/translator.py +12 -10
  13. easyrip/easyrip_prompt.py +73 -0
  14. easyrip/easyrip_web/__init__.py +2 -1
  15. easyrip/easyrip_web/http_server.py +56 -42
  16. easyrip/easyrip_web/third_party_api.py +60 -8
  17. easyrip/global_val.py +21 -1
  18. easyrip/ripper/media_info.py +10 -3
  19. easyrip/ripper/param.py +482 -0
  20. easyrip/ripper/ripper.py +260 -574
  21. easyrip/ripper/sub_and_font/__init__.py +10 -0
  22. easyrip/ripper/{font_subset → sub_and_font}/ass.py +95 -84
  23. easyrip/ripper/{font_subset → sub_and_font}/font.py +72 -79
  24. easyrip/ripper/{font_subset → sub_and_font}/subset.py +122 -81
  25. easyrip/utils.py +129 -27
  26. easyrip-4.9.1.dist-info/METADATA +92 -0
  27. easyrip-4.9.1.dist-info/RECORD +31 -0
  28. easyrip/easyrip_config.py +0 -198
  29. easyrip/ripper/__init__.py +0 -10
  30. easyrip/ripper/font_subset/__init__.py +0 -7
  31. easyrip-3.13.2.dist-info/METADATA +0 -89
  32. easyrip-3.13.2.dist-info/RECORD +0 -29
  33. {easyrip-3.13.2.dist-info → easyrip-4.9.1.dist-info}/WHEEL +0 -0
  34. {easyrip-3.13.2.dist-info → easyrip-4.9.1.dist-info}/entry_points.txt +0 -0
  35. {easyrip-3.13.2.dist-info → easyrip-4.9.1.dist-info}/licenses/LICENSE +0 -0
  36. {easyrip-3.13.2.dist-info → easyrip-4.9.1.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
1
- import ctypes
1
+ import locale
2
2
 
3
3
  from . import lang_en, lang_zh_Hans_CN
4
4
  from .global_lang_val import (
@@ -18,6 +18,7 @@ __all__ = [
18
18
  "Lang_tag_region",
19
19
  "Lang_tag_script",
20
20
  "Lang_tag_val",
21
+ "Mlang_exception",
21
22
  "get_system_language",
22
23
  "gettext",
23
24
  "translate_subtitles",
@@ -31,50 +32,10 @@ all_supported_lang_map: dict[Lang_tag, dict[str, str]] = {
31
32
 
32
33
 
33
34
  def get_system_language() -> Lang_tag:
34
- # 获取系统默认的 UI 语言
35
- user_default_ui_lang = ctypes.windll.kernel32.GetUserDefaultUILanguage()
36
- lang_int = user_default_ui_lang & 0xFF # 主要语言
37
- sub_lang_int = user_default_ui_lang >> 10 # 次要语言
38
-
39
- # 语言代码映射
40
- lang_map = {
41
- 0x09: Lang_tag_language.en, # 英语
42
- 0x04: Lang_tag_language.zh, # 中文
43
- 0x0C: Lang_tag_language.fr, # 法语
44
- 0x07: Lang_tag_language.de, # 德语
45
- 0x0A: Lang_tag_language.es, # 西班牙语
46
- 0x10: Lang_tag_language.it, # 意大利语
47
- 0x13: Lang_tag_language.ja, # 日语
48
- 0x14: Lang_tag_language.ko, # 韩语
49
- 0x16: Lang_tag_language.ru, # 俄语
50
- }
51
-
52
- # 次要语言代码映射
53
- sub_lang_map = {
54
- 0x01: Lang_tag_region.US, # 美国
55
- 0x02: Lang_tag_region.GB, # 英国
56
- 0x03: Lang_tag_region.AU, # 澳大利亚
57
- 0x04: Lang_tag_region.CA, # 加拿大
58
- 0x05: Lang_tag_region.NZ, # 新西兰
59
- 0x06: Lang_tag_region.IE, # 爱尔兰
60
- 0x07: Lang_tag_region.ZA, # 南非
61
- 0x08: Lang_tag_region.JM, # 牙买加
62
- 0x09: Lang_tag_region.TT, # 加勒比地区
63
- 0x0A: Lang_tag_region.BZ, # 伯利兹
64
- 0x0B: Lang_tag_region.TT, # 特立尼达和多巴哥
65
- 0x0D: Lang_tag_region.PH, # 菲律宾
66
- 0x0E: Lang_tag_region.IN, # 印度
67
- 0x0F: Lang_tag_region.MY, # 马来西亚
68
- 0x10: Lang_tag_region.SG, # 新加坡
69
- 0x11: Lang_tag_region.HK, # 香港特别行政区
70
- 0x12: Lang_tag_region.MO, # 澳门特别行政区
71
- 0x13: Lang_tag_region.TW, # 台湾地区
72
- 0x00: Lang_tag_region.CN, # 中国大陆
73
- }
74
-
75
- return Lang_tag(
76
- language=lang_map.get(lang_int, Lang_tag_language.Unknown),
77
- region=sub_lang_map.get(sub_lang_int, Lang_tag_region.Unknown),
35
+ return (
36
+ Lang_tag()
37
+ if (sys_lang := locale.getdefaultlocale()[0]) is None
38
+ else Lang_tag.from_str(sys_lang.replace("_", "-"))
78
39
  )
79
40
 
80
41
 
@@ -102,3 +63,17 @@ def gettext(
102
63
  log.debug(f"{e!r} in gettext when str.format", deep=True, is_format=False)
103
64
 
104
65
  return new_text
66
+
67
+
68
+ class Mlang_exception(Exception):
69
+ def __init__(
70
+ self,
71
+ *args: object,
72
+ **kwargs: object,
73
+ ) -> None:
74
+ msg = args[0]
75
+ if isinstance(msg, str):
76
+ new_msg: str = gettext(msg, *args[1:], is_format=True, **kwargs)
77
+ super().__init__(new_msg)
78
+ else:
79
+ super().__init__(msg, *args[1:])
@@ -1,8 +1,10 @@
1
1
  import enum
2
+ from collections.abc import Iterable
2
3
  from dataclasses import dataclass
3
- from typing import Iterable, Self
4
+ from typing import Self, final
4
5
 
5
6
 
7
+ @final
6
8
  @dataclass(slots=True, init=False, eq=False)
7
9
  class Lang_tag_val:
8
10
  en_name: str
@@ -13,13 +15,13 @@ class Lang_tag_val:
13
15
  return self.en_name if self._local_name is None else self._local_name
14
16
 
15
17
  @local_name.setter
16
- def local_name(self, val: str | None):
18
+ def local_name(self, val: str | None) -> None:
17
19
  if val is not None and len(val) == 0:
18
20
  raise ValueError("The length of the name cannot be 0")
19
21
 
20
22
  self._local_name = val
21
23
 
22
- def __init__(self, *, en_name: str, local_name: str | None = None):
24
+ def __init__(self, *, en_name: str, local_name: str | None = None) -> None:
23
25
  if len(en_name) == 0:
24
26
  raise ValueError("The length of the name cannot be 0")
25
27
 
@@ -144,6 +146,7 @@ class Lang_tag_region(enum.Enum):
144
146
  return cls.Unknown
145
147
 
146
148
 
149
+ @final
147
150
  @dataclass(slots=True, kw_only=True)
148
151
  class Lang_tag:
149
152
  language: Lang_tag_language = Lang_tag_language.Unknown
@@ -163,7 +166,6 @@ class Lang_tag:
163
166
  is_allow_mismatch_language: bool = False,
164
167
  ) -> Self | None:
165
168
  """启用不完整匹配时,找到最匹配的第一项"""
166
-
167
169
  target_tags_tuple = tuple(target_tags)
168
170
  del target_tags
169
171
 
@@ -178,7 +180,7 @@ class Lang_tag:
178
180
 
179
181
  if self in matching_tags_tuple:
180
182
  return self
181
- elif not is_incomplete_match:
183
+ if not is_incomplete_match:
182
184
  return None
183
185
 
184
186
  same_region_tuple = tuple(
@@ -204,26 +206,26 @@ class Lang_tag:
204
206
  str_tag: str,
205
207
  ) -> Self:
206
208
  """
207
- #### 输入语言标签字符串,输出标签对象
209
+ 输入语言标签字符串,输出标签对象
210
+
208
211
  e.g. zh-Hans-CN -> Self(Language.zh, Script.Hans, Region.CN)
209
212
  """
210
-
211
213
  from ..easyrip_mlang import gettext
212
214
 
213
- str_tag_tuple = tuple(s for s in str_tag.split("-"))
215
+ str_tag_list = str_tag.split("-")
214
216
 
215
- language = Lang_tag_language.from_name(str_tag_tuple[0])
217
+ language = Lang_tag_language.from_name(str_tag_list[0])
216
218
  script: Lang_tag_script = Lang_tag_script.Unknown
217
219
  region: Lang_tag_region = Lang_tag_region.Unknown
218
220
 
219
- for i, s in enumerate(str_tag_tuple[1:]):
220
- if s in Lang_tag_script._member_map_.keys():
221
+ for i, s in enumerate(str_tag_list[1:]):
222
+ if s in Lang_tag_script._member_map_:
221
223
  if i != 0:
222
224
  raise ValueError(
223
225
  gettext("The input language tag string format is illegal")
224
226
  )
225
227
  script = Lang_tag_script[s]
226
- elif s in Lang_tag_region._member_map_.keys():
228
+ elif s in Lang_tag_region._member_map_:
227
229
  region = Lang_tag_region[s]
228
230
 
229
231
  return cls(
@@ -264,7 +266,7 @@ class Global_lang_val:
264
266
 
265
267
  res_str_list: list[str] = [
266
268
  _local_name
267
- if (_org_name := tag_list[0]) in Lang_tag_language._member_map_.keys()
269
+ if (_org_name := tag_list[0]) in Lang_tag_language._member_map_
268
270
  and (_local_name := Lang_tag_language[_org_name].value.local_name)
269
271
  else _org_name
270
272
  ]
@@ -272,9 +274,9 @@ class Global_lang_val:
272
274
  if tag_list_len >= 2:
273
275
  _org_name = tag_list[1]
274
276
 
275
- if _org_name in Lang_tag_script._member_map_.keys():
277
+ if _org_name in Lang_tag_script.__members__:
276
278
  _local_name = Lang_tag_script[_org_name].value.local_name
277
- elif _org_name in Lang_tag_region._member_map_.keys():
279
+ elif _org_name in Lang_tag_region._member_map_:
278
280
  _local_name = Lang_tag_region[_org_name].value.local_name
279
281
  else:
280
282
  _local_name = _org_name
@@ -284,7 +286,7 @@ class Global_lang_val:
284
286
  if tag_list_len >= 3:
285
287
  _org_name = tag_list[2]
286
288
 
287
- if _org_name in Lang_tag_region._member_map_.keys():
289
+ if _org_name in Lang_tag_region._member_map_:
288
290
  _local_name = Lang_tag_region[_org_name].value.local_name
289
291
  else:
290
292
  _local_name = _org_name
@@ -2,4 +2,4 @@ from .global_lang_val import Lang_tag, Lang_tag_language
2
2
 
3
3
  LANG_TAG = Lang_tag(language=Lang_tag_language.en)
4
4
 
5
- LANG_MAP = dict[str, str]()
5
+ LANG_MAP: dict[str, str] = {}
@@ -1,4 +1,4 @@
1
- from ..easyrip_command import Cmd_type, Opt_type
1
+ from ..easyrip_command import Audio_codec, Cmd_type, Opt_type, Preset_name
2
2
  from .global_lang_val import (
3
3
  Lang_tag,
4
4
  Lang_tag_language,
@@ -16,16 +16,20 @@ LANG_MAP: dict[str, str] = {
16
16
  # doc
17
17
  "Version": "版本",
18
18
  "Help": "帮助",
19
- "You can input command or use command-line arguments to run.": "输入命令或使用命令行传参以运行。",
20
- "Commands": "命令",
19
+ "Enter '<cmd> [<param> ...]' to execute Easy Rip commands or any commands that exist in environment.\nOr enter '<option> <param> [<option> <param> ...]' to add Ripper.": "键入 '<命令> [<参数> ...]' 以执行 Easy Rip 命令或任何环境中存在的命令。\n或者键入 '<选项> <参数> [<选项> <参数> ...]' 以添加 Ripper。",
20
+ "Easy Rip Commands": "Easy Rip 命令",
21
21
  "Ripper options": "Ripper 选项",
22
- Cmd_type.help.value.description: "展示全部帮助文档或展示 <cmd> 的帮助文档",
22
+ Cmd_type.help.value.description: (
23
+ "展示全部帮助文档或展示 <cmd> 的帮助文档\n"
24
+ "例如 help list\n" # .
25
+ "例如 h -p x265slow"
26
+ ),
23
27
  Cmd_type.version.value.description: "展示版本信息",
24
28
  Cmd_type.init.value.description: (
25
29
  "执行初始化函数\n" # .
26
30
  "例如你可以在修改动态翻译文件后执行它"
27
31
  ),
28
- Cmd_type.log.value.opt_str: "log [<日志级别>] <string>",
32
+ Cmd_type.log.value.param: "[<日志级别>] <string>",
29
33
  Cmd_type.log.value.description: (
30
34
  "输出自定义日志\n"
31
35
  "日志级别:\n"
@@ -46,7 +50,7 @@ LANG_MAP: dict[str, str] = {
46
50
  Cmd_type.dir.value.description: "打印当前目录的所有文件和文件夹的名字",
47
51
  Cmd_type.mkdir.value.description: "新建文件目录",
48
52
  Cmd_type.cls.value.description: "清屏",
49
- Cmd_type.list.value.opt_str: "list <list 选项>",
53
+ Cmd_type.list.value.param: "<list 选项>",
50
54
  Cmd_type.list.value.description: (
51
55
  "操作 Ripper list\n"
52
56
  " \n"
@@ -67,27 +71,30 @@ LANG_MAP: dict[str, str] = {
67
71
  "<int> <int>:\n"
68
72
  " 交换指定索引"
69
73
  ),
70
- Cmd_type.run.value.opt_str: "run <run 选项>",
74
+ Cmd_type.run.value.param: "[<run 选项>] [-multithreading <0 | 1>]",
71
75
  Cmd_type.run.value.description: (
72
76
  "执行 Ripper list 中的 Ripper\n"
73
- " \n"
77
+ "\n"
74
78
  "默认:\n"
75
79
  " 仅执行\n"
76
- " \n"
80
+ "\n"
77
81
  "exit:\n"
78
82
  " 执行后退出程序\n"
79
- " \n"
83
+ "\n"
80
84
  "shutdown [<秒数>]:\n"
81
85
  " 执行后关机\n"
82
- " 默认: 60"
86
+ " 默认: 60\n"
87
+ "\n"
88
+ "server [<地址>]:[<端口>]@[<密码>]:\n"
89
+ " 详见对应的 help"
83
90
  ),
84
- Cmd_type.server.value.opt_str: "server [[-a | -address] <地址>[:<端口>] [[-p | -password] <密码>]]",
91
+ Cmd_type.server.value.param: "[<地址>]:[<端口>]@[<密码>]",
85
92
  Cmd_type.server.value.description: (
86
93
  "启动 web 服务\n"
87
94
  "默认: server localhost:0\n"
88
- "客户端发送命令 'kill' 可以退出 Ripper 的运行,注意,FFmpeg需要接受多次^C信号才能强制终止,单次^C会等待文件输出完才会终止"
95
+ "客户端发送命令 'kill' 可以退出 Ripper 的运行, 注意, FFmpeg需要接受多次^C信号才能强制终止, 单次^C会等待文件输出完才会终止"
89
96
  ),
90
- Cmd_type.config.value.opt_str: "config <config 选项>",
97
+ Cmd_type.config.value.param: "<config 选项>",
91
98
  Cmd_type.config.value.description: (
92
99
  "regenerate | clear | clean | reset\n"
93
100
  " 重新生成 config 文件\n"
@@ -99,7 +106,8 @@ LANG_MAP: dict[str, str] = {
99
106
  " 设置 config\n"
100
107
  " 例如 config set language en"
101
108
  ),
102
- Cmd_type.translate.value.opt_str: "translate <中缀> <目标语言标签> [-overwrite]",
109
+ Cmd_type.prompt.value.param: "<prompt 选项>",
110
+ Cmd_type.translate.value.param: "<中缀> <目标语言标签> [-overwrite]",
103
111
  Cmd_type.translate.value.description: (
104
112
  "翻译字幕文件\n"
105
113
  "例如 'translate zh-Hans zh-Hant' 将翻译所有 '*.zh-Hans.ass' 文件为 zh-Hant"
@@ -107,11 +115,11 @@ LANG_MAP: dict[str, str] = {
107
115
  Cmd_type.Option.value.description: (
108
116
  "-i <输入> -p <预设名> [-o <输出>] [-o:dir <目录>] [-pipe <vpy 路径名> -crf <值> -psy-rd <值> ...] [-sub <字幕文件路径名>] [-c:a <音频编码器> -b:a <音频码率>] [-muxer <复用器> [-r <帧率>]] [-run [<run 选项>]] [...]\n"
109
117
  " \n"
110
- "往 Ripper list 中添加一个 Ripper,你可以单独设置预设中每个选项的值,使用 -run 执行 Ripper"
118
+ "往 Ripper list 中添加一个 Ripper, 你可以单独设置预设中每个选项的值, 使用 -run 执行 Ripper"
111
119
  ),
112
120
  Opt_type._i.value.description: (
113
- "输入文件的路径名或输入 'fd' 以使用文件对话框,'cfd' 从当前目录打开\n"
114
- "部分情况下允许使用 '?' 作为间隔符往一个 Ripper 中输入多个,例如 '-preset subset' 允许输入多个 ASS"
121
+ "输入文件的路径名或输入 'fd' 以使用文件对话框, 'cfd' 从当前目录打开\n"
122
+ "部分情况下允许使用 '?' 作为间隔符往一个 Ripper 中输入多个, 例如 '-preset subset' 允许输入多个 ASS"
115
123
  ),
116
124
  Opt_type._o_dir.value.description: "输出文件的目标目录",
117
125
  Opt_type._o.value.description: (
@@ -120,30 +128,22 @@ LANG_MAP: dict[str, str] = {
120
128
  ' e.g. "name--?{start=6,padding=4,increment=2}--?{time:%Y.%m.%S}"'
121
129
  ),
122
130
  Opt_type._auto_infix.value.description: (
123
- "如果启用,输出的文件将添加自动中缀:\n" # .
131
+ "如果启用, 输出的文件将添加自动中缀:\n" # .
124
132
  " 无音轨: '.v'\n"
125
133
  " 有音轨: '.va'\n"
126
134
  "默认: 1"
127
135
  ),
128
136
  Opt_type._preset.value.description: (
129
137
  "设置预设\n"
130
- "预设名:\n"
131
- " custom\n"
132
- " subset\n"
133
- " copy\n"
134
- " flac\n"
135
- " x264fast x264slow\n"
136
- " x265fast4 x265fast3 x265fast2 x265fast x265slow x265full\n"
137
- " h264_amf h264_nvenc h264_qsv\n"
138
- " hevc_amf hevc_nvenc hevc_qsv\n"
139
- " av1_amf av1_nvenc av1_qsv"
138
+ "预设名:\n" # .
139
+ f"{Preset_name.to_help_string(' ')}"
140
140
  ),
141
141
  Opt_type._pipe.value.description: (
142
- "选择一个 vpy 文件作为管道的输入,这个 vpy 必须有 input 全局变量\n"
142
+ "选择一个 vpy 文件作为管道的输入, 这个 vpy 必须有 input 全局变量\n"
143
143
  "演示如何 input: vspipe -a input=<input> filter.vpy"
144
144
  ),
145
145
  Opt_type._pipe_gvar.value.description: (
146
- "自定义传给 vspipe 的全局变量,多个则用':'间隔\n"
146
+ "自定义传给 vspipe 的全局变量, 多个则用':'间隔\n"
147
147
  '例如: -pipe:gvar "a=1 2 3:b=abc" -> vspipe -a "a=1 2 3" -a "b=abc"'
148
148
  ),
149
149
  Opt_type._vf.value.description: (
@@ -151,19 +151,22 @@ LANG_MAP: dict[str, str] = {
151
151
  "与 -sub 同时使用为未定义行为"
152
152
  ),
153
153
  Opt_type._sub.value.description: (
154
- "它使用 libass 制作硬字幕,需要硬字幕时请输入字幕路径名\n"
155
- '使用 "::" 以输入多个字幕,例如: 01.zh-Hans.ass::01.zh-Hant.ass::01.en.ass\n'
156
- "如果使用'auto',相同前缀的字幕文件将作为输入\n"
157
- "'auto:...'可以只选择指定中缀,例如'auto:zh-Hans:zh-Hant'"
154
+ "它使用 libass 制作硬字幕, 需要硬字幕时请输入字幕路径名\n"
155
+ '使用 "::" 以输入多个字幕, 例如: 01.zh-Hans.ass::01.zh-Hant.ass::01.en.ass\n'
156
+ "如果使用'auto', 相同前缀的字幕文件将作为输入\n"
157
+ "'auto:...'可以只选择指定中缀, 例如'auto:zh-Hans:zh-Hant'"
158
158
  ),
159
159
  Opt_type._only_mux_sub_path.value.description: "该目录下所有的字幕和字体文件将加入混流",
160
- Opt_type._soft_sub.value.description: "往 MKV 中封装子集化字幕",
160
+ Opt_type._soft_sub.value.description: (
161
+ "往 MKV 中封装子集化字幕\n" # .
162
+ "'auto' 的用法详见 '-sub'"
163
+ ),
161
164
  Opt_type._subset_font_dir.value.description: (
162
165
  "子集化时使用的字体的目录\n"
163
- '默认: 优先当前目录,其次当前目录下含有 "font" 的文件夹 (不分大小写)'
166
+ '默认: 优先当前目录, 其次当前目录下含有 "font" 的文件夹 (不分大小写)'
164
167
  ),
165
168
  Opt_type._subset_font_in_sub.value.description: (
166
- "将字体编码到 ASS 文件中,而不是单独的字体文件\n" # .
169
+ "将字体编码到 ASS 文件中, 而不是单独的字体文件\n" # .
167
170
  "默认: 0"
168
171
  ),
169
172
  Opt_type._subset_use_win_font.value.description: (
@@ -189,18 +192,15 @@ LANG_MAP: dict[str, str] = {
189
192
  "子集化时报错则中断\n" # .
190
193
  "默认: 0"
191
194
  ),
192
- Opt_type._translate_sub.value.opt_str: "-translate-sub <中缀>:<语言标签>",
195
+ Opt_type._translate_sub.value.param: "<中缀>:<语言标签>",
193
196
  Opt_type._translate_sub.value.description: (
194
197
  "临时生成字幕的翻译文件\n" # .
195
198
  "例如 'zh-Hans:zh-Hant' 将临时生成繁体字幕"
196
199
  ),
197
200
  Opt_type._c_a.value.description: (
198
201
  "设置音频编码器\n"
199
- " \n" # .
200
- "音频编码器:\n"
201
- " copy\n"
202
- " libopus\n"
203
- " flac"
202
+ "音频编码器:\n" # .
203
+ f"{Audio_codec.to_help_string(' ')}"
204
204
  ),
205
205
  Opt_type._b_a.value.description: "设置音频码率。默认值 '160k'",
206
206
  Opt_type._muxer.value.description: (
@@ -212,33 +212,36 @@ LANG_MAP: dict[str, str] = {
212
212
  ),
213
213
  Opt_type._r.value.description: (
214
214
  "设置封装的帧率\n" # .
215
- "使用 auto 时,自动从输入的视频获取帧率,并吸附到最近的预设点位"
215
+ "使用 auto 时, 自动从输入的视频获取帧率, 并吸附到最近的预设点位"
216
216
  ),
217
217
  Opt_type._r.value.description: (
218
218
  "指定添加的章节文件\n" # .
219
219
  "支持与 '-o' 相同的迭代语法"
220
220
  ),
221
221
  Opt_type._custom_template.value.description: (
222
- "当 -preset custom 时,将运行这个选项\n"
222
+ "当 -preset custom 时, 将运行这个选项\n"
223
223
  "字符串转义: \\34/ -> \", \\39/ -> ', '' -> \"\n"
224
224
  '例如 -custom:format \'-i "{input}" -map {testmap123} "{output}" \' -custom:suffix mp4 -testmap123 0:v:0'
225
225
  ),
226
226
  Opt_type._custom_suffix.value.description: (
227
- "当 -preset custom 时,这个选项将作为输出文件的后缀\n" # .
227
+ "当 -preset custom 时, 这个选项将作为输出文件的后缀\n" # .
228
228
  '默认: ""'
229
229
  ),
230
230
  Opt_type._run.value.description: (
231
231
  "执行 Ripper list 中的 Ripper\n"
232
- " \n"
232
+ "\n"
233
233
  "默认:\n"
234
234
  " 仅执行\n"
235
- " \n"
235
+ "\n"
236
236
  "exit:\n"
237
237
  " 执行后退出程序\n"
238
- " \n"
238
+ "\n"
239
239
  "shutdown [<秒数>]:\n"
240
240
  " 执行后关机\n"
241
- " 默认: 60"
241
+ " 默认: 60\n"
242
+ "\n"
243
+ "server [<地址>]:[<端口>]@[<密码>]:\n"
244
+ " 详见对应的 help"
242
245
  ),
243
246
  Opt_type._ff_params_ff.value.description: (
244
247
  "设置 FFmpeg 的全局选项\n" # .
@@ -261,9 +264,17 @@ LANG_MAP: dict[str, str] = {
261
264
  "设置 FFmpeg 输出的文件的持续时间\n" # .
262
265
  "等同于 ffmpeg -i ... -t <time> ..."
263
266
  ),
267
+ Opt_type._hevc_strict.value.description: (
268
+ "当分辨率 >= 4K 时, 关闭 HME, 并自动降低 -ref\n" # .
269
+ "默认: 1"
270
+ ),
271
+ Opt_type._multithreading.value.description: (
272
+ "使用多线程执行 Ripper list, 适合性能占用低的情况\n" # .
273
+ "例如 -p subset 或 -p copy"
274
+ ),
264
275
  # utils
265
- "{} has new version {}. You can download it: {}": "检测到 {} 有新版本 {}。可在此下载: {}",
266
- "{} not found, download it: {}": "没找到 {},在此下载: {}",
276
+ "{} has new version ({} -> {}). Suggest upgrading it: {}": "检测到 {} 有新版本 ({} -> {})。建议更新: {}",
277
+ "{} not found, download it: {}": "没找到 {}, 在此下载: {}",
267
278
  "flac ver ({}) must >= 1.5.0": "flac 版本 ({}) 必须 >= 1.5.0",
268
279
  # main
269
280
  "Check env...": "检测环境中...",
@@ -272,7 +283,7 @@ LANG_MAP: dict[str, str] = {
272
283
  "Stop run Ripper": "Ripper 执行终止",
273
284
  "There are {} {} during run": "执行期间有 {} 个 {}",
274
285
  "Execute shutdown in {}s": "{}s 后执行关机",
275
- "{} run completed, shutdown in {}s": "{} 执行完成,{}s 后关机",
286
+ "{} run completed, shutdown in {}s": "{} 执行完成, {}s 后关机",
276
287
  "Run completed": "执行完成",
277
288
  "Your input command has error:\n{}": "输入的命令报错:\n{}",
278
289
  "Delete the {}th Ripper success": "成功删除第 {} 个 Ripper",
@@ -280,20 +291,24 @@ LANG_MAP: dict[str, str] = {
280
291
  "Can not start multiple services": "禁止重复启用服务",
281
292
  "Disable the use of '{}' on the web": "禁止在 web 使用 '{}'",
282
293
  'Illegal char in -o "{}"': '-o "{}" 中有非法字符',
283
- 'The directory "{}" did not exist and was created': '目录 "{}" 不存在,自动创建',
284
- "Missing '-preset' option, set to default value 'custom'": "缺少 '-preset' 选项,自动设为默认值 'custom'",
294
+ 'The directory "{}" did not exist and was created': '目录 "{}" 不存在, 自动创建',
295
+ "Missing '-preset' option, set to default value 'custom'": "缺少 '-preset' 选项, 自动设为默认值 'custom'",
285
296
  "Input file number == 0": "输入的文件数量为 0",
286
297
  'The file "{}" does not exist': '文件 "{}" 不存在',
287
298
  "No subtitle file exist as -sub auto when -i {} -o:dir {}": "-sub auto 没有在 -i {} -o:dir {} 中找到对应字幕文件",
299
+ "The new value is the same as the old value, cancel the modification": "新值与旧值相同, 取消修改",
300
+ "'{}' successfully: {}": "'{}' 成功: {}",
288
301
  "Unsupported option: {}": "不支持的选项: {}",
289
302
  "Unsupported param: {}": "不支持的参数: {}",
303
+ "There is no audio stream in the video, so '-c:a' cannot be used": "视频中没有音频流,所以无法使用 '-c:a'",
304
+ "Unsupported '{}' param: {}": "'{}' 不支持此参数: {}",
290
305
  "Manually force exit": "手动强制退出",
291
- "or run '{}' when you use pip": "或运行 '{}' 以使用 pip 更新",
292
- "Wrong sec in -shutdown, change to default 60s": "-shutdown 设定的秒数错误,改为默认值 60s",
306
+ "Suggest running the following command to upgrade using pip: {}": "建议运行以下命令以使用 pip 更新: {}",
307
+ "Wrong sec in -shutdown, change to default 60s": "-shutdown 设定的秒数错误, 改为默认值 60s",
293
308
  "Current work directory has an other Easy Rip is running: {}": "当前工作目录存在其他 Easy Rip 正在运行: {}",
294
309
  "Stop run command": "命令执行终止",
295
310
  # log
296
- "encoding_log.html": "编码日志.html",
311
+ "EasyRip_log.html": "EasyRip日志.html",
297
312
  "Start": "开始",
298
313
  "Input file pathname": "输入文件路径名",
299
314
  "Output directory": "输出目录",
@@ -303,40 +318,47 @@ LANG_MAP: dict[str, str] = {
303
318
  "File size": "文件体积",
304
319
  "Time consuming": "耗时",
305
320
  "End": "结束",
306
- # ripper.py
321
+ # ripper
322
+ "'{}' is not a member of preset": "'{}' 不存在于 preset",
307
323
  "Failed to add Ripper: {}": "添加 Ripper 失败: {}",
308
- "'{}' is not a valid '{}', set to default value '{}'. Valid options are: {}": "'{}' 不存在于 '{}',已设为默认值 '{}'。有以下值可用: {}",
324
+ "'{}' is not a valid '{}', set to default value '{}'. Valid options are: {}": "'{}' 不存在于 '{}', 已设为默认值 '{}'。有以下值可用: {}",
309
325
  "The preset custom must have custom:format or custom:template": "custom 预设必须要有 custom:format 或 custom:template",
310
326
  "There have error in running": "执行时出错",
311
327
  "{} param illegal": "{} 参数非法",
312
- 'The file "{}" already exists, skip translating it': '文件 "{}" 已存在,跳过翻译',
313
- "Subset faild, cancel mux": "子集化失败,取消混流",
328
+ 'The file "{}" already exists, skip translating it': '文件 "{}" 已存在, 跳过翻译',
329
+ "Subset faild, cancel mux": "子集化失败, 取消混流",
314
330
  "FFmpeg report: {}": "FFmpeg 报告: {}",
315
331
  "{} not found. Skip it": "没找到 {}。默认跳过",
332
+ "{} not found. Skip it. Perhaps you want the {}": "没找到 {}。默认跳过。或许你想要的是 {}",
316
333
  'The font "{}" does not contain these characters: {}': '字体 "{}" 不包含字符: {}',
334
+ "The style '{}' not in Styles. Defaulting to the style '{}'": "样式 '{}' 不在 Styles 中。默认使用样式 '{}'",
335
+ "The style '{}' and the style 'Default' not in Styles. Defaulting to no font": "样式 '{}' 和样式 'Default' 都不在 Styles 中。默认不使用字体",
336
+ "The \\r style '{}' not in Styles": "\\r 样式 '{}' 不在 Styles 中",
337
+ "Illegal format: '{}' in file \"{}\" in line: {}": "非法格式: '{}' 在文件 \"{}\" 的此行: {}",
317
338
  # web
318
- "Starting HTTP service on port {}...": "在端口 {} 启动 HTTP 服务...",
319
- "HTTP service stopped by ^C": "HTTP 服务被 ^C 停止",
320
- "There is a running command, terminate this request": "存在正在运行的命令,终止此次请求",
339
+ "Starting {protocol} service on port {port}...": "在端口 {port} 启动 {protocol} 服务...",
340
+ "{} service stopped by ^C": "{} 服务被 ^C 停止",
341
+ "There is a running command, terminate this request": "存在正在运行的命令, 终止此次请求",
321
342
  "Prohibited from use $ <code> in web service when no password": "禁止在未设定密码的 Web 服务中使用 $ <code>",
322
343
  # config
323
- "The config version is not match, use '{}' to regenerate config file": "配置文件版本不匹配,使用 '{}' 重新生成配置文件",
344
+ "The config version is not match, use '{}' to regenerate config file": "配置文件版本不匹配, 使用 '{}' 重新生成配置文件",
324
345
  "Regenerate config file": "重新生成 config 文件",
325
346
  "Config file is not found": "配置文件不存在",
326
347
  "Config data is not found": "配置文件数据不存在",
327
- "User profile is not found, regenerate config": "用户配置文件不存在,重新生成配置",
348
+ "User profile is not found, regenerate config": "用户配置文件不存在, 重新生成配置",
328
349
  "User profile is not a valid dictionary": "用户配置文件不是有效的字典",
329
- "User profile is not found": "用户配置文件不存在",
350
+ "User profile is not found in config file": "用户配置文件不存在于配置文件",
351
+ "Type mismatch: need '{}'": "类型不匹配: 需要 '{}'",
330
352
  "Key '{}' is not found in user profile": "用户配置文件中不存在 {}",
353
+ "Save prompt history to config directory, otherwise save to memory. Take effect after reboot. Default: {}": "将 prompt 历史保存到 config 目录,否则保存到内存。重启后生效。默认: {}",
331
354
  # config about
332
- "Easy Rip's language, support incomplete matching. Support: {}": "Easy Rip 的语言,支持不完整匹配。支持: {}",
333
- "Auto check the update of Easy Rip": "自动检测 Easy Rip 更新",
334
- "Auto check the versions of all dependent programs": "自动检测所有依赖的程序的版本",
335
- "Program startup directory, when the value is empty, starts in the working directory": "程序启动目录,值为空时在工作目录启动",
336
- "Force change of log file path, when the value is empty, it is the working directory": "强制更改日志文件所在路径,值为空时为工作目录",
337
- "Do not write to log file": "不写入日志文件",
338
- "Logs this level and above will be printed, and if the value is '{}', they will not be printed. Support: {}": "此等级及以上的日志会打印到控制台,若值为 '{}' 则不打印。支持: {}",
339
- "Logs this level and above will be written, and if the value is '{}', the '{}' only be written when 'server', they will not be written. Support: {}": "此等级及以上的日志会写入日志文件,若值为 '{}' 则不写入,'{}' 仅在 'server' 时写入。支持: {}",
355
+ "Easy Rip's language, support incomplete matching. Default: {}. Supported: {}": "Easy Rip 的语言, 支持不完整匹配。默认: {}。支持: {}",
356
+ "Auto check the update of Easy Rip. Default: {}": "自动检测 Easy Rip 更新。默认: {}",
357
+ "Auto check the versions of all dependent programs. Default: {}": "自动检测所有依赖的程序的版本。默认: {}",
358
+ "Program startup directory, when the value is empty, starts in the working directory. Default: {}": "程序启动目录, 值为空时在工作目录启动。默认: {}",
359
+ "Force change of log file path, when the value is empty, it is the working directory. Default: {}": "强制更改日志文件所在路径, 值为空时为工作目录。默认: {}",
360
+ "Logs this level and above will be printed, and if the value is '{}', they will not be printed. Default: {}. Supported: {}": "此等级及以上的日志会打印到控制台, 若值为 '{}' 则不打印。默认: {}。支持: {}",
361
+ "Logs this level and above will be written, and if the value is '{}', the '{}' only be written when 'server', they will not be written. Default: {}. Supported: {}": "此等级及以上的日志会写入日志文件, 若值为 '{}' 则不写入, '{}' 仅在 'server' 时写入。默认: {}。支持: {}",
340
362
  # 第三方 API
341
363
  "Translating into '{target_lang}' using '{api_name}'": "正在使用 '{api_name}' 翻译为 '{target_lang}'",
342
364
  # mlang
@@ -347,4 +369,6 @@ LANG_MAP: dict[str, str] = {
347
369
  # 通用
348
370
  "Run {} failed": "执行 {} 失败",
349
371
  "Unknown error": "未知错误",
372
+ "'{}' execution failed: {}": "'{}' 执行失败: {}",
373
+ "No closing quotation": "没有闭合引号",
350
374
  }
@@ -1,7 +1,8 @@
1
+ from collections.abc import Iterable
1
2
  from pathlib import Path
2
3
  from threading import Thread
3
4
  from time import sleep
4
- from typing import Final, Iterable
5
+ from typing import Final
5
6
 
6
7
  from ..easyrip_web.third_party_api import zhconvert
7
8
  from .global_lang_val import (
@@ -21,11 +22,12 @@ def translate_subtitles(
21
22
  enable_multithreading: bool = True,
22
23
  ) -> list[tuple[Path, str]]:
23
24
  """
24
- #### 自动搜索符合中缀的字幕文件,翻译为目标语言
25
- 文件交集选择器: 不为 None 时,与选择器有交集的 Path 才会选择
26
- Return: list[tuple[file, content]]
27
- """
25
+ 自动搜索符合中缀的字幕文件,翻译为目标语言
26
+
27
+ :param 文件交集选择器: 不为 None 时,与选择器有交集的 Path 才会选择
28
28
 
29
+ :return: list[tuple[file, content]]
30
+ """
29
31
  from ..easyrip_log import log
30
32
  from ..easyrip_mlang import gettext
31
33
  from ..utils import read_text
@@ -38,7 +40,7 @@ def translate_subtitles(
38
40
  if file_intersection_selector is not None:
39
41
  file_intersection_selector = set(file_intersection_selector)
40
42
 
41
- file_list: Final = list[tuple[Path, str]]()
43
+ file_list: Final[list[tuple[Path, str]]] = []
42
44
  for f in directory.iterdir():
43
45
  if f.suffix not in {".ass", ".ssa", ".srt"} or (
44
46
  file_intersection_selector is not None
@@ -152,10 +154,10 @@ def translate_subtitles(
152
154
  case _:
153
155
  raise Exception(gettext("Unsupported language tag: {}", infix))
154
156
 
155
- res_file_list: Final = list[tuple[Path, str]]()
156
- res_file_dict: Final = dict[int, tuple[Path, str]]()
157
+ res_file_list: Final[list[tuple[Path, str]]] = []
158
+ res_file_dict: Final[dict[int, tuple[Path, str]]] = {}
157
159
 
158
- def _tr(index: int, path: Path, org_text: str):
160
+ def _tr(index: int, path: Path, org_text: str) -> None:
159
161
  log.info('Start translating file "{}"', path)
160
162
  res_file_dict[index] = (
161
163
  path,
@@ -166,7 +168,7 @@ def translate_subtitles(
166
168
  )
167
169
  log.info("Successfully translated: {}", path)
168
170
 
169
- threads = list[Thread]()
171
+ threads: Final[list[Thread]] = []
170
172
 
171
173
  for i, f in enumerate(file_list):
172
174
  t = Thread(target=_tr, args=(i, f[0], f[1]), daemon=False)