Skip to content

Commit 76d78d4

Browse files
committed
Windows improvements
* pass locations of external dependencies through arguments * use ly2video.cfg to bring in windows paths for lilypond, ffmpeg, and timidity. Could probably be used on other OSs too. * ly2video.cfg works on Windows in Wine, not tested on other OSs. * works properly on linux if ly2video.cfg is not defined * don't run convert.ly on windows. The process fails for an unknown reason. * tmpPath does not store the path from the last time ly2video was run. Since the temporary directory name is random, there's no point in deleting 'old temporary files'. * tmpPath was using RUNDIR for some strange reason. Though at execution time, it's "" which leaves just the temporary directory and the desired subdirectory name to join. * detect exception and print a message to the user. Not sure if this is only a wine problem. Will need someone to test on Windows. * Added some details regarding convert-ly.py on Windows
1 parent 41364ad commit 76d78d4

File tree

2 files changed

+91
-45
lines changed

2 files changed

+91
-45
lines changed

ly2video/cli.py

Lines changed: 90 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from collections import namedtuple
3838
from distutils.version import StrictVersion
3939
from argparse import ArgumentParser
40+
from configparser import ConfigParser
4041
from struct import pack
4142
from fractions import Fraction
4243
from 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

10871125
def 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

11211158
def 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("\nFailed 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 + ".")

ly2video/utils.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,7 @@ def tmpPath(*dirs):
9595
if not TMPDIR:
9696
TMPDIR = tempfile.mkdtemp(prefix='ly2video')
9797

98-
segments = [ TMPDIR ]
99-
segments.extend(dirs)
100-
return os.path.join(RUNDIR, *segments)
98+
return os.path.join(TMPDIR, *dirs)
10199

102100

103101
class Observable:

0 commit comments

Comments
 (0)