25 Commits

Author SHA1 Message Date
Jamie Hardt
8ebfd32e02 Update __init__.py
Nudge version
2024-07-10 21:16:45 -07:00
Jamie Hardt
83a9adb48a Merge remote-tracking branch 'refs/remotes/origin/master' 2023-11-15 23:09:27 -08:00
Jamie Hardt
013ebcbe75 movie options 2023-11-15 23:09:12 -08:00
Jamie Hardt
c87695e5fe Merge pull request #12 from iluvcapra/maint-py312
Add Python 3.12 support
2023-11-08 10:33:13 -08:00
Jamie Hardt
4a8983cbbb Update python-package.yml
Added 3.12 to test matrix
2023-11-08 10:27:56 -08:00
Jamie Hardt
9123cbd0b5 Update pyproject.toml
Added 3.12 classifier
2023-11-08 10:27:04 -08:00
Jamie Hardt
4224d106b0 Fixed compyright notice 2023-11-04 11:50:03 -07:00
Jamie Hardt
ac22fab97f Some style fixes (all E231) 2023-11-04 11:36:34 -07:00
Jamie Hardt
64ca2c6c5c Silenced some more errors 2023-11-04 11:23:40 -07:00
Jamie Hardt
c3af30dc6a Renamed my JSONEncoder something useful 2023-11-04 11:21:59 -07:00
Jamie Hardt
c30f675cec Cleared up a type warning 2023-11-04 11:17:48 -07:00
Jamie Hardt
204af7d9cb A bunch of typo cleanups and styling. 2023-11-04 11:13:49 -07:00
Jamie Hardt
10fc211e80 Some typos 2023-11-04 10:56:44 -07:00
Jamie Hardt
d56c7df376 Updated documentation to reflect current usage
No longer have to output a text export.
Some formatting changes.
2023-11-04 10:49:56 -07:00
Jamie Hardt
7b38449a5f Fixed formatting of a list. 2023-11-04 10:43:21 -07:00
Jamie Hardt
17b87b6e69 Update __init__.py
Nudged version
2023-07-27 23:23:39 -07:00
Jamie Hardt
a636791539 Autopep 2023-07-27 23:17:23 -07:00
Jamie Hardt
dfde3c4493 Fixed errors with track_index field
In tests
2023-07-27 23:15:49 -07:00
Jamie Hardt
81909c8a51 Added track index to TrackDescriptor
to indicate a track's import order.
2023-07-27 22:58:06 -07:00
Jamie Hardt
e2b9a20870 Added some documentation 2023-07-27 22:10:29 -07:00
Jamie Hardt
006cec05e5 Merge pull request #10 from iluvcapra/bug-flake8
Flake8 code cleanups and a bug fix
2023-07-22 13:01:15 -07:00
Jamie Hardt
a95f0b5cca Nudged version number 2023-07-22 12:58:32 -07:00
Jamie Hardt
70a5206d73 Fixed dumb typo that made ptsl break 2023-07-21 22:21:48 -07:00
Jamie Hardt
128eed002d Update README.md 2023-07-21 14:54:54 -07:00
Jamie Hardt
f8a0d70942 Update README.md
Dumb typo in "last commit" badge
2023-07-21 14:54:23 -07:00
13 changed files with 110 additions and 81 deletions

View File

@@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [3.8, 3.9, "3.10", "3.11"]
python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v2.5.0

View File

@@ -2,7 +2,7 @@
![](https://img.shields.io/github/license/iluvcapra/ptulsconv.svg)
![](https://img.shields.io/pypi/pyversions/ptulsconv.svg)
[![](https://img.shields.io/pypi/v/ptulsconv.svg)][pypi]
![GitHub last commit](https://img.shields.io/github/last-commit/iluvcapra/pycmx)
![GitHub last commit](https://img.shields.io/github/last-commit/iluvcapra/ptulsconv)
[![Lint and Test](https://github.com/iluvcapra/ptulsconv/actions/workflows/python-package.yml/badge.svg)](https://github.com/iluvcapra/ptulsconv/actions/workflows/python-package.yml)
[pypi]: https://pypi.org/project/ptulsconv/
@@ -10,7 +10,7 @@
# ptulsconv
Read Pro Tools text exports and generate PDF reports, JSON output.
Parse Pro Tools text exports and generate PDF reports, JSON output.
## Quick Start

View File

@@ -15,7 +15,7 @@ import ptulsconv
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'ptulsconv'
# copyright = ptulsconv.__copyright__
copyright = '2019-2023 Jamie Hardt. All rights reserved'
# author = ptulsconv.__author__
release = ptulsconv.__version__

View File

@@ -24,19 +24,21 @@ Step 2: Add More Information to Your Spots
Clips, tracks and markers in your session can contain additional information
to make your ADR reports more complete and useful. You add this information
with *tagging*.
with :ref:`tagging<tags>`.
* Every ADR clip must have a unique cue number. After the name of each clip,
add the letters "$QN=" and then a unique number (any combination of letters
or numbers that don't contain a space). You can type these yourself or add
them with batch-renaming when you're done spotting.
* **Every ADR clip must have a unique cue number.** After the name of each
clip, add the letters ``$QN=`` and then a unique number (any combination of
letters or numbers that don't contain a space). You can type these yourself
or add them with batch-renaming when you're done spotting.
* ADR spots should usually have a reason indicated, so you can remember exactly
why you're replacing a particular line. Do this by adding the the text "{R="
to your clip names after the prompt and then some short text describing the
reason, and then a closing "}". You can type anything, including spaces.
* If a line is a TV cover line, you can add the text "[TV]" to the end.
why you're replacing a particular line. Do this by adding the the text
``{R=`` to your clip names after the prompt and then some short text
describing the reason, and then a closing ``}``. You can type anything,
including spaces.
* If, for example, a line is a TV cover line, you can add the text ``[TV]`` to
the end.
So for example, some ADR spot's clip name might look like:
So for example, some ADR spot's clip name might look like::
Get to the ladder! {R=Noise} $QN=J1001
"Forget your feelings! {R=TV Cover} $QN=J1002 [TV]
@@ -45,32 +47,26 @@ These tags can appear in any order.
* You can add the name of an actor to a character's track, so this information
will appear on your reports. In the track name, or in the track comments,
type "{Actor=xxx}" replacing the xxx with the actor's name.
type ``{Actor=xxx}`` replacing the xxx with the actor's name.
* Characters need to have a number (perhaps from the cast list) to express how
they should be collated. Add "$CN=xxx" with a unique number to each track (or
the track's comments.)
they should be collated. Add ``$CN=xxx`` with
a unique number to each track (or the track's comments.)
* Set the scene for each line with markers. Create a marker at the beginning of
a scene and make it's name "{Sc=xxx}", replacing the xxx with the scene
a scene and make it's name ``{Sc=xxx}``, replacing the xxx with the scene
number and name.
Step 3: Export Tracks from Pro Tools as a Text File
---------------------------------------------------
Step 3: Run `ptulsconv`
------------------------
Export the file as a UTF-8 and be sure to include clips and markers. Export
using the Timecode time format.
In Pro Tools, select the tracks that contain your spot clips.
Do not export crossfades.
Then, in your Terminal, run the following command::
ptulsconv
Step 4: Run `ptulsconv` on the Text Export
------------------------------------------
In your Terminal, run the following command:
ptulsconv path/to/your/TEXT_EXPORT.txt
`ptulsconv` will create a folder named "Title_CURRENT_DATE", and within that
`ptulsconv` will connect to Pro Tools and read all of the clips on the selected
track. It will then create a folder named "Title_CURRENT_DATE", and within that
folder it will create several PDFs and folders:
- "TITLE ADR Report" 📄 a PDF tabular report of every ADR line you've spotted.

View File

@@ -4,8 +4,8 @@ Tagging
=======
Tags are used to add additional data to a clip in an organized way. The
tagging system in `ptulsconv` allows is flexible and can be used to add
any kind of extra data to a clip.
tagging system in `ptulsconv` is flexible and can be used to add any kind of
extra data to a clip.
Fields in Clip Names
--------------------
@@ -14,7 +14,7 @@ Track names, track comments, and clip names can also contain meta-tags, or
"fields," to add additional columns to the output. Thus, if a clip has the
name:::
`Fireworks explosion {note=Replace for final} $V=1 [FX] [DESIGN]`
Fireworks explosion {note=Replace for final} $V=1 [FX] [DESIGN]
The row output for this clip will contain columns for the values:
@@ -27,20 +27,24 @@ The row output for this clip will contain columns for the values:
These fields can be defined in the clip name in three ways:
* `$NAME=VALUE` creates a field named `NAME` with a one-word value `VALUE`.
* `{NAME=VALUE}` creates a field named `NAME` with the value `VALUE`. `VALUE`
in this case may contain spaces or any chartacter up to the closing bracket.
* `[NAME]` creates a field named `NAME` with a value `NAME`. This can be used
to create a boolean-valued field; in the output, clips with the field
will have it, and clips without will have the column with an empty value.
* ``$NAME=VALUE`` creates a field named ``NAME`` with a one-word value
``VALUE``.
* ``{NAME=VALUE}`` creates a field named ``NAME`` with the value ``VALUE``.
``VALUE`` in this case may contain spaces or any chartacter up to the
closing bracket.
* ``[NAME]`` creates a field named ``NAME`` with a value ``NAME``. This can
be used to create a boolean-valued field; in the output, clips with the
field will have it, and clips without will have the column with an empty
value.
For example, if three clips are named:::
`"Squad fifty-one, what is your status?" [FUTZ] {Ch=Dispatcher} [ADR]`
"Squad fifty-one, what is your status?" [FUTZ] {Ch=Dispatcher} [ADR]
`"We are ten-eight at Rampart Hospital." {Ch=Gage} [ADR]`
"We are ten-eight at Rampart Hospital." {Ch=Gage} [ADR]
`(1M) FC callouts rescuing trapped survivors. {Ch=Group} $QN=1001 [GROUP]`
(1M) FC callouts rescuing trapped survivors. {Ch=Group} $QN=1001 [GROUP]
The output will contain the range:
@@ -63,7 +67,7 @@ Fields in Track Names and Markers
---------------------------------
Fields set in track names, and in track comments, will be applied to *each*
clip on that track. If a track comment contains the text `{Dept=Foley}` for
clip on that track. If a track comment contains the text ``{Dept=Foley}`` for
example, every clip on that track will have a "Foley" value in a "Dept" column.
Likewise, fields set on the session name will apply to all clips in the session.
@@ -84,17 +88,17 @@ track, the value set on the clip will prevail.
Apply Fields to a Time Range of Clips
-------------------------------------
A clip name beginning with "@" will not be included in the output, but its
A clip name beginning with ``@`` will not be included in the output, but its
fields will be applied to clips within its time range on lower tracks.
If track 1 has a clip named `@ {Sc=1- The House}`, any clips beginning within
that range on lower tracks will have a field `Sc` with that value.
If track 1 has a clip named ``@ {Sc=1- The House}``, any clips beginning within
that range on lower tracks will have a field ``Sc`` with that value.
Combining Clips with Long Names or Many Tags
--------------------------------------------
A clip name beginning with `&` will have its parsed clip name appended to the
A clip name beginning with ``&`` will have its parsed clip name appended to the
preceding cue, and the fields of following cues will be applied, earlier clips
having precedence. The clips need not be touching, and the clips will be
combined into a single row of the output. The start time of the first clip will
@@ -108,23 +112,24 @@ Setting Document Options
.. note::
Document options are not yet implemented.
A clip beginning with `!` sends a command to `ptulsconv`. These commands can
appear anywhere in the document and apply to the entire document. Commands are
a list of words
..
A clip beginning with ``!`` sends a command to `ptulsconv`. These commands can
appear anywhere in the document and apply to the entire document. Commands are
a list of words
The following commands are available:
The following commands are available:
page $SIZE=`(letter|legal|a4)`
Sets the PDF page size for the output.
page $SIZE=`(letter|legal|a4)`
Sets the PDF page size for the output.
font {NAME=`name`} {PATH=`path`}
Sets the primary font for the output.
font {NAME=`name`} {PATH=`path`}
Sets the primary font for the output.
sub `replacement text` {FOR=`text_to_replace`} {IN=`tag`}
Declares a substitution. Whereever text_to_replace is encountered in the
document it will be replaced with "replacement text".
sub `replacement text` {FOR=`text_to_replace`} {IN=`tag`}
Declares a substitution. Whereever text_to_replace is encountered in the
document it will be replaced with "replacement text".
If `tag` is set, this substitution will only be applied to the values of
that tag.
If `tag` is set, this substitution will only be applied to the values of
that tag.

View File

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

View File

@@ -41,6 +41,11 @@ def main():
default='doc',
help='Set output format, `raw`, `tagged`, `doc`.')
parser.add_option('-m', '--movie-opts',
dest='movie_opts',
metavar="MOVIE_OPTS",
help="Set movie options")
warn_options = OptionGroup(title="Warning and Validation Options",
parser=parser)

View File

@@ -14,9 +14,9 @@ from fractions import Fraction
import ptsl
from .docparser.adr_entity import make_entities, ADRLine
from .reporting import print_section_header_style, print_status_style,\
from .reporting import print_section_header_style, print_status_style, \
print_warning
from .validations import validate_unique_field, validate_non_empty_field,\
from .validations import validate_unique_field, validate_non_empty_field, \
validate_dependent_value
from ptulsconv.docparser import parse_document
@@ -32,7 +32,7 @@ from ptulsconv.pdf.continuity import output_report as output_continuity
from json import JSONEncoder
class MyEncoder(JSONEncoder):
class FractionEncoder(JSONEncoder):
"""
A subclass of :class:`JSONEncoder` which encodes :class:`Fraction` objects
as a dict.
@@ -97,7 +97,7 @@ def output_adr_csv(lines: List[ADRLine], time_format: TimecodeFormat):
os.chdir("..")
def generate_documents(session_tc_format, scenes, adr_lines: Iterator[ADRLine],
def generate_documents(session_tc_format, scenes, adr_lines: List[ADRLine],
title):
"""
Create PDF output.
@@ -112,7 +112,7 @@ def generate_documents(session_tc_format, scenes, adr_lines: Iterator[ADRLine],
supervisor = next((x.supervisor for x in adr_lines), "")
output_continuity(scenes=scenes, tc_display_format=session_tc_format,
title=title, client=client,
title=title, client=client or "",
supervisor=supervisor)
reels = ['R1', 'R2', 'R3', 'R4', 'R5', 'R6']
@@ -187,13 +187,13 @@ def convert(major_mode, input_file=None, output=sys.stdout, warnings=True):
req.time_type("tc")
req.dont_show_crossfades()
req.selected_tracks_only()
session_text = req.export_string
session_text = req.export_string()
session = parse_document(session_text)
session_tc_format = session.header.timecode_format
if major_mode == 'raw':
output.write(MyEncoder().encode(session))
output.write(FractionEncoder().encode(session))
else:
compiler = TagCompiler()
@@ -201,7 +201,7 @@ def convert(major_mode, input_file=None, output=sys.stdout, warnings=True):
compiled_events = list(compiler.compile_events())
if major_mode == 'tagged':
output.write(MyEncoder().encode(compiled_events))
output.write(FractionEncoder().encode(compiled_events))
elif major_mode == 'doc':
generic_events, adr_lines = make_entities(compiled_events)
@@ -225,9 +225,10 @@ def convert(major_mode, input_file=None, output=sys.stdout, warnings=True):
print_status_style("%i ADR events found." % len(adr_lines))
if warnings:
perform_adr_validations(adr_lines)
perform_adr_validations(iter(adr_lines))
generate_documents(session_tc_format, scenes, adr_lines, title)
generate_documents(session_tc_format, scenes, adr_lines,
title)
def perform_adr_validations(lines: Iterator[ADRLine]):

View File

@@ -20,6 +20,9 @@ class SessionDescriptor:
self.markers = kwargs['markers']
def markers_timed(self) -> Iterator[Tuple['MarkerDescriptor', Fraction]]:
"""
Iterate each marker in the session with its respective time reference.
"""
for marker in self.markers:
marker_time = Fraction(marker.time_reference,
int(self.header.sample_rate))
@@ -28,6 +31,9 @@ class SessionDescriptor:
def tracks_clips(self) -> Iterator[Tuple['TrackDescriptor',
'TrackClipDescriptor']]:
"""
Iterate each track clip with its respective owning clip.
"""
for track in self.tracks:
for clip in track.clips:
yield track, clip
@@ -37,7 +43,10 @@ class SessionDescriptor:
Fraction, Fraction, Fraction]
]:
"""
:return: A Generator that yields track, clip, start time, finish time,
Iterate each track clip with its respective owning clip and timing
information.
:returns: A Generator that yields track, clip, start time, finish time,
and timestamp
"""
for track, clip in self.tracks_clips():
@@ -115,6 +124,7 @@ class HeaderDescriptor:
class TrackDescriptor:
index: int
name: str
comments: str
user_delay_samples: int
@@ -123,6 +133,7 @@ class TrackDescriptor:
clips: List["TrackClipDescriptor"]
def __init__(self, **kwargs):
self.index = kwargs['index']
self.name = kwargs['name']
self.comments = kwargs['comments']
self.user_delay_samples = kwargs['user_delay_samples']

View File

@@ -1,8 +1,8 @@
from parsimonious.nodes import NodeVisitor
from parsimonious.grammar import Grammar
from .doc_entity import SessionDescriptor, HeaderDescriptor, TrackDescriptor,\
FileDescriptor, TrackClipDescriptor, ClipDescriptor, PluginDescriptor,\
from .doc_entity import SessionDescriptor, HeaderDescriptor, TrackDescriptor, \
FileDescriptor, TrackClipDescriptor, ClipDescriptor, PluginDescriptor, \
MarkerDescriptor
@@ -108,8 +108,12 @@ def parse_document(session_text: str) -> SessionDescriptor:
class DocParserVisitor(NodeVisitor):
@staticmethod
def visit_document(_, visited_children) -> SessionDescriptor:
def __init__(self):
self.track_index = 0
# @staticmethod
def visit_document(self, _, visited_children) -> SessionDescriptor:
self.track_index = 0
files = next(iter(visited_children[1]), None)
clips = next(iter(visited_children[2]), None)
plugins = next(iter(visited_children[3]), None)
@@ -166,8 +170,8 @@ class DocParserVisitor(NodeVisitor):
count_instances=child[10]),
visited_children[2]))
@staticmethod
def visit_track_block(_, visited_children):
# @staticmethod
def visit_track_block(self, _, visited_children):
track_header, track_clip_list = visited_children
clips = []
for clip in track_clip_list:
@@ -179,7 +183,11 @@ class DocParserVisitor(NodeVisitor):
for plugin in plugin_opt[1]:
plugins.append(plugin[1])
this_index = self.track_index
self.track_index += 1
return TrackDescriptor(
index=this_index,
name=track_header[2],
comments=track_header[6],
user_delay_samples=track_header[10],

View File

@@ -5,7 +5,7 @@ from .__init__ import make_doc_template
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter
from reportlab.platypus import Paragraph, Spacer, KeepTogether, Table,\
from reportlab.platypus import Paragraph, Spacer, KeepTogether, Table, \
HRFlowable
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib import colors

View File

@@ -17,6 +17,7 @@ classifiers = [
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Development Status :: 5 - Production/Stable",
"Topic :: Text Processing :: Filters"
]

View File

@@ -88,7 +88,9 @@ class TestTagCompiler(unittest.TestCase):
state='Unmuted',
timestamp=None),
]
test_track = doc_entity.TrackDescriptor(name="Track 1 [A] {Color=Red} $Mode=1",
test_track = doc_entity.TrackDescriptor(
index=0,
name="Track 1 [A] {Color=Red} $Mode=1",
comments="{Comment=This is some text in the comments}",
user_delay_samples=0,
plugins=[],