+1
-58
@@ -1,53 +1,8 @@
|
||||
"""Pure score navigation, cloning and identity operations."""
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import json
|
||||
import pathlib
|
||||
import re
|
||||
|
||||
import generator.engraving.validation
|
||||
import generator.fretboard
|
||||
import generator.model
|
||||
import generator.pico_artwork
|
||||
import generator.performance
|
||||
|
||||
def load_json(path: pathlib.Path) -> dict:
|
||||
document = json.loads(path.read_text(encoding='utf-8'))
|
||||
return validate_json(document, path)
|
||||
|
||||
def validate_json(document: dict, path: pathlib.Path, validated_pages: set[str] | None = None) -> dict:
|
||||
if not isinstance(document, dict):
|
||||
raise ValueError('expected a score object')
|
||||
if 'music' in document:
|
||||
presentation = document.get('presentation', {})
|
||||
if not isinstance(document['music'], dict) or not isinstance(presentation, dict) or not isinstance(presentation.get('pages'), list) or not presentation['pages']:
|
||||
raise ValueError('invalid score bundle; regenerate score assets')
|
||||
if (presentation.get('engine'), presentation.get('width'), presentation.get('height')) != (generator.engraving.validation.ENGINE_NAME, generator.engraving.validation.PAGE_WIDTH, generator.engraving.validation.PAGE_HEIGHT):
|
||||
raise ValueError('unsupported score presentation; regenerate score assets')
|
||||
generator.performance.validate_views(document['music'], presentation['pages'], presentation.get('playback_pages'))
|
||||
if document.get('performance') != generator.performance.timing(document['music']):
|
||||
raise ValueError('invalid performance timing')
|
||||
for page in presentation['pages'] + presentation['playback_pages']:
|
||||
if not isinstance(page, dict) or not isinstance(page.get('path'), str) or not isinstance(page.get('sha256'), str):
|
||||
raise ValueError('invalid score page manifest')
|
||||
name = page['path']
|
||||
relative = pathlib.Path(name)
|
||||
if '\x00' in name or '\\' in name or relative.is_absolute() or any(part in ('', '.', '..') for part in name.split('/')) or relative.suffix != '.png':
|
||||
raise ValueError('invalid score page path')
|
||||
resolved = (path.parent / relative).resolve()
|
||||
if not resolved.is_relative_to(path.parent.resolve()):
|
||||
raise ValueError('score page escapes its asset directory')
|
||||
generator.engraving.validation.check_page(resolved, validated_pages, expected_sha256=page['sha256'])
|
||||
document = document['music']
|
||||
generator.model.validate_document(document)
|
||||
return document
|
||||
|
||||
def write_json(path: pathlib.Path, document: dict) -> None:
|
||||
generator.model.validate_document(document)
|
||||
generator.fretboard.validate_document(document)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(document, indent=2, ensure_ascii=False) + '\n', encoding='utf-8')
|
||||
|
||||
def slugify(value: str) -> str:
|
||||
return re.sub(r'[^a-z0-9]+', '_', value.strip().lower()).strip('_') or 'score'
|
||||
|
||||
@@ -124,15 +79,3 @@ def reidentify(measures: list[dict], source_tuplets: list[dict]) -> list[dict]:
|
||||
|
||||
def make_arranged_document(source_document: dict, staff: dict, *, staff_id: str, staff_name: str, role: str, arrangement_part_type: str, measures: list[dict], tuplets=None, octave_shifts=None) -> dict:
|
||||
return {'metadata': copy.deepcopy(source_document['metadata']), 'staves': [{'id': staff_id, 'name': staff_name, 'clef': copy.deepcopy(staff['clef']), 'role': role, 'arranged': True, 'arrangement_part_type': arrangement_part_type, 'measures': measures, 'tuplets': tuplets or [], 'octave_shifts': copy.deepcopy(octave_shifts or []), 'instruments': copy.deepcopy(staff.get('instruments', []))}]}
|
||||
|
||||
def count_json_files(root: pathlib.Path) -> int:
|
||||
return sum(1 for p in root.rglob('*.json') if p.is_file())
|
||||
|
||||
def validate_generated_tree(root: pathlib.Path, validated_pages: set[str] | None = None) -> None:
|
||||
validated_pages = set() if validated_pages is None else validated_pages
|
||||
for path in root.rglob('*.json'):
|
||||
document = json.loads(path.read_text(encoding='utf-8'))
|
||||
music = validate_json(document, path, validated_pages)
|
||||
generator.fretboard.validate_document(music)
|
||||
if 'presentation' in document:
|
||||
generator.pico_artwork.validate_title(root, document['presentation'].get('title_image'))
|
||||
|
||||
Reference in New Issue
Block a user