mirror of
https://github.com/iluvcapra/ptulsconv.git
synced 2026-01-01 17:30:47 +00:00
Refactoring reports to use docparser
This commit is contained in:
1
.idea/dictionaries/jamie.xml
generated
1
.idea/dictionaries/jamie.xml
generated
@@ -6,6 +6,7 @@
|
|||||||
<w>futura</w>
|
<w>futura</w>
|
||||||
<w>ptulsconv</w>
|
<w>ptulsconv</w>
|
||||||
<w>retval</w>
|
<w>retval</w>
|
||||||
|
<w>smpte</w>
|
||||||
<w>timecode</w>
|
<w>timecode</w>
|
||||||
<w>timespan</w>
|
<w>timespan</w>
|
||||||
</words>
|
</words>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from ptulsconv.commands import convert, dump_field_map, raw_output
|
from ptulsconv.commands import convert, dump_field_map
|
||||||
from ptulsconv import __name__, __version__, __author__
|
from ptulsconv import __name__, __version__, __author__
|
||||||
from optparse import OptionParser, OptionGroup
|
from optparse import OptionParser, OptionGroup
|
||||||
from .xml.common import dump_xform_options
|
from .xml.common import dump_xform_options
|
||||||
@@ -60,17 +60,13 @@ def main():
|
|||||||
|
|
||||||
(options, args) = parser.parse_args(sys.argv)
|
(options, args) = parser.parse_args(sys.argv)
|
||||||
|
|
||||||
if options.output_format == 'raw':
|
|
||||||
raw_output(args[1])
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
print_banner_style("%s %s (c) 2020 %s. All rights reserved." % (__name__, __version__, __author__))
|
print_banner_style("%s %s (c) 2020 %s. All rights reserved." % (__name__, __version__, __author__))
|
||||||
|
|
||||||
print_section_header_style("Startup")
|
print_section_header_style("Startup")
|
||||||
print_status_style("This run started %s" % (datetime.datetime.now().isoformat()))
|
print_status_style("This run started %s" % (datetime.datetime.now().isoformat()))
|
||||||
|
|
||||||
if options.show_tags:
|
if options.show_tags:
|
||||||
dump_field_map('ADR')
|
dump_field_map()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
if options.show_transforms:
|
if options.show_transforms:
|
||||||
@@ -84,16 +80,6 @@ def main():
|
|||||||
|
|
||||||
print_status_style("Input file is %s" % (args[1]))
|
print_status_style("Input file is %s" % (args[1]))
|
||||||
|
|
||||||
# if options.in_time:
|
|
||||||
# print_status_style("Start at time %s" % (options.in_time))
|
|
||||||
# else:
|
|
||||||
# print_status_style("No start time given.")
|
|
||||||
#
|
|
||||||
# if options.out_time:
|
|
||||||
# print_status_style("End at time %s." % (options.out_time))
|
|
||||||
# else:
|
|
||||||
# print_status_style("No end time given.")
|
|
||||||
|
|
||||||
if options.include_muted:
|
if options.include_muted:
|
||||||
print_status_style("Muted regions are included.")
|
print_status_style("Muted regions are included.")
|
||||||
else:
|
else:
|
||||||
@@ -105,11 +91,8 @@ def main():
|
|||||||
output_format = 'fmpxml'
|
output_format = 'fmpxml'
|
||||||
|
|
||||||
convert(input_file=args[1], output_format=output_format,
|
convert(input_file=args[1], output_format=output_format,
|
||||||
#start=options.in_time,
|
|
||||||
#end=options.out_time,
|
|
||||||
include_muted=options.include_muted,
|
include_muted=options.include_muted,
|
||||||
xsl=options.xslt,
|
xsl=options.xslt,
|
||||||
#select_reel=options.select_reel,
|
|
||||||
progress=False, output=sys.stdout, log_output=sys.stderr,
|
progress=False, output=sys.stdout, log_output=sys.stderr,
|
||||||
warnings=options.warnings)
|
warnings=options.warnings)
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
|
|||||||
@@ -1,6 +1,22 @@
|
|||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
import re
|
import re
|
||||||
import math
|
import math
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
|
class TimecodeFormat(namedtuple("_TimecodeFormat", "frame_duration logical_fps drop_frame")):
|
||||||
|
|
||||||
|
def smpte_to_seconds(self, smpte: str) -> Fraction:
|
||||||
|
frame_count = smpte_to_frame_count(smpte, self.logical_fps, drop_frame_hint=self.drop_frame)
|
||||||
|
return frame_count * self.frame_duration
|
||||||
|
|
||||||
|
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) -> int:
|
def smpte_to_frame_count(smpte_rep_string: str, frames_per_logical_second: int, drop_frame_hint=False) -> int:
|
||||||
@@ -39,14 +55,11 @@ def smpte_to_frame_count(smpte_rep_string: str, frames_per_logical_second: int,
|
|||||||
dropped_frames = frames_dropped_per_inst * inst_count
|
dropped_frames = frames_dropped_per_inst * inst_count
|
||||||
frames = raw_frames - dropped_frames
|
frames = raw_frames - dropped_frames
|
||||||
|
|
||||||
# if include_fractional:
|
|
||||||
# return frames, frac
|
|
||||||
# else:
|
|
||||||
return frames
|
return frames
|
||||||
|
|
||||||
|
|
||||||
def frame_count_to_smpte(frame_count: int, frames_per_logical_second: int, drop_frame: bool = False,
|
def frame_count_to_smpte(frame_count: int, frames_per_logical_second: int, drop_frame: bool = False,
|
||||||
fractional_frame: float = None):
|
fractional_frame: float = None) -> 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
|
||||||
|
|
||||||
@@ -72,24 +85,18 @@ def frame_count_to_smpte(frame_count: int, frames_per_logical_second: int, drop_
|
|||||||
return "%02i:%02i:%02i%s%02i" % (hh, mm, ss, separator, ff)
|
return "%02i:%02i:%02i%s%02i" % (hh, mm, ss, separator, ff)
|
||||||
|
|
||||||
|
|
||||||
def footage_to_frame_count(footage_string, include_fractional=False):
|
def footage_to_frame_count(footage_string):
|
||||||
m = re.search("(\d+)\+(\d+)(\.\d+)?", footage_string)
|
m = re.search("(\d+)\+(\d+)(\.\d+)?", footage_string)
|
||||||
feet, frm, frac = m.groups()
|
feet, frm, frac = m.groups()
|
||||||
feet, frm, frac = int(feet), int(frm), float(frac or 0.0)
|
feet, frm, frac = int(feet), int(frm), float(frac or 0.0)
|
||||||
|
|
||||||
frames = feet * 16 + frm
|
frames = feet * 16 + frm
|
||||||
|
|
||||||
if include_fractional:
|
return frames
|
||||||
return frames, frac
|
|
||||||
else:
|
|
||||||
return frames
|
|
||||||
|
|
||||||
|
|
||||||
def frame_count_to_footage(frame_count, fractional_frames=None):
|
def frame_count_to_footage(frame_count):
|
||||||
assert fractional_frames is None or fractional_frames < 1.0
|
|
||||||
feet, frm = divmod(frame_count, 16)
|
feet, frm = divmod(frame_count, 16)
|
||||||
|
return "%i+%02i" % (feet, frm)
|
||||||
|
|
||||||
|
|
||||||
if fractional_frames is None:
|
|
||||||
return "%i+%02i" % (feet, frm)
|
|
||||||
else:
|
|
||||||
return "%i+%02i%s" % (feet, frm, ("%.3f" % fractional_frames)[1:])
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ from .validations import *
|
|||||||
|
|
||||||
from ptulsconv.docparser import parse_document
|
from ptulsconv.docparser import parse_document
|
||||||
from ptulsconv.docparser.tag_compiler import TagCompiler
|
from ptulsconv.docparser.tag_compiler import TagCompiler
|
||||||
|
from ptulsconv.broadcast_timecode import TimecodeFormat
|
||||||
|
from fractions import Fraction
|
||||||
|
|
||||||
from ptulsconv.pdf.supervisor_1pg import output_report as output_supervisor_1pg
|
from ptulsconv.pdf.supervisor_1pg import output_report as output_supervisor_1pg
|
||||||
from ptulsconv.pdf.line_count import output_report as output_line_count
|
from ptulsconv.pdf.line_count import output_report as output_line_count
|
||||||
@@ -18,10 +20,18 @@ from ptulsconv.pdf.talent_sides import output_report as output_talent_sides
|
|||||||
from ptulsconv.pdf.summary_log import output_report as output_summary
|
from ptulsconv.pdf.summary_log import output_report as output_summary
|
||||||
|
|
||||||
from json import JSONEncoder
|
from json import JSONEncoder
|
||||||
|
|
||||||
|
|
||||||
class MyEncoder(JSONEncoder):
|
class MyEncoder(JSONEncoder):
|
||||||
def default(self, o):
|
force_denominator: Optional[int]
|
||||||
|
|
||||||
|
def default(self, o):
|
||||||
|
if isinstance(o, Fraction):
|
||||||
|
return dict(numerator=o.numerator, denominator=o.denominator)
|
||||||
|
else:
|
||||||
return o.__dict__
|
return o.__dict__
|
||||||
|
|
||||||
|
|
||||||
def dump_csv(events, output=sys.stdout):
|
def dump_csv(events, output=sys.stdout):
|
||||||
keys = set()
|
keys = set()
|
||||||
for e in events:
|
for e in events:
|
||||||
@@ -69,6 +79,7 @@ def output_adr_csv(lines):
|
|||||||
with open(outfile_name, mode='w', newline='') as outfile:
|
with open(outfile_name, mode='w', newline='') as outfile:
|
||||||
dump_keyed_csv(these_lines, adr_keys, outfile)
|
dump_keyed_csv(these_lines, adr_keys, outfile)
|
||||||
|
|
||||||
|
|
||||||
def output_avid_markers(lines):
|
def output_avid_markers(lines):
|
||||||
reels = set([ln['Reel'] for ln in lines if 'Reel' in ln.keys()])
|
reels = set([ln['Reel'] for ln in lines if 'Reel' in ln.keys()])
|
||||||
|
|
||||||
@@ -76,44 +87,43 @@ def output_avid_markers(lines):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def create_adr_reports(parsed):
|
def create_adr_reports(lines: List[ADRLine], tc_display_format: TimecodeFormat):
|
||||||
lines = [e for e in parsed['events'] if 'ADR' in e.keys()]
|
|
||||||
|
|
||||||
print_section_header_style("Creating PDF Reports")
|
print_section_header_style("Creating PDF Reports")
|
||||||
print_status_style("Creating ADR Report")
|
print_status_style("Creating ADR Report")
|
||||||
output_summary(lines)
|
output_summary(lines, tc_display_format=tc_display_format)
|
||||||
|
|
||||||
print_status_style("Creating Line Count")
|
# print_status_style("Creating Line Count")
|
||||||
output_line_count(lines)
|
# output_line_count(lines)
|
||||||
|
#
|
||||||
print_status_style("Creating Supervisor Logs directory and reports")
|
# print_status_style("Creating Supervisor Logs directory and reports")
|
||||||
os.makedirs("Supervisor Logs", exist_ok=True)
|
# os.makedirs("Supervisor Logs", exist_ok=True)
|
||||||
os.chdir("Supervisor Logs")
|
# os.chdir("Supervisor Logs")
|
||||||
output_supervisor_1pg(lines)
|
# output_supervisor_1pg(lines)
|
||||||
os.chdir("..")
|
# os.chdir("..")
|
||||||
|
#
|
||||||
print_status_style("Creating Director's Logs director and reports")
|
# print_status_style("Creating Director's Logs director and reports")
|
||||||
os.makedirs("Director Logs", exist_ok=True)
|
# os.makedirs("Director Logs", exist_ok=True)
|
||||||
os.chdir("Director Logs")
|
# os.chdir("Director Logs")
|
||||||
output_summary(lines, by_character=True)
|
# output_summary(lines, tc_display_format=tc_display_format, by_character=True)
|
||||||
os.chdir("..")
|
# os.chdir("..")
|
||||||
|
#
|
||||||
print_status_style("Creating CSV outputs")
|
# print_status_style("Creating CSV outputs")
|
||||||
os.makedirs("CSV", exist_ok=True)
|
# os.makedirs("CSV", exist_ok=True)
|
||||||
os.chdir("CSV")
|
# os.chdir("CSV")
|
||||||
output_adr_csv(lines)
|
# output_adr_csv(lines)
|
||||||
os.chdir("..")
|
# os.chdir("..")
|
||||||
|
#
|
||||||
print_status_style("Creating Avid Marker XML files")
|
# print_status_style("Creating Avid Marker XML files")
|
||||||
os.makedirs("Avid Markers", exist_ok=True)
|
# os.makedirs("Avid Markers", exist_ok=True)
|
||||||
os.chdir("Avid Markers")
|
# os.chdir("Avid Markers")
|
||||||
output_avid_markers(lines)
|
# output_avid_markers(lines)
|
||||||
os.chdir("..")
|
# os.chdir("..")
|
||||||
|
#
|
||||||
print_status_style("Creating Scripts directory and reports")
|
# print_status_style("Creating Scripts directory and reports")
|
||||||
os.makedirs("Talent Scripts", exist_ok=True)
|
# os.makedirs("Talent Scripts", exist_ok=True)
|
||||||
os.chdir("Talent Scripts")
|
# os.chdir("Talent Scripts")
|
||||||
output_talent_sides(lines)
|
# output_talent_sides(lines)
|
||||||
|
|
||||||
|
|
||||||
def parse_text_export(file):
|
def parse_text_export(file):
|
||||||
@@ -127,51 +137,44 @@ def parse_text_export(file):
|
|||||||
return parsed
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
def raw_output(input_file, output=sys.stdout):
|
|
||||||
from .docparser.doc_parser_visitor import DocParserVisitor
|
|
||||||
from json import JSONEncoder
|
|
||||||
|
|
||||||
class DescriptorJSONEncoder(JSONEncoder):
|
|
||||||
def default(self, obj):
|
|
||||||
return obj.__dict__
|
|
||||||
|
|
||||||
with open(input_file, 'r') as file:
|
|
||||||
ast = ptulsconv.protools_text_export_grammar.parse(file.read())
|
|
||||||
visitor = DocParserVisitor()
|
|
||||||
parsed = visitor.visit(ast)
|
|
||||||
json.dump(parsed, output, cls=DescriptorJSONEncoder)
|
|
||||||
|
|
||||||
|
|
||||||
def convert(input_file, output_format='fmpxml',
|
def convert(input_file, output_format='fmpxml',
|
||||||
progress=False, include_muted=False, xsl=None,
|
progress=False, include_muted=False, xsl=None,
|
||||||
output=sys.stdout, log_output=sys.stderr, warnings=True):
|
output=sys.stdout, log_output=sys.stderr, warnings=True):
|
||||||
|
|
||||||
session = parse_document(input_file)
|
session = parse_document(input_file)
|
||||||
compiler = TagCompiler()
|
session_tc_format = session.header.timecode_format
|
||||||
compiler.session = session
|
|
||||||
compiled_events = compiler.compile_events()
|
|
||||||
|
|
||||||
lines = list(map(ADRLine.from_event, compiled_events))
|
if output_format == 'raw':
|
||||||
|
output.write(MyEncoder().encode(session))
|
||||||
|
|
||||||
if warnings:
|
else:
|
||||||
for warning in chain(validate_unique_field(lines, field='cue_number'),
|
|
||||||
validate_non_empty_field(lines, field='cue_number'),
|
|
||||||
validate_non_empty_field(lines, field='character_id'),
|
|
||||||
validate_non_empty_field(lines, field='title'),
|
|
||||||
validate_dependent_value(lines, key_field='character_id',
|
|
||||||
dependent_field='character_name'),
|
|
||||||
validate_dependent_value(lines, key_field='character_id',
|
|
||||||
dependent_field='actor_name')):
|
|
||||||
print_warning(warning.report_message())
|
|
||||||
|
|
||||||
if output_format == 'json':
|
compiler = TagCompiler()
|
||||||
print(MyEncoder().encode(lines))
|
compiler.session = session
|
||||||
|
compiled_events = list(compiler.compile_events())
|
||||||
|
if output_format == 'json':
|
||||||
|
output.write(MyEncoder().encode(compiled_events))
|
||||||
|
|
||||||
|
else:
|
||||||
|
lines = list(map(ADRLine.from_event, compiled_events))
|
||||||
|
|
||||||
|
if warnings:
|
||||||
|
for warning in chain(validate_unique_field(lines, field='cue_number'),
|
||||||
|
validate_non_empty_field(lines, field='cue_number'),
|
||||||
|
validate_non_empty_field(lines, field='character_id'),
|
||||||
|
validate_non_empty_field(lines, field='title'),
|
||||||
|
validate_dependent_value(lines, key_field='character_id',
|
||||||
|
dependent_field='character_name'),
|
||||||
|
validate_dependent_value(lines, key_field='character_id',
|
||||||
|
dependent_field='actor_name')):
|
||||||
|
print_warning(warning.report_message())
|
||||||
|
|
||||||
|
if output_format == 'adr':
|
||||||
|
create_adr_reports(lines, tc_display_format=session_tc_format)
|
||||||
|
|
||||||
# elif output_format == 'csv':
|
# elif output_format == 'csv':
|
||||||
# dump_csv(parsed['events'])
|
# dump_csv(parsed['events'])
|
||||||
#
|
#
|
||||||
# elif output_format == 'adr':
|
|
||||||
# create_adr_reports(parsed)
|
|
||||||
|
|
||||||
# elif output_format == 'fmpxml':
|
# elif output_format == 'fmpxml':
|
||||||
# if xsl is None:
|
# if xsl is None:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from ptulsconv.docparser.tag_compiler import Event
|
from ptulsconv.docparser.tag_compiler import Event
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from fractions import Fraction
|
||||||
|
|
||||||
from ptulsconv.docparser.tag_mapping import TagMapping
|
from ptulsconv.docparser.tag_mapping import TagMapping
|
||||||
|
|
||||||
@@ -13,8 +14,8 @@ class ADRLine:
|
|||||||
scene: Optional[str]
|
scene: Optional[str]
|
||||||
version: Optional[str]
|
version: Optional[str]
|
||||||
reel: Optional[str]
|
reel: Optional[str]
|
||||||
start: Optional[str]
|
start: Optional[Fraction]
|
||||||
finish: Optional[str]
|
finish: Optional[Fraction]
|
||||||
priority: Optional[int]
|
priority: Optional[int]
|
||||||
cue_number: Optional[str]
|
cue_number: Optional[str]
|
||||||
character_id: Optional[str]
|
character_id: Optional[str]
|
||||||
@@ -101,6 +102,8 @@ class ADRLine:
|
|||||||
new = cls()
|
new = cls()
|
||||||
TagMapping.apply_rules(cls.tag_mapping, event.tags,
|
TagMapping.apply_rules(cls.tag_mapping, event.tags,
|
||||||
event.clip_name, event.track_name, event.session_name, new)
|
event.clip_name, event.track_name, event.session_name, new)
|
||||||
|
new.start = event.start
|
||||||
|
new.finish = event.finish
|
||||||
return new
|
return new
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from ptulsconv.broadcast_timecode import smpte_to_frame_count
|
from ptulsconv.broadcast_timecode import TimecodeFormat
|
||||||
from typing import Tuple, List, Iterator
|
from typing import Tuple, List, Iterator
|
||||||
|
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ class SessionDescriptor:
|
|||||||
yield track, clip
|
yield track, clip
|
||||||
|
|
||||||
def track_clips_timed(self) -> Iterator[Tuple["TrackDescriptor", "TrackClipDescriptor",
|
def track_clips_timed(self) -> Iterator[Tuple["TrackDescriptor", "TrackClipDescriptor",
|
||||||
Fraction, Fraction, Fraction]]:
|
Fraction, Fraction, Fraction]]:
|
||||||
"""
|
"""
|
||||||
:return: A Generator that yields track, clip, start time, finish time, and timestamp
|
:return: A Generator that yields track, clip, start time, finish time, and timestamp
|
||||||
"""
|
"""
|
||||||
@@ -48,7 +48,7 @@ class HeaderDescriptor:
|
|||||||
sample_rate: float
|
sample_rate: float
|
||||||
bit_depth: int
|
bit_depth: int
|
||||||
start_timecode: str
|
start_timecode: str
|
||||||
timecode_format: str
|
timecode_fps: str
|
||||||
timecode_drop_frame: bool
|
timecode_drop_frame: bool
|
||||||
count_audio_tracks: int
|
count_audio_tracks: int
|
||||||
count_clips: int
|
count_clips: int
|
||||||
@@ -59,18 +59,20 @@ class HeaderDescriptor:
|
|||||||
self.sample_rate = kwargs['sample_rate']
|
self.sample_rate = kwargs['sample_rate']
|
||||||
self.bit_depth = kwargs['bit_depth']
|
self.bit_depth = kwargs['bit_depth']
|
||||||
self.start_timecode = kwargs['start_timecode']
|
self.start_timecode = kwargs['start_timecode']
|
||||||
self.timecode_format = kwargs['timecode_format']
|
self.timecode_fps = kwargs['timecode_format']
|
||||||
self.timecode_drop_frame = kwargs['timecode_drop_frame']
|
self.timecode_drop_frame = kwargs['timecode_drop_frame']
|
||||||
self.count_audio_tracks = kwargs['count_audio_tracks']
|
self.count_audio_tracks = kwargs['count_audio_tracks']
|
||||||
self.count_clips = kwargs['count_clips']
|
self.count_clips = kwargs['count_clips']
|
||||||
self.count_files = kwargs['count_files']
|
self.count_files = kwargs['count_files']
|
||||||
|
|
||||||
def convert_timecode(self, tc_string: str) -> Fraction:
|
@property
|
||||||
frame_count = smpte_to_frame_count(tc_string,
|
def timecode_format(self):
|
||||||
self.logical_fps,
|
return TimecodeFormat(frame_duration=self.frame_duration,
|
||||||
self.timecode_drop_frame)
|
logical_fps=self.logical_fps,
|
||||||
|
drop_frame=self.timecode_drop_frame)
|
||||||
|
|
||||||
return self.frame_duration * frame_count
|
def convert_timecode(self, tc_string: str) -> Fraction:
|
||||||
|
return self.timecode_format.smpte_to_seconds(tc_string)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def start_time(self) -> Fraction:
|
def start_time(self) -> Fraction:
|
||||||
@@ -91,16 +93,16 @@ class HeaderDescriptor:
|
|||||||
@property
|
@property
|
||||||
def _get_tc_format_params(self) -> Tuple[int, Fraction]:
|
def _get_tc_format_params(self) -> Tuple[int, Fraction]:
|
||||||
frame_rates = {"23.976": (24, Fraction(1001, 24_000)),
|
frame_rates = {"23.976": (24, Fraction(1001, 24_000)),
|
||||||
"24": (24, Fraction(1, 24)),
|
"24": (24, Fraction(1, 24)),
|
||||||
"25": (25, Fraction(1, 25)),
|
"25": (25, Fraction(1, 25)),
|
||||||
"29.97": (30, Fraction(1001, 30_000)),
|
"29.97": (30, Fraction(1001, 30_000)),
|
||||||
"30": (30, Fraction(1, 30)),
|
"30": (30, Fraction(1, 30)),
|
||||||
"59.94": (60, Fraction(1001, 60_000)),
|
"59.94": (60, Fraction(1001, 60_000)),
|
||||||
"60": (60, Fraction(1, 60))
|
"60": (60, Fraction(1, 60))
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.timecode_format in frame_rates.keys():
|
if self.timecode_fps in frame_rates.keys():
|
||||||
return frame_rates[self.timecode_format]
|
return frame_rates[self.timecode_fps]
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unrecognized TC rate (%s)" % self.timecode_format)
|
raise ValueError("Unrecognized TC rate (%s)" % self.timecode_format)
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,28 @@
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from typing import Iterator, Tuple, Callable, Generator
|
from typing import Iterator, Tuple, Callable, Generator, Dict
|
||||||
|
|
||||||
import ptulsconv.docparser.doc_entity as doc_entity
|
import ptulsconv.docparser.doc_entity as doc_entity
|
||||||
from .tagged_string_parser_visitor import parse_tags, TagPreModes
|
from .tagged_string_parser_visitor import parse_tags, TagPreModes
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
class Event(namedtuple('Event', 'clip_name track_name session_name tags start finish')):
|
|
||||||
pass
|
@dataclass
|
||||||
|
class Event:
|
||||||
|
clip_name: str
|
||||||
|
track_name: str
|
||||||
|
session_name: str
|
||||||
|
tags: Dict[str, str]
|
||||||
|
start: Fraction
|
||||||
|
finish: Fraction
|
||||||
|
|
||||||
|
|
||||||
class TagCompiler:
|
class TagCompiler:
|
||||||
|
|
||||||
|
Intermediate = namedtuple('Intermediate', 'track_content track_tags track_comment_tags '
|
||||||
|
'clip_content clip_tags clip_tag_mode start finish')
|
||||||
|
|
||||||
session: doc_entity.SessionDescriptor
|
session: doc_entity.SessionDescriptor
|
||||||
|
|
||||||
def compile_events(self) -> Iterator[Event]:
|
def compile_events(self) -> Iterator[Event]:
|
||||||
@@ -19,11 +31,12 @@ class TagCompiler:
|
|||||||
step2 = self.collect_time_spans(step1)
|
step2 = self.collect_time_spans(step1)
|
||||||
step3 = self.apply_tags(step2)
|
step3 = self.apply_tags(step2)
|
||||||
for datum in step3:
|
for datum in step3:
|
||||||
yield Event(*datum)
|
yield Event(clip_name=datum[0], track_name=datum[1], session_name=datum[2],
|
||||||
|
tags=datum[3], start=datum[4], finish=datum[5])
|
||||||
|
|
||||||
def _marker_tags(self, at):
|
def _marker_tags(self, at):
|
||||||
retval = dict()
|
retval = dict()
|
||||||
applicable = [(m, t) for (m, t) in self.session.markers_timed() if t >= at]
|
applicable = [(m, t) for (m, t) in self.session.markers_timed() if t <= at]
|
||||||
for marker, time in sorted(applicable, key=lambda x: x[1]):
|
for marker, time in sorted(applicable, key=lambda x: x[1]):
|
||||||
retval.update(parse_tags(marker.comments).tag_dict)
|
retval.update(parse_tags(marker.comments).tag_dict)
|
||||||
retval.update(parse_tags(marker.name).tag_dict)
|
retval.update(parse_tags(marker.name).tag_dict)
|
||||||
@@ -44,9 +57,6 @@ class TagCompiler:
|
|||||||
effective_tags.update(clip_tags)
|
effective_tags.update(clip_tags)
|
||||||
return effective_tags
|
return effective_tags
|
||||||
|
|
||||||
Intermediate = namedtuple('Intermediate', 'track_content track_tags track_comment_tags '
|
|
||||||
'clip_content clip_tags clip_tag_mode start finish')
|
|
||||||
|
|
||||||
def parse_data(self) -> Iterator[Intermediate]:
|
def parse_data(self) -> Iterator[Intermediate]:
|
||||||
|
|
||||||
for track, clip, start, finish, _ in self.session.track_clips_timed():
|
for track, clip, start, finish, _ in self.session.track_clips_timed():
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ from reportlab.platypus.frames import Frame
|
|||||||
from reportlab.pdfbase import pdfmetrics
|
from reportlab.pdfbase import pdfmetrics
|
||||||
from reportlab.pdfbase.ttfonts import TTFont
|
from reportlab.pdfbase.ttfonts import TTFont
|
||||||
|
|
||||||
|
from ptulsconv.docparser.adr_entity import ADRLine
|
||||||
|
|
||||||
|
|
||||||
# This is from https://code.activestate.com/recipes/576832/ for
|
# This is from https://code.activestate.com/recipes/576832/ for
|
||||||
# generating page count messages
|
# generating page count messages
|
||||||
@@ -50,7 +52,7 @@ class ADRDocTemplate(BaseDocTemplate):
|
|||||||
BaseDocTemplate.build(self, flowables, filename, canvasmaker)
|
BaseDocTemplate.build(self, flowables, filename, canvasmaker)
|
||||||
|
|
||||||
|
|
||||||
def make_doc_template(page_size, filename, document_title, record, document_header="", left_margin=0.5 * inch):
|
def make_doc_template(page_size, filename, document_title, record: ADRLine, document_header="", left_margin=0.5 * inch):
|
||||||
right_margin = top_margin = bottom_margin = 0.5 * inch
|
right_margin = top_margin = bottom_margin = 0.5 * inch
|
||||||
page_box = GRect(0., 0., page_size[0], page_size[1])
|
page_box = GRect(0., 0., page_size[0], page_size[1])
|
||||||
_, page_box = page_box.split_x(left_margin, direction='l')
|
_, page_box = page_box.split_x(left_margin, direction='l')
|
||||||
@@ -70,7 +72,7 @@ def make_doc_template(page_size, filename, document_title, record, document_head
|
|||||||
pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc'))
|
pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc'))
|
||||||
doc = ADRDocTemplate(filename,
|
doc = ADRDocTemplate(filename,
|
||||||
title=document_title,
|
title=document_title,
|
||||||
author=record.get('Supervisor', ""),
|
author=record.supervisor,
|
||||||
pagesize=page_size,
|
pagesize=page_size,
|
||||||
leftMargin=left_margin, rightMargin=right_margin,
|
leftMargin=left_margin, rightMargin=right_margin,
|
||||||
topMargin=top_margin, bottomMargin=bottom_margin)
|
topMargin=top_margin, bottomMargin=bottom_margin)
|
||||||
@@ -91,11 +93,11 @@ def time_format(mins, zero_str=""):
|
|||||||
return "%i:%02i" % (hh, mm)
|
return "%i:%02i" % (hh, mm)
|
||||||
|
|
||||||
|
|
||||||
def draw_header_footer(a_canvas: ReportCanvas, title_box, doc_title_box, footer_box, record, doc_title=""):
|
def draw_header_footer(a_canvas: ReportCanvas, title_box, doc_title_box, footer_box, record: ADRLine, doc_title=""):
|
||||||
|
|
||||||
(supervisor, client,), title = title_box.divide_y([16., 16., ])
|
(supervisor, client,), title = title_box.divide_y([16., 16., ])
|
||||||
title.draw_text_cell(a_canvas, record['Title'], "Futura", 18, inset_y=2., inset_x=5.)
|
title.draw_text_cell(a_canvas, record.title, "Futura", 18, inset_y=2., inset_x=5.)
|
||||||
client.draw_text_cell(a_canvas, record.get('Client', ''), "Futura", 11, inset_y=2., inset_x=5.)
|
client.draw_text_cell(a_canvas, record.client, "Futura", 11, inset_y=2., inset_x=5.)
|
||||||
|
|
||||||
a_canvas.saveState()
|
a_canvas.saveState()
|
||||||
a_canvas.setLineWidth(0.5)
|
a_canvas.setLineWidth(0.5)
|
||||||
@@ -114,12 +116,12 @@ def draw_header_footer(a_canvas: ReportCanvas, title_box, doc_title_box, footer_
|
|||||||
|
|
||||||
doc_title_cell.draw_text_cell(a_canvas, doc_title, 'Futura', 14., inset_y=2.)
|
doc_title_cell.draw_text_cell(a_canvas, doc_title, 'Futura', 14., inset_y=2.)
|
||||||
|
|
||||||
if 'Spot' in record.keys():
|
if record.spot is not None:
|
||||||
spotting_version_cell.draw_text_cell(a_canvas, record['Spot'], 'Futura', 12., inset_y=2.)
|
spotting_version_cell.draw_text_cell(a_canvas, record.spot, 'Futura', 12., inset_y=2.)
|
||||||
|
|
||||||
a_canvas.setFont('Futura', 11.)
|
a_canvas.setFont('Futura', 11.)
|
||||||
a_canvas.drawCentredString(footer_box.min_x + footer_box.width / 2., footer_box.min_y,
|
a_canvas.drawCentredString(footer_box.min_x + footer_box.width / 2., footer_box.min_y,
|
||||||
record.get('Supervisor', 'Supervisor: ________________'))
|
record.supervisor or "")
|
||||||
|
|
||||||
|
|
||||||
class GRect:
|
class GRect:
|
||||||
@@ -254,6 +256,9 @@ class GRect:
|
|||||||
def draw_text_cell(self, a_canvas, text, font_name, font_size,
|
def draw_text_cell(self, a_canvas, text, font_name, font_size,
|
||||||
vertical_align='t', force_baseline=None, inset_x=0.,
|
vertical_align='t', force_baseline=None, inset_x=0.,
|
||||||
inset_y=0., draw_baseline=False):
|
inset_y=0., draw_baseline=False):
|
||||||
|
if text is None:
|
||||||
|
return
|
||||||
|
|
||||||
a_canvas.saveState()
|
a_canvas.saveState()
|
||||||
|
|
||||||
inset_rect = self.inset_xy(inset_x, inset_y)
|
inset_rect = self.inset_xy(inset_x, inset_y)
|
||||||
|
|||||||
@@ -6,40 +6,41 @@ from reportlab.lib.pagesizes import letter, portrait
|
|||||||
|
|
||||||
from reportlab.platypus import Paragraph, Spacer, KeepTogether, Table
|
from reportlab.platypus import Paragraph, Spacer, KeepTogether, Table
|
||||||
from reportlab.lib.styles import getSampleStyleSheet
|
from reportlab.lib.styles import getSampleStyleSheet
|
||||||
from reportlab.lib import colors
|
|
||||||
|
from typing import List
|
||||||
|
from ptulsconv.docparser.adr_entity import ADRLine
|
||||||
|
from ptulsconv.broadcast_timecode import TimecodeFormat
|
||||||
|
|
||||||
|
|
||||||
def build_aux_data_field(line):
|
def build_aux_data_field(line: ADRLine):
|
||||||
entries = list()
|
entries = list()
|
||||||
if 'Reason' in line.keys():
|
if line.reason is not None:
|
||||||
entries.append("Reason: " + line["Reason"])
|
entries.append("Reason: " + line.reason)
|
||||||
if 'Note' in line.keys():
|
if line.note is not None:
|
||||||
entries.append("Note: " + line["Note"])
|
entries.append("Note: " + line.note)
|
||||||
if 'Requested by' in line.keys():
|
if line.requested_by is not None:
|
||||||
entries.append("Requested by: " + line["Requested by"])
|
entries.append("Requested by: " + line.requested_by)
|
||||||
if 'Shot' in line.keys():
|
if line.shot is not None:
|
||||||
entries.append("Shot: " + line["Shot"])
|
entries.append("Shot: " + line.shot)
|
||||||
|
|
||||||
|
fg_color = 'white'
|
||||||
tag_field = ""
|
tag_field = ""
|
||||||
for tag in line.keys():
|
if line.effort:
|
||||||
if line[tag] == tag and tag != 'ADR':
|
bg_color = 'red'
|
||||||
fcolor = 'white'
|
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font> " % (bg_color, fg_color, "EFF")
|
||||||
bcolor = 'black'
|
elif line.tv:
|
||||||
if tag == 'ADLIB' or tag == 'TBW':
|
bg_color = 'blue'
|
||||||
bcolor = 'darkmagenta'
|
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font> " % (bg_color, fg_color, "TV")
|
||||||
elif tag == 'EFF':
|
elif line.adlib:
|
||||||
bcolor = 'red'
|
bg_color = 'purple'
|
||||||
elif tag == 'TV':
|
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font> " % (bg_color, fg_color, "ADLIB")
|
||||||
bcolor = 'blue'
|
|
||||||
|
|
||||||
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font> " % (bcolor, fcolor, tag)
|
|
||||||
|
|
||||||
entries.append(tag_field)
|
entries.append(tag_field)
|
||||||
|
|
||||||
return "<br />".join(entries)
|
return "<br />".join(entries)
|
||||||
|
|
||||||
|
|
||||||
def build_story(lines):
|
def build_story(lines: List[ADRLine], tc_rate: TimecodeFormat):
|
||||||
story = list()
|
story = list()
|
||||||
|
|
||||||
this_scene = None
|
this_scene = None
|
||||||
@@ -56,20 +57,20 @@ def build_story(lines):
|
|||||||
('LEFTPADDING', (0, 0), (0, 0), 0.0),
|
('LEFTPADDING', (0, 0), (0, 0), 0.0),
|
||||||
('BOTTOMPADDING', (0, 0), (-1, -1), 24.)]
|
('BOTTOMPADDING', (0, 0), (-1, -1), 24.)]
|
||||||
|
|
||||||
cue_number_field = "%s<br /><font fontSize=7>%s</font>" % (line['Cue Number'], line['Character Name'])
|
cue_number_field = "%s<br /><font fontSize=7>%s</font>" % (line.cue_number, line.character_name)
|
||||||
|
|
||||||
time_data = time_format(line.get('Time Budget Mins', 0.))
|
time_data = time_format(line.time_budget_mins)
|
||||||
|
|
||||||
if 'Priority' in line.keys():
|
if line.priority is not None:
|
||||||
time_data = time_data + "<br />" + "P: " + int(line['Priority'])
|
time_data = time_data + "<br />" + "P: " + line.priority
|
||||||
|
|
||||||
aux_data_field = build_aux_data_field(line)
|
aux_data_field = build_aux_data_field(line)
|
||||||
|
|
||||||
tc_data = build_tc_data(line)
|
tc_data = build_tc_data(line, tc_rate)
|
||||||
|
|
||||||
line_table_data = [[Paragraph(cue_number_field, line_style),
|
line_table_data = [[Paragraph(cue_number_field, line_style),
|
||||||
Paragraph(tc_data, line_style),
|
Paragraph(tc_data, line_style),
|
||||||
Paragraph(line['Line'], line_style),
|
Paragraph(line.prompt, line_style),
|
||||||
Paragraph(time_data, line_style),
|
Paragraph(time_data, line_style),
|
||||||
Paragraph(aux_data_field, line_style)
|
Paragraph(aux_data_field, line_style)
|
||||||
]]
|
]]
|
||||||
@@ -78,8 +79,8 @@ def build_story(lines):
|
|||||||
colWidths=[inch * 0.75, inch, inch * 3., 0.5 * inch, inch * 2.],
|
colWidths=[inch * 0.75, inch, inch * 3., 0.5 * inch, inch * 2.],
|
||||||
style=table_style)
|
style=table_style)
|
||||||
|
|
||||||
if line.get('Scene', "[No Scene]") != this_scene:
|
if (line.scene or "[No Scene]") != this_scene:
|
||||||
this_scene = line.get('Scene', "[No Scene]")
|
this_scene = line.scene or "[No Scene]"
|
||||||
story.append(KeepTogether([
|
story.append(KeepTogether([
|
||||||
Spacer(1., 0.25 * inch),
|
Spacer(1., 0.25 * inch),
|
||||||
Paragraph("<u>" + this_scene + "</u>", scene_style),
|
Paragraph("<u>" + this_scene + "</u>", scene_style),
|
||||||
@@ -91,52 +92,51 @@ def build_story(lines):
|
|||||||
return story
|
return story
|
||||||
|
|
||||||
|
|
||||||
def build_tc_data(line):
|
def build_tc_data(line: ADRLine, tc_format: TimecodeFormat):
|
||||||
tc_data = line['PT.Clip.Start'] + "<br />" + line['PT.Clip.Finish']
|
tc_data = tc_format.seconds_to_smpte(line.start) + "<br />" + \
|
||||||
|
tc_format.seconds_to_smpte(line.finish)
|
||||||
third_line = []
|
third_line = []
|
||||||
if 'Reel' in line.keys():
|
if line.reel is not None:
|
||||||
if line['Reel'][0:1] == 'R':
|
if line.reel[0:1] == 'R':
|
||||||
third_line.append("%s" % (line['Reel']))
|
third_line.append("%s" % line.reel)
|
||||||
else:
|
else:
|
||||||
third_line.append("Reel %s" % (line['Reel']))
|
third_line.append("Reel %s" % line.reel)
|
||||||
if 'Version' in line.keys():
|
if line.version is not None:
|
||||||
third_line.append("(%s)" % line['Version'])
|
third_line.append("(%s)" % line.version)
|
||||||
if len(third_line) > 0:
|
if len(third_line) > 0:
|
||||||
tc_data = tc_data + "<br/>" + " ".join(third_line)
|
tc_data = tc_data + "<br/>" + " ".join(third_line)
|
||||||
return tc_data
|
return tc_data
|
||||||
|
|
||||||
|
|
||||||
def generate_report(page_size, lines, character_number=None, include_done=True,
|
def generate_report(page_size, lines: List[ADRLine], tc_rate: TimecodeFormat, character_number=None,
|
||||||
include_omitted=True):
|
include_omitted=True):
|
||||||
if character_number is not None:
|
if character_number is not None:
|
||||||
lines = [r for r in lines if r['Character Number'] == character_number]
|
lines = [r for r in lines if r.character_id == character_number]
|
||||||
title = "%s ADR Report (%s)" % (lines[0]['Title'], lines[0]['Character Name'])
|
title = "%s ADR Report (%s)" % (lines[0].title, lines[0].character_name)
|
||||||
document_header = "%s ADR Report" % (lines[0]['Character Name'])
|
document_header = "%s ADR Report" % lines[0].character_name
|
||||||
else:
|
else:
|
||||||
title = "%s ADR Report" % (lines[0]['Title'])
|
title = "%s ADR Report" % lines[0].title
|
||||||
document_header = 'ADR Report'
|
document_header = 'ADR Report'
|
||||||
|
|
||||||
if not include_done:
|
|
||||||
lines = [line for line in lines if 'Done' not in line.keys()]
|
|
||||||
|
|
||||||
if not include_omitted:
|
if not include_omitted:
|
||||||
lines = [line for line in lines if 'Omitted' not in line.keys()]
|
lines = [line for line in lines if not line.omitted]
|
||||||
|
|
||||||
lines = sorted(lines, key=lambda line: line['PT.Clip.Start_Seconds'])
|
lines = sorted(lines, key=lambda line: line.start)
|
||||||
|
|
||||||
filename = title + ".pdf"
|
filename = title + ".pdf"
|
||||||
doc = make_doc_template(page_size=page_size,
|
doc = make_doc_template(page_size=page_size,
|
||||||
filename=filename, document_title=title,
|
filename=filename, document_title=title,
|
||||||
record=lines[0], document_header=document_header,
|
record=lines[0], document_header=document_header,
|
||||||
left_margin=0.75 * inch)
|
left_margin=0.75 * inch)
|
||||||
story = build_story(lines)
|
story = build_story(lines, tc_rate)
|
||||||
doc.build(story)
|
doc.build(story)
|
||||||
|
|
||||||
|
|
||||||
def output_report(lines, page_size=portrait(letter), by_character=False):
|
def output_report(lines: List[ADRLine], tc_display_format: TimecodeFormat,
|
||||||
|
page_size=portrait(letter), by_character=False):
|
||||||
if by_character:
|
if by_character:
|
||||||
character_numbers = set((r['Character Number'] for r in lines))
|
character_numbers = set((r.character_id for r in lines))
|
||||||
for n in character_numbers:
|
for n in character_numbers:
|
||||||
generate_report(page_size, lines, n)
|
generate_report(page_size, lines, tc_display_format, n)
|
||||||
else:
|
else:
|
||||||
generate_report(page_size, lines)
|
generate_report(page_size, lines, tc_display_format)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from ptulsconv import broadcast_timecode
|
from ptulsconv import broadcast_timecode
|
||||||
|
from fractions import Fraction
|
||||||
|
|
||||||
class TestBroadcastTimecode(unittest.TestCase):
|
class TestBroadcastTimecode(unittest.TestCase):
|
||||||
def test_basic_to_framecount(self):
|
def test_basic_to_frame_count(self):
|
||||||
r1 = "01:00:00:00"
|
r1 = "01:00:00:00"
|
||||||
f1 = broadcast_timecode.smpte_to_frame_count(r1, 24, False)
|
f1 = broadcast_timecode.smpte_to_frame_count(r1, 24, False)
|
||||||
self.assertEqual(f1, 86_400)
|
self.assertEqual(f1, 86_400)
|
||||||
@@ -32,13 +32,7 @@ class TestBroadcastTimecode(unittest.TestCase):
|
|||||||
s1 = broadcast_timecode.frame_count_to_smpte(c1, 30, drop_frame=True)
|
s1 = broadcast_timecode.frame_count_to_smpte(c1, 30, drop_frame=True)
|
||||||
self.assertEqual(s1, "01:00:03;18")
|
self.assertEqual(s1, "01:00:03;18")
|
||||||
|
|
||||||
def test_fractional_to_string(self):
|
def test_drop_frame_to_frame_count(self):
|
||||||
c1 = 99
|
|
||||||
f1 = .145
|
|
||||||
s1 = broadcast_timecode.frame_count_to_smpte(c1, 25, drop_frame=False, fractional_frame=f1)
|
|
||||||
self.assertEqual(s1, "00:00:03:24.145")
|
|
||||||
|
|
||||||
def test_drop_frame_to_framecount(self):
|
|
||||||
r1 = "01:00:00;00"
|
r1 = "01:00:00;00"
|
||||||
z1 = broadcast_timecode.smpte_to_frame_count(r1, 30, drop_frame_hint=True)
|
z1 = broadcast_timecode.smpte_to_frame_count(r1, 30, drop_frame_hint=True)
|
||||||
self.assertEqual(z1, 107_892)
|
self.assertEqual(z1, 107_892)
|
||||||
@@ -55,13 +49,13 @@ class TestBroadcastTimecode(unittest.TestCase):
|
|||||||
f3 = broadcast_timecode.smpte_to_frame_count(r3, 30, True)
|
f3 = broadcast_timecode.smpte_to_frame_count(r3, 30, True)
|
||||||
self.assertEqual(f3, 1799)
|
self.assertEqual(f3, 1799)
|
||||||
|
|
||||||
def test_footage_to_framecount(self):
|
def test_footage_to_frame_count(self):
|
||||||
s1 = "194+11"
|
s1 = "194+11"
|
||||||
f1 = broadcast_timecode.footage_to_frame_count(s1)
|
f1 = broadcast_timecode.footage_to_frame_count(s1)
|
||||||
self.assertEqual(f1, 3115)
|
self.assertEqual(f1, 3115)
|
||||||
|
|
||||||
s3 = "0+0.1"
|
s3 = "0+0.1"
|
||||||
f3 = broadcast_timecode.footage_to_frame_count(s3, include_fractional=False)
|
f3 = broadcast_timecode.footage_to_frame_count(s3)
|
||||||
self.assertEqual(f3, 0)
|
self.assertEqual(f3, 0)
|
||||||
|
|
||||||
def test_frame_count_to_footage(self):
|
def test_frame_count_to_footage(self):
|
||||||
@@ -69,10 +63,12 @@ class TestBroadcastTimecode(unittest.TestCase):
|
|||||||
s1 = broadcast_timecode.frame_count_to_footage(c1)
|
s1 = broadcast_timecode.frame_count_to_footage(c1)
|
||||||
self.assertEqual(s1, "1+03")
|
self.assertEqual(s1, "1+03")
|
||||||
|
|
||||||
c2 = 24
|
def test_seconds_to_smpte(self):
|
||||||
f2 = .1
|
secs = Fraction(25, 24)
|
||||||
s2 = broadcast_timecode.frame_count_to_footage(c2, fractional_frames=f2)
|
frame_duration = Fraction(1, 24)
|
||||||
self.assertEqual(s2, "1+08.100")
|
s1 = broadcast_timecode.seconds_to_smpte(secs, frame_duration, 24, False)
|
||||||
|
self.assertEqual(s1, "00:00:01:01")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ from fractions import Fraction
|
|||||||
|
|
||||||
|
|
||||||
class TestTagCompiler(unittest.TestCase):
|
class TestTagCompiler(unittest.TestCase):
|
||||||
|
|
||||||
|
#TODO Test marker comment application
|
||||||
|
|
||||||
def test_one_track(self):
|
def test_one_track(self):
|
||||||
c = ptulsconv.docparser.tag_compiler.TagCompiler()
|
c = ptulsconv.docparser.tag_compiler.TagCompiler()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user