97 lines
4.1 KiB
Python
97 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import shutil
|
|
import tempfile
|
|
from pathlib import Path
|
|
from time import monotonic
|
|
|
|
from generator.arrange.generate import generate_performance_scores
|
|
from generator.practice.generate import generate_practice_scores
|
|
from generator.score import count_json_files, validate_generated_tree
|
|
from generator.summary import validate_expected_performance_summary, validate_practice_variants
|
|
from generator.musicxml import load_musicxml
|
|
from generator.engraving import engrave_tree
|
|
from generator.playback import compile_tree
|
|
from generator.catalogue import build_catalogue
|
|
|
|
RUNTIME_FONT_FILES = ("PetalumaScript.otf", "OFL.txt")
|
|
|
|
def copy_runtime_fonts(static_assets_dir: Path, output_dir: Path) -> None:
|
|
source = static_assets_dir / "fonts" / "petaluma"
|
|
destination = output_dir / "fonts" / "petaluma"
|
|
destination.mkdir(parents=True, exist_ok=True)
|
|
for name in RUNTIME_FONT_FILES:
|
|
shutil.copy2(source / name, destination / name)
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description="Generate Product Vamp package score assets.")
|
|
parser.add_argument("--data-dir", type=Path, required=True)
|
|
parser.add_argument("--output-dir", type=Path, required=True)
|
|
parser.add_argument("--static-assets-dir", type=Path, default=Path("package/assets"))
|
|
parser.add_argument("--check", action="store_true")
|
|
parser.add_argument("--engraving-cache", type=Path)
|
|
parser.add_argument("--jobs", type=int, default=1, choices=range(1, 17), metavar="1-16")
|
|
parser.add_argument("--progress", action="store_true")
|
|
return parser.parse_args()
|
|
|
|
def generate_assets(args: argparse.Namespace) -> None:
|
|
data_dir = args.data_dir.resolve()
|
|
output_dir = args.output_dir.resolve()
|
|
static_assets_dir = args.static_assets_dir.resolve()
|
|
cache_dir = getattr(args, 'engraving_cache', None)
|
|
cache_dir = cache_dir.resolve() if cache_dir else None
|
|
if cache_dir and (cache_dir.is_relative_to(output_dir) or output_dir.is_relative_to(cache_dir)):
|
|
raise ValueError('engraving cache and generated assets must not overlap')
|
|
progress = getattr(args, 'progress', False)
|
|
started = monotonic()
|
|
|
|
def report(message):
|
|
if progress:
|
|
print(f'Assets [{monotonic() - started:.0f}s]: {message}', flush=True)
|
|
|
|
report('validating MusicXML sources')
|
|
for source in sorted(data_dir.rglob('*.xml')):
|
|
load_musicxml(source)
|
|
output_dir.parent.mkdir(parents=True, exist_ok=True)
|
|
with tempfile.TemporaryDirectory(prefix='.vamp-assets-', dir=output_dir.parent) as temporary:
|
|
staged = Path(temporary) / 'assets'
|
|
staged.mkdir(parents=True, exist_ok=True)
|
|
copy_runtime_fonts(static_assets_dir, staged)
|
|
report('generating performance parts and practice variants')
|
|
performance_count = generate_performance_scores(data_dir / 'songs', staged / 'songs')
|
|
practice_count = generate_practice_scores(data_dir, staged / 'practice')
|
|
validate_generated_tree(staged)
|
|
if args.check:
|
|
validate_expected_performance_summary(data_dir / 'expected' / 'performance_summary.json', staged / 'songs')
|
|
validate_practice_variants(staged / 'practice')
|
|
report('compiling playback')
|
|
compile_tree(staged)
|
|
report('engraving pages')
|
|
engrave_tree(staged, static_assets_dir / 'fonts' / 'petaluma', cache_dir=cache_dir, jobs=getattr(args, 'jobs', 1), progress=progress)
|
|
report('validating score, page, and audio assets')
|
|
validate_generated_tree(staged)
|
|
report('building catalogue database')
|
|
build_catalogue(staged)
|
|
backup = Path(temporary) / 'previous'
|
|
if output_dir.exists():
|
|
output_dir.rename(backup)
|
|
try:
|
|
staged.rename(output_dir)
|
|
except OSError:
|
|
if backup.exists():
|
|
backup.rename(output_dir)
|
|
raise
|
|
report('publishing complete')
|
|
print(f'Generated Product Vamp assets: {performance_count} performance scores, '
|
|
f'{practice_count} practice score variants, {count_json_files(output_dir)} total JSON files.')
|
|
if args.check:
|
|
print('Generator check passed.')
|
|
|
|
def main() -> int:
|
|
generate_assets(parse_args())
|
|
return 0
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|