easyrip 4.12.0__tar.gz → 4.13.0__tar.gz

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.
Files changed (37) hide show
  1. {easyrip-4.12.0 → easyrip-4.13.0}/PKG-INFO +1 -1
  2. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/__main__.py +3 -1
  3. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_command.py +10 -2
  4. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_main.py +51 -3
  5. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_mlang/lang_zh_Hans_CN.py +5 -3
  6. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/global_val.py +1 -1
  7. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/ripper/ripper.py +8 -7
  8. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/ripper/sub_and_font/ass.py +160 -0
  9. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/ripper/sub_and_font/subset.py +1 -1
  10. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip.egg-info/PKG-INFO +1 -1
  11. {easyrip-4.12.0 → easyrip-4.13.0}/LICENSE +0 -0
  12. {easyrip-4.12.0 → easyrip-4.13.0}/README.md +0 -0
  13. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/__init__.py +0 -0
  14. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_config/config.py +0 -0
  15. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_config/config_key.py +0 -0
  16. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_log.py +0 -0
  17. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_mlang/__init__.py +0 -0
  18. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_mlang/global_lang_val.py +0 -0
  19. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_mlang/lang_en.py +0 -0
  20. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_mlang/lang_tag_val.py +0 -0
  21. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_mlang/translator.py +0 -0
  22. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_prompt.py +0 -0
  23. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_web/__init__.py +0 -0
  24. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_web/http_server.py +0 -0
  25. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/easyrip_web/third_party_api.py +0 -0
  26. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/ripper/media_info.py +0 -0
  27. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/ripper/param.py +0 -0
  28. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/ripper/sub_and_font/__init__.py +0 -0
  29. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/ripper/sub_and_font/font.py +0 -0
  30. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip/utils.py +0 -0
  31. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip.egg-info/SOURCES.txt +0 -0
  32. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip.egg-info/dependency_links.txt +0 -0
  33. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip.egg-info/entry_points.txt +0 -0
  34. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip.egg-info/requires.txt +0 -0
  35. {easyrip-4.12.0 → easyrip-4.13.0}/easyrip.egg-info/top_level.txt +0 -0
  36. {easyrip-4.12.0 → easyrip-4.13.0}/pyproject.toml +0 -0
  37. {easyrip-4.12.0 → easyrip-4.13.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easyrip
3
- Version: 4.12.0
3
+ Version: 4.13.0
4
4
  Author: op200
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Project-URL: Homepage, https://github.com/op200/EasyRip
@@ -74,6 +74,7 @@ def run() -> NoReturn:
74
74
  in {
75
75
  *Cmd_type.cd.value.names,
76
76
  *Cmd_type.mediainfo.value.names,
77
+ *Cmd_type.assinfo.value.names,
77
78
  *Cmd_type.fontinfo.value.names,
78
79
  }
79
80
  else ()
@@ -98,6 +99,7 @@ def run() -> NoReturn:
98
99
  *Opt_type._i.value.names,
99
100
  *Opt_type._o_dir.value.names,
100
101
  *Opt_type._o.value.names,
102
+ *Opt_type._pipe.value.names,
101
103
  *Opt_type._sub.value.names,
102
104
  *Opt_type._only_mux_sub_path.value.names,
103
105
  *Opt_type._soft_sub.value.names,
@@ -141,7 +143,7 @@ def run() -> NoReturn:
141
143
 
142
144
  try:
143
145
  if not run_command(command):
144
- log.warning("Stop run command")
146
+ log.warning("Command run terminated")
145
147
  except KeyboardInterrupt:
146
148
  log.warning("Manually stop run command")
147
149
 
@@ -268,7 +268,6 @@ class Cmd_type(enum.Enum):
268
268
  "Translate subtitle files\n"
269
269
  "e.g. 'translate zh-Hans zh-Hant' will translate all '*.zh-Hans.ass' files into zh-Hant"
270
270
  ),
271
- childs=(Cmd_type_val(("-overwrite",)),),
272
271
  )
273
272
  mediainfo = Cmd_type_val(
274
273
  ("mediainfo",),
@@ -279,6 +278,15 @@ class Cmd_type(enum.Enum):
279
278
  Cmd_type_val(("cfd",)),
280
279
  ),
281
280
  )
281
+ assinfo = Cmd_type_val(
282
+ ("assinfo",),
283
+ param="<<path> | 'fd' | 'cfd'> [-use-libass-spec <0|1>] [-show-chars-len <0|1>]",
284
+ description="Get the ass info by the Ass class",
285
+ childs=(
286
+ Cmd_type_val(("fd",)),
287
+ Cmd_type_val(("cfd",)),
288
+ ),
289
+ )
282
290
  fontinfo = Cmd_type_val(
283
291
  ("fontinfo",),
284
292
  param="<<path> | 'fd' | 'cfd'>",
@@ -443,7 +451,7 @@ class Opt_type(enum.Enum):
443
451
  'e.g. "11\\{22}33" ->\n'
444
452
  ' "11\\33" (VSFilter)\n'
445
453
  ' "11{22}33" (libass)\n'
446
- "Default: 0"
454
+ "Default: 1"
447
455
  ),
448
456
  childs=(Cmd_type_val(("0", "1")),),
449
457
  )
@@ -37,7 +37,7 @@ from .easyrip_mlang import (
37
37
  from .easyrip_prompt import easyrip_prompt
38
38
  from .ripper.media_info import Media_info
39
39
  from .ripper.ripper import Ripper
40
- from .ripper.sub_and_font import load_fonts
40
+ from .ripper.sub_and_font import Ass, load_fonts
41
41
  from .utils import change_title, check_ver, read_text
42
42
 
43
43
  __all__ = ["init", "run_command"]
@@ -305,6 +305,10 @@ def run_ripper_list(
305
305
  except Exception as e:
306
306
  log.error(e, deep=True)
307
307
  log.warning("Stop run Ripper")
308
+ except KeyboardInterrupt:
309
+ log.warning("Manually stop run and clear Ripper list")
310
+ Ripper.ripper_list.clear()
311
+ raise
308
312
 
309
313
  with ThreadPoolExecutor() as executor:
310
314
  for ripper in Ripper.ripper_list:
@@ -322,6 +326,10 @@ def run_ripper_list(
322
326
  except Exception as e:
323
327
  log.error(e, deep=True)
324
328
  log.warning("Stop run Ripper")
329
+ except KeyboardInterrupt:
330
+ log.warning("Manually stop run and clear Ripper list")
331
+ Ripper.ripper_list.clear()
332
+ raise
325
333
  sleep(0.5)
326
334
 
327
335
  if log.warning_num > warning_num:
@@ -494,8 +502,8 @@ def run_command(command: Iterable[str] | str) -> bool:
494
502
  case Cmd_type.exit:
495
503
  sys.exit(0)
496
504
 
497
- case Cmd_type.cd | Cmd_type.mediainfo | Cmd_type.fontinfo:
498
- _path_tuple: tuple[str, ...] | None = None
505
+ case Cmd_type.cd | Cmd_type.mediainfo | Cmd_type.assinfo | Cmd_type.fontinfo:
506
+ _path_tuple: Iterable[str] | None = None
499
507
 
500
508
  match cmd_list[1]:
501
509
  case "fd" | "cfd" as fd_param:
@@ -527,6 +535,46 @@ def run_command(command: Iterable[str] | str) -> bool:
527
535
  case Cmd_type.mediainfo:
528
536
  for _path in _path_tuple:
529
537
  log.send(f"{_path}: {Media_info.from_path(_path)}")
538
+ case Cmd_type.assinfo:
539
+ USE_LIBASS_SPEC_OPT_NAME = "-use-libass-spec"
540
+ use_libass_spec: bool = True
541
+ SHOW_CHARS_LEN_OPT_NAME = "-show-chars-len"
542
+ show_chars_len: bool = False
543
+ is_use_opt: bool = False
544
+ for i, s in tuple(enumerate(cmd_list))[2:]:
545
+ if s == USE_LIBASS_SPEC_OPT_NAME:
546
+ use_libass_spec = cmd_list[i + 1] != "0"
547
+ is_use_opt = True
548
+ if s == SHOW_CHARS_LEN_OPT_NAME:
549
+ show_chars_len = cmd_list[i + 1] != "0"
550
+ is_use_opt = True
551
+ if is_use_opt:
552
+ _path_tuple = cmd_list[1:2]
553
+ log.send(
554
+ " ".join(
555
+ (
556
+ f"{USE_LIBASS_SPEC_OPT_NAME} {1 if use_libass_spec else 0}",
557
+ f"{SHOW_CHARS_LEN_OPT_NAME} {1 if show_chars_len else 0}",
558
+ )
559
+ )
560
+ )
561
+ for _path in _path_tuple:
562
+ try:
563
+ ass = Ass(_path)
564
+ except Exception as e:
565
+ log.error(e)
566
+ return False
567
+
568
+ log.send(_path)
569
+ for font_sign, ss in ass.get_font_info(
570
+ use_libass_spec=use_libass_spec,
571
+ ).items():
572
+ show_chars_len_log_str = ""
573
+ if show_chars_len:
574
+ show_chars_len_log_str = f": {len(ss)}"
575
+ log.send(
576
+ f" ( {font_sign[0]} / {font_sign[1].name} ){show_chars_len_log_str}"
577
+ )
530
578
  case Cmd_type.fontinfo:
531
579
  for _font in itertools.chain.from_iterable(
532
580
  load_fonts(_path) for _path in _path_tuple
@@ -174,7 +174,7 @@ LANG_MAP: dict[str, str] = {
174
174
  'e.g. "11\\{22}33" ->\n'
175
175
  ' "11\\33" (VSFilter)\n'
176
176
  ' "11{22}33" (libass)\n'
177
- "默认: 0"
177
+ "默认: 1"
178
178
  ),
179
179
  Opt_type._subset_drop_non_render.value.description: (
180
180
  "丢弃 ASS 中的注释行、Name、Effect等非渲染内容\n" # .
@@ -277,7 +277,8 @@ LANG_MAP: dict[str, str] = {
277
277
  "Check env...": "检测环境中...",
278
278
  # "The MediaInfo must be CLI ver": "MediaInfo 必须是 CLI 版本",
279
279
  "Easy Rip command": "Easy Rip 命令",
280
- "Stop run Ripper": "Ripper 执行终止",
280
+ "Stop run and clear Ripper list": "终止执行并清空 Ripper list",
281
+ "Manually stop run and clear Ripper list": "手动终止执行并清空 Ripper list",
281
282
  "There are {} {} during run": "执行期间有 {} 个 {}",
282
283
  "Execute shutdown in {}s": "{}s 后执行关机",
283
284
  "{} run completed, shutdown in {}s": "{} 执行完成, {}s 后关机",
@@ -300,10 +301,11 @@ LANG_MAP: dict[str, str] = {
300
301
  "There is no audio stream in the video, so '-c:a' cannot be used": "视频中没有音频流,所以无法使用 '-c:a'",
301
302
  "Unsupported '{}' param: {}": "'{}' 不支持此参数: {}",
302
303
  "Manually force exit": "手动强制退出",
304
+ "Command run terminated": "命令执行终止",
305
+ "Manually stop run command": "手动终止执行命令",
303
306
  "Suggest running the following command to upgrade using pip: {}": "建议运行以下命令以使用 pip 更新: {}",
304
307
  "Wrong sec in -shutdown, change to default 60s": "-shutdown 设定的秒数错误, 改为默认值 60s",
305
308
  "Current work directory has an other Easy Rip is running: {}": "当前工作目录存在其他 Easy Rip 正在运行: {}",
306
- "Stop run command": "命令执行终止",
307
309
  # log
308
310
  "EasyRip_log.html": "EasyRip日志.html",
309
311
  "Start": "开始",
@@ -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.12.0"
7
+ PROJECT_VERSION = "4.13.0"
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"
@@ -901,18 +901,18 @@ class Ripper:
901
901
 
902
902
  _font_in_sub = self.option_map.get("subset-font-in-sub", "0") == "1"
903
903
  _use_win_font = (
904
- self.option_map.get("subset-use-win-font", "0") == "1"
904
+ self.option_map.get("subset-use-win-font", "0") != "0"
905
905
  )
906
906
  _use_libass_spec = (
907
- self.option_map.get("subset-use-libass-spec", "0") == "1"
907
+ self.option_map.get("subset-use-libass-spec", "1") != "0"
908
908
  )
909
909
  _drop_non_render = (
910
- self.option_map.get("subset-drop-non-render", "1") == "1"
910
+ self.option_map.get("subset-drop-non-render", "1") != "0"
911
911
  )
912
912
  _drop_unkow_data = (
913
- self.option_map.get("subset-drop-unkow-data", "1") == "1"
913
+ self.option_map.get("subset-drop-unkow-data", "1") != "0"
914
914
  )
915
- _strict = self.option_map.get("subset-strict", "0") == "1"
915
+ _strict = self.option_map.get("subset-strict", "0") != "0"
916
916
 
917
917
  subset_res = subset(
918
918
  _ass_list,
@@ -1057,11 +1057,12 @@ class Ripper:
1057
1057
  )
1058
1058
  is_cmd_run_failed: bool = False
1059
1059
  for i, cmd in enumerate(cmd_list, 1):
1060
- log.info(
1060
+ log.info("Run the command {}", i)
1061
+ log.debug(
1061
1062
  "Run the command {}",
1062
1063
  f"{i}:\n {cmd}",
1063
1064
  )
1064
- if _cmd_res := os.system(cmd) != 0:
1065
+ if (_cmd_res := os.system(cmd)) != 0:
1065
1066
  is_cmd_run_failed = True
1066
1067
  log.error(
1067
1068
  "Command run failed: status code {}\n Failed command: {}",
@@ -862,3 +862,163 @@ class Ass:
862
862
  ),
863
863
  )
864
864
  return "\n\n".join(filter(bool, generator)) + "\n"
865
+
866
+ def get_font_info(
867
+ self,
868
+ *,
869
+ use_libass_spec: bool = True,
870
+ ):
871
+ """获取 ASS 中需要的字体,以及对应的字符"""
872
+ from .font import Font_type
873
+ from .subset import _bold_italic_to_font_type
874
+
875
+ DEFAULT_STYLE_NAME = "Default"
876
+
877
+ font_sign__str: dict[tuple[str, Font_type], str] = {}
878
+
879
+ # Styles
880
+ style__font_sign: dict[str, tuple[str, Font_type]] = {}
881
+ for style in self.styles.data:
882
+ _is_vertical: bool = style.Fontname[0] == "@"
883
+ _font_name: str = style.Fontname[1:] if _is_vertical else style.Fontname
884
+ # 获取
885
+ style__font_sign[style.Name] = (
886
+ _font_name,
887
+ _bold_italic_to_font_type(style.Bold, style.Italic),
888
+ )
889
+
890
+ # Events
891
+ for event in self.events.data:
892
+ if event.type != Event_type.Dialogue:
893
+ continue
894
+
895
+ default_font_sign: tuple[str, Font_type]
896
+
897
+ # 获取每行的默认字体
898
+ if event.Style not in style__font_sign:
899
+ if DEFAULT_STYLE_NAME in style__font_sign:
900
+ log.warning(
901
+ "The style '{}' not in Styles. Defaulting to the style '{}'",
902
+ event.Style,
903
+ DEFAULT_STYLE_NAME,
904
+ )
905
+ default_font_sign = style__font_sign[DEFAULT_STYLE_NAME]
906
+ else:
907
+ log.warning(
908
+ "The style '{}' and the style 'Default' not in Styles. Defaulting to no font",
909
+ event.Style,
910
+ )
911
+ default_font_sign = ("", Font_type.Regular)
912
+ else:
913
+ default_font_sign = style__font_sign[event.Style]
914
+
915
+ # 解析 Text
916
+ current_font_sign: tuple[str, Font_type] = default_font_sign
917
+ for is_tag, text in Event_data.parse_text(event.Text, use_libass_spec):
918
+ if is_tag:
919
+ tag_fn: str | None = None
920
+ tag_bold: str | None = None
921
+ tag_italic: str | None = None
922
+
923
+ for tag, value in re.findall(
924
+ r"\\\s*(fn|b(?![a-zA-Z])|i(?![a-zA-Z])|r)([^\\}]*)", text
925
+ ):
926
+ assert isinstance(tag, str) and isinstance(value, str)
927
+
928
+ proc_value = value.strip()
929
+ if proc_value.startswith("("):
930
+ proc_value = proc_value[1:]
931
+ if (_index := proc_value.find(")")) != -1:
932
+ proc_value = proc_value[:_index]
933
+ proc_value = proc_value.strip()
934
+
935
+ match tag:
936
+ case "fn":
937
+ tag_fn = proc_value
938
+ case "b":
939
+ tag_bold = proc_value
940
+ case "i":
941
+ tag_italic = proc_value
942
+ case "r":
943
+ r_value = proc_value if "(" in value else value.rstrip()
944
+ if r_value in style__font_sign:
945
+ current_font_sign = style__font_sign[r_value]
946
+ else:
947
+ # 空为还原样式, 非样式表内样式名效果同空, 但发出不规范警告
948
+ current_font_sign = default_font_sign
949
+ if r_value != "":
950
+ log.warning(
951
+ "The \\r style '{}' not in Styles", r_value
952
+ )
953
+
954
+ new_fontname: str = current_font_sign[0]
955
+ new_bold: bool
956
+ new_italic: bool
957
+ new_bold, new_italic = current_font_sign[1].value
958
+
959
+ if tag_fn is not None:
960
+ match tag_fn:
961
+ case "":
962
+ new_fontname = default_font_sign[0]
963
+ case _:
964
+ _is_vertical: bool = tag_fn.startswith("@")
965
+ new_fontname = tag_fn[1:] if _is_vertical else tag_fn
966
+
967
+ if tag_bold is not None:
968
+ match tag_bold:
969
+ case "":
970
+ new_bold = default_font_sign[1].value[0]
971
+ case "0":
972
+ new_bold = False
973
+ case "1":
974
+ new_bold = True
975
+ case _:
976
+ log.error(
977
+ "Illegal format: '{}' in line: {}",
978
+ f"\\b{tag_bold}",
979
+ event.Text,
980
+ )
981
+
982
+ if tag_italic is not None:
983
+ match tag_italic:
984
+ case "":
985
+ new_italic = default_font_sign[1].value[1]
986
+ case "0":
987
+ new_italic = False
988
+ case "1":
989
+ new_italic = True
990
+ case _:
991
+ log.error(
992
+ "Illegal format: '{}' in line: {}",
993
+ f"\\i{tag_italic}",
994
+ event.Text,
995
+ )
996
+
997
+ current_font_sign = (
998
+ new_fontname,
999
+ Font_type((new_bold, new_italic)),
1000
+ )
1001
+
1002
+ elif current_font_sign[0]: # 空字符串为不使用字体
1003
+ add_text = re.sub(r"\\[nN]", "", text).replace("\\h", "\u00a0")
1004
+
1005
+ if current_font_sign not in font_sign__str:
1006
+ font_sign__str[current_font_sign] = ""
1007
+
1008
+ font_sign__str[current_font_sign] += add_text
1009
+
1010
+ return {font_sign: set(s) for font_sign, s in font_sign__str.items()}
1011
+
1012
+ @classmethod
1013
+ def analysis_font_info(
1014
+ cls,
1015
+ ass: Self | Path | str,
1016
+ /,
1017
+ *,
1018
+ use_libass_spec: bool = True,
1019
+ ):
1020
+ if isinstance(ass, str):
1021
+ ass = Path(ass)
1022
+ if isinstance(ass, Path):
1023
+ ass = cls(ass)
1024
+ return ass.get_font_info(use_libass_spec=use_libass_spec)
@@ -33,7 +33,7 @@ def subset(
33
33
  *,
34
34
  font_in_sub: bool = False,
35
35
  use_win_font: bool = False,
36
- use_libass_spec: bool = False,
36
+ use_libass_spec: bool = True,
37
37
  drop_non_render: bool = True,
38
38
  drop_unkow_data: bool = True,
39
39
  strict: bool = False,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easyrip
3
- Version: 4.12.0
3
+ Version: 4.13.0
4
4
  Author: op200
5
5
  License-Expression: AGPL-3.0-or-later
6
6
  Project-URL: Homepage, https://github.com/op200/EasyRip
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes