16 Commits

Author SHA1 Message Date
Jamie Hardt
aa7b418121 Update __init__.py
Nudging version to 2.2.1
2025-05-24 21:58:50 -07:00
Jamie Hardt
a519a525b2 Update pythonpublish.yml
Updating python publish action to the latest version
2025-05-24 21:54:42 -07:00
Jamie Hardt
1412efe509 autopep 2025-05-18 13:39:06 -07:00
Jamie Hardt
12a6c05467 autopep 2025-05-18 13:37:46 -07:00
Jamie Hardt
cf87986014 autopep'd test 2025-05-18 13:35:12 -07:00
Jamie Hardt
67533879f8 Rewrote parsing to handle old & new-style markers 2025-05-18 13:33:51 -07:00
Jamie Hardt
f847b88aa3 Nudged version and copyright date 2025-05-17 12:06:56 -07:00
Jamie Hardt
c3a600c5d7 Integrated track marker test case and fixed parser 2025-05-17 12:05:27 -07:00
Jamie Hardt
914783a809 Updated documentation 2025-05-17 11:26:07 -07:00
Jamie Hardt
c638c673e8 Adding track marker export case 2025-05-17 11:23:54 -07:00
Jamie Hardt
15fe6667af Fixed up unit test 2025-05-17 11:23:02 -07:00
Jamie Hardt
d4e23b59eb Adding support for track markers
(Always ignore for now)
2025-05-17 11:19:22 -07:00
Jamie Hardt
a602b09551 flake8 2025-05-17 10:47:21 -07:00
Jamie Hardt
448d93d717 Fix for flake 2025-05-17 10:45:40 -07:00
Jamie Hardt
59e7d40d97 Merge branch 'master' of https://github.com/iluvcapra/ptulsconv 2025-05-11 22:19:26 -07:00
Jamie Hardt
eaa5fe824f Fixed parser logic to handle new-style marker tracks 2025-05-11 22:17:42 -07:00
8 changed files with 115 additions and 29 deletions

View File

@@ -26,7 +26,7 @@ jobs:
- name: Build package - name: Build package
run: python -m build run: python -m build
- name: pypi-publish - name: pypi-publish
uses: pypa/gh-action-pypi-publish@v1.8.6 uses: pypa/gh-action-pypi-publish@v1.12.4
# - name: Report to Mastodon # - name: Report to Mastodon
# uses: cbrgm/mastodon-github-action@v1.0.1 # uses: cbrgm/mastodon-github-action@v1.0.1
# with: # with:

View File

@@ -76,7 +76,10 @@ Fields set in markers, and in marker comments, will be applied to all clips
whose finish is *after* that marker. Fields in markers are applied cumulatively whose finish is *after* that marker. Fields in markers are applied cumulatively
from breakfast to dinner in the session. The latest marker applying to a clip has from breakfast to dinner in the session. The latest marker applying to a clip has
precedence, so if one marker comes after the other, but both define a field, the precedence, so if one marker comes after the other, but both define a field, the
value in the later marker value in the later marker.
All markers on all rulers will be scanned for tags. All markers on tracks will
be ignored.
An important note here is that, always, fields set on the clip name have the An important note here is that, always, fields set on the clip name have the
highest precedence. If a field is set in a clip name, the same field set on the highest precedence. If a field is set in a clip name, the same field set on the

View File

@@ -2,8 +2,8 @@
Parse and convert Pro Tools text exports Parse and convert Pro Tools text exports
""" """
__version__ = '2.1.1' __version__ = '2.2.1'
__author__ = 'Jamie Hardt' __author__ = 'Jamie Hardt'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = "%s %s (c) 2023 %s. All rights reserved." \ __copyright__ = "%s %s (c) 2025 %s. All rights reserved." \
% (__name__, __version__, __author__) % (__name__, __version__, __author__)

View File

@@ -19,11 +19,17 @@ class SessionDescriptor:
self.tracks = kwargs['tracks'] self.tracks = kwargs['tracks']
self.markers = kwargs['markers'] self.markers = kwargs['markers']
def markers_timed(self) -> Iterator[Tuple['MarkerDescriptor', Fraction]]: def markers_timed(self,
only_ruler_markers: bool = True) -> \
Iterator[Tuple['MarkerDescriptor', Fraction]]:
""" """
Iterate each marker in the session with its respective time reference. Iterate each marker in the session with its respective time reference.
""" """
for marker in self.markers: for marker in self.markers:
if marker.track_marker and only_ruler_markers:
continue
marker_time = Fraction(marker.time_reference, marker_time = Fraction(marker.time_reference,
int(self.header.sample_rate)) int(self.header.sample_rate))
# marker_time = self.header.convert_timecode(marker.location) # marker_time = self.header.convert_timecode(marker.location)
@@ -182,6 +188,7 @@ class MarkerDescriptor:
units: str units: str
name: str name: str
comments: str comments: str
track_marker: bool
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.number = kwargs['number'] self.number = kwargs['number']
@@ -190,3 +197,4 @@ class MarkerDescriptor:
self.units = kwargs['units'] self.units = kwargs['units']
self.name = kwargs['name'] self.name = kwargs['name']
self.comments = kwargs['comments'] self.comments = kwargs['comments']
self.track_marker = kwargs['track_marker']

View File

@@ -9,7 +9,8 @@ from .doc_entity import SessionDescriptor, HeaderDescriptor, TrackDescriptor, \
protools_text_export_grammar = Grammar( protools_text_export_grammar = Grammar(
r""" r"""
document = header files_section? clips_section? plugin_listing? document = header files_section? clips_section? plugin_listing?
track_listing? markers_listing? track_listing? markers_block?
header = "SESSION NAME:" fs string_value rs header = "SESSION NAME:" fs string_value rs
"SAMPLE RATE:" fs float_value rs "SAMPLE RATE:" fs float_value rs
"BIT DEPTH:" fs integer_value "-bit" rs "BIT DEPTH:" fs integer_value "-bit" rs
@@ -74,17 +75,33 @@ protools_text_export_grammar = Grammar(
track_clip_state = ("Muted" / "Unmuted") track_clip_state = ("Muted" / "Unmuted")
markers_listing = markers_listing_header markers_column_header markers_block = markers_block_header
marker_record* (markers_list / markers_list_simple)
markers_listing_header = "M A R K E R S L I S T I N G" rs
markers_column_header = "# " fs "LOCATION " fs markers_list_simple = markers_column_header_simple marker_record_simple*
"TIME REFERENCE " fs
"UNITS " fs markers_list = markers_column_header marker_record*
"NAME " fs
"COMMENTS" rs markers_block_header = "M A R K E R S L I S T I N G" rs
markers_column_header_simple =
"# LOCATION TIME REFERENCE "
"UNITS NAME "
"COMMENTS" rs
markers_column_header =
"# LOCATION TIME REFERENCE "
"UNITS NAME "
"TRACK NAME "
"TRACK TYPE COMMENTS" rs
marker_record_simple = integer_value isp fs string_value fs
integer_value isp fs string_value fs string_value
fs string_value rs
marker_record = integer_value isp fs string_value fs integer_value isp fs marker_record = integer_value isp fs string_value fs integer_value isp fs
string_value fs string_value fs string_value rs string_value fs string_value fs string_value fs
string_value fs string_value rs
fs = "\t" fs = "\t"
rs = "\n" rs = "\n"
@@ -231,22 +248,37 @@ class DocParserVisitor(NodeVisitor):
return node.text return node.text
@staticmethod @staticmethod
def visit_markers_listing(_, visited_children): def visit_markers_block(_, visited_children):
markers = [] markers = []
for marker in visited_children[2]: for marker in visited_children[1][0][1]:
markers.append(marker) markers.append(marker)
return markers return markers
@staticmethod @staticmethod
def visit_marker_record(_, visited_children): def visit_marker_record_simple(_, 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],
track_marker=False)
@staticmethod
def visit_marker_record(_, visited_children):
track_type = visited_children[15]
is_track_marker = (track_type == "Track")
return MarkerDescriptor(number=visited_children[0],
location=visited_children[3],
time_reference=visited_children[5],
units=visited_children[8],
name=visited_children[10],
comments=visited_children[16],
track_marker=is_track_marker)
@staticmethod @staticmethod
def visit_formatted_clip_name(_, visited_children): def visit_formatted_clip_name(_, visited_children):

View File

@@ -0,0 +1,24 @@
SESSION NAME: Test for ptulsconv
SAMPLE RATE: 48000.000000
BIT DEPTH: 24-bit
SESSION START TIMECODE: 00:00:00:00
TIMECODE FORMAT: 23.976 Frame
# OF AUDIO TRACKS: 1
# OF AUDIO CLIPS: 0
# OF AUDIO FILES: 0
T R A C K L I S T I N G
TRACK NAME: Hamlet
COMMENTS: {Actor=Laurence Olivier}
USER DELAY: 0 Samples
STATE:
CHANNEL EVENT CLIP NAME START TIME END TIME DURATION STATE
1 1 Test Line 1 $QN=T1001 00:00:00:00 00:00:02:00 00:00:02:00 Unmuted
1 2 Test Line 2 $QN=T1002 00:00:04:00 00:00:06:00 00:00:02:00 Unmuted
M A R K E R S L I S T I N G
# LOCATION TIME REFERENCE UNITS NAME TRACK NAME TRACK TYPE COMMENTS
1 00:00:00:00 0 Samples {Title=Multiple Marker Rulers Project} Markers Ruler
2 00:00:04:00 192192 Samples Track Marker Hamlet Track

View File

@@ -2,33 +2,52 @@ import unittest
import tempfile import tempfile
import sys
import os.path import os.path
import os import os
import glob import glob
from ptulsconv import commands from ptulsconv import commands
class TestPDFExport(unittest.TestCase): class TestPDFExport(unittest.TestCase):
def test_report_generation(self): def test_report_generation(self):
""" """
Setp through every text file in export_cases and make sure it can Setp through every text file in export_cases and make sure it can
be converted into PDF docs without throwing an error be converted into PDF docs without throwing an error
""" """
files = [os.path.dirname(__file__) + "/../export_cases/Robin Hood Spotting.txt"] files = []
#files.append(os.path.dirname(__file__) + "/../export_cases/Robin Hood Spotting2.txt") files = [os.path.dirname(__file__) +
"/../export_cases/Robin Hood Spotting.txt"]
for path in files: for path in files:
tempdir = tempfile.TemporaryDirectory() tempdir = tempfile.TemporaryDirectory()
os.chdir(tempdir.name) os.chdir(tempdir.name)
try: try:
commands.convert(input_file=path, major_mode='doc') commands.convert(input_file=path, major_mode='doc')
except: except Exception as e:
assert False, "Error processing file %s" % path print("Error in test_report_generation")
print(f"File: {path}")
print(repr(e))
raise e
finally:
tempdir.cleanup()
def test_report_generation_track_markers(self):
files = []
files.append(os.path.dirname(__file__) +
"/../export_cases/Test for ptulsconv.txt")
for path in files:
tempdir = tempfile.TemporaryDirectory()
os.chdir(tempdir.name)
try:
commands.convert(input_file=path, major_mode='doc')
except Exception as e:
print("Error in test_report_generation_track_markers")
print(f"File: {path}")
print(repr(e))
raise e
finally: finally:
tempdir.cleanup() tempdir.cleanup()
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@@ -102,14 +102,14 @@ class TestTagCompiler(unittest.TestCase):
time_reference=48000 * 3600, time_reference=48000 * 3600,
units="Samples", units="Samples",
name="Marker 1 {Part=1}", name="Marker 1 {Part=1}",
comments="" comments="", track_marker=False,
), ),
doc_entity.MarkerDescriptor(number=2, doc_entity.MarkerDescriptor(number=2,
location="01:00:01:00", location="01:00:01:00",
time_reference=48000 * 3601, time_reference=48000 * 3601,
units="Samples", units="Samples",
name="Marker 2 {Part=2}", name="Marker 2 {Part=2}",
comments="[M1]" comments="[M1]", track_marker=False,
), ),
] ]