diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml new file mode 100644 index 0000000..c8dded9 --- /dev/null +++ b/.github/workflows/python-package.yml @@ -0,0 +1,40 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Lint and Test + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: [3.7, 3.8, 3.9, "3.10"] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 ptulsconv tests --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 ptulsconv tests --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + PYTHONPATH=. pytest diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 118a290..0000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: python -python: - - "3.7" - - "3.8 -script: - - "python -m unittest discover tests" -install: - - "pip install setuptools" - - "pip install parsimonious tqdm" diff --git a/README.md b/README.md index 8c83f3a..f43ae87 100644 --- a/README.md +++ b/README.md @@ -1,97 +1,55 @@ -[![Build Status](https://travis-ci.com/iluvcapra/ptulsconv.svg?branch=master)](https://travis-ci.com/iluvcapra/ptulsconv) - ![](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)](https://pypi.org/project/ptulsconv/) ![](https://img.shields.io/pypi/wheel/ptulsconv.svg) - - ![Upload Python Package](https://github.com/iluvcapra/ptulsconv/workflows/Upload%20Python%20Package/badge.svg) - +![](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] +![Lint and Test](https://github.com/iluvcapra/ptulsconv/actions/workflows/python-package.yml/badge.svg) + +[pypi]: https://pypi.org/project/ptulsconv/ + + # ptulsconv -Read Pro Tools text exports and generate JSON, PDF reports. -## Notice! - -At this time there are a lot of changes in the HEAD of this package and you should use the last posted Pypi package. -New features and much better reporting, including native PDF reports, are coming soon! +Read Pro Tools text exports and generate PDF reports, JSON output. + +## Theory of Operation + +[Avid Pro Tools][avp] can be used to make spotting notes for ADR recording +sessions by creating spotting regions with descriptive text and exporting the +session as text. This file can then be dropped into Excel or any CSV-reading +app like Filemaker Pro. + +**ptulsconv** accepts a text export from Pro Tools and automatically creates +PDF and CSV documents for use in ADR spotting, recording, editing and +reporting, and supplemental JSON documents can be output for use with other +workflows. + +### Reports Generated by ptulsconv by Default + +1. "ADR Report" lists every line in an export with most useful fields, sorted + by time. +2. "Continuity" lists every scene sorted by time. +3. "Line Count" lists a count of every line, collated by reel number and by + effort/TV/optional line designation. +4. "CSV" is a folder of files of all lines collated by character and reel + as CSV files, for use by studio cueing workflows. +5. "Director Logs" is a folder of PDFs formatted like the "ADR Report" except + collated by character. +6. "Supervisor Logs" creates a PDF report for every character, with one line + per page, optimized for note-taking. +7. "Talent Scripts" is a minimal PDF layout of just timecode and line prompt, + collated by character. + + +[avp]: http://www.avid.com/pro-tools + + + ## Installation The easiest way to install on your site is to use `pip`: % pip3 install ptulsconv -This will install the necessary libraries on your host and gives you command-line access to the tool through an -entry-point `ptulsconv`. In a terminal window type `ptulsconv -h` for a list of available options. - -## Theory of Operation - -[Avid Pro Tools][avp] exports a tab-delimited text file organized in multiple parts with an uneven syntax that usually -can't "drop in" to other tools like Excel or Filemaker. This tool accepts a text export from Pro Tools and produces an -XML file in the `FMPXMLRESULT` schema which Filemaker Pro can import directly into a new table. - -In the default mode, all of the clips are parsed and converted into a flat list of events, one Filemaker Pro row per -clip with a start and finish time, track name, session name, etc. Timecodes are parsed and converted into frame counts -and seconds. Text is then parsed for descriptive meta-tags and these are assigned to columns in the output list. - -[avp]: http://www.avid.com/pro-tools - -### Fields in Clip Names - -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]` - -The row output for this clip will contain columns for the values: - -|...| PT.Clip.Name| note | V | FX | DESIGN | ...| -|---|------------|------|---|----|--------|----| -|...| Fireworks explosion| Replace for final | 1 | FX | DESIGN | ... | - -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 - character 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 two clips are named: - -`"Squad fifty-one, what is your status?" [FUTZ] {Ch=Dispatcher} [ADR]` - -`"We are ten-eight at Rampart Hospital." {Ch=Gage} [ADR]` - -The output will contain the range: - -|...| PT.Clip.Name| Ch | FUTZ | ADR | ...| -|---|------------|------|---|----|-----| -|...| "Squad fifty-one, what is your status?"| Dispatcher | FUTZ | ADR | ... | -|...| "We are ten-eight at Rampart Hospital."| Gage | | ADR | ... | - - -### 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 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. - -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 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 value in the later marker - -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 track, the value set on the clip will prevail. - -### Using `@` to Apply Fields to a Span of Clips - -A clip name beginning with "@" will not be included in the CSV 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. - -### Using `&` to Combine Clips - -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 (later 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 become the start time of the row, and the finish -time of the last clip will become the finish time of the row. - +This will install the necessary libraries on your host and gives you +command-line access to the tool through an entry-point `ptulsconv`. In a +terminal window type `ptulsconv -h` for a list of available options. \ No newline at end of file diff --git a/man/ptulsconv.1 b/man/ptulsconv.1 index 70442b5..f02858c 100644 --- a/man/ptulsconv.1 +++ b/man/ptulsconv.1 @@ -1,38 +1,18 @@ .\" Manpage for ptulsconv .\" Contact https://github.com/iluvcapra/ptulsconv -.TH ptulsconv 1 "15 May 2020" "0.4.0" "ptulsconv man page" +.TH ptulsconv 1 "15 May 2020" "0.8.2" "ptulsconv man page" .SH NAME .BR "ptulsconv" " \- convert .IR "Avid Pro Tools" " text exports" .SH SYNOPSIS ptulsconv [OPTIONS] Export.txt .SH DESCRIPTION -Convert a Pro Tools text export into a flat list of clip names with timecodes. A tagging -language is interpreted to add columns and type the data. The default output format is -an XML file for import into Filemaker Pro. +Convert a Pro Tools text export into ADR reports. .SH OPTIONS .IP "-h, --help" show a help message and exit. -.TP -.RI "-i " "TC" -Drop events before this timecode. -.TP -.RI "-o " "TC" -Drop events after this timecode. -.TP -.RI "-m " -Include muted clips. -.TP -.RI "--json " -Output a JSON document instead of XML. (--xform will have no effect.) -.TP -.RI "--xform " "NAME" -Convert the output with a built-in output transform. .TP .RI "--show-available-tags" Print a list of tags that are interpreted and exit. -.TP -.RI "--show-available-transforms" -Print a list of built-in output transforms and exit. .SH AUTHOR Jamie Hardt (contact at https://github.com/iluvcapra/ptulsconv) diff --git a/ptulsconv/__init__.py b/ptulsconv/__init__.py index 2fbdf43..8bb00ec 100644 --- a/ptulsconv/__init__.py +++ b/ptulsconv/__init__.py @@ -1,5 +1,5 @@ from ptulsconv.docparser.ptuls_grammar import protools_text_export_grammar -__version__ = '0.8.0' +__version__ = '0.8.2' __author__ = 'Jamie Hardt' __license__ = 'MIT' diff --git a/ptulsconv/pdf/__init__.py b/ptulsconv/pdf/__init__.py index cb3aea3..d9ca587 100644 --- a/ptulsconv/pdf/__init__.py +++ b/ptulsconv/pdf/__init__.py @@ -71,14 +71,19 @@ def make_doc_template(page_size, filename, document_title, footer_box, page_box = page_box.split_y(0.25 * inch, direction='u') header_box, page_box = page_box.split_y(0.75 * inch, direction='d') title_box, report_box = header_box.split_x(3.5 * inch, direction='r') - + + on_page_lambda = (lambda c, _: + draw_header_footer(c, report_box, title_box, + footer_box,title=title, + supervisor=supervisor, + document_subheader=document_subheader, + client=client, doc_title=document_header)) + + frames = [Frame(page_box.min_x, page_box.min_y, page_box.width, page_box.height)] + page_template = PageTemplate(id="Main", - frames=[Frame(page_box.min_x, page_box.min_y, page_box.width, page_box.height)], - onPage=lambda c, _: draw_header_footer(c, report_box, title_box, footer_box, - title=title, supervisor=supervisor, - document_subheader=document_subheader, - client=client, - doc_title=document_header)) + frames=frames, + onPage=on_page_lambda) pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc')) doc = ADRDocTemplate(filename, diff --git a/reaper/Export Items as Text.py b/reaper/Export Items as Text.py new file mode 100644 index 0000000..fce6828 --- /dev/null +++ b/reaper/Export Items as Text.py @@ -0,0 +1,62 @@ +# Export Items as Text.py +# (c) 2021 Jamie Hardt. All rights reserved. +# +# + +import json +import os.path +import datetime + +item_records = list() + +for i in range(0, RPR_CountMediaItems(0) ): + this_item = RPR_GetMediaItem(0, i) + + item_record = {} + item_record["mute"] = True if RPR_GetMediaItemInfo_Value(this_item, "B_MUTE_ACTUAL") > 0. else False + + item_record["duration"] = RPR_GetMediaItemInfo_Value(this_item, "D_LENGTH") + _, item_record["duration_tc"], _, _, _ = RPR_format_timestr_len(item_record["duration"], "", 128, 0., 5) + + item_record["position"] = RPR_GetMediaItemInfo_Value(this_item, "D_POSITION") + _, item_record["position_tc"], _, _ = RPR_format_timestr_pos(item_record["position"], "", 128, 5) + + item_record["selected"] = True if RPR_GetMediaItemInfo_Value(this_item, "B_UISEL") > 0. else False + _, _, _, item_record["notes"], _ = RPR_GetSetMediaItemInfo_String(this_item, "P_NOTES", "", False) + _, _, _, item_record["item_guid"], _ = RPR_GetSetMediaItemInfo_String(this_item, "GUID", "", False) + + active_take = RPR_GetActiveTake(this_item) + _, _, _, item_record["active_take_name"], _ = RPR_GetSetMediaItemTakeInfo_String(active_take, "P_NAME", "", False) + _, _, _, item_record["active_take_guid"], _ = RPR_GetSetMediaItemTakeInfo_String(active_take, "GUID", "", False) + + item_track = RPR_GetMediaItemTrack(this_item) + _, _, _, item_record["track_name"], _ = RPR_GetSetMediaTrackInfo_String(item_track, "P_NAME", "", False) + _, _, _, item_record["track_guid"], _ = RPR_GetSetMediaTrackInfo_String(item_track, "GUID", "", False) + item_record["track_index"] = RPR_GetMediaTrackInfo_Value(item_track, "IP_TRACKNUMBER") + item_record["track_muted"] = True if RPR_GetMediaTrackInfo_Value(item_track, "B_MUTE") > 0. else False + + item_records = item_records + [item_record] + +output = dict() +output["items"] = item_records +_, output["project_title"], _ = RPR_GetProjectName(0, "", 1024) +_, _, output["project_author"], _ = RPR_GetSetProjectAuthor(0, False, "", 1024) +output["project_frame_rate"], _, output["project_drop_frame"] = RPR_TimeMap_curFrameRate(0, True) + +output_path, _ = RPR_GetProjectPath("", 1024) + +now = datetime.datetime.now() +output_title = output["project_title"] + +if output_title == "": + output_title = "unsaved project" + +output_file_name = "%s Text Export %s.txt" % (output_title, now.strftime('%Y%m%d_%H%M')) +output_path = output_path + "/" + output_file_name + +with open(output_path, "w") as f: + json.dump(output, f, allow_nan=True, indent=4) + +RPR_ShowMessageBox("Exported text file \"%s\" to project folder." % output_file_name, "Text Export Complete", 0) + +#RPR_ShowConsoleMsg(output_path) \ No newline at end of file diff --git a/setup.py b/setup.py index c05a291..a5220c7 100644 --- a/setup.py +++ b/setup.py @@ -25,9 +25,10 @@ setup(name='ptulsconv', 'Topic :: Multimedia :: Sound/Audio', "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Development Status :: 4 - Beta", - "Topic :: Text Processing :: Filters", - "Topic :: Text Processing :: Markup :: XML"], + "Topic :: Text Processing :: Filters"], packages=['ptulsconv'], keywords='text-processing parsers film tv editing editorial', install_requires=['parsimonious', 'tqdm', 'reportlab'],