mirror of
https://github.com/iluvcapra/pycmx.git
synced 2025-12-31 08:50:54 +00:00
Doc, file path
Documentation, cleaned up interface, and we now parse file handles, not file paths
This commit is contained in:
@@ -1,4 +1,14 @@
|
||||
# pycmx init
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
pycmx is a module for parsing CMX 3600-style EDLs. For more information and
|
||||
examples see README.md
|
||||
|
||||
This module (c) 2018 Jamie Hardt. For more information on your rights to
|
||||
copy and reuse this software, refer to the LICENSE file included with the
|
||||
distribution.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
from .parse_cmx_events import parse_cmx3600, Transition, Event, Edit
|
||||
from . import parse_cmx_events
|
||||
|
||||
@@ -9,7 +9,7 @@ class ChannelMap:
|
||||
Represents a set of all the channels to which an event applies.
|
||||
"""
|
||||
|
||||
chan_map = { "V" : (True, False, False),
|
||||
_chan_map = { "V" : (True, False, False),
|
||||
"A" : (False, True, False),
|
||||
"A2" : (False, False, True),
|
||||
"AA" : (False, True, True),
|
||||
@@ -35,6 +35,7 @@ class ChannelMap:
|
||||
|
||||
@property
|
||||
def a1(self):
|
||||
"""True if A1 is included."""
|
||||
return self.get_audio_channel(1)
|
||||
|
||||
@a1.setter
|
||||
@@ -43,6 +44,7 @@ class ChannelMap:
|
||||
|
||||
@property
|
||||
def a2(self):
|
||||
"""True if A2 is included."""
|
||||
return self.get_audio_channel(2)
|
||||
|
||||
@a2.setter
|
||||
@@ -51,6 +53,7 @@ class ChannelMap:
|
||||
|
||||
@property
|
||||
def a3(self):
|
||||
"""True if A3 is included."""
|
||||
return self.get_audio_channel(3)
|
||||
|
||||
@a3.setter
|
||||
@@ -59,6 +62,7 @@ class ChannelMap:
|
||||
|
||||
@property
|
||||
def a4(self):
|
||||
"""True if A4 is included."""
|
||||
return self.get_audio_channel(4)
|
||||
|
||||
@a4.setter
|
||||
@@ -66,18 +70,20 @@ class ChannelMap:
|
||||
self.set_audio_channel(4,val)
|
||||
|
||||
def get_audio_channel(self,chan_num):
|
||||
"""True if chan_num is included."""
|
||||
return (chan_num in self._audio_channel_set)
|
||||
|
||||
def set_audio_channel(self,chan_num,enabled):
|
||||
"""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):
|
||||
def _append_event(self, event_str):
|
||||
alt_channel_re = compile('^A(\d+)')
|
||||
if event_str in self.chan_map:
|
||||
channels = self.chan_map[event_str]
|
||||
if event_str in self._chan_map:
|
||||
channels = self._chan_map[event_str]
|
||||
self.v = channels[0]
|
||||
self.a1 = channels[1]
|
||||
self.a2 = channels[2]
|
||||
@@ -86,7 +92,7 @@ class ChannelMap:
|
||||
if matchresult:
|
||||
self.set_audio_channel(int( matchresult.group(1)), True )
|
||||
|
||||
def append_sxt(self, audio_ext):
|
||||
def _append_sxt(self, audio_ext):
|
||||
self.a3 = ext.audio3
|
||||
self.a4 = ext.audio4
|
||||
|
||||
|
||||
@@ -8,18 +8,35 @@ from .channel_map import ChannelMap
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
def parse_cmx3600(path):
|
||||
statements = parse_cmx3600_statements(path)
|
||||
def parse_cmx3600(f):
|
||||
"""
|
||||
Parse a CMX 3600 EDL.
|
||||
|
||||
Args:
|
||||
f : a file-like object, anything that's readlines-able.
|
||||
|
||||
Returns:
|
||||
An :obj:`EditList`.
|
||||
"""
|
||||
statements = parse_cmx3600_statements(f)
|
||||
return EditList(statements)
|
||||
|
||||
|
||||
class EditList:
|
||||
"""
|
||||
Represents an entire edit decision list as returned by `parse_cmx3600()`.
|
||||
|
||||
"""
|
||||
def __init__(self, statements):
|
||||
self.title_statement = statements[0]
|
||||
self.event_statements = statements[1:]
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
"""
|
||||
The title of this edit list, as attensted by the 'TITLE:' statement on
|
||||
the first line.
|
||||
"""
|
||||
'The title of the edit list'
|
||||
return self.title_statement.title
|
||||
|
||||
@@ -59,44 +76,81 @@ class Edit:
|
||||
|
||||
@property
|
||||
def channels(self):
|
||||
"""
|
||||
Get the :obj:`ChannelMap` object associated with this Edit.
|
||||
"""
|
||||
cm = ChannelMap()
|
||||
cm.append_event(self.edit_statement.channels)
|
||||
cm._append_event(self.edit_statement.channels)
|
||||
if self.audio_ext != None:
|
||||
cm.append_ext(self.audio_ext)
|
||||
cm._append_ext(self.audio_ext)
|
||||
return cm
|
||||
|
||||
@property
|
||||
def transition(self):
|
||||
"""
|
||||
Get the :obj:`Transition` object associated with this edit.
|
||||
"""
|
||||
return Transition(self.edit_statement.trans, self.edit_statement.trans_op)
|
||||
|
||||
@property
|
||||
def source_in(self):
|
||||
"""
|
||||
Get the source in timecode.
|
||||
"""
|
||||
return self.edit_statement.source_in
|
||||
|
||||
@property
|
||||
def source_out(self):
|
||||
"""
|
||||
Get the source out timecode.
|
||||
"""
|
||||
|
||||
return self.edit_statement.source_out
|
||||
|
||||
@property
|
||||
def record_in(self):
|
||||
"""
|
||||
Get the record in timecode.
|
||||
"""
|
||||
|
||||
return self.edit_statement.record_in
|
||||
|
||||
@property
|
||||
def record_out(self):
|
||||
"""
|
||||
Get the record out timecode.
|
||||
"""
|
||||
|
||||
return self.edit_statement.record_out
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
"""
|
||||
Get the source column. This is the 8, 32 or 128-character string on the
|
||||
event record line, this usually references the tape name of the source.
|
||||
"""
|
||||
return self.edit_statement.source
|
||||
|
||||
|
||||
@property
|
||||
def source_file(self):
|
||||
return self.source_file_statement.filename
|
||||
"""
|
||||
Get the source file, as attested by a "* SOURCE FILE" remark on the
|
||||
EDL. This will return None if the information is not present.
|
||||
"""
|
||||
if self.source_file_statement is None:
|
||||
return None
|
||||
else:
|
||||
return self.source_file_statement.filename
|
||||
|
||||
|
||||
@property
|
||||
def clip_name(self):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
if self.clip_name_statement != None:
|
||||
return self.clip_name_statement.name
|
||||
else:
|
||||
@@ -110,10 +164,16 @@ class Event:
|
||||
|
||||
@property
|
||||
def number(self):
|
||||
return self._edit_statements()[0].event
|
||||
"""Return the event number."""
|
||||
return int(self._edit_statements()[0].event)
|
||||
|
||||
@property
|
||||
def edits(self):
|
||||
"""
|
||||
Returns the edits. Most events will have a single edit, a single 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()
|
||||
@@ -170,12 +230,18 @@ class Transition:
|
||||
"""Represents a CMX transition, a wipe, dissolve or cut."""
|
||||
|
||||
Cut = "C"
|
||||
|
||||
Dissolve = "D"
|
||||
|
||||
Wipe = "W"
|
||||
|
||||
KeyBackground = "KB"
|
||||
|
||||
Key = "K"
|
||||
|
||||
KeyOut = "KO"
|
||||
|
||||
|
||||
def __init__(self, transition, operand):
|
||||
self.transition = transition
|
||||
self.operand = operand
|
||||
@@ -184,6 +250,9 @@ class Transition:
|
||||
|
||||
@property
|
||||
def kind(self):
|
||||
"""
|
||||
Return the kind of transition: Cut, Wipe, etc
|
||||
"""
|
||||
if self.cut:
|
||||
return Transition.Cut
|
||||
elif self.dissolve:
|
||||
@@ -216,7 +285,7 @@ class Transition:
|
||||
|
||||
@property
|
||||
def effect_duration(self):
|
||||
""""`The duration of this transition, in frames of the record target.
|
||||
"""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.
|
||||
"""
|
||||
|
||||
@@ -23,14 +23,16 @@ StmtMotionMemory = namedtuple("MotionMemory",["source","fps"]) # FIXME needs mor
|
||||
StmtUnrecognized = namedtuple("Unrecognized",["content","line_number"])
|
||||
|
||||
|
||||
def parse_cmx3600_statements(path):
|
||||
with open(path,'r') as file:
|
||||
lines = file.readlines()
|
||||
line_numbers = count()
|
||||
return [parse_cmx3600_line(line.strip(), line_number) \
|
||||
for (line, line_number) in zip(lines,line_numbers)]
|
||||
def parse_cmx3600_statements(file):
|
||||
"""
|
||||
Return a list of every statement in the file argument.
|
||||
"""
|
||||
lines = file.readlines()
|
||||
line_numbers = count()
|
||||
return [_parse_cmx3600_line(line.strip(), line_number) \
|
||||
for (line, line_number) in zip(lines,line_numbers)]
|
||||
|
||||
def edl_column_widths(event_field_length, source_field_length):
|
||||
def _edl_column_widths(event_field_length, source_field_length):
|
||||
return [event_field_length,2, source_field_length,1,
|
||||
4,2, # chans
|
||||
4,1, # trans
|
||||
@@ -40,63 +42,63 @@ def edl_column_widths(event_field_length, source_field_length):
|
||||
11,1,
|
||||
11]
|
||||
|
||||
def edl_m2_column_widths():
|
||||
def _edl_m2_column_widths():
|
||||
return [2, # "M2"
|
||||
3,3, #
|
||||
8,8,1,4,2,1,4,13,3,1,1]
|
||||
|
||||
|
||||
def parse_cmx3600_line(line, line_number):
|
||||
def _parse_cmx3600_line(line, line_number):
|
||||
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)
|
||||
return _parse_title(line,line_number)
|
||||
elif line.startswith("FCM:"):
|
||||
return parse_fcm(line, line_number)
|
||||
return _parse_fcm(line, line_number)
|
||||
elif long_event_num_p.match(line) != None:
|
||||
length_file_128 = sum(edl_column_widths(6,128))
|
||||
length_file_128 = sum(_edl_column_widths(6,128))
|
||||
if len(line) < length_file_128:
|
||||
return parse_long_standard_form(line, 32, line_number)
|
||||
return _parse_long_standard_form(line, 32, line_number)
|
||||
else:
|
||||
return parse_long_standard_form(line, 128, line_number)
|
||||
return _parse_long_standard_form(line, 128, line_number)
|
||||
elif short_event_num_p.match(line) != None:
|
||||
return parse_standard_form(line, line_number)
|
||||
return _parse_standard_form(line, line_number)
|
||||
elif line.startswith("AUD"):
|
||||
return parse_extended_audio_channels(line,line_number)
|
||||
return _parse_extended_audio_channels(line,line_number)
|
||||
elif line.startswith("*"):
|
||||
return parse_remark( line[1:].strip(), line_number)
|
||||
return _parse_remark( line[1:].strip(), line_number)
|
||||
elif line.startswith(">>>"):
|
||||
return parse_trailer_statement(line, line_number)
|
||||
return _parse_trailer_statement(line, line_number)
|
||||
elif line.startswith("EFFECTS NAME IS"):
|
||||
return parse_effects_name(line, line_number)
|
||||
return _parse_effects_name(line, line_number)
|
||||
elif line.startswith("SPLIT:"):
|
||||
return parse_split(line, line_number)
|
||||
return _parse_split(line, line_number)
|
||||
elif line.startswith("M2"):
|
||||
return parse_motion_memory(line, line_number)
|
||||
return _parse_motion_memory(line, line_number)
|
||||
else:
|
||||
return parse_unrecognized(line, line_number)
|
||||
return _parse_unrecognized(line, line_number)
|
||||
|
||||
|
||||
def parse_title(line, line_num):
|
||||
def _parse_title(line, line_num):
|
||||
title = line[6:].strip()
|
||||
return StmtTitle(title=title,line_number=line_num)
|
||||
|
||||
def parse_fcm(line, line_num):
|
||||
def _parse_fcm(line, line_num):
|
||||
val = line[4:].strip()
|
||||
if val == "DROP FRAME":
|
||||
return StmtFCM(drop= True, line_number=line_num)
|
||||
else:
|
||||
return StmtFCM(drop= False, line_number=line_num)
|
||||
|
||||
def parse_long_standard_form(line,source_field_length, line_number):
|
||||
return parse_columns_for_standard_form(line, 6, source_field_length, line_number)
|
||||
def _parse_long_standard_form(line,source_field_length, line_number):
|
||||
return _parse_columns_for_standard_form(line, 6, source_field_length, line_number)
|
||||
|
||||
def parse_standard_form(line, line_number):
|
||||
return parse_columns_for_standard_form(line, 3, 8, line_number)
|
||||
def _parse_standard_form(line, line_number):
|
||||
return _parse_columns_for_standard_form(line, 3, 8, line_number)
|
||||
|
||||
def parse_extended_audio_channels(line, line_number):
|
||||
def _parse_extended_audio_channels(line, line_number):
|
||||
content = line.strip()
|
||||
if content == "AUD 3":
|
||||
return StmtAudioExt(audio3=True, audio4=False, line_number=line_number)
|
||||
@@ -107,7 +109,7 @@ def parse_extended_audio_channels(line, line_number):
|
||||
else:
|
||||
return StmtUnrecognized(content=line, line_number=line_number)
|
||||
|
||||
def parse_remark(line, line_number):
|
||||
def _parse_remark(line, line_number):
|
||||
if line.startswith("FROM CLIP NAME:"):
|
||||
return StmtClipName(name=line[15:].strip() , affect="from", line_number=line_number)
|
||||
elif line.startswith("TO CLIP NAME:"):
|
||||
@@ -117,11 +119,11 @@ def parse_remark(line, line_number):
|
||||
else:
|
||||
return StmtRemark(text=line, line_number=line_number)
|
||||
|
||||
def parse_effects_name(line, line_number):
|
||||
def _parse_effects_name(line, line_number):
|
||||
name = line[16:].strip()
|
||||
return StmtEffectsName(name=name, line_number=line_number)
|
||||
|
||||
def parse_split(line, line_number):
|
||||
def _parse_split(line, line_number):
|
||||
split_type = line[10:21]
|
||||
is_video = False
|
||||
if split_type.startswith("VIDEO"):
|
||||
@@ -131,15 +133,15 @@ def parse_split(line, line_number):
|
||||
return StmtSplitEdit(video=is_video, magnitude=split_mag, line_number=line_number)
|
||||
|
||||
|
||||
def parse_motion_memory(line, line_number):
|
||||
def _parse_motion_memory(line, line_number):
|
||||
return StmtMotionMemory(source = "", fps="")
|
||||
|
||||
|
||||
def parse_unrecognized(line, line_number):
|
||||
def _parse_unrecognized(line, line_number):
|
||||
return StmtUnrecognized(content=line, line_number=line_number)
|
||||
|
||||
def parse_columns_for_standard_form(line, event_field_length, source_field_length, line_number):
|
||||
col_widths = edl_column_widths(event_field_length, source_field_length)
|
||||
def _parse_columns_for_standard_form(line, event_field_length, source_field_length, line_number):
|
||||
col_widths = _edl_column_widths(event_field_length, source_field_length)
|
||||
|
||||
if sum(col_widths) > len(line):
|
||||
return StmtUnrecognized(content=line, line_number=line_number)
|
||||
@@ -158,7 +160,7 @@ def parse_columns_for_standard_form(line, event_field_length, source_field_lengt
|
||||
line_number=line_number)
|
||||
|
||||
|
||||
def parse_trailer_statement(line, line_number):
|
||||
def _parse_trailer_statement(line, line_number):
|
||||
trimmed = line[3:].strip()
|
||||
return StmtTrailer(trimmed, line_number=line_number)
|
||||
|
||||
|
||||
@@ -4,7 +4,22 @@
|
||||
# Utility functions
|
||||
|
||||
def collimate(a_string, column_widths):
|
||||
'Splits a string into substrings that are column_widths length.'
|
||||
"""
|
||||
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.
|
||||
|
||||
Returns:
|
||||
A list of slices. The len() of the returned list will *always* equal
|
||||
len(:column_widths:).
|
||||
"""
|
||||
|
||||
if len(column_widths) == 0:
|
||||
return []
|
||||
@@ -14,51 +29,3 @@ def collimate(a_string, column_widths):
|
||||
rest = a_string[width:]
|
||||
return [element] + collimate(rest, column_widths[1:])
|
||||
|
||||
|
||||
class NamedTupleParser:
|
||||
"""
|
||||
Accepts a list of namedtuple and the client can step through the list with
|
||||
parser operations such as `accept()` and `expect()`
|
||||
"""
|
||||
|
||||
def __init__(self, tuple_list):
|
||||
self.tokens = tuple_list
|
||||
self.current_token = None
|
||||
|
||||
def peek(self):
|
||||
"""
|
||||
Returns the token to come after the `current_token` without
|
||||
popping the current token.
|
||||
"""
|
||||
return self.tokens[0]
|
||||
|
||||
def at_end(self):
|
||||
"`True` if the `current_token` is the last one."
|
||||
return len(self.tokens) == 0
|
||||
|
||||
def next_token(self):
|
||||
"Sets `current_token` to the next token popped from the list"
|
||||
self.current_token = self.peek()
|
||||
self.tokens = self.tokens[1:]
|
||||
|
||||
def accept(self, type_name):
|
||||
"""
|
||||
If the next token.__name__ is `type_name`, returns true and advances
|
||||
to the next token with `next_token()`.
|
||||
"""
|
||||
if self.at_end():
|
||||
return False
|
||||
elif (type(self.peek()).__name__ == type_name ):
|
||||
self.next_token()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def expect(self, type_name):
|
||||
"""
|
||||
If the next token.__name__ is `type_name`, the parser is advanced.
|
||||
If it is not, an assertion failure occurs.
|
||||
"""
|
||||
assert( self.accept(type_name) )
|
||||
|
||||
|
||||
|
||||
@@ -14,61 +14,61 @@ class TestParse(TestCase):
|
||||
|
||||
counts = [ 287, 466, 250 , 376, 120 ]
|
||||
|
||||
|
||||
for fn, count in zip(files, counts):
|
||||
edl = pycmx.parse_cmx3600(f"tests/edls/{fn}" )
|
||||
actual = len(list(edl.events ))
|
||||
self.assertTrue( actual == count , f"expected {count} in file {fn} but found {actual}")
|
||||
with open(f"tests/edls/{fn}" ,'r') as f:
|
||||
edl = pycmx.parse_cmx3600(f)
|
||||
actual = len(list(edl.events ))
|
||||
self.assertTrue( actual == count , f"expected {count} in file {fn} but found {actual}")
|
||||
|
||||
|
||||
def test_events(self):
|
||||
edl = pycmx.parse_cmx3600("tests/edls/TEST.edl")
|
||||
events = list( edl.events )
|
||||
|
||||
self.assertEqual( int(events[0].number) , 1)
|
||||
self.assertEqual( events[0].edits[0].source , "OY_HEAD_")
|
||||
self.assertEqual( events[0].edits[0].clip_name , "HEAD LEADER MONO")
|
||||
self.assertEqual( events[0].edits[0].source_file , "OY_HEAD_LEADER.MOV")
|
||||
self.assertEqual( events[0].edits[0].source_in , "00:00:00:00")
|
||||
self.assertEqual( events[0].edits[0].source_out , "00:00:00:00")
|
||||
self.assertEqual( events[0].edits[0].record_in , "01:00:00:00")
|
||||
self.assertEqual( events[0].edits[0].record_out , "01:00:08:00")
|
||||
self.assertTrue( events[0].edits[0].transition.kind == pycmx.Transition.Cut)
|
||||
with open("tests/edls/TEST.edl",'r') as f:
|
||||
edl = pycmx.parse_cmx3600(f)
|
||||
events = list( edl.events )
|
||||
|
||||
self.assertEqual( int(events[0].number) , 1)
|
||||
self.assertEqual( events[0].edits[0].source , "OY_HEAD_")
|
||||
self.assertEqual( events[0].edits[0].clip_name , "HEAD LEADER MONO")
|
||||
self.assertEqual( events[0].edits[0].source_file , "OY_HEAD_LEADER.MOV")
|
||||
self.assertEqual( events[0].edits[0].source_in , "00:00:00:00")
|
||||
self.assertEqual( events[0].edits[0].source_out , "00:00:00:00")
|
||||
self.assertEqual( events[0].edits[0].record_in , "01:00:00:00")
|
||||
self.assertEqual( events[0].edits[0].record_out , "01:00:08:00")
|
||||
self.assertTrue( events[0].edits[0].transition.kind == pycmx.Transition.Cut)
|
||||
|
||||
def test_channel_mop(self):
|
||||
edl = pycmx.parse_cmx3600("tests/edls/TEST.edl")
|
||||
events = list( edl.events )
|
||||
self.assertFalse( events[0].edits[0].channels.video)
|
||||
self.assertFalse( events[0].edits[0].channels.a1)
|
||||
self.assertTrue( events[0].edits[0].channels.a2)
|
||||
self.assertTrue( events[2].edits[0].channels.get_audio_channel(7) )
|
||||
with open("tests/edls/TEST.edl",'r') as f:
|
||||
edl = pycmx.parse_cmx3600(f)
|
||||
events = list( edl.events )
|
||||
self.assertFalse( events[0].edits[0].channels.video)
|
||||
self.assertFalse( events[0].edits[0].channels.a1)
|
||||
self.assertTrue( events[0].edits[0].channels.a2)
|
||||
self.assertTrue( events[2].edits[0].channels.get_audio_channel(7) )
|
||||
|
||||
|
||||
def test_multi_edit_events(self):
|
||||
edl = pycmx.parse_cmx3600("tests/edls/TEST.edl")
|
||||
events = list( edl.events )
|
||||
with open("tests/edls/TEST.edl",'r') as f:
|
||||
edl = pycmx.parse_cmx3600(f)
|
||||
events = list( edl.events )
|
||||
|
||||
self.assertEqual( int(events[42].number) , 43)
|
||||
self.assertEqual( len(events[42].edits), 2)
|
||||
|
||||
self.assertEqual( int(events[42].number) , 43)
|
||||
self.assertEqual( len(events[42].edits), 2)
|
||||
self.assertEqual( events[42].edits[0].source , "TC_R1_V1")
|
||||
self.assertEqual( events[42].edits[0].clip_name , "TC R1 V1.2 TEMP1 FX ST.WAV")
|
||||
self.assertEqual( events[42].edits[0].source_in , "00:00:00:00")
|
||||
self.assertEqual( events[42].edits[0].source_out , "00:00:00:00")
|
||||
self.assertEqual( events[42].edits[0].record_in , "01:08:56:09")
|
||||
self.assertEqual( events[42].edits[0].record_out , "01:08:56:09")
|
||||
self.assertTrue( events[42].edits[0].transition.kind == pycmx.Transition.Cut)
|
||||
|
||||
|
||||
self.assertEqual( events[42].edits[0].source , "TC_R1_V1")
|
||||
self.assertEqual( events[42].edits[0].clip_name , "TC R1 V1.2 TEMP1 FX ST.WAV")
|
||||
self.assertEqual( events[42].edits[0].source_in , "00:00:00:00")
|
||||
self.assertEqual( events[42].edits[0].source_out , "00:00:00:00")
|
||||
self.assertEqual( events[42].edits[0].record_in , "01:08:56:09")
|
||||
self.assertEqual( events[42].edits[0].record_out , "01:08:56:09")
|
||||
self.assertTrue( events[42].edits[0].transition.kind == pycmx.Transition.Cut)
|
||||
|
||||
|
||||
self.assertEqual( events[42].edits[1].source , "TC_R1_V6")
|
||||
self.assertEqual( events[42].edits[1].clip_name , "TC R1 V6 TEMP2 ST FX.WAV")
|
||||
self.assertEqual( events[42].edits[1].source_in , "00:00:00:00")
|
||||
self.assertEqual( events[42].edits[1].source_out , "00:00:00:00")
|
||||
self.assertEqual( events[42].edits[1].record_in , "01:08:56:09")
|
||||
self.assertEqual( events[42].edits[1].record_out , "01:08:56:11")
|
||||
self.assertTrue( events[42].edits[1].transition.kind == pycmx.Transition.Dissolve)
|
||||
self.assertEqual( events[42].edits[1].source , "TC_R1_V6")
|
||||
self.assertEqual( events[42].edits[1].clip_name , "TC R1 V6 TEMP2 ST FX.WAV")
|
||||
self.assertEqual( events[42].edits[1].source_in , "00:00:00:00")
|
||||
self.assertEqual( events[42].edits[1].source_out , "00:00:00:00")
|
||||
self.assertEqual( events[42].edits[1].record_in , "01:08:56:09")
|
||||
self.assertEqual( events[42].edits[1].record_out , "01:08:56:11")
|
||||
self.assertTrue( events[42].edits[1].transition.kind == pycmx.Transition.Dissolve)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user