mirror of
https://github.com/iluvcapra/pycmx.git
synced 2025-12-31 00:40:57 +00:00
Flake8 lints, flake8 linting fully activated.
This commit is contained in:
3
.github/workflows/python-package.yml
vendored
3
.github/workflows/python-package.yml
vendored
@@ -32,8 +32,7 @@ jobs:
|
||||
run: |
|
||||
# stop the build if there are Python syntax errors or undefined names
|
||||
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||
flake8 . --count --max-complexity=10 --max-line-length=79 --statistics
|
||||
- name: Test with pytest
|
||||
run: |
|
||||
pytest
|
||||
|
||||
@@ -12,6 +12,7 @@ logging.basicConfig(format=FORMAT)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def all_video_edits(edl):
|
||||
for event in edl.events:
|
||||
for edit in event.edits:
|
||||
@@ -30,46 +31,49 @@ def get_scene_name(edit, pattern):
|
||||
else:
|
||||
return edit.clip_name
|
||||
|
||||
|
||||
def output_cmx(outfile, out_list):
|
||||
outfile.write("TITLE: SCENE LIST\r\n")
|
||||
outfile.write("FCM: NON-DROP FRAME\r\n")
|
||||
|
||||
for o in out_list:
|
||||
line = "%03i AX V C 00:00:00:00 00:00:00:00 %s %s\r\n" % (0, o['start'],o['end'])
|
||||
line = "%03i AX V C 00:00:00:00 00:00:00:00 %s %s\r\n" % (
|
||||
0, o['start'], o['end'])
|
||||
outfile.write(line)
|
||||
outfile.write("* FROM CLIP NAME: %s\r\n" % (o['scene']) )
|
||||
outfile.write("* FROM CLIP NAME: %s\r\n" % (o['scene']))
|
||||
|
||||
|
||||
def output_cols(outfile, out_list):
|
||||
for o in out_list:
|
||||
outfile.write("%-12s\t%-12s\t%s\n" % (o['start'], o['end'], o['scene'] ))
|
||||
outfile.write("%-12s\t%-12s\t%s\n" %
|
||||
(o['start'], o['end'], o['scene']))
|
||||
|
||||
|
||||
def scene_list(infile, outfile, out_format, pattern):
|
||||
|
||||
|
||||
edl = pycmx.parse_cmx3600(infile)
|
||||
|
||||
current_scene_name = None
|
||||
|
||||
grouped_edits = [ ]
|
||||
|
||||
grouped_edits = []
|
||||
|
||||
for edit in all_video_edits(edl):
|
||||
this_scene_name = get_scene_name(edit, pattern)
|
||||
if this_scene_name is not None:
|
||||
if current_scene_name != this_scene_name:
|
||||
grouped_edits.append([ ])
|
||||
grouped_edits.append([])
|
||||
current_scene_name = this_scene_name
|
||||
|
||||
grouped_edits[-1].append(edit)
|
||||
|
||||
out_list = [ ]
|
||||
|
||||
out_list = []
|
||||
for group in grouped_edits:
|
||||
out_list.append({
|
||||
'start': group[0].record_in,
|
||||
out_list.append({
|
||||
'start': group[0].record_in,
|
||||
'end': group[-1].record_out,
|
||||
'scene': get_scene_name(group[0], pattern ) }
|
||||
)
|
||||
|
||||
'scene': get_scene_name(group[0], pattern)}
|
||||
)
|
||||
|
||||
if out_format == 'cmx':
|
||||
output_cmx(outfile, out_list)
|
||||
if out_format == 'cols':
|
||||
@@ -80,23 +84,24 @@ def scene_list(infile, outfile, out_format, pattern):
|
||||
|
||||
|
||||
def scene_list_cli():
|
||||
parser = argparse.ArgumentParser(description=
|
||||
'Read video events from an input CMX EDL and output events merged into scenes.')
|
||||
parser.add_argument('-o','--outfile', default=sys.stdout, type=argparse.FileType('w'),
|
||||
help='Output file. Default is stdout.')
|
||||
parser.add_argument('-f','--format', default='cmx', type=str,
|
||||
help='Output format. Options are cols and cmx, cmx is the default.')
|
||||
parser.add_argument('-p','--pattern', default='V?([A-Z]*[0-9]+)',
|
||||
help='RE pattern for extracting scene name from clip name. The default is "V?([A-Z]*[0-9]+)". ' + \
|
||||
'This pattern will be matched case-insensitively.')
|
||||
parser.add_argument('input_edl', default=sys.stdin, type=argparse.FileType('r'), nargs='?',
|
||||
help='Input file. Default is stdin.')
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Read video events from an input CMX EDL and output events merged into scenes.')
|
||||
parser.add_argument('-o', '--outfile', default=sys.stdout, type=argparse.FileType('w'),
|
||||
help='Output file. Default is stdout.')
|
||||
parser.add_argument('-f', '--format', default='cmx', type=str,
|
||||
help='Output format. Options are cols and cmx, cmx is the default.')
|
||||
parser.add_argument('-p', '--pattern', default='V?([A-Z]*[0-9]+)',
|
||||
help='RE pattern for extracting scene name from clip name. The default is "V?([A-Z]*[0-9]+)". ' +
|
||||
'This pattern will be matched case-insensitively.')
|
||||
parser.add_argument('input_edl', default=sys.stdin, type=argparse.FileType('r'), nargs='?',
|
||||
help='Input file. Default is stdin.')
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
infile = args.input_edl
|
||||
|
||||
scene_list(infile=infile, outfile=args.outfile , out_format=args.format, pattern=args.pattern)
|
||||
scene_list(infile=infile, outfile=args.outfile,
|
||||
out_format=args.format, pattern=args.pattern)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
scene_list_cli()
|
||||
scene_list_cli()
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
"""
|
||||
pycmx is a parser for CMX 3600-style EDLs.
|
||||
|
||||
This module (c) 2023 Jamie Hardt. For more information on your rights to
|
||||
This module (c) 2025 Jamie Hardt. For more information on your rights to
|
||||
copy and reuse this software, refer to the LICENSE file included with the
|
||||
distribution.
|
||||
"""
|
||||
|
||||
__version__ = '1.2.2'
|
||||
|
||||
from .parse_cmx_events import parse_cmx3600
|
||||
from .transition import Transition
|
||||
from .event import Event
|
||||
|
||||
@@ -4,23 +4,24 @@
|
||||
from re import (compile, match)
|
||||
from typing import Dict, Tuple, Generator
|
||||
|
||||
|
||||
class ChannelMap:
|
||||
"""
|
||||
Represents a set of all the channels to which an event applies.
|
||||
"""
|
||||
|
||||
_chan_map : Dict[str, Tuple] = {
|
||||
"V" : (True, False, False),
|
||||
"A" : (False, True, False),
|
||||
"A2" : (False, False, True),
|
||||
"AA" : (False, True, True),
|
||||
"B" : (True, True, False),
|
||||
"AA/V" : (True, True, True),
|
||||
"A2/V" : (True, False, True)
|
||||
}
|
||||
_chan_map: Dict[str, Tuple] = {
|
||||
"V": (True, False, False),
|
||||
"A": (False, True, False),
|
||||
"A2": (False, False, True),
|
||||
"AA": (False, True, True),
|
||||
"B": (True, True, False),
|
||||
"AA/V": (True, True, True),
|
||||
"A2/V": (True, False, True)
|
||||
}
|
||||
|
||||
def __init__(self, v=False, audio_channels=set()):
|
||||
self._audio_channel_set = audio_channels
|
||||
self._audio_channel_set = audio_channels
|
||||
self.v = v
|
||||
|
||||
@property
|
||||
@@ -46,7 +47,7 @@ class ChannelMap:
|
||||
|
||||
@a1.setter
|
||||
def a1(self, val: bool):
|
||||
self.set_audio_channel(1,val)
|
||||
self.set_audio_channel(1, val)
|
||||
|
||||
@property
|
||||
def a2(self) -> bool:
|
||||
@@ -55,7 +56,7 @@ class ChannelMap:
|
||||
|
||||
@a2.setter
|
||||
def a2(self, val: bool):
|
||||
self.set_audio_channel(2,val)
|
||||
self.set_audio_channel(2, val)
|
||||
|
||||
@property
|
||||
def a3(self) -> bool:
|
||||
@@ -64,28 +65,28 @@ class ChannelMap:
|
||||
|
||||
@a3.setter
|
||||
def a3(self, val: bool):
|
||||
self.set_audio_channel(3,val)
|
||||
|
||||
self.set_audio_channel(3, val)
|
||||
|
||||
@property
|
||||
def a4(self) -> bool:
|
||||
"""True if A4 is included"""
|
||||
return self.get_audio_channel(4)
|
||||
|
||||
@a4.setter
|
||||
def a4(self,val: bool):
|
||||
self.set_audio_channel(4,val)
|
||||
def a4(self, val: bool):
|
||||
self.set_audio_channel(4, val)
|
||||
|
||||
def get_audio_channel(self, chan_num) -> bool:
|
||||
"""True if chan_num is included"""
|
||||
return (chan_num in self._audio_channel_set)
|
||||
|
||||
def set_audio_channel(self,chan_num, enabled: bool):
|
||||
def set_audio_channel(self, chan_num, enabled: bool):
|
||||
"""If enabled is true, chan_num will be included"""
|
||||
if enabled:
|
||||
self._audio_channel_set.add(chan_num)
|
||||
elif self.get_audio_channel(chan_num):
|
||||
self._audio_channel_set.remove(chan_num)
|
||||
|
||||
|
||||
def _append_event(self, event_str):
|
||||
alt_channel_re = compile(r'^A(\d+)')
|
||||
if event_str in self._chan_map:
|
||||
@@ -96,7 +97,7 @@ class ChannelMap:
|
||||
else:
|
||||
matchresult = match(alt_channel_re, event_str)
|
||||
if matchresult:
|
||||
self.set_audio_channel(int( matchresult.group(1)), True )
|
||||
self.set_audio_channel(int(matchresult.group(1)), True)
|
||||
|
||||
def _append_ext(self, audio_ext):
|
||||
self.a3 = audio_ext.audio3
|
||||
@@ -109,5 +110,4 @@ class ChannelMap:
|
||||
out_v = self.video | other.video
|
||||
out_a = self._audio_channel_set | other._audio_channel_set
|
||||
|
||||
return ChannelMap(v=out_v,audio_channels = out_a)
|
||||
|
||||
return ChannelMap(v=out_v, audio_channels=out_a)
|
||||
|
||||
@@ -7,23 +7,27 @@ from .channel_map import ChannelMap
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class Edit:
|
||||
"""
|
||||
An individual source-to-record operation, with a source roll, source and
|
||||
An individual source-to-record operation, with a source roll, source and
|
||||
recorder timecode in and out, a transition and channels.
|
||||
"""
|
||||
def __init__(self, edit_statement, audio_ext_statement, clip_name_statement, source_file_statement, trans_name_statement = None):
|
||||
|
||||
def __init__(self, edit_statement, audio_ext_statement,
|
||||
clip_name_statement, source_file_statement,
|
||||
trans_name_statement=None):
|
||||
self.edit_statement = edit_statement
|
||||
self.audio_ext = audio_ext_statement
|
||||
self.clip_name_statement = clip_name_statement
|
||||
self.source_file_statement = source_file_statement
|
||||
self.trans_name_statement = trans_name_statement
|
||||
self.trans_name_statement = trans_name_statement
|
||||
|
||||
@property
|
||||
def line_number(self) -> int:
|
||||
"""
|
||||
Get the line number for the "standard form" statement associated with
|
||||
this edit. Line numbers a zero-indexed, such that the
|
||||
this edit. Line numbers a zero-indexed, such that the
|
||||
"TITLE:" record is line zero.
|
||||
"""
|
||||
return self.edit_statement.line_number
|
||||
@@ -35,7 +39,7 @@ class Edit:
|
||||
"""
|
||||
cm = ChannelMap()
|
||||
cm._append_event(self.edit_statement.channels)
|
||||
if self.audio_ext != None:
|
||||
if self.audio_ext is not None:
|
||||
cm._append_ext(self.audio_ext)
|
||||
return cm
|
||||
|
||||
@@ -45,10 +49,13 @@ class Edit:
|
||||
Get the :obj:`Transition` object associated with this edit.
|
||||
"""
|
||||
if self.trans_name_statement:
|
||||
return Transition(self.edit_statement.trans, self.edit_statement.trans_op, self.trans_name_statement.name)
|
||||
return Transition(self.edit_statement.trans,
|
||||
self.edit_statement.trans_op,
|
||||
self.trans_name_statement.name)
|
||||
else:
|
||||
return Transition(self.edit_statement.trans, self.edit_statement.trans_op, None)
|
||||
|
||||
return Transition(self.edit_statement.trans,
|
||||
self.edit_statement.trans_op, None)
|
||||
|
||||
@property
|
||||
def source_in(self) -> str:
|
||||
"""
|
||||
@@ -116,7 +123,7 @@ class Edit:
|
||||
@property
|
||||
def clip_name(self) -> Optional[str]:
|
||||
"""
|
||||
Get the clip name, as attested by a "* FROM CLIP NAME" or "* TO CLIP
|
||||
Get the clip name, as attested by a "* FROM CLIP NAME" or "* TO CLIP
|
||||
NAME" remark on the EDL. This will return None if the information is
|
||||
not present.
|
||||
"""
|
||||
@@ -124,5 +131,3 @@ class Edit:
|
||||
return None
|
||||
else:
|
||||
return self.clip_name_statement.name
|
||||
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
# pycmx
|
||||
# (c) 2018 Jamie Hardt
|
||||
|
||||
from .parse_cmx_statements import (StmtUnrecognized, StmtFCM, StmtEvent, StmtSourceUMID)
|
||||
from .parse_cmx_statements import (
|
||||
StmtUnrecognized, StmtEvent, StmtSourceUMID)
|
||||
from .event import Event
|
||||
from .channel_map import ChannelMap
|
||||
|
||||
from typing import Generator
|
||||
|
||||
|
||||
class EditList:
|
||||
"""
|
||||
Represents an entire edit decision list as returned by :func:`~pycmx.parse_cmx3600()`.
|
||||
Represents an entire edit decision list as returned by
|
||||
:func:`~pycmx.parse_cmx3600()`.
|
||||
"""
|
||||
|
||||
def __init__(self, statements):
|
||||
self.title_statement = statements[0]
|
||||
self.event_statements = statements[1:]
|
||||
@@ -23,7 +27,8 @@ class EditList:
|
||||
|
||||
Adobe EDLs with more than 999 events will be reported as "3600".
|
||||
"""
|
||||
first_event = next( (s for s in self.event_statements if type(s) is StmtEvent), None)
|
||||
first_event = next(
|
||||
(s for s in self.event_statements if type(s) is StmtEvent), None)
|
||||
|
||||
if first_event:
|
||||
if first_event.format == 8:
|
||||
@@ -36,7 +41,6 @@ class EditList:
|
||||
return 'unknown'
|
||||
else:
|
||||
return 'unknown'
|
||||
|
||||
|
||||
@property
|
||||
def channels(self) -> ChannelMap:
|
||||
@@ -50,7 +54,6 @@ class EditList:
|
||||
retval = retval | edit.channels
|
||||
|
||||
return retval
|
||||
|
||||
|
||||
@property
|
||||
def title(self) -> str:
|
||||
@@ -59,27 +62,23 @@ class EditList:
|
||||
"""
|
||||
return self.title_statement.title
|
||||
|
||||
|
||||
@property
|
||||
def unrecognized_statements(self) -> Generator[StmtUnrecognized, None, None]:
|
||||
def unrecognized_statements(self) -> Generator[StmtUnrecognized,
|
||||
None, None]:
|
||||
"""
|
||||
A generator for all the unrecognized statements in the list.
|
||||
"""
|
||||
for s in self.event_statements:
|
||||
if type(s) is StmtUnrecognized:
|
||||
yield s
|
||||
|
||||
|
||||
@property
|
||||
def events(self) -> Generator[Event, None, None]:
|
||||
'A generator for all the events in the edit list'
|
||||
is_drop = None
|
||||
current_event_num = None
|
||||
event_statements = []
|
||||
for stmt in self.event_statements:
|
||||
if type(stmt) is StmtFCM:
|
||||
is_drop = stmt.drop
|
||||
elif type(stmt) is StmtEvent:
|
||||
if type(stmt) is StmtEvent:
|
||||
if current_event_num is None:
|
||||
current_event_num = stmt.event
|
||||
event_statements.append(stmt)
|
||||
@@ -103,9 +102,7 @@ class EditList:
|
||||
"""
|
||||
A generator for all of the sources in the list
|
||||
"""
|
||||
|
||||
|
||||
for stmt in self.event_statements:
|
||||
if type(stmt) is StmtSourceUMID:
|
||||
yield stmt
|
||||
|
||||
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
# pycmx
|
||||
# (c) 2023 Jamie Hardt
|
||||
|
||||
from .parse_cmx_statements import (StmtEvent, StmtClipName, StmtSourceFile, StmtAudioExt, StmtUnrecognized, StmtEffectsName)
|
||||
from .edit import Edit
|
||||
from .parse_cmx_statements import (
|
||||
StmtEvent, StmtClipName, StmtSourceFile, StmtAudioExt, StmtUnrecognized,
|
||||
StmtEffectsName)
|
||||
from .edit import Edit
|
||||
|
||||
from typing import List, Generator, Optional, Tuple, Any
|
||||
|
||||
|
||||
class Event:
|
||||
"""
|
||||
Represents a collection of :class:`~pycmx.edit.Edit` s, all with the same event number.
|
||||
"""
|
||||
Represents a collection of :class:`~pycmx.edit.Edit` s, all with the same
|
||||
event number. """
|
||||
|
||||
def __init__(self, statements):
|
||||
self.statements = statements
|
||||
|
||||
|
||||
@property
|
||||
def number(self) -> int:
|
||||
"""
|
||||
@@ -28,10 +31,10 @@ class Event:
|
||||
will have multiple edits when a dissolve, wipe or key transition needs
|
||||
to be performed.
|
||||
"""
|
||||
edits_audio = list( self._statements_with_audio_ext() )
|
||||
clip_names = self._clip_name_statements()
|
||||
source_files= self._source_file_statements()
|
||||
|
||||
edits_audio = list(self._statements_with_audio_ext())
|
||||
clip_names = self._clip_name_statements()
|
||||
source_files = self._source_file_statements()
|
||||
|
||||
the_zip: List[List[Any]] = [edits_audio]
|
||||
|
||||
if len(edits_audio) == 2:
|
||||
@@ -45,19 +48,19 @@ class Event:
|
||||
end_name = clip_name
|
||||
|
||||
the_zip.append([start_name, end_name])
|
||||
else:
|
||||
else:
|
||||
if len(edits_audio) == len(clip_names):
|
||||
the_zip.append(clip_names)
|
||||
else:
|
||||
the_zip.append([None] * len(edits_audio) )
|
||||
the_zip.append([None] * len(edits_audio))
|
||||
|
||||
if len(edits_audio) == len(source_files):
|
||||
the_zip.append(source_files)
|
||||
elif len(source_files) == 1:
|
||||
the_zip.append( source_files * len(edits_audio) )
|
||||
the_zip.append(source_files * len(edits_audio))
|
||||
else:
|
||||
the_zip.append([None] * len(edits_audio) )
|
||||
|
||||
the_zip.append([None] * len(edits_audio))
|
||||
|
||||
# attach trans name to last event
|
||||
try:
|
||||
trans_statement = self._trans_name_statements()[0]
|
||||
@@ -65,23 +68,25 @@ class Event:
|
||||
trans_names.append(trans_statement)
|
||||
the_zip.append(trans_names)
|
||||
except IndexError:
|
||||
the_zip.append([None] * len(edits_audio) )
|
||||
the_zip.append([None] * len(edits_audio))
|
||||
|
||||
return [Edit(edit_statement=e1[0],
|
||||
audio_ext_statement=e1[1],
|
||||
clip_name_statement=n1,
|
||||
source_file_statement=s1,
|
||||
trans_name_statement=u1)
|
||||
for (e1, n1, s1, u1) in zip(*the_zip)]
|
||||
|
||||
return [ Edit(edit_statement=e1[0],
|
||||
audio_ext_statement=e1[1],
|
||||
clip_name_statement=n1,
|
||||
source_file_statement=s1,
|
||||
trans_name_statement=u1) for (e1,n1,s1,u1) in zip(*the_zip) ]
|
||||
|
||||
@property
|
||||
def unrecognized_statements(self) -> Generator[StmtUnrecognized, None, None]:
|
||||
def unrecognized_statements(self) -> Generator[StmtUnrecognized, None,
|
||||
None]:
|
||||
"""
|
||||
A generator for all the unrecognized statements in the event.
|
||||
"""
|
||||
for s in self.statements:
|
||||
if type(s) is StmtUnrecognized:
|
||||
yield s
|
||||
|
||||
|
||||
def _trans_name_statements(self) -> List[StmtEffectsName]:
|
||||
return [s for s in self.statements if type(s) is StmtEffectsName]
|
||||
|
||||
@@ -90,15 +95,14 @@ class Event:
|
||||
|
||||
def _clip_name_statements(self) -> List[StmtClipName]:
|
||||
return [s for s in self.statements if type(s) is StmtClipName]
|
||||
|
||||
|
||||
def _source_file_statements(self) -> List[StmtSourceFile]:
|
||||
return [s for s in self.statements if type(s) is StmtSourceFile]
|
||||
|
||||
def _statements_with_audio_ext(self) -> Generator[Tuple[StmtEvent, Optional[StmtAudioExt]], None, None]:
|
||||
|
||||
def _statements_with_audio_ext(self) -> Generator[
|
||||
Tuple[StmtEvent, Optional[StmtAudioExt]], None, None]:
|
||||
for (s1, s2) in zip(self.statements, self.statements[1:]):
|
||||
if type(s1) is StmtEvent and type(s2) is StmtAudioExt:
|
||||
yield (s1,s2)
|
||||
yield (s1, s2)
|
||||
elif type(s1) is StmtEvent:
|
||||
yield (s1, None)
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
from .parse_cmx_statements import (parse_cmx3600_statements)
|
||||
from .edit_list import EditList
|
||||
|
||||
from typing import TextIO
|
||||
from typing import TextIO
|
||||
|
||||
|
||||
def parse_cmx3600(f: TextIO) -> EditList:
|
||||
"""
|
||||
@@ -17,4 +18,3 @@ def parse_cmx3600(f: TextIO) -> EditList:
|
||||
"""
|
||||
statements = parse_cmx3600_statements(f)
|
||||
return EditList(statements)
|
||||
|
||||
|
||||
@@ -56,24 +56,23 @@ def _parse_cmx3600_line(line: str, line_number: int) -> object:
|
||||
Parses a single CMX EDL line.
|
||||
|
||||
:param line: A single EDL line.
|
||||
:param line_number: The index of this line in the file.
|
||||
:param line_number: The index of this line in the file.
|
||||
"""
|
||||
long_event_num_p = re.compile("^[0-9]{6} ")
|
||||
short_event_num_p = re.compile("^[0-9]{3} ")
|
||||
|
||||
|
||||
if isinstance(line, str):
|
||||
if line.startswith("TITLE:"):
|
||||
return _parse_title(line, line_number)
|
||||
elif line.startswith("FCM:"):
|
||||
return _parse_fcm(line, line_number)
|
||||
elif long_event_num_p.match(line) != None:
|
||||
elif long_event_num_p.match(line) is not None:
|
||||
length_file_128 = sum(_edl_column_widths(6, 128))
|
||||
if len(line) < length_file_128:
|
||||
return _parse_long_standard_form(line, 32, line_number)
|
||||
else:
|
||||
return _parse_long_standard_form(line, 128, line_number)
|
||||
elif short_event_num_p.match(line) != None:
|
||||
elif short_event_num_p.match(line) is not None:
|
||||
return _parse_standard_form(line, line_number)
|
||||
elif line.startswith("AUD"):
|
||||
return _parse_extended_audio_channels(line, line_number)
|
||||
@@ -185,5 +184,5 @@ def _parse_columns_for_standard_form(line, event_field_length,
|
||||
|
||||
|
||||
def _parse_source_umid_statement(line, line_number):
|
||||
trimmed = line[3:].strip()
|
||||
# trimmed = line[3:].strip()
|
||||
return StmtSourceUMID(name=None, umid=None, line_number=line_number)
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class Transition:
|
||||
"""
|
||||
A CMX transition: a wipe, dissolve or cut.
|
||||
"""
|
||||
|
||||
|
||||
Cut = "C"
|
||||
Dissolve = "D"
|
||||
Wipe = "W"
|
||||
@@ -41,7 +42,7 @@ class Transition:
|
||||
@property
|
||||
def cut(self) -> bool:
|
||||
"`True` if this transition is a cut."
|
||||
return self.transition == 'C'
|
||||
return self.transition == 'C'
|
||||
|
||||
@property
|
||||
def dissolve(self) -> bool:
|
||||
@@ -56,7 +57,7 @@ class Transition:
|
||||
@property
|
||||
def effect_duration(self) -> int:
|
||||
"""The duration of this transition, in frames of the record target.
|
||||
|
||||
|
||||
In the event of a key event, this is the duration of the fade in.
|
||||
"""
|
||||
return int(self.operand)
|
||||
@@ -86,5 +87,3 @@ class Transition:
|
||||
the key foreground and replaced with the key background.
|
||||
"""
|
||||
return self.transition == Transition.KeyOut
|
||||
|
||||
|
||||
|
||||
@@ -3,29 +3,28 @@
|
||||
|
||||
# Utility functions
|
||||
|
||||
def collimate(a_string, column_widths):
|
||||
def collimate(a_string, column_widths):
|
||||
"""
|
||||
Split a list-type thing, like a string, into slices that are column_widths
|
||||
Split a list-type thing, like a string, into slices that are column_widths
|
||||
length.
|
||||
|
||||
|
||||
>>> collimate("a b1 c2345",[2,3,3,2])
|
||||
['a ','b1 ','c23','45']
|
||||
|
||||
Args:
|
||||
a_string: The string to split. This parameter can actually be anything
|
||||
sliceable.
|
||||
column_widths: A list of integers, each one is the length of a column.
|
||||
column_widths: A list of integers, each one is the length of a column.
|
||||
|
||||
Returns:
|
||||
A list of slices. The len() of the returned list will *always* equal
|
||||
len(:column_widths:).
|
||||
A list of slices. The len() of the returned list will *always* equal
|
||||
len(:column_widths:).
|
||||
"""
|
||||
|
||||
|
||||
if len(column_widths) == 0:
|
||||
return []
|
||||
|
||||
|
||||
width = column_widths[0]
|
||||
element = a_string[:width]
|
||||
rest = a_string[width:]
|
||||
return [element] + collimate(rest, column_widths[1:])
|
||||
|
||||
return [element] + collimate(rest, column_widths[1:])
|
||||
|
||||
Reference in New Issue
Block a user