pycaption 2.2.12__tar.gz → 2.2.13__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.
- {pycaption-2.2.12/pycaption.egg-info → pycaption-2.2.13}/PKG-INFO +1 -1
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/__init__.py +3 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/base.py +45 -41
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/scc/__init__.py +115 -101
- pycaption-2.2.12/pycaption/scc/translator.py → pycaption-2.2.13/pycaption/scc/constants.py +1089 -38
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/scc/specialized_collections.py +193 -108
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/scc/state_machines.py +4 -5
- pycaption-2.2.13/pycaption/scc/translator.py +32 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/transcript.py +1 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/webvtt.py +87 -88
- {pycaption-2.2.12 → pycaption-2.2.13/pycaption.egg-info}/PKG-INFO +1 -1
- pycaption-2.2.13/setup.py +54 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/conftest.py +14 -1
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/dfxp.py +13 -12
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/scc.py +155 -5
- pycaption-2.2.13/tests/test_scc.py +977 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_scc_conversion.py +21 -15
- pycaption-2.2.12/pycaption/scc/constants.py +0 -1062
- pycaption-2.2.12/setup.py +0 -62
- pycaption-2.2.12/tests/test_scc.py +0 -593
- {pycaption-2.2.12 → pycaption-2.2.13}/LICENSE +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/MANIFEST.in +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/README.rst +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/dfxp/__init__.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/dfxp/base.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/dfxp/extras.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/english.pickle +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/exceptions.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/geometry.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/microdvd.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/sami.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/srt.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/utils.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption.egg-info/SOURCES.txt +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption.egg-info/dependency_links.txt +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption.egg-info/requires.txt +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/pycaption.egg-info/top_level.txt +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/setup.cfg +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/__init__.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/__init__.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/microdvd.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/sami.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/srt.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/translated_scc.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/webvtt.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/mixins.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_base.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_dfxp.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_dfxp_conversion.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_dfxp_extras.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_functions.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_geometry.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_microdvd.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_microdvd_conversion.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_sami.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_sami_conversion.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_scc_translator.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_srt.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_srt_conversion.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_webvtt.py +0 -0
- {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_webvtt_conversion.py +0 -0
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from collections import defaultdict
|
|
2
3
|
from datetime import timedelta
|
|
3
4
|
from numbers import Number
|
|
4
5
|
|
|
5
6
|
from .exceptions import CaptionReadError, CaptionReadTimingError
|
|
6
7
|
|
|
7
8
|
# `und` a special identifier for an undetermined language according to ISO 639-2
|
|
8
|
-
DEFAULT_LANGUAGE_CODE = os.getenv(
|
|
9
|
+
DEFAULT_LANGUAGE_CODE = os.getenv("PYCAPTION_DEFAULT_LANG", "und")
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def force_byte_string(content):
|
|
12
13
|
try:
|
|
13
|
-
return content.encode(
|
|
14
|
+
return content.encode("UTF-8")
|
|
14
15
|
except UnicodeEncodeError:
|
|
15
|
-
raise RuntimeError(
|
|
16
|
+
raise RuntimeError("Invalid content encoding")
|
|
16
17
|
except UnicodeDecodeError:
|
|
17
18
|
return content
|
|
18
19
|
|
|
@@ -50,8 +51,9 @@ class BaseReader:
|
|
|
50
51
|
|
|
51
52
|
|
|
52
53
|
class BaseWriter:
|
|
53
|
-
def __init__(
|
|
54
|
-
|
|
54
|
+
def __init__(
|
|
55
|
+
self, relativize=True, video_width=None, video_height=None, fit_to_screen=True
|
|
56
|
+
):
|
|
55
57
|
"""
|
|
56
58
|
Initialize writer with the given parameters.
|
|
57
59
|
|
|
@@ -81,7 +83,8 @@ class BaseWriter:
|
|
|
81
83
|
if self.relativize:
|
|
82
84
|
# Transform absolute values (e.g. px) into percentages
|
|
83
85
|
layout_info = layout_info.as_percentage_of(
|
|
84
|
-
self.video_width, self.video_height
|
|
86
|
+
self.video_width, self.video_height
|
|
87
|
+
)
|
|
85
88
|
if self.fit_to_screen:
|
|
86
89
|
# Make sure origin + extent <= 100%
|
|
87
90
|
layout_info = layout_info.fit_to_screen()
|
|
@@ -115,7 +118,7 @@ class CaptionNode:
|
|
|
115
118
|
BREAK = 3
|
|
116
119
|
|
|
117
120
|
def __init__(
|
|
118
|
-
|
|
121
|
+
self, type_, layout_info=None, content=None, start=None, position=None
|
|
119
122
|
):
|
|
120
123
|
"""
|
|
121
124
|
:type type_: int
|
|
@@ -135,30 +138,34 @@ class CaptionNode:
|
|
|
135
138
|
if t == CaptionNode.TEXT:
|
|
136
139
|
return repr(self.content)
|
|
137
140
|
elif t == CaptionNode.BREAK:
|
|
138
|
-
return repr(
|
|
141
|
+
return repr("BREAK")
|
|
139
142
|
elif t == CaptionNode.STYLE:
|
|
140
|
-
return repr(f
|
|
143
|
+
return repr(f"STYLE: {self.start} {self.content}")
|
|
141
144
|
else:
|
|
142
|
-
raise RuntimeError(f
|
|
145
|
+
raise RuntimeError(f"Unknown node type: {t}")
|
|
143
146
|
|
|
144
147
|
@staticmethod
|
|
145
148
|
def create_text(text, layout_info=None, position=None):
|
|
146
149
|
return CaptionNode(
|
|
147
|
-
type_=CaptionNode.TEXT,
|
|
148
|
-
|
|
150
|
+
type_=CaptionNode.TEXT,
|
|
151
|
+
layout_info=layout_info,
|
|
152
|
+
position=position,
|
|
153
|
+
content=text,
|
|
149
154
|
)
|
|
150
155
|
|
|
151
156
|
@staticmethod
|
|
152
157
|
def create_style(start, content, layout_info=None):
|
|
153
158
|
return CaptionNode(
|
|
154
|
-
type_=CaptionNode.STYLE,
|
|
155
|
-
|
|
159
|
+
type_=CaptionNode.STYLE,
|
|
160
|
+
layout_info=layout_info,
|
|
161
|
+
content=content,
|
|
162
|
+
start=start,
|
|
163
|
+
)
|
|
156
164
|
|
|
157
165
|
@staticmethod
|
|
158
166
|
def create_break(layout_info=None, content=None):
|
|
159
167
|
return CaptionNode(
|
|
160
|
-
type_=CaptionNode.BREAK, layout_info=layout_info,
|
|
161
|
-
content=content
|
|
168
|
+
type_=CaptionNode.BREAK, layout_info=layout_info, content=content
|
|
162
169
|
)
|
|
163
170
|
|
|
164
171
|
|
|
@@ -184,11 +191,13 @@ class Caption:
|
|
|
184
191
|
:type layout_info: Layout
|
|
185
192
|
"""
|
|
186
193
|
if not isinstance(start, Number):
|
|
187
|
-
raise CaptionReadTimingError(
|
|
188
|
-
|
|
194
|
+
raise CaptionReadTimingError(
|
|
195
|
+
"Captions must be initialized with a" " valid start time"
|
|
196
|
+
)
|
|
189
197
|
if not isinstance(end, Number):
|
|
190
|
-
raise CaptionReadTimingError(
|
|
191
|
-
|
|
198
|
+
raise CaptionReadTimingError(
|
|
199
|
+
"Captions must be initialized with a" " valid end time"
|
|
200
|
+
)
|
|
192
201
|
if not nodes:
|
|
193
202
|
raise CaptionReadError("Node list cannot be empty")
|
|
194
203
|
self.start = start
|
|
@@ -216,9 +225,7 @@ class Caption:
|
|
|
216
225
|
return self._format_timestamp(self.end, msec_separator)
|
|
217
226
|
|
|
218
227
|
def __repr__(self):
|
|
219
|
-
return repr(
|
|
220
|
-
f'{self.format_start()} --> {self.format_end()}\n{self.get_text()}'
|
|
221
|
-
)
|
|
228
|
+
return repr(f"{self.format_start()} --> {self.format_end()}\n{self.get_text()}")
|
|
222
229
|
|
|
223
230
|
def get_text_nodes(self):
|
|
224
231
|
"""
|
|
@@ -229,22 +236,24 @@ class Caption:
|
|
|
229
236
|
if node.type_ == CaptionNode.TEXT:
|
|
230
237
|
return node.content
|
|
231
238
|
if node.type_ == CaptionNode.BREAK:
|
|
232
|
-
return
|
|
233
|
-
return
|
|
239
|
+
return "\n"
|
|
240
|
+
return ""
|
|
234
241
|
|
|
235
242
|
return [get_text_for_node(node) for node in self.nodes]
|
|
236
243
|
|
|
237
244
|
def get_text(self):
|
|
238
245
|
text_nodes = self.get_text_nodes()
|
|
239
|
-
return
|
|
246
|
+
return "".join(text_nodes).strip()
|
|
240
247
|
|
|
241
248
|
def _format_timestamp(self, microseconds, msec_separator=None):
|
|
242
249
|
duration = timedelta(microseconds=microseconds)
|
|
243
250
|
hours, rem = divmod(duration.seconds, 3600)
|
|
244
251
|
minutes, seconds = divmod(rem, 60)
|
|
245
252
|
milliseconds = f"{duration.microseconds // 1000:03d}"
|
|
246
|
-
timestamp = (
|
|
247
|
-
|
|
253
|
+
timestamp = (
|
|
254
|
+
f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
255
|
+
f"{msec_separator or '.'}{milliseconds:.3s}"
|
|
256
|
+
)
|
|
248
257
|
return timestamp
|
|
249
258
|
|
|
250
259
|
|
|
@@ -261,8 +270,7 @@ class CaptionList(list):
|
|
|
261
270
|
super().__init__(*args)
|
|
262
271
|
|
|
263
272
|
def __getslice__(self, i, j):
|
|
264
|
-
return CaptionList(
|
|
265
|
-
list.__getslice__(self, i, j), layout_info=self.layout_info)
|
|
273
|
+
return CaptionList(list.__getslice__(self, i, j), layout_info=self.layout_info)
|
|
266
274
|
|
|
267
275
|
def __getitem__(self, y):
|
|
268
276
|
item = list.__getitem__(self, y)
|
|
@@ -272,20 +280,19 @@ class CaptionList(list):
|
|
|
272
280
|
|
|
273
281
|
def __add__(self, other):
|
|
274
282
|
add_is_safe = (
|
|
275
|
-
not hasattr(other,
|
|
283
|
+
not hasattr(other, "layout_info")
|
|
276
284
|
or not other.layout_info
|
|
277
285
|
or self.layout_info == other.layout_info
|
|
278
286
|
)
|
|
279
287
|
if add_is_safe:
|
|
280
|
-
return CaptionList(
|
|
281
|
-
list.__add__(self, other), layout_info=self.layout_info)
|
|
288
|
+
return CaptionList(list.__add__(self, other), layout_info=self.layout_info)
|
|
282
289
|
else:
|
|
283
290
|
raise ValueError(
|
|
284
|
-
"Cannot add CaptionList objects with different layout_info"
|
|
291
|
+
"Cannot add CaptionList objects with different layout_info"
|
|
292
|
+
)
|
|
285
293
|
|
|
286
294
|
def __mul__(self, other):
|
|
287
|
-
return CaptionList(
|
|
288
|
-
list.__mul__(self, other), layout_info=self.layout_info)
|
|
295
|
+
return CaptionList(list.__mul__(self, other), layout_info=self.layout_info)
|
|
289
296
|
|
|
290
297
|
__rmul__ = __mul__
|
|
291
298
|
|
|
@@ -341,9 +348,7 @@ class CaptionSet:
|
|
|
341
348
|
self._styles = styles
|
|
342
349
|
|
|
343
350
|
def is_empty(self):
|
|
344
|
-
return all(
|
|
345
|
-
[len(captions) == 0 for captions in list(self._captions.values())]
|
|
346
|
-
)
|
|
351
|
+
return all([len(captions) == 0 for captions in list(self._captions.values())])
|
|
347
352
|
|
|
348
353
|
def set_layout_info(self, lang, layout_info):
|
|
349
354
|
self._captions[lang].layout_info = layout_info
|
|
@@ -412,6 +417,5 @@ def merge(captions):
|
|
|
412
417
|
new_nodes.append(CaptionNode.create_break())
|
|
413
418
|
for node in caption.nodes:
|
|
414
419
|
new_nodes.append(node)
|
|
415
|
-
caption = Caption(
|
|
416
|
-
captions[0].start, captions[0].end, new_nodes, captions[0].style)
|
|
420
|
+
caption = Caption(captions[0].start, captions[0].end, new_nodes, captions[0].style)
|
|
417
421
|
return caption
|