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.
Files changed (61) hide show
  1. {pycaption-2.2.12/pycaption.egg-info → pycaption-2.2.13}/PKG-INFO +1 -1
  2. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/__init__.py +3 -0
  3. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/base.py +45 -41
  4. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/scc/__init__.py +115 -101
  5. pycaption-2.2.12/pycaption/scc/translator.py → pycaption-2.2.13/pycaption/scc/constants.py +1089 -38
  6. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/scc/specialized_collections.py +193 -108
  7. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/scc/state_machines.py +4 -5
  8. pycaption-2.2.13/pycaption/scc/translator.py +32 -0
  9. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/transcript.py +1 -0
  10. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/webvtt.py +87 -88
  11. {pycaption-2.2.12 → pycaption-2.2.13/pycaption.egg-info}/PKG-INFO +1 -1
  12. pycaption-2.2.13/setup.py +54 -0
  13. {pycaption-2.2.12 → pycaption-2.2.13}/tests/conftest.py +14 -1
  14. {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/dfxp.py +13 -12
  15. {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/scc.py +155 -5
  16. pycaption-2.2.13/tests/test_scc.py +977 -0
  17. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_scc_conversion.py +21 -15
  18. pycaption-2.2.12/pycaption/scc/constants.py +0 -1062
  19. pycaption-2.2.12/setup.py +0 -62
  20. pycaption-2.2.12/tests/test_scc.py +0 -593
  21. {pycaption-2.2.12 → pycaption-2.2.13}/LICENSE +0 -0
  22. {pycaption-2.2.12 → pycaption-2.2.13}/MANIFEST.in +0 -0
  23. {pycaption-2.2.12 → pycaption-2.2.13}/README.rst +0 -0
  24. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/dfxp/__init__.py +0 -0
  25. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/dfxp/base.py +0 -0
  26. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/dfxp/extras.py +0 -0
  27. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/english.pickle +0 -0
  28. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/exceptions.py +0 -0
  29. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/geometry.py +0 -0
  30. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/microdvd.py +0 -0
  31. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/sami.py +0 -0
  32. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/srt.py +0 -0
  33. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption/utils.py +0 -0
  34. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption.egg-info/SOURCES.txt +0 -0
  35. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption.egg-info/dependency_links.txt +0 -0
  36. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption.egg-info/requires.txt +0 -0
  37. {pycaption-2.2.12 → pycaption-2.2.13}/pycaption.egg-info/top_level.txt +0 -0
  38. {pycaption-2.2.12 → pycaption-2.2.13}/setup.cfg +0 -0
  39. {pycaption-2.2.12 → pycaption-2.2.13}/tests/__init__.py +0 -0
  40. {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/__init__.py +0 -0
  41. {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/microdvd.py +0 -0
  42. {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/sami.py +0 -0
  43. {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/srt.py +0 -0
  44. {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/translated_scc.py +0 -0
  45. {pycaption-2.2.12 → pycaption-2.2.13}/tests/fixtures/webvtt.py +0 -0
  46. {pycaption-2.2.12 → pycaption-2.2.13}/tests/mixins.py +0 -0
  47. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_base.py +0 -0
  48. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_dfxp.py +0 -0
  49. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_dfxp_conversion.py +0 -0
  50. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_dfxp_extras.py +0 -0
  51. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_functions.py +0 -0
  52. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_geometry.py +0 -0
  53. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_microdvd.py +0 -0
  54. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_microdvd_conversion.py +0 -0
  55. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_sami.py +0 -0
  56. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_sami_conversion.py +0 -0
  57. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_scc_translator.py +0 -0
  58. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_srt.py +0 -0
  59. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_srt_conversion.py +0 -0
  60. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_webvtt.py +0 -0
  61. {pycaption-2.2.12 → pycaption-2.2.13}/tests/test_webvtt_conversion.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pycaption
3
- Version: 2.2.12
3
+ Version: 2.2.13
4
4
  Summary: Closed caption converter
5
5
  Author: Joe Norton
6
6
  Author-email: joey@nortoncrew.com
@@ -34,6 +34,9 @@ def detect_format(caps):
34
34
 
35
35
  :returns: the reader class for the detected format.
36
36
  """
37
+ if not len(caps):
38
+ raise CaptionReadNoCaptions("Empty caption file")
39
+
37
40
  for reader in SUPPORTED_READERS:
38
41
  if reader().detect(caps):
39
42
  return reader
@@ -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('PYCAPTION_DEFAULT_LANG', 'und')
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('UTF-8')
14
+ return content.encode("UTF-8")
14
15
  except UnicodeEncodeError:
15
- raise RuntimeError('Invalid content encoding')
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__(self, relativize=True, video_width=None, video_height=None,
54
- fit_to_screen=True):
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
- self, type_, layout_info=None, content=None, start=None, position=None
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('BREAK')
141
+ return repr("BREAK")
139
142
  elif t == CaptionNode.STYLE:
140
- return repr(f'STYLE: {self.start} {self.content}')
143
+ return repr(f"STYLE: {self.start} {self.content}")
141
144
  else:
142
- raise RuntimeError(f'Unknown node type: {t}')
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, layout_info=layout_info,
148
- position=position, content=text
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, layout_info=layout_info, content=content,
155
- start=start)
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("Captions must be initialized with a"
188
- " valid start time")
194
+ raise CaptionReadTimingError(
195
+ "Captions must be initialized with a" " valid start time"
196
+ )
189
197
  if not isinstance(end, Number):
190
- raise CaptionReadTimingError("Captions must be initialized with a"
191
- " valid end time")
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 '\n'
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 ''.join(text_nodes).strip()
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 = (f"{hours:02d}:{minutes:02d}:{seconds:02d}"
247
- f"{msec_separator or '.'}{milliseconds:.3s}")
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, 'layout_info')
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