mirror of
https://github.com/iluvcapra/pycmx.git
synced 2025-12-31 08:50:54 +00:00
retyping some CDL items
This commit is contained in:
31
pycmx/cdl.py
Normal file
31
pycmx/cdl.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# pycmx
|
||||||
|
# (c) 2025 Jamie Hardt
|
||||||
|
|
||||||
|
from typing import Generic, NamedTuple, TypeVar
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
|
||||||
|
class Rgb(NamedTuple, Generic[T]):
|
||||||
|
red: T
|
||||||
|
green: T
|
||||||
|
blue: T
|
||||||
|
|
||||||
|
|
||||||
|
class AscSopComponents(NamedTuple, Generic[T]):
|
||||||
|
"""
|
||||||
|
Fields in an ASC SOP (Slope-Offset-Power) color transfer function
|
||||||
|
statement
|
||||||
|
"""
|
||||||
|
slope: Rgb[T]
|
||||||
|
offset: Rgb[T]
|
||||||
|
power: Rgb[T]
|
||||||
|
|
||||||
|
|
||||||
|
class FramecountTriple(NamedTuple):
|
||||||
|
"""
|
||||||
|
Fields in an FRMC statement
|
||||||
|
"""
|
||||||
|
start: int
|
||||||
|
end: int
|
||||||
|
duration: int
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
# pycmx
|
# pycmx
|
||||||
# (c) 2018-2025 Jamie Hardt
|
# (c) 2018-2025 Jamie Hardt
|
||||||
|
|
||||||
|
from pycmx.cdl import AscSopComponents, FramecountTriple, Rgb
|
||||||
from pycmx.statements import StmtCdlSat, StmtCdlSop, StmtFrmc
|
from pycmx.statements import StmtCdlSat, StmtCdlSop, StmtFrmc
|
||||||
from .transition import Transition
|
from .transition import Transition
|
||||||
from .channel_map import ChannelMap
|
from .channel_map import ChannelMap
|
||||||
@@ -19,14 +20,14 @@ class Edit:
|
|||||||
trans_name_statement=None, asc_sop_statement=None,
|
trans_name_statement=None, asc_sop_statement=None,
|
||||||
asc_sat_statement=None, frmc_statement=None):
|
asc_sat_statement=None, frmc_statement=None):
|
||||||
|
|
||||||
self.edit_statement = edit_statement
|
self._edit_statement = edit_statement
|
||||||
self.audio_ext = audio_ext_statement
|
self._audio_ext = audio_ext_statement
|
||||||
self.clip_name_statement = clip_name_statement
|
self.clip_name_statement = clip_name_statement
|
||||||
self.source_file_statement = source_file_statement
|
self.source_file_statement = source_file_statement
|
||||||
self.trans_name_statement = trans_name_statement
|
self.trans_name_statement = trans_name_statement
|
||||||
self.asc_sop_statement: Optional[StmtCdlSop] = asc_sop_statement
|
self._asc_sop_statement: Optional[StmtCdlSop] = asc_sop_statement
|
||||||
self.asc_sat_statement: Optional[StmtCdlSat] = asc_sat_statement
|
self._asc_sat_statement: Optional[StmtCdlSat] = asc_sat_statement
|
||||||
self.frmc_statement: Optional[StmtFrmc] = frmc_statement
|
self._frmc_statement: Optional[StmtFrmc] = frmc_statement
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def line_number(self) -> int:
|
def line_number(self) -> int:
|
||||||
@@ -35,7 +36,7 @@ class Edit:
|
|||||||
this edit. Line numbers a zero-indexed, such that the
|
this edit. Line numbers a zero-indexed, such that the
|
||||||
"TITLE:" record is line zero.
|
"TITLE:" record is line zero.
|
||||||
"""
|
"""
|
||||||
return self.edit_statement.line_number
|
return self._edit_statement.line_number
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def channels(self) -> ChannelMap:
|
def channels(self) -> ChannelMap:
|
||||||
@@ -43,9 +44,9 @@ class Edit:
|
|||||||
Get the :obj:`ChannelMap` object associated with this Edit.
|
Get the :obj:`ChannelMap` object associated with this Edit.
|
||||||
"""
|
"""
|
||||||
cm = ChannelMap()
|
cm = ChannelMap()
|
||||||
cm._append_event(self.edit_statement.channels)
|
cm._append_event(self._edit_statement.channels)
|
||||||
if self.audio_ext is not None:
|
if self._audio_ext is not None:
|
||||||
cm._append_ext(self.audio_ext)
|
cm._append_ext(self._audio_ext)
|
||||||
return cm
|
return cm
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -54,19 +55,19 @@ class Edit:
|
|||||||
Get the :obj:`Transition` object associated with this edit.
|
Get the :obj:`Transition` object associated with this edit.
|
||||||
"""
|
"""
|
||||||
if self.trans_name_statement:
|
if self.trans_name_statement:
|
||||||
return Transition(self.edit_statement.trans,
|
return Transition(self._edit_statement.trans,
|
||||||
self.edit_statement.trans_op,
|
self._edit_statement.trans_op,
|
||||||
self.trans_name_statement.name)
|
self.trans_name_statement.name)
|
||||||
else:
|
else:
|
||||||
return Transition(self.edit_statement.trans,
|
return Transition(self._edit_statement.trans,
|
||||||
self.edit_statement.trans_op, None)
|
self._edit_statement.trans_op, None)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source_in(self) -> str:
|
def source_in(self) -> str:
|
||||||
"""
|
"""
|
||||||
Get the source in timecode.
|
Get the source in timecode.
|
||||||
"""
|
"""
|
||||||
return self.edit_statement.source_in
|
return self._edit_statement.source_in
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source_out(self) -> str:
|
def source_out(self) -> str:
|
||||||
@@ -74,7 +75,7 @@ class Edit:
|
|||||||
Get the source out timecode.
|
Get the source out timecode.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.edit_statement.source_out
|
return self._edit_statement.source_out
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def record_in(self) -> str:
|
def record_in(self) -> str:
|
||||||
@@ -82,7 +83,7 @@ class Edit:
|
|||||||
Get the record in timecode.
|
Get the record in timecode.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.edit_statement.record_in
|
return self._edit_statement.record_in
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def record_out(self) -> str:
|
def record_out(self) -> str:
|
||||||
@@ -90,7 +91,7 @@ class Edit:
|
|||||||
Get the record out timecode.
|
Get the record out timecode.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.edit_statement.record_out
|
return self._edit_statement.record_out
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source(self) -> str:
|
def source(self) -> str:
|
||||||
@@ -98,7 +99,7 @@ class Edit:
|
|||||||
Get the source column. This is the 8, 32 or 128-character string on the
|
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.
|
event record line, this usually references the tape name of the source.
|
||||||
"""
|
"""
|
||||||
return self.edit_statement.source
|
return self._edit_statement.source
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def black(self) -> bool:
|
def black(self) -> bool:
|
||||||
@@ -138,22 +139,40 @@ class Edit:
|
|||||||
return self.clip_name_statement.name
|
return self.clip_name_statement.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def asc_sop(self) -> Optional[StmtCdlSop]:
|
def asc_sop(self) -> Optional[AscSopComponents[str]]:
|
||||||
"""
|
"""
|
||||||
Get ASC CDL Slope-Offset-Power transfer function for clip, if present
|
Get ASC CDL Slope-Offset-Power transfer function for clip, if present
|
||||||
"""
|
"""
|
||||||
return self.asc_sop_statement
|
if self._asc_sop_statement is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
s = self._asc_sop_statement
|
||||||
|
|
||||||
|
return AscSopComponents(
|
||||||
|
slope=Rgb(red=s.slope_r, green=s.slope_g,
|
||||||
|
blue=s.slope_b),
|
||||||
|
offset=Rgb(red=s.offset_r, green=s.offset_g,
|
||||||
|
blue=s.offset_g),
|
||||||
|
power=Rgb(red=s.power_r, green=s.power_g,
|
||||||
|
blue=s.power_b)
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def asc_sat(self) -> Optional[StmtCdlSat]:
|
def asc_sat(self) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
Get ASC CDL saturation value for clip, if present
|
Get ASC CDL saturation value for clip, if present
|
||||||
"""
|
"""
|
||||||
return self.asc_sat_statement
|
if self._asc_sat_statement is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self._asc_sat_statement.value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def frmc(self) -> Optional[StmtFrmc]:
|
def frmc(self) -> Optional[FramecountTriple]:
|
||||||
"""
|
"""
|
||||||
Get FRMC data
|
Get FRMC data
|
||||||
"""
|
"""
|
||||||
return self.frmc_statement
|
if not self._frmc_statement:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return FramecountTriple(int())
|
||||||
|
|||||||
@@ -141,8 +141,13 @@ def _parse_remark(line, line_number) -> object:
|
|||||||
return StmtRemark(line, line_number)
|
return StmtRemark(line, line_number)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return StmtFrmc(start=match.group(1), end=match.group(2),
|
try:
|
||||||
duration=match.group(3), line_number=line_number)
|
return StmtFrmc(start=int(match.group(1)),
|
||||||
|
end=int(match.group(2)),
|
||||||
|
duration=int(match.group(3)),
|
||||||
|
line_number=line_number)
|
||||||
|
except ValueError:
|
||||||
|
return StmtRemark(line, line_number)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return StmtRemark(text=line, line_number=line_number)
|
return StmtRemark(text=line, line_number=line_number)
|
||||||
|
|||||||
@@ -66,9 +66,9 @@ class StmtCdlSat(NamedTuple):
|
|||||||
|
|
||||||
|
|
||||||
class StmtFrmc(NamedTuple):
|
class StmtFrmc(NamedTuple):
|
||||||
start: str
|
start: int
|
||||||
end: str
|
end: int
|
||||||
duration: str
|
duration: int
|
||||||
line_number: int
|
line_number: int
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -143,36 +143,36 @@ class TestParse(TestCase):
|
|||||||
edl = pycmx.parse_cmx3600(f)
|
edl = pycmx.parse_cmx3600(f)
|
||||||
for event in edl.events:
|
for event in edl.events:
|
||||||
if event.number == 1:
|
if event.number == 1:
|
||||||
sop = event.edits[0].asc_sop_statement
|
sop = event.edits[0].asc_sop
|
||||||
self.assertIsNotNone(sop)
|
self.assertIsNotNone(sop)
|
||||||
assert sop
|
assert sop
|
||||||
self.assertEqual(sop.slope_r, "0.9405")
|
self.assertEqual(sop.slope.red, "0.9405")
|
||||||
self.assertEqual(sop.offset_g, "-0.0276")
|
self.assertEqual(sop.offset.green, "-0.0276")
|
||||||
|
|
||||||
sat = event.edits[0].asc_sat_statement
|
sat = event.edits[0].asc_sat
|
||||||
self.assertIsNotNone(sat)
|
self.assertIsNotNone(sat)
|
||||||
assert sat
|
assert sat
|
||||||
self.assertEqual(sat.value, '0.9640')
|
self.assertEqual(sat, '0.9640')
|
||||||
|
|
||||||
def test_frmc(self):
|
def test_frmc(self):
|
||||||
with open("tests/edls/cdl_frmc_example01.edl", "r") as f:
|
with open("tests/edls/cdl_frmc_example01.edl", "r") as f:
|
||||||
edl = pycmx.parse_cmx3600(f)
|
edl = pycmx.parse_cmx3600(f)
|
||||||
for event in edl.events:
|
for event in edl.events:
|
||||||
if event.number == 1:
|
if event.number == 1:
|
||||||
frmc = event.edits[0].frmc_statement
|
frmc = event.edits[0]._frmc_statement
|
||||||
self.assertIsNotNone(frmc)
|
self.assertIsNotNone(frmc)
|
||||||
assert frmc
|
assert frmc
|
||||||
self.assertEqual(frmc.start, "1001")
|
self.assertEqual(frmc.start, 1001)
|
||||||
self.assertEqual(frmc.end, "1102")
|
self.assertEqual(frmc.end, 1102)
|
||||||
self.assertEqual(frmc.duration, "102")
|
self.assertEqual(frmc.duration, 102)
|
||||||
|
|
||||||
with open("tests/edls/cdl_frmc_example02.edl", "r") as f:
|
with open("tests/edls/cdl_frmc_example02.edl", "r") as f:
|
||||||
edl = pycmx.parse_cmx3600(f)
|
edl = pycmx.parse_cmx3600(f)
|
||||||
for event in edl.events:
|
for event in edl.events:
|
||||||
if event.number == 6:
|
if event.number == 6:
|
||||||
frmc = event.edits[0].frmc_statement
|
frmc = event.edits[0]._frmc_statement
|
||||||
self.assertIsNotNone(frmc)
|
self.assertIsNotNone(frmc)
|
||||||
assert frmc
|
assert frmc
|
||||||
self.assertEqual(frmc.start, "1001")
|
self.assertEqual(frmc.start, 1001)
|
||||||
self.assertEqual(frmc.end, "1486")
|
self.assertEqual(frmc.end, 1486)
|
||||||
self.assertEqual(frmc.duration, "486")
|
self.assertEqual(frmc.duration, 486)
|
||||||
|
|||||||
Reference in New Issue
Block a user