#!/usr/bin/env python3 import argparse import hashlib from pathlib import Path import platform import subprocess import tempfile import urllib.request VERSION = '4.7.4.260706075' SHA256 = { 'x86_64': '9233ed1b87d3e6b45722278f3c286dcd41e83da778bd0f80a1dd04949696ad93', 'aarch64': '162ae55b317660f196b2e73d566bdb45c1e990ed4cc140709d25d97b9ef366b0', } def install(destination: Path) -> Path: architecture = platform.machine() if architecture not in SHA256: raise RuntimeError('the pinned engraver requires an x86_64 or aarch64 Linux build host') destination = destination.resolve() executable = destination / 'squashfs-root' / 'AppRun' marker = destination / 'sha256' if executable.is_file() and marker.is_file() and marker.read_text().strip() == SHA256[architecture]: return executable if destination.exists() and any(destination.iterdir()): raise RuntimeError(f'{destination} contains a different or incomplete installation; choose an empty directory') destination.mkdir(parents=True, exist_ok=True) name = f'MuseScore-Studio-{VERSION}-{architecture}.AppImage' url = f'https://github.com/musescore/MuseScore/releases/download/v4.7.4/{name}' with tempfile.TemporaryDirectory(prefix='vamp-musescore-download-') as temporary: archive = Path(temporary) / name digest = hashlib.sha256() with urllib.request.urlopen(url, timeout=120) as response, archive.open('wb') as output: while chunk := response.read(1024 * 1024): digest.update(chunk) output.write(chunk) if digest.hexdigest() != SHA256[architecture]: raise RuntimeError('MuseScore AppImage checksum mismatch') archive.chmod(0o755) subprocess.run([str(archive), '--appimage-extract'], cwd=destination, check=True, stdout=subprocess.DEVNULL) if not executable.is_file(): raise RuntimeError('MuseScore AppImage does not contain AppRun') marker.write_text(SHA256[architecture] + '\n') return executable def main() -> None: parser = argparse.ArgumentParser(description='Install the pinned build-only MuseScore engraver without FUSE.') parser.add_argument('destination', type=Path) print(install(parser.parse_args().destination)) if __name__ == '__main__': main()