3737from collections import namedtuple
3838from distutils .version import StrictVersion
3939from argparse import ArgumentParser
40+ from configparser import ConfigParser
4041from struct import pack
4142from fractions import Fraction
4243from io import BytesIO
@@ -119,18 +120,41 @@ def getAbsolutePitch(self):
119120 return pitch , token
120121
121122
122- def preprocessLyFile (lyFile , lilypondVersion ):
123+ # TODO:
124+ # On Wine, convert-ly can't be found and yet this function still returns 0.
125+ # convert-ly can be run on wine as "python /path/to/convert-ly.py".
126+ #
127+ # if convert-ly is run on windows:
128+ # warning: cannot find file: `C:\users\me\Temp\ly2video4gs3sqqy\converted.ly'
129+ # fatal error: failed files: "C:\\users\\me\\Temp\\ly2video4gs3sqqy\\converted.ly"
130+ #
131+ # Could this be related to deleting the tmp directory from shutil.rmtree that
132+ # raises an exception?
133+ def runConvertLy (lilypond , lyFile , newLyFile ):
134+ if sys .platform .startswith ("win" ):
135+ # rc = os.system("python %sconvert-ly.py '%s' >> '%s'"
136+ # % (lilypond, lyFile, newLyFile))
137+ print ("not running convert-ly.py on windows..." );
138+ rc = 1 # for now error out so that converted.ly is created by copy
139+ else :
140+ rc = os .system ("convert-ly '%s' >> '%s'" % (lyFile , newLyFile ))
141+
142+ if rc :
143+ warn ("Convert of input file has failed. " +
144+ "This could cause some problems." )
145+
146+ return rc
147+
148+ def preprocessLyFile (lilypond , lyFile , lilypondVersion ):
123149 version = getLyVersion (lyFile )
124150 progress ("Version in %s: %s" %
125151 (lyFile , version if version else "unspecified" ))
126152 if version and version != lilypondVersion :
127153 progress ("Will convert to: %s" % lilypondVersion )
128154 newLyFile = tmpPath ('converted.ly' )
129- if os .system ("convert-ly '%s' >> '%s'" % (lyFile , newLyFile )) == 0 :
155+ if runConvertLy (lilypond , lyFile , newLyFile ) == 0 :
156+ print ('convertly success' )
130157 return newLyFile
131- else :
132- warn ("Convert of input file has failed. " +
133- "This could cause some problems." )
134158
135159 newLyFile = tmpPath ('unconverted.ly' )
136160
@@ -748,7 +772,7 @@ def getNoteIndices(leftmostGrobsByMoment,
748772 return alignedNoteIndices
749773
750774
751- def genWavFile (timidity , midiPath ):
775+ def genWavFile (midiPath ):
752776 """
753777 Call TiMidity++ to convert MIDI to .wav.
754778 It has a weird problem where it converts any '.' into '_'
@@ -758,7 +782,7 @@ def genWavFile(timidity, midiPath):
758782 progress ("Running TiMidity++ on %s to generate .wav audio ..." % midiPath )
759783 dirname , midiFile = os .path .split (midiPath )
760784 os .chdir (dirname )
761- cmd = [timidity , midiFile , "-Ow" ]
785+ cmd = [" timidity" , midiFile , "-Ow" ]
762786 progress (safeRun (cmd , exitcode = 11 ))
763787 wavExpected = midiPath .replace ('.midi' , '.wav' )
764788 if not os .path .exists (wavExpected ):
@@ -921,14 +945,19 @@ def parseOptions():
921945 group_os = parser .add_argument_group (title = 'External programs' )
922946
923947 group_os .add_argument (
924- "--windows-ffmpeg" , dest = "winFfmpeg" ,
925- help = '(for Windows users) folder with ffpeg.exe '
948+ "--lilypond" , dest = "lilypond" ,
949+ help = 'folder with lilypond executable '
950+ '(e.g. "C:\\ lilypond\\ bin\\ ")' ,
951+ metavar = "PATH" , default = "" )
952+ group_os .add_argument (
953+ "--ffmpeg" , dest = "ffmpeg" ,
954+ help = 'folder with ffmpeg executable '
926955 '(e.g. "C:\\ ffmpeg\\ bin\\ ")' ,
927956 metavar = "PATH" , default = "" )
928957 group_os .add_argument (
929- "--windows- timidity" , dest = "winTimidity " ,
930- help = '(for Windows users) folder with '
931- 'timidity.exe (e.g. "C:\\ timidity\\ ")' ,
958+ "--timidity" , dest = "timidity " ,
959+ help = 'folder with timidity executable '
960+ ' (e.g. "C:\\ timidity\\ ")' ,
932961 metavar = "PATH" , default = "" )
933962
934963 group_debug = parser .add_argument_group (title = 'Debug' )
@@ -952,6 +981,15 @@ def parseOptions():
952981
953982 options = parser .parse_args ()
954983
984+ # Check for options in config file. Don't apply them if cli arguments
985+ # were supplied.
986+ config = ConfigParser ()
987+ if config .read ('ly2video.cfg' ):
988+ section = config ['External programs' ]
989+ options .lilypond = options .lilypond or section .get ('lilypond' )
990+ options .ffmpeg = options .ffmpeg or section .get ('ffmpeg' )
991+ options .timidity = options .timidity or section .get ('timidity' )
992+
955993 if options .showVersion :
956994 showVersion ()
957995
@@ -1015,7 +1053,7 @@ def safeRun(cmd, errormsg=None, exitcode=None, shell=False, issues=[], preproces
10151053 debug ("Running: %s\n " % quotedCmd )
10161054
10171055 try :
1018- stdout = subprocess .check_output (cmd , shell = shell )
1056+ stdout = subprocess .check_output (cmd , shell = shell , env = os . environ )
10191057 except KeyboardInterrupt :
10201058 fatal ("Interrupted via keyboard; aborting." )
10211059 except :
@@ -1085,7 +1123,8 @@ def safeRunInput(cmd, inputs, errormsg=None, exitcode=None, issues=[], preproces
10851123
10861124
10871125def findExecutableDependencies (options ):
1088- stdout = safeRun (["lilypond" , "-v" ], "LilyPond was not found." , 1 )
1126+ stdout = safeRun (["lilypond" , "-v" ],
1127+ "LilyPond was not found (maybe use --lilypond?)." , 1 )
10891128 progress ("LilyPond was found." )
10901129 m = re .search ('\AGNU LilyPond (\d[\d.]+\d)' , stdout )
10911130 if not m :
@@ -1103,19 +1142,17 @@ def findExecutableDependencies(options):
11031142
11041143 redirectToNull = " >%s" % portableDevNull ()
11051144
1106- ffmpeg = options .winFfmpeg + "ffmpeg"
1107- if os .system (ffmpeg + " -version" + redirectToNull ) != 0 :
1108- fatal ("FFmpeg was not found (maybe use --windows-ffmpeg?)." , 2 )
1145+ stdout = safeRun (["ffmpeg" , "-version" , redirectToNull ],
1146+ "FFmpeg was not found (maybe use --ffmpeg?)." , 2 )
11091147 progress ("FFmpeg was found." )
11101148
1111- timidity = options .winTimidity + "timidity"
1112- if os .system (timidity + " -v" + redirectToNull ) != 0 :
1113- fatal ("TiMidity++ was not found (maybe use --windows-timidity?)." , 3 )
1149+ stdout = safeRun (["timidity" , "-v" , redirectToNull ],
1150+ "Timidity was not found (maybe use --timidity?)." , 3 )
11141151 progress ("TiMidity++ was found." )
11151152
11161153 output_divider_line ()
11171154
1118- return version , ffmpeg , timidity
1155+ return version
11191156
11201157
11211158def getCursorLineColor (options ):
@@ -1157,11 +1194,11 @@ def imageToBytes(image):
11571194 return f .getvalue ()
11581195
11591196
1160- def generateNotesVideo (ffmpeg , fps , quality , frames , wavPath ):
1197+ def generateNotesVideo (fps , quality , frames , wavPath ):
11611198 progress ("Generating video with animated notation\n " )
11621199 notesPath = tmpPath ("notes.mpg" )
11631200 cmd = [
1164- ffmpeg ,
1201+ " ffmpeg" ,
11651202 "-nostdin" ,
11661203 "-f" , "image2pipe" ,
11671204 "-r" , str (fps ),
@@ -1176,15 +1213,15 @@ def generateNotesVideo(ffmpeg, fps, quality, frames, wavPath):
11761213 return notesPath
11771214
11781215
1179- def generateSilentVideo (ffmpeg , fps , quality , desiredDuration , name , srcFrame ):
1216+ def generateSilentVideo (fps , quality , desiredDuration , name , srcFrame ):
11801217 out = tmpPath ('%s.mpg' % name )
11811218 frames = int (desiredDuration * fps )
11821219 trueDuration = float (frames ) / fps
11831220 progress ("Generating silent video %s, duration %fs\n " %
11841221 (out , trueDuration ))
11851222 silentAudio = generateSilence (name , trueDuration )
11861223 cmd = [
1187- ffmpeg ,
1224+ " ffmpeg" ,
11881225 "-nostdin" ,
11891226 "-f" , "image2pipe" ,
11901227 "-r" , str (fps ),
@@ -1199,22 +1236,22 @@ def generateSilentVideo(ffmpeg, fps, quality, desiredDuration, name, srcFrame):
11991236 return out
12001237
12011238
1202- def generateVideo (ffmpeg , options , wavPath , titleText , frameWriter , outputFile ):
1239+ def generateVideo (options , wavPath , titleText , frameWriter , outputFile ):
12031240 fps = float (options .fps )
12041241 quality = str (options .quality )
12051242
1206- videos = [generateNotesVideo (ffmpeg , fps , quality , frameWriter .frames , wavPath )]
1243+ videos = [generateNotesVideo (fps , quality , frameWriter .frames , wavPath )]
12071244
12081245 initialPadding , finalPadding = options .padding .split ("," )
12091246
12101247 if float (initialPadding ) > 0 :
1211- video = generateSilentVideo (ffmpeg , fps , quality ,
1248+ video = generateSilentVideo (fps , quality ,
12121249 float (initialPadding ), 'initial-padding' ,
12131250 frameWriter .firstFrame )
12141251 videos .insert (0 , video )
12151252
12161253 if float (finalPadding ) > 0 :
1217- video = generateSilentVideo (ffmpeg , fps , quality ,
1254+ video = generateSilentVideo (fps , quality ,
12181255 float (finalPadding ), 'final-padding' ,
12191256 frameWriter .lastFrame )
12201257 videos .append (video )
@@ -1225,7 +1262,7 @@ def generateVideo(ffmpeg, options, wavPath, titleText, frameWriter, outputFile):
12251262 options .titleTtfFile )
12261263 output_divider_line ()
12271264
1228- video = generateSilentVideo (ffmpeg , fps , quality ,
1265+ video = generateSilentVideo (fps , quality ,
12291266 float (options .titleDuration ),
12301267 'title' , titleFrame )
12311268 videos .insert (0 , video )
@@ -1242,7 +1279,7 @@ def generateVideo(ffmpeg, options, wavPath, titleText, frameWriter, outputFile):
12421279 #
12431280 # See: http://stackoverflow.com/questions/7333232/concatenate-two-mp4-files-using-ffmpeg
12441281 cmd = [
1245- ffmpeg ,
1282+ " ffmpeg" ,
12461283 "-nostdin" ,
12471284 "-i" , "concat:%s" % "|" .join (videos ),
12481285 "-codec" , "copy" ,
@@ -1538,25 +1575,27 @@ def main():
15381575 """
15391576 options = parseOptions ()
15401577
1541- lilypondVersion , ffmpeg , timidity = findExecutableDependencies (options )
1578+ # old behaviour was to use path+executable, new behaviour is to modify
1579+ # the path variable for subproceses. I don't think there's any harm
1580+ # in empty path separators.
1581+ os .environ ["PATH" ] += os .pathsep + options .lilypond
1582+ os .environ ["PATH" ] += os .pathsep + options .ffmpeg
1583+ os .environ ["PATH" ] += os .pathsep + options .timidity
1584+
1585+ lilypondVersion = findExecutableDependencies (options )
15421586
15431587 # FIXME. Ugh, eventually this will be an instance method, and
15441588 # we'll have somewhere nice to save state.
15451589 global runDir
15461590 runDir = os .getcwd ()
1547- setRunDir (runDir )
1548-
1549- # Delete old temporary files.
1550- if os .path .isdir (tmpPath ()):
1551- shutil .rmtree (tmpPath ())
1552- os .mkdir (tmpPath ())
1591+ setRunDir (runDir ) # runDir is needed for lilypond includes
15531592
15541593 # .ly input file from user (string)
15551594 lyFile = options .input
15561595
15571596 # If the input .ly doesn't match the currently installed LilyPond
15581597 # version, try to convert it
1559- lyFile = preprocessLyFile (lyFile , lilypondVersion )
1598+ lyFile = preprocessLyFile (options . lilypond , lyFile , lilypondVersion )
15601599
15611600 # https://pillow.readthedocs.io/en/5.1.x/releasenotes/5.0.0.html#decompression-bombs-now-raise-exceptions
15621601 Image .MAX_IMAGE_PIXELS = None
@@ -1634,20 +1673,29 @@ def main():
16341673 # frameWriter.write()
16351674 output_divider_line ()
16361675
1637- wavPath = genWavFile (timidity , midiPath )
1676+ wavPath = genWavFile (midiPath )
16381677
16391678 output_divider_line ()
16401679
16411680 outputFile = getOutputFile (options )
16421681 # finalFrame = "notes/frame%d.png" % (frameWriter.frameNum - 1)
1643- generateVideo (ffmpeg , options , wavPath , titleText , frameWriter , outputFile )
1682+ generateVideo (options , wavPath , titleText , frameWriter , outputFile )
16441683
16451684 output_divider_line ()
16461685
16471686 if options .keepTempFiles :
16481687 progress ("Left temporary files in %s" % tmpPath ())
16491688 else :
1650- shutil .rmtree (tmpPath ())
1689+ try :
1690+ # On Wine, rmtree raises an exception.
1691+ shutil .rmtree (tmpPath ())
1692+ except :
1693+ progress ("\n Failed to remove temporary directory."
1694+ " Please remove manually.\n " )
1695+ if sys .platform .startswith ("win" ):
1696+ progress (" Try: rmdir %s\n " % tmpPath ())
1697+ else :
1698+ progress (" Try: rm -r %s\n " % tmpPath ())
16511699
16521700 # end
16531701 progress ("Ly2video has ended. Your generated file: " + outputFile + "." )
0 commit comments