mirror of
https://github.com/iluvcapra/ptulsconv.git
synced 2026-10-02 01:37:17 +00:00
Compare commits
8 Commits
ab19793bd1
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 9349f4ba1e | |||
| e0e3024280 | |||
| 2fc8a653cd | |||
| 183a7ef687 | |||
| e0fe946455 | |||
| 12ecb86bdf | |||
| 6f2367c9fd | |||
| 8a23d7c702 |
@@ -16,7 +16,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
||||
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2.5.0
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
name: Test Toot
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
jobs:
|
||||
print-tag:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Report to Mastodon
|
||||
uses: cbrgm/mastodon-github-action@v1.0.1
|
||||
env:
|
||||
MASTODON_URL: ${{ secrets.MASTODON_URL }}
|
||||
MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }}
|
||||
with:
|
||||
message: |
|
||||
This is a test toot, automatically posted by a github action.
|
||||
|
||||
${{ github.server_url }}/${{ github.repository }}
|
||||
|
||||
${{ github.ref }}
|
||||
@@ -14,9 +14,6 @@ build:
|
||||
pre_install:
|
||||
- pip install --upgrade pip
|
||||
- pip install --upgrade --upgrade-strategy only-if-needed --group doc .
|
||||
# build:
|
||||
# html:
|
||||
# - python -m sphinx -T -b html -d _build/doctrees -D language=en . $READTHEDOCS_OUTPUT/html
|
||||
post_build:
|
||||
- echo "Command run at 'post_build' step"
|
||||
- echo `date`
|
||||
@@ -29,16 +26,3 @@ sphinx:
|
||||
#If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||
formats:
|
||||
- pdf
|
||||
|
||||
# FIXME: We should be installing the `doc` dependency group and not the `doc`
|
||||
# extra.
|
||||
|
||||
#Optionally declare the Python requirements required to build your docs
|
||||
|
||||
python:
|
||||
install:
|
||||
- method: pip
|
||||
path: .
|
||||
extra_requirements:
|
||||
- doc
|
||||
|
||||
|
||||
+9
-2
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "ptulsconv"
|
||||
version = "4.0.0"
|
||||
version = "4.0.2"
|
||||
description = "Read Pro Tools Text exports and generate PDF ADR Reports, JSON"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
@@ -9,7 +9,6 @@ license-files = ["LICENSE"]
|
||||
keywords = ["text-processing", "parsers", "film",
|
||||
"broadcast", "editing", "editorial"]
|
||||
classifiers = [
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Topic :: Multimedia',
|
||||
'Topic :: Multimedia :: Sound/Audio',
|
||||
"Programming Language :: Python :: 3.9",
|
||||
@@ -17,6 +16,7 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Topic :: Text Processing :: Filters"
|
||||
]
|
||||
@@ -42,6 +42,13 @@ doc = [
|
||||
"sphinx-rtd-theme>=3.0.2",
|
||||
]
|
||||
|
||||
[tool.ruff.lint]
|
||||
ignore = [
|
||||
"DTZ005" # datetime created without a tz
|
||||
]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"reaper/Export Items as Text.py" = ["F821"]
|
||||
|
||||
[project.scripts]
|
||||
ptulsconv = "ptulsconv:__main__.main"
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
# type: ignore[reportUndefinedVariable]
|
||||
|
||||
# Export Items as Text.py
|
||||
# (c) 2021 Jamie Hardt. All rights reserved.
|
||||
#
|
||||
#
|
||||
|
||||
import datetime
|
||||
import json
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
item_records = list()
|
||||
item_records = []
|
||||
|
||||
for i in range(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.0 else False
|
||||
)
|
||||
item_record["mute"] = RPR_GetMediaItemInfo_Value(this_item, "B_MUTE_ACTUAL") > 0.0
|
||||
|
||||
item_record["duration"] = RPR_GetMediaItemInfo_Value(this_item, "D_LENGTH")
|
||||
_, item_record["duration_tc"], _, _, _ = RPR_format_timestr_len(
|
||||
@@ -26,9 +26,7 @@ for i in range(RPR_CountMediaItems(0)):
|
||||
item_record["position"], "", 128, 5
|
||||
)
|
||||
|
||||
item_record["selected"] = (
|
||||
True if RPR_GetMediaItemInfo_Value(this_item, "B_UISEL") > 0.0 else False
|
||||
)
|
||||
item_record["selected"] = RPR_GetMediaItemInfo_Value(this_item, "B_UISEL") > 0.0
|
||||
_, _, _, item_record["notes"], _ = RPR_GetSetMediaItemInfo_String(
|
||||
this_item, "P_NOTES", "", False
|
||||
)
|
||||
@@ -54,13 +52,11 @@ for i in range(RPR_CountMediaItems(0)):
|
||||
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.0 else False
|
||||
)
|
||||
item_record["track_muted"] = RPR_GetMediaTrackInfo_Value(item_track, "B_MUTE") > 0.0
|
||||
|
||||
item_records = item_records + [item_record]
|
||||
|
||||
output = dict()
|
||||
output = {}
|
||||
output["items"] = item_records
|
||||
_, output["project_title"], _ = RPR_GetProjectName(0, "", 1024)
|
||||
_, _, output["project_author"], _ = RPR_GetSetProjectAuthor(0, False, "", 1024)
|
||||
@@ -76,15 +72,14 @@ 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_file_name = f"{output_title} Text Export {now.strftime('%Y%m%d_%H%M')}.txt"
|
||||
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",
|
||||
f'Exported text file "{output_file_name}" to project folder. Text Export Complete',
|
||||
0,
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ from ptulsconv.commands import convert
|
||||
from ptulsconv.docparser.adr_entity import ADR_TAG_MAPPING, GENERIC_TAG_MAPPING
|
||||
from ptulsconv.reporting import (
|
||||
print_banner_style,
|
||||
print_fatal_error,
|
||||
print_section_header_style,
|
||||
print_status_style,
|
||||
)
|
||||
@@ -102,7 +101,7 @@ def main():
|
||||
(options, args) = parser.parse_args(sys.argv)
|
||||
|
||||
print_section_header_style("Startup")
|
||||
print_status_style("This run started %s" % (datetime.datetime.now().isoformat()))
|
||||
print_status_style(f"This run started {datetime.datetime.now().isoformat()}")
|
||||
|
||||
if options.show_tags:
|
||||
dump_field_map()
|
||||
@@ -111,7 +110,7 @@ def main():
|
||||
elif options.show_formats:
|
||||
dump_formats()
|
||||
sys.exit(0)
|
||||
try:
|
||||
|
||||
major_mode = options.output_format
|
||||
|
||||
if len(args) < 2:
|
||||
@@ -120,20 +119,7 @@ def main():
|
||||
)
|
||||
convert(major_mode=major_mode, warnings=options.warnings)
|
||||
else:
|
||||
convert(
|
||||
input_file=args[1], major_mode=major_mode, warnings=options.warnings
|
||||
)
|
||||
|
||||
except FileNotFoundError as e:
|
||||
print_fatal_error("Error trying to read input file")
|
||||
raise e
|
||||
|
||||
except Exception as e:
|
||||
import traceback
|
||||
|
||||
print_fatal_error("Error trying to convert file")
|
||||
print("\033[31m" + e.__repr__() + "\033[0m", file=sys.stderr)
|
||||
print(traceback.format_exc())
|
||||
convert(input_file=args[1], major_mode=major_mode, warnings=options.warnings)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -2,21 +2,24 @@
|
||||
Useful functions for parsing and working with timecode.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import re
|
||||
from collections import namedtuple
|
||||
from fractions import Fraction
|
||||
from typing import Optional, SupportsFloat
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class TimecodeFormat(
|
||||
namedtuple("_TimecodeFormat", "frame_duration logical_fps drop_frame")
|
||||
):
|
||||
class TimecodeFormat(NamedTuple):
|
||||
"""
|
||||
A struct reperesenting a timecode datum.
|
||||
"""
|
||||
|
||||
def smpte_to_seconds(self, smpte: str) -> Optional[Fraction]:
|
||||
frame_duration: Fraction
|
||||
logical_fps: int
|
||||
drop_frame: bool
|
||||
|
||||
def smpte_to_seconds(self, smpte: str) -> Fraction | None:
|
||||
frame_count = smpte_to_frame_count(
|
||||
smpte, self.logical_fps, drop_frame_hint=self.drop_frame
|
||||
)
|
||||
@@ -25,14 +28,14 @@ class TimecodeFormat(
|
||||
else:
|
||||
return frame_count * self.frame_duration
|
||||
|
||||
def seconds_to_smpte(self, seconds: SupportsFloat) -> str:
|
||||
def seconds_to_smpte(self, seconds: Fraction) -> str:
|
||||
frame_count = int(seconds / self.frame_duration)
|
||||
return frame_count_to_smpte(frame_count, self.logical_fps, self.drop_frame)
|
||||
|
||||
|
||||
def smpte_to_frame_count(
|
||||
smpte_rep_string: str, frames_per_logical_second: int, drop_frame_hint=False
|
||||
) -> Optional[int]:
|
||||
) -> int | None:
|
||||
"""
|
||||
Convert a string with a SMPTE timecode representation into a frame count.
|
||||
|
||||
@@ -83,7 +86,7 @@ def frame_count_to_smpte(
|
||||
frame_count: int,
|
||||
frames_per_logical_second: int,
|
||||
drop_frame: bool = False,
|
||||
fractional_frame: Optional[float] = None,
|
||||
fractional_frame: float | None = None,
|
||||
) -> str:
|
||||
assert frames_per_logical_second in [24, 25, 30, 48, 50, 60]
|
||||
assert fractional_frame is None or fractional_frame < 1.0
|
||||
@@ -104,20 +107,21 @@ def frame_count_to_smpte(
|
||||
ss, ff = divmod(rem, frames_per_logical_second)
|
||||
|
||||
hh = hh % 24
|
||||
|
||||
hh = int(hh)
|
||||
mm = int(mm)
|
||||
ss = int(ss)
|
||||
ff = int(ff)
|
||||
|
||||
if fractional_frame is not None and fractional_frame > 0:
|
||||
return "%02i:%02i:%02i%s%02i%s" % (
|
||||
hh,
|
||||
mm,
|
||||
ss,
|
||||
separator,
|
||||
ff,
|
||||
("%.3f" % fractional_frame)[1:],
|
||||
)
|
||||
fpart = "{fractional_frame:.3f}"[1:]
|
||||
return f"{hh:02}:{mm:02}:{ss:02}{separator}{ff:02}{fpart}"
|
||||
else:
|
||||
return "%02i:%02i:%02i%s%02i" % (hh, mm, ss, separator, ff)
|
||||
return f"{hh:02}:{mm:02}:{ss:02}{separator}{ff:02}"
|
||||
# "%02i:%02i:%02i%s%02i" % (hh, mm, ss, separator, ff)
|
||||
|
||||
|
||||
def footage_to_frame_count(footage_string) -> Optional[int]:
|
||||
def footage_to_frame_count(footage_string) -> int | None:
|
||||
m = re.search(r"(\d+)\+(\d+)(\.\d+)?", footage_string)
|
||||
if m is None:
|
||||
return None
|
||||
@@ -131,4 +135,4 @@ def footage_to_frame_count(footage_string) -> Optional[int]:
|
||||
|
||||
def frame_count_to_footage(frame_count):
|
||||
feet, frm = divmod(frame_count, 16)
|
||||
return "%i+%02i" % (feet, frm)
|
||||
return f"{feet}+{frm:02}"
|
||||
|
||||
+18
-21
@@ -3,6 +3,8 @@ This module provides the main input document parsing and transform
|
||||
implementation.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import datetime
|
||||
import os
|
||||
@@ -11,7 +13,6 @@ from collections.abc import Iterator
|
||||
from fractions import Fraction
|
||||
from itertools import chain
|
||||
from json import JSONEncoder
|
||||
from typing import List, Optional
|
||||
|
||||
import ptsl
|
||||
|
||||
@@ -39,26 +40,25 @@ class FractionEncoder(JSONEncoder):
|
||||
as a dict.
|
||||
"""
|
||||
|
||||
force_denominator: Optional[int]
|
||||
force_denominator: int | None
|
||||
|
||||
def default(self, o):
|
||||
""" """
|
||||
if isinstance(o, Fraction):
|
||||
return dict(numerator=o.numerator, denominator=o.denominator)
|
||||
return {"numerator": o.numerator, "denominator": o.denominator}
|
||||
else:
|
||||
return o.__dict__
|
||||
|
||||
|
||||
def output_adr_csv(lines: List[ADRLine], time_format: TimecodeFormat):
|
||||
def output_adr_csv(lines: list[ADRLine], time_format: TimecodeFormat):
|
||||
"""
|
||||
Writes ADR lines as CSV to the current working directory. Creates
|
||||
directories for each character number and name pair, and within that
|
||||
directory, creates a CSV file for each reel.
|
||||
"""
|
||||
|
||||
reels: set[str | None] = set([ln.reel for ln in lines])
|
||||
reels: set[str | None] = {ln.reel for ln in lines}
|
||||
for n, name in [(n.character_id, n.character_name) for n in lines]:
|
||||
dir_name = "%s_%s" % (n, name)
|
||||
dir_name = f"{n}_{name}"
|
||||
os.makedirs(dir_name, exist_ok=True)
|
||||
os.chdir(dir_name)
|
||||
for reel in reels:
|
||||
@@ -69,11 +69,8 @@ def output_adr_csv(lines: List[ADRLine], time_format: TimecodeFormat):
|
||||
if len(these_lines) == 0:
|
||||
continue
|
||||
|
||||
outfile_name = "%s_%s_%s_%s.csv" % (
|
||||
these_lines[0].title,
|
||||
n,
|
||||
these_lines[0].character_name,
|
||||
reel,
|
||||
outfile_name = (
|
||||
f"{these_lines[0].title}_{n}_{these_lines[0].character_name}_{reel}.csv"
|
||||
)
|
||||
|
||||
with open(outfile_name, mode="w", newline="") as outfile:
|
||||
@@ -119,13 +116,13 @@ def output_adr_csv(lines: List[ADRLine], time_format: TimecodeFormat):
|
||||
os.chdir("..")
|
||||
|
||||
|
||||
def generate_documents(session_tc_format, scenes, adr_lines: List[ADRLine], title):
|
||||
def generate_documents(session_tc_format, scenes, adr_lines: list[ADRLine], title):
|
||||
"""
|
||||
Create PDF output.
|
||||
"""
|
||||
print_section_header_style("Creating PDF Reports")
|
||||
report_date = datetime.datetime.now()
|
||||
reports_dir = "%s_%s" % (title, report_date.strftime("%Y-%m-%d_%H%M%S"))
|
||||
reports_dir = f"{title}_{report_date.strftime('%Y-%m-%d_%H%M%S')}"
|
||||
os.makedirs(reports_dir, exist_ok=False)
|
||||
os.chdir(reports_dir)
|
||||
|
||||
@@ -155,7 +152,7 @@ def generate_documents(session_tc_format, scenes, adr_lines: List[ADRLine], titl
|
||||
|
||||
|
||||
def create_adr_reports(
|
||||
lines: List[ADRLine], tc_display_format: TimecodeFormat, reel_list: List[str]
|
||||
lines: list[ADRLine], tc_display_format: TimecodeFormat, reel_list: list[str]
|
||||
):
|
||||
"""
|
||||
Creates a directory heirarchy and a respective set of ADR reports,
|
||||
@@ -240,18 +237,18 @@ def convert(major_mode, input_file=None, output=sys.stdout, warnings=True):
|
||||
)
|
||||
|
||||
# TODO: Breakdown by titles
|
||||
titles = set([x.title for x in (generic_events + adr_lines)])
|
||||
titles = {x.title for x in (generic_events + adr_lines)}
|
||||
if len(titles) != 1:
|
||||
print_warning(
|
||||
"Multiple titles per export is not supported, "
|
||||
"found multiple titles: %s Exiting." % titles
|
||||
f"found multiple titles: {titles} Exiting."
|
||||
)
|
||||
exit(-1)
|
||||
sys.exit(-1)
|
||||
|
||||
title = list(titles)[0]
|
||||
title = next(iter(titles))
|
||||
|
||||
print_status_style("%i generic events found." % len(generic_events))
|
||||
print_status_style("%i ADR events found." % len(adr_lines))
|
||||
print_status_style(f"{len(generic_events)} generic events found.")
|
||||
print_status_style(f"{len(adr_lines)} ADR events found.")
|
||||
|
||||
if warnings:
|
||||
perform_adr_validations(iter(adr_lines))
|
||||
|
||||
@@ -2,7 +2,4 @@
|
||||
Docparser module
|
||||
"""
|
||||
|
||||
|
||||
from .pt_doc_parser import parse_document
|
||||
|
||||
__all__ = [parse_document]
|
||||
from .pt_doc_parser import parse_document # pyright: ignore[reportUnusedImport, reportUnusedImport] # noqa: F401, I001
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import namedtuple
|
||||
from collections.abc import Generator, Iterator
|
||||
from dataclasses import dataclass
|
||||
from fractions import Fraction
|
||||
from typing import Callable
|
||||
from typing import Callable, NamedTuple
|
||||
|
||||
from ptulsconv.docparser import doc_entity
|
||||
|
||||
@@ -27,12 +26,15 @@ class TagCompiler:
|
||||
items.
|
||||
"""
|
||||
|
||||
Intermediate = namedtuple(
|
||||
"Intermediate",
|
||||
"track_content track_tags track_comment_tags "
|
||||
"clip_content clip_tags clip_tag_mode start "
|
||||
"finish",
|
||||
)
|
||||
class Intermediate(NamedTuple):
|
||||
track_content: str
|
||||
clip_content: str
|
||||
track_tags: dict[str, str]
|
||||
track_comment_tags: dict[str, str]
|
||||
clip_tags: dict[str, str]
|
||||
clip_tag_mode: TagPreModes
|
||||
start: Fraction
|
||||
finish: Fraction
|
||||
|
||||
session: doc_entity.SessionDescriptor
|
||||
|
||||
@@ -131,7 +133,6 @@ class TagCompiler:
|
||||
return effective_tags
|
||||
|
||||
def parse_data(self) -> Iterator[Intermediate]:
|
||||
|
||||
for track, clip, start, finish, _ in self.session.track_clips_timed():
|
||||
if clip.state == "Muted":
|
||||
continue
|
||||
@@ -153,7 +154,6 @@ class TagCompiler:
|
||||
|
||||
@staticmethod
|
||||
def apply_appends(parsed: Iterator[Intermediate]) -> Iterator[Intermediate]:
|
||||
|
||||
def should_append(a, b):
|
||||
return b.clip_tag_mode == TagPreModes.APPEND and b.start >= a.finish
|
||||
|
||||
@@ -177,14 +177,13 @@ class TagCompiler:
|
||||
def collect_time_spans(
|
||||
parsed: Iterator[Intermediate],
|
||||
) -> Iterator[tuple[Intermediate, tuple[dict, Fraction, Fraction]]]:
|
||||
|
||||
time_spans = []
|
||||
|
||||
for item in parsed:
|
||||
if item.clip_tag_mode == TagPreModes.TIMESPAN:
|
||||
time_spans.append((item.clip_tags, item.start, item.finish))
|
||||
else:
|
||||
yield item, list(time_spans)
|
||||
yield item, tuple(time_spans)
|
||||
|
||||
@staticmethod
|
||||
def _time_span_tags(at_time: Fraction, applicable_spans) -> dict:
|
||||
@@ -199,7 +198,6 @@ class TagCompiler:
|
||||
def apply_tags(
|
||||
self, parsed_with_time_spans
|
||||
) -> Iterator[tuple[str, str, str, dict, Fraction, Fraction]]:
|
||||
|
||||
session_parsed = parse_tags(self.session.header.session_name)
|
||||
|
||||
for event, time_spans in parsed_with_time_spans:
|
||||
@@ -226,7 +224,7 @@ class TagCompiler:
|
||||
|
||||
|
||||
def apply_appends(
|
||||
source: Iterator, should_append: Callable, do_append: Callable
|
||||
source: Iterator, should_append: Callable, preform_append: Callable
|
||||
) -> Generator:
|
||||
"""
|
||||
:param source:
|
||||
@@ -240,7 +238,7 @@ def apply_appends(
|
||||
this_element = next(source)
|
||||
for element in source:
|
||||
if should_append(this_element, element):
|
||||
this_element = do_append(this_element, element)
|
||||
this_element = preform_append(this_element, element)
|
||||
else:
|
||||
yield this_element
|
||||
this_element = element
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from enum import Enum
|
||||
from typing import Dict
|
||||
|
||||
from parsimonious import Grammar, NodeVisitor
|
||||
|
||||
@@ -37,7 +36,7 @@ def parse_tags(prompt: str) -> "TaggedStringResult":
|
||||
|
||||
class TaggedStringResult:
|
||||
content: str
|
||||
tag_dict: Dict[str, str]
|
||||
tag_dict: dict[str, str]
|
||||
mode: TagPreModes
|
||||
|
||||
def __init__(self, content, tag_dict, mode):
|
||||
@@ -53,7 +52,7 @@ class TagListVisitor(NodeVisitor):
|
||||
|
||||
return TaggedStringResult(
|
||||
content=next(iter(line_opt), None),
|
||||
tag_dict=next(iter(tag_list_opt), dict()),
|
||||
tag_dict=next(iter(tag_list_opt), {}),
|
||||
mode=TagPreModes(next(iter(modifier_opt), "Normal")),
|
||||
)
|
||||
|
||||
@@ -74,7 +73,7 @@ class TagListVisitor(NodeVisitor):
|
||||
|
||||
@staticmethod
|
||||
def visit_tag_list(_, visited_children):
|
||||
retdict = dict()
|
||||
retdict = {}
|
||||
for child in visited_children:
|
||||
if child[0] is not None:
|
||||
k, v = child[0]
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
Methods for converting string reprentations of film footage.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from fractions import Fraction
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def footage_to_seconds(footage: str) -> Optional[Fraction]:
|
||||
def footage_to_seconds(footage: str) -> Fraction | None:
|
||||
"""
|
||||
Converts a string representation of a footage (35mm, 24fps)
|
||||
into a :class:`Fraction`, this fraction being a some number of
|
||||
|
||||
@@ -26,7 +26,7 @@ class ReportCanvas(canvas.Canvas):
|
||||
|
||||
def showPage(self):
|
||||
self._saved_page_states.append(dict(self.__dict__))
|
||||
self._startPage()
|
||||
self._startPage() # pyright: ignore[reportAttributeAccessIssue]
|
||||
|
||||
def save(self):
|
||||
"""add page info to each page (page x of y)"""
|
||||
@@ -41,9 +41,11 @@ class ReportCanvas(canvas.Canvas):
|
||||
self.saveState()
|
||||
self.setFont("Helvetica", 10) # FIXME make this customizable
|
||||
self.drawString(
|
||||
0.5 * inch, 0.5 * inch, "Page %d of %d" % (self._pageNumber, page_count)
|
||||
0.5 * inch,
|
||||
0.5 * inch,
|
||||
f"Page {self._pageNumber} of {page_count}", # pyright: ignore[reportAttributeAccessIssue]
|
||||
)
|
||||
right_edge = self._pagesize[0] - 0.5 * inch
|
||||
right_edge = self._pagesize[0] - 0.5 * inch # pyright: ignore[reportAttributeAccessIssue]
|
||||
self.drawRightString(
|
||||
right_edge, 0.5 * inch, self._report_date.strftime("%m/%d/%Y %H:%M")
|
||||
)
|
||||
@@ -62,7 +64,7 @@ class ADRDocTemplate(BaseDocTemplate):
|
||||
|
||||
|
||||
def make_doc_template(
|
||||
page_size,
|
||||
page_size: tuple[float, float],
|
||||
filename,
|
||||
document_title,
|
||||
title: str,
|
||||
@@ -71,18 +73,22 @@ def make_doc_template(
|
||||
client: str,
|
||||
document_subheader: str,
|
||||
left_margin=0.5 * inch,
|
||||
fonts: list[TTFont] = [],
|
||||
fonts: list[TTFont] | None = None,
|
||||
) -> ADRDocTemplate:
|
||||
|
||||
if fonts is None:
|
||||
fonts = []
|
||||
|
||||
right_margin = top_margin = bottom_margin = 0.5 * inch
|
||||
page_box: GRect = GRect(0.0, 0.0, page_size[0], page_size[1])
|
||||
_, page_box = page_box.split_x(left_margin, direction="l")
|
||||
_, page_box = page_box.split_x(right_margin, direction="r")
|
||||
_, page_box = page_box.split_y(bottom_margin, direction="u")
|
||||
_, page_box = page_box.split_y(top_margin, direction="d")
|
||||
_, page_box = page_box.split_x(left_margin, direction="l") # pyright: ignore[reportAssignmentType]
|
||||
_, page_box = page_box.split_x(right_margin, direction="r") # pyright: ignore[reportAssignmentType]
|
||||
_, page_box = page_box.split_y(bottom_margin, direction="u") # pyright: ignore[reportAssignmentType]
|
||||
_, page_box = page_box.split_y(top_margin, direction="d") # pyright: ignore[reportAssignmentType]
|
||||
|
||||
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")
|
||||
footer_box, page_box = page_box.split_y(0.25 * inch, direction="u") # pyright: ignore[reportAssignmentType]
|
||||
header_box, page_box = page_box.split_y(0.75 * inch, direction="d") # pyright: ignore[reportAssignmentType]
|
||||
title_box, report_box = header_box.split_x(3.5 * inch, direction="r") # pyright: ignore[reportOptionalMemberAccess]
|
||||
|
||||
def on_page_lambda(c, _):
|
||||
draw_header_footer(
|
||||
@@ -97,7 +103,6 @@ def make_doc_template(
|
||||
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=frames, onPage=on_page_lambda)
|
||||
@@ -292,7 +297,7 @@ class GRect:
|
||||
|
||||
rem = self
|
||||
for item in x_list:
|
||||
s, rem = rem.split_x(item, direction)
|
||||
s, rem = rem.split_x(item, direction) # pyright: ignore[reportOptionalMemberAccess]
|
||||
ret_list.append(s)
|
||||
|
||||
return ret_list, rem
|
||||
@@ -302,7 +307,7 @@ class GRect:
|
||||
|
||||
rem = self
|
||||
for item in y_list:
|
||||
s, rem = rem.split_y(item, direction)
|
||||
s, rem = rem.split_y(item, direction) # pyright: ignore[reportOptionalMemberAccess]
|
||||
ret_list.append(s)
|
||||
|
||||
return ret_list, rem
|
||||
@@ -399,7 +404,7 @@ class GRect:
|
||||
cp.rect(self.min_x, self.min_y, self.width, self.height)
|
||||
a_canvas.clipPath(cp, stroke=0, fill=0)
|
||||
|
||||
w, h = flowable.wrap(inset_rect.width, inset_rect.height)
|
||||
_w, h = flowable.wrap(inset_rect.width, inset_rect.height)
|
||||
|
||||
flowable.drawOn(a_canvas, inset_rect.x, inset_rect.max_y - h)
|
||||
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
from typing import List, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import letter, portrait
|
||||
from reportlab.lib.styles import getSampleStyleSheet
|
||||
|
||||
# from reportlab.pdfbase import pdfmetrics
|
||||
# from reportlab.pdfbase.ttfonts import TTFont
|
||||
from reportlab.lib.units import inch
|
||||
from reportlab.platypus import Paragraph, Spacer, Table
|
||||
|
||||
@@ -14,15 +11,13 @@ from .__init__ import make_doc_template, time_format
|
||||
|
||||
|
||||
def build_columns(
|
||||
lines: List[ADRLine],
|
||||
reel_list: Optional[List[str]],
|
||||
lines: list[ADRLine],
|
||||
reel_list: list[str] | None,
|
||||
show_priorities=False,
|
||||
include_omitted=False,
|
||||
):
|
||||
columns = list()
|
||||
reel_numbers = reel_list or sorted(
|
||||
set([x.reel for x in lines if x.reel is not None])
|
||||
)
|
||||
columns = []
|
||||
reel_numbers = reel_list or sorted({x.reel for x in lines if x.reel is not None})
|
||||
|
||||
num_column_width = 15.0 / 32.0 * inch
|
||||
|
||||
@@ -160,17 +155,17 @@ def build_columns(
|
||||
):
|
||||
columns.append(
|
||||
{
|
||||
"heading": "P%i" % n,
|
||||
"value_getter": lambda recs: len(
|
||||
[r for r in recs if r.priority == n]
|
||||
"heading": f"P{n}",
|
||||
"value_getter": lambda recs, N=n: len(
|
||||
[r for r in recs if r.priority == N]
|
||||
),
|
||||
"value_getter2": (
|
||||
lambda recs: time_format(
|
||||
lambda recs, N=n: time_format(
|
||||
sum(
|
||||
[
|
||||
r.time_budget_mins or 0.0
|
||||
for r in recs
|
||||
if r.priority == n
|
||||
if r.priority == N
|
||||
]
|
||||
)
|
||||
)
|
||||
@@ -243,22 +238,20 @@ def build_columns(
|
||||
return columns
|
||||
|
||||
|
||||
def populate_columns(lines: List[ADRLine], columns, include_omitted, _page_size):
|
||||
data = list()
|
||||
styles = list()
|
||||
columns_widths = list()
|
||||
def populate_columns(lines: list[ADRLine], columns, include_omitted, _page_size):
|
||||
data = []
|
||||
styles = []
|
||||
columns_widths = []
|
||||
|
||||
sorted_character_numbers: List[str] = sorted(
|
||||
set([x.character_id for x in lines]), key=lambda x: str(x)
|
||||
sorted_character_numbers: list[str | None] = sorted(
|
||||
{x.character_id for x in lines}, key=lambda x: str(x)
|
||||
)
|
||||
|
||||
# construct column styles
|
||||
|
||||
for i, c in enumerate(columns):
|
||||
styles.extend(c["style_getter"](i))
|
||||
columns_widths.append(c["width"])
|
||||
|
||||
data.append(list(map(lambda x: x["heading"], columns)))
|
||||
data.append([x["heading"] for x in columns])
|
||||
|
||||
if not include_omitted:
|
||||
lines = [x for x in lines if not x.omitted]
|
||||
@@ -266,8 +259,8 @@ def populate_columns(lines: List[ADRLine], columns, include_omitted, _page_size)
|
||||
for n in sorted_character_numbers:
|
||||
char_records = [x for x in lines if x.character_id == n]
|
||||
if len(char_records) > 0:
|
||||
row_data = list()
|
||||
row_data2 = list()
|
||||
row_data = []
|
||||
row_data2 = []
|
||||
|
||||
for col in columns:
|
||||
row1_index = len(data)
|
||||
@@ -291,8 +284,8 @@ def populate_columns(lines: List[ADRLine], columns, include_omitted, _page_size)
|
||||
data.append(row_data)
|
||||
data.append(row_data2)
|
||||
|
||||
summary_row1 = list()
|
||||
summary_row2 = list()
|
||||
summary_row1 = []
|
||||
summary_row2 = []
|
||||
row1_index = len(data)
|
||||
|
||||
for col in columns:
|
||||
@@ -316,12 +309,15 @@ def populate_columns(lines: List[ADRLine], columns, include_omitted, _page_size)
|
||||
|
||||
|
||||
def output_report(
|
||||
lines: List[ADRLine],
|
||||
reel_list: List[str],
|
||||
lines: list[ADRLine],
|
||||
reel_list: list[str],
|
||||
page_size: tuple[float, float] | None = None,
|
||||
include_omitted=False,
|
||||
page_size=portrait(letter),
|
||||
font_name="Helvetica",
|
||||
):
|
||||
if page_size is None:
|
||||
page_size = portrait(letter)
|
||||
|
||||
columns = build_columns(lines, include_omitted=include_omitted, reel_list=reel_list)
|
||||
data, style, columns_widths = populate_columns(
|
||||
lines, columns, include_omitted, page_size
|
||||
@@ -334,16 +330,17 @@ def output_report(
|
||||
|
||||
# pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc'))
|
||||
|
||||
title = "%s Line Count" % lines[0].title
|
||||
title = f"{lines[0].title} Line Count"
|
||||
filename = title + ".pdf"
|
||||
|
||||
doc = make_doc_template(
|
||||
page_size=page_size,
|
||||
filename=filename,
|
||||
document_title=title,
|
||||
title=lines[0].title,
|
||||
document_subheader=lines[0].spot,
|
||||
client=lines[0].client,
|
||||
supervisor=lines[0].supervisor,
|
||||
document_subheader=lines[0].spot or "",
|
||||
client=lines[0].client or "",
|
||||
supervisor=lines[0].supervisor or "",
|
||||
document_header="Line Count",
|
||||
)
|
||||
|
||||
@@ -364,8 +361,6 @@ def output_report(
|
||||
omitted_count = len([x for x in lines if x.omitted])
|
||||
|
||||
if not include_omitted and omitted_count > 0:
|
||||
story.append(
|
||||
Paragraph("* %i Omitted lines are excluded." % omitted_count, style)
|
||||
)
|
||||
story.append(Paragraph(f"* {omitted_count} Omitted lines are excluded.", style))
|
||||
|
||||
doc.build(story)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
from typing import List
|
||||
from __future__ import annotations
|
||||
|
||||
from reportlab.lib.pagesizes import letter, portrait
|
||||
from reportlab.lib.styles import getSampleStyleSheet
|
||||
@@ -13,7 +12,7 @@ from .__init__ import make_doc_template, time_format
|
||||
|
||||
|
||||
def build_aux_data_field(line: ADRLine):
|
||||
entries = list()
|
||||
entries = []
|
||||
if line.reason is not None:
|
||||
entries.append("Reason: " + line.reason)
|
||||
if line.note is not None:
|
||||
@@ -27,40 +26,30 @@ def build_aux_data_field(line: ADRLine):
|
||||
tag_field = ""
|
||||
if line.effort:
|
||||
bg_color = "red"
|
||||
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font> " % (
|
||||
bg_color,
|
||||
fg_color,
|
||||
"EFF",
|
||||
tag_field += (
|
||||
f"<font backColor={bg_color} textColor={fg_color} fontSize=11>EFF</font> "
|
||||
)
|
||||
elif line.tv:
|
||||
bg_color = "blue"
|
||||
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font> " % (
|
||||
bg_color,
|
||||
fg_color,
|
||||
"TV",
|
||||
tag_field += (
|
||||
f"<font backColor={bg_color} textColor={fg_color} fontSize=11>TV</font> "
|
||||
)
|
||||
elif line.adlib:
|
||||
bg_color = "purple"
|
||||
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font> " % (
|
||||
bg_color,
|
||||
fg_color,
|
||||
"ADLIB",
|
||||
tag_field += (
|
||||
f"<font backColor={bg_color} textColor={fg_color} fontSize=11>ADLIB</font> "
|
||||
)
|
||||
elif line.optional:
|
||||
bg_color = "green"
|
||||
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font>" % (
|
||||
bg_color,
|
||||
fg_color,
|
||||
"OPTIONAL",
|
||||
)
|
||||
tag_field += f"font backColor={bg_color} textColor={fg_color} fontSize=11>OPTIONAL</font>"
|
||||
|
||||
entries.append(tag_field)
|
||||
|
||||
return "<br />".join(entries)
|
||||
|
||||
|
||||
def build_story(lines: List[ADRLine], tc_rate: TimecodeFormat, font_name="Helvetica"):
|
||||
story = list()
|
||||
def build_story(lines: list[ADRLine], tc_rate: TimecodeFormat, font_name="Helvetica"):
|
||||
story = []
|
||||
|
||||
this_scene = None
|
||||
scene_style = getSampleStyleSheet()["Normal"]
|
||||
@@ -78,15 +67,14 @@ def build_story(lines: List[ADRLine], tc_rate: TimecodeFormat, font_name="Helvet
|
||||
("BOTTOMPADDING", (0, 0), (-1, -1), 24.0),
|
||||
]
|
||||
|
||||
cue_number_field = "%s<br /><font fontSize=7>%s</font>" % (
|
||||
line.cue_number,
|
||||
line.character_name,
|
||||
cue_number_field = (
|
||||
f"{line.cue_number}<br /><font fontSize=7>{line.character_name}</font>"
|
||||
)
|
||||
|
||||
time_data = time_format(line.time_budget_mins)
|
||||
|
||||
if line.priority is not None:
|
||||
time_data = time_data + "<br />" + "P: " + line.priority
|
||||
time_data = time_data + "<br />" + "P: " + str(line.priority)
|
||||
|
||||
aux_data_field = build_aux_data_field(line)
|
||||
|
||||
@@ -96,7 +84,7 @@ def build_story(lines: List[ADRLine], tc_rate: TimecodeFormat, font_name="Helvet
|
||||
[
|
||||
Paragraph(cue_number_field, line_style),
|
||||
Paragraph(tc_data, line_style),
|
||||
Paragraph(line.prompt, line_style),
|
||||
Paragraph(line.prompt or "[No Prompt]", line_style),
|
||||
Paragraph(time_data, line_style),
|
||||
Paragraph(aux_data_field, line_style),
|
||||
]
|
||||
@@ -135,11 +123,11 @@ def build_tc_data(line: ADRLine, tc_format: TimecodeFormat):
|
||||
third_line = []
|
||||
if line.reel is not None:
|
||||
if line.reel[0:1] == "R":
|
||||
third_line.append("%s" % line.reel)
|
||||
third_line.append(f"{line.reel}")
|
||||
else:
|
||||
third_line.append("Reel %s" % line.reel)
|
||||
third_line.append(f"Reel {line.reel}")
|
||||
if line.version is not None:
|
||||
third_line.append("(%s)" % line.version)
|
||||
third_line.append(f"({line.version})")
|
||||
if len(third_line) > 0:
|
||||
tc_data = tc_data + "<br/>" + " ".join(third_line)
|
||||
return tc_data
|
||||
@@ -147,17 +135,17 @@ def build_tc_data(line: ADRLine, tc_format: TimecodeFormat):
|
||||
|
||||
def generate_report(
|
||||
page_size,
|
||||
lines: List[ADRLine],
|
||||
lines: list[ADRLine],
|
||||
tc_rate: TimecodeFormat,
|
||||
character_number=None,
|
||||
include_omitted=True,
|
||||
):
|
||||
if character_number is not None:
|
||||
lines = [r for r in lines if r.character_id == character_number]
|
||||
title = "%s ADR Report (%s)" % (lines[0].title, lines[0].character_name)
|
||||
document_header = "%s ADR Report" % lines[0].character_name
|
||||
title = f"{lines[0].title} ADR Report ({lines[0].character_name})"
|
||||
document_header = f"{lines[0].character_name} ADR Report"
|
||||
else:
|
||||
title = "%s ADR Report" % lines[0].title
|
||||
title = f"{lines[0].title} ADR Report"
|
||||
document_header = "ADR Report"
|
||||
|
||||
if not include_omitted:
|
||||
@@ -172,9 +160,9 @@ def generate_report(
|
||||
document_title=title,
|
||||
document_header=document_header,
|
||||
title=lines[0].title,
|
||||
supervisor=lines[0].supervisor,
|
||||
client=lines[0].client,
|
||||
document_subheader=lines[0].spot,
|
||||
supervisor=lines[0].supervisor or "",
|
||||
client=lines[0].client or "",
|
||||
document_subheader=lines[0].spot or "",
|
||||
left_margin=0.75 * inch,
|
||||
)
|
||||
story = build_story(lines, tc_rate)
|
||||
@@ -182,13 +170,16 @@ def generate_report(
|
||||
|
||||
|
||||
def output_report(
|
||||
lines: List[ADRLine],
|
||||
lines: list[ADRLine],
|
||||
tc_display_format: TimecodeFormat,
|
||||
page_size=portrait(letter),
|
||||
by_character=False,
|
||||
page_size: tuple[float, float] | None = None,
|
||||
by_character: bool = False,
|
||||
):
|
||||
if page_size is None:
|
||||
page_size = portrait(letter)
|
||||
|
||||
if by_character:
|
||||
character_numbers = set(r.character_id for r in lines)
|
||||
character_numbers = {r.character_id for r in lines}
|
||||
for n in character_numbers:
|
||||
generate_report(page_size, lines, tc_display_format, n)
|
||||
else:
|
||||
|
||||
@@ -24,7 +24,7 @@ def draw_header_block(canvas, rect, record: ADRLine):
|
||||
def draw_character_row(canvas, rect, record: ADRLine):
|
||||
label_frame, value_frame = rect.split_x(1.25 * inch)
|
||||
label_frame.draw_text_cell(canvas, "CHARACTER", font_name, 10, force_baseline=9.0)
|
||||
line = "%s / %s" % (record.character_id, record.character_name)
|
||||
line = f"{record.character_id} / {record.character_name}"
|
||||
if record.actor_name is not None:
|
||||
line = line + " / " + record.actor_name
|
||||
value_frame.draw_text_cell(canvas, line, font_name, 12, force_baseline=9.0)
|
||||
@@ -61,9 +61,9 @@ def draw_cue_number_block(canvas, rect, record: ADRLine):
|
||||
"omitted": "OMIT",
|
||||
}
|
||||
tag_field = ""
|
||||
for key in tags:
|
||||
for key, value in tags.items():
|
||||
if getattr(record, key):
|
||||
tag_field = tag_field + tags[key] + " "
|
||||
tag_field = tag_field + value + " "
|
||||
|
||||
aux_frame.draw_text_cell(
|
||||
canvas, tag_field, font_name, 10, inset_x=10.0, inset_y=2.0, vertical_align="t"
|
||||
@@ -221,7 +221,7 @@ def draw_aux_block(canvas, rect, recording_time_sec_this_line, recording_time_se
|
||||
rect.draw_border(canvas, "min_x")
|
||||
|
||||
content_rect = rect.inset_xy(10.0, 10.0)
|
||||
lines, last_line = content_rect.divide_y(
|
||||
lines, _ = content_rect.divide_y(
|
||||
[12.0, 12.0, 24.0, 24.0, 24.0, 24.0], direction="d"
|
||||
)
|
||||
|
||||
@@ -255,7 +255,7 @@ def draw_footer(canvas, rect, record: ADRLine, report_date, line_no, total_lines
|
||||
rect.draw_border(canvas, "max_y")
|
||||
report_date_s = [report_date.strftime("%c")]
|
||||
spotting_name = [record.spot] if record.spot is not None else []
|
||||
pages_s = ["Line %i of %i" % (line_no, total_lines)]
|
||||
pages_s = [f"Line {line_no} of {total_lines}"]
|
||||
footer_s = " - ".join(report_date_s + spotting_name + pages_s)
|
||||
rect.draw_text_cell(
|
||||
canvas, footer_s, font_name=font_name, font_size=10.0, inset_y=2.0
|
||||
@@ -266,11 +266,8 @@ def create_report_for_character(
|
||||
records, report_date, tc_display_format: TimecodeFormat
|
||||
):
|
||||
|
||||
outfile = "%s_%s_%s_Log.pdf" % (
|
||||
records[0].title,
|
||||
records[0].character_id,
|
||||
records[0].character_name,
|
||||
)
|
||||
outfile = f"{records[0].title}_{records[0].character_id}_{records[0].character_name}_Log.pdf"
|
||||
|
||||
assert outfile is not None
|
||||
assert outfile[-4:] == ".pdf", "Output file must have 'pdf' extension!"
|
||||
|
||||
@@ -285,7 +282,7 @@ def create_report_for_character(
|
||||
)
|
||||
)
|
||||
|
||||
cue_header_block, title_header_block = header_row.split_x(4.0 * inch)
|
||||
cue_header_block, _title_header_block = header_row.split_x(4.0 * inch)
|
||||
(cue_number_block, timecode_block), reason_block = data_row.divide_x(
|
||||
[1.5 * inch, 1.5 * inch]
|
||||
)
|
||||
@@ -297,8 +294,7 @@ def create_report_for_character(
|
||||
)
|
||||
|
||||
c.setTitle(
|
||||
"%s %s (%s) Supervisor's Log"
|
||||
% (records[0].title, records[0].character_name, records[0].character_id)
|
||||
f"{records[0].title} {records[0].character_name} ({records[0].character_id}) Supervisor's Log"
|
||||
)
|
||||
c.setAuthor(records[0].supervisor)
|
||||
|
||||
@@ -339,7 +335,7 @@ def create_report_for_character(
|
||||
def output_report(lines, tc_display_format: TimecodeFormat):
|
||||
report_date = datetime.datetime.now()
|
||||
events = sorted(lines, key=lambda x: x.start)
|
||||
character_numbers = set([x.character_id for x in lines])
|
||||
character_numbers = {x.character_id for x in lines}
|
||||
|
||||
for n in character_numbers:
|
||||
create_report_for_character(
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from typing import List
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.pagesizes import letter
|
||||
from reportlab.lib.styles import getSampleStyleSheet
|
||||
@@ -14,9 +12,9 @@ from .__init__ import make_doc_template
|
||||
|
||||
|
||||
def output_report(
|
||||
lines: List[ADRLine], tc_display_format: TimecodeFormat, font_name="Helvetica"
|
||||
lines: list[ADRLine], tc_display_format: TimecodeFormat, font_name="Helvetica"
|
||||
):
|
||||
character_numbers = set([n.character_id for n in lines])
|
||||
character_numbers = {n.character_id for n in lines}
|
||||
# pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc'))
|
||||
|
||||
for n in character_numbers:
|
||||
@@ -27,8 +25,8 @@ def output_report(
|
||||
|
||||
char_lines = sorted(char_lines, key=lambda line: line.start)
|
||||
|
||||
title = "%s (%s) %s ADR Script" % (char_lines[0].title, character_name, n)
|
||||
filename = "%s_%s_%s_ADR Script.pdf" % (char_lines[0].title, n, character_name)
|
||||
title = f"{char_lines[0].title} ({character_name}) {n} ADR Script"
|
||||
filename = f"{char_lines[0].title}_{n}_{character_name}_ADR Script.pdf"
|
||||
|
||||
doc = make_doc_template(
|
||||
page_size=letter,
|
||||
@@ -64,7 +62,7 @@ def output_report(
|
||||
finish_tc = tc_display_format.seconds_to_smpte(line.finish)
|
||||
data_block = [
|
||||
[
|
||||
Paragraph(line.cue_number, number_style),
|
||||
Paragraph(line.cue_number or "[No QN]", number_style),
|
||||
Paragraph(start_tc + " - " + finish_tc, number_style),
|
||||
]
|
||||
]
|
||||
@@ -80,7 +78,7 @@ def output_report(
|
||||
colWidths=[1.5 * inch, 6.0 * inch],
|
||||
style=[("LEFTPADDING", (0, 0), (-1, -1), 0.0)],
|
||||
),
|
||||
Paragraph(line.prompt, prompt_style),
|
||||
Paragraph(line.prompt or "[No Prompt]", prompt_style),
|
||||
Spacer(1.0, inch * 1.5),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -43,9 +43,7 @@ def print_advisory_tagging_error(
|
||||
sys.stderr.write(" ! \033[33;1mTagging error: \033[0m")
|
||||
ok_string = failed_string[:position]
|
||||
not_ok_string = failed_string[position:]
|
||||
sys.stderr.write(
|
||||
f'\033[32m"{ok_string}\033[31;1m{not_ok_string}"\033[0m\n'
|
||||
)
|
||||
sys.stderr.write(f'\033[32m"{ok_string}\033[31;1m{not_ok_string}"\033[0m\n')
|
||||
|
||||
if parent_track_name is not None:
|
||||
sys.stderr.write(f' ! > On track "{parent_track_name}"\n')
|
||||
|
||||
+18
-10
@@ -1,11 +1,13 @@
|
||||
import datetime
|
||||
from __future__ import annotations
|
||||
|
||||
import glob
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import List
|
||||
from datetime import datetime
|
||||
from importlib.metadata import version as module_version
|
||||
from xml.etree.ElementTree import TreeBuilder, tostring
|
||||
|
||||
import ptulsconv
|
||||
@@ -15,13 +17,16 @@ from ptulsconv.docparser.adr_entity import ADRLine
|
||||
|
||||
|
||||
def avid_marker_list(
|
||||
lines: List[ADRLine],
|
||||
report_date=datetime.datetime.now(),
|
||||
lines: list[ADRLine],
|
||||
report_date: datetime | None = None,
|
||||
reel_start_frame=0,
|
||||
fps=24,
|
||||
):
|
||||
doc = TreeBuilder(element_factory=None)
|
||||
|
||||
if report_date is None:
|
||||
report_date = datetime.now()
|
||||
|
||||
doc.start("Avid:StreamItems", {"xmlns:Avid": "http://www.avid.com"})
|
||||
doc.start("Avid:XMLFileData", {})
|
||||
doc.start("AvProp", {"name": "DomainMagic", "type": "string"})
|
||||
@@ -57,6 +62,8 @@ def avid_marker_list(
|
||||
|
||||
doc.start("List", {"id": "OMFI:ATTR:AttrRefs"})
|
||||
|
||||
assert report_date
|
||||
|
||||
insert_elem(
|
||||
"1",
|
||||
"OMFI:ATTB:IntAttribute",
|
||||
@@ -75,7 +82,7 @@ def avid_marker_list(
|
||||
line.supervisor or "",
|
||||
)
|
||||
|
||||
marker_name = "%s: %s" % (line.cue_number, line.prompt)
|
||||
marker_name = f"{line.cue_number}: {line.prompt}"
|
||||
insert_elem(
|
||||
"2", "OMFI:ATTB:StringAttribute", "string", "_ATN_CRM_COM", marker_name
|
||||
)
|
||||
@@ -112,7 +119,8 @@ def dump_fmpxml(data, input_file_name, output, adr_field_map):
|
||||
doc.data("0")
|
||||
doc.end("ERRORCODE")
|
||||
|
||||
doc.start("PRODUCT", {"NAME": ptulsconv.__name__, "VERSION": ptulsconv.__version__})
|
||||
version = module_version("ptulsconv")
|
||||
doc.start("PRODUCT", {"NAME": ptulsconv.__name__, "VERSION": f"{version}"})
|
||||
doc.end("PRODUCT")
|
||||
|
||||
doc.start(
|
||||
@@ -147,7 +155,7 @@ def dump_fmpxml(data, input_file_name, output, adr_field_map):
|
||||
doc.start("COL", {})
|
||||
doc.start("DATA", {})
|
||||
for key_attempt in field[0]:
|
||||
if key_attempt in event.keys():
|
||||
if key_attempt in event:
|
||||
doc.data(str(event[key_attempt]))
|
||||
break
|
||||
doc.end("DATA")
|
||||
@@ -170,7 +178,7 @@ def xform_options():
|
||||
|
||||
def dump_xform_options(output=sys.stdout):
|
||||
print("# Available transforms:", file=output)
|
||||
print("# Transform dir: %s" % xslt_path, file=output)
|
||||
print(f"# Transform dir: {xslt_path}", file=output)
|
||||
for f in xform_options():
|
||||
base = os.path.basename(f)
|
||||
name, _ = os.path.splitext(base)
|
||||
@@ -188,14 +196,14 @@ def fmp_transformed_dump(data, input_file, xsl_name, output, adr_field_map):
|
||||
dump_fmpxml(data, input_file, pipe, adr_field_map)
|
||||
|
||||
str_data = pipe.getvalue()
|
||||
print_status_style("Base XML size %i" % (len(str_data)))
|
||||
print_status_style(f"Base XML size {len(str_data)}")
|
||||
|
||||
print_status_style("Running xsltproc")
|
||||
|
||||
xsl_path = os.path.join(
|
||||
pathlib.Path(__file__).parent.absolute(), "xslt", xsl_name + ".xsl"
|
||||
)
|
||||
print_status_style("Using xsl: %s" % xsl_path)
|
||||
print_status_style(f"Using xsl: {xsl_path}")
|
||||
subprocess.run(
|
||||
["xsltproc", xsl_path, "-"],
|
||||
input=str_data,
|
||||
|
||||
Reference in New Issue
Block a user