easyrip 4.1.0__py3-none-any.whl → 4.3.0__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 -8
- easyrip/easyrip_command.py +75 -27
- easyrip/{easyrip_config.py → easyrip_config/config.py} +16 -33
- easyrip/easyrip_config/config_key.py +14 -0
- easyrip/easyrip_main.py +107 -60
- easyrip/easyrip_mlang/lang_zh_Hans_CN.py +18 -10
- easyrip/easyrip_prompt.py +17 -0
- easyrip/easyrip_web/http_server.py +17 -1
- easyrip/global_val.py +21 -1
- {easyrip-4.1.0.dist-info → easyrip-4.3.0.dist-info}/METADATA +16 -34
- {easyrip-4.1.0.dist-info → easyrip-4.3.0.dist-info}/RECORD +15 -13
- {easyrip-4.1.0.dist-info → easyrip-4.3.0.dist-info}/WHEEL +0 -0
- {easyrip-4.1.0.dist-info → easyrip-4.3.0.dist-info}/entry_points.txt +0 -0
- {easyrip-4.1.0.dist-info → easyrip-4.3.0.dist-info}/licenses/LICENSE +0 -0
- {easyrip-4.1.0.dist-info → easyrip-4.3.0.dist-info}/top_level.txt +0 -0
easyrip/__main__.py
CHANGED
|
@@ -6,9 +6,10 @@ import Crypto
|
|
|
6
6
|
import fontTools
|
|
7
7
|
import prompt_toolkit
|
|
8
8
|
from prompt_toolkit import ANSI, prompt
|
|
9
|
-
from prompt_toolkit.completion import
|
|
9
|
+
from prompt_toolkit.completion import PathCompleter, merge_completers
|
|
10
10
|
from prompt_toolkit.history import InMemoryHistory
|
|
11
|
-
from prompt_toolkit.key_binding import KeyBindings
|
|
11
|
+
from prompt_toolkit.key_binding import KeyBindings, KeyPressEvent
|
|
12
|
+
from prompt_toolkit.keys import Keys
|
|
12
13
|
|
|
13
14
|
from .easyrip_command import (
|
|
14
15
|
Cmd_type,
|
|
@@ -18,7 +19,10 @@ from .easyrip_command import (
|
|
|
18
19
|
OptCompleter,
|
|
19
20
|
nested_dict,
|
|
20
21
|
)
|
|
22
|
+
from .easyrip_config.config import Config_key, config
|
|
21
23
|
from .easyrip_main import Ripper, get_input_prompt, init, log, run_command
|
|
24
|
+
from .easyrip_prompt import ConfigFileHistory, easyrip_prompt
|
|
25
|
+
from .global_val import C_D, C_Z
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
def run() -> NoReturn:
|
|
@@ -36,11 +40,15 @@ def run() -> NoReturn:
|
|
|
36
40
|
|
|
37
41
|
key_bindings = KeyBindings()
|
|
38
42
|
|
|
39
|
-
@key_bindings.add(
|
|
40
|
-
def _(event) -> None:
|
|
41
|
-
event.app.
|
|
43
|
+
@key_bindings.add(Keys.ControlC)
|
|
44
|
+
def _(event: KeyPressEvent) -> None:
|
|
45
|
+
event.app.exit(exception=KeyboardInterrupt, style="class:exiting")
|
|
42
46
|
|
|
43
|
-
|
|
47
|
+
@key_bindings.add(Keys.ControlD)
|
|
48
|
+
def _(event: KeyPressEvent) -> None:
|
|
49
|
+
event.app.current_buffer.insert_text(C_D)
|
|
50
|
+
|
|
51
|
+
path_completer = PathCompleter()
|
|
44
52
|
|
|
45
53
|
def _ctv_to_nc(ctvs: Iterable[Cmd_type_val]) -> CmdCompleter:
|
|
46
54
|
return CmdCompleter(
|
|
@@ -92,7 +100,11 @@ def run() -> NoReturn:
|
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
cmd_ctv_tuple = tuple(ct.value for ct in Cmd_type if ct != Cmd_type.Option)
|
|
95
|
-
prompt_history =
|
|
103
|
+
prompt_history = (
|
|
104
|
+
ConfigFileHistory(easyrip_prompt.PROMPT_HISTORY_FILE)
|
|
105
|
+
if config.get_user_profile(Config_key.prompt_history_save_file)
|
|
106
|
+
else InMemoryHistory()
|
|
107
|
+
)
|
|
96
108
|
while True:
|
|
97
109
|
try:
|
|
98
110
|
command = prompt(
|
|
@@ -107,7 +119,7 @@ def run() -> NoReturn:
|
|
|
107
119
|
history=prompt_history,
|
|
108
120
|
complete_while_typing=True,
|
|
109
121
|
)
|
|
110
|
-
if command.startswith(
|
|
122
|
+
if command.startswith(C_Z):
|
|
111
123
|
raise EOFError
|
|
112
124
|
except KeyboardInterrupt:
|
|
113
125
|
continue
|
easyrip/easyrip_command.py
CHANGED
|
@@ -3,10 +3,11 @@ import itertools
|
|
|
3
3
|
import textwrap
|
|
4
4
|
from collections.abc import Iterable
|
|
5
5
|
from dataclasses import dataclass
|
|
6
|
-
from typing import Final, Self,
|
|
6
|
+
from typing import Final, Self, final
|
|
7
7
|
|
|
8
8
|
from prompt_toolkit.completion import (
|
|
9
9
|
Completer,
|
|
10
|
+
DeduplicateCompleter,
|
|
10
11
|
FuzzyCompleter,
|
|
11
12
|
FuzzyWordCompleter,
|
|
12
13
|
NestedCompleter,
|
|
@@ -17,6 +18,7 @@ from prompt_toolkit.completion.base import CompleteEvent, Completion
|
|
|
17
18
|
from prompt_toolkit.document import Document
|
|
18
19
|
|
|
19
20
|
from . import global_val
|
|
21
|
+
from .easyrip_config.config_key import Config_key
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
@final
|
|
@@ -175,19 +177,22 @@ class Cmd_type(enum.Enum):
|
|
|
175
177
|
)
|
|
176
178
|
run = Cmd_type_val(
|
|
177
179
|
("run",),
|
|
178
|
-
param="[<run option>]",
|
|
180
|
+
param="[<run option>] [-multithreading <0 | 1>]",
|
|
179
181
|
description=(
|
|
180
182
|
"Run the Ripper in the Ripper list\n"
|
|
181
|
-
"
|
|
183
|
+
"\n"
|
|
182
184
|
"Default:\n"
|
|
183
185
|
" Only run\n"
|
|
184
|
-
"
|
|
186
|
+
"\n"
|
|
185
187
|
"exit:\n"
|
|
186
188
|
" Close program when run finished\n"
|
|
187
|
-
"
|
|
189
|
+
"\n"
|
|
188
190
|
"shutdown [<sec>]:\n"
|
|
189
191
|
" Shutdown when run finished\n"
|
|
190
|
-
" Default: 60"
|
|
192
|
+
" Default: 60\n"
|
|
193
|
+
"\n"
|
|
194
|
+
"server [<address>]:[<port>]@[<password>]:\n"
|
|
195
|
+
" See the corresponding help for details"
|
|
191
196
|
),
|
|
192
197
|
childs=(
|
|
193
198
|
Cmd_type_val(("exit",)),
|
|
@@ -196,22 +201,18 @@ class Cmd_type(enum.Enum):
|
|
|
196
201
|
)
|
|
197
202
|
server = Cmd_type_val(
|
|
198
203
|
("server",),
|
|
199
|
-
param="[
|
|
204
|
+
param="[<address>]:[<port>]@[<password>]",
|
|
200
205
|
description=(
|
|
201
206
|
"Boot web service\n"
|
|
202
207
|
"Default: server localhost:0\n"
|
|
203
208
|
"Client send command 'kill' can exit Ripper's run, note that FFmpeg needs to accept multiple ^C signals to forcibly terminate, and a single ^C signal will wait for the file output to be complete before terminating"
|
|
204
209
|
),
|
|
205
|
-
childs=(
|
|
206
|
-
Cmd_type_val(("-a", "-address")),
|
|
207
|
-
Cmd_type_val(("-p", "-password")),
|
|
208
|
-
),
|
|
209
210
|
)
|
|
210
211
|
config = Cmd_type_val(
|
|
211
212
|
("config",),
|
|
212
213
|
param="<config option>",
|
|
213
214
|
description=(
|
|
214
|
-
"regenerate | clear | clean
|
|
215
|
+
"regenerate | clear | clean\n"
|
|
215
216
|
" Regenerate config file\n"
|
|
216
217
|
"open\n"
|
|
217
218
|
" Open the directory where the config file is located\n"
|
|
@@ -222,10 +223,27 @@ class Cmd_type(enum.Enum):
|
|
|
222
223
|
" e.g. config set language zh"
|
|
223
224
|
),
|
|
224
225
|
childs=(
|
|
225
|
-
Cmd_type_val(("regenerate", "clear", "clean"
|
|
226
|
+
Cmd_type_val(("regenerate", "clear", "clean")),
|
|
226
227
|
Cmd_type_val(("open",)),
|
|
227
228
|
Cmd_type_val(("list",)),
|
|
228
|
-
Cmd_type_val(
|
|
229
|
+
Cmd_type_val(
|
|
230
|
+
("set",),
|
|
231
|
+
childs=tuple(Cmd_type_val((k,)) for k in Config_key._value2member_map_),
|
|
232
|
+
),
|
|
233
|
+
),
|
|
234
|
+
)
|
|
235
|
+
prompt = Cmd_type_val(
|
|
236
|
+
("prompt",),
|
|
237
|
+
param="<prompt option>",
|
|
238
|
+
description=(
|
|
239
|
+
"history\n" # .
|
|
240
|
+
" Show prompt history\n"
|
|
241
|
+
"clear | clean\n"
|
|
242
|
+
" Delete history file"
|
|
243
|
+
),
|
|
244
|
+
childs=(
|
|
245
|
+
Cmd_type_val(("history",)),
|
|
246
|
+
Cmd_type_val(("clear", "clean")),
|
|
229
247
|
),
|
|
230
248
|
)
|
|
231
249
|
translate = Cmd_type_val(
|
|
@@ -545,16 +563,19 @@ class Opt_type(enum.Enum):
|
|
|
545
563
|
param="[<string>]",
|
|
546
564
|
description=(
|
|
547
565
|
"Run the Ripper from the Ripper list\n"
|
|
548
|
-
"
|
|
566
|
+
"\n"
|
|
549
567
|
"Default:\n"
|
|
550
568
|
" Only run\n"
|
|
551
|
-
"
|
|
569
|
+
"\n"
|
|
552
570
|
"exit:\n"
|
|
553
571
|
" Close program when run finished\n"
|
|
554
|
-
"
|
|
572
|
+
"\n"
|
|
555
573
|
"shutdown [<sec>]:\n"
|
|
556
574
|
" Shutdown when run finished\n"
|
|
557
575
|
" Default: 60\n"
|
|
576
|
+
"\n"
|
|
577
|
+
"server [<address>]:[<port>]@[<password>]:\n"
|
|
578
|
+
" See the corresponding help for details"
|
|
558
579
|
),
|
|
559
580
|
childs=(
|
|
560
581
|
Cmd_type_val(("exit",)),
|
|
@@ -665,7 +686,7 @@ def get_help_doc() -> str:
|
|
|
665
686
|
)
|
|
666
687
|
|
|
667
688
|
|
|
668
|
-
nested_dict
|
|
689
|
+
type nested_dict = dict[str, "nested_dict | Completer"]
|
|
669
690
|
META_DICT_OPT_TYPE = {
|
|
670
691
|
name: opt.value.param for opt in Opt_type for name in opt.value.names
|
|
671
692
|
}
|
|
@@ -746,7 +767,17 @@ class OptCompleter(Completer):
|
|
|
746
767
|
)
|
|
747
768
|
)
|
|
748
769
|
|
|
749
|
-
if
|
|
770
|
+
if opt_tree_pos_list[-1] is not self.opt_tree and not text.endswith(" "):
|
|
771
|
+
yield from (
|
|
772
|
+
Completion(
|
|
773
|
+
text=words[-1],
|
|
774
|
+
start_position=-len(words[-1]),
|
|
775
|
+
display_meta=META_DICT_OPT_TYPE.get(words[-1], ""),
|
|
776
|
+
),
|
|
777
|
+
Completion(text="", display="✔"),
|
|
778
|
+
)
|
|
779
|
+
|
|
780
|
+
elif isinstance(opt_tree_pos_list[-1], Completer):
|
|
750
781
|
# 直接使用 PathCompleter 会因为上下文问题失效,所以将上文套进 NestedCompleter
|
|
751
782
|
new_nd: nested_dict = {}
|
|
752
783
|
new_nd_pos: nested_dict = new_nd
|
|
@@ -767,11 +798,22 @@ class OptCompleter(Completer):
|
|
|
767
798
|
|
|
768
799
|
yield from merge_completers(
|
|
769
800
|
(
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
801
|
+
DeduplicateCompleter(
|
|
802
|
+
merge_completers(
|
|
803
|
+
(
|
|
804
|
+
_nested_dict_to_nc(new_nd),
|
|
805
|
+
FuzzyCompleter(_nested_dict_to_nc(new_nd), WORD=True),
|
|
806
|
+
)
|
|
807
|
+
)
|
|
808
|
+
),
|
|
809
|
+
FuzzyCompleter(
|
|
810
|
+
WordCompleter(
|
|
811
|
+
words=tuple(opt_tree_pos_list[-1]),
|
|
812
|
+
meta_dict=META_DICT_OPT_TYPE,
|
|
813
|
+
WORD=True, # 匹配标点
|
|
814
|
+
match_middle=True,
|
|
815
|
+
),
|
|
816
|
+
WORD=False,
|
|
775
817
|
),
|
|
776
818
|
)
|
|
777
819
|
).get_completions(document=document, complete_event=complete_event)
|
|
@@ -779,10 +821,16 @@ class OptCompleter(Completer):
|
|
|
779
821
|
else:
|
|
780
822
|
yield from FuzzyCompleter(
|
|
781
823
|
WordCompleter(
|
|
782
|
-
words=tuple(
|
|
824
|
+
words=tuple(
|
|
825
|
+
opt_tree_pos_list[-1] | {}
|
|
826
|
+
if text.endswith(" ")
|
|
827
|
+
or len(words) <= 1
|
|
828
|
+
or isinstance(opt_tree_pos_list[-2], Completer)
|
|
829
|
+
else opt_tree_pos_list[-2]
|
|
830
|
+
),
|
|
783
831
|
meta_dict=META_DICT_OPT_TYPE,
|
|
784
|
-
WORD=True,
|
|
832
|
+
WORD=True, # 匹配标点
|
|
785
833
|
match_middle=True,
|
|
786
834
|
),
|
|
787
|
-
WORD=
|
|
835
|
+
WORD=not text.endswith(" "),
|
|
788
836
|
).get_completions(document=document, complete_event=complete_event)
|
|
@@ -1,26 +1,11 @@
|
|
|
1
|
-
import enum
|
|
2
1
|
import json
|
|
3
2
|
import os
|
|
4
|
-
import sys
|
|
5
3
|
from pathlib import Path
|
|
6
4
|
|
|
7
|
-
from
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
|
|
11
|
-
PROJECT_NAME = global_val.PROJECT_NAME
|
|
12
|
-
CONFIG_VERSION = "2.9.4"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Config_key(enum.Enum):
|
|
16
|
-
language = "language"
|
|
17
|
-
check_update = "check_update"
|
|
18
|
-
check_dependent = "check_dependent"
|
|
19
|
-
startup_directory = "startup_directory"
|
|
20
|
-
force_log_file_path = "force_log_file_path"
|
|
21
|
-
log_print_level = "log_print_level"
|
|
22
|
-
log_write_level = "log_write_level"
|
|
23
|
-
|
|
5
|
+
from ..easyrip_log import log
|
|
6
|
+
from ..easyrip_mlang import all_supported_lang_map, gettext
|
|
7
|
+
from ..global_val import CONFIG_DIR
|
|
8
|
+
from .config_key import CONFIG_VERSION, Config_key
|
|
24
9
|
|
|
25
10
|
CONFIG_DEFAULT_DICT: dict[Config_key, str | bool] = {
|
|
26
11
|
Config_key.language: "auto",
|
|
@@ -30,6 +15,7 @@ CONFIG_DEFAULT_DICT: dict[Config_key, str | bool] = {
|
|
|
30
15
|
Config_key.force_log_file_path: "",
|
|
31
16
|
Config_key.log_print_level: log.LogLevel.send.name,
|
|
32
17
|
Config_key.log_write_level: log.LogLevel.send.name,
|
|
18
|
+
Config_key.prompt_history_save_file: True,
|
|
33
19
|
}
|
|
34
20
|
|
|
35
21
|
|
|
@@ -40,19 +26,8 @@ class config:
|
|
|
40
26
|
|
|
41
27
|
@classmethod
|
|
42
28
|
def init(cls) -> None:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
cls._config_dir = Path(os.getenv("APPDATA", ""))
|
|
46
|
-
elif sys.platform == "darwin":
|
|
47
|
-
# macOS: ~/Library/Application Support/<app_name>
|
|
48
|
-
cls._config_dir = (
|
|
49
|
-
Path(os.path.expanduser("~")) / "Library" / "Application Support"
|
|
50
|
-
)
|
|
51
|
-
else:
|
|
52
|
-
# Linux: ~/.config/<app_name>
|
|
53
|
-
cls._config_dir = Path(os.path.expanduser("~")) / ".config"
|
|
54
|
-
cls._config_dir = Path(cls._config_dir) / PROJECT_NAME
|
|
55
|
-
cls._config_file = Path(cls._config_dir) / "config.json"
|
|
29
|
+
cls._config_dir = CONFIG_DIR
|
|
30
|
+
cls._config_file = cls._config_dir / "config.json"
|
|
56
31
|
|
|
57
32
|
if not cls._config_file.is_file():
|
|
58
33
|
cls._config_dir.mkdir(exist_ok=True)
|
|
@@ -149,7 +124,11 @@ class config:
|
|
|
149
124
|
return cls._write_config()
|
|
150
125
|
|
|
151
126
|
@classmethod
|
|
152
|
-
def get_user_profile(
|
|
127
|
+
def get_user_profile(
|
|
128
|
+
cls, config_key: Config_key | str
|
|
129
|
+
) -> str | int | float | bool | None:
|
|
130
|
+
key = config_key.value if isinstance(config_key, Config_key) else config_key
|
|
131
|
+
|
|
153
132
|
if cls._config is None:
|
|
154
133
|
cls._read_config()
|
|
155
134
|
if cls._config is None:
|
|
@@ -216,6 +195,10 @@ class config:
|
|
|
216
195
|
CONFIG_DEFAULT_DICT[Config_key.log_write_level],
|
|
217
196
|
", ".join(log.LogLevel._member_names_),
|
|
218
197
|
),
|
|
198
|
+
Config_key.prompt_history_save_file.value: gettext(
|
|
199
|
+
"Save prompt history to config directory, otherwise save to memory. Take effect after reboot. Default: {}",
|
|
200
|
+
CONFIG_DEFAULT_DICT[Config_key.prompt_history_save_file],
|
|
201
|
+
),
|
|
219
202
|
}
|
|
220
203
|
| (cls._config or {})
|
|
221
204
|
).get(key, "None about")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
CONFIG_VERSION = "4.2.0"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Config_key(enum.Enum):
|
|
7
|
+
language = "language"
|
|
8
|
+
check_update = "check_update"
|
|
9
|
+
check_dependent = "check_dependent"
|
|
10
|
+
startup_directory = "startup_directory"
|
|
11
|
+
force_log_file_path = "force_log_file_path"
|
|
12
|
+
log_print_level = "log_print_level"
|
|
13
|
+
log_write_level = "log_write_level"
|
|
14
|
+
prompt_history_save_file = "prompt_history_file"
|
easyrip/easyrip_main.py
CHANGED
|
@@ -9,6 +9,7 @@ import subprocess
|
|
|
9
9
|
import sys
|
|
10
10
|
import threading
|
|
11
11
|
import tkinter as tk
|
|
12
|
+
import tomllib
|
|
12
13
|
from collections.abc import Callable
|
|
13
14
|
from concurrent.futures import ThreadPoolExecutor
|
|
14
15
|
from datetime import datetime
|
|
@@ -17,13 +18,11 @@ from pathlib import Path
|
|
|
17
18
|
from threading import Thread
|
|
18
19
|
from time import sleep
|
|
19
20
|
from tkinter import filedialog
|
|
20
|
-
from typing import Final
|
|
21
|
-
|
|
22
|
-
import tomllib
|
|
21
|
+
from typing import Final, Literal
|
|
23
22
|
|
|
24
23
|
from . import easyrip_mlang, easyrip_web, global_val
|
|
25
24
|
from .easyrip_command import Cmd_type, Opt_type, get_help_doc
|
|
26
|
-
from .easyrip_config import config
|
|
25
|
+
from .easyrip_config.config import Config_key, config
|
|
27
26
|
from .easyrip_log import Event as LogEvent
|
|
28
27
|
from .easyrip_log import log
|
|
29
28
|
from .easyrip_mlang import (
|
|
@@ -34,6 +33,7 @@ from .easyrip_mlang import (
|
|
|
34
33
|
gettext,
|
|
35
34
|
translate_subtitles,
|
|
36
35
|
)
|
|
36
|
+
from .easyrip_prompt import easyrip_prompt
|
|
37
37
|
from .ripper import Media_info, Ripper
|
|
38
38
|
from .ripper.ripper import DEFAULT_PRESET_PARAMS
|
|
39
39
|
from .utils import change_title, check_ver, read_text
|
|
@@ -73,7 +73,7 @@ def check_env() -> None:
|
|
|
73
73
|
try:
|
|
74
74
|
change_title(f"{gettext('Check env...')} {PROJECT_TITLE}")
|
|
75
75
|
|
|
76
|
-
if config.get_user_profile(
|
|
76
|
+
if config.get_user_profile(Config_key.check_dependent):
|
|
77
77
|
_url = "https://ffmpeg.org/download.html"
|
|
78
78
|
for _name in ("FFmpeg", "FFprobe"):
|
|
79
79
|
if not shutil.which(_name):
|
|
@@ -198,7 +198,7 @@ def check_env() -> None:
|
|
|
198
198
|
# ).stdout:
|
|
199
199
|
# log.error("The MediaInfo must be CLI ver")
|
|
200
200
|
|
|
201
|
-
if config.get_user_profile(
|
|
201
|
+
if config.get_user_profile(Config_key.check_update):
|
|
202
202
|
log_new_ver(
|
|
203
203
|
easyrip_web.github.get_latest_release_ver(
|
|
204
204
|
global_val.PROJECT_RELEASE_API
|
|
@@ -369,6 +369,36 @@ def run_ripper_list(
|
|
|
369
369
|
log.info("Run completed")
|
|
370
370
|
|
|
371
371
|
|
|
372
|
+
def get_web_server_params(
|
|
373
|
+
opt: str,
|
|
374
|
+
) -> Literal[False] | tuple[str, int, str | None]:
|
|
375
|
+
"""[<address>]:[<port>]@[<password>]"""
|
|
376
|
+
if easyrip_web.http_server.Event.is_run_command:
|
|
377
|
+
log.error("Can not start multiple services")
|
|
378
|
+
return False
|
|
379
|
+
|
|
380
|
+
if ":" not in opt:
|
|
381
|
+
log.error("{} param illegal", f"':' not in '{opt}':")
|
|
382
|
+
return False
|
|
383
|
+
|
|
384
|
+
opt_list: list[str] = opt.split(":")
|
|
385
|
+
opt_list = [opt_list[0], *opt_list[1].split("@")]
|
|
386
|
+
|
|
387
|
+
if len(opt_list) != 3:
|
|
388
|
+
log.error("{} param illegal", f"len('{opt}') != 3:")
|
|
389
|
+
return False
|
|
390
|
+
|
|
391
|
+
host, port, password = opt_list
|
|
392
|
+
|
|
393
|
+
port = port or "0"
|
|
394
|
+
|
|
395
|
+
if not port.isdigit():
|
|
396
|
+
log.error("{} param illegal", f"The port in '{opt}' not a digit:")
|
|
397
|
+
return False
|
|
398
|
+
|
|
399
|
+
return (host or "", int(port), password)
|
|
400
|
+
|
|
401
|
+
|
|
372
402
|
def run_command(command: list[str] | str) -> bool:
|
|
373
403
|
if isinstance(command, list):
|
|
374
404
|
cmd_list = command
|
|
@@ -564,8 +594,12 @@ def run_command(command: list[str] | str) -> bool:
|
|
|
564
594
|
log.error("list index out of range")
|
|
565
595
|
|
|
566
596
|
case Cmd_type.run:
|
|
567
|
-
is_run_exit = False
|
|
597
|
+
is_run_exit: bool = False
|
|
598
|
+
|
|
599
|
+
_web_server_params = None
|
|
600
|
+
|
|
568
601
|
_enable_multithreading: bool = False
|
|
602
|
+
|
|
569
603
|
_shutdown_sec_str: str | None = None
|
|
570
604
|
|
|
571
605
|
_skip_run_param: int = 0
|
|
@@ -594,6 +628,13 @@ def run_command(command: list[str] | str) -> bool:
|
|
|
594
628
|
log.error("{} need param", cmd)
|
|
595
629
|
return False
|
|
596
630
|
|
|
631
|
+
case "server":
|
|
632
|
+
_skip_run_param += 1
|
|
633
|
+
if (
|
|
634
|
+
_web_server_params := get_web_server_params(cmd_list[2])
|
|
635
|
+
) is False:
|
|
636
|
+
return False
|
|
637
|
+
|
|
597
638
|
case "-multithreading":
|
|
598
639
|
_skip_run_param += 1
|
|
599
640
|
if i + 1 < len(cmd_list[1:]):
|
|
@@ -606,58 +647,32 @@ def run_command(command: list[str] | str) -> bool:
|
|
|
606
647
|
log.error("Unsupported param: {}", param)
|
|
607
648
|
return False
|
|
608
649
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
case Cmd_type.server:
|
|
616
|
-
if easyrip_web.http_server.Event.is_run_command:
|
|
617
|
-
log.error("Can not start multiple services")
|
|
618
|
-
return False
|
|
619
|
-
|
|
620
|
-
address, password = None, None
|
|
621
|
-
|
|
622
|
-
for i in range(1, len(cmd_list)):
|
|
623
|
-
match cmd_list[i]:
|
|
624
|
-
case "-a" | "-address":
|
|
625
|
-
address = cmd_list[i + 1]
|
|
626
|
-
case "-p" | "-password":
|
|
627
|
-
password = cmd_list[i + 1]
|
|
628
|
-
case _:
|
|
629
|
-
if address is None:
|
|
630
|
-
address = cmd_list[i]
|
|
631
|
-
elif password is None:
|
|
632
|
-
password = cmd_list[i]
|
|
633
|
-
if address:
|
|
634
|
-
res = re.match(
|
|
635
|
-
r"^([a-zA-Z0-9.-]+)(:(\d+))?$",
|
|
636
|
-
address,
|
|
650
|
+
if _web_server_params is None:
|
|
651
|
+
run_ripper_list(
|
|
652
|
+
is_exit_when_run_finished=is_run_exit,
|
|
653
|
+
shutdow_sec_str=_shutdown_sec_str,
|
|
654
|
+
enable_multithreading=_enable_multithreading,
|
|
637
655
|
)
|
|
638
|
-
if res:
|
|
639
|
-
host = res.group(1)
|
|
640
|
-
port = res.group(2)
|
|
641
|
-
if port:
|
|
642
|
-
port = int(port.lstrip(":"))
|
|
643
|
-
elif host.isdigit():
|
|
644
|
-
port = int(host)
|
|
645
|
-
host = None
|
|
646
|
-
else:
|
|
647
|
-
port = None
|
|
648
|
-
host = None
|
|
649
|
-
else:
|
|
650
|
-
host, port = "localhost", 0
|
|
651
656
|
else:
|
|
652
|
-
|
|
657
|
+
easyrip_web.run_server(
|
|
658
|
+
*_web_server_params,
|
|
659
|
+
after_start_server_hook=lambda: run_ripper_list(
|
|
660
|
+
is_exit_when_run_finished=is_run_exit,
|
|
661
|
+
shutdow_sec_str=_shutdown_sec_str,
|
|
662
|
+
enable_multithreading=_enable_multithreading,
|
|
663
|
+
),
|
|
664
|
+
)
|
|
653
665
|
|
|
654
|
-
|
|
666
|
+
case Cmd_type.server:
|
|
667
|
+
if (_params := get_web_server_params(cmd_list[1])) is False:
|
|
668
|
+
return False
|
|
669
|
+
easyrip_web.run_server(*_params)
|
|
655
670
|
|
|
656
671
|
case Cmd_type.config:
|
|
657
672
|
match cmd_list[1]:
|
|
658
673
|
case "list" | "":
|
|
659
674
|
config.show_config_list()
|
|
660
|
-
case "
|
|
675
|
+
case "regenerate" | "clear" | "clean":
|
|
661
676
|
config.regenerate_config()
|
|
662
677
|
init()
|
|
663
678
|
case "open":
|
|
@@ -703,6 +718,17 @@ def run_command(command: list[str] | str) -> bool:
|
|
|
703
718
|
log.error("Unsupported param: {}", param)
|
|
704
719
|
return False
|
|
705
720
|
|
|
721
|
+
case Cmd_type.prompt:
|
|
722
|
+
match cmd_list[1]:
|
|
723
|
+
case "history":
|
|
724
|
+
with easyrip_prompt.PROMPT_HISTORY_FILE.open(
|
|
725
|
+
"rt", encoding="utf-8"
|
|
726
|
+
) as f:
|
|
727
|
+
for line in f.read().splitlines():
|
|
728
|
+
log.send(line, is_format=False)
|
|
729
|
+
case "clear" | "clean":
|
|
730
|
+
easyrip_prompt.clear()
|
|
731
|
+
|
|
706
732
|
case Cmd_type.translate:
|
|
707
733
|
if not (_infix := cmd_list[1]):
|
|
708
734
|
log.error("Need target infix")
|
|
@@ -761,6 +787,7 @@ def run_command(command: list[str] | str) -> bool:
|
|
|
761
787
|
preset_name: str | Ripper.PresetName | None = None
|
|
762
788
|
option_map: dict[str, str] = {}
|
|
763
789
|
is_run: bool = False
|
|
790
|
+
web_server_params = None
|
|
764
791
|
is_exit_when_run_finished: bool = False
|
|
765
792
|
shutdown_sec_str: str | None = None
|
|
766
793
|
enable_multithreading: bool = False
|
|
@@ -829,6 +856,13 @@ def run_command(command: list[str] | str) -> bool:
|
|
|
829
856
|
case "shutdown":
|
|
830
857
|
shutdown_sec_str = cmd_list[i + 2] or "60"
|
|
831
858
|
_skip += 1
|
|
859
|
+
case "server":
|
|
860
|
+
web_server_params = get_web_server_params(
|
|
861
|
+
cmd_list[i + 2]
|
|
862
|
+
)
|
|
863
|
+
if web_server_params is False:
|
|
864
|
+
return False
|
|
865
|
+
_skip += 1
|
|
832
866
|
case _:
|
|
833
867
|
_skip -= 1
|
|
834
868
|
|
|
@@ -1023,11 +1057,21 @@ def run_command(command: list[str] | str) -> bool:
|
|
|
1023
1057
|
return False
|
|
1024
1058
|
|
|
1025
1059
|
if is_run:
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1060
|
+
if web_server_params is None:
|
|
1061
|
+
run_ripper_list(
|
|
1062
|
+
is_exit_when_run_finished=is_exit_when_run_finished,
|
|
1063
|
+
shutdow_sec_str=shutdown_sec_str,
|
|
1064
|
+
enable_multithreading=enable_multithreading,
|
|
1065
|
+
)
|
|
1066
|
+
else:
|
|
1067
|
+
easyrip_web.run_server(
|
|
1068
|
+
*web_server_params,
|
|
1069
|
+
after_start_server_hook=lambda: run_ripper_list(
|
|
1070
|
+
is_exit_when_run_finished=is_exit_when_run_finished,
|
|
1071
|
+
shutdow_sec_str=shutdown_sec_str,
|
|
1072
|
+
enable_multithreading=enable_multithreading,
|
|
1073
|
+
),
|
|
1074
|
+
)
|
|
1031
1075
|
|
|
1032
1076
|
return True
|
|
1033
1077
|
|
|
@@ -1045,7 +1089,10 @@ def init(is_first_run: bool = False) -> None:
|
|
|
1045
1089
|
# 设置语言
|
|
1046
1090
|
_sys_lang = get_system_language()
|
|
1047
1091
|
Global_lang_val.gettext_target_lang = _sys_lang
|
|
1048
|
-
if (_lang_config := config.get_user_profile(
|
|
1092
|
+
if (_lang_config := config.get_user_profile(Config_key.language)) not in {
|
|
1093
|
+
"auto",
|
|
1094
|
+
None,
|
|
1095
|
+
}:
|
|
1049
1096
|
Global_lang_val.gettext_target_lang = Lang_tag.from_str(str(_lang_config))
|
|
1050
1097
|
|
|
1051
1098
|
# 设置日志文件路径名
|
|
@@ -1056,10 +1103,10 @@ def init(is_first_run: bool = False) -> None:
|
|
|
1056
1103
|
# 设置日志级别
|
|
1057
1104
|
try:
|
|
1058
1105
|
log.print_level = getattr(
|
|
1059
|
-
log.LogLevel, str(config.get_user_profile(
|
|
1106
|
+
log.LogLevel, str(config.get_user_profile(Config_key.log_print_level))
|
|
1060
1107
|
)
|
|
1061
1108
|
log.write_level = getattr(
|
|
1062
|
-
log.LogLevel, str(config.get_user_profile(
|
|
1109
|
+
log.LogLevel, str(config.get_user_profile(Config_key.log_write_level))
|
|
1063
1110
|
)
|
|
1064
1111
|
except Exception as e:
|
|
1065
1112
|
log.error(f"{e!r} {e}", deep=True)
|
|
@@ -1067,7 +1114,7 @@ def init(is_first_run: bool = False) -> None:
|
|
|
1067
1114
|
if is_first_run:
|
|
1068
1115
|
# 设置启动目录
|
|
1069
1116
|
try:
|
|
1070
|
-
if _path := str(config.get_user_profile(
|
|
1117
|
+
if _path := str(config.get_user_profile(Config_key.startup_directory)):
|
|
1071
1118
|
os.chdir(_path)
|
|
1072
1119
|
except Exception as e:
|
|
1073
1120
|
log.error(f"{e!r} {e}", deep=True)
|
|
@@ -71,21 +71,24 @@ LANG_MAP: dict[str, str] = {
|
|
|
71
71
|
"<int> <int>:\n"
|
|
72
72
|
" 交换指定索引"
|
|
73
73
|
),
|
|
74
|
-
Cmd_type.run.value.param: "<run 选项>",
|
|
74
|
+
Cmd_type.run.value.param: "[<run 选项>] [-multithreading <0 | 1>]",
|
|
75
75
|
Cmd_type.run.value.description: (
|
|
76
76
|
"执行 Ripper list 中的 Ripper\n"
|
|
77
|
-
"
|
|
77
|
+
"\n"
|
|
78
78
|
"默认:\n"
|
|
79
79
|
" 仅执行\n"
|
|
80
|
-
"
|
|
80
|
+
"\n"
|
|
81
81
|
"exit:\n"
|
|
82
82
|
" 执行后退出程序\n"
|
|
83
|
-
"
|
|
83
|
+
"\n"
|
|
84
84
|
"shutdown [<秒数>]:\n"
|
|
85
85
|
" 执行后关机\n"
|
|
86
|
-
" 默认: 60"
|
|
86
|
+
" 默认: 60\n"
|
|
87
|
+
"\n"
|
|
88
|
+
"server [<地址>]:[<端口>]@[<密码>]:\n"
|
|
89
|
+
" 详见对应的 help"
|
|
87
90
|
),
|
|
88
|
-
Cmd_type.server.value.param: "[
|
|
91
|
+
Cmd_type.server.value.param: "[<地址>]:[<端口>]@[<密码>]",
|
|
89
92
|
Cmd_type.server.value.description: (
|
|
90
93
|
"启动 web 服务\n"
|
|
91
94
|
"默认: server localhost:0\n"
|
|
@@ -103,6 +106,7 @@ LANG_MAP: dict[str, str] = {
|
|
|
103
106
|
" 设置 config\n"
|
|
104
107
|
" 例如 config set language en"
|
|
105
108
|
),
|
|
109
|
+
Cmd_type.prompt.value.param: "<prompt 选项>",
|
|
106
110
|
Cmd_type.translate.value.param: "<中缀> <目标语言标签> [-overwrite]",
|
|
107
111
|
Cmd_type.translate.value.description: (
|
|
108
112
|
"翻译字幕文件\n"
|
|
@@ -238,16 +242,19 @@ LANG_MAP: dict[str, str] = {
|
|
|
238
242
|
),
|
|
239
243
|
Opt_type._run.value.description: (
|
|
240
244
|
"执行 Ripper list 中的 Ripper\n"
|
|
241
|
-
"
|
|
245
|
+
"\n"
|
|
242
246
|
"默认:\n"
|
|
243
247
|
" 仅执行\n"
|
|
244
|
-
"
|
|
248
|
+
"\n"
|
|
245
249
|
"exit:\n"
|
|
246
250
|
" 执行后退出程序\n"
|
|
247
|
-
"
|
|
251
|
+
"\n"
|
|
248
252
|
"shutdown [<秒数>]:\n"
|
|
249
253
|
" 执行后关机\n"
|
|
250
|
-
" 默认: 60"
|
|
254
|
+
" 默认: 60\n"
|
|
255
|
+
"\n"
|
|
256
|
+
"server [<地址>]:[<端口>]@[<密码>]:\n"
|
|
257
|
+
" 详见对应的 help"
|
|
251
258
|
),
|
|
252
259
|
Opt_type._ff_params_ff.value.description: (
|
|
253
260
|
"设置 FFmpeg 的全局选项\n" # .
|
|
@@ -353,6 +360,7 @@ LANG_MAP: dict[str, str] = {
|
|
|
353
360
|
"User profile is not a valid dictionary": "用户配置文件不是有效的字典",
|
|
354
361
|
"User profile is not found": "用户配置文件不存在",
|
|
355
362
|
"Key '{}' is not found in user profile": "用户配置文件中不存在 {}",
|
|
363
|
+
"Save prompt history to config directory, otherwise save to memory. Take effect after reboot. Default: {}": "将 prompt 历史保存到 config 目录,否则保存到内存。重启后生效。默认: {}",
|
|
356
364
|
# config about
|
|
357
365
|
"Easy Rip's language, support incomplete matching. Default: {}. Supported: {}": "Easy Rip 的语言, 支持不完整匹配。默认: {}。支持: {}",
|
|
358
366
|
"Auto check the update of Easy Rip. Default: {}": "自动检测 Easy Rip 更新。默认: {}",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from prompt_toolkit.history import FileHistory
|
|
2
|
+
|
|
3
|
+
from .global_val import C_Z, CONFIG_DIR
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class easyrip_prompt:
|
|
7
|
+
PROMPT_HISTORY_FILE = CONFIG_DIR / "prompt_history.txt"
|
|
8
|
+
|
|
9
|
+
@classmethod
|
|
10
|
+
def clear(cls) -> None:
|
|
11
|
+
cls.PROMPT_HISTORY_FILE.unlink(True)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConfigFileHistory(FileHistory):
|
|
15
|
+
def store_string(self, string: str) -> None:
|
|
16
|
+
if not string.startswith(C_Z):
|
|
17
|
+
super().store_string(string)
|
|
@@ -4,6 +4,7 @@ import os
|
|
|
4
4
|
import secrets
|
|
5
5
|
import signal
|
|
6
6
|
from collections import deque
|
|
7
|
+
from collections.abc import Callable
|
|
7
8
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
8
9
|
from threading import Thread
|
|
9
10
|
from time import sleep
|
|
@@ -204,7 +205,13 @@ class MainHTTPRequestHandler(BaseHTTPRequestHandler):
|
|
|
204
205
|
)
|
|
205
206
|
|
|
206
207
|
|
|
207
|
-
def run_server(
|
|
208
|
+
def run_server(
|
|
209
|
+
host: str = "",
|
|
210
|
+
port: int = 0,
|
|
211
|
+
password: str | None = None,
|
|
212
|
+
*,
|
|
213
|
+
after_start_server_hook: Callable[[], None] = lambda: None,
|
|
214
|
+
) -> None:
|
|
208
215
|
from ..easyrip_log import log
|
|
209
216
|
|
|
210
217
|
MainHTTPRequestHandler.token = secrets.token_urlsafe(16)
|
|
@@ -223,6 +230,15 @@ def run_server(host: str = "", port: int = 0, password: str | None = None) -> No
|
|
|
223
230
|
|
|
224
231
|
protocol = "HTTP"
|
|
225
232
|
|
|
233
|
+
def _hook() -> None:
|
|
234
|
+
try:
|
|
235
|
+
after_start_server_hook()
|
|
236
|
+
finally:
|
|
237
|
+
Event.is_run_command = False
|
|
238
|
+
|
|
239
|
+
Event.is_run_command = True
|
|
240
|
+
Thread(target=_hook, daemon=True).start()
|
|
241
|
+
|
|
226
242
|
log.info(
|
|
227
243
|
"Starting {protocol} service on port {port}...",
|
|
228
244
|
protocol=protocol,
|
easyrip/global_val.py
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
1
5
|
PROJECT_NAME = "Easy Rip"
|
|
2
|
-
PROJECT_VERSION = "4.
|
|
6
|
+
PROJECT_VERSION = "4.3.0"
|
|
3
7
|
PROJECT_TITLE = f"{PROJECT_NAME} v{PROJECT_VERSION}"
|
|
4
8
|
PROJECT_URL = "https://github.com/op200/EasyRip"
|
|
5
9
|
PROJECT_RELEASE_API = "https://api.github.com/repos/op200/EasyRip/releases/latest"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
if sys.platform == "win32":
|
|
13
|
+
# Windows: C:\Users\<用户名>\AppData\Roaming\<app_name>
|
|
14
|
+
__config_dir = Path(os.getenv("APPDATA", ""))
|
|
15
|
+
elif sys.platform == "darwin":
|
|
16
|
+
# macOS: ~/Library/Application Support/<app_name>
|
|
17
|
+
__config_dir = Path(os.path.expanduser("~")) / "Library" / "Application Support"
|
|
18
|
+
else:
|
|
19
|
+
# Linux: ~/.config/<app_name>
|
|
20
|
+
__config_dir = Path(os.path.expanduser("~")) / ".config"
|
|
21
|
+
CONFIG_DIR = Path(__config_dir) / PROJECT_NAME
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
C_D = "\x04"
|
|
25
|
+
C_Z = "\x1a"
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easyrip
|
|
3
|
-
Version: 4.
|
|
4
|
-
|
|
3
|
+
Version: 4.3.0
|
|
4
|
+
Author: op200
|
|
5
|
+
License-Expression: AGPL-3.0-or-later
|
|
6
|
+
Project-URL: Homepage, https://github.com/op200/EasyRip
|
|
7
|
+
Project-URL: Source, https://github.com/op200/EasyRip
|
|
5
8
|
Classifier: Topic :: Multimedia
|
|
6
9
|
Classifier: Development Status :: 5 - Production/Stable
|
|
7
|
-
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
|
8
10
|
Classifier: Programming Language :: Python :: 3.12
|
|
9
11
|
Classifier: Programming Language :: Python :: 3.13
|
|
10
12
|
Classifier: Programming Language :: Python :: 3.14
|
|
@@ -21,13 +23,7 @@ License-File: LICENSE
|
|
|
21
23
|
Requires-Dist: prompt-toolkit>=3.0.52
|
|
22
24
|
Requires-Dist: fonttools>=4.61.0
|
|
23
25
|
Requires-Dist: pycryptodome>=3.21.0
|
|
24
|
-
Dynamic: classifier
|
|
25
|
-
Dynamic: description
|
|
26
|
-
Dynamic: description-content-type
|
|
27
26
|
Dynamic: license-file
|
|
28
|
-
Dynamic: project-url
|
|
29
|
-
Dynamic: requires-dist
|
|
30
|
-
Dynamic: requires-python
|
|
31
27
|
|
|
32
28
|
# Easy Rip
|
|
33
29
|
|
|
@@ -39,31 +35,17 @@ Easy Rip 网页版控制台](https://op200.github.io/EasyRip-WebPanel/)**
|
|
|
39
35
|
|
|
40
36
|
## Start
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
1. Install [Python](https://www.python.org/)
|
|
39
|
+
安装 [Python](https://www.python.org/)
|
|
40
|
+
2. Install Easy Rip using pip: `pip install -U easyrip`
|
|
41
|
+
使用 pip 安装 Easy Rip: `pip install -U easyrip`
|
|
42
|
+
3. The you can use Easy Rip directly, run the command `easyrip`
|
|
43
|
+
然后你就可以直接 Easy Rip 了,运行命令 `easyrip`
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
安装后直接在命令行运行 `easyrip`。
|
|
50
|
-
|
|
51
|
-
* Or if you want to download a standalone exe file (not recommended)
|
|
52
|
-
或者如果你想下载一个独立的可执行文件(不推荐)
|
|
53
|
-
|
|
54
|
-
*
|
|
55
|
-
Download exe in [Actions](https://github.com/op200/EasyRip/actions).
|
|
56
|
-
Or download exe or bat script collection in [Releases](https://github.com/op200/EasyRip/releases).
|
|
57
|
-
|
|
58
|
-
在 [Actions](https://github.com/op200/EasyRip/actions) 中下载最新的 exe。
|
|
59
|
-
或者在 [Releases](https://github.com/op200/EasyRip/releases) 中下载 exe 或 bat 脚本包。
|
|
60
|
-
|
|
61
|
-
*
|
|
62
|
-
The file `BatchScriptPackage` in [Releases](https://github.com/op200/EasyRip/releases) is a bat script collection.
|
|
63
|
-
It is used to facilitate ordinary users, it only has Chinese.
|
|
64
|
-
|
|
65
|
-
[Releases](https://github.com/op200/EasyRip/releases) 中每隔一段时间发布一次名为 BatchScriptPackage 的 bat 脚本包
|
|
66
|
-
用于方便一般用户,其内只有中文
|
|
45
|
+
* (If you have special requirements and want to use a separate file, you can download it from [Github Actions](https://github.com/op200/EasyRip/actions))
|
|
46
|
+
(如果你有特殊需求,想使用独立文件,可以在 [Github Actions](https://github.com/op200/EasyRip/actions) 中下载)
|
|
47
|
+
* (The file `BatchScriptPackage` in [Releases](https://github.com/op200/EasyRip/releases) is a bat script collection. It is used to facilitate ordinary users, it only has Chinese.)
|
|
48
|
+
([Releases](https://github.com/op200/EasyRip/releases) 中每隔一段时间发布一次名为 `BatchScriptPackage` 的 bat 脚本包,用于方便一般用户,其内只有中文。)
|
|
67
49
|
|
|
68
50
|
## Usage
|
|
69
51
|
|
|
@@ -75,7 +57,7 @@ Run `easyrip`, input `help` to get help doc
|
|
|
75
57
|
|
|
76
58
|
## Dependency
|
|
77
59
|
|
|
78
|
-
* ### Python 3.
|
|
60
|
+
* ### Python 3.14 (must >=3.12)
|
|
79
61
|
|
|
80
62
|
If you want to develop, you need to install dependencies. If you just want to use them, you don't need to manually install dependencies.
|
|
81
63
|
如果你想开发,需要安装依赖,如果你只是想使用,不需要手动安装依赖。
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
easyrip/__init__.py,sha256=BlDIN9htmQXETt7M8pjYSOVrKevuuyfRn2ajQI4J7us,648
|
|
2
|
-
easyrip/__main__.py,sha256=
|
|
3
|
-
easyrip/easyrip_command.py,sha256=
|
|
4
|
-
easyrip/easyrip_config.py,sha256=CO7nTilnbhzGw9HCl2aiXCxtYs6eTfrAds8E-_qvCos,8360
|
|
2
|
+
easyrip/__main__.py,sha256=lwEV17Xi1cBWWHd7GXloKDjuWe2empflf7rMkMiLWGw,4375
|
|
3
|
+
easyrip/easyrip_command.py,sha256=KirlybDIkz3aYPlxqT_VCPsxfdCp-pi62KJc1P6Yc6g,26960
|
|
5
4
|
easyrip/easyrip_log.py,sha256=0V5wBwbqWt56w_NHoLmQmkTHPnPLkYZCAbhDSYnLnDw,15611
|
|
6
|
-
easyrip/easyrip_main.py,sha256=
|
|
7
|
-
easyrip/
|
|
5
|
+
easyrip/easyrip_main.py,sha256=dEDUoBDF6flbFrgwdnR8x7yofBdprHNe8Nwkqp00J04,43497
|
|
6
|
+
easyrip/easyrip_prompt.py,sha256=cEWI3XFooUwwQcQ_C8dVfd5bP6TlqQSwRHtkj_ezZs4,432
|
|
7
|
+
easyrip/global_val.py,sha256=_8DiZFRvM97uzbKlD2ELPtJwV5WjRXd-sTUtJAOuL1E,773
|
|
8
8
|
easyrip/utils.py,sha256=u1Qi5z0ZF31f_dQHtvsfcfeKk5PirlTeuHnlqL4mkrs,6452
|
|
9
|
+
easyrip/easyrip_config/config.py,sha256=9uskiGIpYLuS-vjzNASZlhdWLWXkEVxtSjODxcZU3pc,7935
|
|
10
|
+
easyrip/easyrip_config/config_key.py,sha256=wANVusz9kNqsYJUvXM7DUQn6k_G6EO2VWTHWh9dOifw,394
|
|
9
11
|
easyrip/easyrip_mlang/__init__.py,sha256=0XYa0kcnUPH1aaRV3ceIPuYO1Oov8LBxgVjel-1slvI,3226
|
|
10
12
|
easyrip/easyrip_mlang/global_lang_val.py,sha256=Un20KGMVVMQbOaV_7VaHg3E1dvK2Y7kI1cPzq_sFWKY,9984
|
|
11
13
|
easyrip/easyrip_mlang/lang_en.py,sha256=heUSPeVtY1Nf9eDhrjPM28N3PLsu62op3llbXAfXvAs,140
|
|
12
|
-
easyrip/easyrip_mlang/lang_zh_Hans_CN.py,sha256=
|
|
14
|
+
easyrip/easyrip_mlang/lang_zh_Hans_CN.py,sha256=0DtvBg7khE6NbFt1carSwgMNkigQbMx2e0LsDv4wFhY,18520
|
|
13
15
|
easyrip/easyrip_mlang/translator.py,sha256=Vg0S0p2rpMNAy3LHTdK7qKFdkPEXlOoCCXcaH7PyAC4,5939
|
|
14
16
|
easyrip/easyrip_web/__init__.py,sha256=tMyEeaSGeEJjND7MF0MBv9aDiDgaO3MOnppwxA70U2c,177
|
|
15
|
-
easyrip/easyrip_web/http_server.py,sha256=
|
|
17
|
+
easyrip/easyrip_web/http_server.py,sha256=iyulCAFQrJlz86Lrr-Dm3fhOnNCf79Bp6fVHhr0ephY,8350
|
|
16
18
|
easyrip/easyrip_web/third_party_api.py,sha256=umj-QsfOa0IM60Ic2pXigVfnGfAiiC95fJSXQ-WFpJA,4419
|
|
17
19
|
easyrip/ripper/__init__.py,sha256=o5gj8QRvkuVc0S9jbRjVOWXpLAaQKC0-n-o6Zx9Dowo,171
|
|
18
20
|
easyrip/ripper/media_info.py,sha256=U_XLhNrprIQXWWdxOkGqQGN5V1wla55XxYhf5z9hRI0,4986
|
|
@@ -21,9 +23,9 @@ easyrip/ripper/font_subset/__init__.py,sha256=a6UoRH1AU1l5FkFgQ-wQxOswLmovgVUUl3
|
|
|
21
23
|
easyrip/ripper/font_subset/ass.py,sha256=Z1TYmF32pJoXhwsjjZKgScjcsbiayXjp9tnM533ULqs,25806
|
|
22
24
|
easyrip/ripper/font_subset/font.py,sha256=9yhZmZc-8wvbk2NDN3B4oJMt-UVByzM_pukzMzd-du4,7114
|
|
23
25
|
easyrip/ripper/font_subset/subset.py,sha256=yj5JYZg1XPO1U0zsZUHSsOX105wrmE_g-2jLpcrvqqc,17137
|
|
24
|
-
easyrip-4.
|
|
25
|
-
easyrip-4.
|
|
26
|
-
easyrip-4.
|
|
27
|
-
easyrip-4.
|
|
28
|
-
easyrip-4.
|
|
29
|
-
easyrip-4.
|
|
26
|
+
easyrip-4.3.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
27
|
+
easyrip-4.3.0.dist-info/METADATA,sha256=ynq-ebYL9XrnQDIZsnE279h1RXP1ABOpZ-VgjJUFreI,3506
|
|
28
|
+
easyrip-4.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
29
|
+
easyrip-4.3.0.dist-info/entry_points.txt,sha256=D6GBMMTzZ-apgX76KyZ6jxMmIFqGYwU9neeLLni_qKI,49
|
|
30
|
+
easyrip-4.3.0.dist-info/top_level.txt,sha256=kuEteBXm-Gf90jRQgH3-fTo-Z-Q6czSuUEqY158H4Ww,8
|
|
31
|
+
easyrip-4.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|