mirror of
https://github.com/iluvcapra/pycmx.git
synced 2025-12-31 17:00:53 +00:00
Deleted parse_cmx etc
Going to reimplement these
This commit is contained in:
@@ -1,137 +0,0 @@
|
|||||||
|
|
||||||
class CmxEvent:
|
|
||||||
"""Represents a source-record event.
|
|
||||||
|
|
||||||
Aside from exposing properites related to the raw CMX event itself,
|
|
||||||
(the `source_start`, `source_finish`, `transition` etc.) the event
|
|
||||||
also contains contextual information from the parsed CMX list, such as
|
|
||||||
`clip_name` and the frame counting mode in effect on the event.
|
|
||||||
"""
|
|
||||||
|
|
||||||
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
|
|
||||||
self.record_finish = record_finish
|
|
||||||
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 accept_statement(self, statement):
|
|
||||||
"""Used by the parser to attach clip names and notes to this event."""
|
|
||||||
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:
|
|
||||||
"""Represents a CMX transition, a wipe, dissolve or cut."""
|
|
||||||
|
|
||||||
Cut = "C"
|
|
||||||
Dissolve = "V"
|
|
||||||
Wipe = "W"
|
|
||||||
KeyBackground = "KB"
|
|
||||||
Key = "K"
|
|
||||||
KeyOut = "KO"
|
|
||||||
|
|
||||||
def __init__(self, transition, operand):
|
|
||||||
self.transition = transition
|
|
||||||
self.operand = operand
|
|
||||||
self.name = ''
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def kind(self):
|
|
||||||
if self.cut:
|
|
||||||
return Cut
|
|
||||||
elif self.dissove:
|
|
||||||
return Dissolve
|
|
||||||
elif self.wipe:
|
|
||||||
return Wipe
|
|
||||||
elif self.key_background:
|
|
||||||
return KeyBackground
|
|
||||||
elif self.key_foreground:
|
|
||||||
return Key
|
|
||||||
elif self.key_out:
|
|
||||||
return KeyOut
|
|
||||||
|
|
||||||
@property
|
|
||||||
def cut(self):
|
|
||||||
"`True` if this transition is a cut."
|
|
||||||
return self.transition == Cut
|
|
||||||
|
|
||||||
@property
|
|
||||||
def dissolve(self):
|
|
||||||
"`True` if this traansition is a dissolve."
|
|
||||||
return self.transition == Dissolve
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def wipe(self):
|
|
||||||
"`True` if this transition is a wipe."
|
|
||||||
return self.transition.startswith('W')
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def effect_duration(self):
|
|
||||||
""""`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)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def wipe_number(self):
|
|
||||||
"Wipes are identified by a particular number."
|
|
||||||
if self.wipe:
|
|
||||||
return int(self.transition[1:])
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@property
|
|
||||||
def key_background(self):
|
|
||||||
"`True` if this is a key background event."
|
|
||||||
return self.transition == KeyBackground
|
|
||||||
|
|
||||||
@property
|
|
||||||
def key_foreground(self):
|
|
||||||
"`True` if this is a key foreground event."
|
|
||||||
return self.transition == Key
|
|
||||||
|
|
||||||
@property
|
|
||||||
def key_out(self):
|
|
||||||
"`True` if this is a key out event."
|
|
||||||
return self.transition == KeyOut
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"""CmxTransition(transition={self.transition.__repr__()},operand={self.operand.__repr__()})"""
|
|
||||||
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
# pycmx
|
|
||||||
# (c) 2018 Jamie Hardt
|
|
||||||
|
|
||||||
from .util import NamedTupleParser
|
|
||||||
|
|
||||||
from .parse_cmx_statements import parse_cmx3600_statements
|
|
||||||
from .cmx_event import CmxEvent, CmxTransition
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
from re import compile, match
|
|
||||||
|
|
||||||
class CmxChannelMap:
|
|
||||||
"""
|
|
||||||
Represents a set of all the channels to which an event applies.
|
|
||||||
"""
|
|
||||||
|
|
||||||
chan_map = { "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.v = v
|
|
||||||
|
|
||||||
@property
|
|
||||||
def a1(self):
|
|
||||||
return self.get_audio_channel(1)
|
|
||||||
|
|
||||||
@a1.setter
|
|
||||||
def a1(self,val):
|
|
||||||
self.set_audio_channel(1,val)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def a2(self):
|
|
||||||
return self.get_audio_channel(2)
|
|
||||||
|
|
||||||
@a2.setter
|
|
||||||
def a2(self,val):
|
|
||||||
self.set_audio_channel(2,val)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def a3(self):
|
|
||||||
return self.get_audio_channel(3)
|
|
||||||
|
|
||||||
@a3.setter
|
|
||||||
def a3(self,val):
|
|
||||||
self.set_audio_channel(3,val)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def a4(self):
|
|
||||||
return self.get_audio_channel(4)
|
|
||||||
|
|
||||||
@a4.setter
|
|
||||||
def a4(self,val):
|
|
||||||
self.set_audio_channel(4,val)
|
|
||||||
|
|
||||||
def get_audio_channel(self,chan_num):
|
|
||||||
return (chan_num in self._audio_channel_set)
|
|
||||||
|
|
||||||
def set_audio_channel(self,chan_num,enabled):
|
|
||||||
if enabled:
|
|
||||||
self._audio_channel_set.add(chan_num)
|
|
||||||
elif self.get_audio_channel(chan_num):
|
|
||||||
self._audio_channel_set.remove(chan_num)
|
|
||||||
|
|
||||||
def appendEvent(self, event_str):
|
|
||||||
alt_channel_re = compile('^A(\d+)')
|
|
||||||
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]
|
|
||||||
else:
|
|
||||||
matchresult = match(alt_channel_re, event_str)
|
|
||||||
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 f"CmxChannelMap(v={self.v.__repr__()}, audio_channels={self._audio_channel_set.__repr__()})"
|
|
||||||
|
|
||||||
|
|
||||||
def parse_cmx3600(file):
|
|
||||||
"""Accepts the path to a CMX EDL and returns a list of all events contained therein."""
|
|
||||||
statements = parse_cmx3600_statements(file)
|
|
||||||
parser = NamedTupleParser(statements)
|
|
||||||
parser.expect('Title')
|
|
||||||
title = parser.current_token.title
|
|
||||||
return event_list(title, parser)
|
|
||||||
|
|
||||||
def event_list(title, parser):
|
|
||||||
state = {"fcm_drop" : False}
|
|
||||||
|
|
||||||
events_result = []
|
|
||||||
this_event = None
|
|
||||||
|
|
||||||
while not parser.at_end():
|
|
||||||
if parser.accept('FCM'):
|
|
||||||
state['fcm_drop'] = parser.current_token.drop
|
|
||||||
elif parser.accept('Event'):
|
|
||||||
if this_event != None:
|
|
||||||
events_result.append(this_event)
|
|
||||||
|
|
||||||
raw_event = parser.current_token
|
|
||||||
channels = CmxChannelMap(v=False, audio_channels=set([]))
|
|
||||||
channels.appendEvent(raw_event.channels)
|
|
||||||
|
|
||||||
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:
|
|
||||||
events_result.append(this_event)
|
|
||||||
|
|
||||||
return events_result
|
|
||||||
|
|
||||||
13
pycmx/parse_cmx_events.py
Normal file
13
pycmx/parse_cmx_events.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# pycmx
|
||||||
|
# (c) 2018 Jamie Hardt
|
||||||
|
|
||||||
|
def events(statements=[]):
|
||||||
|
if statements[0].
|
||||||
|
|
||||||
|
class Event:
|
||||||
|
def __init__(self, statements):
|
||||||
|
self.statements = statements
|
||||||
|
|
||||||
|
def number():
|
||||||
|
return statements[0].event
|
||||||
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
|
# pycmx
|
||||||
# Parsed Statement Data Structures
|
# (c) 2018 Jamie Hardt
|
||||||
#
|
|
||||||
# These represent individual lines that have been typed and have undergone some light symbolic parsing.
|
|
||||||
|
|
||||||
from .util import collimate
|
from .util import collimate
|
||||||
import re
|
import re
|
||||||
@@ -29,7 +27,8 @@ def parse_cmx3600_statements(path):
|
|||||||
with open(path,'r') as file:
|
with open(path,'r') as file:
|
||||||
lines = file.readlines()
|
lines = file.readlines()
|
||||||
line_numbers = count()
|
line_numbers = count()
|
||||||
return [parse_cmx3600_line(line.strip(), line_number) for (line, line_number) in zip(lines,line_numbers)]
|
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,
|
return [event_field_length,2, source_field_length,1,
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ def collimate(a_string, column_widths):
|
|||||||
|
|
||||||
class NamedTupleParser:
|
class NamedTupleParser:
|
||||||
"""
|
"""
|
||||||
Accepts a list of namedtuple and the client can step through the list with
|
Accepts a list of namedtuple and the client can step through the list with
|
||||||
parser operations such as `accept()` and `expect()`
|
parser operations such as `accept()` and `expect()`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, tuple_list):
|
def __init__(self, tuple_list):
|
||||||
@@ -26,8 +26,10 @@ parser operations such as `accept()` and `expect()`
|
|||||||
self.current_token = None
|
self.current_token = None
|
||||||
|
|
||||||
def peek(self):
|
def peek(self):
|
||||||
"""Returns the token to come after the `current_token` without
|
"""
|
||||||
popping the current token."""
|
Returns the token to come after the `current_token` without
|
||||||
|
popping the current token.
|
||||||
|
"""
|
||||||
return self.tokens[0]
|
return self.tokens[0]
|
||||||
|
|
||||||
def at_end(self):
|
def at_end(self):
|
||||||
@@ -40,8 +42,10 @@ popping the current token."""
|
|||||||
self.tokens = self.tokens[1:]
|
self.tokens = self.tokens[1:]
|
||||||
|
|
||||||
def accept(self, type_name):
|
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 the next token.__name__ is `type_name`, returns true and advances
|
||||||
|
to the next token with `next_token()`.
|
||||||
|
"""
|
||||||
if self.at_end():
|
if self.at_end():
|
||||||
return False
|
return False
|
||||||
elif (type(self.peek()).__name__ == type_name ):
|
elif (type(self.peek()).__name__ == type_name ):
|
||||||
@@ -52,8 +56,9 @@ to the next token with `next_token()`."""
|
|||||||
|
|
||||||
def expect(self, type_name):
|
def expect(self, type_name):
|
||||||
"""
|
"""
|
||||||
If the next token.__name__ is `type_name`, the parser is advanced.
|
If the next token.__name__ is `type_name`, the parser is advanced.
|
||||||
If it is not, an assertion failure occurs."""
|
If it is not, an assertion failure occurs.
|
||||||
|
"""
|
||||||
assert( self.accept(type_name) )
|
assert( self.accept(type_name) )
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user