mirror of
https://github.com/iluvcapra/ptulsconv.git
synced 2025-12-31 08:50:48 +00:00
Work on rewrting the parser
And a unit test
This commit is contained in:
@@ -4,7 +4,7 @@ import math
|
|||||||
|
|
||||||
|
|
||||||
def smpte_to_frame_count(smpte_rep_string: str, frames_per_logical_second: int, drop_frame_hint=False,
|
def smpte_to_frame_count(smpte_rep_string: str, frames_per_logical_second: int, drop_frame_hint=False,
|
||||||
include_fractional=False):
|
include_fractional=False) -> object:
|
||||||
"""
|
"""
|
||||||
Convert a string with a SMPTE timecode representation into a frame count.
|
Convert a string with a SMPTE timecode representation into a frame count.
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import os
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from collections import namedtuple
|
|
||||||
import csv
|
import csv
|
||||||
|
|
||||||
import ptulsconv
|
import ptulsconv
|
||||||
@@ -55,6 +54,7 @@ adr_field_map = ((['Title', 'PT.Session.Name'], 'Title', str),
|
|||||||
(['Movie.Start_Offset_Seconds'], 'Movie Seconds', float),
|
(['Movie.Start_Offset_Seconds'], 'Movie Seconds', float),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def dump_csv(events, output=sys.stdout):
|
def dump_csv(events, output=sys.stdout):
|
||||||
keys = set()
|
keys = set()
|
||||||
for e in events:
|
for e in events:
|
||||||
|
|||||||
5
ptulsconv/docparser/adr_entity.py
Normal file
5
ptulsconv/docparser/adr_entity.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from .doc_entity import SessionDescriptor
|
||||||
|
|
||||||
|
class ADRDocument:
|
||||||
|
def __init__(self, session: SessionDescriptor):
|
||||||
|
self.document = session
|
||||||
108
ptulsconv/docparser/doc_entity.py
Normal file
108
ptulsconv/docparser/doc_entity.py
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
from fractions import Fraction
|
||||||
|
from ptulsconv.broadcast_timecode import smpte_to_frame_count
|
||||||
|
|
||||||
|
|
||||||
|
class SessionDescriptor:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.header = kwargs['header']
|
||||||
|
self.files = kwargs['files']
|
||||||
|
self.clips = kwargs['clips']
|
||||||
|
self.plugins = kwargs['plugins']
|
||||||
|
self.tracks = kwargs['tracks']
|
||||||
|
self.markers = kwargs['markers']
|
||||||
|
|
||||||
|
|
||||||
|
class HeaderDescriptor:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.session_name = kwargs['session_name']
|
||||||
|
self.sample_rate = kwargs['sample_rate']
|
||||||
|
self.bit_depth = kwargs['bit_depth']
|
||||||
|
self.start_timecode = kwargs['start_timecode']
|
||||||
|
self.timecode_format = kwargs['timecode_format']
|
||||||
|
self.timecode_drop_frame = kwargs['timecode_drop_frame']
|
||||||
|
self.count_audio_tracks = kwargs['count_audio_tracks']
|
||||||
|
self.count_clips = kwargs['count_clips']
|
||||||
|
self.count_files = kwargs['count_files']
|
||||||
|
|
||||||
|
def convert_timecode(self, tc_string) -> Fraction:
|
||||||
|
frame_count = smpte_to_frame_count(tc_string,
|
||||||
|
self.logical_fps,
|
||||||
|
self.timecode_drop_frame,
|
||||||
|
include_fractional=False)
|
||||||
|
|
||||||
|
return self.frame_duration * frame_count
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start_time(self) -> Fraction:
|
||||||
|
"""
|
||||||
|
The start time of this session.
|
||||||
|
:return: Start time in seconds
|
||||||
|
"""
|
||||||
|
return self.convert_timecode(self.start_timecode)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def logical_fps(self) -> int:
|
||||||
|
return self._get_tcformat_params[0]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def frame_duration(self) -> Fraction:
|
||||||
|
return self._get_tcformat_params[1]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _get_tcformat_params(self):
|
||||||
|
frame_rates = {"23.976": (24, Fraction(1001, 24_000)),
|
||||||
|
"24": (24, Fraction(1, 24)),
|
||||||
|
"29.97": (30, Fraction(1001, 30_000)),
|
||||||
|
"30": (30, Fraction(1, 30)),
|
||||||
|
"59.94": (60, Fraction(1001, 60_000)),
|
||||||
|
"60": (60, Fraction(1, 60))
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.timecode_format in frame_rates.keys():
|
||||||
|
return frame_rates[self.timecode_format]
|
||||||
|
else:
|
||||||
|
raise ValueError("Unrecognized TC rate (%s)" % self.timecode_format)
|
||||||
|
|
||||||
|
|
||||||
|
class TrackDescriptor:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.name = kwargs['name']
|
||||||
|
self.comments = kwargs['comments']
|
||||||
|
self.user_delay_samples = kwargs['user_delay_samples']
|
||||||
|
self.state = kwargs['state']
|
||||||
|
self.plugins = kwargs['plugins']
|
||||||
|
self.clips = kwargs['clips']
|
||||||
|
|
||||||
|
|
||||||
|
class FileDescriptor(dict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TrackClipDescriptor:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.channel = kwargs['channel']
|
||||||
|
self.event = kwargs['event']
|
||||||
|
self.clip_name = kwargs['clip_name']
|
||||||
|
self.start_time = kwargs['start_time']
|
||||||
|
self.end_time = kwargs['end_time']
|
||||||
|
self.duration = kwargs['duration']
|
||||||
|
self.timestamp = kwargs['timestamp']
|
||||||
|
self.state = kwargs['state']
|
||||||
|
|
||||||
|
|
||||||
|
class ClipDescriptor(dict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PluginDescriptor(dict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MarkerDescriptor:
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.number = kwargs['number']
|
||||||
|
self.location = kwargs['location']
|
||||||
|
self.time_reference = kwargs['time_reference']
|
||||||
|
self.units = kwargs['units']
|
||||||
|
self.name = kwargs['name']
|
||||||
|
self.comments = kwargs['comments']
|
||||||
@@ -1,99 +1,7 @@
|
|||||||
from parsimonious.nodes import NodeVisitor, Node
|
from parsimonious.nodes import NodeVisitor
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
# _SessionDescriptor = namedtuple('_SessionDescriptor',
|
|
||||||
# "header files clips plugins tracks markers")
|
|
||||||
#
|
|
||||||
# _HeaderDescriptor = namedtuple('_HeaderDescriptor',
|
|
||||||
# "session_name sample_rate bit_depth start_timecode "
|
|
||||||
# "timecode_format timecode_drop_frame "
|
|
||||||
# "count_audio_tracks count_clips count_files")
|
|
||||||
#
|
|
||||||
# _TrackDescriptor = namedtuple("_TrackDescriptor",
|
|
||||||
# "name comments user_delay_samples state plugins "
|
|
||||||
# "clips")
|
|
||||||
#
|
|
||||||
# _TrackClipDescriptor = namedtuple("_TrackClipDescriptor",
|
|
||||||
# "channel event clip_name start_time end_time "
|
|
||||||
# "duration timestamp state")
|
|
||||||
#
|
|
||||||
# _PluginDescriptor = namedtuple("_PluginDescriptor",
|
|
||||||
# "manufacturer plugin_name version format stems "
|
|
||||||
# "count_instances")
|
|
||||||
#
|
|
||||||
# _MarkerDescriptor = namedtuple("_MarkerDescriptor",
|
|
||||||
# "number location time_reference units name "
|
|
||||||
# "comments")
|
|
||||||
#
|
|
||||||
# _FileDescriptor = namedtuple("_FileDescriptor", "filename path")
|
|
||||||
|
|
||||||
|
|
||||||
class SessionDescriptor:
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.header = kwargs['header']
|
|
||||||
self.files = kwargs['files']
|
|
||||||
self.clips = kwargs['clips']
|
|
||||||
self.plugins = kwargs['plugins']
|
|
||||||
self.tracks = kwargs['tracks']
|
|
||||||
self.markers = kwargs['markers']
|
|
||||||
|
|
||||||
|
|
||||||
class HeaderDescriptor:
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.session_name = kwargs['session_name']
|
|
||||||
self.sample_rate = kwargs['sample_rate']
|
|
||||||
self.bit_depth = kwargs['bit_depth']
|
|
||||||
self.start_timecode = kwargs['start_timecode']
|
|
||||||
self.timecode_format = kwargs['timecode_format']
|
|
||||||
self.timecode_drop_frame = kwargs['timecode_drop_frame']
|
|
||||||
self.count_audio_tracks = kwargs['count_audio_tracks']
|
|
||||||
self.count_clips = kwargs['count_clips']
|
|
||||||
self.count_files = kwargs['count_files']
|
|
||||||
|
|
||||||
|
|
||||||
class TrackDescriptor:
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.name = kwargs['name']
|
|
||||||
self.comments = kwargs['comments']
|
|
||||||
self.user_delay_samples = kwargs['user_delay_samples']
|
|
||||||
self.state = kwargs['state']
|
|
||||||
self.plugins = kwargs['plugins']
|
|
||||||
self.clips = kwargs['clips']
|
|
||||||
|
|
||||||
|
|
||||||
class FileDescriptor(dict):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TrackClipDescriptor:
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.channel = kwargs['channel']
|
|
||||||
self.event = kwargs['event']
|
|
||||||
self.clip_name = kwargs['clip_name']
|
|
||||||
self.start_time = kwargs['start_time']
|
|
||||||
self.end_time = kwargs['end_time']
|
|
||||||
self.duration = kwargs['duration']
|
|
||||||
self.timestamp = kwargs['timestamp']
|
|
||||||
self.state = kwargs['state']
|
|
||||||
|
|
||||||
|
|
||||||
class ClipDescriptor(dict):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class PluginDescriptor(dict):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class MarkerDescriptor:
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.number = kwargs['number']
|
|
||||||
self.location = kwargs['location']
|
|
||||||
self.time_reference = kwargs['time_reference']
|
|
||||||
self.units = kwargs['units']
|
|
||||||
self.name = kwargs['name']
|
|
||||||
self.comments = kwargs['comments']
|
|
||||||
|
|
||||||
|
from .doc_entity import SessionDescriptor, HeaderDescriptor, TrackDescriptor, FileDescriptor, \
|
||||||
|
TrackClipDescriptor, ClipDescriptor, PluginDescriptor, MarkerDescriptor
|
||||||
|
|
||||||
|
|
||||||
class DocParserVisitor(NodeVisitor):
|
class DocParserVisitor(NodeVisitor):
|
||||||
@@ -215,11 +123,11 @@ class DocParserVisitor(NodeVisitor):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def visit_marker_record(_, visited_children):
|
def visit_marker_record(_, visited_children):
|
||||||
return MarkerDescriptor(number=visited_children[0],
|
return MarkerDescriptor(number=visited_children[0],
|
||||||
location=visited_children[3],
|
location=visited_children[3],
|
||||||
time_reference=visited_children[5],
|
time_reference=visited_children[5],
|
||||||
units=visited_children[8],
|
units=visited_children[8],
|
||||||
name=visited_children[10],
|
name=visited_children[10],
|
||||||
comments=visited_children[12])
|
comments=visited_children[12])
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def visit_formatted_clip_name(_, visited_children):
|
def visit_formatted_clip_name(_, visited_children):
|
||||||
|
|||||||
24
tests/test_doc_entities.py
Normal file
24
tests/test_doc_entities.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import unittest
|
||||||
|
from ptulsconv.docparser.doc_entity import HeaderDescriptor
|
||||||
|
from fractions import Fraction
|
||||||
|
|
||||||
|
|
||||||
|
class DocParserTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_header(self):
|
||||||
|
header = HeaderDescriptor(session_name="Test Session",
|
||||||
|
sample_rate=48000.0,
|
||||||
|
bit_depth=24,
|
||||||
|
start_timecode="00:59:52:00",
|
||||||
|
timecode_format="30",
|
||||||
|
timecode_drop_frame=False,
|
||||||
|
count_audio_tracks=0,
|
||||||
|
count_clips=0,
|
||||||
|
count_files=0)
|
||||||
|
|
||||||
|
self.assertEqual(header.session_name, "Test Session")
|
||||||
|
self.assertEqual(header.convert_timecode(header.start_timecode), Fraction((59 * 60 + 52) * 30, 30))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user