26 Commits
v0.3 ... v0.5

Author SHA1 Message Date
Jamie Hardt
57ea48e5e8 Improved __repr__() methods 2018-12-05 16:49:54 -08:00
Jamie Hardt
7e13978d9a Added line numbers to statement parser 2018-12-05 16:36:46 -08:00
Jamie Hardt
f358704139 Travis 2018-12-05 16:19:14 -08:00
Jamie Hardt
6201633956 Travis 2018-12-05 12:17:30 -08:00
Jamie Hardt
2924ea548b Travis tweak 2018-12-05 12:14:55 -08:00
Jamie Hardt
88bf68c78e .travis.yml 2018-12-05 12:03:34 -08:00
Jamie Hardt
6d1ca12e42 Some work in the travis file and adding line numbers to parsing statements 2018-12-05 11:59:00 -08:00
Jamie Hardt
829d98f4b4 Tweaking travis.yml 2018-12-05 11:46:48 -08:00
Jamie Hardt
8969e31969 Tweaking travis.yml 2018-12-05 11:43:46 -08:00
Jamie Hardt
484d2ae98f Tweaking travis.yml 2018-12-05 11:41:15 -08:00
Jamie Hardt
07652eaaa8 Tweaking travis.yml 2018-12-05 11:38:37 -08:00
Jamie Hardt
a9124e1f97 Adding .travis.yml 2018-12-05 11:35:33 -08:00
Jamie Hardt
7e709241f8 Revert "Adding a travis config script"
This reverts commit 597dceb9c5.
2018-12-05 11:35:33 -08:00
Jamie Hardt
731b8fcc00 Added Travis Status image 2018-12-05 11:31:38 -08:00
Jamie Hardt
597dceb9c5 Adding a travis config script 2018-12-05 11:23:10 -08:00
Jamie Hardt
c2c83d826a Added version symbol to __init__ 2018-12-02 21:04:39 -08:00
Jamie Hardt
08dd1f956d Implemented better logic for reading event-modifying lines 2018-12-01 15:33:50 -08:00
Jamie Hardt
989d52aaee CmxEvent and CmxTransition implementation 2018-12-01 15:15:09 -08:00
Jamie Hardt
b60610aa8b Some infrastructure for CmxTransitions 2018-12-01 14:35:26 -08:00
Jamie Hardt
6611e38b9f modified copyright notices 2018-12-01 14:16:54 -08:00
Jamie Hardt
abcce06865 Added some more test files from other projects 2018-12-01 13:52:26 -08:00
Jamie Hardt
9d586342be Roadmap note 2018-12-01 13:10:10 -08:00
Jamie Hardt
e5f632d8a4 Updated setup, mnudge to version 0.5 2018-12-01 13:08:16 -08:00
Jamie Hardt
30cd99431b updated code example 2018-12-01 13:05:58 -08:00
Jamie Hardt
cc76223cbc Tweaked tests to catch my previous error
Also nudged version number
2018-12-01 12:48:41 -08:00
Jamie Hardt
966f8c1ca4 Fixed another audio channel parsing bug 2018-12-01 12:46:10 -08:00
12 changed files with 366 additions and 103 deletions

7
.travis.yml Normal file
View File

@@ -0,0 +1,7 @@
language: python
python:
- "3.6"
script:
- "python3 setup.py test"
install:
- "pip3 install setuptools"

View File

@@ -1,3 +1,5 @@
[![Build Status](https://travis-ci.com/iluvcapra/pycmx.svg?branch=master)](https://travis-ci.com/iluvcapra/pycmx)
# pycmx
The `pycmx` package provides a basic interface for parsing a CMX 3600 EDL and its most most common variations.
@@ -12,27 +14,29 @@ The `pycmx` package provides a basic interface for parsing a CMX 3600 EDL and it
## Usage
```
import pycmx
pycmx.parse_cmx3600("INS4_R1_010417.edl")
print(events[5:8])
>>> [CmxEvent(title='INS4_R1_010417', number='000006',
>>> import pycmx
>>> events = pycmx.parse_cmx3600("INS4_R1_010417.edl")
>>> print(events[5:8])
[CmxEvent(title='INS4_R1_010417', number='000006',
clip_name='V1A-6A', source_name='A192C008_160909_R1BY',
channels=CmxChannelMap(v=True,a1=False,a2=False,a3=False,a4=False),
channels=CmxChannelMap(v=True, audio_channels=set()),
source_start='19:26:38:13', source_finish='19:27:12:03',
record_start='01:00:57:15', record_finish='01:01:31:05',
fcm_drop=False),
CmxEvent(title='INS4_R1_010417', number='000007',
clip_name='1-4A', source_name='A188C004_160908_R1BY',
channels=CmxChannelMap(v=True,a1=False,a2=False,a3=False,a4=False),
channels=CmxChannelMap(v=True, audio_channels=set()),
source_start='19:29:48:01', source_finish='19:30:01:00',
record_start='01:01:31:05', record_finish='01:01:44:04',
fcm_drop=False),
CmxEvent(title='INS4_R1_010417', number='000008',
clip_name='2G-3', source_name='A056C007_160819_R1BY',
channels=CmxChannelMap(v=True,a1=False,a2=False,a3=False,a4=False),
channels=CmxChannelMap(v=True, audio_channels=set()),
source_start='19:56:27:14', source_finish='19:56:41:00',
record_start='01:01:44:04', record_finish='01:01:57:14',
fcm_drop=False)]
```
## Known Issues/Roadmap
@@ -41,6 +45,7 @@ To be addressed:
* Does not decode transitions.
* Does not decode "M2" speed changes.
* Does not decode repair notes, audio notes or other Avid-specific notes.
* Does not decode Avid marker list.
May not be addressed:

View File

@@ -1,3 +1,5 @@
# pycmx init
from .parse_cmx import parse_cmx3600
__version__ = '0.5'

View File

@@ -1,11 +1,14 @@
class CmxEvent:
def __init__(self,title,number,clip_name,source_name,channels,source_start,source_finish,
record_start, record_finish, fcm_drop, remarks = [] , unrecognized = []):
def __init__(self,title,number,clip_name,source_name,channels,
transition,source_start,source_finish,
record_start, record_finish, fcm_drop, remarks = [] ,
unrecognized = [], line_number = None):
self.title = title
self.number = number
self.clip_name = clip_name
self.source_name = source_name
self.channels = channels
self.transition = transition
self.source_start = source_start
self.source_finish = source_finish
self.record_start = record_start
@@ -13,5 +16,79 @@ class CmxEvent:
self.fcm_drop = fcm_drop
self.remarks = remarks
self.unrecgonized = unrecognized
self.black = (source_name == 'BL')
self.aux_source = (source_name == 'AX')
self.line_number = line_number
def can_accept(self):
return {'AudioExt','Remark','SourceFile','ClipName','EffectsName'}
def accept_statement(self, statement):
statement_type = type(statement).__name__
if statement_type == 'AudioExt':
self.channels.appendExt(statement)
elif statement_type == 'Remark':
self.remarks.append(statement.text)
elif statement_type == 'SourceFile':
self.source_name = statement.filename
elif statement_type == 'ClipName':
self.clip_name = statement.name
elif statement_type == 'EffectsName':
self.transition.name = statement.name
def __repr__(self):
return f"""CmxEvent(title={self.title.__repr__()},number={self.number.__repr__()},\
clip_name={self.clip_name.__repr__()},source_name={self.source_name.__repr__()},\
channels={self.channels.__repr__()},transition={self.transition.__repr__()},\
source_start={self.source_start.__repr__()},source_finish={self.source_finish.__repr__()},\
record_start={self.source_start.__repr__()},record_finish={self.record_finish.__repr__()},\
fcm_drop={self.fcm_drop.__repr__()},remarks={self.remarks.__repr__()},line_number={self.line_number.__repr__()})"""
class CmxTransition:
def __init__(self, transition, operand):
self.transition = transition
self.operand = operand
self.name = ''
@property
def cut(self):
return self.transition == 'C'
@property
def dissolve(self):
return self.transition == 'D'
@property
def wipe(self):
return self.transition.startswith('W')
@property
def effect_duration(self):
return int(self.operand)
@property
def wipe_number(self):
if self.wipe:
return int(self.transition[1:])
else:
return None
@property
def key_background(self):
return self.transition == 'KB'
@property
def key_foreground(self):
return self.transition == 'K'
@property
def key_out(self):
return self.transition == 'KO'
def __repr__(self):
return f"""CmxTransition(transition={self.transition.__repr__()},operand={self.operand.__repr__()})"""

View File

@@ -2,6 +2,7 @@
# (c) 2018 Jamie Hardt
from .parse_cmx_statements import parse_cmx3600_statements
from .cmx_event import CmxEvent, CmxTransition
from collections import namedtuple
from re import compile, match
@@ -109,19 +110,12 @@ class CmxChannelMap:
if matchresult:
self.set_audio_channel(int( matchresult.group(1)), True )
def appendExt(self, audio_ext):
self.a3 = ext.audio3
self.a4 = ext.audio4
def __repr__(self):
return "CmxChannelMap(v="+ self.v.__repr__( ) + \
",a1=" + self.a1.__repr__() + \
",a2=" + self.a2.__repr__() + \
",a3=" + self.a3.__repr__() + \
",a4=" + self.a4.__repr__() +")"
return f"CmxChannelMap(v={self.v.__repr__()}, audio_channels={self._audio_channel_set.__repr__()})"
def parse_cmx3600(file):
@@ -133,11 +127,6 @@ def parse_cmx3600(file):
return event_list(title, parser)
CmxEvent = namedtuple('CmxEvent',['title','number','clip_name',
'source_name','channels','source_start','source_finish','record_start',
'record_finish','fcm_drop'])
def event_list(title, parser):
state = {"fcm_drop" : False}
@@ -149,35 +138,33 @@ def event_list(title, parser):
state['fcm_drop'] = parser.current_token.drop
elif parser.accept('Event'):
if this_event != None:
event_t = CmxEvent(**this_event)
events_result.append(event_t)
events_result.append(this_event)
raw_event = parser.current_token
channels = CmxChannelMap({})
channels = CmxChannelMap(v=False, audio_channels=set([]))
channels.appendEvent(raw_event.channels)
this_event = {'title': title, 'number': raw_event.event, 'clip_name': None ,
'source_name': raw_event.source,
'channels': channels,
'source_start': raw_event.source_in,
'source_finish': raw_event.source_out,
'record_start': raw_event.record_in,
'record_finish': raw_event.record_out,
'fcm_drop': state['fcm_drop']}
elif parser.accept('AudioExt'):
this_event['channels'].appendExt(parser.current_token)
elif parser.accept('ClipName'):
this_event['clip_name'] = parser.current_token.name
elif parser.accept('SourceFile'):
this_event['source_name'] = parser.current_token.filename
this_event = CmxEvent(title=title,number=int(raw_event.event), clip_name=None ,
source_name=raw_event.source,
channels=channels,
transition=CmxTransition(raw_event.trans, raw_event.trans_op),
source_start= raw_event.source_in,
source_finish= raw_event.source_out,
record_start= raw_event.record_in,
record_finish= raw_event.record_out,
fcm_drop= state['fcm_drop'],
line_number = raw_event.line_number)
elif parser.accept('AudioExt') or parser.accept('ClipName') or \
parser.accept('SourceFile') or parser.accept('EffectsName') or \
parser.accept('Remark'):
this_event.accept_statement(parser.current_token)
elif parser.accept('Trailer'):
break
else:
parser.next_token()
if this_event != None:
event_t = CmxEvent(**this_event)
events_result.append(event_t)
events_result.append(this_event)
return events_result

View File

@@ -7,23 +7,26 @@ from .util import collimate
import re
import sys
from collections import namedtuple
from itertools import count
StmtTitle = namedtuple("Title",["title"])
StmtFCM = namedtuple("FCM",["drop"])
StmtEvent = namedtuple("Event",["event","source","channels","trans","trans_op","source_in","source_out","record_in","record_out"])
StmtAudioExt = namedtuple("AudioExt",["audio3","audio4"])
StmtClipName = namedtuple("ClipName",["name"])
StmtSourceFile = namedtuple("SourceFile",["filename"])
StmtRemark = namedtuple("Remark",["text"])
StmtTrailer = namedtuple("Trailer",["text"])
StmtUnrecognized = namedtuple("Unrecognized",["content"])
StmtTitle = namedtuple("Title",["title","line_number"])
StmtFCM = namedtuple("FCM",["drop","line_number"])
StmtEvent = namedtuple("Event",["event","source","channels","trans","trans_op","source_in","source_out","record_in","record_out","line_number"])
StmtAudioExt = namedtuple("AudioExt",["audio3","audio4","line_number"])
StmtClipName = namedtuple("ClipName",["name","line_number"])
StmtSourceFile = namedtuple("SourceFile",["filename","line_number"])
StmtRemark = namedtuple("Remark",["text","line_number"])
StmtEffectsName = namedtuple("EffectsName",["name","line_number"])
StmtTrailer = namedtuple("Trailer",["text","line_number"])
StmtUnrecognized = namedtuple("Unrecognized",["content","line_number"])
def parse_cmx3600_statements(path):
with open(path,'r') as file:
lines = file.readlines()
return [parse_cmx3600_line(line.strip()) for line in lines]
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):
return [event_field_length,2, source_field_length,1,
@@ -35,77 +38,83 @@ def edl_column_widths(event_field_length, source_field_length):
11,1,
11]
def parse_cmx3600_line(line):
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)
return parse_title(line,line_number)
elif line.startswith("FCM:"):
return parse_fcm(line)
return parse_fcm(line, line_number)
elif long_event_num_p.match(line) != None:
length_file_128 = sum(edl_column_widths(6,128))
if len(line) < length_file_128:
return parse_long_standard_form(line, 32)
return parse_long_standard_form(line, 32, line_number)
else:
return parse_long_standard_form(line, 128)
return parse_long_standard_form(line, 128, line_number)
elif short_event_num_p.match(line) != None:
return parse_standard_form(line)
return parse_standard_form(line, line_number)
elif line.startswith("AUD"):
return parse_extended_audio_channels(line)
return parse_extended_audio_channels(line,line_number)
elif line.startswith("*"):
return parse_remark( line[1:].strip())
return parse_remark( line[1:].strip(), line_number)
elif line.startswith(">>>"):
return parse_trailer_statement(line)
return parse_trailer_statement(line, line_number)
elif line.startswith("EFFECTS NAME IS"):
return parse_effects_name(line, line_number)
else:
return parse_unrecognized(line)
return parse_unrecognized(line, line_number)
def parse_title(line):
def parse_title(line, line_num):
title = line[6:].strip()
return StmtTitle(title=title)
return StmtTitle(title=title,line_number=line_num)
def parse_fcm(line):
def parse_fcm(line, line_num):
val = line[4:].strip()
if val == "DROP FRAME":
return StmtFCM(drop= True)
return StmtFCM(drop= True, line_number=line_num)
else:
return StmtFCM(drop= False)
return StmtFCM(drop= False, line_number=line_num)
def parse_long_standard_form(line,source_field_length):
return parse_columns_for_standard_form(line, 6, source_field_length)
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):
return parse_columns_for_standard_form(line, 3, 8)
def parse_standard_form(line, line_number):
return parse_columns_for_standard_form(line, 3, 8, line_number)
def parse_extended_audio_channels(line):
def parse_extended_audio_channels(line, line_number):
content = line.strip()
if content == "AUD 3":
return StmtAudioExt(audio3=True, audio4=False)
return StmtAudioExt(audio3=True, audio4=False, line_number=line_number)
elif content == "AUD 4":
return StmtAudioExt(audio3=False, audio4=True)
return StmtAudioExt(audio3=False, audio4=True, line_number=line_number)
elif content == "AUD 3 4":
return StmtAudioExt(audio3=True, audio4=True)
return StmtAudioExt(audio3=True, audio4=True, line_number=line_number)
else:
return StmtUnrecognized(content=line)
return StmtUnrecognized(content=line, line_number=line_number)
def parse_remark(line):
def parse_remark(line, line_number):
if line.startswith("FROM CLIP NAME:"):
return StmtClipName(name=line[15:].strip() )
return StmtClipName(name=line[15:].strip() , line_number=line_number)
elif line.startswith("SOURCE FILE:"):
return StmtSourceFile(filename=line[12:].strip() )
return StmtSourceFile(filename=line[12:].strip() , line_number=line_number)
else:
return StmtRemark(text=line)
return StmtRemark(text=line, line_number=line_number)
def parse_unrecognized(line):
return StmtUnrecognized(content=line)
def parse_effects_name(line, line_number):
name = line[16:].strip()
return StmtEffectsName(name=name, line_number=line_number)
def parse_columns_for_standard_form(line, event_field_length, source_field_length):
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)
if sum(col_widths) > len(line):
return StmtUnrecognized(content=line)
return StmtUnrecognized(content=line, line_number=line_number)
column_strings = collimate(line,col_widths)
@@ -117,10 +126,11 @@ def parse_columns_for_standard_form(line, event_field_length, source_field_lengt
source_in=column_strings[10].strip(),
source_out=column_strings[12].strip(),
record_in=column_strings[14].strip(),
record_out=column_strings[16].strip())
record_out=column_strings[16].strip(),
line_number=line_number)
def parse_trailer_statement(line):
def parse_trailer_statement(line, line_number):
trimmed = line[3:].strip()
return StmtTrailer(trimmed)
return StmtTrailer(trimmed, line_number=line_number)

View File

@@ -4,14 +4,14 @@ with open("README.md", "r") as fh:
long_description = fh.read()
setup(name='pycmx',
version='0.3',
version='0.5',
author='Jamie Hardt',
author_email='jamiehardt@me.com',
description='CMX 3600 Edit Decision List Parser',
long_description_content_type="text/markdown",
long_description=long_description,
url='https://github.com/iluvcapra/pycmx',
classifiers=['Development Status :: 3 - Alpha',
classifiers=['Development Status :: 4 - Beta',
'License :: OSI Approved :: MIT License',
'Topic :: Multimedia',
'Topic :: Multimedia :: Video',

Binary file not shown.

60
tests/edls/test_24psf.edl Executable file
View File

@@ -0,0 +1,60 @@
TITLE: Test EDL 24
001 AX V C 01:00:00:00 01:00:59:24 00:00:00:00 00:00:59:24
* FROM CLIP NAME: clip 1
002 AX AA C 00:00:00:00 00:01:30:00 00:00:00:00 00:01:30:00
* FROM CLIP NAME: clip #2
003 AX V C 00:00:00:00 00:00:30:01 00:00:59:24 00:01:30:00
* FROM CLIP NAME: clip -3
AUD 3 4
004 AX V C 00:00:00:00 00:00:24:17 00:01:30:00 00:01:54:17
* FROM CLIP NAME: clip $4
005 AX V C 00:00:00:00 00:00:24:17 00:01:30:00 00:01:54:17
* FROM CLIP NAME: clip &5
006 AX AA C 00:00:29:01 00:00:29:01 00:01:59:01 00:01:59:01
006 BL AA W001 025 00:00:00:00 00:00:10:20 00:01:59:01 00:02:10:21
EFFECTS NAME IS Constant Power
* FROM CLIP NAME: Test rename
* TO CLIP NAME: BL
007 AX V C 01:00:00:00 01:00:05:00 00:02:01:10 00:02:06:10
* FROM CLIP NAME: Black Video
008 AX V C 01:00:10:14 01:00:15:00 00:02:06:10 00:02:10:21
* FROM CLIP NAME: Jellyfish.jpg
009 AX V C 00:00:00:00 00:00:30:01 00:02:10:21 00:02:40:22
* FROM CLIP NAME: Test rename
M2 AX -25.0 00:00:00:00
010 AX AA C 00:00:00:00 00:00:30:01 00:02:10:21 00:02:40:22
* FROM CLIP NAME: Test rename
M2 AX -25.0 00:00:00:00
REM The MIT License (MIT)
REM
REM Copyright (c) 2013 <simon@simon-hargreaves.com>
REM
REM Permission is hereby granted, free of charge, to any person obtaining a copy
REM of this software and associated documentation files (the "Software"), to deal
REM in the Software without restriction, including without limitation the rights
REM to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
REM copies of the Software, and to permit persons to whom the Software is
REM furnished to do so, subject to the following conditions:
REM
REM The above copyright notice and this permission notice shall be included in
REM all copies or substantial portions of the Software.
REM
REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
REM IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
REM FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
REM AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
REM LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
REM OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
REM THE SOFTWARE.
REM

66
tests/edls/test_25.edl Executable file
View File

@@ -0,0 +1,66 @@
TITLE: Sequence 01
001 AX V C 01:00:00:00 01:00:59:24 00:00:00:00 00:00:59:24
* FROM CLIP NAME: Jellyfish.jpg
002 AX AA C 00:00:00:00 00:01:30:00 00:00:00:00 00:01:30:00
* FROM CLIP NAME: N5_final screensaver_mp4.mov
003 AX V C 00:00:00:00 00:00:30:01 00:00:59:24 00:01:30:00
* FROM CLIP NAME: Test rename
AUD 3 4
004 AX V C 00:00:00:00 00:00:24:17 00:01:30:00 00:01:54:17
* FROM CLIP NAME: Test rename
005 AX V C 00:00:24:17 00:00:24:17 00:01:54:17 00:01:54:17
005 AX V D 070 00:59:58:21 01:00:05:14 00:01:54:17 00:02:01:10
EFFECTS NAME IS CROSS DISSOLVE
* FROM CLIP NAME: Test rename
* TO CLIP NAME: Jellyfish.jpg
006 AX AA C 00:00:00:00 00:00:29:01 00:01:30:00 00:01:59:01
* FROM CLIP NAME: Test rename
007 AX AA C 00:00:29:01 00:00:29:01 00:01:59:01 00:01:59:01
007 BL AA W001 025 00:00:00:00 00:00:10:20 00:01:59:01 00:02:10:21
EFFECTS NAME IS Constant Power
* FROM CLIP NAME: Test rename
* TO CLIP NAME: BL
008 AX V C 01:00:00:00 01:00:05:00 00:02:01:10 00:02:06:10
* FROM CLIP NAME: Black Video
009 AX V C 01:00:10:14 01:00:15:00 00:02:06:10 00:02:10:21
* FROM CLIP NAME: Jellyfish.jpg
010 AX V C 00:00:00:00 00:00:30:01 00:02:10:21 00:02:40:22
* FROM CLIP NAME: Test rename
M2 AX -25.0 00:00:00:00
011 AX AA C 00:00:00:00 00:00:30:01 00:02:10:21 00:02:40:22
* FROM CLIP NAME: Test rename
M2 AX -25.0 00:00:00:00
REM The MIT License (MIT)
REM
REM Copyright (c) 2013 <simon@simon-hargreaves.com>
REM
REM Permission is hereby granted, free of charge, to any person obtaining a copy
REM of this software and associated documentation files (the "Software"), to deal
REM in the Software without restriction, including without limitation the rights
REM to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
REM copies of the Software, and to permit persons to whom the Software is
REM furnished to do so, subject to the following conditions:
REM
REM The above copyright notice and this permission notice shall be included in
REM all copies or substantial portions of the Software.
REM
REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
REM IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
REM FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
REM AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
REM LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
REM OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
REM THE SOFTWARE.
REM

View File

@@ -0,0 +1,42 @@
TITLE: test_edl_cdl
FCM: NON-DROP FRAME
000001 BAAAAAAA_00001_ABCD V C 01:00:00:01 01:00:02:01 01:00:00:00 01:00:02:00
*ASC_SOP (0.8111 0.8112 0.8113)(0.2111 0.2112 0.2113)(1.8111 1.8112 1.8113)
*ASC_SAT 0.91
*Descript:
*FROM CLIP NAME: dra_001_0001_v0001
*LOC: 1:00:01:00 YELLOW DRA_001_0001
000002 BAAAAAAA_00001_ABCD V C 01:00:00:02 01:00:02:02 01:00:02:00 01:00:04:00
*ASC_SOP (0.8121 0.8122 0.8123)(0.2121 0.2122 0.2123)(1.8121 1.8122 1.8123)
*ASC_SAT 0.82
*Descript:
*FROM CLIP NAME: dra_001_0002_v0001
*LOC: 1:00:03:00 YELLOW DRA_001_0002
000003 BAAAAAAA_00001_ABCD V C 01:00:00:03 01:00:02:03 01:00:04:00 01:00:06:00
*ASC_SOP (0.8131 0.8132 0.8133)(0.2131 0.2132 0.2133)(1.8131 1.8132 1.8133)
*ASC_SAT 0.73
*Descript:
*FROM CLIP NAME: dra_001_0003_v0001
*LOC: 1:00:05:00 YELLOW DRA_001_0003
REM The MIT License (MIT)
REM
REM Copyright (c) 2014 Simon Hargreaves
REM
REM Permission is hereby granted, free of charge, to any person obtaining a copy
REM of this software and associated documentation files (the "Software"), to deal
REM in the Software without restriction, including without limitation the rights
REM to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
REM copies of the Software, and to permit persons to whom the Software is
REM furnished to do so, subject to the following conditions:
REM
REM The above copyright notice and this permission notice shall be included in all
REM copies or substantial portions of the Software.
REM
REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
REM IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
REM FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
REM AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
REM LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
REM OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
REM SOFTWARE.

View File

@@ -23,5 +23,12 @@ class TestParse(TestCase):
self.assertTrue(events[0].channels.a2)
self.assertFalse(events[0].channels.a1)
self.assertTrue(events[2].channels.get_audio_channel(7))
self.assertFalse(events[2].channels.get_audio_channel(1))
self.assertFalse(events[2].channels.get_audio_channel(2))
self.assertFalse(events[2].channels.get_audio_channel(3))
self.assertFalse(events[2].channels.get_audio_channel(4))
self.assertFalse(events[2].channels.get_audio_channel(10))
self.assertFalse(events[2].channels.get_audio_channel(11))
self.assertFalse(events[2].channels.get_audio_channel(12))
self.assertFalse(events[2].channels.get_audio_channel(13))