diff --git a/ptulsconv/__init__.py b/ptulsconv/__init__.py index e6af373..50b6c7e 100644 --- a/ptulsconv/__init__.py +++ b/ptulsconv/__init__.py @@ -2,6 +2,6 @@ from .ptuls_grammar import protools_text_export_grammar from .ptuls_parser_visitor import DictionaryParserVisitor from .transformations import TimecodeInterpreter -__version__ = '0.3.3' +__version__ = '0.4.0' __author__ = 'Jamie Hardt' __license__ = 'MIT' diff --git a/ptulsconv/__main__.py b/ptulsconv/__main__.py index f37a2e0..2c5f508 100644 --- a/ptulsconv/__main__.py +++ b/ptulsconv/__main__.py @@ -1,10 +1,12 @@ -from ptulsconv.commands import convert, dump_field_map +from ptulsconv.commands import convert, dump_field_map, dump_xform_options from ptulsconv import __name__, __version__, __author__ from optparse import OptionParser, OptionGroup from .reporting import print_status_style, print_banner_style, print_section_header_style, print_fatal_error import datetime import sys +import traceback + def main(): parser = OptionParser() parser.usage = "ptulsconv TEXT_EXPORT.txt" @@ -21,9 +23,15 @@ def main(): action='store_true', default=False, help='Display tag mappings for the FMP XML output style and exit.') + parser.add_option('--show-available-transforms', dest='show_transforms', + action='store_true', + default=False, help='Display available built-in XSLT transforms.') + + parser.add_option('--xform', dest='xslt', help="Convert with built-is XSLT transform.", + default=None, metavar='NAME') (options, args) = parser.parse_args(sys.argv) - print_banner_style("%s %s (c) 2019 %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_status_style("This run started %s" % (datetime.datetime.now().isoformat() ) ) @@ -32,6 +40,10 @@ def main(): dump_field_map('ADR') sys.exit(0) + if options.show_transforms: + dump_xform_options() + sys.exit(0) + if len(args) < 2: print_fatal_error("Error: No input file") parser.print_help(sys.stderr) @@ -55,7 +67,7 @@ def main(): try: convert(input_file=args[1], start=options.in_time, end=options.out_time, - include_muted=options.include_muted, + include_muted=options.include_muted, xsl=options.xslt, progress=False, output=sys.stdout, log_output=sys.stderr) except FileNotFoundError as e: print_fatal_error("Error trying to read input file") @@ -63,6 +75,7 @@ def main(): except Exception as e: 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__": diff --git a/ptulsconv/commands.py b/ptulsconv/commands.py index e35706c..31eb6d9 100644 --- a/ptulsconv/commands.py +++ b/ptulsconv/commands.py @@ -1,8 +1,11 @@ import io import json +import os import os.path import sys from xml.etree.ElementTree import TreeBuilder, tostring +import subprocess +import pathlib import ptulsconv from .reporting import print_section_header_style, print_status_style @@ -91,6 +94,21 @@ def fmp_dump(data, input_file_name, output): output.write(xmlstr) +import glob + +xslt_path = os.path.join(pathlib.Path(__file__).parent.absolute(), 'xslt') + +def xform_options(): + return glob.glob(os.path.join(xslt_path, "*.xsl")) + +def dump_xform_options(output=sys.stdout): + print("# Available transforms:", file=output) + print("# Transform dir: %s" % (xslt_path), file=output) + for f in xform_options(): + base = os.path.basename(f) + name, _ = os.path.splitext(base) + print("# " + name, file=output) + def dump_field_map(field_map_name, output=sys.stdout): output.write("# Map of Tag fields to XML output columns\n") output.write("# (in order of precedence)\n") @@ -106,13 +124,28 @@ def dump_field_map(field_map_name, output=sys.stdout): for n, field in enumerate(field_map): for tag in field[0]: - output.write("# %-24s-> %-20s | %-8s| %-7i\n" % (tag[:24], field[1][:20], field[2].__name__, n+1 )) + output.write("# %-24s-> %-20s | %-8s| %-7i\n" % (tag[:24], field[1][:20], field[2].__name__, n + 1)) + + +def fmp_transformed_dump(data, input_file, xsl_name, output): + pipe = io.StringIO() + print_status_style("Generating base XML") + fmp_dump(data, input_file, pipe) + + strdata = pipe.getvalue() + print_status_style("Base XML size %i" % (len(strdata))) + + 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)) + result = subprocess.run(['xsltproc', xsl_path, '-'], input=strdata, text=True, + stdout=output, shell=False, check=True) def convert(input_file, output_format='fmpxml', start=None, end=None, - progress=False, include_muted=False, + progress=False, include_muted=False, xsl=None, output=sys.stdout, log_output=sys.stderr): - with open(input_file, 'r') as file: print_section_header_style('Parsing') ast = ptulsconv.protools_text_export_grammar.parse(file.read()) @@ -146,4 +179,9 @@ def convert(input_file, output_format='fmpxml', start=None, end=None, if output_format == 'json': json.dump(parsed, output) elif output_format == 'fmpxml': - fmp_dump(parsed, input_file, output) + if xsl is None: + fmp_dump(parsed, input_file, output) + else: + print_section_header_style("Performing XSL Translation") + print_status_style("Using builtin translation: %s" % (xsl)) + fmp_transformed_dump(parsed, input_file, xsl, output)