This commit is contained in:
2026-09-19 15:20:18 -07:00
parent 888d9aa5d9
commit 754e4d5251
5 changed files with 45 additions and 50 deletions
+5
View File
@@ -42,6 +42,11 @@ doc = [
"sphinx-rtd-theme>=3.0.2", "sphinx-rtd-theme>=3.0.2",
] ]
[tool.ruff.lint]
ignore = [
"DTZ005" # datetime created without a tz
]
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"reaper/Export Items as Text.py" = ["F821"] "reaper/Export Items as Text.py" = ["F821"]
+2 -2
View File
@@ -6,7 +6,7 @@
# #
import datetime import datetime
import json import json
import time from zoneinfo import ZoneInfo
item_records = [] item_records = []
@@ -66,7 +66,7 @@ output["project_frame_rate"], _, output["project_drop_frame"] = (
output_path, _ = RPR_GetProjectPath("", 1024) output_path, _ = RPR_GetProjectPath("", 1024)
now = datetime.datetime.now(tz=time.tzname) now = datetime.datetime.now()
output_title = output["project_title"] output_title = output["project_title"]
if output_title == "": if output_title == "":
+1 -12
View File
@@ -102,7 +102,7 @@ def main():
(options, args) = parser.parse_args(sys.argv) (options, args) = parser.parse_args(sys.argv)
print_section_header_style("Startup") 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: if options.show_tags:
dump_field_map() dump_field_map()
@@ -124,17 +124,6 @@ def main():
input_file=args[1], major_mode=major_mode, warnings=options.warnings 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())
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+18 -15
View File
@@ -2,11 +2,13 @@
Useful functions for parsing and working with timecode. Useful functions for parsing and working with timecode.
""" """
from __future__ import annotations
import math import math
import re import re
from collections import namedtuple from collections import namedtuple
from fractions import Fraction from fractions import Fraction
from typing import Optional, SupportsFloat from typing import SupportsFloat
class TimecodeFormat( class TimecodeFormat(
@@ -16,7 +18,7 @@ class TimecodeFormat(
A struct reperesenting a timecode datum. A struct reperesenting a timecode datum.
""" """
def smpte_to_seconds(self, smpte: str) -> Optional[Fraction]: def smpte_to_seconds(self, smpte: str) -> Fraction | None:
frame_count = smpte_to_frame_count( frame_count = smpte_to_frame_count(
smpte, self.logical_fps, drop_frame_hint=self.drop_frame smpte, self.logical_fps, drop_frame_hint=self.drop_frame
) )
@@ -32,7 +34,7 @@ class TimecodeFormat(
def smpte_to_frame_count( def smpte_to_frame_count(
smpte_rep_string: str, frames_per_logical_second: int, drop_frame_hint=False 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. Convert a string with a SMPTE timecode representation into a frame count.
@@ -83,7 +85,7 @@ def frame_count_to_smpte(
frame_count: int, frame_count: int,
frames_per_logical_second: int, frames_per_logical_second: int,
drop_frame: bool = False, drop_frame: bool = False,
fractional_frame: Optional[float] = None, fractional_frame: float | None = None,
) -> str: ) -> str:
assert frames_per_logical_second in [24, 25, 30, 48, 50, 60] assert frames_per_logical_second in [24, 25, 30, 48, 50, 60]
assert fractional_frame is None or fractional_frame < 1.0 assert fractional_frame is None or fractional_frame < 1.0
@@ -104,20 +106,21 @@ def frame_count_to_smpte(
ss, ff = divmod(rem, frames_per_logical_second) ss, ff = divmod(rem, frames_per_logical_second)
hh = hh % 24 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: if fractional_frame is not None and fractional_frame > 0:
return "%02i:%02i:%02i%s%02i%s" % ( fpart = "{fractional_frame:.3f}"[1:]
hh, return f"{hh:02}:{mm:02}:{ss:02}{separator}{ff:02}{fpart}"
mm,
ss,
separator,
ff,
("%.3f" % fractional_frame)[1:],
)
else: 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) m = re.search(r"(\d+)\+(\d+)(\.\d+)?", footage_string)
if m is None: if m is None:
return None return None
@@ -131,4 +134,4 @@ def footage_to_frame_count(footage_string) -> Optional[int]:
def frame_count_to_footage(frame_count): def frame_count_to_footage(frame_count):
feet, frm = divmod(frame_count, 16) feet, frm = divmod(frame_count, 16)
return "%i+%02i" % (feet, frm) return f"{feet}+{frm:02}"
+19 -21
View File
@@ -3,6 +3,8 @@ This module provides the main input document parsing and transform
implementation. implementation.
""" """
from __future__ import annotations
import csv import csv
import datetime import datetime
import os import os
@@ -11,7 +13,7 @@ from collections.abc import Iterator
from fractions import Fraction from fractions import Fraction
from itertools import chain from itertools import chain
from json import JSONEncoder from json import JSONEncoder
from typing import List, Optional from zoneinfo import ZoneInfo
import ptsl import ptsl
@@ -39,26 +41,25 @@ class FractionEncoder(JSONEncoder):
as a dict. as a dict.
""" """
force_denominator: Optional[int] force_denominator: int | None
def default(self, o): def default(self, o):
""" """
if isinstance(o, Fraction): if isinstance(o, Fraction):
return dict(numerator=o.numerator, denominator=o.denominator) return {"numerator": o.numerator, "denominator": o.denominator}
else: else:
return o.__dict__ 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 Writes ADR lines as CSV to the current working directory. Creates
directories for each character number and name pair, and within that directories for each character number and name pair, and within that
directory, creates a CSV file for each reel. 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]: 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.makedirs(dir_name, exist_ok=True)
os.chdir(dir_name) os.chdir(dir_name)
for reel in reels: for reel in reels:
@@ -69,11 +70,8 @@ def output_adr_csv(lines: List[ADRLine], time_format: TimecodeFormat):
if len(these_lines) == 0: if len(these_lines) == 0:
continue continue
outfile_name = "%s_%s_%s_%s.csv" % ( outfile_name = (
these_lines[0].title, f"{these_lines[0].title}_{n}_{these_lines[0].character_name}_{reel}.csv"
n,
these_lines[0].character_name,
reel,
) )
with open(outfile_name, mode="w", newline="") as outfile: with open(outfile_name, mode="w", newline="") as outfile:
@@ -119,13 +117,13 @@ def output_adr_csv(lines: List[ADRLine], time_format: TimecodeFormat):
os.chdir("..") 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. Create PDF output.
""" """
print_section_header_style("Creating PDF Reports") print_section_header_style("Creating PDF Reports")
report_date = datetime.datetime.now() 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.makedirs(reports_dir, exist_ok=False)
os.chdir(reports_dir) os.chdir(reports_dir)
@@ -155,7 +153,7 @@ def generate_documents(session_tc_format, scenes, adr_lines: List[ADRLine], titl
def create_adr_reports( 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, Creates a directory heirarchy and a respective set of ADR reports,
@@ -240,18 +238,18 @@ def convert(major_mode, input_file=None, output=sys.stdout, warnings=True):
) )
# TODO: Breakdown by titles # 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: if len(titles) != 1:
print_warning( print_warning(
"Multiple titles per export is not supported, " "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(f"{len(generic_events)} generic events found.")
print_status_style("%i ADR events found." % len(adr_lines)) print_status_style(f"{len(adr_lines)} ADR events found.")
if warnings: if warnings:
perform_adr_validations(iter(adr_lines)) perform_adr_validations(iter(adr_lines))