easyrip 4.11.2__py3-none-any.whl → 4.11.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.
@@ -7,7 +7,6 @@ from typing import Final, Self, final
7
7
 
8
8
  from prompt_toolkit.completion import (
9
9
  Completer,
10
- DeduplicateCompleter,
11
10
  FuzzyCompleter,
12
11
  FuzzyWordCompleter,
13
12
  NestedCompleter,
@@ -860,14 +859,7 @@ class OptCompleter(Completer):
860
859
 
861
860
  yield from merge_completers(
862
861
  (
863
- DeduplicateCompleter(
864
- merge_completers(
865
- (
866
- _nested_dict_to_nc(new_nd),
867
- FuzzyCompleter(_nested_dict_to_nc(new_nd), WORD=True),
868
- )
869
- )
870
- ),
862
+ _nested_dict_to_nc(new_nd),
871
863
  FuzzyCompleter(
872
864
  WordCompleter(
873
865
  words=tuple(opt_tree_pos_list[-1]),
easyrip/easyrip_prompt.py CHANGED
@@ -1,8 +1,10 @@
1
1
  import os
2
+ import re
2
3
  from collections.abc import Iterable
3
4
 
4
5
  from prompt_toolkit.completion import CompleteEvent, Completer, Completion
5
6
  from prompt_toolkit.document import Document
7
+ from prompt_toolkit.formatted_text import StyleAndTextTuples
6
8
  from prompt_toolkit.history import FileHistory
7
9
 
8
10
  from .global_val import C_Z, get_CONFIG_DIR
@@ -26,47 +28,123 @@ class SmartPathCompleter(Completer):
26
28
  def __init__(self) -> None:
27
29
  pass
28
30
 
31
+ def _highlight_fuzzy_match(
32
+ self,
33
+ suggestion: str,
34
+ user_input: str,
35
+ style_config: dict | None = None,
36
+ ) -> StyleAndTextTuples:
37
+ """
38
+ 高亮显示模糊匹配结果
39
+
40
+ Args:
41
+ suggestion: 建议的完整字符串
42
+ user_input: 用户输入的匹配字符
43
+ style_config: 样式配置字典
44
+
45
+ Returns:
46
+ 包含样式信息的格式化文本
47
+
48
+ """
49
+ if style_config is None:
50
+ style_config = {
51
+ "match_char": "class:fuzzymatch.inside.character",
52
+ "match_section": "class:fuzzymatch.inside",
53
+ "non_match": "class:fuzzymatch.outside",
54
+ }
55
+
56
+ if not user_input:
57
+ # 用户没有输入,返回原始字符串
58
+ return [(style_config["non_match"], suggestion)]
59
+
60
+ # 找到最佳匹配位置
61
+ result = []
62
+
63
+ # 简化的模糊匹配算法
64
+ pattern = ".*?".join(map(re.escape, user_input))
65
+ regex = re.compile(pattern, re.IGNORECASE)
66
+
67
+ match = regex.search(suggestion)
68
+ if not match:
69
+ # 没有匹配,返回原始字符串
70
+ return [(style_config["non_match"], suggestion)]
71
+
72
+ start, end = match.span()
73
+ match_text = suggestion[start:end]
74
+
75
+ # 匹配段之前的文本
76
+ if start > 0:
77
+ result.append((style_config["non_match"], suggestion[:start]))
78
+
79
+ # 匹配段内部的字符
80
+ input_chars = list(user_input)
81
+ for char in match_text:
82
+ if input_chars and char.lower() == input_chars[0].lower():
83
+ result.append((style_config["match_char"], char))
84
+ input_chars.pop(0)
85
+ else:
86
+ result.append((style_config["match_section"], char))
87
+
88
+ # 匹配段之后的文本
89
+ if end < len(suggestion):
90
+ result.append((style_config["non_match"], suggestion[end:]))
91
+
92
+ return result
93
+
94
+ def _fuzzy_filter_and_sort(self, filenames: list[str], match_str: str) -> list[str]:
95
+ """模糊过滤和排序"""
96
+ if not match_str:
97
+ return sorted(filenames)
98
+
99
+ # 构建模糊匹配模式
100
+ pattern = ".*?".join(map(re.escape, match_str))
101
+ regex = re.compile(f"(?=({pattern}))", re.IGNORECASE)
102
+
103
+ matches = []
104
+ for filename in filenames:
105
+ regex_matches = list(regex.finditer(filename))
106
+ if regex_matches:
107
+ # 找到最佳匹配(最左、最短)
108
+ best = min(regex_matches, key=lambda m: (m.start(), len(m.group(1))))
109
+ matches.append((best.start(), len(best.group(1)), filename))
110
+
111
+ # 按匹配质量排序:先按匹配位置,再按匹配长度
112
+ matches.sort(key=lambda x: (x[0], x[1]))
113
+ return [item[2] for item in matches]
114
+
29
115
  def get_completions(
30
116
  self,
31
117
  document: Document,
32
118
  complete_event: CompleteEvent, # noqa: ARG002
33
119
  ) -> Iterable[Completion]:
34
- text = document.text_before_cursor.strip("\"'")
120
+ text = document.text_before_cursor
121
+ input_path = text.strip("\"'")
35
122
 
36
123
  try:
37
- directory = (
38
- os.path.dirname(os.path.join(".", text))
39
- if os.path.dirname(text)
40
- else "."
41
- )
42
-
43
- prefix = os.path.basename(text)
124
+ directory = os.path.dirname(input_path) or "."
125
+ basename = os.path.basename(input_path)
44
126
 
45
- filenames: list[tuple[str, str]] = (
46
- [
47
- (directory, filename)
48
- for filename in os.listdir(directory)
49
- if filename.startswith(prefix)
50
- ]
51
- if os.path.isdir(directory)
52
- else []
127
+ filenames: list[str] = (
128
+ os.listdir(directory) if os.path.isdir(directory) else []
53
129
  )
54
130
 
55
- for directory, filename in sorted(filenames, key=lambda k: k[1]):
56
- completion = filename[len(prefix) :]
57
- full_name = os.path.join(directory, filename)
131
+ for filename in self._fuzzy_filter_and_sort(filenames, basename):
132
+ full_name = (
133
+ filename if directory == "." else os.path.join(directory, filename)
134
+ )
58
135
 
59
136
  if os.path.isdir(full_name):
60
137
  filename += "/"
61
138
 
139
+ completion = full_name
140
+
141
+ if any(c in r""" !$%&()*:;<=>?[]^`{|}~""" for c in completion):
142
+ completion = f'"{completion}"'
143
+
62
144
  yield Completion(
63
- text=(
64
- f'{"" if any(c in text for c in "\\/") else '"'}{completion}"'
65
- if any(c in r"""!$%&()*:;<=>?[]^`{|}~""" for c in completion)
66
- else completion
67
- ),
68
- start_position=0,
69
- display=filename,
145
+ text=completion,
146
+ start_position=-len(text),
147
+ display=self._highlight_fuzzy_match(filename, basename),
70
148
  )
71
149
 
72
150
  except OSError:
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.11.2"
7
+ PROJECT_VERSION = "4.11.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/param.py CHANGED
@@ -213,6 +213,7 @@ _DEFAULT_X265_PARAMS: Final[dict[LiteralString, LiteralString]] = {
213
213
  "open-gop": "1",
214
214
  "gop-lookahead": "0",
215
215
  "rc-lookahead": "20",
216
+ "lookahead-slices": "8",
216
217
  "rect": "0",
217
218
  "amp": "0",
218
219
  "cbqpoffs": "0",
@@ -227,6 +228,11 @@ _DEFAULT_X265_PARAMS: Final[dict[LiteralString, LiteralString]] = {
227
228
  "sao": "0",
228
229
  "weightb": "1",
229
230
  "info": "1",
231
+ # 性能
232
+ "lookahead-threads": "0",
233
+ "asm": "auto",
234
+ "frame-threads": "0",
235
+ "pools": "*",
230
236
  }
231
237
 
232
238
 
@@ -391,6 +397,7 @@ DEFAULT_PRESET_PARAMS: Final[dict[Preset_name, dict[LiteralString, LiteralString
391
397
  "subme": "5",
392
398
  "gop-lookahead": "8",
393
399
  "rc-lookahead": "216",
400
+ "lookahead-slices": "4",
394
401
  "cbqpoffs": "-2",
395
402
  "crqpoffs": "-2",
396
403
  "pbratio": "1.2",
@@ -421,6 +428,7 @@ DEFAULT_PRESET_PARAMS: Final[dict[Preset_name, dict[LiteralString, LiteralString
421
428
  "subme": "6",
422
429
  "gop-lookahead": "14",
423
430
  "rc-lookahead": "250",
431
+ "lookahead-slices": "2",
424
432
  "rect": "1",
425
433
  "min-keyint": "2",
426
434
  "cbqpoffs": "-2",
@@ -456,6 +464,7 @@ DEFAULT_PRESET_PARAMS: Final[dict[Preset_name, dict[LiteralString, LiteralString
456
464
  "open-gop": "1",
457
465
  "gop-lookahead": "14",
458
466
  "rc-lookahead": "250",
467
+ "lookahead-slices": "1",
459
468
  "rect": "1",
460
469
  "amp": "1",
461
470
  "cbqpoffs": "-3",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easyrip
3
- Version: 4.11.2
3
+ Version: 4.11.3
4
4
  Author: op200
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Project-URL: Homepage, https://github.com/op200/EasyRip
@@ -1,10 +1,10 @@
1
1
  easyrip/__init__.py,sha256=PIvSPDgswsIkWL4dsCe87knnxKmtvcrWYzmqAwZix_M,765
2
2
  easyrip/__main__.py,sha256=bPVlHqJb3BG-qZOY4JlJdKj7MKeRb_3EfoMbfoNN-gU,4943
3
- easyrip/easyrip_command.py,sha256=IvoNf9Q79nGTvFeCGfq0oz0Cr4jZY7beJAgbWUlUyuk,29633
3
+ easyrip/easyrip_command.py,sha256=U5fe33YqRghpuOY3xmjTxErZzjG_ykJMTWyU-GAJlsY,29315
4
4
  easyrip/easyrip_log.py,sha256=R-dM3CWUBFITtG7GSD1zy4X4MhZqxkoiBPjlIpI76cY,15573
5
5
  easyrip/easyrip_main.py,sha256=l_LMkM0EDpY0N9Ib5O1wSho6k9JG7JIh62ajU622XHE,44710
6
- easyrip/easyrip_prompt.py,sha256=RJoE4H_ft4jmlMIBxDcEAfLvLQqabYKUuQUBqJAztlY,2155
7
- easyrip/global_val.py,sha256=yrFt8nVY7zTtyBeeatvKZsXtYhAGBUoWXHjiao32og4,866
6
+ easyrip/easyrip_prompt.py,sha256=3or0Vt4s6L53MCJtQmSylrTADZIIjX5gvpSb-JRe2P4,4844
7
+ easyrip/global_val.py,sha256=YU7OKCDpWi3ez6Z28xy4txjWKEWSTFyPiO-doO5Jh48,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
@@ -17,15 +17,15 @@ easyrip/easyrip_web/__init__.py,sha256=tMyEeaSGeEJjND7MF0MBv9aDiDgaO3MOnppwxA70U
17
17
  easyrip/easyrip_web/http_server.py,sha256=iyulCAFQrJlz86Lrr-Dm3fhOnNCf79Bp6fVHhr0ephY,8350
18
18
  easyrip/easyrip_web/third_party_api.py,sha256=GhP6LmR1sVMeLLbnj82r-QYjoUdSnyaU9xp0LRnRLsw,4623
19
19
  easyrip/ripper/media_info.py,sha256=mQq_vbQ7S9fWpb39HLkoZlAL-pqNfwxewv6X776Nf50,5078
20
- easyrip/ripper/param.py,sha256=wPy2zMmL0zNk8ShhCA8KmpLs7h4sRpNuEsllF6sGBlI,11734
20
+ easyrip/ripper/param.py,sha256=PYUCfrXtGKaUxQCLWiDK99jih_JSRxtLsi5udzsZZUQ,11980
21
21
  easyrip/ripper/ripper.py,sha256=jjQKDepCAPDCnNEk3RQ-r7py-cFDBT5g7pdZMIR60WY,50515
22
22
  easyrip/ripper/sub_and_font/__init__.py,sha256=cBT7mxL7RRFaJXFPXuZ7RT-YK6FbnanaU5v6U9BOquw,153
23
23
  easyrip/ripper/sub_and_font/ass.py,sha256=hJhVN7CqehN9xW1W295gmkPnG-somqlxnwXzRidbA2M,28645
24
24
  easyrip/ripper/sub_and_font/font.py,sha256=X2dPcPzbwQf3fv_g_mxO-zY7puVAX9Nv-9QHn88q4oA,7745
25
25
  easyrip/ripper/sub_and_font/subset.py,sha256=qGH3H26nHnyGFfFwvktEIKncHpm086DqxYjVhNoVDdM,18654
26
- easyrip-4.11.2.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
27
- easyrip-4.11.2.dist-info/METADATA,sha256=_KvAAeJEnpiExsKXMhKGTvvvxz4qTinkjtQbzWv8sCI,3507
28
- easyrip-4.11.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
- easyrip-4.11.2.dist-info/entry_points.txt,sha256=D6GBMMTzZ-apgX76KyZ6jxMmIFqGYwU9neeLLni_qKI,49
30
- easyrip-4.11.2.dist-info/top_level.txt,sha256=kuEteBXm-Gf90jRQgH3-fTo-Z-Q6czSuUEqY158H4Ww,8
31
- easyrip-4.11.2.dist-info/RECORD,,
26
+ easyrip-4.11.3.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
27
+ easyrip-4.11.3.dist-info/METADATA,sha256=7jXHv8WOOGUm90wHDqG650tJdjSfej0NMsAIqJACimo,3507
28
+ easyrip-4.11.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
29
+ easyrip-4.11.3.dist-info/entry_points.txt,sha256=D6GBMMTzZ-apgX76KyZ6jxMmIFqGYwU9neeLLni_qKI,49
30
+ easyrip-4.11.3.dist-info/top_level.txt,sha256=kuEteBXm-Gf90jRQgH3-fTo-Z-Q6czSuUEqY158H4Ww,8
31
+ easyrip-4.11.3.dist-info/RECORD,,