#!/usr/bin/env python3 ## Generate pdf form lilypond file ## by using standard command ## ## Most of programming was done by Sven Axelsson, http://svenax.net/ import io, os from argparse import ArgumentParser class MakeDrum: LILYPOND = 'lilypond' VERSION = '0.9.7' MASTER_DIR = os.path.dirname(os.path.abspath(__file__)) RUN_DIR = os.path.abspath(os.curdir) TMP_DIR = 'tmp' TMP_PREFIX = 'tmp_' OUT_DIR = '.' def __init__(self): # Gather options and create the template file usage = __file__ parser = ArgumentParser(usage) parser.add_argument('--version', action='store_true', dest='show_version', default=False, help='show makeDrum version and exit') parser.add_argument('--lilyversion', action='store_true', dest='show_lilyversion', default=False, help='show Lilypond version and exit') parser.add_argument('-l', '--lilycommand', default=self.LILYPOND, dest='lilypond', help='Command used to compile') parser.add_argument('-i', '--include', dest='includes', nargs='*', default=[],action='append', help='Include the specified file for compiling') parser.add_argument('-I', '--path-include', dest='search_paths', nargs='*', default=[], action='append', help='Paths for lilypond to search through while compiling') parser.add_argument('-p', '--paper-size', dest='papersize', default='a4', help='Paper size. Default: A4') parser.add_argument('-o', '--orientation', dest='orientation', default='landscape', help='Paper orientation. Default: landscape') parser.add_argument('-s', '--staff-size', dest='staffsize', default='18', help='Staff size. Default: 18pt.') parser.add_argument('-w', '--view-spacing', action='store_true', dest='view_spacing', default=False, help='Turn on "Paper.annotatespacing".') parser.add_argument('--rename', action='store_true', dest='rename', default=False, help='Rename the final output to it\'s relative location, replacing /\'s with -\'s') parser.add_argument('-r', '--suffix', dest='suffix', default='', help='String added at end of pdf\'s filename') parser.add_argument('-g', '--generated', dest='gen_out', default=self.TMP_DIR, help='Put generated lilyfiles in $gen_out') parser.add_argument('-q', '--self-compilable', default=False, action='store_true', dest='compilable', help='Make a self compilable file') parser.add_argument('--no-compile', default=True, action='store_false', dest='compile', help='Do not compile generated Lilypond files') parser.add_argument('--no-log', action='store_false', dest='log', default=True, help='Do not generate log files.') parser.add_argument('--no-cleanup', action='store_false', dest='clean', default=True, help='Leave all temporary files in place') parser.add_argument('-d', '--out-dir', dest='out_dir', default=self.OUT_DIR, help='Output dir, for lilypond. If it doesn\'t exist, try to create it') parser.add_argument('music_file', default='', nargs='*', help='file to process') parser.add_argument('-@', '--list_file', dest='list_file', default='', help='list of files to process') # All unknown args are passed on to the lilypond command self.args, self.unknownargs = parser.parse_known_args() if self.args.show_version: print(__name__, ' ', self.VERSION) return if self.args.show_lilyversion: print(os.system(self.LILYPOND+' --version')) return if self.args.view_spacing: self.args.view_spacing = "##t" else: self.args.view_spacing = "##f" # Input files if self.args.list_file != '': with io.open(self.args.list_file, 'r', encoding='utf8') as list_file: for line in list_file.readlines(): self.args.music_file.append(line) # Check for files if not self.args.music_file: parser.print_usage() return # Check for files to include self.args.includes = [el for elements in self.args.includes for el in elements] # Clean up of files self.remove_tmp_dir = self.args.clean if not os.path.exists(os.path.join(os.path.curdir, self.TMP_DIR)): try: os.makedirs(os.path.join(os.path.curdir, self.TMP_DIR)) except: print('Seems like no temporary directory can be created') return if not os.path.exists(os.path.join(os.path.curdir, self.args.out_dir)): try: os.makedirs(os.path.join(os.path.curdir, self.args.out_dir)) except: print('Seems like no output directory can be created') return for file_path in self.args.music_file: self.processit(self.TMP_DIR, os.path.join(self.RUN_DIR, file_path), self.args.gen_out, self.args.compile) if not os.listdir(self.TMP_DIR): os.rmdir(self.TMP_DIR) def processit(self, tmp_dir, file, gen_out, compile): print ('Generating for ',file, end=' ', flush=True) tmp_file = self.maketemplate(tmp_dir, file, self.args.compilable) print ('[OK]') if gen_out is not None and gen_out != tmp_dir: new_tmp_file = os.path.basename(tmp_file).replace(self.TMP_PREFIX, ''); print ('Moving ', tmp_file, ' to ', new_tmp_file, end=' ', flush=True) gen_dir = os.path.join(self.RUN_DIR, gen_out); # if not dir $gen_out, make it if not os.path.exists(gen_dir): try: os.makedirs(gen_dir) except: print('[Error]') print(' ! Seems like the {} directory cannot be created'.format(gen_dir)) return # mv file to dir, remove self.TMP_PREFIX os.rename(tmp_file, os.path.join(gen_dir, new_tmp_file)) tmp_file = new_tmp_file print('[OK]') if compile: if self.args.log: logfile = os.path.join(self.TMP_DIR, os.path.relpath(file).replace(".ly", '').replace('/', '-')+'.log') log = ' > '+logfile+' 2>&1' else: log = '' print ('Compiling ', file, end=' ', flush=True) if not self.args.log: print() # stringify search paths #TODO: this is ugly code paths = ' -I '.join(str(x)[1:-1] for x in self.args.search_paths) if paths: paths = ' -I ' + paths if self.args.rename: lilyout = os.path.join(self.RUN_DIR, self.args.out_dir, os.path.basename(tmp_file).replace(self.TMP_PREFIX, '').rsplit( ".", 1 )[ 0 ]) else: lilyout = os.path.basename(file).rsplit( ".", 1 )[ 0 ] unknownargs = " " + " ".join(self.unknownargs) lilycmd = self.LILYPOND + unknownargs + paths + ' --output='+lilyout+' '+tmp_file+log if os.system(lilycmd) != 0: self.remove_tmp_dir = False print ('[Error]') if self.args.log: print (' ! Did not compile, please see the log at ', logfile) else : print ('[OK]') if self.args.clean: #remove files if self.args.log: os.remove(logfile) if not self.args.compilable: os.remove(tmp_file) def maketemplate(self, tmp_dir, file, compilable): lily_includes = '' # set up a tmp file with template and file combined tmp_file = os.path.join(tmp_dir, self.TMP_PREFIX + os.path.relpath(file).replace('../','').replace('music/','',1).replace('/', '-')[:-3] + self.args.suffix + '.ly') # Make the file with io.open(tmp_file, 'w+', encoding='utf8') as out_file: def printline(line, relpath = file): # Check if there's an include in the line, if there is try to copy it all (Recursive) if line.startswith(u'\\include'): # Rewrite includes to absolute location of file incline = line.replace('\\include', '').strip('"\'\n ') printline(u"\n %%%% \"{}\"\n".format(incline)) # Only rewrite if it begins with a dot # if / it's absolute, and something else means search_path if incline.startswith('.'): # incline = os.path.join(os.path.abspath(os.path.dirname(relpath)), incline) if compilable: try: inc_file = io.open(incline,'r',encoding='utf8') except IOError: out_file.write(line + "%% Error to copy %%\n") return with inc_file: for subline in inc_file.readlines(): printline(subline, incline) else: out_file.write(u"\\include \""+incline+"\"\n") return out_file.write(line.replace('\r', '')) # Go do things with it printline(u'\ufeff') printline( u"""% Generated from """+file+""" by """+__file__+""" version """+self.VERSION+""" \\version "2.18.0" #(ly:set-option 'point-and-click #f) %\layout { % \context { % \Score { % \override NonMusicalPaperColumn #'line-break-permission = ##f % } % } %} """) for f in self.args.includes: printline(u"\n") printline(u"\\include \"{}\"".format(f), self.RUN_DIR+'/build') printline(u""" #(set-global-staff-size """+self.args.staffsize+""") #(set-default-paper-size \""""+self.args.papersize+"""\" '"""+self.args.orientation+""") % The tune to generate. """) # Read lily file into tmp file with io.open(file, 'r', encoding='utf8') as in_file: for line in in_file.readlines(): if line.startswith(u'\ufeff'): continue printline(line) # Return tmp_file_path return tmp_file MakeDrum();