mirror of
https://github.com/iluvcapra/pycmx.git
synced 2025-12-31 08:50:54 +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 @@
|
||||
|
||||
# Parsed Statement Data Structures
|
||||
#
|
||||
# These represent individual lines that have been typed and have undergone some light symbolic parsing.
|
||||
# pycmx
|
||||
# (c) 2018 Jamie Hardt
|
||||
|
||||
from .util import collimate
|
||||
import re
|
||||
@@ -29,7 +27,8 @@ 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)]
|
||||
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,
|
||||
|
||||
@@ -26,8 +26,10 @@ parser operations such as `accept()` and `expect()`
|
||||
self.current_token = None
|
||||
|
||||
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]
|
||||
|
||||
def at_end(self):
|
||||
@@ -40,8 +42,10 @@ popping the current token."""
|
||||
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 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 ):
|
||||
@@ -53,7 +57,8 @@ to the next token with `next_token()`."""
|
||||
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."""
|
||||
If it is not, an assertion failure occurs.
|
||||
"""
|
||||
assert( self.accept(type_name) )
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user