This commit is contained in:
Manfred Stock 2023-01-01 00:45:56 +01:00 committed by GitHub
commit 629abfbbee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 31 deletions

View file

@ -56,6 +56,10 @@ parser.add_argument('--resvg', action="store_true", default=False, help='''
Render frames using resvg instead of Inkscape. Render frames using resvg instead of Inkscape.
Usage: ./make.py yourproject/ --resvg Usage: ./make.py yourproject/ --resvg
''') ''')
parser.add_argument('--audio-streams', action="store", default=2, type=int, help='''
Number of audio streams to generate.
Usage: ./make.py yourproject/ --audio-streams 4
''')
if len(sys.argv) < 2: if len(sys.argv) < 2:
parser.print_help() parser.print_help()

View file

@ -11,6 +11,7 @@ from svgtemplate import SVGTemplate
from lxml import etree from lxml import etree
from urllib.request import urlopen from urllib.request import urlopen
from wand.image import Image from wand.image import Image
from tempfile import NamedTemporaryFile
# Frames per second. Increasing this renders more frames, the avconf-statements would still need modifications # Frames per second. Increasing this renders more frames, the avconf-statements would still need modifications
fps = 25 fps = 25
@ -83,12 +84,16 @@ def ensureFilesRemoved(pattern):
for f in glob.glob(pattern): for f in glob.glob(pattern):
os.unlink(f) os.unlink(f)
def renderFrame(infile, task, outfile): def renderFrame(svg, task, outfile):
width = 1920 width = 1920
height = 1080 height = 1080
outfile = os.path.abspath(outfile)
if args.imagemagick: if args.imagemagick:
# invoke imagemagick to convert the generated svg-file into a png inside the .frames-directory # invoke imagemagick to convert the generated svg-file into a png inside the .frames-directory
with Image(filename=infile) as img: with NamedTemporaryFile(dir=task.workdir, suffix='.svg') as svgfile:
svgfile.write(svg.svgstr.encode('utf-8'))
svgfile.flush()
with Image(filename=svgfile.name) as img:
with img.convert('png') as converted: with img.convert('png') as converted:
converted.save(filename=outfile) converted.save(filename=outfile)
elif args.resvg: elif args.resvg:
@ -98,11 +103,10 @@ def renderFrame(infile, task, outfile):
if errorReturn != '': if errorReturn != '':
print("resvg exited with error\n" + errorReturn) print("resvg exited with error\n" + errorReturn)
# sys.exit(42) # sys.exit(42)
else: else:
# invoke inkscape to convert the generated svg-file into a png inside the .frames-directory # invoke inkscape to convert the generated svg-file into a png inside the .frames-directory
cmd = 'inkscape --export-background=white --export-background-opacity=0 --export-width={1} --export-height={2} --export-filename="{3}" "{4}" --pipe 2>&1 >/dev/null'.format(task.workdir, width, height, os.path.abspath(outfile), os.path.abspath(infile)) cmd = 'inkscape --export-background=white --export-background-opacity=0 --export-width={1} --export-height={2} --export-filename="{3}" --pipe 2>&1 >/dev/null'.format(task.workdir, width, height, outfile)
errorReturn = subprocess.check_output(cmd, shell=True, universal_newlines=True, stderr=subprocess.STDOUT, cwd=task.workdir) errorReturn = subprocess.check_output(cmd, shell=True, universal_newlines=True, input=svg.svgstr, stderr=subprocess.STDOUT, cwd=task.workdir)
if errorReturn != '': if errorReturn != '':
print("inkscape exited with error\n" + errorReturn) print("inkscape exited with error\n" + errorReturn)
# sys.exit(42) # sys.exit(42)
@ -135,26 +139,20 @@ def cachedRenderFrame(frame, frameNr, task, cache):
elif not skip_rendering: elif not skip_rendering:
cache[frame] = frameNr cache[frame] = frameNr
svgfile = '{0}/.frames/{1:04d}.svg'.format(task.workdir, frameNr) outfile = '{0}/.frames/{1:04d}.png'.format(task.workdir, frameNr)
with SVGTemplate(task) as svg:
with SVGTemplate(task, svgfile) as svg:
svg.replacetext() svg.replacetext()
svg.transform(frame) svg.transform(frame)
svg.write() renderFrame(svg, task, outfile)
outfile = '{0}/.frames/{1:04d}.png'.format(task.workdir, frameNr)
renderFrame(svgfile, task, outfile)
# increment frame-number # increment frame-number
frameNr += 1 frameNr += 1
def rendertask_image(task): def rendertask_image(task):
svgfile = '{0}/image.svg'.format(task.workdir) with SVGTemplate(task) as svg:
with SVGTemplate(task, svgfile) as svg:
svg.replacetext() svg.replacetext()
svg.write() renderFrame(svg, task, task.outfile)
renderFrame(svgfile, task, task.outfile)
def rendertask_video(task): def rendertask_video(task):
# iterate through the animation sequence frame by frame # iterate through the animation sequence frame by frame
@ -178,16 +176,18 @@ def rendertask_video(task):
cmd = 'cd {0} && '.format(task.workdir) cmd = 'cd {0} && '.format(task.workdir)
cmd += 'ffmpeg -f image2 -i .frames/%04d.png ' cmd += 'ffmpeg -f image2 -i .frames/%04d.png '
if task.audiofile is None: if task.audiofile is None:
cmd += '-ar 48000 -ac 1 -f s16le -i /dev/zero -ar 48000 -ac 1 -f s16le -i /dev/zero ' audio_input = '-ar 48000 -ac 1 -f s16le -i /dev/zero '
else: else:
cmd += '-i {0} -i {0} '.format(task.audiofile) audio_input = '-i {0} '.format(task.audiofile)
cmd += audio_input * args.audio_streams
cmd += '-map 0:0 -c:v mpeg2video -q:v 2 -aspect 16:9 ' cmd += '-map 0:0 -c:v mpeg2video -q:v 2 -aspect 16:9 '
if task.audiofile is None: if task.audiofile is None:
cmd += '-map 1:0 -map 2:0 ' audio_map = '-map {0}:0 '
else: else:
cmd += '-map 1:0 -c:a copy -map 2:0 -c:a copy ' audio_map = '-map {0}:0 -c:a copy '
cmd += ''.join(audio_map.format(index + 1) for index in range(args.audio_streams))
cmd += '-shortest -f mpegts "{0}"'.format(task.outfile) cmd += '-shortest -f mpegts "{0}"'.format(task.outfile)
elif task.outfile.endswith('.mov'): elif task.outfile.endswith('.mov'):
cmd = 'cd {0} && '.format(task.workdir) cmd = 'cd {0} && '.format(task.workdir)

View file

@ -12,21 +12,14 @@ cssutils.ser.prefs.lineSeparator = ' '
cssutils.log.setLevel(logging.FATAL) cssutils.log.setLevel(logging.FATAL)
class SVGTemplate: class SVGTemplate:
def __init__(self, task, outfile): def __init__(self, task):
self.task = task self.task = task
self.outfile = outfile
def __enter__(self): def __enter__(self):
with builtins.open(os.path.join(self.task.workdir, self.task.infile), 'r') as fp: with builtins.open(os.path.join(self.task.workdir, self.task.infile), 'r') as fp:
self.svgstr = fp.read() self.svgstr = fp.read()
return self return self
def write(self):
# open the output-file (named ".gen.svg" in the workdir)
with builtins.open(self.outfile, 'w') as fp:
# write the generated svg-text into the output-file
fp.write(self.svgstr)
def replacetext(self): def replacetext(self):
for key in self.task.parameters.keys(): for key in self.task.parameters.keys():
self.svgstr = self.svgstr.replace(key, xmlescape(str(self.task.parameters[key]))) self.svgstr = self.svgstr.replace(key, xmlescape(str(self.task.parameters[key])))
@ -49,7 +42,7 @@ class SVGTemplate:
# if '$subtitle' in task.parameters and task.parameters['$subtitle'] == '': # if '$subtitle' in task.parameters and task.parameters['$subtitle'] == '':
# child = svg.findall(".//*[@id='subtitle']")[0] # child = svg.findall(".//*[@id='subtitle']")[0]
# child.getparent().remove(child) # child.getparent().remove(child)
self.svgstr = etree.tostring(svg, encoding='unicode') self.svgstr = etree.tostring(svg).decode('UTF-8')
def __exit__(self, exception_type, exception_value, traceback): def __exit__(self, exception_type, exception_value, traceback):
pass pass