easyrip 4.15.0__py3-none-any.whl → 4.15.2__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 +5 -33
- easyrip/easyrip_command.py +264 -203
- easyrip/easyrip_main.py +17 -1
- easyrip/easyrip_mlang/lang_zh_Hans_CN.py +47 -85
- easyrip/easyrip_prompt.py +8 -6
- easyrip/global_val.py +1 -1
- easyrip/ripper/ripper.py +49 -9
- {easyrip-4.15.0.dist-info → easyrip-4.15.2.dist-info}/METADATA +9 -1
- {easyrip-4.15.0.dist-info → easyrip-4.15.2.dist-info}/RECORD +13 -13
- {easyrip-4.15.0.dist-info → easyrip-4.15.2.dist-info}/WHEEL +0 -0
- {easyrip-4.15.0.dist-info → easyrip-4.15.2.dist-info}/entry_points.txt +0 -0
- {easyrip-4.15.0.dist-info → easyrip-4.15.2.dist-info}/licenses/LICENSE +0 -0
- {easyrip-4.15.0.dist-info → easyrip-4.15.2.dist-info}/top_level.txt +0 -0
easyrip/__main__.py
CHANGED
|
@@ -5,6 +5,7 @@ from typing import Any, NoReturn
|
|
|
5
5
|
import Crypto
|
|
6
6
|
import fontTools
|
|
7
7
|
import prompt_toolkit
|
|
8
|
+
import pyperclip
|
|
8
9
|
from prompt_toolkit import ANSI, prompt
|
|
9
10
|
from prompt_toolkit.application import get_app
|
|
10
11
|
from prompt_toolkit.clipboard.pyperclip import PyperclipClipboard
|
|
@@ -16,19 +17,18 @@ from prompt_toolkit.key_binding.bindings import named_commands
|
|
|
16
17
|
from prompt_toolkit.keys import Keys
|
|
17
18
|
|
|
18
19
|
from .easyrip_command import (
|
|
19
|
-
Cmd_type,
|
|
20
20
|
Cmd_type_val,
|
|
21
21
|
CmdCompleter,
|
|
22
22
|
Opt_type,
|
|
23
23
|
OptCompleter,
|
|
24
24
|
nested_dict,
|
|
25
|
+
path_completer,
|
|
25
26
|
)
|
|
26
27
|
from .easyrip_config.config import Config_key, config
|
|
27
28
|
from .easyrip_main import Ripper, get_input_prompt, init, log, run_command
|
|
28
29
|
from .easyrip_prompt import (
|
|
29
30
|
ConfigFileHistory,
|
|
30
31
|
CustomPromptCompleter,
|
|
31
|
-
SmartPathCompleter,
|
|
32
32
|
easyrip_prompt,
|
|
33
33
|
)
|
|
34
34
|
from .global_val import C_D, C_Z
|
|
@@ -38,6 +38,7 @@ def run() -> NoReturn:
|
|
|
38
38
|
init(True)
|
|
39
39
|
|
|
40
40
|
log.debug(f"Python: v{sys.version}")
|
|
41
|
+
log.debug(f"pyperclip: v{pyperclip.__version__}") # pyright: ignore[reportAttributeAccessIssue]
|
|
41
42
|
log.debug(f"prompt-toolkit: v{prompt_toolkit.__version__}")
|
|
42
43
|
log.debug(f"fonttools: v{fontTools.__version__}")
|
|
43
44
|
log.debug(f"pycryptodome: v{Crypto.__version__}")
|
|
@@ -79,37 +80,8 @@ def run() -> NoReturn:
|
|
|
79
80
|
) -> object | Coroutine[Any, Any, object]:
|
|
80
81
|
return named_commands.get_by_name("unix-word-rubout").handler(event)
|
|
81
82
|
|
|
82
|
-
path_completer = SmartPathCompleter()
|
|
83
83
|
clipboard = PyperclipClipboard()
|
|
84
84
|
|
|
85
|
-
def _ctv_to_nc(ctvs: Iterable[Cmd_type_val]) -> CmdCompleter:
|
|
86
|
-
return CmdCompleter(
|
|
87
|
-
{
|
|
88
|
-
name: (
|
|
89
|
-
merge_completers(completer_tuple)
|
|
90
|
-
if (
|
|
91
|
-
completer_tuple := (
|
|
92
|
-
*((_ctv_to_nc(ctv.childs),) if ctv.childs else ()),
|
|
93
|
-
*(
|
|
94
|
-
(path_completer,)
|
|
95
|
-
if name
|
|
96
|
-
in {
|
|
97
|
-
*Cmd_type.cd.value.names,
|
|
98
|
-
*Cmd_type.mediainfo.value.names,
|
|
99
|
-
*Cmd_type.assinfo.value.names,
|
|
100
|
-
*Cmd_type.fontinfo.value.names,
|
|
101
|
-
}
|
|
102
|
-
else ()
|
|
103
|
-
),
|
|
104
|
-
)
|
|
105
|
-
)
|
|
106
|
-
else None
|
|
107
|
-
)
|
|
108
|
-
for ctv in ctvs
|
|
109
|
-
for name in ctv.names
|
|
110
|
-
}
|
|
111
|
-
)
|
|
112
|
-
|
|
113
85
|
def _ctv_to_nd(ctvs: Iterable[Cmd_type_val]) -> nested_dict:
|
|
114
86
|
return {
|
|
115
87
|
name: (
|
|
@@ -132,9 +104,9 @@ def run() -> NoReturn:
|
|
|
132
104
|
)
|
|
133
105
|
for ctv in ctvs
|
|
134
106
|
for name in ctv.names
|
|
107
|
+
if not ctv.is_no_prompt_child
|
|
135
108
|
}
|
|
136
109
|
|
|
137
|
-
cmd_ctv_tuple = tuple(ct.value for ct in Cmd_type if ct != Cmd_type.Option)
|
|
138
110
|
prompt_history = (
|
|
139
111
|
ConfigFileHistory(easyrip_prompt.PROMPT_HISTORY_FILE)
|
|
140
112
|
if config.get_user_profile(Config_key.save_prompt_history)
|
|
@@ -148,7 +120,7 @@ def run() -> NoReturn:
|
|
|
148
120
|
key_bindings=key_bindings,
|
|
149
121
|
completer=merge_completers(
|
|
150
122
|
(
|
|
151
|
-
|
|
123
|
+
CmdCompleter(),
|
|
152
124
|
OptCompleter(opt_tree=_ctv_to_nd(ct.value for ct in Opt_type)),
|
|
153
125
|
CustomPromptCompleter(),
|
|
154
126
|
)
|
easyrip/easyrip_command.py
CHANGED
|
@@ -8,7 +8,6 @@ from typing import Final, Self, final
|
|
|
8
8
|
from prompt_toolkit.completion import (
|
|
9
9
|
Completer,
|
|
10
10
|
FuzzyCompleter,
|
|
11
|
-
FuzzyWordCompleter,
|
|
12
11
|
NestedCompleter,
|
|
13
12
|
WordCompleter,
|
|
14
13
|
merge_completers,
|
|
@@ -18,6 +17,11 @@ from prompt_toolkit.document import Document
|
|
|
18
17
|
|
|
19
18
|
from . import global_val
|
|
20
19
|
from .easyrip_config.config_key import Config_key
|
|
20
|
+
from .easyrip_prompt import (
|
|
21
|
+
SmartPathCompleter,
|
|
22
|
+
fuzzy_filter_and_sort,
|
|
23
|
+
highlight_fuzzy_match,
|
|
24
|
+
)
|
|
21
25
|
from .ripper.param import Audio_codec, Muxer, Preset_name
|
|
22
26
|
|
|
23
27
|
|
|
@@ -28,6 +32,12 @@ class Cmd_type_val:
|
|
|
28
32
|
_param: str
|
|
29
33
|
_description: str
|
|
30
34
|
childs: tuple["Cmd_type_val", ...]
|
|
35
|
+
is_no_prompt_child: bool
|
|
36
|
+
"""此项作为父项的子项时,不在自动补全时使用"""
|
|
37
|
+
is_no_doc_child: bool
|
|
38
|
+
"""此项作为父项的子项时,不在 doc 时使用"""
|
|
39
|
+
is_all_no_doc_childs: bool
|
|
40
|
+
"""此项的所有子项不在 doc 时使用"""
|
|
31
41
|
|
|
32
42
|
@property
|
|
33
43
|
def param(self) -> str:
|
|
@@ -64,11 +74,17 @@ class Cmd_type_val:
|
|
|
64
74
|
param: str = "",
|
|
65
75
|
description: str = "",
|
|
66
76
|
childs: tuple["Cmd_type_val", ...] = (),
|
|
77
|
+
is_no_prompt_child: bool = False,
|
|
78
|
+
is_no_doc_child: bool = False,
|
|
79
|
+
is_all_no_doc_childs: bool = False,
|
|
67
80
|
) -> None:
|
|
68
81
|
self.names = names
|
|
69
82
|
self.param = param
|
|
70
83
|
self.description = description
|
|
71
84
|
self.childs = childs
|
|
85
|
+
self.is_no_prompt_child = is_no_prompt_child
|
|
86
|
+
self.is_no_doc_child = is_no_doc_child
|
|
87
|
+
self.is_all_no_doc_childs = is_all_no_doc_childs
|
|
72
88
|
|
|
73
89
|
def __eq__(self, other: object) -> bool:
|
|
74
90
|
if isinstance(other, Cmd_type_val):
|
|
@@ -79,7 +95,19 @@ class Cmd_type_val:
|
|
|
79
95
|
return hash(self.names)
|
|
80
96
|
|
|
81
97
|
def to_doc(self) -> str:
|
|
82
|
-
|
|
98
|
+
description: str = ((f"{self.description}\n") if self.description else "") + (
|
|
99
|
+
""
|
|
100
|
+
if self.is_all_no_doc_childs
|
|
101
|
+
else "".join(
|
|
102
|
+
f"\n{child.to_doc()}"
|
|
103
|
+
for child in self.childs
|
|
104
|
+
if not child.is_no_doc_child
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
return (
|
|
108
|
+
f"{' / '.join(self.names)} {self.param}\n"
|
|
109
|
+
f"{textwrap.indent(description, ' │ ', lambda _: True)}" # 永远返回 True 才能保证空行前也能加字符
|
|
110
|
+
)
|
|
83
111
|
|
|
84
112
|
|
|
85
113
|
class Cmd_type(enum.Enum):
|
|
@@ -123,6 +151,7 @@ class Cmd_type(enum.Enum):
|
|
|
123
151
|
Cmd_type_val(("send",)),
|
|
124
152
|
Cmd_type_val(("debug",)),
|
|
125
153
|
),
|
|
154
|
+
is_all_no_doc_childs=True,
|
|
126
155
|
)
|
|
127
156
|
_run_any = Cmd_type_val(
|
|
128
157
|
("$",),
|
|
@@ -146,6 +175,7 @@ class Cmd_type(enum.Enum):
|
|
|
146
175
|
Cmd_type_val(("fd",)),
|
|
147
176
|
Cmd_type_val(("cfd",)),
|
|
148
177
|
),
|
|
178
|
+
is_all_no_doc_childs=True,
|
|
149
179
|
)
|
|
150
180
|
dir = ls = Cmd_type_val(
|
|
151
181
|
("dir", "ls"),
|
|
@@ -163,55 +193,54 @@ class Cmd_type(enum.Enum):
|
|
|
163
193
|
list = Cmd_type_val(
|
|
164
194
|
("list",),
|
|
165
195
|
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
|
-
),
|
|
196
|
+
description="Operate Ripper list",
|
|
186
197
|
childs=(
|
|
187
|
-
Cmd_type_val(
|
|
188
|
-
|
|
189
|
-
|
|
198
|
+
Cmd_type_val(
|
|
199
|
+
("Default",), description="Show Ripper list", is_no_prompt_child=True
|
|
200
|
+
),
|
|
201
|
+
Cmd_type_val(("clear", "clean"), description="Clear Ripper list"),
|
|
202
|
+
Cmd_type_val(
|
|
203
|
+
("del", "pop"),
|
|
204
|
+
param="<index>",
|
|
205
|
+
description="Delete a Ripper from Ripper list",
|
|
206
|
+
),
|
|
207
|
+
Cmd_type_val(
|
|
208
|
+
("sort",),
|
|
209
|
+
param="[n][r]",
|
|
210
|
+
description=(
|
|
211
|
+
"Sort list\n" # .
|
|
212
|
+
"'n': Natural Sorting\n"
|
|
213
|
+
"'r': Reverse"
|
|
214
|
+
),
|
|
215
|
+
childs=(Cmd_type_val(("n", "r", "nr")),),
|
|
216
|
+
),
|
|
217
|
+
Cmd_type_val(
|
|
218
|
+
("<int> <int>",),
|
|
219
|
+
description="Exchange specified index",
|
|
220
|
+
is_no_prompt_child=True,
|
|
221
|
+
),
|
|
190
222
|
),
|
|
191
223
|
)
|
|
192
224
|
run = Cmd_type_val(
|
|
193
225
|
("run",),
|
|
194
226
|
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
|
-
),
|
|
227
|
+
description="Run the Ripper from the Ripper list",
|
|
211
228
|
childs=(
|
|
212
|
-
Cmd_type_val(("
|
|
213
|
-
Cmd_type_val(("
|
|
214
|
-
Cmd_type_val(
|
|
229
|
+
Cmd_type_val(("Default",), description="Only run", is_no_prompt_child=True),
|
|
230
|
+
Cmd_type_val(("exit",), description="Close program when run finished"),
|
|
231
|
+
Cmd_type_val(
|
|
232
|
+
("shutdown",),
|
|
233
|
+
param="[<sec>]",
|
|
234
|
+
description=(
|
|
235
|
+
"Shutdown when run finished\n" # .
|
|
236
|
+
"Default: 60"
|
|
237
|
+
),
|
|
238
|
+
),
|
|
239
|
+
Cmd_type_val(
|
|
240
|
+
("server",),
|
|
241
|
+
param="[<address>]:[<port>]@[<password>]",
|
|
242
|
+
description="See the corresponding help for details",
|
|
243
|
+
),
|
|
215
244
|
),
|
|
216
245
|
)
|
|
217
246
|
server = Cmd_type_val(
|
|
@@ -226,23 +255,23 @@ class Cmd_type(enum.Enum):
|
|
|
226
255
|
config = Cmd_type_val(
|
|
227
256
|
("config",),
|
|
228
257
|
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
258
|
childs=(
|
|
241
|
-
Cmd_type_val(
|
|
242
|
-
|
|
243
|
-
|
|
259
|
+
Cmd_type_val(
|
|
260
|
+
("regenerate", "clear", "clean", "reset"),
|
|
261
|
+
description="Regenerate config file",
|
|
262
|
+
),
|
|
263
|
+
Cmd_type_val(
|
|
264
|
+
("open",),
|
|
265
|
+
description="Open the directory where the config file is located",
|
|
266
|
+
),
|
|
267
|
+
Cmd_type_val(("list",), description="Show all config adjustable options"),
|
|
244
268
|
Cmd_type_val(
|
|
245
269
|
("set",),
|
|
270
|
+
param="<key> <val>",
|
|
271
|
+
description=(
|
|
272
|
+
"Set config\n" # .
|
|
273
|
+
"e.g. config set language zh"
|
|
274
|
+
),
|
|
246
275
|
childs=tuple(Cmd_type_val((k,)) for k in Config_key._member_map_),
|
|
247
276
|
),
|
|
248
277
|
),
|
|
@@ -250,25 +279,21 @@ class Cmd_type(enum.Enum):
|
|
|
250
279
|
prompt = Cmd_type_val(
|
|
251
280
|
("prompt",),
|
|
252
281
|
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
282
|
childs=(
|
|
267
|
-
Cmd_type_val(("history",)),
|
|
268
|
-
Cmd_type_val(("history_clear",)),
|
|
269
|
-
Cmd_type_val(
|
|
270
|
-
|
|
271
|
-
|
|
283
|
+
Cmd_type_val(("history",), description="Show prompt history"),
|
|
284
|
+
Cmd_type_val(("history_clear",), description="Delete history file"),
|
|
285
|
+
Cmd_type_val(
|
|
286
|
+
("add",),
|
|
287
|
+
param="<name:string> <cmd:string>",
|
|
288
|
+
description=(
|
|
289
|
+
"Add a custom prompt\n" # .
|
|
290
|
+
"e.g. prompt add myprompt echo my prompt"
|
|
291
|
+
),
|
|
292
|
+
),
|
|
293
|
+
Cmd_type_val(
|
|
294
|
+
("del",), param="<name:string>", description="Delete a custom prompt"
|
|
295
|
+
),
|
|
296
|
+
Cmd_type_val(("show",), description="Show custom prompt"),
|
|
272
297
|
),
|
|
273
298
|
)
|
|
274
299
|
translate = Cmd_type_val(
|
|
@@ -287,6 +312,7 @@ class Cmd_type(enum.Enum):
|
|
|
287
312
|
Cmd_type_val(("fd",)),
|
|
288
313
|
Cmd_type_val(("cfd",)),
|
|
289
314
|
),
|
|
315
|
+
is_all_no_doc_childs=True,
|
|
290
316
|
)
|
|
291
317
|
assinfo = Cmd_type_val(
|
|
292
318
|
("assinfo",),
|
|
@@ -296,6 +322,7 @@ class Cmd_type(enum.Enum):
|
|
|
296
322
|
Cmd_type_val(("fd",)),
|
|
297
323
|
Cmd_type_val(("cfd",)),
|
|
298
324
|
),
|
|
325
|
+
is_all_no_doc_childs=True,
|
|
299
326
|
)
|
|
300
327
|
fontinfo = Cmd_type_val(
|
|
301
328
|
("fontinfo",),
|
|
@@ -305,6 +332,7 @@ class Cmd_type(enum.Enum):
|
|
|
305
332
|
Cmd_type_val(("fd",)),
|
|
306
333
|
Cmd_type_val(("cfd",)),
|
|
307
334
|
),
|
|
335
|
+
is_all_no_doc_childs=True,
|
|
308
336
|
)
|
|
309
337
|
Option = Cmd_type_val(
|
|
310
338
|
("Option",),
|
|
@@ -325,7 +353,7 @@ class Cmd_type(enum.Enum):
|
|
|
325
353
|
|
|
326
354
|
@classmethod
|
|
327
355
|
def to_doc(cls) -> str:
|
|
328
|
-
return "\n
|
|
356
|
+
return "\n".join(ct.value.to_doc() for ct in cls)
|
|
329
357
|
|
|
330
358
|
|
|
331
359
|
class Opt_type(enum.Enum):
|
|
@@ -340,6 +368,7 @@ class Opt_type(enum.Enum):
|
|
|
340
368
|
Cmd_type_val(("fd",)),
|
|
341
369
|
Cmd_type_val(("cfd",)),
|
|
342
370
|
),
|
|
371
|
+
is_all_no_doc_childs=True,
|
|
343
372
|
)
|
|
344
373
|
_o_dir = Cmd_type_val(
|
|
345
374
|
("-o:dir",),
|
|
@@ -361,10 +390,12 @@ class Opt_type(enum.Enum):
|
|
|
361
390
|
description=(
|
|
362
391
|
"If enable, output file name will add auto infix:\n"
|
|
363
392
|
" no audio: '.v'\n"
|
|
364
|
-
" with audio: '.va'
|
|
365
|
-
|
|
393
|
+
" with audio: '.va'"
|
|
394
|
+
),
|
|
395
|
+
childs=(
|
|
396
|
+
Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
|
|
397
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
366
398
|
),
|
|
367
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
368
399
|
)
|
|
369
400
|
_preset = _p = Cmd_type_val(
|
|
370
401
|
("-p", "-preset"),
|
|
@@ -375,7 +406,9 @@ class Opt_type(enum.Enum):
|
|
|
375
406
|
"Preset name:\n"
|
|
376
407
|
f"{Preset_name.to_help_string(' ')}"
|
|
377
408
|
),
|
|
378
|
-
childs=(
|
|
409
|
+
childs=(
|
|
410
|
+
Cmd_type_val(tuple(Preset_name._value2member_map_), is_no_doc_child=True),
|
|
411
|
+
),
|
|
379
412
|
)
|
|
380
413
|
_pipe = Cmd_type_val(
|
|
381
414
|
("-pipe",),
|
|
@@ -411,7 +444,7 @@ class Opt_type(enum.Enum):
|
|
|
411
444
|
"'auto:...' can only select which match infix.\n"
|
|
412
445
|
" e.g. 'auto:zh-Hans:zh-Hant'"
|
|
413
446
|
),
|
|
414
|
-
childs=(Cmd_type_val(("auto",)),),
|
|
447
|
+
childs=(Cmd_type_val(("auto",), is_no_doc_child=True),),
|
|
415
448
|
)
|
|
416
449
|
_only_mux_sub_path = Cmd_type_val(
|
|
417
450
|
("-only-mux-sub-path",),
|
|
@@ -425,7 +458,7 @@ class Opt_type(enum.Enum):
|
|
|
425
458
|
"Mux ASS subtitles in MKV with subset\n" # .
|
|
426
459
|
"The usage of 'auto' is detailed in '-sub'"
|
|
427
460
|
),
|
|
428
|
-
childs=(Cmd_type_val(("auto",)),),
|
|
461
|
+
childs=(Cmd_type_val(("auto",), is_no_doc_child=True),),
|
|
429
462
|
)
|
|
430
463
|
_subset_font_dir = Cmd_type_val(
|
|
431
464
|
("-subset-font-dir",),
|
|
@@ -438,20 +471,20 @@ class Opt_type(enum.Enum):
|
|
|
438
471
|
_subset_font_in_sub = Cmd_type_val(
|
|
439
472
|
("-subset-font-in-sub",),
|
|
440
473
|
param="<0 | 1>",
|
|
441
|
-
description=
|
|
442
|
-
|
|
443
|
-
"Default: 0"
|
|
474
|
+
description="Encode fonts into ASS file instead of standalone files",
|
|
475
|
+
childs=(
|
|
476
|
+
Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
|
|
477
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
444
478
|
),
|
|
445
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
446
479
|
)
|
|
447
480
|
_subset_use_win_font = Cmd_type_val(
|
|
448
481
|
("-subset-use-win-font",),
|
|
449
482
|
param="<0 | 1>",
|
|
450
|
-
description=
|
|
451
|
-
|
|
452
|
-
"Default: 0"
|
|
483
|
+
description="Use Windows fonts when can not find font in subset-font-dir",
|
|
484
|
+
childs=(
|
|
485
|
+
Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
|
|
486
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
453
487
|
),
|
|
454
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
455
488
|
)
|
|
456
489
|
_subset_use_libass_spec = Cmd_type_val(
|
|
457
490
|
("-subset-use-libass-spec",),
|
|
@@ -459,38 +492,44 @@ class Opt_type(enum.Enum):
|
|
|
459
492
|
description=(
|
|
460
493
|
"Use libass specification when subset\n"
|
|
461
494
|
'e.g. "11\\{22}33" ->\n'
|
|
462
|
-
'
|
|
463
|
-
'
|
|
464
|
-
|
|
495
|
+
' "11\\33" (VSFilter)\n'
|
|
496
|
+
' "11{22}33" (libass)'
|
|
497
|
+
),
|
|
498
|
+
childs=(
|
|
499
|
+
Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
|
|
500
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
465
501
|
),
|
|
466
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
467
502
|
)
|
|
468
503
|
_subset_drop_non_render = Cmd_type_val(
|
|
469
504
|
("-subset-drop-non-render",),
|
|
470
505
|
param="<0 | 1>",
|
|
471
506
|
description=(
|
|
472
|
-
"Drop non rendered content such as Comment lines, Name, Effect, etc. in ASS
|
|
473
|
-
|
|
507
|
+
"Drop non rendered content such as Comment lines, Name, Effect, etc. in ASS"
|
|
508
|
+
),
|
|
509
|
+
childs=(
|
|
510
|
+
Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
|
|
511
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
474
512
|
),
|
|
475
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
476
513
|
)
|
|
477
514
|
_subset_drop_unkow_data = Cmd_type_val(
|
|
478
515
|
("-subset-drop-unkow-data",),
|
|
479
516
|
param="<0 | 1>",
|
|
480
517
|
description=(
|
|
481
|
-
"Drop lines that are not in {[Script Info], [V4+ Styles], [Events]} in ASS
|
|
482
|
-
|
|
518
|
+
"Drop lines that are not in {[Script Info], [V4+ Styles], [Events]} in ASS"
|
|
519
|
+
),
|
|
520
|
+
childs=(
|
|
521
|
+
Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
|
|
522
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
483
523
|
),
|
|
484
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
485
524
|
)
|
|
486
525
|
_subset_strict = Cmd_type_val(
|
|
487
526
|
("-subset-strict",),
|
|
488
527
|
param="<0 | 1>",
|
|
489
|
-
description=
|
|
490
|
-
|
|
491
|
-
"Default: 0"
|
|
528
|
+
description="Some error will interrupt subset",
|
|
529
|
+
childs=(
|
|
530
|
+
Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
|
|
531
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
492
532
|
),
|
|
493
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
494
533
|
)
|
|
495
534
|
_translate_sub = Cmd_type_val(
|
|
496
535
|
("-translate-sub",),
|
|
@@ -509,12 +548,15 @@ class Opt_type(enum.Enum):
|
|
|
509
548
|
"Audio encoder:\n"
|
|
510
549
|
f"{Audio_codec.to_help_string(' ')}"
|
|
511
550
|
),
|
|
512
|
-
childs=(
|
|
551
|
+
childs=(
|
|
552
|
+
Cmd_type_val(tuple(Audio_codec._value2member_map_), is_no_doc_child=True),
|
|
553
|
+
),
|
|
513
554
|
)
|
|
514
555
|
_b_a = Cmd_type_val(
|
|
515
556
|
("-b:a",),
|
|
516
557
|
param="<string>",
|
|
517
|
-
description="Setting audio bitrate
|
|
558
|
+
description="Setting audio bitrate",
|
|
559
|
+
childs=(Cmd_type_val(("Default:",), param="160k", is_no_prompt_child=True),),
|
|
518
560
|
)
|
|
519
561
|
_muxer = Cmd_type_val(
|
|
520
562
|
("-muxer",),
|
|
@@ -523,9 +565,17 @@ class Opt_type(enum.Enum):
|
|
|
523
565
|
"Setting muxer\n"
|
|
524
566
|
"\n" # .
|
|
525
567
|
"Muxer:\n"
|
|
526
|
-
f"{
|
|
568
|
+
f"{Muxer.to_help_string(' ')}"
|
|
569
|
+
),
|
|
570
|
+
childs=(Cmd_type_val(tuple(Muxer._value2member_map_), is_no_doc_child=True),),
|
|
571
|
+
)
|
|
572
|
+
_track_name = Cmd_type_val(
|
|
573
|
+
("-track-name",),
|
|
574
|
+
param="<string>",
|
|
575
|
+
description=(
|
|
576
|
+
"Python list[str] format\n" # .
|
|
577
|
+
"e.g. \"['0:name1', '1:name2']\""
|
|
527
578
|
),
|
|
528
|
-
childs=(Cmd_type_val(tuple(Muxer._value2member_map_)),),
|
|
529
579
|
)
|
|
530
580
|
_r = _fps = Cmd_type_val(
|
|
531
581
|
("-r", "-fps"),
|
|
@@ -534,7 +584,7 @@ class Opt_type(enum.Enum):
|
|
|
534
584
|
"Setting FPS when muxing\n"
|
|
535
585
|
"When using auto, the frame rate is automatically obtained from the input video and adsorbed to the nearest preset point"
|
|
536
586
|
),
|
|
537
|
-
childs=(Cmd_type_val(("auto",)),),
|
|
587
|
+
childs=(Cmd_type_val(("auto",), is_no_doc_child=True),),
|
|
538
588
|
)
|
|
539
589
|
_chapters = Cmd_type_val(
|
|
540
590
|
("-chapters",),
|
|
@@ -564,26 +614,23 @@ class Opt_type(enum.Enum):
|
|
|
564
614
|
_run = Cmd_type_val(
|
|
565
615
|
("-run",),
|
|
566
616
|
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
|
-
),
|
|
617
|
+
description=("Run the Ripper from the Ripper list"),
|
|
583
618
|
childs=(
|
|
584
|
-
Cmd_type_val(("
|
|
585
|
-
Cmd_type_val(("
|
|
586
|
-
Cmd_type_val(
|
|
619
|
+
Cmd_type_val(("Default",), description="Only run", is_no_prompt_child=True),
|
|
620
|
+
Cmd_type_val(("exit",), description="Close program when run finished"),
|
|
621
|
+
Cmd_type_val(
|
|
622
|
+
("shutdown",),
|
|
623
|
+
param="[<sec>]",
|
|
624
|
+
description=(
|
|
625
|
+
"Shutdown when run finished\n" # .
|
|
626
|
+
"Default: 60"
|
|
627
|
+
),
|
|
628
|
+
),
|
|
629
|
+
Cmd_type_val(
|
|
630
|
+
("server",),
|
|
631
|
+
param="[<address>]:[<port>]@[<password>]",
|
|
632
|
+
description="See the corresponding help for details",
|
|
633
|
+
),
|
|
587
634
|
),
|
|
588
635
|
)
|
|
589
636
|
_ff_params_ff = _ff_params = Cmd_type_val(
|
|
@@ -615,19 +662,15 @@ class Opt_type(enum.Enum):
|
|
|
615
662
|
param="<string>",
|
|
616
663
|
description="Use FFmpeg hwaccel (See 'ffmpeg -hwaccels' for details)",
|
|
617
664
|
childs=(
|
|
618
|
-
Cmd_type_val(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
"d3d12va",
|
|
628
|
-
"amf",
|
|
629
|
-
)
|
|
630
|
-
),
|
|
665
|
+
Cmd_type_val(("cuda",)),
|
|
666
|
+
Cmd_type_val(("vaapi",)),
|
|
667
|
+
Cmd_type_val(("dxva2",)),
|
|
668
|
+
Cmd_type_val(("qsv",)),
|
|
669
|
+
Cmd_type_val(("d3d11va",)),
|
|
670
|
+
Cmd_type_val(("opencl",)),
|
|
671
|
+
Cmd_type_val(("vulkan",)),
|
|
672
|
+
Cmd_type_val(("d3d12va",)),
|
|
673
|
+
Cmd_type_val(("amf",)),
|
|
631
674
|
),
|
|
632
675
|
)
|
|
633
676
|
_ss = Cmd_type_val(
|
|
@@ -649,11 +692,11 @@ class Opt_type(enum.Enum):
|
|
|
649
692
|
_hevc_strict = Cmd_type_val(
|
|
650
693
|
("-hevc-strict",),
|
|
651
694
|
param="<0 | 1>",
|
|
652
|
-
description=
|
|
653
|
-
|
|
654
|
-
"Default: 1"
|
|
695
|
+
description="When the resolution >= 4K, close HME, and auto reduce the -ref",
|
|
696
|
+
childs=(
|
|
697
|
+
Cmd_type_val(("Default:",), param="1", is_no_prompt_child=True),
|
|
698
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
655
699
|
),
|
|
656
|
-
childs=(Cmd_type_val(("0", "1")),),
|
|
657
700
|
)
|
|
658
701
|
_multithreading = Cmd_type_val(
|
|
659
702
|
("-multithreading",),
|
|
@@ -662,16 +705,24 @@ class Opt_type(enum.Enum):
|
|
|
662
705
|
"Use multi-threading to run Ripper list, suitable for situations with low performance occupancy\n"
|
|
663
706
|
"e.g. -p subset or -p copy"
|
|
664
707
|
),
|
|
665
|
-
childs=(
|
|
708
|
+
childs=(
|
|
709
|
+
Cmd_type_val(("Default:",), param="0", is_no_prompt_child=True),
|
|
710
|
+
Cmd_type_val(("0", "1"), is_no_doc_child=True),
|
|
711
|
+
),
|
|
666
712
|
)
|
|
667
713
|
_quality_detection = Cmd_type_val(
|
|
668
714
|
("-quality-detection",),
|
|
669
715
|
param="<algorithm>[:<threshold>]",
|
|
670
716
|
description=(
|
|
671
717
|
"Comparison of quality between detection and source after encoding is completed\n"
|
|
672
|
-
"
|
|
718
|
+
"\n"
|
|
719
|
+
"Algorithm:"
|
|
720
|
+
),
|
|
721
|
+
childs=(
|
|
722
|
+
Cmd_type_val(("ssim",), description="Default threshold: 0.9"),
|
|
723
|
+
Cmd_type_val(("psnr",), description="Default threshold: 30"),
|
|
724
|
+
Cmd_type_val(("vmaf",), description="Default threshold: 80"),
|
|
673
725
|
),
|
|
674
|
-
childs=(Cmd_type_val(("ssim", "psnr", "vmaf")),),
|
|
675
726
|
)
|
|
676
727
|
|
|
677
728
|
@classmethod
|
|
@@ -683,12 +734,13 @@ class Opt_type(enum.Enum):
|
|
|
683
734
|
|
|
684
735
|
@classmethod
|
|
685
736
|
def to_doc(cls) -> str:
|
|
686
|
-
return "\n
|
|
737
|
+
return "\n".join(ct.value.to_doc() for ct in cls)
|
|
687
738
|
|
|
688
739
|
|
|
689
740
|
Cmd_type.help.value.childs = tuple(
|
|
690
741
|
ct.value for ct in itertools.chain(Cmd_type, Opt_type) if ct is not Cmd_type.help
|
|
691
742
|
)
|
|
743
|
+
Cmd_type.help.value.is_all_no_doc_childs = True
|
|
692
744
|
|
|
693
745
|
|
|
694
746
|
def get_help_doc() -> str:
|
|
@@ -720,11 +772,6 @@ META_DICT_OPT_TYPE = {
|
|
|
720
772
|
for opt in Opt_type
|
|
721
773
|
for name in opt.value.names
|
|
722
774
|
}
|
|
723
|
-
META_DICT_CMD_TYPE = {
|
|
724
|
-
name: lambda opt=opt: opt.value.param
|
|
725
|
-
for opt in Cmd_type
|
|
726
|
-
for name in opt.value.names
|
|
727
|
-
}
|
|
728
775
|
|
|
729
776
|
|
|
730
777
|
def _nested_dict_to_nc(n_dict: nested_dict) -> NestedCompleter:
|
|
@@ -736,60 +783,74 @@ def _nested_dict_to_nc(n_dict: nested_dict) -> NestedCompleter:
|
|
|
736
783
|
)
|
|
737
784
|
|
|
738
785
|
|
|
739
|
-
|
|
786
|
+
path_completer = SmartPathCompleter()
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
class CmdCompleter(Completer):
|
|
790
|
+
def __init__(self) -> None:
|
|
791
|
+
self.root: Final[Cmd_type_val] = Cmd_type_val(
|
|
792
|
+
(), childs=tuple(ct.value for ct in Cmd_type if ct != Cmd_type.Option)
|
|
793
|
+
)
|
|
794
|
+
|
|
740
795
|
def get_completions(
|
|
741
796
|
self, document: Document, complete_event: CompleteEvent
|
|
742
797
|
) -> Iterable[Completion]:
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
words = text.split()
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
),
|
|
773
|
-
|
|
774
|
-
text="",
|
|
775
|
-
display="✔",
|
|
776
|
-
display_meta=(
|
|
777
|
-
f"{_desc_list[0]}..."
|
|
778
|
-
if len(_desc_list := _cmd.value.description.split("\n")) > 1
|
|
779
|
-
else _desc_list[0]
|
|
780
|
-
),
|
|
781
|
-
),
|
|
798
|
+
if (text := document.text_before_cursor.lstrip()).startswith("-"):
|
|
799
|
+
return
|
|
800
|
+
words: Final[list[str]] = text.split()
|
|
801
|
+
|
|
802
|
+
node: Cmd_type_val = self.root
|
|
803
|
+
|
|
804
|
+
def _refresh_node(word: str) -> bool:
|
|
805
|
+
nonlocal node
|
|
806
|
+
for ctv in node.childs:
|
|
807
|
+
for name in ctv.names:
|
|
808
|
+
if name == word:
|
|
809
|
+
node = ctv
|
|
810
|
+
return True
|
|
811
|
+
return False
|
|
812
|
+
|
|
813
|
+
for word in words if text.endswith(" ") else words[:-1]:
|
|
814
|
+
if not _refresh_node(word):
|
|
815
|
+
return
|
|
816
|
+
|
|
817
|
+
match_word = "" if text.endswith(" ") else words[-1]
|
|
818
|
+
ctv_tuple: Final[tuple[Cmd_type_val, ...]] = tuple(itertools.chain(node.childs))
|
|
819
|
+
name__ctv: Final[dict[str, Cmd_type_val]] = {
|
|
820
|
+
name: ctv for ctv in ctv_tuple for name in ctv.names
|
|
821
|
+
}
|
|
822
|
+
names: Final[tuple[str, ...]] = tuple(name__ctv)
|
|
823
|
+
if match_word in names:
|
|
824
|
+
ctv = name__ctv[match_word]
|
|
825
|
+
yield Completion(
|
|
826
|
+
text=match_word,
|
|
827
|
+
start_position=-len(match_word),
|
|
828
|
+
display_meta=ctv.param,
|
|
782
829
|
)
|
|
783
|
-
|
|
784
|
-
|
|
830
|
+
for i, desc in enumerate(ctv.description.split("\n")):
|
|
831
|
+
yield Completion(
|
|
832
|
+
text="",
|
|
833
|
+
display="" if i else "✔",
|
|
834
|
+
display_meta=desc,
|
|
835
|
+
)
|
|
785
836
|
else:
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
837
|
+
for name in fuzzy_filter_and_sort(names, match_word):
|
|
838
|
+
yield Completion(
|
|
839
|
+
text=name,
|
|
840
|
+
start_position=0 if text.endswith(" ") else -len(words[-1]),
|
|
841
|
+
display=highlight_fuzzy_match(name, match_word),
|
|
842
|
+
display_meta=name__ctv[name].param,
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
if 1 <= len(words) <= 2 and words[0] in {
|
|
846
|
+
*Cmd_type.cd.value.names,
|
|
847
|
+
*Cmd_type.mediainfo.value.names,
|
|
848
|
+
*Cmd_type.assinfo.value.names,
|
|
849
|
+
*Cmd_type.fontinfo.value.names,
|
|
850
|
+
}:
|
|
851
|
+
yield from path_completer.get_completions(
|
|
852
|
+
Document(words[1] if len(words) > 1 else ""), complete_event
|
|
791
853
|
)
|
|
792
|
-
yield from completer.get_completions(document, complete_event)
|
|
793
854
|
|
|
794
855
|
|
|
795
856
|
class OptCompleter(Completer):
|
easyrip/easyrip_main.py
CHANGED
|
@@ -52,6 +52,12 @@ PROJECT_URL = global_val.PROJECT_URL
|
|
|
52
52
|
def log_new_ver(
|
|
53
53
|
new_ver: str | None, old_ver: str, program_name: str, dl_url: str
|
|
54
54
|
) -> None:
|
|
55
|
+
log.debug(
|
|
56
|
+
"{}({})",
|
|
57
|
+
log_new_ver.__name__,
|
|
58
|
+
", ".join(f"{k}={v!r}" for k, v in locals().copy().items()),
|
|
59
|
+
print_level=log.LogLevel._detail,
|
|
60
|
+
)
|
|
55
61
|
if new_ver is None:
|
|
56
62
|
return
|
|
57
63
|
try:
|
|
@@ -467,7 +473,17 @@ def run_command(command: Iterable[str] | str) -> bool:
|
|
|
467
473
|
log.error("'{}' does not exist", cmd_list[1])
|
|
468
474
|
|
|
469
475
|
case _:
|
|
470
|
-
|
|
476
|
+
_child = _want_doc_cmd_type.value
|
|
477
|
+
for s in cmd_list[2:-1]:
|
|
478
|
+
for ch in _child.childs:
|
|
479
|
+
if s in ch.names:
|
|
480
|
+
_child = ch
|
|
481
|
+
break
|
|
482
|
+
else:
|
|
483
|
+
log.error("'{}' does not exist", s)
|
|
484
|
+
break
|
|
485
|
+
else:
|
|
486
|
+
log.send(_child.to_doc(), is_format=False)
|
|
471
487
|
else:
|
|
472
488
|
log.send(get_help_doc(), is_format=False)
|
|
473
489
|
|
|
@@ -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,6 +288,7 @@ 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
293
|
"Subset failed, cancel mux": "子集化失败, 取消混流",
|
|
332
294
|
"FFmpeg report: {}": "FFmpeg 报告: {}",
|
easyrip/easyrip_prompt.py
CHANGED
|
@@ -83,7 +83,7 @@ class ConfigFileHistory(FileHistory):
|
|
|
83
83
|
super().store_string(string)
|
|
84
84
|
|
|
85
85
|
|
|
86
|
-
def
|
|
86
|
+
def highlight_fuzzy_match(
|
|
87
87
|
suggestion: str,
|
|
88
88
|
user_input: str,
|
|
89
89
|
style_config: dict | None = None,
|
|
@@ -146,7 +146,9 @@ def _highlight_fuzzy_match(
|
|
|
146
146
|
return result
|
|
147
147
|
|
|
148
148
|
|
|
149
|
-
def
|
|
149
|
+
def fuzzy_filter_and_sort(
|
|
150
|
+
names: list[str] | tuple[str, ...], match_str: str
|
|
151
|
+
) -> list[str]:
|
|
150
152
|
"""模糊过滤和排序"""
|
|
151
153
|
if not match_str:
|
|
152
154
|
return sorted(names)
|
|
@@ -188,7 +190,7 @@ class SmartPathCompleter(Completer):
|
|
|
188
190
|
os.listdir(directory) if os.path.isdir(directory) else []
|
|
189
191
|
)
|
|
190
192
|
|
|
191
|
-
for filename in
|
|
193
|
+
for filename in fuzzy_filter_and_sort(filenames, basename):
|
|
192
194
|
full_name = (
|
|
193
195
|
filename if directory == "." else os.path.join(directory, filename)
|
|
194
196
|
)
|
|
@@ -204,7 +206,7 @@ class SmartPathCompleter(Completer):
|
|
|
204
206
|
yield Completion(
|
|
205
207
|
text=completion,
|
|
206
208
|
start_position=-len(text),
|
|
207
|
-
display=
|
|
209
|
+
display=highlight_fuzzy_match(filename, basename),
|
|
208
210
|
)
|
|
209
211
|
|
|
210
212
|
except OSError:
|
|
@@ -222,11 +224,11 @@ class CustomPromptCompleter(Completer):
|
|
|
222
224
|
|
|
223
225
|
custom_prompt = easyrip_prompt.get_custom_prompt()
|
|
224
226
|
for word in words[-1:]:
|
|
225
|
-
for name in
|
|
227
|
+
for name in fuzzy_filter_and_sort(tuple(custom_prompt), word):
|
|
226
228
|
target_cmd = custom_prompt[name]
|
|
227
229
|
yield Completion(
|
|
228
230
|
text=target_cmd,
|
|
229
231
|
start_position=-len(word),
|
|
230
|
-
display=
|
|
232
|
+
display=highlight_fuzzy_match(name, word),
|
|
231
233
|
display_meta=target_cmd,
|
|
232
234
|
)
|
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.15.
|
|
7
|
+
PROJECT_VERSION = "4.15.2"
|
|
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/ripper.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import ast
|
|
1
2
|
import csv
|
|
2
3
|
import os
|
|
3
4
|
import re
|
|
@@ -14,8 +15,13 @@ from typing import Final, Self, final
|
|
|
14
15
|
|
|
15
16
|
from .. import easyrip_web
|
|
16
17
|
from ..easyrip_log import log
|
|
17
|
-
from ..easyrip_mlang import
|
|
18
|
-
|
|
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
|
|
19
25
|
from .media_info import Media_info, Stream_error
|
|
20
26
|
from .param import (
|
|
21
27
|
FONT_SUFFIX_SET,
|
|
@@ -245,6 +251,38 @@ class Ripper:
|
|
|
245
251
|
|
|
246
252
|
# Muxer
|
|
247
253
|
muxer_format_str_list: list[str]
|
|
254
|
+
track_name_list: list[str] = []
|
|
255
|
+
if (_track_name_org_str := self.option_map.get("track-name")) is not None:
|
|
256
|
+
try:
|
|
257
|
+
track_name_list = ast.literal_eval(_track_name_org_str)
|
|
258
|
+
except Exception as e:
|
|
259
|
+
raise Mlang_exception("{} param illegal", "-track-name") from e
|
|
260
|
+
if not type_match(track_name_list, list[str]):
|
|
261
|
+
raise Mlang_exception("{} param illegal", "-track-name")
|
|
262
|
+
for i in range(len(track_name_list)):
|
|
263
|
+
track_name = track_name_list[i]
|
|
264
|
+
if '"' in track_name:
|
|
265
|
+
if "'" in track_name:
|
|
266
|
+
raise Mlang_exception(
|
|
267
|
+
"{} param illegal: {}",
|
|
268
|
+
"-track-name",
|
|
269
|
+
"The '\"' and \"'\" can not exist simultaneously",
|
|
270
|
+
)
|
|
271
|
+
track_name = f"'{track_name}'"
|
|
272
|
+
else:
|
|
273
|
+
track_name = f'"{track_name}"'
|
|
274
|
+
track_name_list[i] = track_name
|
|
275
|
+
log.debug(f"-track-name <- {_track_name_org_str!r}", is_format=False)
|
|
276
|
+
log.debug(f"-track-name -> {track_name_list!r}", is_format=False)
|
|
277
|
+
|
|
278
|
+
mkv_all_need_opt_str: str = (
|
|
279
|
+
"".join(f"--track-name {track_name} " for track_name in track_name_list)
|
|
280
|
+
) + (
|
|
281
|
+
f"--chapters {chapters} "
|
|
282
|
+
if (chapters := self.option_map.get("chapters"))
|
|
283
|
+
else ""
|
|
284
|
+
)
|
|
285
|
+
|
|
248
286
|
if muxer := self.option_map.get("muxer"):
|
|
249
287
|
muxer = Ripper.Muxer(muxer)
|
|
250
288
|
|
|
@@ -284,11 +322,7 @@ class Ripper:
|
|
|
284
322
|
if force_fps and only_mux_sub_path is None
|
|
285
323
|
else ""
|
|
286
324
|
)
|
|
287
|
-
+
|
|
288
|
-
f"--chapters {chapters} "
|
|
289
|
-
if (chapters := self.option_map.get("chapters"))
|
|
290
|
-
else ""
|
|
291
|
-
)
|
|
325
|
+
+ mkv_all_need_opt_str
|
|
292
326
|
+ (
|
|
293
327
|
" ".join(
|
|
294
328
|
(
|
|
@@ -428,13 +462,19 @@ class Ripper:
|
|
|
428
462
|
_encoder_format_str += f" -rem {_audio_info.index + 1}"
|
|
429
463
|
else:
|
|
430
464
|
_encoder_format_str = (
|
|
431
|
-
'mkvmerge -o "{output}"
|
|
465
|
+
'mkvmerge -o "{output}" '
|
|
466
|
+
+ mkv_all_need_opt_str
|
|
467
|
+
+ '--no-audio "{input}"'
|
|
432
468
|
)
|
|
433
469
|
case "copy":
|
|
434
470
|
_encoder_format_str = (
|
|
435
471
|
'mp4box -add "{input}" -new "{output}"'
|
|
436
472
|
if muxer == Ripper.Muxer.mp4
|
|
437
|
-
else
|
|
473
|
+
else (
|
|
474
|
+
'mkvmerge -o "{output}" '
|
|
475
|
+
+ mkv_all_need_opt_str
|
|
476
|
+
+ '"{input}"'
|
|
477
|
+
)
|
|
438
478
|
)
|
|
439
479
|
if _encoder_format_str is not None:
|
|
440
480
|
encoder_format_str_list = [_encoder_format_str]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easyrip
|
|
3
|
-
Version: 4.15.
|
|
3
|
+
Version: 4.15.2
|
|
4
4
|
Author: op200
|
|
5
5
|
License-Expression: AGPL-3.0-or-later
|
|
6
6
|
Project-URL: Homepage, https://github.com/op200/EasyRip
|
|
@@ -34,6 +34,14 @@ Self-use codec tool
|
|
|
34
34
|
**[Easy Rip Web Panel
|
|
35
35
|
Easy Rip 网页版控制台](https://op200.github.io/EasyRip-WebPanel/)**
|
|
36
36
|
|
|
37
|
+
<a href="https://www.star-history.com/#op200/EasyRip&type=date&legend=top-left">
|
|
38
|
+
<picture>
|
|
39
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=op200/EasyRip&type=date&theme=dark&legend=top-left" />
|
|
40
|
+
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=op200/EasyRip&type=date&legend=top-left" />
|
|
41
|
+
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=op200/EasyRip&type=date&legend=top-left" />
|
|
42
|
+
</picture>
|
|
43
|
+
</a>
|
|
44
|
+
|
|
37
45
|
## Start
|
|
38
46
|
|
|
39
47
|
1. Install [Python](https://www.python.org/)
|
|
@@ -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=oiDLhxR-o7dh6jAZMuiOX6RMX_LTFRMOhg16kR1dVZM,4743
|
|
3
|
+
easyrip/easyrip_command.py,sha256=RfJMjlFIlFznxM2YyDkrlAoxbaJUC8xOZEkxvOUD1Pk,34010
|
|
4
4
|
easyrip/easyrip_log.py,sha256=R-dM3CWUBFITtG7GSD1zy4X4MhZqxkoiBPjlIpI76cY,15573
|
|
5
|
-
easyrip/easyrip_main.py,sha256=
|
|
6
|
-
easyrip/easyrip_prompt.py,sha256=
|
|
7
|
-
easyrip/global_val.py,sha256=
|
|
5
|
+
easyrip/easyrip_main.py,sha256=EjEjs1UCVSMGxdQ-XUggiDbZ0pOxiKcC85toNdbFTV4,48766
|
|
6
|
+
easyrip/easyrip_prompt.py,sha256=OidVzSkBksC89HzDcvqGkF9CXyhPNINYDp1sgLarxf8,7087
|
|
7
|
+
easyrip/global_val.py,sha256=imyOEDnS1dmIULjmq0UrwEcKBF7Bcb4ELybrsPws57s,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
19
|
easyrip/easyrip_web/third_party_api.py,sha256=E-60yoY6D0pPUfYW1VIh0763htyV5z6getzlLtLAdQc,4624
|
|
20
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=qppP5D-4e4LYKemlsOvq8wTJwYBlPhEo7k2nhwKSQEs,60501
|
|
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.15.
|
|
28
|
-
easyrip-4.15.
|
|
29
|
-
easyrip-4.15.
|
|
30
|
-
easyrip-4.15.
|
|
31
|
-
easyrip-4.15.
|
|
32
|
-
easyrip-4.15.
|
|
27
|
+
easyrip-4.15.2.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
28
|
+
easyrip-4.15.2.dist-info/METADATA,sha256=vaOTNo3-DbhZBoHbfyfPnrcvR_C0qdl1j94LwGRu63Y,4061
|
|
29
|
+
easyrip-4.15.2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
30
|
+
easyrip-4.15.2.dist-info/entry_points.txt,sha256=D6GBMMTzZ-apgX76KyZ6jxMmIFqGYwU9neeLLni_qKI,49
|
|
31
|
+
easyrip-4.15.2.dist-info/top_level.txt,sha256=kuEteBXm-Gf90jRQgH3-fTo-Z-Q6czSuUEqY158H4Ww,8
|
|
32
|
+
easyrip-4.15.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|