easyrip 4.9.1__tar.gz → 4.11.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.
- {easyrip-4.9.1 → easyrip-4.11.0}/PKG-INFO +1 -1
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_log.py +0 -4
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/global_val.py +1 -1
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/ripper/ripper.py +2 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/ripper/sub_and_font/ass.py +217 -125
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip.egg-info/PKG-INFO +1 -1
- {easyrip-4.9.1 → easyrip-4.11.0}/LICENSE +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/README.md +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/__init__.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/__main__.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_command.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_config/config.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_config/config_key.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_main.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_mlang/__init__.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_mlang/global_lang_val.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_mlang/lang_en.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_mlang/lang_zh_Hans_CN.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_mlang/translator.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_prompt.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_web/__init__.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_web/http_server.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/easyrip_web/third_party_api.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/ripper/media_info.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/ripper/param.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/ripper/sub_and_font/__init__.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/ripper/sub_and_font/font.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/ripper/sub_and_font/subset.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip/utils.py +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip.egg-info/SOURCES.txt +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip.egg-info/dependency_links.txt +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip.egg-info/entry_points.txt +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip.egg-info/requires.txt +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/easyrip.egg-info/top_level.txt +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/pyproject.toml +0 -0
- {easyrip-4.9.1 → easyrip-4.11.0}/setup.cfg +0 -0
|
@@ -3,7 +3,7 @@ import sys
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
PROJECT_NAME = "Easy Rip"
|
|
6
|
-
PROJECT_VERSION = "4.
|
|
6
|
+
PROJECT_VERSION = "4.11.0"
|
|
7
7
|
PROJECT_TITLE = f"{PROJECT_NAME} v{PROJECT_VERSION}"
|
|
8
8
|
PROJECT_URL = "https://github.com/op200/EasyRip"
|
|
9
9
|
PROJECT_RELEASE_API = "https://api.github.com/repos/op200/EasyRip/releases/latest"
|
|
@@ -1081,6 +1081,8 @@ class Ripper:
|
|
|
1081
1081
|
soft_sub_list = [Path(s) for s in soft_sub.split("?")]
|
|
1082
1082
|
|
|
1083
1083
|
subset_folder = Path(self.output_dir) / f"subset_temp_{temp_name}"
|
|
1084
|
+
if not soft_sub_list:
|
|
1085
|
+
log.warning("-soft-sub is empty")
|
|
1084
1086
|
log.info("-soft-sub list = {}", soft_sub_list)
|
|
1085
1087
|
|
|
1086
1088
|
# 临时翻译
|
|
@@ -2,11 +2,13 @@ import enum
|
|
|
2
2
|
import itertools
|
|
3
3
|
import re
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
|
+
from functools import total_ordering
|
|
5
6
|
from pathlib import Path
|
|
7
|
+
from typing import Self
|
|
6
8
|
|
|
7
9
|
from ...easyrip_log import log
|
|
8
10
|
from ...easyrip_mlang import Mlang_exception
|
|
9
|
-
from ...utils import read_text, uudecode_ssa, uuencode_ssa
|
|
11
|
+
from ...utils import read_text, time_str_to_sec, uudecode_ssa, uuencode_ssa
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class Style_fmt_it(enum.Enum):
|
|
@@ -63,7 +65,7 @@ class Script_info:
|
|
|
63
65
|
class Style_data:
|
|
64
66
|
Name: str
|
|
65
67
|
Fontname: str
|
|
66
|
-
Fontsize:
|
|
68
|
+
Fontsize: float
|
|
67
69
|
PrimaryColour: str
|
|
68
70
|
SecondaryColour: str
|
|
69
71
|
OutlineColour: str
|
|
@@ -212,7 +214,7 @@ class Styles:
|
|
|
212
214
|
res = Style_data(
|
|
213
215
|
Name=style_tuple[self.fmt_index[Style_fmt_it.Name]],
|
|
214
216
|
Fontname=style_tuple[self.fmt_index[Style_fmt_it.Fontname]],
|
|
215
|
-
Fontsize=
|
|
217
|
+
Fontsize=float(style_tuple[self.fmt_index[Style_fmt_it.Fontsize]]),
|
|
216
218
|
PrimaryColour=style_tuple[self.fmt_index[Style_fmt_it.PrimaryColour]],
|
|
217
219
|
SecondaryColour=style_tuple[
|
|
218
220
|
self.fmt_index[Style_fmt_it.SecondaryColour]
|
|
@@ -264,6 +266,75 @@ class Styles:
|
|
|
264
266
|
)
|
|
265
267
|
|
|
266
268
|
|
|
269
|
+
@total_ordering
|
|
270
|
+
class Ass_time:
|
|
271
|
+
def __init__(
|
|
272
|
+
self,
|
|
273
|
+
h: int = 0,
|
|
274
|
+
m: int = 0,
|
|
275
|
+
s: int = 0,
|
|
276
|
+
ms: int = 0,
|
|
277
|
+
) -> None:
|
|
278
|
+
self.h = h
|
|
279
|
+
self.m = m
|
|
280
|
+
self.s = s
|
|
281
|
+
self.ms = ms
|
|
282
|
+
|
|
283
|
+
def __str__(self) -> str:
|
|
284
|
+
return f"{self.h:02d}:{self.m:02d}:{self.s:02d}.{self.ms // 10:02d}"
|
|
285
|
+
|
|
286
|
+
def __hash__(self) -> int:
|
|
287
|
+
return hash((self.h, self.m, self.s, self.ms))
|
|
288
|
+
|
|
289
|
+
def __eq__(self, value: object) -> bool:
|
|
290
|
+
if isinstance(value, Ass_time):
|
|
291
|
+
return (self.h, self.m, self.s, self.ms) == (
|
|
292
|
+
value.h,
|
|
293
|
+
value.m,
|
|
294
|
+
value.s,
|
|
295
|
+
value.ms,
|
|
296
|
+
)
|
|
297
|
+
return NotImplemented
|
|
298
|
+
|
|
299
|
+
def __lt__(self, value: object) -> bool:
|
|
300
|
+
if isinstance(value, Ass_time):
|
|
301
|
+
return (self.h, self.m, self.s, self.ms) < (
|
|
302
|
+
value.h,
|
|
303
|
+
value.m,
|
|
304
|
+
value.s,
|
|
305
|
+
value.ms,
|
|
306
|
+
)
|
|
307
|
+
return NotImplemented
|
|
308
|
+
|
|
309
|
+
def __add__(self, other: object) -> Self:
|
|
310
|
+
if isinstance(other, Ass_time):
|
|
311
|
+
return self.__class__.from_ms(self.total_ms() + other.total_ms())
|
|
312
|
+
return NotImplemented
|
|
313
|
+
|
|
314
|
+
def __sub__(self, other: object) -> Self:
|
|
315
|
+
if isinstance(other, Ass_time):
|
|
316
|
+
return self.__class__.from_ms(self.total_ms() - other.total_ms())
|
|
317
|
+
return NotImplemented
|
|
318
|
+
|
|
319
|
+
@classmethod
|
|
320
|
+
def from_ms(cls, ms: int) -> Self:
|
|
321
|
+
return cls(
|
|
322
|
+
ms // 3_600_000,
|
|
323
|
+
(ms % 3_600_000) // 60_000,
|
|
324
|
+
(ms % 60_000) // 1000,
|
|
325
|
+
ms % 1000,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
@classmethod
|
|
329
|
+
def from_str(cls, ass_time_str: str) -> Self:
|
|
330
|
+
return cls.from_ms(
|
|
331
|
+
round(time_str_to_sec(ass_time_str) * 1000),
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
def total_ms(self) -> int:
|
|
335
|
+
return self.h * 3_600_000 + self.m * 60_000 + self.s * 1000 + self.ms
|
|
336
|
+
|
|
337
|
+
|
|
267
338
|
class Event_fmt_it(enum.Enum):
|
|
268
339
|
Layer = "Layer"
|
|
269
340
|
Start = "Start"
|
|
@@ -291,8 +362,8 @@ class Event_data:
|
|
|
291
362
|
type: Event_type
|
|
292
363
|
|
|
293
364
|
Layer: int
|
|
294
|
-
Start:
|
|
295
|
-
End:
|
|
365
|
+
Start: Ass_time
|
|
366
|
+
End: Ass_time
|
|
296
367
|
Style: str
|
|
297
368
|
Name: str
|
|
298
369
|
MarginL: int
|
|
@@ -451,8 +522,12 @@ class Events:
|
|
|
451
522
|
res = Event_data(
|
|
452
523
|
type=event_type,
|
|
453
524
|
Layer=int(event_tuple[self.fmt_index[Event_fmt_it.Layer]]),
|
|
454
|
-
Start=
|
|
455
|
-
|
|
525
|
+
Start=Ass_time.from_str(
|
|
526
|
+
event_tuple[self.fmt_index[Event_fmt_it.Start]],
|
|
527
|
+
),
|
|
528
|
+
End=Ass_time.from_str(
|
|
529
|
+
event_tuple[self.fmt_index[Event_fmt_it.End]],
|
|
530
|
+
),
|
|
456
531
|
Style=event_tuple[self.fmt_index[Event_fmt_it.Style]],
|
|
457
532
|
Name=event_tuple[self.fmt_index[Event_fmt_it.Name]],
|
|
458
533
|
MarginL=int(event_tuple[self.fmt_index[Event_fmt_it.MarginL]]),
|
|
@@ -496,7 +571,11 @@ class Events:
|
|
|
496
571
|
)
|
|
497
572
|
for event in self.data
|
|
498
573
|
if (drop_non_render is False)
|
|
499
|
-
or (
|
|
574
|
+
or (
|
|
575
|
+
event.type != Event_type.Comment
|
|
576
|
+
and event.Text
|
|
577
|
+
and event.Start < event.End
|
|
578
|
+
)
|
|
500
579
|
),
|
|
501
580
|
)
|
|
502
581
|
)
|
|
@@ -613,143 +692,156 @@ class Ass:
|
|
|
613
692
|
new_unknown_data: Unknown_data | None = None
|
|
614
693
|
|
|
615
694
|
for line in filter(bool, map(str.strip, read_text(path).splitlines())):
|
|
616
|
-
|
|
617
|
-
if
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
695
|
+
try:
|
|
696
|
+
if line.startswith("[") and line.endswith("]"):
|
|
697
|
+
if new_unknown_data is not None:
|
|
698
|
+
self.unknown_data.append(new_unknown_data)
|
|
699
|
+
new_unknown_data = None
|
|
700
|
+
|
|
701
|
+
match head := line[1:-1]:
|
|
702
|
+
case "Script Info":
|
|
703
|
+
state = State.script_info
|
|
704
|
+
case "V4+ Styles":
|
|
705
|
+
state = State.styles
|
|
706
|
+
case "Fonts":
|
|
707
|
+
state = State.fonts
|
|
708
|
+
case "Graphics":
|
|
709
|
+
state = State.graphics
|
|
710
|
+
case "Events":
|
|
711
|
+
state = State.events
|
|
712
|
+
case _:
|
|
713
|
+
if bool(re.search(r"[a-z]", head)):
|
|
714
|
+
state = State.unknown
|
|
715
|
+
new_unknown_data = Unknown_data(head=head)
|
|
716
|
+
|
|
717
|
+
elif line.startswith("Format:"):
|
|
718
|
+
formats_tuple = tuple(map(str.strip, line[7:].split(",")))
|
|
719
|
+
match state:
|
|
720
|
+
case State.styles:
|
|
721
|
+
format_order = tuple(map(Style_fmt_it, formats_tuple))
|
|
722
|
+
if len(format_order) != 23:
|
|
723
|
+
raise Ass_generate_error("Style Format len != 23")
|
|
724
|
+
|
|
725
|
+
self.styles.fmt_order = format_order
|
|
726
|
+
|
|
727
|
+
case State.events:
|
|
728
|
+
try:
|
|
729
|
+
format_order = tuple(
|
|
730
|
+
map(Event_fmt_it.__getitem__, formats_tuple)
|
|
731
|
+
)
|
|
732
|
+
except ValueError as e:
|
|
733
|
+
raise Ass_generate_error from e
|
|
654
734
|
|
|
655
|
-
|
|
656
|
-
|
|
735
|
+
if len(format_order) != 10:
|
|
736
|
+
raise Ass_generate_error("Event Format len != 10")
|
|
657
737
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
738
|
+
if "Marked" in formats_tuple:
|
|
739
|
+
log.error(
|
|
740
|
+
"The ASS Events Format version too old: {}",
|
|
741
|
+
"It used 'Marked' instead of 'Layer'. 'Marked' has been replaced with 'Layer', which will result in irreversible info loss",
|
|
742
|
+
)
|
|
663
743
|
|
|
664
|
-
|
|
744
|
+
self.events.fmt_order = format_order
|
|
665
745
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
746
|
+
else:
|
|
747
|
+
match state:
|
|
748
|
+
case State.script_info:
|
|
749
|
+
self.script_info.data.append(Script_info_data(raw_str=line))
|
|
750
|
+
|
|
751
|
+
case State.styles:
|
|
752
|
+
if not line.startswith("Style:"):
|
|
753
|
+
log.warning(
|
|
754
|
+
"Skip a Style line (illegal format): {}", line
|
|
755
|
+
)
|
|
756
|
+
continue
|
|
757
|
+
|
|
758
|
+
style_tuple = tuple(map(str.strip, line[6:].split(",")))
|
|
759
|
+
if len(style_tuple) != 23:
|
|
760
|
+
log.warning(
|
|
761
|
+
"Skip a Style line (Style Format len != 23): {}",
|
|
762
|
+
line,
|
|
763
|
+
)
|
|
764
|
+
continue
|
|
682
765
|
|
|
683
|
-
|
|
766
|
+
self.styles.data.append(self.styles.new_data(style_tuple))
|
|
684
767
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
768
|
+
case State.graphics:
|
|
769
|
+
if line.startswith("filename:"):
|
|
770
|
+
self.attachments.data.append(
|
|
771
|
+
Attachment_data(
|
|
772
|
+
type=Attach_type.Graphics,
|
|
773
|
+
name=line[9:].strip(),
|
|
774
|
+
data="",
|
|
775
|
+
)
|
|
776
|
+
)
|
|
777
|
+
else:
|
|
778
|
+
if self.attachments.data[-1].data is None:
|
|
779
|
+
log.error("Unknown error", deep=True)
|
|
780
|
+
continue
|
|
781
|
+
self.attachments.data[-1].data += line + "\n"
|
|
782
|
+
|
|
783
|
+
case State.fonts:
|
|
784
|
+
if line.startswith("fontname:"):
|
|
785
|
+
self.attachments.data.append(
|
|
786
|
+
Attachment_data(
|
|
787
|
+
type=Attach_type.Fonts,
|
|
788
|
+
name=line[9:].strip(),
|
|
789
|
+
data="",
|
|
790
|
+
)
|
|
791
|
+
)
|
|
792
|
+
else:
|
|
793
|
+
if self.attachments.data[-1].data is None:
|
|
794
|
+
log.error("Unknown error", deep=True)
|
|
795
|
+
continue
|
|
796
|
+
self.attachments.data[-1].data += line + "\n"
|
|
797
|
+
|
|
798
|
+
case State.events:
|
|
799
|
+
event_type: Event_type
|
|
800
|
+
if line.startswith("Dialogue:"):
|
|
801
|
+
event_type = Event_type.Dialogue
|
|
802
|
+
elif line.startswith("Comment:"):
|
|
803
|
+
event_type = Event_type.Comment
|
|
804
|
+
else:
|
|
805
|
+
log.warning(
|
|
806
|
+
"Skip a Event line (illegal format): {}", line
|
|
692
807
|
)
|
|
693
|
-
)
|
|
694
|
-
else:
|
|
695
|
-
if self.attachments.data[-1].data is None:
|
|
696
|
-
log.error("Unknown error", deep=True)
|
|
697
808
|
continue
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
name=line[9:].strip(),
|
|
706
|
-
data="",
|
|
809
|
+
|
|
810
|
+
event_tuple = tuple(
|
|
811
|
+
map(
|
|
812
|
+
str.strip,
|
|
813
|
+
line.split(":", maxsplit=1)[1].split(
|
|
814
|
+
",", maxsplit=9
|
|
815
|
+
),
|
|
707
816
|
)
|
|
708
817
|
)
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
818
|
+
if len(event_tuple) != 10:
|
|
819
|
+
log.warning(
|
|
820
|
+
"Skip a Event line (Event Format len != 10): {}",
|
|
821
|
+
line,
|
|
822
|
+
)
|
|
712
823
|
continue
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
event_type: Event_type
|
|
717
|
-
if line.startswith("Dialogue:"):
|
|
718
|
-
event_type = Event_type.Dialogue
|
|
719
|
-
elif line.startswith("Comment:"):
|
|
720
|
-
event_type = Event_type.Comment
|
|
721
|
-
else:
|
|
722
|
-
log.warning("Skip a Event line (illegal format): {}", line)
|
|
723
|
-
continue
|
|
724
|
-
|
|
725
|
-
event_tuple = tuple(
|
|
726
|
-
map(
|
|
727
|
-
str.strip,
|
|
728
|
-
line.split(":", maxsplit=1)[1].split(",", maxsplit=9),
|
|
729
|
-
)
|
|
730
|
-
)
|
|
731
|
-
if len(event_tuple) != 10:
|
|
732
|
-
log.warning(
|
|
733
|
-
"Skip a Event line (Event Format len != 10): {}", line
|
|
824
|
+
|
|
825
|
+
self.events.data.append(
|
|
826
|
+
self.events.new_data(event_tuple, event_type)
|
|
734
827
|
)
|
|
735
|
-
continue
|
|
736
828
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
829
|
+
case State.unknown:
|
|
830
|
+
if new_unknown_data is None:
|
|
831
|
+
raise Ass_generate_error(
|
|
832
|
+
"Unknown error occurred when read line: {}", line
|
|
833
|
+
)
|
|
834
|
+
new_unknown_data.data.append(line)
|
|
740
835
|
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
raise Ass_generate_error(
|
|
744
|
-
"Unknown error occurred when read line: {}", line
|
|
745
|
-
)
|
|
746
|
-
new_unknown_data.data.append(line)
|
|
836
|
+
except Exception as e:
|
|
837
|
+
raise Ass_generate_error("Unkown error in line: {}", line) from e
|
|
747
838
|
|
|
748
839
|
if new_unknown_data is not None:
|
|
749
840
|
self.unknown_data.append(new_unknown_data)
|
|
750
841
|
|
|
751
842
|
def __str__(
|
|
752
843
|
self,
|
|
844
|
+
*,
|
|
753
845
|
drop_non_render: bool = False,
|
|
754
846
|
drop_unkow_data: bool = False,
|
|
755
847
|
drop_fonts: bool = False,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|