Source code for music21.musicxml.archiveTools

# -----------------------------------------------------------------------------
# Name:         musicxml/archiveTools.py
# Purpose:      Tools for compressing and decompressing MusicXML files
#
# Authors:      Christopher Ariza
#               Michael Scott Asato Cuthbert
#
# Copyright:    Copyright © 2009, 2017-2024 Michael Scott Asato Cuthbert
# License:      BSD, see license.txt
# -----------------------------------------------------------------------------
'''
Tools for compressing and decompressing musicxml files.
'''
from __future__ import annotations

import os
import pathlib
import zipfile

from music21 import common
from music21 import environment

environLocal = environment.Environment('musicxml.archiveTools')


# -----------------------------------------------------------------------------
# compression


[docs] def compressAllXMLFiles(*, deleteOriginal=False): ''' Takes all filenames in corpus.paths and runs :meth:`music21.musicxml.archiveTools.compressXML` on each. If the musicXML files are compressed, the originals are deleted from the system. ''' from music21.corpus.corpora import CoreCorpus environLocal.warn('Compressing musicXML files...') # this gets all .xml, .musicxml, .mxl etc. for filename in CoreCorpus().getPaths(fileExtensions=('.xml',)): compressXML(filename, deleteOriginal=deleteOriginal) environLocal.warn( 'Compression complete. ' 'Run the main test suite, fix bugs if necessary,' 'and then commit modified directories in corpus.' )
[docs] def compressXML(filename: str|pathlib.Path, *, deleteOriginal=False, silent=False, strictMxlCheck=True) -> bool: ''' Takes a filename, and if the filename corresponds to a musicXML file with an .musicxml or .xml extension, creates a corresponding compressed .mxl file in the same directory. If deleteOriginal is set to True, the original musicXML file is deleted from the system. If strictMxlCheck is False then any suffix will do. Returns bool if successful. ''' filename = str(filename) if strictMxlCheck and not filename.endswith('.xml') and not filename.endswith('.musicxml'): return False # not a musicXML file fp = common.pathTools.cleanpath(filename, returnPathlib=True) if not silent: # pragma: no cover environLocal.warn(f'Updating file: {fp}') newFilename = str(fp.with_suffix('.mxl')) # contents of container.xml file in META-INF folder container = f'''<?xml version="1.0" encoding="UTF-8"?> <container> <rootfiles> <rootfile full-path="{fp.name}"/> </rootfiles> </container> ''' # Export container and original xml file to system as a compressed XML. with zipfile.ZipFile( newFilename, 'w', compression=zipfile.ZIP_DEFLATED, ) as myZip: myZip.write(filename, fp.name) myZip.writestr( 'META-INF' + os.path.sep + 'container.xml', container, ) # Delete uncompressed xml file from system if deleteOriginal: fp.unlink() return True
[docs] def uncompressMXL(filename: str|pathlib.Path, *, deleteOriginal=False, silent=False, strictMxlCheck=True) -> bool: ''' Takes a filename, and if the filename corresponds to a compressed musicXML file with an .mxl extension, creates a corresponding uncompressed .musicxml file in the same directory. If deleteOriginal is set to True, the original compressed musicXML file is deleted from the system. If strictMxlCheck is False then any type of file will attempt to be extracted. Returns bool if successful. ''' filename = str(filename) if not filename.endswith('.mxl') and strictMxlCheck: return False # not a compressed musicXML file fp: pathlib.Path = common.pathTools.cleanpath(filename, returnPathlib=True) if not silent: # pragma: no cover environLocal.warn(f'Updating file: {fp}') extractPath = str(fp.parent) unarchivedName = fp.with_suffix('.musicxml').name # Export container and original xml file to system as a compressed XML. with zipfile.ZipFile(filename, 'r', compression=zipfile.ZIP_DEFLATED) as myZip: try: myZip.extract(member=unarchivedName, path=extractPath) except KeyError: try: unarchivedName = unarchivedName.replace('.xml', '.musicxml') myZip.extract(member=unarchivedName, path=extractPath) except KeyError: found_one_file = False for storedName in myZip.namelist(): if 'META-INF' in storedName: continue myZip.extract(member=storedName, path=extractPath) if not found_one_file: # only rename one file: hope it is the right one. extractPath_pathlib = pathlib.Path(extractPath) wrongName = extractPath_pathlib / storedName correctName = extractPath_pathlib / unarchivedName wrongName.rename(correctName) found_one_file = True # Delete uncompressed xml file from system if deleteOriginal: fp.unlink() return True
if __name__ == '__main__': import sys if len(sys.argv) >= 1: for xmlName in sys.argv[1:]: if xmlName.endswith('.xml'): compressXML(xmlName) elif xmlName.endswith('.musicxml'): compressXML(xmlName) elif xmlName.endswith('.mxl'): uncompressMXL(xmlName)