diff --git a/ptulsconv/docparser/__init__.py b/ptulsconv/docparser/__init__.py index f154512..dbbf11a 100644 --- a/ptulsconv/docparser/__init__.py +++ b/ptulsconv/docparser/__init__.py @@ -1,25 +1 @@ -from typing import Generator, Callable, Iterator -from enum import Enum - - -def apply_appends(source: Iterator, - should_append: Callable, - do_append: Callable) -> Generator: - """ - :param source: - :param should_append: Called with two variables a and b, your - function should return true if b should be - appended to a - :param do_append: Called with two variables a and b, your function - should return - :returns: A Generator - """ - this_element = next(source) - for element in source: - if should_append(this_element, element): - this_element = do_append(this_element, element) - else: - yield this_element - this_element = element - - yield this_element +from .doc_parser_visitor import parse_document \ No newline at end of file diff --git a/ptulsconv/docparser/doc_entity.py b/ptulsconv/docparser/doc_entity.py index 5b86c76..4d5d1f0 100644 --- a/ptulsconv/docparser/doc_entity.py +++ b/ptulsconv/docparser/doc_entity.py @@ -1,9 +1,6 @@ from fractions import Fraction from ptulsconv.broadcast_timecode import smpte_to_frame_count from typing import Tuple, List, Iterator -from collections import namedtuple -from . import apply_appends -from .tagged_string_parser_visitor import parse_tags class SessionDescriptor: diff --git a/ptulsconv/docparser/doc_parser_visitor.py b/ptulsconv/docparser/doc_parser_visitor.py index de8161b..e3582f0 100644 --- a/ptulsconv/docparser/doc_parser_visitor.py +++ b/ptulsconv/docparser/doc_parser_visitor.py @@ -4,6 +4,18 @@ from .doc_entity import SessionDescriptor, HeaderDescriptor, TrackDescriptor, Fi TrackClipDescriptor, ClipDescriptor, PluginDescriptor, MarkerDescriptor +def parse_document(path: str) -> SessionDescriptor: + """ + Parse a Pro Tools text export. + :param path: path to a file + :return: the session descriptor + """ + from .ptuls_grammar import protools_text_export_grammar + with open(path, 'r') as f: + ast = protools_text_export_grammar.parse(f.read()) + return DocParserVisitor().visit(ast) + + class DocParserVisitor(NodeVisitor): @staticmethod diff --git a/ptulsconv/docparser/tag_compiler.py b/ptulsconv/docparser/tag_compiler.py index 736e7ff..9bd60cd 100644 --- a/ptulsconv/docparser/tag_compiler.py +++ b/ptulsconv/docparser/tag_compiler.py @@ -1,10 +1,9 @@ from collections import namedtuple from fractions import Fraction -from typing import Iterator, Tuple +from typing import Iterator, Tuple, Callable, Generator -from ptulsconv.docparser import apply_appends -from ptulsconv.docparser.doc_entity import SessionDescriptor -from ptulsconv.docparser.tagged_string_parser_visitor import parse_tags, TagPreModes +import ptulsconv.docparser.doc_entity as doc_entity +from .tagged_string_parser_visitor import parse_tags, TagPreModes class Event(namedtuple('Event', 'clip_name track_name session_name tags start finish')): @@ -12,7 +11,7 @@ class Event(namedtuple('Event', 'clip_name track_name session_name tags start fi class TagCompiler: - session: SessionDescriptor + session: doc_entity.SessionDescriptor def compile_events(self) -> Iterator[Event]: step0 = self.parse_data() @@ -121,3 +120,25 @@ class TagCompiler: yield event.clip_content, event.track_content, session_parsed.content, tags, event.start, event.finish + +def apply_appends(source: Iterator, + should_append: Callable, + do_append: Callable) -> Generator: + """ + :param source: + :param should_append: Called with two variables a and b, your + function should return true if b should be + appended to a + :param do_append: Called with two variables a and b, your function + should return + :returns: A Generator + """ + this_element = next(source) + for element in source: + if should_append(this_element, element): + this_element = do_append(this_element, element) + else: + yield this_element + this_element = element + + yield this_element \ No newline at end of file diff --git a/tests/test_robinhood1.py b/tests/test_robinhood1.py index bc48ae7..fefd161 100644 --- a/tests/test_robinhood1.py +++ b/tests/test_robinhood1.py @@ -1,6 +1,5 @@ import unittest -import ptulsconv -#import pprint +from ptulsconv.docparser import parse_document import os.path @@ -8,102 +7,77 @@ class TestRobinHood1(unittest.TestCase): path = os.path.dirname(__file__) + '/export_cases/Robin Hood Spotting.txt' def test_header_export(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) - self.assertTrue('header' in parsed.keys()) - self.assertEqual(parsed['header']['session_name'], 'Robin Hood Spotting') - self.assertEqual(parsed['header']['sample_rate'], 48000.0) - self.assertEqual(parsed['header']['bit_depth'], 24) - self.assertEqual(parsed['header']['timecode_format'], 29.97) - self.assertEqual(parsed['header']['timecode_drop_frame'], False) + session = parse_document(self.path) + + self.assertIsNotNone(session.header) + self.assertEqual(session.header.session_name, 'Robin Hood Spotting') + self.assertEqual(session.header.sample_rate, 48000.0) + self.assertEqual(session.header.bit_depth, 24) + self.assertEqual(session.header.timecode_format, '29.97') + self.assertEqual(session.header.timecode_drop_frame, False) def test_all_sections(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) - self.assertIn('header', parsed.keys()) - self.assertIn('files', parsed.keys()) - self.assertIn('clips', parsed.keys()) - self.assertIn('plugins', parsed.keys()) - self.assertIn('tracks', parsed.keys()) - self.assertIn('markers', parsed.keys()) + session = parse_document(self.path) + + self.assertIsNotNone(session.header) + self.assertIsNotNone(session.files) + self.assertIsNotNone(session.clips) + self.assertIsNotNone(session.plugins) + self.assertIsNotNone(session.tracks) + self.assertIsNotNone(session.markers) def test_tracks(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) - self.assertEqual(len(parsed['tracks']), 14) - self.assertListEqual(["Scenes", "Robin", "Will", "Marian", "John", - "Guy", "Much", "Butcher", "Town Crier", - "Soldier 1", "Soldier 2", "Soldier 3", - "Priest", "Guest at Court"], - list(map(lambda n: n['name'], parsed['tracks']))) - self.assertListEqual(["", "[ADR] {Actor=Errol Flynn} $CN=1", - "[ADR] {Actor=Patrick Knowles} $CN=2", - "[ADR] {Actor=Olivia DeHavilland} $CN=3", - "[ADR] {Actor=Claude Raines} $CN=4", - "[ADR] {Actor=Basil Rathbone} $CN=5", - "[ADR] {Actor=Herbert Mundin} $CN=6", - "[ADR] {Actor=George Bunny} $CN=101", - "[ADR] {Actor=Leonard Mundie} $CN=102", - "[ADR] $CN=103", - "[ADR] $CN=104", - "[ADR] $CN=105", - "[ADR] {Actor=Thomas R. Mills} $CN=106", - "[ADR] $CN=107"], - list(map(lambda n: n['comments'], parsed['tracks']))) + + session = parse_document(self.path) + + self.assertEqual(len(session.tracks), 14) + self.assertListEqual(["Scenes", "Robin", "Will", "Marian", "John", + "Guy", "Much", "Butcher", "Town Crier", + "Soldier 1", "Soldier 2", "Soldier 3", + "Priest", "Guest at Court"], + list(map(lambda t: t.name, session.tracks))) + self.assertListEqual(["", "[ADR] {Actor=Errol Flynn} $CN=1", + "[ADR] {Actor=Patrick Knowles} $CN=2", + "[ADR] {Actor=Olivia DeHavilland} $CN=3", + "[ADR] {Actor=Claude Raines} $CN=4", + "[ADR] {Actor=Basil Rathbone} $CN=5", + "[ADR] {Actor=Herbert Mundin} $CN=6", + "[ADR] {Actor=George Bunny} $CN=101", + "[ADR] {Actor=Leonard Mundie} $CN=102", + "[ADR] $CN=103", + "[ADR] $CN=104", + "[ADR] $CN=105", + "[ADR] {Actor=Thomas R. Mills} $CN=106", + "[ADR] $CN=107"], + list(map(lambda t: t.comments, session.tracks))) def test_a_track(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) - guy_track = parsed['tracks'][5] - self.assertEqual(guy_track['name'], 'Guy') - self.assertEqual(guy_track['comments'], '[ADR] {Actor=Basil Rathbone} $CN=5') - self.assertEqual(guy_track['user_delay_samples'], 0) - self.assertListEqual(guy_track['state'], []) - self.assertEqual(len(guy_track['clips']), 16) - self.assertEqual(guy_track['clips'][5]['channel'], 1) - self.assertEqual(guy_track['clips'][5]['event'], 6) - self.assertEqual(guy_track['clips'][5]['clip_name'], "\"What's your name? You Saxon dog!\" $QN=GY106") - self.assertEqual(guy_track['clips'][5]['start_time'], "01:04:19:15") - self.assertEqual(guy_track['clips'][5]['end_time'], "01:04:21:28") - self.assertEqual(guy_track['clips'][5]['duration'], "00:00:02:13") - self.assertEqual(guy_track['clips'][5]['timestamp'], None) - self.assertEqual(guy_track['clips'][5]['state'], 'Unmuted') + session = parse_document(self.path) + guy_track = session.tracks[5] + self.assertEqual(guy_track.name, 'Guy') + self.assertEqual(guy_track.comments, '[ADR] {Actor=Basil Rathbone} $CN=5') + self.assertEqual(guy_track.user_delay_samples, 0) + self.assertListEqual(guy_track.state, []) + self.assertEqual(len(guy_track.clips), 16) + self.assertEqual(guy_track.clips[5].channel, 1) + self.assertEqual(guy_track.clips[5].event, 6) + self.assertEqual(guy_track.clips[5].clip_name, "\"What's your name? You Saxon dog!\" $QN=GY106") + self.assertEqual(guy_track.clips[5].start_timecode, "01:04:19:15") + self.assertEqual(guy_track.clips[5].finish_timecode, "01:04:21:28") + self.assertEqual(guy_track.clips[5].duration, "00:00:02:13") + self.assertEqual(guy_track.clips[5].timestamp, None) + self.assertEqual(guy_track.clips[5].state, 'Unmuted') def test_memory_locations(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) - - self.assertEqual(len(parsed['markers']),1) - self.assertEqual(parsed['markers'][0]['number'], 1) - self.assertEqual(parsed['markers'][0]['location'], "01:00:00:00") - self.assertEqual(parsed['markers'][0]['time_reference'], 0) - self.assertEqual(parsed['markers'][0]['units'], "Samples") - - def test_transform_timecode(self): - parsed = dict() - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed = visitor.visit(result) - - xformer = ptulsconv.TimecodeInterpreter() - xformer.apply_session_start = True - - xformed = xformer.transform(parsed) - + session = parse_document(self.path) + self.assertEqual(len(session.markers), 1) + self.assertEqual(session.markers[0].number, 1) + self.assertEqual(session.markers[0].location, "01:00:00:00") + self.assertEqual(session.markers[0].time_reference, 0) + self.assertEqual(session.markers[0].units, "Samples") if __name__ == '__main__': diff --git a/tests/test_robinhood5.py b/tests/test_robinhood5.py index 8769db3..e38e040 100644 --- a/tests/test_robinhood5.py +++ b/tests/test_robinhood5.py @@ -1,5 +1,5 @@ import unittest -import ptulsconv +from ptulsconv.docparser import parse_document import os.path @@ -7,50 +7,38 @@ class TestRobinHood5(unittest.TestCase): path = os.path.dirname(__file__) + '/export_cases/Robin Hood Spotting5.txt' def test_skipped_segments(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) - self.assertIsNone(parsed['files']) - self.assertIsNone(parsed['clips']) + session = parse_document(self.path) + self.assertIsNone(session.files) + self.assertIsNone(session.clips) def test_plugins(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) - self.assertEqual(len(parsed['plugins']), 2) + session = parse_document(self.path) + self.assertEqual(len(session.plugins), 2) def test_stereo_track(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) - self.assertEqual(parsed['tracks'][1]['name'], 'MX WT (Stereo)') - self.assertEqual(len(parsed['tracks'][1]['clips']), 2) - self.assertEqual(parsed['tracks'][1]['clips'][0]['clip_name'], 'RobinHood.1-01.L') - self.assertEqual(parsed['tracks'][1]['clips'][1]['clip_name'], 'RobinHood.1-01.R') + session = parse_document(self.path) + self.assertEqual(session.tracks[1].name, 'MX WT (Stereo)') + self.assertEqual(len(session.tracks[1].clips), 2) + self.assertEqual(session.tracks[1].clips[0].clip_name, 'RobinHood.1-01.L') + self.assertEqual(session.tracks[1].clips[1].clip_name, 'RobinHood.1-01.R') def test_a_track(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) + session = parse_document(self.path) - guy_track = parsed['tracks'][8] - self.assertEqual(guy_track['name'], 'Guy') - self.assertEqual(guy_track['comments'], '[ADR] {Actor=Basil Rathbone} $CN=5') - self.assertEqual(guy_track['user_delay_samples'], 0) - self.assertListEqual(guy_track['state'], ['Solo']) - self.assertEqual(len(guy_track['clips']), 16) - self.assertEqual(guy_track['clips'][5]['channel'], 1) - self.assertEqual(guy_track['clips'][5]['event'], 6) - self.assertEqual(guy_track['clips'][5]['clip_name'], "\"What's your name? You Saxon dog!\" $QN=GY106") - self.assertEqual(guy_track['clips'][5]['start_time'], "01:04:19:15.00") - self.assertEqual(guy_track['clips'][5]['end_time'], "01:04:21:28.00") - self.assertEqual(guy_track['clips'][5]['duration'], "00:00:02:13.00") - self.assertEqual(guy_track['clips'][5]['timestamp'], "01:04:19:09.70") - self.assertEqual(guy_track['clips'][5]['state'], 'Unmuted') + guy_track = session.tracks[8] + self.assertEqual(guy_track.name, 'Guy') + self.assertEqual(guy_track.comments, '[ADR] {Actor=Basil Rathbone} $CN=5') + self.assertEqual(guy_track.user_delay_samples, 0) + self.assertListEqual(guy_track.state, ['Solo']) + self.assertEqual(len(guy_track.clips), 16) + self.assertEqual(guy_track.clips[5].channel, 1) + self.assertEqual(guy_track.clips[5].event, 6) + self.assertEqual(guy_track.clips[5].clip_name, "\"What's your name? You Saxon dog!\" $QN=GY106") + self.assertEqual(guy_track.clips[5].start_timecode, "01:04:19:15.00") + self.assertEqual(guy_track.clips[5].finish_timecode, "01:04:21:28.00") + self.assertEqual(guy_track.clips[5].duration, "00:00:02:13.00") + self.assertEqual(guy_track.clips[5].timestamp, "01:04:19:09.70") + self.assertEqual(guy_track.clips[5].state, 'Unmuted') if __name__ == '__main__': diff --git a/tests/test_robinhood6.py b/tests/test_robinhood6.py index 2f08b6a..024f662 100644 --- a/tests/test_robinhood6.py +++ b/tests/test_robinhood6.py @@ -1,5 +1,5 @@ import unittest -import ptulsconv +from ptulsconv.docparser import parse_document import os.path @@ -7,25 +7,24 @@ class TestRobinHood6(unittest.TestCase): path = os.path.dirname(__file__) + '/export_cases/Robin Hood Spotting6.txt' def test_a_track(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) - marian_track = parsed['tracks'][6] - self.assertEqual(marian_track['name'], 'Marian') - self.assertEqual(marian_track['comments'], '[ADR] {Actor=Olivia DeHavilland} $CN=3') - self.assertEqual(marian_track['user_delay_samples'], 0) - self.assertListEqual(marian_track['state'], ['Solo']) - self.assertEqual(len(marian_track['clips']), 4) - self.assertListEqual(marian_track['plugins'], ['Channel Strip (mono)', 'ReVibe II (mono/5.1)']) - self.assertEqual(marian_track['clips'][2]['channel'], 1) - self.assertEqual(marian_track['clips'][2]['event'], 3) - self.assertEqual(marian_track['clips'][2]['clip_name'], "\"Isn't that reason enough for a Royal Ward who must obey her guardian?\" $QN=M103") - self.assertEqual(marian_track['clips'][2]['start_time'], "01:08:01:11") - self.assertEqual(marian_track['clips'][2]['end_time'], "01:08:04:24") - self.assertEqual(marian_track['clips'][2]['duration'], "00:00:03:12") - self.assertEqual(marian_track['clips'][2]['timestamp'], "01:08:01:11") - self.assertEqual(marian_track['clips'][2]['state'], 'Unmuted') + session = parse_document(self.path) + + marian_track = session.tracks[6] + self.assertEqual(marian_track.name, 'Marian') + self.assertEqual(marian_track.comments, '[ADR] {Actor=Olivia DeHavilland} $CN=3') + self.assertEqual(marian_track.user_delay_samples, 0) + self.assertListEqual(marian_track.state, ['Solo']) + self.assertEqual(len(marian_track.clips), 4) + self.assertListEqual(marian_track.plugins, ['Channel Strip (mono)', 'ReVibe II (mono/5.1)']) + self.assertEqual(marian_track.clips[2].channel, 1) + self.assertEqual(marian_track.clips[2].event, 3) + self.assertEqual(marian_track.clips[2].clip_name, + "\"Isn't that reason enough for a Royal Ward who must obey her guardian?\" $QN=M103") + self.assertEqual(marian_track.clips[2].start_timecode, "01:08:01:11") + self.assertEqual(marian_track.clips[2].finish_timecode, "01:08:04:24") + self.assertEqual(marian_track.clips[2].duration, "00:00:03:12") + self.assertEqual(marian_track.clips[2].timestamp, "01:08:01:11") + self.assertEqual(marian_track.clips[2].state, 'Unmuted') if __name__ == '__main__': diff --git a/tests/test_robinhooddf.py b/tests/test_robinhooddf.py index 8fcfedc..d92a2d1 100644 --- a/tests/test_robinhooddf.py +++ b/tests/test_robinhooddf.py @@ -1,5 +1,5 @@ import unittest -import ptulsconv +from ptulsconv.docparser import parse_document import os.path @@ -7,30 +7,23 @@ class TestRobinHoodDF(unittest.TestCase): path = os.path.dirname(__file__) + '/export_cases/Robin Hood SpottingDF.txt' def test_header_export_df(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) - - self.assertTrue('header' in parsed.keys()) - self.assertEqual(parsed['header']['timecode_drop_frame'], True) + session = parse_document(self.path) + self.assertEqual(session.header.timecode_drop_frame, True) def test_a_track(self): - with open(self.path, 'r') as f: - visitor = ptulsconv.DictionaryParserVisitor() - result = ptulsconv.protools_text_export_grammar.parse(f.read()) - parsed: dict = visitor.visit(result) - guy_track = parsed['tracks'][4] - self.assertEqual(guy_track['name'], 'Robin') - self.assertEqual(guy_track['comments'], '[ADR] {Actor=Errol Flynn} $CN=1') - self.assertEqual(guy_track['user_delay_samples'], 0) - self.assertListEqual(guy_track['state'], []) - self.assertEqual(len(guy_track['clips']), 10) - self.assertEqual(guy_track['clips'][5]['channel'], 1) - self.assertEqual(guy_track['clips'][5]['event'], 6) - self.assertEqual(guy_track['clips'][5]['clip_name'], "\"Hold there! What's his fault?\" $QN=R106") - self.assertEqual(guy_track['clips'][5]['start_time'], "01:05:30;15") - self.assertEqual(guy_track['clips'][5]['end_time'], "01:05:32;01") - self.assertEqual(guy_track['clips'][5]['duration'], "00:00:01;16") - self.assertEqual(guy_track['clips'][5]['timestamp'], None) - self.assertEqual(guy_track['clips'][5]['state'], 'Unmuted') + session = parse_document(self.path) + + guy_track = session.tracks[4] + self.assertEqual(guy_track.name, 'Robin') + self.assertEqual(guy_track.comments, '[ADR] {Actor=Errol Flynn} $CN=1') + self.assertEqual(guy_track.user_delay_samples, 0) + self.assertListEqual(guy_track.state, []) + self.assertEqual(len(guy_track.clips), 10) + self.assertEqual(guy_track.clips[5].channel, 1) + self.assertEqual(guy_track.clips[5].event, 6) + self.assertEqual(guy_track.clips[5].clip_name, "\"Hold there! What's his fault?\" $QN=R106") + self.assertEqual(guy_track.clips[5].start_timecode, "01:05:30;15") + self.assertEqual(guy_track.clips[5].finish_timecode, "01:05:32;01") + self.assertEqual(guy_track.clips[5].duration, "00:00:01;16") + self.assertEqual(guy_track.clips[5].timestamp, None) + self.assertEqual(guy_track.clips[5].state, 'Unmuted') diff --git a/tests/test_tag_interpreter.py b/tests/test_tag_interpreter.py index aa4dfc3..59cb33e 100644 --- a/tests/test_tag_interpreter.py +++ b/tests/test_tag_interpreter.py @@ -1,6 +1,5 @@ import unittest - from ptulsconv.docparser.tagged_string_parser_visitor import parse_tags, TagPreModes diff --git a/tests/test_utils.py b/tests/test_utils.py index 2fcf226..78e59ca 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,6 @@ import unittest -from ptulsconv.docparser import apply_appends +from ptulsconv.docparser.tag_compiler import apply_appends class MyTestCase(unittest.TestCase):