pycmx 1.2.0__tar.gz → 1.2.1__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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pycmx
3
- Version: 1.2.0
4
- Summary: pycmx is a module for parsing CMX 3600-style EDLs. For more information and
3
+ Version: 1.2.1
4
+ Summary: pycmx is a parser for CMX 3600-style EDLs.
5
5
  Keywords: parser,film,broadcast
6
6
  Author-email: Jamie Hardt <jamiehardt@me.com>
7
7
  Requires-Python: ~=3.7
@@ -1,14 +1,13 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """
3
- pycmx is a module for parsing CMX 3600-style EDLs. For more information and
4
- examples see README.md
3
+ pycmx is a parser for CMX 3600-style EDLs.
5
4
 
6
- This module (c) 2022 Jamie Hardt. For more information on your rights to
5
+ This module (c) 2023 Jamie Hardt. For more information on your rights to
7
6
  copy and reuse this software, refer to the LICENSE file included with the
8
7
  distribution.
9
8
  """
10
9
 
11
- __version__ = '1.2.0'
10
+ __version__ = '1.2.1'
12
11
 
13
12
  from .parse_cmx_events import parse_cmx3600
14
13
  from .transition import Transition
@@ -2,7 +2,7 @@
2
2
  # (c) 2018 Jamie Hardt
3
3
 
4
4
  from re import (compile, match)
5
- from typing import Dict, Tuple
5
+ from typing import Dict, Tuple, Generator
6
6
 
7
7
  class ChannelMap:
8
8
  """
@@ -24,62 +24,62 @@ class ChannelMap:
24
24
  self.v = v
25
25
 
26
26
  @property
27
- def video(self):
27
+ def video(self) -> bool:
28
28
  'True if video is included'
29
29
  return self.v
30
30
 
31
31
  @property
32
- def audio(self):
32
+ def audio(self) -> bool:
33
33
  'True if an audio channel is included'
34
34
  return len(self._audio_channel_set) > 0
35
35
 
36
36
  @property
37
- def channels(self):
37
+ def channels(self) -> Generator[int, None, None]:
38
38
  'A generator for each audio channel'
39
39
  for c in self._audio_channel_set:
40
40
  yield c
41
41
 
42
42
  @property
43
- def a1(self):
43
+ def a1(self) -> bool:
44
44
  """True if A1 is included"""
45
45
  return self.get_audio_channel(1)
46
46
 
47
47
  @a1.setter
48
- def a1(self,val):
48
+ def a1(self, val: bool):
49
49
  self.set_audio_channel(1,val)
50
50
 
51
51
  @property
52
- def a2(self):
52
+ def a2(self) -> bool:
53
53
  """True if A2 is included"""
54
54
  return self.get_audio_channel(2)
55
55
 
56
56
  @a2.setter
57
- def a2(self,val):
57
+ def a2(self, val: bool):
58
58
  self.set_audio_channel(2,val)
59
59
 
60
60
  @property
61
- def a3(self):
61
+ def a3(self) -> bool:
62
62
  """True if A3 is included"""
63
63
  return self.get_audio_channel(3)
64
64
 
65
65
  @a3.setter
66
- def a3(self,val):
66
+ def a3(self, val: bool):
67
67
  self.set_audio_channel(3,val)
68
68
 
69
69
  @property
70
- def a4(self):
70
+ def a4(self) -> bool:
71
71
  """True if A4 is included"""
72
72
  return self.get_audio_channel(4)
73
73
 
74
74
  @a4.setter
75
- def a4(self,val):
75
+ def a4(self,val: bool):
76
76
  self.set_audio_channel(4,val)
77
77
 
78
- def get_audio_channel(self,chan_num):
78
+ def get_audio_channel(self, chan_num) -> bool:
79
79
  """True if chan_num is included"""
80
80
  return (chan_num in self._audio_channel_set)
81
81
 
82
- def set_audio_channel(self,chan_num,enabled):
82
+ def set_audio_channel(self,chan_num, enabled: bool):
83
83
  """If enabled is true, chan_num will be included"""
84
84
  if enabled:
85
85
  self._audio_channel_set.add(chan_num)
@@ -3,7 +3,9 @@
3
3
 
4
4
  from .transition import Transition
5
5
  from .channel_map import ChannelMap
6
- from .parse_cmx_statements import StmtEffectsName
6
+ # from .parse_cmx_statements import StmtEffectsName
7
+
8
+ from typing import Optional
7
9
 
8
10
  class Edit:
9
11
  """
@@ -18,7 +20,7 @@ class Edit:
18
20
  self.trans_name_statement = trans_name_statement
19
21
 
20
22
  @property
21
- def line_number(self):
23
+ def line_number(self) -> int:
22
24
  """
23
25
  Get the line number for the "standard form" statement associated with
24
26
  this edit. Line numbers a zero-indexed, such that the
@@ -27,7 +29,7 @@ class Edit:
27
29
  return self.edit_statement.line_number
28
30
 
29
31
  @property
30
- def channels(self):
32
+ def channels(self) -> ChannelMap:
31
33
  """
32
34
  Get the :obj:`ChannelMap` object associated with this Edit.
33
35
  """
@@ -38,7 +40,7 @@ class Edit:
38
40
  return cm
39
41
 
40
42
  @property
41
- def transition(self):
43
+ def transition(self) -> Transition:
42
44
  """
43
45
  Get the :obj:`Transition` object associated with this edit.
44
46
  """
@@ -48,14 +50,14 @@ class Edit:
48
50
  return Transition(self.edit_statement.trans, self.edit_statement.trans_op, None)
49
51
 
50
52
  @property
51
- def source_in(self):
53
+ def source_in(self) -> str:
52
54
  """
53
55
  Get the source in timecode.
54
56
  """
55
57
  return self.edit_statement.source_in
56
58
 
57
59
  @property
58
- def source_out(self):
60
+ def source_out(self) -> str:
59
61
  """
60
62
  Get the source out timecode.
61
63
  """
@@ -63,7 +65,7 @@ class Edit:
63
65
  return self.edit_statement.source_out
64
66
 
65
67
  @property
66
- def record_in(self):
68
+ def record_in(self) -> str:
67
69
  """
68
70
  Get the record in timecode.
69
71
  """
@@ -71,7 +73,7 @@ class Edit:
71
73
  return self.edit_statement.record_in
72
74
 
73
75
  @property
74
- def record_out(self):
76
+ def record_out(self) -> str:
75
77
  """
76
78
  Get the record out timecode.
77
79
  """
@@ -79,7 +81,7 @@ class Edit:
79
81
  return self.edit_statement.record_out
80
82
 
81
83
  @property
82
- def source(self):
84
+ def source(self) -> str:
83
85
  """
84
86
  Get the source column. This is the 8, 32 or 128-character string on the
85
87
  event record line, this usually references the tape name of the source.
@@ -87,21 +89,21 @@ class Edit:
87
89
  return self.edit_statement.source
88
90
 
89
91
  @property
90
- def black(self):
92
+ def black(self) -> bool:
91
93
  """
92
94
  Black video or silence should be used as the source for this event.
93
95
  """
94
96
  return self.source == "BL"
95
97
 
96
98
  @property
97
- def aux_source(self):
99
+ def aux_source(self) -> bool:
98
100
  """
99
101
  An auxiliary source is the source of this event.
100
102
  """
101
103
  return self.source == "AX"
102
104
 
103
105
  @property
104
- def source_file(self):
106
+ def source_file(self) -> Optional[str]:
105
107
  """
106
108
  Get the source file, as attested by a "* SOURCE FILE" remark on the
107
109
  EDL. This will return None if the information is not present.
@@ -112,7 +114,7 @@ class Edit:
112
114
  return self.source_file_statement.filename
113
115
 
114
116
  @property
115
- def clip_name(self):
117
+ def clip_name(self) -> Optional[str]:
116
118
  """
117
119
  Get the clip name, as attested by a "* FROM CLIP NAME" or "* TO CLIP
118
120
  NAME" remark on the EDL. This will return None if the information is
@@ -5,21 +5,21 @@ from .parse_cmx_statements import (StmtUnrecognized, StmtFCM, StmtEvent, StmtSou
5
5
  from .event import Event
6
6
  from .channel_map import ChannelMap
7
7
 
8
+ from typing import Generator
9
+
8
10
  class EditList:
9
11
  """
10
- Represents an entire edit decision list as returned by `parse_cmx3600()`.
11
-
12
+ Represents an entire edit decision list as returned by :func:`~pycmx.parse_cmx3600()`.
12
13
  """
13
14
  def __init__(self, statements):
14
15
  self.title_statement = statements[0]
15
16
  self.event_statements = statements[1:]
16
17
 
17
-
18
18
  @property
19
- def format(self):
19
+ def format(self) -> str:
20
20
  """
21
- The detected format of the EDL. Possible values are: `3600`,`File32`,
22
- `File128`, and `unknown`
21
+ The detected format of the EDL. Possible values are: "3600", "File32",
22
+ "File128", and "unknown".
23
23
  """
24
24
  first_event = next( (s for s in self.event_statements if type(s) is StmtEvent), None)
25
25
 
@@ -37,7 +37,7 @@ class EditList:
37
37
 
38
38
 
39
39
  @property
40
- def channels(self):
40
+ def channels(self) -> ChannelMap:
41
41
  """
42
42
  Return the union of every channel channel.
43
43
  """
@@ -51,7 +51,7 @@ class EditList:
51
51
 
52
52
 
53
53
  @property
54
- def title(self):
54
+ def title(self) -> str:
55
55
  """
56
56
  The title of this edit list.
57
57
  """
@@ -59,7 +59,7 @@ class EditList:
59
59
 
60
60
 
61
61
  @property
62
- def unrecognized_statements(self):
62
+ def unrecognized_statements(self) -> Generator[StmtUnrecognized, None, None]:
63
63
  """
64
64
  A generator for all the unrecognized statements in the list.
65
65
  """
@@ -69,7 +69,7 @@ class EditList:
69
69
 
70
70
 
71
71
  @property
72
- def events(self):
72
+ def events(self) -> Generator[Event, None, None]:
73
73
  'A generator for all the events in the edit list'
74
74
  is_drop = None
75
75
  current_event_num = None
@@ -97,7 +97,7 @@ class EditList:
97
97
  yield Event(statements=event_statements)
98
98
 
99
99
  @property
100
- def sources(self):
100
+ def sources(self) -> Generator[StmtSourceUMID, None, None]:
101
101
  """
102
102
  A generator for all of the sources in the list
103
103
  """
@@ -1,26 +1,28 @@
1
1
  # pycmx
2
- # (c) 2018 Jamie Hardt
2
+ # (c) 2023 Jamie Hardt
3
3
 
4
4
  from .parse_cmx_statements import (StmtEvent, StmtClipName, StmtSourceFile, StmtAudioExt, StmtUnrecognized, StmtEffectsName)
5
- from .edit import Edit
5
+ from .edit import Edit
6
+
7
+ from typing import List, Generator, Optional, Tuple, Any
6
8
 
7
9
  class Event:
8
10
  """
9
- Represents a collection of :class:`Edit`s, all with the same event number.
11
+ Represents a collection of :class:`~pycmx.edit.Edit` s, all with the same event number.
10
12
  """
11
13
 
12
14
  def __init__(self, statements):
13
15
  self.statements = statements
14
16
 
15
17
  @property
16
- def number(self):
18
+ def number(self) -> int:
17
19
  """
18
20
  Return the event number.
19
21
  """
20
22
  return int(self._edit_statements()[0].event)
21
23
 
22
24
  @property
23
- def edits(self):
25
+ def edits(self) -> List[Edit]:
24
26
  """
25
27
  Returns the edits. Most events will have a single edit, a single event
26
28
  will have multiple edits when a dissolve, wipe or key transition needs
@@ -30,17 +32,19 @@ class Event:
30
32
  clip_names = self._clip_name_statements()
31
33
  source_files= self._source_file_statements()
32
34
 
33
- the_zip = [edits_audio]
35
+ the_zip: List[List[Any]] = [edits_audio]
34
36
 
35
37
  if len(edits_audio) == 2:
36
- cn = [None, None]
38
+ start_name: Optional[StmtClipName] = None
39
+ end_name: Optional[StmtClipName] = None
40
+
37
41
  for clip_name in clip_names:
38
42
  if clip_name.affect == 'from':
39
- cn[0] = clip_name
43
+ start_name = clip_name
40
44
  elif clip_name.affect == 'to':
41
- cn[1] = clip_name
45
+ end_name = clip_name
42
46
 
43
- the_zip.append(cn)
47
+ the_zip.append([start_name, end_name])
44
48
  else:
45
49
  if len(edits_audio) == len(clip_names):
46
50
  the_zip.append(clip_names)
@@ -57,17 +61,20 @@ class Event:
57
61
  # attach trans name to last event
58
62
  try:
59
63
  trans_statement = self._trans_name_statements()[0]
60
- trans_names = [None] * (len(edits_audio) - 1)
64
+ trans_names: List[Optional[Any]] = [None] * (len(edits_audio) - 1)
61
65
  trans_names.append(trans_statement)
62
66
  the_zip.append(trans_names)
63
67
  except IndexError:
64
68
  the_zip.append([None] * len(edits_audio) )
65
69
 
66
-
67
- return [ Edit(e1[0],e1[1],n1,s1,u1) for (e1,n1,s1,u1) in zip(*the_zip) ]
70
+ return [ Edit(edit_statement=e1[0],
71
+ audio_ext_statement=e1[1],
72
+ clip_name_statement=n1,
73
+ source_file_statement=s1,
74
+ trans_name_statement=u1) for (e1,n1,s1,u1) in zip(*the_zip) ]
68
75
 
69
76
  @property
70
- def unrecognized_statements(self):
77
+ def unrecognized_statements(self) -> Generator[StmtUnrecognized, None, None]:
71
78
  """
72
79
  A generator for all the unrecognized statements in the event.
73
80
  """
@@ -75,19 +82,19 @@ class Event:
75
82
  if type(s) is StmtUnrecognized:
76
83
  yield s
77
84
 
78
- def _trans_name_statements(self):
85
+ def _trans_name_statements(self) -> List[StmtEffectsName]:
79
86
  return [s for s in self.statements if type(s) is StmtEffectsName]
80
87
 
81
- def _edit_statements(self):
88
+ def _edit_statements(self) -> List[StmtEvent]:
82
89
  return [s for s in self.statements if type(s) is StmtEvent]
83
90
 
84
- def _clip_name_statements(self):
91
+ def _clip_name_statements(self) -> List[StmtClipName]:
85
92
  return [s for s in self.statements if type(s) is StmtClipName]
86
93
 
87
- def _source_file_statements(self):
94
+ def _source_file_statements(self) -> List[StmtSourceFile]:
88
95
  return [s for s in self.statements if type(s) is StmtSourceFile]
89
96
 
90
- def _statements_with_audio_ext(self):
97
+ def _statements_with_audio_ext(self) -> Generator[Tuple[StmtEvent, Optional[StmtAudioExt]], None, None]:
91
98
  for (s1, s2) in zip(self.statements, self.statements[1:]):
92
99
  if type(s1) is StmtEvent and type(s2) is StmtAudioExt:
93
100
  yield (s1,s2)
@@ -0,0 +1,20 @@
1
+ # pycmx
2
+ # (c) 2018 Jamie Hardt
3
+
4
+ # from collections import namedtuple
5
+
6
+ from .parse_cmx_statements import (parse_cmx3600_statements)
7
+ from .edit_list import EditList
8
+
9
+ from typing import TextIO
10
+
11
+ def parse_cmx3600(f: TextIO):
12
+ """
13
+ Parse a CMX 3600 EDL.
14
+
15
+ :param TextIO f: a file-like object, anything that's readlines-able.
16
+ :returns: An :class:`pycmx.edit_list.EditList`.
17
+ """
18
+ statements = parse_cmx3600_statements(f)
19
+ return EditList(statements)
20
+
@@ -5,6 +5,8 @@ import re
5
5
  import sys
6
6
  from collections import namedtuple
7
7
  from itertools import count
8
+ from typing import TextIO, List
9
+
8
10
 
9
11
  from .util import collimate
10
12
 
@@ -18,12 +20,12 @@ StmtSourceFile = namedtuple("SourceFile",["filename","line_number"])
18
20
  StmtRemark = namedtuple("Remark",["text","line_number"])
19
21
  StmtEffectsName = namedtuple("EffectsName",["name","line_number"])
20
22
  StmtSourceUMID = namedtuple("Source",["name","umid","line_number"])
21
- StmtSplitEdit = namedtuple("SplitEdit",["video","magnitue", "line_number"])
23
+ StmtSplitEdit = namedtuple("SplitEdit",["video","magnitude", "line_number"])
22
24
  StmtMotionMemory = namedtuple("MotionMemory",["source","fps"]) # FIXME needs more fields
23
25
  StmtUnrecognized = namedtuple("Unrecognized",["content","line_number"])
24
26
 
25
27
 
26
- def parse_cmx3600_statements(file):
28
+ def parse_cmx3600_statements(file: TextIO) -> List[object]:
27
29
  """
28
30
  Return a list of every statement in the file argument.
29
31
  """
@@ -109,7 +111,7 @@ def _parse_extended_audio_channels(line, line_number):
109
111
  else:
110
112
  return StmtUnrecognized(content=line, line_number=line_number)
111
113
 
112
- def _parse_remark(line, line_number):
114
+ def _parse_remark(line, line_number) -> object:
113
115
  if line.startswith("FROM CLIP NAME:"):
114
116
  return StmtClipName(name=line[15:].strip() , affect="from", line_number=line_number)
115
117
  elif line.startswith("TO CLIP NAME:"):
@@ -119,7 +121,7 @@ def _parse_remark(line, line_number):
119
121
  else:
120
122
  return StmtRemark(text=line, line_number=line_number)
121
123
 
122
- def _parse_effects_name(line, line_number):
124
+ def _parse_effects_name(line, line_number) -> StmtEffectsName:
123
125
  name = line[16:].strip()
124
126
  return StmtEffectsName(name=name, line_number=line_number)
125
127
 
@@ -1,5 +1,7 @@
1
1
  # pycmx
2
- # (c) 2018 Jamie Hardt
2
+ # (c) 2023 Jamie Hardt
3
+
4
+ from typing import Optional
3
5
 
4
6
  class Transition:
5
7
  """
@@ -19,7 +21,7 @@ class Transition:
19
21
  self.name = name
20
22
 
21
23
  @property
22
- def kind(self):
24
+ def kind(self) -> Optional[str]:
23
25
  """
24
26
  Return the kind of transition: Cut, Wipe, etc
25
27
  """
@@ -37,22 +39,22 @@ class Transition:
37
39
  return Transition.KeyOut
38
40
 
39
41
  @property
40
- def cut(self):
42
+ def cut(self) -> bool:
41
43
  "`True` if this transition is a cut."
42
44
  return self.transition == 'C'
43
45
 
44
46
  @property
45
- def dissolve(self):
47
+ def dissolve(self) -> bool:
46
48
  "`True` if this traansition is a dissolve."
47
49
  return self.transition == 'D'
48
50
 
49
51
  @property
50
- def wipe(self):
52
+ def wipe(self) -> bool:
51
53
  "`True` if this transition is a wipe."
52
54
  return self.transition.startswith('W')
53
55
 
54
56
  @property
55
- def effect_duration(self):
57
+ def effect_duration(self) -> int:
56
58
  """The duration of this transition, in frames of the record target.
57
59
 
58
60
  In the event of a key event, this is the duration of the fade in.
@@ -60,7 +62,7 @@ class Transition:
60
62
  return int(self.operand)
61
63
 
62
64
  @property
63
- def wipe_number(self):
65
+ def wipe_number(self) -> Optional[int]:
64
66
  "Wipes are identified by a particular number."
65
67
  if self.wipe:
66
68
  return int(self.transition[1:])
@@ -68,19 +70,21 @@ class Transition:
68
70
  return None
69
71
 
70
72
  @property
71
- def key_background(self):
73
+ def key_background(self) -> bool:
72
74
  "`True` if this edit is a key background."
73
75
  return self.transition == Transition.KeyBackground
74
76
 
75
77
  @property
76
- def key_foreground(self):
78
+ def key_foreground(self) -> bool:
77
79
  "`True` if this edit is a key foreground."
78
80
  return self.transition == Transition.Key
79
81
 
80
82
  @property
81
- def key_out(self):
83
+ def key_out(self) -> bool:
82
84
  """
83
85
  `True` if this edit is a key out. This material will removed from
84
86
  the key foreground and replaced with the key background.
85
87
  """
86
88
  return self.transition == Transition.KeyOut
89
+
90
+
@@ -1,21 +0,0 @@
1
- # pycmx
2
- # (c) 2018 Jamie Hardt
3
-
4
- from collections import namedtuple
5
-
6
- from .parse_cmx_statements import (parse_cmx3600_statements, StmtEvent,StmtFCM )
7
- from .edit_list import EditList
8
-
9
- def parse_cmx3600(f):
10
- """
11
- Parse a CMX 3600 EDL.
12
-
13
- Args:
14
- f : a file-like object, anything that's readlines-able.
15
-
16
- Returns:
17
- An :class:`pycmx.edit_list.EditList`.
18
- """
19
- statements = parse_cmx3600_statements(f)
20
- return EditList(statements)
21
-
File without changes
File without changes
File without changes