easyrip 4.14.0__py3-none-any.whl → 4.15.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.
- easyrip/__main__.py +20 -0
- easyrip/easyrip_command.py +204 -147
- easyrip/easyrip_main.py +11 -1
- easyrip/easyrip_mlang/lang_zh_Hans_CN.py +48 -86
- easyrip/easyrip_web/third_party_api.py +1 -1
- easyrip/global_val.py +1 -1
- easyrip/ripper/media_info.py +4 -4
- easyrip/ripper/ripper.py +179 -16
- {easyrip-4.14.0.dist-info → easyrip-4.15.1.dist-info}/METADATA +2 -1
- {easyrip-4.14.0.dist-info → easyrip-4.15.1.dist-info}/RECORD +14 -14
- {easyrip-4.14.0.dist-info → easyrip-4.15.1.dist-info}/WHEEL +1 -1
- {easyrip-4.14.0.dist-info → easyrip-4.15.1.dist-info}/entry_points.txt +0 -0
- {easyrip-4.14.0.dist-info → easyrip-4.15.1.dist-info}/licenses/LICENSE +0 -0
- {easyrip-4.14.0.dist-info → easyrip-4.15.1.dist-info}/top_level.txt +0 -0
easyrip/__main__.py
CHANGED
|
@@ -6,6 +6,8 @@ import Crypto
|
|
|
6
6
|
import fontTools
|
|
7
7
|
import prompt_toolkit
|
|
8
8
|
from prompt_toolkit import ANSI, prompt
|
|
9
|
+
from prompt_toolkit.application import get_app
|
|
10
|
+
from prompt_toolkit.clipboard.pyperclip import PyperclipClipboard
|
|
9
11
|
from prompt_toolkit.completion import merge_completers
|
|
10
12
|
from prompt_toolkit.history import InMemoryHistory
|
|
11
13
|
from prompt_toolkit.input.ansi_escape_sequences import ANSI_SEQUENCES
|
|
@@ -49,8 +51,22 @@ def run() -> NoReturn:
|
|
|
49
51
|
|
|
50
52
|
@key_bindings.add(Keys.ControlC)
|
|
51
53
|
def _(event: KeyPressEvent) -> None:
|
|
54
|
+
buffer = event.app.current_buffer
|
|
55
|
+
|
|
56
|
+
# 检查是否有选中的文本
|
|
57
|
+
if buffer.selection_state is not None:
|
|
58
|
+
get_app().clipboard.set_data(buffer.copy_selection())
|
|
59
|
+
return
|
|
60
|
+
|
|
52
61
|
event.app.exit(exception=KeyboardInterrupt, style="class:exiting")
|
|
53
62
|
|
|
63
|
+
@key_bindings.add(Keys.ControlA)
|
|
64
|
+
def _(event: KeyPressEvent) -> None:
|
|
65
|
+
buff = event.app.current_buffer
|
|
66
|
+
buff.cursor_position = 0
|
|
67
|
+
buff.start_selection()
|
|
68
|
+
buff.cursor_position = len(buff.text)
|
|
69
|
+
|
|
54
70
|
@key_bindings.add(Keys.ControlD)
|
|
55
71
|
def _(event: KeyPressEvent) -> None:
|
|
56
72
|
event.app.current_buffer.insert_text(C_D)
|
|
@@ -64,6 +80,7 @@ def run() -> NoReturn:
|
|
|
64
80
|
return named_commands.get_by_name("unix-word-rubout").handler(event)
|
|
65
81
|
|
|
66
82
|
path_completer = SmartPathCompleter()
|
|
83
|
+
clipboard = PyperclipClipboard()
|
|
67
84
|
|
|
68
85
|
def _ctv_to_nc(ctvs: Iterable[Cmd_type_val]) -> CmdCompleter:
|
|
69
86
|
return CmdCompleter(
|
|
@@ -90,6 +107,7 @@ def run() -> NoReturn:
|
|
|
90
107
|
)
|
|
91
108
|
for ctv in ctvs
|
|
92
109
|
for name in ctv.names
|
|
110
|
+
if not ctv.is_no_prompt_child
|
|
93
111
|
}
|
|
94
112
|
)
|
|
95
113
|
|
|
@@ -115,6 +133,7 @@ def run() -> NoReturn:
|
|
|
115
133
|
)
|
|
116
134
|
for ctv in ctvs
|
|
117
135
|
for name in ctv.names
|
|
136
|
+
if not ctv.is_no_prompt_child
|
|
118
137
|
}
|
|
119
138
|
|
|
120
139
|
cmd_ctv_tuple = tuple(ct.value for ct in Cmd_type if ct != Cmd_type.Option)
|
|
@@ -138,6 +157,7 @@ def run() -> NoReturn:
|
|
|
138
157
|
),
|
|
139
158
|
history=prompt_history,
|
|
140
159
|
complete_while_typing=True,
|
|
160
|
+
clipboard=clipboard,
|
|
141
161
|
)
|
|
142
162
|
if command.startswith(C_Z):
|
|
143
163
|
raise EOFError
|
easyrip/easyrip_command.py
CHANGED
|
@@ -28,6 +28,12 @@ class Cmd_type_val:
|
|
|
28
28
|
_param: str
|
|
29
29
|
_description: str
|
|
30
30
|
childs: tuple["Cmd_type_val", ...]
|
|
31
|
+
is_no_prompt_child: bool
|
|
32
|
+
"""此项作为父项的子项时,不在自动补全时使用"""
|
|
33
|
+
is_no_doc_child: bool
|
|
34
|
+
"""此项作为父项的子项时,不在 doc 时使用"""
|
|
35
|
+
is_all_no_doc_childs: bool
|
|
36
|
+
"""此项的所有子项不在 doc 时使用"""
|
|
31
37
|
|
|
32
38
|
@property
|
|
33
39
|
def param(self) -> str:
|
|
@@ -64,11 +70,17 @@ class Cmd_type_val:
|
|
|
64
70
|
param: str = "",
|
|
65
71
|
description: str = "",
|
|
66
72
|
childs: tuple["Cmd_type_val", ...] = (),
|
|
73
|
+
is_no_prompt_child: bool = False,
|
|
74
|
+
is_no_doc_child: bool = False,
|
|
75
|
+
is_all_no_doc_childs: bool = False,
|
|
67
76
|
) -> None:
|
|
68
77
|
self.names = names
|
|
69
78
|
self.param = param
|
|
70
79
|
self.description = description
|
|
71
80
|
self.childs = childs
|
|
81
|
+
self.is_no_prompt_child = is_no_prompt_child
|
|
82
|
+
self.is_no_doc_child = is_no_doc_child
|
|
83
|
+
self.is_all_no_doc_childs = is_all_no_doc_childs
|
|
72
84
|
|
|
73
85
|
def __eq__(self, other: object) -> bool:
|
|
74
86
|
if isinstance(other, Cmd_type_val):
|
|
@@ -79,7 +91,19 @@ class Cmd_type_val:
|
|
|
79
91
|
return hash(self.names)
|
|
80
92
|
|
|
81
93
|
def to_doc(self) -> str:
|
|
82
|
-
|
|
94
|
+
description: str = ((f"{self.description}\n") if self.description else "") + (
|
|
95
|
+
""
|
|
96
|
+
if self.is_all_no_doc_childs
|
|
97
|
+
else "".join(
|
|
98
|
+
f"\n{child.to_doc()}"
|
|
99
|
+
for child in self.childs
|
|
100
|
+
if not child.is_no_doc_child
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
return (
|
|
104
|
+
f"{' / '.join(self.names)} {self.param}\n"
|
|
105
|
+
f"{textwrap.indent(description, ' │ ', lambda _: True)}" # 永远返回 True 才能保证空行前也能加字符
|
|
106
|
+
)
|
|
83
107
|
|
|
84
108
|
|
|
85
109
|
class Cmd_type(enum.Enum):
|
|
@@ -123,6 +147,7 @@ class Cmd_type(enum.Enum):
|
|
|
123
147
|
Cmd_type_val(("send",)),
|
|
124
148
|
Cmd_type_val(("debug",)),
|
|
125
149
|
),
|
|
150
|
+
is_all_no_doc_childs=True,
|
|
126
151
|
)
|
|
127
152
|
_run_any = Cmd_type_val(
|
|
128
153
|
("$",),
|
|
@@ -146,6 +171,7 @@ class Cmd_type(enum.Enum):
|
|
|
146
171
|
Cmd_type_val(("fd",)),
|
|
147
172
|
Cmd_type_val(("cfd",)),
|
|
148
173
|
),
|
|
174
|
+
is_all_no_doc_childs=True,
|
|
149
175
|
)
|
|
150
176
|
dir = ls = Cmd_type_val(
|
|
151
177
|
("dir", "ls"),
|
|
@@ -163,55 +189,54 @@ class Cmd_type(enum.Enum):
|
|
|
163
189
|
list = Cmd_type_val(
|
|
164
190
|
("list",),
|
|
165
191
|
param="<list option>",
|
|
166
|
-
description=
|
|
167
|
-
"Operate Ripper list\n"
|
|
168
|
-
" \n"
|
|
169
|
-
"Default:\n"
|
|
170
|
-
" Show Ripper list\n"
|
|
171
|
-
" \n"
|
|
172
|
-
"clear / clean:\n"
|
|
173
|
-
" Clear Ripper list\n"
|
|
174
|
-
" \n"
|
|
175
|
-
"del / pop <index>:\n"
|
|
176
|
-
" Delete a Ripper from Ripper list\n"
|
|
177
|
-
" \n"
|
|
178
|
-
"sort [n][r]:\n"
|
|
179
|
-
" Sort list\n"
|
|
180
|
-
" 'n': Natural Sorting\n"
|
|
181
|
-
" 'r': Reverse\n"
|
|
182
|
-
" \n"
|
|
183
|
-
"<int> <int>:\n"
|
|
184
|
-
" Exchange specified index"
|
|
185
|
-
),
|
|
192
|
+
description="Operate Ripper list",
|
|
186
193
|
childs=(
|
|
187
|
-
Cmd_type_val(
|
|
188
|
-
|
|
189
|
-
|
|
194
|
+
Cmd_type_val(
|
|
195
|
+
("Default",), description="Show Ripper list", is_no_prompt_child=True
|
|
196
|
+
),
|
|
197
|
+
Cmd_type_val(("clear", "clean"), description="Clear Ripper list"),
|
|
198
|
+
Cmd_type_val(
|
|
199
|
+
("del", "pop"),
|
|
200
|
+
param="<index>",
|
|
201
|
+
description="Delete a Ripper from Ripper list",
|
|
202
|
+
),
|
|
203
|
+
Cmd_type_val(
|
|
204
|
+
("sort",),
|
|
205
|
+
param="[n][r]",
|
|
206
|
+
description=(
|
|
207
|
+
"Sort list\n" # .
|
|
208
|
+
"'n': Natural Sorting\n"
|
|
209
|
+
"'r': Reverse"
|
|
210
|
+
),
|
|
211
|
+
childs=(Cmd_type_val(("n", "r", "nr")),),
|
|
212
|
+
),
|
|
213
|
+
Cmd_type_val(
|
|
214
|
+
("<int> <int>",),
|
|
215
|
+
description="Exchange specified index",
|
|
216
|
+
is_no_prompt_child=True,
|
|
217
|
+
),
|
|
190
218
|
),
|
|
191
219
|
)
|
|
192
220
|
run = Cmd_type_val(
|
|
193
221
|
("run",),
|
|
194
222
|
param="[<run option>] [-multithreading <0 | 1>]",
|
|
195
|
-
description=
|
|
196
|
-
"Run the Ripper in the Ripper list\n"
|
|
197
|
-
"\n"
|
|
198
|
-
"Default:\n"
|
|
199
|
-
" Only run\n"
|
|
200
|
-
"\n"
|
|
201
|
-
"exit:\n"
|
|
202
|
-
" Close program when run finished\n"
|
|
203
|
-
"\n"
|
|
204
|
-
"shutdown [<sec>]:\n"
|
|
205
|
-
" Shutdown when run finished\n"
|
|
206
|
-
" Default: 60\n"
|
|
207
|
-
"\n"
|
|
208
|
-
"server [<address>]:[<port>]@[<password>]:\n"
|
|
209
|
-
" See the corresponding help for details"
|
|
210
|
-
),
|
|
223
|
+
description="Run the Ripper from the Ripper list",
|
|
211
224
|
childs=(
|
|
212
|
-
Cmd_type_val(("
|
|
213
|
-
Cmd_type_val(("
|
|
214
|
-
Cmd_type_val(
|
|
225
|
+
Cmd_type_val(("Default",), description="Only run", is_no_prompt_child=True),
|
|
226
|
+
Cmd_type_val(("exit",), description="Close program when run finished"),
|
|
227
|
+
Cmd_type_val(
|
|
228
|
+
("shutdown",),
|
|
229
|
+
param="[<sec>]",
|
|
230
|
+
description=(
|
|
231
|
+
"Shutdown when run finished\n" # .
|
|
232
|
+
"Default: 60"
|
|
233
|
+
),
|
|
234
|
+
),
|
|
235
|
+
Cmd_type_val(
|
|
236
|
+
("server",),
|
|
237
|
+
param="[<address>]:[<port>]@[<password>]",
|
|
238
|
+
description="See the corresponding help for details",
|
|
239
|
+
),
|
|
215
240
|
),
|
|
216
241
|
)
|
|
217
242
|
server = Cmd_type_val(
|
|
@@ -226,23 +251,23 @@ class Cmd_type(enum.Enum):
|
|
|
226
251
|
config = Cmd_type_val(
|
|
227
252
|
("config",),
|
|
228
253
|
param="<config option>",
|
|
229
|
-
description=(
|
|
230
|
-
"regenerate | clear | clean\n"
|
|
231
|
-
" Regenerate config file\n"
|
|
232
|
-
"open\n"
|
|
233
|
-
" Open the directory where the config file is located\n"
|
|
234
|
-
"list\n"
|
|
235
|
-
" Show all config adjustable options\n"
|
|
236
|
-
"set <key> <val>\n"
|
|
237
|
-
" Set config\n"
|
|
238
|
-
" e.g. config set language zh"
|
|
239
|
-
),
|
|
240
254
|
childs=(
|
|
241
|
-
Cmd_type_val(
|
|
242
|
-
|
|
243
|
-
|
|
255
|
+
Cmd_type_val(
|
|
256
|
+
("regenerate", "clear", "clean", "reset"),
|
|
257
|
+
description="Regenerate config file",
|
|
258
|
+
),
|
|
259
|
+
Cmd_type_val(
|
|
260
|
+
("open",),
|
|
261
|
+
description="Open the directory where the config file is located",
|
|
262
|
+
),
|
|
263
|
+
Cmd_type_val(("list",), description="Show all config adjustable options"),
|
|
244
264
|
Cmd_type_val(
|
|
245
265
|
("set",),
|
|
266
|
+
param="<key> <val>",
|
|
267
|
+
description=(
|
|
268
|
+
"Set config\n" # .
|
|
269
|
+
"e.g. config set language zh"
|
|
270
|
+
),
|
|
246
271
|
childs=tuple(Cmd_type_val((k,)) for k in Config_key._member_map_),
|
|
247
272
|
),
|
|
248
273
|
),
|
|
@@ -250,25 +275,21 @@ class Cmd_type(enum.Enum):
|
|
|
250
275
|
prompt = Cmd_type_val(
|
|
251
276
|
("prompt",),
|
|
252
277
|
param="<prompt option>",
|
|
253
|
-
description=(
|
|
254
|
-
"history\n" # .
|
|
255
|
-
" Show prompt history\n"
|
|
256
|
-
"history_clear\n"
|
|
257
|
-
" Delete history file\n"
|
|
258
|
-
"add <name:string> <cmd:string>\n"
|
|
259
|
-
" Add a custom prompt\n"
|
|
260
|
-
" e.g. prompt add myprompt echo my prompt\n"
|
|
261
|
-
"del <name:string>\n"
|
|
262
|
-
" Delete a custom prompt"
|
|
263
|
-
"show\n"
|
|
264
|
-
" Show custom prompt"
|
|
265
|
-
),
|
|
266
278
|
childs=(
|
|
267
|
-
Cmd_type_val(("history",)),
|
|
268
|
-
Cmd_type_val(("history_clear",)),
|
|
269
|
-
Cmd_type_val(
|
|
270
|
-
|
|
271
|
-
|
|
279
|
+
Cmd_type_val(("history",), description="Show prompt history"),
|
|
280
|
+
Cmd_type_val(("history_clear",), description="Delete history file"),
|
|
281
|
+
Cmd_type_val(
|
|
282
|
+
("add",),
|
|
283
|
+
param="<name:string> <cmd:string>",
|
|
284
|
+
description=(
|
|
285
|
+
"Add a custom prompt\n" # .
|
|
286
|
+
"e.g. prompt add myprompt echo my prompt"
|
|
287
|
+
),
|
|
288
|
+
),
|
|
289
|
+
Cmd_type_val(
|
|
290
|
+
("del",), param="<name:string>", description="Delete a custom prompt"
|
|
291
|
+
),
|
|
292
|
+
Cmd_type_val(("show",), description="Show custom prompt"),
|
|
272
293
|
),
|
|
273
294
|
)
|
|
274
295
|
translate = Cmd_type_val(
|
|
@@ -287,6 +308,7 @@ class Cmd_type(enum.Enum):
|
|
|
287
308
|
Cmd_type_val(("fd",)),
|
|
288
309
|
Cmd_type_val(("cfd",)),
|
|
289
310
|
),
|
|
311
|
+
is_all_no_doc_childs=True,
|
|
290
312
|
)
|
|
291
313
|
assinfo = Cmd_type_val(
|
|
292
314
|
("assinfo",),
|
|
@@ -296,6 +318,7 @@ class Cmd_type(enum.Enum):
|
|
|
296
318
|
Cmd_type_val(("fd",)),
|
|
297
319
|
Cmd_type_val(("cfd",)),
|
|
298
320
|
),
|
|
321
|
+
is_all_no_doc_childs=True,
|
|
299
322
|
)
|
|
300
323
|
fontinfo = Cmd_type_val(
|
|
301
324
|
("fontinfo",),
|
|
@@ -305,6 +328,7 @@ class Cmd_type(enum.Enum):
|
|
|
305
328
|
Cmd_type_val(("fd",)),
|
|
306
329
|
Cmd_type_val(("cfd",)),
|
|
307
330
|
),
|
|
331
|
+
is_all_no_doc_childs=True,
|
|
308
332
|
)
|
|
309
333
|
Option = Cmd_type_val(
|
|
310
334
|
("Option",),
|
|
@@ -325,7 +349,7 @@ class Cmd_type(enum.Enum):
|
|
|
325
349
|
|
|
326
350
|
@classmethod
|
|
327
351
|
def to_doc(cls) -> str:
|
|
328
|
-
return "\n
|
|
352
|
+
return "\n".join(ct.value.to_doc() for ct in cls)
|
|
329
353
|
|
|
330
354
|
|
|
331
355
|
class Opt_type(enum.Enum):
|
|
@@ -340,6 +364,7 @@ class Opt_type(enum.Enum):
|
|
|
340
364
|
Cmd_type_val(("fd",)),
|
|
341
365
|
Cmd_type_val(("cfd",)),
|
|
342
366
|
),
|
|
367
|
+
is_all_no_doc_childs=True,
|
|
343
368
|
)
|
|
344
369
|
_o_dir = Cmd_type_val(
|
|
345
370
|
("-o:dir",),
|
|
@@ -361,10 +386,12 @@ class Opt_type(enum.Enum):
|
|
|
361
386
|
description=(
|
|
362
387
|
"If enable, output file name will add auto infix:\n"
|
|
363
388
|
" no audio: '.v'\n"
|
|
364
|
-
" with audio: '.va'
|
|
365
|
-
|
|
389
|
+
" with audio: '.va'"
|
|
390
|
+
),
|
|
391
|
+
childs=(
|
|
392
|
+
Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
|
|
393
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
366
394
|
),
|
|
367
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
368
395
|
)
|
|
369
396
|
_preset = _p = Cmd_type_val(
|
|
370
397
|
("-p", "-preset"),
|
|
@@ -375,7 +402,9 @@ class Opt_type(enum.Enum):
|
|
|
375
402
|
"Preset name:\n"
|
|
376
403
|
f"{Preset_name.to_help_string(' ')}"
|
|
377
404
|
),
|
|
378
|
-
childs=(
|
|
405
|
+
childs=(
|
|
406
|
+
Cmd_type_val(tuple(Preset_name._value2member_map_), is_no_doc_child=True),
|
|
407
|
+
),
|
|
379
408
|
)
|
|
380
409
|
_pipe = Cmd_type_val(
|
|
381
410
|
("-pipe",),
|
|
@@ -411,7 +440,7 @@ class Opt_type(enum.Enum):
|
|
|
411
440
|
"'auto:...' can only select which match infix.\n"
|
|
412
441
|
" e.g. 'auto:zh-Hans:zh-Hant'"
|
|
413
442
|
),
|
|
414
|
-
childs=(Cmd_type_val(("auto",)),),
|
|
443
|
+
childs=(Cmd_type_val(("auto",), is_no_doc_child=True),),
|
|
415
444
|
)
|
|
416
445
|
_only_mux_sub_path = Cmd_type_val(
|
|
417
446
|
("-only-mux-sub-path",),
|
|
@@ -425,7 +454,7 @@ class Opt_type(enum.Enum):
|
|
|
425
454
|
"Mux ASS subtitles in MKV with subset\n" # .
|
|
426
455
|
"The usage of 'auto' is detailed in '-sub'"
|
|
427
456
|
),
|
|
428
|
-
childs=(Cmd_type_val(("auto",)),),
|
|
457
|
+
childs=(Cmd_type_val(("auto",), is_no_doc_child=True),),
|
|
429
458
|
)
|
|
430
459
|
_subset_font_dir = Cmd_type_val(
|
|
431
460
|
("-subset-font-dir",),
|
|
@@ -438,20 +467,20 @@ class Opt_type(enum.Enum):
|
|
|
438
467
|
_subset_font_in_sub = Cmd_type_val(
|
|
439
468
|
("-subset-font-in-sub",),
|
|
440
469
|
param="<0 | 1>",
|
|
441
|
-
description=
|
|
442
|
-
|
|
443
|
-
"Default: 0"
|
|
470
|
+
description="Encode fonts into ASS file instead of standalone files",
|
|
471
|
+
childs=(
|
|
472
|
+
Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
|
|
473
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
444
474
|
),
|
|
445
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
446
475
|
)
|
|
447
476
|
_subset_use_win_font = Cmd_type_val(
|
|
448
477
|
("-subset-use-win-font",),
|
|
449
478
|
param="<0 | 1>",
|
|
450
|
-
description=
|
|
451
|
-
|
|
452
|
-
"Default: 0"
|
|
479
|
+
description="Use Windows fonts when can not find font in subset-font-dir",
|
|
480
|
+
childs=(
|
|
481
|
+
Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
|
|
482
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
453
483
|
),
|
|
454
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
455
484
|
)
|
|
456
485
|
_subset_use_libass_spec = Cmd_type_val(
|
|
457
486
|
("-subset-use-libass-spec",),
|
|
@@ -459,38 +488,44 @@ class Opt_type(enum.Enum):
|
|
|
459
488
|
description=(
|
|
460
489
|
"Use libass specification when subset\n"
|
|
461
490
|
'e.g. "11\\{22}33" ->\n'
|
|
462
|
-
'
|
|
463
|
-
'
|
|
464
|
-
|
|
491
|
+
' "11\\33" (VSFilter)\n'
|
|
492
|
+
' "11{22}33" (libass)'
|
|
493
|
+
),
|
|
494
|
+
childs=(
|
|
495
|
+
Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
|
|
496
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
465
497
|
),
|
|
466
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
467
498
|
)
|
|
468
499
|
_subset_drop_non_render = Cmd_type_val(
|
|
469
500
|
("-subset-drop-non-render",),
|
|
470
501
|
param="<0 | 1>",
|
|
471
502
|
description=(
|
|
472
|
-
"Drop non rendered content such as Comment lines, Name, Effect, etc. in ASS
|
|
473
|
-
|
|
503
|
+
"Drop non rendered content such as Comment lines, Name, Effect, etc. in ASS"
|
|
504
|
+
),
|
|
505
|
+
childs=(
|
|
506
|
+
Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
|
|
507
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
474
508
|
),
|
|
475
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
476
509
|
)
|
|
477
510
|
_subset_drop_unkow_data = Cmd_type_val(
|
|
478
511
|
("-subset-drop-unkow-data",),
|
|
479
512
|
param="<0 | 1>",
|
|
480
513
|
description=(
|
|
481
|
-
"Drop lines that are not in {[Script Info], [V4+ Styles], [Events]} in ASS
|
|
482
|
-
|
|
514
|
+
"Drop lines that are not in {[Script Info], [V4+ Styles], [Events]} in ASS"
|
|
515
|
+
),
|
|
516
|
+
childs=(
|
|
517
|
+
Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
|
|
518
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
483
519
|
),
|
|
484
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
485
520
|
)
|
|
486
521
|
_subset_strict = Cmd_type_val(
|
|
487
522
|
("-subset-strict",),
|
|
488
523
|
param="<0 | 1>",
|
|
489
|
-
description=
|
|
490
|
-
|
|
491
|
-
"Default: 0"
|
|
524
|
+
description="Some error will interrupt subset",
|
|
525
|
+
childs=(
|
|
526
|
+
Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
|
|
527
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
492
528
|
),
|
|
493
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
494
529
|
)
|
|
495
530
|
_translate_sub = Cmd_type_val(
|
|
496
531
|
("-translate-sub",),
|
|
@@ -509,12 +544,15 @@ class Opt_type(enum.Enum):
|
|
|
509
544
|
"Audio encoder:\n"
|
|
510
545
|
f"{Audio_codec.to_help_string(' ')}"
|
|
511
546
|
),
|
|
512
|
-
childs=(
|
|
547
|
+
childs=(
|
|
548
|
+
Cmd_type_val(tuple(Audio_codec._value2member_map_), is_no_doc_child=True),
|
|
549
|
+
),
|
|
513
550
|
)
|
|
514
551
|
_b_a = Cmd_type_val(
|
|
515
552
|
("-b:a",),
|
|
516
553
|
param="<string>",
|
|
517
|
-
description="Setting audio bitrate
|
|
554
|
+
description="Setting audio bitrate",
|
|
555
|
+
childs=(Cmd_type_val(("Default:",), param="160k", is_no_prompt_child=True),),
|
|
518
556
|
)
|
|
519
557
|
_muxer = Cmd_type_val(
|
|
520
558
|
("-muxer",),
|
|
@@ -523,9 +561,17 @@ class Opt_type(enum.Enum):
|
|
|
523
561
|
"Setting muxer\n"
|
|
524
562
|
"\n" # .
|
|
525
563
|
"Muxer:\n"
|
|
526
|
-
f"{
|
|
564
|
+
f"{Muxer.to_help_string(' ')}"
|
|
565
|
+
),
|
|
566
|
+
childs=(Cmd_type_val(tuple(Muxer._value2member_map_), is_no_doc_child=True),),
|
|
567
|
+
)
|
|
568
|
+
_track_name = Cmd_type_val(
|
|
569
|
+
("-track-name",),
|
|
570
|
+
param="<string>",
|
|
571
|
+
description=(
|
|
572
|
+
"Python list[str] format\n" # .
|
|
573
|
+
"e.g. \"['0:name1', '1:name2']\""
|
|
527
574
|
),
|
|
528
|
-
childs=(Cmd_type_val(tuple(Muxer._value2member_map_)),),
|
|
529
575
|
)
|
|
530
576
|
_r = _fps = Cmd_type_val(
|
|
531
577
|
("-r", "-fps"),
|
|
@@ -534,7 +580,7 @@ class Opt_type(enum.Enum):
|
|
|
534
580
|
"Setting FPS when muxing\n"
|
|
535
581
|
"When using auto, the frame rate is automatically obtained from the input video and adsorbed to the nearest preset point"
|
|
536
582
|
),
|
|
537
|
-
childs=(Cmd_type_val(("auto",)),),
|
|
583
|
+
childs=(Cmd_type_val(("auto",), is_no_doc_child=True),),
|
|
538
584
|
)
|
|
539
585
|
_chapters = Cmd_type_val(
|
|
540
586
|
("-chapters",),
|
|
@@ -564,26 +610,23 @@ class Opt_type(enum.Enum):
|
|
|
564
610
|
_run = Cmd_type_val(
|
|
565
611
|
("-run",),
|
|
566
612
|
param="[<string>]",
|
|
567
|
-
description=(
|
|
568
|
-
"Run the Ripper from the Ripper list\n"
|
|
569
|
-
"\n"
|
|
570
|
-
"Default:\n"
|
|
571
|
-
" Only run\n"
|
|
572
|
-
"\n"
|
|
573
|
-
"exit:\n"
|
|
574
|
-
" Close program when run finished\n"
|
|
575
|
-
"\n"
|
|
576
|
-
"shutdown [<sec>]:\n"
|
|
577
|
-
" Shutdown when run finished\n"
|
|
578
|
-
" Default: 60\n"
|
|
579
|
-
"\n"
|
|
580
|
-
"server [<address>]:[<port>]@[<password>]:\n"
|
|
581
|
-
" See the corresponding help for details"
|
|
582
|
-
),
|
|
613
|
+
description=("Run the Ripper from the Ripper list"),
|
|
583
614
|
childs=(
|
|
584
|
-
Cmd_type_val(("
|
|
585
|
-
Cmd_type_val(("
|
|
586
|
-
Cmd_type_val(
|
|
615
|
+
Cmd_type_val(("Default",), description="Only run", is_no_prompt_child=True),
|
|
616
|
+
Cmd_type_val(("exit",), description="Close program when run finished"),
|
|
617
|
+
Cmd_type_val(
|
|
618
|
+
("shutdown",),
|
|
619
|
+
param="[<sec>]",
|
|
620
|
+
description=(
|
|
621
|
+
"Shutdown when run finished\n" # .
|
|
622
|
+
"Default: 60"
|
|
623
|
+
),
|
|
624
|
+
),
|
|
625
|
+
Cmd_type_val(
|
|
626
|
+
("server",),
|
|
627
|
+
param="[<address>]:[<port>]@[<password>]",
|
|
628
|
+
description="See the corresponding help for details",
|
|
629
|
+
),
|
|
587
630
|
),
|
|
588
631
|
)
|
|
589
632
|
_ff_params_ff = _ff_params = Cmd_type_val(
|
|
@@ -615,19 +658,15 @@ class Opt_type(enum.Enum):
|
|
|
615
658
|
param="<string>",
|
|
616
659
|
description="Use FFmpeg hwaccel (See 'ffmpeg -hwaccels' for details)",
|
|
617
660
|
childs=(
|
|
618
|
-
Cmd_type_val(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
"d3d12va",
|
|
628
|
-
"amf",
|
|
629
|
-
)
|
|
630
|
-
),
|
|
661
|
+
Cmd_type_val(("cuda",)),
|
|
662
|
+
Cmd_type_val(("vaapi",)),
|
|
663
|
+
Cmd_type_val(("dxva2",)),
|
|
664
|
+
Cmd_type_val(("qsv",)),
|
|
665
|
+
Cmd_type_val(("d3d11va",)),
|
|
666
|
+
Cmd_type_val(("opencl",)),
|
|
667
|
+
Cmd_type_val(("vulkan",)),
|
|
668
|
+
Cmd_type_val(("d3d12va",)),
|
|
669
|
+
Cmd_type_val(("amf",)),
|
|
631
670
|
),
|
|
632
671
|
)
|
|
633
672
|
_ss = Cmd_type_val(
|
|
@@ -649,11 +688,11 @@ class Opt_type(enum.Enum):
|
|
|
649
688
|
_hevc_strict = Cmd_type_val(
|
|
650
689
|
("-hevc-strict",),
|
|
651
690
|
param="<0 | 1>",
|
|
652
|
-
description=
|
|
653
|
-
|
|
654
|
-
"Default: 1"
|
|
691
|
+
description="When the resolution >= 4K, close HME, and auto reduce the -ref",
|
|
692
|
+
childs=(
|
|
693
|
+
Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
|
|
694
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
655
695
|
),
|
|
656
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
657
696
|
)
|
|
658
697
|
_multithreading = Cmd_type_val(
|
|
659
698
|
("-multithreading",),
|
|
@@ -662,7 +701,24 @@ class Opt_type(enum.Enum):
|
|
|
662
701
|
"Use multi-threading to run Ripper list, suitable for situations with low performance occupancy\n"
|
|
663
702
|
"e.g. -p subset or -p copy"
|
|
664
703
|
),
|
|
665
|
-
childs=(
|
|
704
|
+
childs=(
|
|
705
|
+
Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
|
|
706
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
707
|
+
),
|
|
708
|
+
)
|
|
709
|
+
_quality_detection = Cmd_type_val(
|
|
710
|
+
("-quality-detection",),
|
|
711
|
+
param="<algorithm>[:<threshold>]",
|
|
712
|
+
description=(
|
|
713
|
+
"Comparison of quality between detection and source after encoding is completed\n"
|
|
714
|
+
"\n"
|
|
715
|
+
"Algorithm:"
|
|
716
|
+
),
|
|
717
|
+
childs=(
|
|
718
|
+
Cmd_type_val(("ssim",), description="Default threshold: 0.9"),
|
|
719
|
+
Cmd_type_val(("psnr",), description="Default threshold: 30"),
|
|
720
|
+
Cmd_type_val(("vmaf",), description="Default threshold: 80"),
|
|
721
|
+
),
|
|
666
722
|
)
|
|
667
723
|
|
|
668
724
|
@classmethod
|
|
@@ -674,12 +730,13 @@ class Opt_type(enum.Enum):
|
|
|
674
730
|
|
|
675
731
|
@classmethod
|
|
676
732
|
def to_doc(cls) -> str:
|
|
677
|
-
return "\n
|
|
733
|
+
return "\n".join(ct.value.to_doc() for ct in cls)
|
|
678
734
|
|
|
679
735
|
|
|
680
736
|
Cmd_type.help.value.childs = tuple(
|
|
681
737
|
ct.value for ct in itertools.chain(Cmd_type, Opt_type) if ct is not Cmd_type.help
|
|
682
738
|
)
|
|
739
|
+
Cmd_type.help.value.is_all_no_doc_childs = True
|
|
683
740
|
|
|
684
741
|
|
|
685
742
|
def get_help_doc() -> str:
|
easyrip/easyrip_main.py
CHANGED
|
@@ -467,7 +467,17 @@ def run_command(command: Iterable[str] | str) -> bool:
|
|
|
467
467
|
log.error("'{}' does not exist", cmd_list[1])
|
|
468
468
|
|
|
469
469
|
case _:
|
|
470
|
-
|
|
470
|
+
_child = _want_doc_cmd_type.value
|
|
471
|
+
for s in cmd_list[2:-1]:
|
|
472
|
+
for ch in _child.childs:
|
|
473
|
+
if s in ch.names:
|
|
474
|
+
_child = ch
|
|
475
|
+
break
|
|
476
|
+
else:
|
|
477
|
+
log.error("'{}' does not exist", s)
|
|
478
|
+
break
|
|
479
|
+
else:
|
|
480
|
+
log.send(_child.to_doc(), is_format=False)
|
|
471
481
|
else:
|
|
472
482
|
log.send(get_help_doc(), is_format=False)
|
|
473
483
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from ..easyrip_command import Audio_codec, Cmd_type, Opt_type, Preset_name
|
|
1
|
+
from ..easyrip_command import Audio_codec, Cmd_type, Muxer, Opt_type, Preset_name
|
|
2
2
|
from .global_lang_val import Lang_tag
|
|
3
3
|
|
|
4
4
|
LANG_TAG = Lang_tag(
|
|
@@ -46,43 +46,27 @@ LANG_MAP: dict[str, str] = {
|
|
|
46
46
|
Cmd_type.mkdir.value.description: "新建文件目录",
|
|
47
47
|
Cmd_type.cls.value.description: "清屏",
|
|
48
48
|
Cmd_type.list.value.param: "<list 选项>",
|
|
49
|
-
Cmd_type.list.value.description:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
" \n"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
" 删除 Ripper list 中指定的一个 Ripper\n"
|
|
60
|
-
" \n"
|
|
61
|
-
"sort [n][r]:\n"
|
|
62
|
-
" 排序 list\n"
|
|
63
|
-
" 'n': 自然排序\n"
|
|
64
|
-
" 'r': 倒序\n"
|
|
65
|
-
" \n"
|
|
66
|
-
"<int> <int>:\n"
|
|
67
|
-
" 交换指定索引"
|
|
68
|
-
),
|
|
49
|
+
Cmd_type.list.value.description: "操作 Ripper list",
|
|
50
|
+
Cmd_type.list.value.childs[0].description: "打印 Ripper list",
|
|
51
|
+
Cmd_type.list.value.childs[1].description: "清空 Ripper list",
|
|
52
|
+
Cmd_type.list.value.childs[2].description: "删除 Ripper list 中指定的一个 Ripper",
|
|
53
|
+
Cmd_type.list.value.childs[3].description: (
|
|
54
|
+
"排序 list\n" # .
|
|
55
|
+
"'n': 自然排序\n"
|
|
56
|
+
"'r': 倒序"
|
|
57
|
+
),
|
|
58
|
+
Cmd_type.list.value.childs[4].description: "交换指定索引",
|
|
69
59
|
Cmd_type.run.value.param: "[<run 选项>] [-multithreading <0 | 1>]",
|
|
70
|
-
Cmd_type.run.value.description:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
" 执行后关机\n"
|
|
81
|
-
" 默认: 60\n"
|
|
82
|
-
"\n"
|
|
83
|
-
"server [<地址>]:[<端口>]@[<密码>]:\n"
|
|
84
|
-
" 详见对应的 help"
|
|
85
|
-
),
|
|
60
|
+
Cmd_type.run.value.description: "执行 Ripper list 中的 Ripper",
|
|
61
|
+
Cmd_type.run.value.childs[0].description: "仅执行",
|
|
62
|
+
Cmd_type.run.value.childs[1].description: "执行后退出程序",
|
|
63
|
+
Cmd_type.run.value.childs[2].param: "[<秒数>]",
|
|
64
|
+
Cmd_type.run.value.childs[2].description: (
|
|
65
|
+
"执行后关机\n" # .
|
|
66
|
+
"默认: 60"
|
|
67
|
+
),
|
|
68
|
+
Cmd_type.run.value.childs[3].param: "[<地址>]:[<端口>]@[<密码>]",
|
|
69
|
+
Cmd_type.run.value.childs[3].description: "详见对应的 help",
|
|
86
70
|
Cmd_type.server.value.param: "[<地址>]:[<端口>]@[<密码>]",
|
|
87
71
|
Cmd_type.server.value.description: (
|
|
88
72
|
"启动 web 服务\n"
|
|
@@ -90,18 +74,22 @@ LANG_MAP: dict[str, str] = {
|
|
|
90
74
|
"客户端发送命令 'kill' 可以退出 Ripper 的运行, 注意, FFmpeg需要接受多次^C信号才能强制终止, 单次^C会等待文件输出完才会终止"
|
|
91
75
|
),
|
|
92
76
|
Cmd_type.config.value.param: "<config 选项>",
|
|
93
|
-
Cmd_type.config.value.description:
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
" 展示所有 config 可调选项\n"
|
|
100
|
-
"set <key> <val>\n"
|
|
101
|
-
" 设置 config\n"
|
|
102
|
-
" 例如 config set language en"
|
|
77
|
+
Cmd_type.config.value.childs[0].description: "重新生成 config 文件",
|
|
78
|
+
Cmd_type.config.value.childs[1].description: "打开 config 文件所在目录",
|
|
79
|
+
Cmd_type.config.value.childs[2].description: "展示所有 config 可调选项",
|
|
80
|
+
Cmd_type.config.value.childs[3].description: (
|
|
81
|
+
"设置 config\n" # .
|
|
82
|
+
"例如 config set language en"
|
|
103
83
|
),
|
|
104
84
|
Cmd_type.prompt.value.param: "<prompt 选项>",
|
|
85
|
+
Cmd_type.prompt.value.childs[0].description: "打印 prompt 历史",
|
|
86
|
+
Cmd_type.prompt.value.childs[1].description: "删除 prompt 历史文件",
|
|
87
|
+
Cmd_type.prompt.value.childs[2].description: (
|
|
88
|
+
"增加一个自定义 prompt\n" # .
|
|
89
|
+
"e.g. prompt add myprompt echo my prompt"
|
|
90
|
+
),
|
|
91
|
+
Cmd_type.prompt.value.childs[3].description: "删除一个自定义 prompt",
|
|
92
|
+
Cmd_type.prompt.value.childs[4].description: "打印自定义 prompt",
|
|
105
93
|
Cmd_type.translate.value.param: "<中缀> <目标语言标签> [-overwrite]",
|
|
106
94
|
Cmd_type.translate.value.description: (
|
|
107
95
|
"翻译字幕文件\n"
|
|
@@ -125,8 +113,7 @@ LANG_MAP: dict[str, str] = {
|
|
|
125
113
|
Opt_type._auto_infix.value.description: (
|
|
126
114
|
"如果启用, 输出的文件将添加自动中缀:\n" # .
|
|
127
115
|
" 无音轨: '.v'\n"
|
|
128
|
-
" 有音轨: '.va'
|
|
129
|
-
"默认: 1"
|
|
116
|
+
" 有音轨: '.va'"
|
|
130
117
|
),
|
|
131
118
|
Opt_type._preset.value.description: (
|
|
132
119
|
"设置预设\n"
|
|
@@ -162,32 +149,24 @@ LANG_MAP: dict[str, str] = {
|
|
|
162
149
|
'默认: 优先当前目录, 其次当前目录下含有 "font" 的文件夹 (不分大小写)'
|
|
163
150
|
),
|
|
164
151
|
Opt_type._subset_font_in_sub.value.description: (
|
|
165
|
-
"将字体编码到 ASS 文件中,
|
|
166
|
-
"默认: 0"
|
|
152
|
+
"将字体编码到 ASS 文件中, 而不是单独的字体文件"
|
|
167
153
|
),
|
|
168
154
|
Opt_type._subset_use_win_font.value.description: (
|
|
169
|
-
"无法从 subset-font-dir 找到字体时使用 Windows
|
|
170
|
-
"默认: 0"
|
|
155
|
+
"无法从 subset-font-dir 找到字体时使用 Windows 字体"
|
|
171
156
|
),
|
|
172
157
|
Opt_type._subset_use_libass_spec.value.description: (
|
|
173
158
|
"子集化时使用 libass 规范\n"
|
|
174
159
|
'e.g. "11\\{22}33" ->\n'
|
|
175
|
-
'
|
|
176
|
-
'
|
|
177
|
-
"默认: 1"
|
|
160
|
+
' "11\\33" (VSFilter)\n'
|
|
161
|
+
' "11{22}33" (libass)'
|
|
178
162
|
),
|
|
179
163
|
Opt_type._subset_drop_non_render.value.description: (
|
|
180
|
-
"丢弃 ASS 中的注释行、Name、Effect
|
|
181
|
-
"默认: 1"
|
|
164
|
+
"丢弃 ASS 中的注释行、Name、Effect等非渲染内容"
|
|
182
165
|
),
|
|
183
166
|
Opt_type._subset_drop_unkow_data.value.description: (
|
|
184
|
-
"丢弃 ASS 中的非 {[Script Info], [V4+ Styles], [Events]}
|
|
185
|
-
"默认: 1"
|
|
186
|
-
),
|
|
187
|
-
Opt_type._subset_strict.value.description: (
|
|
188
|
-
"子集化时报错则中断\n" # .
|
|
189
|
-
"默认: 0"
|
|
167
|
+
"丢弃 ASS 中的非 {[Script Info], [V4+ Styles], [Events]} 行"
|
|
190
168
|
),
|
|
169
|
+
Opt_type._subset_strict.value.description: "子集化时报错则中断",
|
|
191
170
|
Opt_type._translate_sub.value.param: "<中缀>:<语言标签>",
|
|
192
171
|
Opt_type._translate_sub.value.description: (
|
|
193
172
|
"临时生成字幕的翻译文件\n" # .
|
|
@@ -199,13 +178,12 @@ LANG_MAP: dict[str, str] = {
|
|
|
199
178
|
"音频编码器:\n"
|
|
200
179
|
f"{Audio_codec.to_help_string(' ')}"
|
|
201
180
|
),
|
|
202
|
-
Opt_type._b_a.value.description: "
|
|
181
|
+
Opt_type._b_a.value.description: "设置音频码率",
|
|
203
182
|
Opt_type._muxer.value.description: (
|
|
204
183
|
"设置复用器\n"
|
|
205
184
|
"\n" # .
|
|
206
185
|
"可用的复用器:\n"
|
|
207
|
-
"
|
|
208
|
-
" mkv"
|
|
186
|
+
f"{Muxer.to_help_string(' ')}"
|
|
209
187
|
),
|
|
210
188
|
Opt_type._r.value.description: (
|
|
211
189
|
"设置封装的帧率\n" # .
|
|
@@ -224,22 +202,6 @@ LANG_MAP: dict[str, str] = {
|
|
|
224
202
|
"当 -preset custom 时, 这个选项将作为输出文件的后缀\n" # .
|
|
225
203
|
'默认: ""'
|
|
226
204
|
),
|
|
227
|
-
Opt_type._run.value.description: (
|
|
228
|
-
"执行 Ripper list 中的 Ripper\n"
|
|
229
|
-
"\n"
|
|
230
|
-
"默认:\n"
|
|
231
|
-
" 仅执行\n"
|
|
232
|
-
"\n"
|
|
233
|
-
"exit:\n"
|
|
234
|
-
" 执行后退出程序\n"
|
|
235
|
-
"\n"
|
|
236
|
-
"shutdown [<秒数>]:\n"
|
|
237
|
-
" 执行后关机\n"
|
|
238
|
-
" 默认: 60\n"
|
|
239
|
-
"\n"
|
|
240
|
-
"server [<地址>]:[<端口>]@[<密码>]:\n"
|
|
241
|
-
" 详见对应的 help"
|
|
242
|
-
),
|
|
243
205
|
Opt_type._ff_params_ff.value.description: (
|
|
244
206
|
"设置 FFmpeg 的全局选项\n" # .
|
|
245
207
|
"等同于 ffmpeg <option> ... -i ..."
|
|
@@ -262,8 +224,7 @@ LANG_MAP: dict[str, str] = {
|
|
|
262
224
|
"等同于 ffmpeg -i ... -t <time> ..."
|
|
263
225
|
),
|
|
264
226
|
Opt_type._hevc_strict.value.description: (
|
|
265
|
-
"当分辨率 >= 4K 时, 关闭 HME, 并自动降低 -ref
|
|
266
|
-
"默认: 1"
|
|
227
|
+
"当分辨率 >= 4K 时, 关闭 HME, 并自动降低 -ref"
|
|
267
228
|
),
|
|
268
229
|
Opt_type._multithreading.value.description: (
|
|
269
230
|
"使用多线程执行 Ripper list, 适合性能占用低的情况\n" # .
|
|
@@ -327,8 +288,9 @@ LANG_MAP: dict[str, str] = {
|
|
|
327
288
|
"Command run failed: status code {}\n Failed command: {}": "命令执行失败: 状态码 {}\n 失败的命令: {}",
|
|
328
289
|
"There have error in running": "执行时出错",
|
|
329
290
|
"{} param illegal": "{} 参数非法",
|
|
291
|
+
"{} param illegal: {}": "{} 参数非法: {}",
|
|
330
292
|
'The file "{}" already exists, skip translating it': '文件 "{}" 已存在, 跳过翻译',
|
|
331
|
-
"Subset
|
|
293
|
+
"Subset failed, cancel mux": "子集化失败, 取消混流",
|
|
332
294
|
"FFmpeg report: {}": "FFmpeg 报告: {}",
|
|
333
295
|
"{} not found. Skip it": "没找到 {}。默认跳过",
|
|
334
296
|
"{} not found. Skip it. Perhaps you want the {}": "没找到 {}。默认跳过。或许你想要的是 {}",
|
|
@@ -120,7 +120,7 @@ class mkvtoolnix:
|
|
|
120
120
|
log.debug(
|
|
121
121
|
"'{}' execution failed: {}",
|
|
122
122
|
f"{cls.__name__}.{cls.get_latest_release_ver.__name__}",
|
|
123
|
-
f"XML parse
|
|
123
|
+
f"XML parse failed: {xml.etree.ElementTree.tostring(xml_tree)}",
|
|
124
124
|
print_level=log.LogLevel._detail,
|
|
125
125
|
)
|
|
126
126
|
return None
|
easyrip/global_val.py
CHANGED
|
@@ -4,7 +4,7 @@ from functools import cache
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
PROJECT_NAME = "Easy Rip"
|
|
7
|
-
PROJECT_VERSION = "4.
|
|
7
|
+
PROJECT_VERSION = "4.15.1"
|
|
8
8
|
PROJECT_TITLE = f"{PROJECT_NAME} v{PROJECT_VERSION}"
|
|
9
9
|
PROJECT_URL = "https://github.com/op200/EasyRip"
|
|
10
10
|
PROJECT_RELEASE_API = "https://api.github.com/repos/op200/EasyRip/releases/latest"
|
easyrip/ripper/media_info.py
CHANGED
|
@@ -120,7 +120,7 @@ class Media_info:
|
|
|
120
120
|
sample_fmt = _audio_info_dict.get("sample_fmt")
|
|
121
121
|
if sample_fmt is None:
|
|
122
122
|
log.debug(
|
|
123
|
-
'
|
|
123
|
+
'Failed to get audio info: {}. file "{}" track index {}',
|
|
124
124
|
"sample_fmt",
|
|
125
125
|
path,
|
|
126
126
|
index,
|
|
@@ -130,7 +130,7 @@ class Media_info:
|
|
|
130
130
|
sample_rate = _audio_info_dict.get("sample_rate")
|
|
131
131
|
if sample_rate is None:
|
|
132
132
|
log.debug(
|
|
133
|
-
'
|
|
133
|
+
'Failed to get audio info: {}. file "{}" track index {}',
|
|
134
134
|
"sample_rate",
|
|
135
135
|
path,
|
|
136
136
|
index,
|
|
@@ -140,7 +140,7 @@ class Media_info:
|
|
|
140
140
|
bits_per_sample = _audio_info_dict.get("bits_per_sample")
|
|
141
141
|
if bits_per_sample is None:
|
|
142
142
|
log.debug(
|
|
143
|
-
'
|
|
143
|
+
'Failed to get audio info: {}. file "{}" track index {}',
|
|
144
144
|
"bits_per_sample",
|
|
145
145
|
path,
|
|
146
146
|
index,
|
|
@@ -150,7 +150,7 @@ class Media_info:
|
|
|
150
150
|
bits_per_raw_sample = _audio_info_dict.get("bits_per_raw_sample")
|
|
151
151
|
if bits_per_raw_sample is None:
|
|
152
152
|
log.debug(
|
|
153
|
-
'
|
|
153
|
+
'Failed to get audio info: {}. file "{}" track index {}',
|
|
154
154
|
"bits_per_raw_sample",
|
|
155
155
|
path,
|
|
156
156
|
index,
|
easyrip/ripper/ripper.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import csv
|
|
1
3
|
import os
|
|
2
4
|
import re
|
|
3
5
|
import shutil
|
|
@@ -13,8 +15,13 @@ from typing import Final, Self, final
|
|
|
13
15
|
|
|
14
16
|
from .. import easyrip_web
|
|
15
17
|
from ..easyrip_log import log
|
|
16
|
-
from ..easyrip_mlang import
|
|
17
|
-
|
|
18
|
+
from ..easyrip_mlang import (
|
|
19
|
+
Global_lang_val,
|
|
20
|
+
Mlang_exception,
|
|
21
|
+
gettext,
|
|
22
|
+
translate_subtitles,
|
|
23
|
+
)
|
|
24
|
+
from ..utils import get_base62_time, read_text, type_match
|
|
18
25
|
from .media_info import Media_info, Stream_error
|
|
19
26
|
from .param import (
|
|
20
27
|
FONT_SUFFIX_SET,
|
|
@@ -244,6 +251,37 @@ class Ripper:
|
|
|
244
251
|
|
|
245
252
|
# Muxer
|
|
246
253
|
muxer_format_str_list: list[str]
|
|
254
|
+
_track_name_org_str = self.option_map.get("track-name", "[]")
|
|
255
|
+
try:
|
|
256
|
+
track_name_list = ast.literal_eval(_track_name_org_str)
|
|
257
|
+
except Exception as e:
|
|
258
|
+
raise Mlang_exception("{} param illegal", "-track-name") from e
|
|
259
|
+
if not type_match(track_name_list, list[str]):
|
|
260
|
+
raise Mlang_exception("{} param illegal", "-track-name")
|
|
261
|
+
for i in range(len(track_name_list)):
|
|
262
|
+
track_name = track_name_list[i]
|
|
263
|
+
if '"' in track_name:
|
|
264
|
+
if "'" in track_name:
|
|
265
|
+
raise Mlang_exception(
|
|
266
|
+
"{} param illegal: {}",
|
|
267
|
+
"-track-name",
|
|
268
|
+
"The '\"' and \"'\" can not exist simultaneously",
|
|
269
|
+
)
|
|
270
|
+
track_name = f"'{track_name}'"
|
|
271
|
+
else:
|
|
272
|
+
track_name = f'"{track_name}"'
|
|
273
|
+
track_name_list[i] = track_name
|
|
274
|
+
log.debug(f"-track-name <- {_track_name_org_str!r}", is_format=False)
|
|
275
|
+
log.debug(f"-track-name -> {track_name_list!r}", is_format=False)
|
|
276
|
+
|
|
277
|
+
mkv_all_need_opt_str: str = (
|
|
278
|
+
"".join(f"--track-name {track_name} " for track_name in track_name_list)
|
|
279
|
+
) + (
|
|
280
|
+
f"--chapters {chapters} "
|
|
281
|
+
if (chapters := self.option_map.get("chapters"))
|
|
282
|
+
else ""
|
|
283
|
+
)
|
|
284
|
+
|
|
247
285
|
if muxer := self.option_map.get("muxer"):
|
|
248
286
|
muxer = Ripper.Muxer(muxer)
|
|
249
287
|
|
|
@@ -283,11 +321,7 @@ class Ripper:
|
|
|
283
321
|
if force_fps and only_mux_sub_path is None
|
|
284
322
|
else ""
|
|
285
323
|
)
|
|
286
|
-
+
|
|
287
|
-
f"--chapters {chapters} "
|
|
288
|
-
if (chapters := self.option_map.get("chapters"))
|
|
289
|
-
else ""
|
|
290
|
-
)
|
|
324
|
+
+ mkv_all_need_opt_str
|
|
291
325
|
+ (
|
|
292
326
|
" ".join(
|
|
293
327
|
(
|
|
@@ -427,13 +461,19 @@ class Ripper:
|
|
|
427
461
|
_encoder_format_str += f" -rem {_audio_info.index + 1}"
|
|
428
462
|
else:
|
|
429
463
|
_encoder_format_str = (
|
|
430
|
-
'mkvmerge -o "{output}"
|
|
464
|
+
'mkvmerge -o "{output}" '
|
|
465
|
+
+ mkv_all_need_opt_str
|
|
466
|
+
+ '--no-audio "{input}"'
|
|
431
467
|
)
|
|
432
468
|
case "copy":
|
|
433
469
|
_encoder_format_str = (
|
|
434
470
|
'mp4box -add "{input}" -new "{output}"'
|
|
435
471
|
if muxer == Ripper.Muxer.mp4
|
|
436
|
-
else
|
|
472
|
+
else (
|
|
473
|
+
'mkvmerge -o "{output}" '
|
|
474
|
+
+ mkv_all_need_opt_str
|
|
475
|
+
+ '"{input}"'
|
|
476
|
+
)
|
|
437
477
|
)
|
|
438
478
|
if _encoder_format_str is not None:
|
|
439
479
|
encoder_format_str_list = [_encoder_format_str]
|
|
@@ -1088,9 +1128,15 @@ class Ripper:
|
|
|
1088
1128
|
f'{gettext("Encoding speed")}: <span style="color:darkcyan;">{speed}</span><br>'
|
|
1089
1129
|
)
|
|
1090
1130
|
|
|
1131
|
+
# 获取 ffmpeg report 中的报错
|
|
1132
|
+
if FF_REPORT_LOG_FILE.is_file():
|
|
1133
|
+
with FF_REPORT_LOG_FILE.open("rt", encoding="utf-8") as file:
|
|
1134
|
+
for line in file.readlines()[2:]:
|
|
1135
|
+
log.warning("FFmpeg report: {}", line)
|
|
1136
|
+
|
|
1091
1137
|
if is_cmd_run_failed:
|
|
1092
1138
|
log.error("There have error in running")
|
|
1093
|
-
else: # 多文件合成
|
|
1139
|
+
else: # 多文件合成 or 后处理
|
|
1094
1140
|
# flac 音频轨合成
|
|
1095
1141
|
if (
|
|
1096
1142
|
self.preset_name != Ripper.Preset_name.flac
|
|
@@ -1245,16 +1291,133 @@ class Ripper:
|
|
|
1245
1291
|
).run() and os.path.exists(new_full_name):
|
|
1246
1292
|
os.remove(new_full_name)
|
|
1247
1293
|
else:
|
|
1248
|
-
log.error("Subset
|
|
1294
|
+
log.error("Subset failed, cancel mux")
|
|
1249
1295
|
|
|
1250
1296
|
# 清理临时文件
|
|
1251
1297
|
shutil.rmtree(subset_folder)
|
|
1252
1298
|
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1299
|
+
# 画质检测
|
|
1300
|
+
if quality_detection := self.option_map.get("quality-detection"):
|
|
1301
|
+
quality_detection = quality_detection.split(":")
|
|
1302
|
+
quality_detection_th: float
|
|
1303
|
+
quality_detection_filter: str
|
|
1304
|
+
while True:
|
|
1305
|
+
match quality_detection[0]:
|
|
1306
|
+
case "ssim":
|
|
1307
|
+
quality_detection_th = 0.9
|
|
1308
|
+
quality_detection_filter = "ssim=f="
|
|
1309
|
+
|
|
1310
|
+
def quality_detection_cmp(
|
|
1311
|
+
text: str, threshold: float
|
|
1312
|
+
) -> None:
|
|
1313
|
+
for line in text.splitlines():
|
|
1314
|
+
values = tuple(
|
|
1315
|
+
s.split(":")[1] for s in line.split()[:-1]
|
|
1316
|
+
)
|
|
1317
|
+
ssim_all = float(values[-1])
|
|
1318
|
+
n = values[0]
|
|
1319
|
+
log.debug(
|
|
1320
|
+
f"{n}: {ssim_all}",
|
|
1321
|
+
is_format=False,
|
|
1322
|
+
print_level=log.LogLevel._detail,
|
|
1323
|
+
)
|
|
1324
|
+
if ssim_all < threshold:
|
|
1325
|
+
log.error(
|
|
1326
|
+
"SSIM {} < threshold {} in frame {}",
|
|
1327
|
+
ssim_all,
|
|
1328
|
+
threshold,
|
|
1329
|
+
n,
|
|
1330
|
+
)
|
|
1331
|
+
|
|
1332
|
+
break
|
|
1333
|
+
case "psnr":
|
|
1334
|
+
quality_detection_th = 30
|
|
1335
|
+
quality_detection_filter = "psnr=f="
|
|
1336
|
+
|
|
1337
|
+
def quality_detection_cmp(
|
|
1338
|
+
text: str, threshold: float
|
|
1339
|
+
) -> None:
|
|
1340
|
+
for line in text.splitlines():
|
|
1341
|
+
values = tuple(
|
|
1342
|
+
s.split(":")[1] for s in line.split()
|
|
1343
|
+
)
|
|
1344
|
+
psnr_avg_all = float(values[-4])
|
|
1345
|
+
n = values[0]
|
|
1346
|
+
log.debug(
|
|
1347
|
+
f"{n}: {psnr_avg_all}",
|
|
1348
|
+
is_format=False,
|
|
1349
|
+
print_level=log.LogLevel._detail,
|
|
1350
|
+
)
|
|
1351
|
+
if psnr_avg_all < threshold:
|
|
1352
|
+
log.error(
|
|
1353
|
+
"PSNR {} < threshold {} in frame {}",
|
|
1354
|
+
psnr_avg_all,
|
|
1355
|
+
threshold,
|
|
1356
|
+
n,
|
|
1357
|
+
)
|
|
1358
|
+
|
|
1359
|
+
break
|
|
1360
|
+
case "vmaf":
|
|
1361
|
+
quality_detection_th = 80
|
|
1362
|
+
quality_detection_filter = "libvmaf=log_fmt=csv:log_path="
|
|
1363
|
+
|
|
1364
|
+
def quality_detection_cmp(
|
|
1365
|
+
text: str, threshold: float
|
|
1366
|
+
) -> None:
|
|
1367
|
+
for line in tuple(csv.reader(text))[1:]:
|
|
1368
|
+
vmaf = float(line[-1])
|
|
1369
|
+
n = int(line[0]) + 1
|
|
1370
|
+
log.debug(
|
|
1371
|
+
f"{n}: {vmaf}",
|
|
1372
|
+
is_format=False,
|
|
1373
|
+
print_level=log.LogLevel._detail,
|
|
1374
|
+
)
|
|
1375
|
+
if vmaf < threshold:
|
|
1376
|
+
log.error(
|
|
1377
|
+
"VMAF {} < threshold {} in frame {}",
|
|
1378
|
+
vmaf,
|
|
1379
|
+
threshold,
|
|
1380
|
+
n,
|
|
1381
|
+
)
|
|
1382
|
+
|
|
1383
|
+
break
|
|
1384
|
+
case _:
|
|
1385
|
+
log.error(
|
|
1386
|
+
"Param error from '{}': {}",
|
|
1387
|
+
"-quality-detection",
|
|
1388
|
+
f"{quality_detection[0]} -> ssim",
|
|
1389
|
+
)
|
|
1390
|
+
quality_detection[0] = "ssim"
|
|
1391
|
+
|
|
1392
|
+
if len(quality_detection) > 1:
|
|
1393
|
+
try:
|
|
1394
|
+
quality_detection_th = float(quality_detection[1])
|
|
1395
|
+
except ValueError as e:
|
|
1396
|
+
log.error("Param error from '{}': {}", "-quality-detection", e)
|
|
1397
|
+
|
|
1398
|
+
quality_detection_data_file: Path = Path(
|
|
1399
|
+
self.output_dir, "quality_detection_data.log"
|
|
1400
|
+
)
|
|
1401
|
+
quality_detection_data_file_filter_str: str = (
|
|
1402
|
+
str(quality_detection_data_file)
|
|
1403
|
+
.replace("\\", "/")
|
|
1404
|
+
.replace(":", "\\\\:")
|
|
1405
|
+
)
|
|
1406
|
+
if os.system(
|
|
1407
|
+
f'ffmpeg -i "{self.input_path_list[0]}" -i "{os.path.join(self.output_dir, temp_name)}" -lavfi "{quality_detection_filter}{quality_detection_data_file_filter_str}" -f null -'
|
|
1408
|
+
):
|
|
1409
|
+
log.error("Run {} failed", "-quality-detection")
|
|
1410
|
+
else:
|
|
1411
|
+
log.debug(
|
|
1412
|
+
"'{}' start: {}",
|
|
1413
|
+
"-quality-detection",
|
|
1414
|
+
f"{quality_detection[0]}:{quality_detection_th}",
|
|
1415
|
+
)
|
|
1416
|
+
quality_detection_cmp(
|
|
1417
|
+
read_text(quality_detection_data_file), quality_detection_th
|
|
1418
|
+
)
|
|
1419
|
+
log.debug("'{}' end", "-quality-detection")
|
|
1420
|
+
quality_detection_data_file.unlink(missing_ok=True)
|
|
1258
1421
|
|
|
1259
1422
|
# 获取体积
|
|
1260
1423
|
temp_name_full = os.path.join(self.output_dir, temp_name)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easyrip
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.15.1
|
|
4
4
|
Author: op200
|
|
5
5
|
License-Expression: AGPL-3.0-or-later
|
|
6
6
|
Project-URL: Homepage, https://github.com/op200/EasyRip
|
|
@@ -20,6 +20,7 @@ Classifier: Typing :: Typed
|
|
|
20
20
|
Requires-Python: >=3.12
|
|
21
21
|
Description-Content-Type: text/markdown
|
|
22
22
|
License-File: LICENSE
|
|
23
|
+
Requires-Dist: pyperclip>=1.11.0
|
|
23
24
|
Requires-Dist: prompt-toolkit>=3.0.52
|
|
24
25
|
Requires-Dist: fonttools>=4.61.1
|
|
25
26
|
Requires-Dist: pycryptodome>=3.23.0
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
easyrip/__init__.py,sha256=DULQoFEAEHYk7dS8Zxky56so7qDPqHm7jUc_Zop1eXw,616
|
|
2
|
-
easyrip/__main__.py,sha256=
|
|
3
|
-
easyrip/easyrip_command.py,sha256=
|
|
2
|
+
easyrip/__main__.py,sha256=cLtwb8SRXw34e9iddeQ1iP3CTGBVdZPWRu2ISxoCKz4,5879
|
|
3
|
+
easyrip/easyrip_command.py,sha256=rHphOQgBVBKFL8bqiAXjP9we1RUPqaLZyyCDt2niblk,33632
|
|
4
4
|
easyrip/easyrip_log.py,sha256=R-dM3CWUBFITtG7GSD1zy4X4MhZqxkoiBPjlIpI76cY,15573
|
|
5
|
-
easyrip/easyrip_main.py,sha256=
|
|
5
|
+
easyrip/easyrip_main.py,sha256=E1gur17OruwlJtd9h32aDXtnwhOYsjJVpb6JcVYEyRc,48586
|
|
6
6
|
easyrip/easyrip_prompt.py,sha256=TOmRJDigGRz7wRWFNakJdfNI1tn9vGekq6FH5ypmQfA,7068
|
|
7
|
-
easyrip/global_val.py,sha256=
|
|
7
|
+
easyrip/global_val.py,sha256=UDCyNlA-5DQ2WznuzjHHD2Vn7TQ2xZSmHJhSr5Qqjgo,866
|
|
8
8
|
easyrip/utils.py,sha256=N1rMF1MyoC-YFBgy10_u29cFoowfhR-5Viea93O7wQ4,8750
|
|
9
9
|
easyrip/easyrip_config/config.py,sha256=KWXZMEYxdXYUGLQ-MR0A7nnOwR6QZdVrWBopfb2QZSA,9869
|
|
10
10
|
easyrip/easyrip_config/config_key.py,sha256=_jjdKOunskUoG7UUWOz3QZK-s4LF_x6hmM9MKttyS2Q,766
|
|
@@ -12,21 +12,21 @@ easyrip/easyrip_mlang/__init__.py,sha256=QqnL0kgV_trGPeLF5gawP1qAlj0GXUadLNhMSdK
|
|
|
12
12
|
easyrip/easyrip_mlang/global_lang_val.py,sha256=pG9DxPl6vUOZoFHYQKCM-AM0TYWbd8L4S65CUQFPRh4,4998
|
|
13
13
|
easyrip/easyrip_mlang/lang_en.py,sha256=fTM9ejuPW6gEfSMbnMEW-LzlUfvj0YGfoUfmHZpRzms,121
|
|
14
14
|
easyrip/easyrip_mlang/lang_tag_val.py,sha256=Ec-U0XglpSYvmkHlcEBueSj8ocTLSTH3cacElAkmYVU,5184
|
|
15
|
-
easyrip/easyrip_mlang/lang_zh_Hans_CN.py,sha256=
|
|
15
|
+
easyrip/easyrip_mlang/lang_zh_Hans_CN.py,sha256=OuWg_sNEzNkvlF1EPMFDZrucfQ1Y92zKFkIfXIfiCR8,19009
|
|
16
16
|
easyrip/easyrip_mlang/translator.py,sha256=jlgZYSPHvwv1Pps3akKkSgVsGcLtV2psKaXyZH4QCbA,5870
|
|
17
17
|
easyrip/easyrip_web/__init__.py,sha256=tMyEeaSGeEJjND7MF0MBv9aDiDgaO3MOnppwxA70U2c,177
|
|
18
18
|
easyrip/easyrip_web/http_server.py,sha256=iyulCAFQrJlz86Lrr-Dm3fhOnNCf79Bp6fVHhr0ephY,8350
|
|
19
|
-
easyrip/easyrip_web/third_party_api.py,sha256=
|
|
20
|
-
easyrip/ripper/media_info.py,sha256=
|
|
19
|
+
easyrip/easyrip_web/third_party_api.py,sha256=E-60yoY6D0pPUfYW1VIh0763htyV5z6getzlLtLAdQc,4624
|
|
20
|
+
easyrip/ripper/media_info.py,sha256=KdSodS6nIp2BWEer5y4mD5xwyhP15_PgNRhz2fnHmw0,5082
|
|
21
21
|
easyrip/ripper/param.py,sha256=PfJzJz9LPCB5hAM9G4GjPxdn_EZRgAz-vxYzuHGQLp8,13084
|
|
22
|
-
easyrip/ripper/ripper.py,sha256=
|
|
22
|
+
easyrip/ripper/ripper.py,sha256=NynEAr3IzjdgC9dwKRPzTA0eTWf7Z3dJUU0eB2_R-6E,60364
|
|
23
23
|
easyrip/ripper/sub_and_font/__init__.py,sha256=cBT7mxL7RRFaJXFPXuZ7RT-YK6FbnanaU5v6U9BOquw,153
|
|
24
24
|
easyrip/ripper/sub_and_font/ass.py,sha256=EhDkVY5JXU77euWPId7H2v85j444m8ZLm7wUid7TYd8,35307
|
|
25
25
|
easyrip/ripper/sub_and_font/font.py,sha256=X2dPcPzbwQf3fv_g_mxO-zY7puVAX9Nv-9QHn88q4oA,7745
|
|
26
26
|
easyrip/ripper/sub_and_font/subset.py,sha256=--rAA3VH1rm_jBOC3yMs3rOJpn3tPuvfXqkimbBtx3s,18653
|
|
27
|
-
easyrip-4.
|
|
28
|
-
easyrip-4.
|
|
29
|
-
easyrip-4.
|
|
30
|
-
easyrip-4.
|
|
31
|
-
easyrip-4.
|
|
32
|
-
easyrip-4.
|
|
27
|
+
easyrip-4.15.1.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
28
|
+
easyrip-4.15.1.dist-info/METADATA,sha256=OS5rQst0rSXHYrhJ7CpQbImZ_PU0nlfKAHNSPOHQ2iA,3540
|
|
29
|
+
easyrip-4.15.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
30
|
+
easyrip-4.15.1.dist-info/entry_points.txt,sha256=D6GBMMTzZ-apgX76KyZ6jxMmIFqGYwU9neeLLni_qKI,49
|
|
31
|
+
easyrip-4.15.1.dist-info/top_level.txt,sha256=kuEteBXm-Gf90jRQgH3-fTo-Z-Q6czSuUEqY158H4Ww,8
|
|
32
|
+
easyrip-4.15.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|