easyrip 4.15.1__py3-none-any.whl → 4.15.3__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 +4 -34
- easyrip/easyrip_command.py +68 -55
- easyrip/easyrip_main.py +6 -0
- easyrip/easyrip_prompt.py +8 -6
- easyrip/global_val.py +1 -1
- easyrip/ripper/ripper.py +71 -83
- {easyrip-4.15.1.dist-info → easyrip-4.15.3.dist-info}/METADATA +9 -1
- {easyrip-4.15.1.dist-info → easyrip-4.15.3.dist-info}/RECORD +12 -12
- {easyrip-4.15.1.dist-info → easyrip-4.15.3.dist-info}/WHEEL +0 -0
- {easyrip-4.15.1.dist-info → easyrip-4.15.3.dist-info}/entry_points.txt +0 -0
- {easyrip-4.15.1.dist-info → easyrip-4.15.3.dist-info}/licenses/LICENSE +0 -0
- {easyrip-4.15.1.dist-info → easyrip-4.15.3.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,38 +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
|
-
if not ctv.is_no_prompt_child
|
|
111
|
-
}
|
|
112
|
-
)
|
|
113
|
-
|
|
114
85
|
def _ctv_to_nd(ctvs: Iterable[Cmd_type_val]) -> nested_dict:
|
|
115
86
|
return {
|
|
116
87
|
name: (
|
|
@@ -136,7 +107,6 @@ def run() -> NoReturn:
|
|
|
136
107
|
if not ctv.is_no_prompt_child
|
|
137
108
|
}
|
|
138
109
|
|
|
139
|
-
cmd_ctv_tuple = tuple(ct.value for ct in Cmd_type if ct != Cmd_type.Option)
|
|
140
110
|
prompt_history = (
|
|
141
111
|
ConfigFileHistory(easyrip_prompt.PROMPT_HISTORY_FILE)
|
|
142
112
|
if config.get_user_profile(Config_key.save_prompt_history)
|
|
@@ -150,7 +120,7 @@ def run() -> NoReturn:
|
|
|
150
120
|
key_bindings=key_bindings,
|
|
151
121
|
completer=merge_completers(
|
|
152
122
|
(
|
|
153
|
-
|
|
123
|
+
CmdCompleter(),
|
|
154
124
|
OptCompleter(opt_tree=_ctv_to_nd(ct.value for ct in Opt_type)),
|
|
155
125
|
CustomPromptCompleter(),
|
|
156
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
|
|
|
@@ -715,7 +719,7 @@ class Opt_type(enum.Enum):
|
|
|
715
719
|
"Algorithm:"
|
|
716
720
|
),
|
|
717
721
|
childs=(
|
|
718
|
-
Cmd_type_val(("ssim",), description="Default threshold: 0.
|
|
722
|
+
Cmd_type_val(("ssim",), description="Default threshold: 0.85"),
|
|
719
723
|
Cmd_type_val(("psnr",), description="Default threshold: 30"),
|
|
720
724
|
Cmd_type_val(("vmaf",), description="Default threshold: 80"),
|
|
721
725
|
),
|
|
@@ -768,11 +772,6 @@ META_DICT_OPT_TYPE = {
|
|
|
768
772
|
for opt in Opt_type
|
|
769
773
|
for name in opt.value.names
|
|
770
774
|
}
|
|
771
|
-
META_DICT_CMD_TYPE = {
|
|
772
|
-
name: lambda opt=opt: opt.value.param
|
|
773
|
-
for opt in Cmd_type
|
|
774
|
-
for name in opt.value.names
|
|
775
|
-
}
|
|
776
775
|
|
|
777
776
|
|
|
778
777
|
def _nested_dict_to_nc(n_dict: nested_dict) -> NestedCompleter:
|
|
@@ -784,60 +783,74 @@ def _nested_dict_to_nc(n_dict: nested_dict) -> NestedCompleter:
|
|
|
784
783
|
)
|
|
785
784
|
|
|
786
785
|
|
|
787
|
-
|
|
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
|
+
|
|
788
795
|
def get_completions(
|
|
789
796
|
self, document: Document, complete_event: CompleteEvent
|
|
790
797
|
) -> Iterable[Completion]:
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
words = text.split()
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
),
|
|
821
|
-
|
|
822
|
-
text="",
|
|
823
|
-
display="✔",
|
|
824
|
-
display_meta=(
|
|
825
|
-
f"{_desc_list[0]}..."
|
|
826
|
-
if len(_desc_list := _cmd.value.description.split("\n")) > 1
|
|
827
|
-
else _desc_list[0]
|
|
828
|
-
),
|
|
829
|
-
),
|
|
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,
|
|
830
829
|
)
|
|
831
|
-
|
|
832
|
-
|
|
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
|
+
)
|
|
833
836
|
else:
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
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
|
|
839
853
|
)
|
|
840
|
-
yield from completer.get_completions(document, complete_event)
|
|
841
854
|
|
|
842
855
|
|
|
843
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:
|
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.3"
|
|
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
|
@@ -8,6 +8,7 @@ from collections.abc import Callable, Iterable
|
|
|
8
8
|
from dataclasses import dataclass
|
|
9
9
|
from datetime import datetime
|
|
10
10
|
from itertools import zip_longest
|
|
11
|
+
from operator import itemgetter
|
|
11
12
|
from pathlib import Path
|
|
12
13
|
from threading import Thread
|
|
13
14
|
from time import sleep
|
|
@@ -21,7 +22,7 @@ from ..easyrip_mlang import (
|
|
|
21
22
|
gettext,
|
|
22
23
|
translate_subtitles,
|
|
23
24
|
)
|
|
24
|
-
from ..utils import get_base62_time,
|
|
25
|
+
from ..utils import get_base62_time, type_match
|
|
25
26
|
from .media_info import Media_info, Stream_error
|
|
26
27
|
from .param import (
|
|
27
28
|
FONT_SUFFIX_SET,
|
|
@@ -251,28 +252,29 @@ class Ripper:
|
|
|
251
252
|
|
|
252
253
|
# Muxer
|
|
253
254
|
muxer_format_str_list: list[str]
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if "'
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
255
|
+
track_name_list: list[str] = []
|
|
256
|
+
if (_track_name_org_str := self.option_map.get("track-name")) is not None:
|
|
257
|
+
try:
|
|
258
|
+
track_name_list = ast.literal_eval(_track_name_org_str)
|
|
259
|
+
except Exception as e:
|
|
260
|
+
raise Mlang_exception("{} param illegal", "-track-name") from e
|
|
261
|
+
if not type_match(track_name_list, list[str]):
|
|
262
|
+
raise Mlang_exception("{} param illegal", "-track-name")
|
|
263
|
+
for i in range(len(track_name_list)):
|
|
264
|
+
track_name = track_name_list[i]
|
|
265
|
+
if '"' in track_name:
|
|
266
|
+
if "'" in track_name:
|
|
267
|
+
raise Mlang_exception(
|
|
268
|
+
"{} param illegal: {}",
|
|
269
|
+
"-track-name",
|
|
270
|
+
"The '\"' and \"'\" can not exist simultaneously",
|
|
271
|
+
)
|
|
272
|
+
track_name = f"'{track_name}'"
|
|
273
|
+
else:
|
|
274
|
+
track_name = f'"{track_name}"'
|
|
275
|
+
track_name_list[i] = track_name
|
|
276
|
+
log.debug(f"-track-name <- {_track_name_org_str!r}", is_format=False)
|
|
277
|
+
log.debug(f"-track-name -> {track_name_list!r}", is_format=False)
|
|
276
278
|
|
|
277
279
|
mkv_all_need_opt_str: str = (
|
|
278
280
|
"".join(f"--track-name {track_name} " for track_name in track_name_list)
|
|
@@ -1304,30 +1306,23 @@ class Ripper:
|
|
|
1304
1306
|
while True:
|
|
1305
1307
|
match quality_detection[0]:
|
|
1306
1308
|
case "ssim":
|
|
1307
|
-
quality_detection_th = 0.
|
|
1309
|
+
quality_detection_th = 0.85
|
|
1308
1310
|
quality_detection_filter = "ssim=f="
|
|
1309
1311
|
|
|
1310
1312
|
def quality_detection_cmp(
|
|
1311
|
-
text: str,
|
|
1312
|
-
) ->
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1313
|
+
text: str,
|
|
1314
|
+
) -> list[tuple[str | int, float]]:
|
|
1315
|
+
return [
|
|
1316
|
+
(n, q)
|
|
1317
|
+
for line in text.splitlines()
|
|
1318
|
+
if (
|
|
1319
|
+
v := [
|
|
1320
|
+
s.split(":")[1] for s in line.split()[:-1]
|
|
1321
|
+
]
|
|
1316
1322
|
)
|
|
1317
|
-
|
|
1318
|
-
n
|
|
1319
|
-
|
|
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
|
-
)
|
|
1323
|
+
and (q := float(v[-1]))
|
|
1324
|
+
and (n := int(v[0]) - 1)
|
|
1325
|
+
]
|
|
1331
1326
|
|
|
1332
1327
|
break
|
|
1333
1328
|
case "psnr":
|
|
@@ -1335,26 +1330,15 @@ class Ripper:
|
|
|
1335
1330
|
quality_detection_filter = "psnr=f="
|
|
1336
1331
|
|
|
1337
1332
|
def quality_detection_cmp(
|
|
1338
|
-
text: str,
|
|
1339
|
-
) ->
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
)
|
|
1344
|
-
|
|
1345
|
-
n
|
|
1346
|
-
|
|
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
|
-
)
|
|
1333
|
+
text: str,
|
|
1334
|
+
) -> list[tuple[str | int, float]]:
|
|
1335
|
+
return [
|
|
1336
|
+
(n, q)
|
|
1337
|
+
for line in text.splitlines()
|
|
1338
|
+
if (v := [s.split(":")[1] for s in line.split()])
|
|
1339
|
+
and (q := float(v[-4]))
|
|
1340
|
+
and (n := int(v[0]) - 1)
|
|
1341
|
+
]
|
|
1358
1342
|
|
|
1359
1343
|
break
|
|
1360
1344
|
case "vmaf":
|
|
@@ -1362,23 +1346,13 @@ class Ripper:
|
|
|
1362
1346
|
quality_detection_filter = "libvmaf=log_fmt=csv:log_path="
|
|
1363
1347
|
|
|
1364
1348
|
def quality_detection_cmp(
|
|
1365
|
-
text: str,
|
|
1366
|
-
) ->
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
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
|
-
)
|
|
1349
|
+
text: str,
|
|
1350
|
+
) -> list[tuple[str | int, float]]:
|
|
1351
|
+
return [
|
|
1352
|
+
(n, q)
|
|
1353
|
+
for v in tuple(csv.reader(text.splitlines()[1:]))
|
|
1354
|
+
if (q := float(v[-2])) and (n := v[0])
|
|
1355
|
+
]
|
|
1382
1356
|
|
|
1383
1357
|
break
|
|
1384
1358
|
case _:
|
|
@@ -1403,6 +1377,7 @@ class Ripper:
|
|
|
1403
1377
|
.replace("\\", "/")
|
|
1404
1378
|
.replace(":", "\\\\:")
|
|
1405
1379
|
)
|
|
1380
|
+
|
|
1406
1381
|
if os.system(
|
|
1407
1382
|
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
1383
|
):
|
|
@@ -1413,9 +1388,22 @@ class Ripper:
|
|
|
1413
1388
|
"-quality-detection",
|
|
1414
1389
|
f"{quality_detection[0]}:{quality_detection_th}",
|
|
1415
1390
|
)
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1391
|
+
with quality_detection_data_file.open("rt", encoding="utf-8") as f:
|
|
1392
|
+
_res = quality_detection_cmp(f.read())
|
|
1393
|
+
for n, q in _res:
|
|
1394
|
+
if q < quality_detection_th:
|
|
1395
|
+
log.error(
|
|
1396
|
+
"{} {} < threshold {} in frame {}",
|
|
1397
|
+
quality_detection[0].upper(),
|
|
1398
|
+
q,
|
|
1399
|
+
quality_detection_th,
|
|
1400
|
+
n,
|
|
1401
|
+
)
|
|
1402
|
+
log.info(
|
|
1403
|
+
"{} min = {}",
|
|
1404
|
+
quality_detection[0].upper(),
|
|
1405
|
+
min(map(itemgetter(1), _res)),
|
|
1406
|
+
)
|
|
1419
1407
|
log.debug("'{}' end", "-quality-detection")
|
|
1420
1408
|
quality_detection_data_file.unlink(missing_ok=True)
|
|
1421
1409
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easyrip
|
|
3
|
-
Version: 4.15.
|
|
3
|
+
Version: 4.15.3
|
|
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=6j2EFg9PesESmPpDbxfs8mzLVTMwcEjj9xeuPfbq7B4,34011
|
|
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=csV8VD8QlV7R-Ttmd58stypV60WujAFIKdkzReJjXQo,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
|
|
@@ -19,14 +19,14 @@ easyrip/easyrip_web/http_server.py,sha256=iyulCAFQrJlz86Lrr-Dm3fhOnNCf79Bp6fVHhr
|
|
|
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=h2jZaysF8r8UB5jEJRA1gqS-L6N_IpsDvtBlEToRgYY,59555
|
|
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.3.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
28
|
+
easyrip-4.15.3.dist-info/METADATA,sha256=nrEtCOX6GwE7TgxjqLQrW7bdL9Q3RyWQdIl76O2U-EY,4061
|
|
29
|
+
easyrip-4.15.3.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
30
|
+
easyrip-4.15.3.dist-info/entry_points.txt,sha256=D6GBMMTzZ-apgX76KyZ6jxMmIFqGYwU9neeLLni_qKI,49
|
|
31
|
+
easyrip-4.15.3.dist-info/top_level.txt,sha256=kuEteBXm-Gf90jRQgH3-fTo-Z-Q6czSuUEqY158H4Ww,8
|
|
32
|
+
easyrip-4.15.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|