mirror of
https://github.com/iluvcapra/pycmx.git
synced 2025-12-31 08:50:54 +00:00
200 lines
6.8 KiB
Python
200 lines
6.8 KiB
Python
# pycmx
|
|
# (c) 2018-2025 Jamie Hardt
|
|
|
|
import re
|
|
from typing import TextIO, List
|
|
|
|
from .statements import (StmtCdlSat, StmtCdlSop, StmtFrmc, StmtRemark,
|
|
StmtTitle, StmtUnrecognized, StmtFCM, StmtAudioExt,
|
|
StmtClipName, StmtEffectsName, StmtEvent,
|
|
StmtSourceFile, StmtSplitEdit, StmtMotionMemory,
|
|
StmtSourceUMID)
|
|
from .util import collimate
|
|
|
|
|
|
def parse_cmx3600_statements(file: TextIO) -> List[object]:
|
|
"""
|
|
Return a list of every statement in the file argument.
|
|
"""
|
|
lines = file.readlines()
|
|
return [_parse_cmx3600_line(line.strip(), line_number)
|
|
for (line_number, line) in enumerate(lines)]
|
|
|
|
|
|
def _edl_column_widths(event_field_length, source_field_length) -> List[int]:
|
|
return [event_field_length, 2, source_field_length, 1,
|
|
4, 2, # chans
|
|
4, 1, # trans
|
|
3, 1, # trans op
|
|
11, 1,
|
|
11, 1,
|
|
11, 1,
|
|
11]
|
|
|
|
# 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: 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.
|
|
"""
|
|
event_num_p = re.compile(r"^(\d+) ")
|
|
line_matcher = event_num_p.match(line)
|
|
|
|
if line.startswith("TITLE:"):
|
|
return _parse_title(line, line_number)
|
|
if line.startswith("FCM:"):
|
|
return _parse_fcm(line, line_number)
|
|
if line_matcher is not None:
|
|
event_field_len = len(line_matcher.group(1))
|
|
source_field_len = len(line) - (event_field_len + 65)
|
|
return _parse_columns_for_standard_form(line, event_field_len,
|
|
source_field_len, line_number)
|
|
if line.startswith("AUD"):
|
|
return _parse_extended_audio_channels(line, line_number)
|
|
if line.startswith("*"):
|
|
return _parse_remark(line[1:].strip(), line_number)
|
|
if line.startswith(">>> SOURCE"):
|
|
return _parse_source_umid_statement(line, line_number)
|
|
if line.startswith("EFFECTS NAME IS"):
|
|
return _parse_effects_name(line, line_number)
|
|
if line.startswith("SPLIT:"):
|
|
return _parse_split(line, line_number)
|
|
if line.startswith("M2"):
|
|
return _parse_motion_memory(line, line_number)
|
|
|
|
return _parse_unrecognized(line, line_number)
|
|
|
|
|
|
def _parse_title(line, line_num) -> StmtTitle:
|
|
title = line[6:].strip()
|
|
return StmtTitle(title=title, line_number=line_num)
|
|
|
|
|
|
def _parse_fcm(line, line_num) -> StmtFCM:
|
|
val = line[4:].strip()
|
|
if val == "DROP FRAME":
|
|
return StmtFCM(drop=True, line_number=line_num)
|
|
|
|
return StmtFCM(drop=False, line_number=line_num)
|
|
|
|
|
|
def _parse_extended_audio_channels(line, line_number):
|
|
content = line.strip()
|
|
audio3 = "3" in content
|
|
audio4 = "4" in content
|
|
|
|
if audio3 or audio4:
|
|
return StmtAudioExt(audio3, audio4, line_number)
|
|
else:
|
|
return StmtUnrecognized(line, line_number)
|
|
|
|
|
|
def _parse_remark(line, line_number) -> object:
|
|
if line.startswith("FROM CLIP NAME:"):
|
|
return StmtClipName(name=line[15:].strip(), affect="from",
|
|
line_number=line_number)
|
|
elif line.startswith("TO CLIP NAME:"):
|
|
return StmtClipName(name=line[13:].strip(), affect="to",
|
|
line_number=line_number)
|
|
elif line.startswith("SOURCE FILE:"):
|
|
return StmtSourceFile(filename=line[12:].strip(),
|
|
line_number=line_number)
|
|
elif line.startswith("ASC_SOP"):
|
|
group_patterns: list[str] = re.findall(r'\((.*?)\)', line)
|
|
|
|
v1: list[list[tuple[str, str]]] = \
|
|
[re.findall(r'(-?\d+(\.\d+)?)', a) for a in group_patterns]
|
|
|
|
v: list[list[str]] = [[a[0] for a in b] for b in v1]
|
|
|
|
if len(v) != 3 or any([len(a) != 3 for a in v]):
|
|
return StmtRemark(line, line_number)
|
|
|
|
else:
|
|
return StmtCdlSop(slope_r=v[0][0], slope_g=v[0][1],
|
|
slope_b=v[0][2], offset_r=v[1][0],
|
|
offset_g=v[1][1], offset_b=v[1][2],
|
|
power_r=v[2][0], power_g=v[2][1],
|
|
power_b=v[2][2], line_number=line_number)
|
|
|
|
elif line.startswith("ASC_SAT"):
|
|
value = re.findall(r'(-?\d+(\.\d+)?)', line)
|
|
|
|
if len(value) != 1:
|
|
return StmtRemark(line, line_number)
|
|
|
|
else:
|
|
return StmtCdlSat(value=value[0][0], line_number=line_number)
|
|
|
|
elif line.startswith("FRMC"):
|
|
match = re.match(
|
|
r'^FRMC START:\s*(\d+)\s+FRMC END:\s*(\d+)'
|
|
r'\s+FRMC DURATION:\s*(\d+)', line, re.IGNORECASE)
|
|
|
|
if match is None:
|
|
return StmtRemark(line, line_number)
|
|
|
|
else:
|
|
return StmtFrmc(start=match.group(1), end=match.group(2),
|
|
duration=match.group(3), line_number=line_number)
|
|
|
|
else:
|
|
return StmtRemark(text=line, line_number=line_number)
|
|
|
|
|
|
def _parse_effects_name(line, line_number) -> StmtEffectsName:
|
|
name = line[16:].strip()
|
|
return StmtEffectsName(name=name, line_number=line_number)
|
|
|
|
|
|
def _parse_split(line: str, line_number):
|
|
split_type = line[10:21]
|
|
is_video = split_type.startswith("VIDEO")
|
|
|
|
split_mag = line[24:35]
|
|
return StmtSplitEdit(video=is_video, magnitude=split_mag,
|
|
line_number=line_number)
|
|
|
|
|
|
def _parse_motion_memory(line, line_number):
|
|
return StmtMotionMemory(source="", fps="")
|
|
|
|
|
|
def _parse_unrecognized(line, line_number):
|
|
return StmtUnrecognized(content=line, line_number=line_number)
|
|
|
|
|
|
def _parse_columns_for_standard_form(line: str, event_field_length: int,
|
|
source_field_length: int,
|
|
line_number: int):
|
|
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)
|
|
|
|
column_strings = collimate(line, col_widths)
|
|
|
|
return StmtEvent(event=column_strings[0],
|
|
source=column_strings[2].strip(),
|
|
channels=column_strings[4].strip(),
|
|
trans=column_strings[6].strip(),
|
|
trans_op=column_strings[8].strip(),
|
|
source_in=column_strings[10].strip(),
|
|
source_out=column_strings[12].strip(),
|
|
record_in=column_strings[14].strip(),
|
|
record_out=column_strings[16].strip(),
|
|
line_number=line_number,
|
|
source_field_size=source_field_length)
|
|
|
|
|
|
def _parse_source_umid_statement(line, line_number):
|
|
# trimmed = line[3:].strip()
|
|
return StmtSourceUMID(name=None, umid=None, line_number=line_number)
|