diff --git a/make-adobe-after-effects.py b/make-adobe-after-effects.py
index 5e4cbca..e65931d 100755
--- a/make-adobe-after-effects.py
+++ b/make-adobe-after-effects.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
+# vim: tabstop=4 shiftwidth=4 expandtab
import subprocess
import renderlib
@@ -8,227 +9,235 @@ import shlex
import time
import sys
import os
-import re
import platform
-
-from xml.sax.saxutils import escape as xmlescape
from shutil import copyfile
# Parse arguments
parser = argparse.ArgumentParser(
- description='C3VOC Intro-Outro-Generator - Variant to use with Adobe After Effects Files',
- usage="./make-adobe-after-effects.py yourproject/ https://url/to/schedule.xml",
- formatter_class=argparse.RawTextHelpFormatter)
+ description='C3VOC Intro-Outro-Generator - Variant to use with Adobe After Effects Files',
+ usage="./make-adobe-after-effects.py yourproject/ https://url/to/schedule.xml",
+ formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('project', action="store", metavar='Project folder', type=str, help='''
- Path to your project folder with After Effects Files (intro.aep/scpt/jsx)
- ''')
-parser.add_argument('schedule', action="store", metavar='Schedule-URL', type=str, nargs='?', help='''
- URL or Path to your schedule.xml
- ''')
+ Path to your project folder with After Effects Files (intro.aep/scpt/jsx)
+ ''')
+parser.add_argument('schedule', action="store", metavar='Schedule-URL', type=str, nargs='?', help='''
+ URL or Path to your schedule.xml
+ ''')
parser.add_argument('--debug', action="store_true", default=False, help='''
- Run script in debug mode and render with placeholder texts,
- not parsing or accessing a schedule. Schedule-URL can be left blank when
- used with --debug
- This argument must not be used together with --id
- Usage: ./make-adobe-after-effects.py yourproject/ --debug
- ''')
+ Run script in debug mode and render with placeholder texts,
+ not parsing or accessing a schedule. Schedule-URL can be left blank when
+ used with --debug
+ This argument must not be used together with --id
+ Usage: ./make-adobe-after-effects.py yourproject/ --debug
+ ''')
parser.add_argument('--id', dest='ids', nargs='+', action="store", type=int, help='''
- Only render the given ID(s) from your projects schedule.
- This argument must not be used together with --debug
- Usage: ./make-adobe-after-effects.py yourproject/ --id 4711 0815 4223 1337
- ''')
+ Only render the given ID(s) from your projects schedule.
+ This argument must not be used together with --debug
+ Usage: ./make-adobe-after-effects.py yourproject/ --id 4711 0815 4223 1337
+ ''')
parser.add_argument('--room', dest='rooms', nargs='+', action="store", type=str, help='''
- Only render the given room(s) from your projects schedule.
- This argument must not be used together with --debug
- Usage: ./make-adobe-after-effects.py yourproject/ --room "HfG_Studio" "ZKM_Vortragssaal"
- ''')
+ Only render the given room(s) from your projects schedule.
+ This argument must not be used together with --debug
+ Usage: ./make-adobe-after-effects.py yourproject/ --room "HfG_Studio" "ZKM_Vortragssaal"
+ ''')
parser.add_argument('--pause', action="store_true", default=False, help='''
- Render a pause loop from the pause.aep file in the project folder.
- ''')
+ Render a pause loop from the pause.aep file in the project folder.
+ ''')
parser.add_argument('--force', action="store_true", default=False, help='''
- Force render if file exists.
- ''')
+ Force render if file exists.
+ ''')
parser.add_argument('--no-finalize', dest='nof', action="store_true", default=False, help='''
- Skip finalize job.
- ''')
+ Skip finalize job.
+ ''')
parser.add_argument('--outro', action="store_true", default=False, help='''
- Render outro from the outro.aep file in the project folder.
- ''')
+ Render outro from the outro.aep file in the project folder.
+ ''')
parser.add_argument('--bgloop', action="store_true", default=False, help='''
- Render background loop from the bgloop.aep file in the project folder.
- ''')
+ Render background loop from the bgloop.aep file in the project folder.
+ ''')
args = parser.parse_args()
+
def headline(str):
- print("##################################################")
- print(str)
- print("##################################################")
- print()
+ print("##################################################")
+ print(str)
+ print("##################################################")
+ print()
+
def error(str):
- headline(str)
- parser.print_help()
- sys.exit(1)
+ headline(str)
+ parser.print_help()
+ sys.exit(1)
+
if not args.project:
- error("The Path to your project with After Effect Files is a required argument")
+ error("The Path to your project with After Effect Files is a required argument")
if not args.debug and not args.pause and not args.outro and not args.bgloop and not args.schedule:
- error("Either specify --debug, --pause, --outro or supply a schedule")
+ error("Either specify --debug, --pause, --outro or supply a schedule")
if args.debug:
- persons = ['watz']
- events = [{
- 'id': 1,
- 'title': 'Eröffnungsveranstaltung',
- 'subtitle': 'Easterhegg 2018',
- 'persons': persons,
- 'personnames': ', '.join(persons),
- 'room': 'Heisenberg 1',
- }]
+ persons = ['watz']
+ events = [{
+ 'id': 1,
+ 'title': 'Eröffnungsveranstaltung',
+ 'subtitle': 'Easterhegg 2018',
+ 'persons': persons,
+ 'personnames': ', '.join(persons),
+ 'room': 'Heisenberg 1',
+ }]
elif args.pause:
- events = [{
- 'id': 'pause',
- 'title': 'Pause Loop',
- }]
+ events = [{
+ 'id': 'pause',
+ 'title': 'Pause Loop',
+ }]
elif args.outro:
- events = [{
- 'id': 'outro',
- 'title': 'Outro',
- }]
+ events = [{
+ 'id': 'outro',
+ 'title': 'Outro',
+ }]
elif args.bgloop:
- events = [{
- 'id': 'bgloop',
- 'title': 'Background Loop',
- }]
+ events = [{
+ 'id': 'bgloop',
+ 'title': 'Background Loop',
+ }]
else:
- events = list(renderlib.events(args.schedule))
+ events = list(renderlib.events(args.schedule))
+
def describe_event(event):
- return "#{}: {}".format(event['id'], event['title'])
+ return "#{}: {}".format(event['id'], event['title'])
+
def event_print(event, message):
- print("{} – {}".format(describe_event(event), message))
+ print("{} – {}".format(describe_event(event), message))
+
tempdir = tempfile.TemporaryDirectory()
-print('working in '+tempdir.name)
+print('working in ' + tempdir.name)
+
def fmt_command(command, **kwargs):
- args = {}
- for key, value in kwargs.items():
- args[key] = shlex.quote(value)
+ args = {}
+ for key, value in kwargs.items():
+ args[key] = shlex.quote(value)
+
+ command = command.format(**args)
+ return shlex.split(command)
- command = command.format(**args)
- return shlex.split(command)
def run_once(command, **kwargs):
- DETACHED_PROCESS = 0x00000008
- return subprocess.Popen(
- fmt_command(command, **kwargs),
- shell=False,
- stdin=None,
- stdout=None,
- stderr=None,
- close_fds=True,
- creationflags=DETACHED_PROCESS)
+ DETACHED_PROCESS = 0x00000008
+ return subprocess.Popen(
+ fmt_command(command, **kwargs),
+ shell=False,
+ stdin=None,
+ stdout=None,
+ stderr=None,
+ close_fds=True,
+ creationflags=DETACHED_PROCESS)
+
def run(command, **kwargs):
- return subprocess.check_call(
- fmt_command(command, **kwargs),
- stderr=subprocess.STDOUT,
- stdout=subprocess.DEVNULL)
+ return subprocess.check_call(
+ fmt_command(command, **kwargs),
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.DEVNULL)
+
def enqueue_job(event):
- event_id = str(event['id'])
- if os.path.exists(os.path.join(args.project, event_id+'.ts')) or os.path.exists(os.path.join(args.project, event_id+'.mov')) and not args.force:
- event_print(event, "file exist, skipping "+str(event['id']))
- return
- work_doc = os.path.join(tempdir.name, event_id+'.aep')
- script_doc = os.path.join(tempdir.name, event_id+'.jsx')
- ascript_doc = os.path.join(tempdir.name, event_id+'.scpt')
- intermediate_clip = os.path.join(tempdir.name, event_id+'.mov')
+ event_id = str(event['id'])
+ if os.path.exists(os.path.join(args.project, event_id + '.ts')) or os.path.exists(os.path.join(args.project, event_id + '.mov')) and not args.force:
+ event_print(event, "file exist, skipping " + str(event['id']))
+ return
+ work_doc = os.path.join(tempdir.name, event_id + '.aep')
+ script_doc = os.path.join(tempdir.name, event_id + '.jsx')
+ ascript_doc = os.path.join(tempdir.name, event_id + '.scpt')
+ intermediate_clip = os.path.join(tempdir.name, event_id + '.mov')
- if event_id == 'pause' or event_id == 'outro' or event_id == 'bgloop':
- copyfile(args.project+event_id+'.aep',work_doc)
- if platform.system() == 'Darwin':
- run('/Applications/Adobe\ After\ Effects\ CC\ 2018/aerender -project {jobpath} -comp {comp} -output {locationpath}',
- jobpath=work_doc,
- comp=event_id,
- locationpath=intermediate_clip)
+ if event_id == 'pause' or event_id == 'outro' or event_id == 'bgloop':
+ copyfile(args.project + event_id + '.aep', work_doc)
+ if platform.system() == 'Darwin':
+ run(r'/Applications/Adobe\ After\ Effects\ CC\ 2018/aerender -project {jobpath} -comp {comp} -output {locationpath}',
+ jobpath=work_doc,
+ comp=event_id,
+ locationpath=intermediate_clip)
- if platform.system() == 'Windows':
- run('C:/Program\ Files/Adobe/Adobe\ After\ Effects\ CC\ 2018/Support\ Files/aerender.exe -project {jobpath} -comp {comp} -output {locationpath}',
- jobpath=work_doc,
- comp=event_id,
- locationpath=intermediate_clip)
+ if platform.system() == 'Windows':
+ run(r'C:/Program\ Files/Adobe/Adobe\ After\ Effects\ CC\ 2018/Support\ Files/aerender.exe -project {jobpath} -comp {comp} -output {locationpath}',
+ jobpath=work_doc,
+ comp=event_id,
+ locationpath=intermediate_clip)
- else:
- with open(args.project+'intro.jsx', 'r') as fp:
- scriptstr = fp.read()
+ else:
+ with open(args.project + 'intro.jsx', 'r') as fp:
+ scriptstr = fp.read()
- for key, value in event.items():
- value = str(value).replace('"', '\\"')
- scriptstr = scriptstr.replace("$"+str(key), value)
+ for key, value in event.items():
+ value = str(value).replace('"', '\\"')
+ scriptstr = scriptstr.replace("$" + str(key), value)
- with open(script_doc, 'w', encoding='utf-8') as fp:
- fp.write(scriptstr)
+ with open(script_doc, 'w', encoding='utf-8') as fp:
+ fp.write(scriptstr)
- copyfile(args.project+'intro.aep',work_doc)
+ copyfile(args.project + 'intro.aep', work_doc)
- if platform.system() == 'Darwin':
- copyfile(args.project+'intro.scpt',ascript_doc)
- run('osascript {ascript_path} {jobpath} {scriptpath}',
- jobpath=work_doc,
- scriptpath=script_doc,
- ascript_path=ascript_doc)
+ if platform.system() == 'Darwin':
+ copyfile(args.project + 'intro.scpt', ascript_doc)
+ run('osascript {ascript_path} {jobpath} {scriptpath}',
+ jobpath=work_doc,
+ scriptpath=script_doc,
+ ascript_path=ascript_doc)
- run('/Applications/Adobe\ After\ Effects\ CC\ 2018/aerender -project {jobpath} -comp "intro" -output {locationpath}',
- jobpath=work_doc,
- locationpath=intermediate_clip)
+ run(r'/Applications/Adobe\ After\ Effects\ CC\ 2018/aerender -project {jobpath} -comp "intro" -output {locationpath}',
+ jobpath=work_doc,
+ locationpath=intermediate_clip)
- if platform.system() == 'Windows':
- run_once('C:/Program\ Files/Adobe/Adobe\ After\ Effects\ CC\ 2018/Support\ Files/AfterFX.exe -noui {jobpath}',
- jobpath=work_doc)
- time.sleep(15)
+ if platform.system() == 'Windows':
+ run_once(r'C:/Program\ Files/Adobe/Adobe\ After\ Effects\ CC\ 2018/Support\ Files/AfterFX.exe -noui {jobpath}',
+ jobpath=work_doc)
+ time.sleep(15)
- run_once('C:/Program\ Files/Adobe/Adobe\ After\ Effects\ CC\ 2018/Support\ Files/AfterFX.exe -noui -r {scriptpath}',
- scriptpath=script_doc)
- time.sleep(5)
+ run_once(r'C:/Program\ Files/Adobe/Adobe\ After\ Effects\ CC\ 2018/Support\ Files/AfterFX.exe -noui -r {scriptpath}',
+ scriptpath=script_doc)
+ time.sleep(5)
- run('C:/Program\ Files/Adobe/Adobe\ After\ Effects\ CC\ 2018/Support\ Files/aerender.exe -project {jobpath} -comp "intro" -output {locationpath}',
- jobpath=work_doc,
- locationpath=intermediate_clip)
+ run(r'C:/Program\ Files/Adobe/Adobe\ After\ Effects\ CC\ 2018/Support\ Files/aerender.exe -project {jobpath} -comp "intro" -output {locationpath}',
+ jobpath=work_doc,
+ locationpath=intermediate_clip)
+
+ return event_id
- return event_id
def finalize_job(job_id, event):
- event_id = str(event['id'])
- intermediate_clip = os.path.join(tempdir.name, event_id+'.mov')
- final_clip = os.path.join(os.path.dirname(args.project), event_id+'.ts')
+ event_id = str(event['id'])
+ intermediate_clip = os.path.join(tempdir.name, event_id + '.mov')
+ final_clip = os.path.join(os.path.dirname(args.project), event_id + '.ts')
- run('ffmpeg -y -hide_banner -loglevel error -i {input} -f lavfi -i anullsrc -ar 48000 -ac 2 -map 0:v -c:v mpeg2video -q:v 0 -aspect 16:9 -map 1:a -map 1:a -map 1:a -map 1:a -shortest -f mpegts {output}',
- #run('ffmpeg -y -hide_banner -loglevel error -i "{input}" -ar 48000 -ac 1 -map 0:v -c:v mpeg2video -q:v 0 -aspect 16:9 -map 1:0 -c:a copy -map 2:0 -c:a copy -shortest -f mpegts "{output}"',
- input=intermediate_clip,
- output=final_clip)
+ run('ffmpeg -y -hide_banner -loglevel error -i {input} -f lavfi -i anullsrc -ar 48000 -ac 2 -map 0:v -c:v mpeg2video -q:v 0 -aspect 16:9 -map 1:a -map 1:a -map 1:a -map 1:a -shortest -f mpegts {output}',
+ # run('ffmpeg -y -hide_banner -loglevel error -i "{input}" -ar 48000 -ac 1 -map 0:v -c:v mpeg2video -q:v 0 -aspect 16:9 -map 1:0 -c:a copy -map 2:0 -c:a copy -shortest -f mpegts "{output}"',
+ input=intermediate_clip,
+ output=final_clip)
- if event_id == 'pause' or event_id == 'outro' or event_id == 'bgloop':
- event_print(event, "finalized "+str(event_id)+" to "+final_clip)
- else:
- event_print(event, "finalized intro to "+final_clip)
+ if event_id == 'pause' or event_id == 'outro' or event_id == 'bgloop':
+ event_print(event, "finalized " + str(event_id) + " to " + final_clip)
+ else:
+ event_print(event, "finalized intro to " + final_clip)
if args.ids:
@@ -243,34 +252,34 @@ else:
print("enqueuing {} jobs into aerender".format(len(events)))
for event in events:
- if args.ids and event['id'] not in args.ids:
- continue
+ if args.ids and event['id'] not in args.ids:
+ continue
- if args.rooms and event['room'] not in args.rooms:
- print("skipping room %s (%s)" % (event['room'], event['title']))
- continue
+ if args.rooms and event['room'] not in args.rooms:
+ print("skipping room %s (%s)" % (event['room'], event['title']))
+ continue
- event_print(event, "enqueued as "+str(event['id']))
+ event_print(event, "enqueued as " + str(event['id']))
- job_id = enqueue_job(event)
- if not job_id:
- event_print(event, "job was not enqueued successfully, skipping postprocessing")
- continue
+ job_id = enqueue_job(event)
+ if not job_id:
+ event_print(event, "job was not enqueued successfully, skipping postprocessing")
+ continue
- if not args.nof:
- event_print(event, "finalizing job")
- finalize_job(job_id, event)
+ if not args.nof:
+ event_print(event, "finalizing job")
+ finalize_job(job_id, event)
+ else:
+ event_id = str(event['id'])
+ event_print(event, "skipping finalizing job")
+ if platform.system() == 'Windows':
+ intermediate_clip = os.path.join(tempdir.name, event_id + '.avi')
+ final_clip = os.path.join(os.path.dirname(args.project), event_id + '.avi')
else:
- event_id = str(event['id'])
- event_print(event, "skipping finalizing job")
- if platform.system() == 'Windows':
- intermediate_clip = os.path.join(tempdir.name, event_id+'.avi')
- final_clip = os.path.join(os.path.dirname(args.project), event_id+'.avi')
- else:
- intermediate_clip = os.path.join(tempdir.name, event_id+'.mov')
- final_clip = os.path.join(os.path.dirname(args.project), event_id+'.mov')
- copyfile(intermediate_clip, final_clip)
- event_print(event, "copied intermediate clip to "+final_clip)
+ intermediate_clip = os.path.join(tempdir.name, event_id + '.mov')
+ final_clip = os.path.join(os.path.dirname(args.project), event_id + '.mov')
+ copyfile(intermediate_clip, final_clip)
+ event_print(event, "copied intermediate clip to " + final_clip)
-print('all done, cleaning up '+tempdir.name)
+print('all done, cleaning up ' + tempdir.name)
tempdir.cleanup()
diff --git a/make-apple-motion.py b/make-apple-motion.py
index 8098809..3513619 100755
--- a/make-apple-motion.py
+++ b/make-apple-motion.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python3
+# vim: tabstop=4 shiftwidth=4 expandtab
import subprocess
import renderlib
@@ -15,179 +16,185 @@ from xml.sax.saxutils import escape as xmlescape
# Parse arguments
parser = argparse.ArgumentParser(
- description='C3VOC Intro-Outro-Generator - Variant to use with apple Motion Files',
- usage="./make.py gpn17/Intro.motn https://url/to/schedule.xml",
- formatter_class=argparse.RawTextHelpFormatter)
+ description='C3VOC Intro-Outro-Generator - Variant to use with apple Motion Files',
+ usage="./make.py gpn17/Intro.motn https://url/to/schedule.xml",
+ formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('motn', action="store", metavar='Motion-File', type=str, help='''
- Path to your Motion-File .motn-File
- ''')
-parser.add_argument('schedule', action="store", metavar='Schedule-URL', type=str, nargs='?', help='''
- URL or Path to your schedule.xml
- ''')
+ Path to your Motion-File .motn-File
+ ''')
+parser.add_argument('schedule', action="store", metavar='Schedule-URL', type=str, nargs='?', help='''
+ URL or Path to your schedule.xml
+ ''')
parser.add_argument('--debug', action="store_true", default=False, help='''
- Run script in debug mode and render with placeholder texts,
- not parsing or accessing a schedule. Schedule-URL can be left blank when
- used with --debug
- This argument must not be used together with --id
- Usage: ./make.py yourproject/ --debug
- ''')
+ Run script in debug mode and render with placeholder texts,
+ not parsing or accessing a schedule. Schedule-URL can be left blank when
+ used with --debug
+ This argument must not be used together with --id
+ Usage: ./make.py yourproject/ --debug
+ ''')
parser.add_argument('--id', dest='ids', nargs='+', action="store", type=int, help='''
- Only render the given ID(s) from your projects schedule.
- This argument must not be used together with --debug
- Usage: ./make.py yourproject/ --id 4711 0815 4223 1337
- ''')
+ Only render the given ID(s) from your projects schedule.
+ This argument must not be used together with --debug
+ Usage: ./make.py yourproject/ --id 4711 0815 4223 1337
+ ''')
parser.add_argument('--exclude-id', dest='exclude_ids', nargs='+', action="store", type=int, help='''
- Do not render the given ID(s) from your projects schedule.
- Usage: ./make.py yourproject/ --exclude-id 1 8 15 16 23 42
- ''')
+ Do not render the given ID(s) from your projects schedule.
+ Usage: ./make.py yourproject/ --exclude-id 1 8 15 16 23 42
+ ''')
args = parser.parse_args()
+
def headline(str):
- print("##################################################")
- print(str)
- print("##################################################")
- print()
+ print("##################################################")
+ print(str)
+ print("##################################################")
+ print()
+
def error(str):
- headline(str)
- parser.print_help()
- sys.exit(1)
+ headline(str)
+ parser.print_help()
+ sys.exit(1)
+
if not args.motn:
- error("The Motion-File is a rquired argument")
+ error("The Motion-File is a rquired argument")
if not args.debug and not args.schedule:
- error("Either specify --debug or supply a schedule")
+ error("Either specify --debug or supply a schedule")
if args.debug:
- persons = ['Arnulf Christl', 'Astrid Emde', 'Dominik Helle', 'Till Adams']
- events = [{
- 'id': 3773,
- 'title': 'Was ist Open Source, wie funktioniert das?',
- 'subtitle': 'Die Organisation der Open Geo- und GIS-Welt. Worauf man achten sollte.',
- 'persons': persons,
- 'personnames': ', '.join(persons),
- 'room': 'Großer Saal',
- }]
+ persons = ['Arnulf Christl', 'Astrid Emde', 'Dominik Helle', 'Till Adams']
+ events = [{
+ 'id': 3773,
+ 'title': 'Was ist Open Source, wie funktioniert das?',
+ 'subtitle': 'Die Organisation der Open Geo- und GIS-Welt. Worauf man achten sollte.',
+ 'persons': persons,
+ 'personnames': ', '.join(persons),
+ 'room': 'Großer Saal',
+ }]
else:
- events = list(renderlib.events(args.schedule))
+ events = list(renderlib.events(args.schedule))
+
def describe_event(event):
- return "#{}: {}".format(event['id'], event['title'])
+ return "#{}: {}".format(event['id'], event['title'])
+
def event_print(event, message):
- print("{} – {}".format(describe_event(event), message))
+ print("{} – {}".format(describe_event(event), message))
+
tempdir = tempfile.TemporaryDirectory()
-print('working in '+tempdir.name)
+print('working in ' + tempdir.name)
def fmt_command(command, **kwargs):
- args = {}
- for key, value in kwargs.items():
- args[key] = shlex.quote(value)
+ args = {}
+ for key, value in kwargs.items():
+ args[key] = shlex.quote(value)
+
+ command = command.format(**args)
+ return shlex.split(command)
- command = command.format(**args)
- return shlex.split(command)
def run(command, **kwargs):
- return subprocess.check_call(
- fmt_command(command, **kwargs))
+ return subprocess.check_call(
+ fmt_command(command, **kwargs))
+
def run_output(command, **kwargs):
- return subprocess.check_output(
- fmt_command(command, **kwargs),
- encoding='utf-8',
- stderr=subprocess.STDOUT)
+ return subprocess.check_output(
+ fmt_command(command, **kwargs),
+ encoding='utf-8',
+ stderr=subprocess.STDOUT)
def enqueue_job(event):
- event_id = str(event['id'])
- work_doc = os.path.join(tempdir.name, event_id+'.motn')
- intermediate_clip = os.path.join(tempdir.name, event_id+'.mov')
+ event_id = str(event['id'])
+ work_doc = os.path.join(tempdir.name, event_id + '.motn')
+ intermediate_clip = os.path.join(tempdir.name, event_id + '.mov')
- with open(args.motn, 'r') as fp:
- xmlstr = fp.read()
+ with open(args.motn, 'r') as fp:
+ xmlstr = fp.read()
- for key, value in event.items():
- xmlstr = xmlstr.replace("$"+str(key), xmlescape(str(value)))
+ for key, value in event.items():
+ xmlstr = xmlstr.replace("$" + str(key), xmlescape(str(value)))
- with open(work_doc, 'w') as fp:
- fp.write(xmlstr)
+ with open(work_doc, 'w') as fp:
+ fp.write(xmlstr)
- compressor_info = run_output(
- '/Applications/Compressor.app/Contents/MacOS/Compressor -batchname {batchname} -jobpath {jobpath} -settingpath apple-prores-4444.cmprstng -locationpath {locationpath}',
- batchname=describe_event(event),
- jobpath=work_doc,
- locationpath=intermediate_clip)
+ compressor_info = run_output(
+ '/Applications/Compressor.app/Contents/MacOS/Compressor -batchname {batchname} -jobpath {jobpath} -settingpath apple-prores-4444.cmprstng -locationpath {locationpath}',
+ batchname=describe_event(event),
+ jobpath=work_doc,
+ locationpath=intermediate_clip)
- match = re.search("", compressor_info)
- if not match:
- event_print(event, "unexpected output from compressor: \n"+compressor_info)
- return
+ match = re.search(r"", compressor_info)
+ if not match:
+ event_print(event, "unexpected output from compressor: \n" + compressor_info)
+ return
+
+ return match.group(1)
- return match.group(1)
def fetch_job_status():
- compressor_status = run_output('/Applications/Compressor.app/Contents/MacOS/Compressor -monitor')
- job_status_matches = re.finditer("", compressor_status)
+ compressor_status = run_output('/Applications/Compressor.app/Contents/MacOS/Compressor -monitor')
+ job_status_matches = re.finditer(r"", compressor_status)
- status_dict = {}
- for match in job_status_matches:
- lexer = shlex.shlex(match.group(1), posix=True)
- lexer.wordchars += "="
-
- job_status = dict(word.split("=", maxsplit=1) for word in lexer)
- job_id = job_status['jobid']
- status_dict[job_id] = job_status
-
- return status_dict
+ status_dict = {}
+ for match in job_status_matches:
+ lexer = shlex.shlex(match.group(1), posix=True)
+ lexer.wordchars += "="
+ job_status = dict(word.split("=", maxsplit=1) for word in lexer)
+ job_id = job_status['jobid']
+ status_dict[job_id] = job_status
+ return status_dict
def filter_finished_jobs(active_jobs):
- job_status = fetch_job_status()
+ job_status = fetch_job_status()
- new_active_jobs = []
- finished_jobs = []
- for job_id, event in active_jobs:
- if job_id not in job_status:
- status = 'Processing'
- else:
- status = job_status[job_id]['status']
+ new_active_jobs = []
+ finished_jobs = []
+ for job_id, event in active_jobs:
+ if job_id not in job_status:
+ status = 'Processing'
+ else:
+ status = job_status[job_id]['status']
- if status == 'Processing':
- new_active_jobs.append((job_id, event))
- continue
- elif status == 'Successful':
- finished_jobs.append((job_id, event))
- else:
- event_print(event, "failed with staus="+status+" – removing from postprocessing queue")
+ if status == 'Processing':
+ new_active_jobs.append((job_id, event))
+ continue
+ elif status == 'Successful':
+ finished_jobs.append((job_id, event))
+ else:
+ event_print(event, "failed with staus=" + status + " – removing from postprocessing queue")
- return new_active_jobs, finished_jobs
+ return new_active_jobs, finished_jobs
def finalize_job(job_id, event):
- event_id = str(event['id'])
- intermediate_clip = os.path.join(tempdir.name, event_id+'.mov')
- final_clip = os.path.join(os.path.dirname(args.motn), event_id+'.ts')
- copy_clip = os.path.join(os.path.dirname(args.motn), event_id+'.mov')
+ event_id = str(event['id'])
+ intermediate_clip = os.path.join(tempdir.name, event_id + '.mov')
+ final_clip = os.path.join(os.path.dirname(args.motn), event_id + '.ts')
+ copy_clip = os.path.join(os.path.dirname(args.motn), event_id + '.mov')
- shutil.copy(intermediate_clip, copy_clip)
+ shutil.copy(intermediate_clip, copy_clip)
- run('ffmpeg -y -hide_banner -loglevel error -i "{input}" -map 0:v -c:v mpeg2video -q:v 0 -aspect 16:9 -map 0:a -map 0:a -map 0:a -map 0:a -shortest -f mpegts "{output}"',
- input=intermediate_clip,
- output=final_clip)
-
- event_print(event, "finalized intro to "+final_clip)
+ run('ffmpeg -y -hide_banner -loglevel error -i {input} -f lavfi -i anullsrc -ar 48000 -ac 2 -map 0:v -c:v mpeg2video -q:v 0 -aspect 16:9 -map 1:a -map 1:a -map 1:a -map 1:a -shortest -f mpegts {output}',
+ input=intermediate_clip,
+ output=final_clip)
+ event_print(event, "finalized intro to " + final_clip)
active_jobs = []
@@ -199,25 +206,25 @@ filtered_events = list(filtered_events)
print("enqueuing {} jobs into compressor".format(len(filtered_events)))
for event in filtered_events:
- job_id = enqueue_job(event)
- if not job_id:
- event_print(event, "job was not enqueued successfully, skipping postprocessing")
- continue
+ job_id = enqueue_job(event)
+ if not job_id:
+ event_print(event, "job was not enqueued successfully, skipping postprocessing")
+ continue
- event_print(event, "enqueued as "+job_id)
- active_jobs.append((job_id, event))
+ event_print(event, "enqueued as " + job_id)
+ active_jobs.append((job_id, event))
print("waiting for rendering to complete")
while len(active_jobs) > 0:
- time.sleep(15)
- active_jobs, finished_jobs = filter_finished_jobs(active_jobs)
+ time.sleep(15)
+ active_jobs, finished_jobs = filter_finished_jobs(active_jobs)
- print("{} jobs in queue, {} ready to finalize".format(len(active_jobs), len(finished_jobs)))
- for job_id, event in finished_jobs:
- event_print(event, "finalizing job")
- finalize_job(job_id, event)
+ print("{} jobs in queue, {} ready to finalize".format(len(active_jobs), len(finished_jobs)))
+ for job_id, event in finished_jobs:
+ event_print(event, "finalizing job")
+ finalize_job(job_id, event)
-print('all done, cleaning up '+tempdir.name)
+print('all done, cleaning up ' + tempdir.name)
tempdir.cleanup()
diff --git a/make.py b/make.py
index 367a0e6..fca6911 100755
--- a/make.py
+++ b/make.py
@@ -1,10 +1,10 @@
#!/usr/bin/env python3
+# vim: tabstop=4 shiftwidth=4 expandtab
import sys
import os
import time
import shutil
-from lxml import etree
import tempfile
import threading
import multiprocessing
@@ -19,103 +19,103 @@ parser.add_argument('projectpath', action="store", metavar='yourproject/', type=
Path to your project is a required argument.
Usage: ./make.py yourproject/
Without any further argument(s) given, your whole project will be rendered.
- ''')
+ ''')
parser.add_argument('--debug', action="store_true", default=False, help='''
Run script in debug mode and just render the debug values
given in your projects __init.py__
This argument must not be used together with --id
Usage: ./make.py yourproject/ --debug
- ''')
+ ''')
parser.add_argument('--only-frame', action="store", default=None, type=int, help='''
Only render the given frames (of the intro), e.g. to quickly render snapshots of the tiles frame.
Usage: ./make.py yourproject/ --debug --only-frame 300
./make.py yourproject/ --only-frame 300
- ''')
+ ''')
parser.add_argument('--id', nargs='+', action="store", type=int, help='''
Only render the given ID(s) from your projects schedule.
This argument must not be used together with --debug
Usage: ./make.py yourproject/ --id 4711 0815 4223 1337
To skip all IDs (just generate intro/outro/background files) use it with --id 000000
- ''')
+ ''')
parser.add_argument('--skip', nargs='+', action="store", type=str, help='''
Skip outro, pause and/or background files in rendering if not needed.
This argument must not be used together with --debug
Usage: ./make.py yourproject/ --skip pause out bg
Example - only generate outro: ./make.py yourproject/ --skip pause bg
Example - only generate pause and background: ./make.py yourproject/ --skip out
- ''')
+ ''')
parser.add_argument('--skip-frames', action="store", default=None, type=int, help='''
Skip first n frames e.g. to quickly rerender during debugging.
Usage: ./make.py yourproject/ --debug --skip-frames 300
- ''')
+ ''')
if len(sys.argv) < 2:
- parser.print_help()
- sys.exit(1)
+ parser.print_help()
+ sys.exit(1)
args = parser.parse_args()
-if not (args.debug==False or args.id==None):
- print("##################################################")
- print("Error! You must not use --debug and --id together!")
- print("##################################################")
- parser.print_help()
- sys.exit(1)
+if not (args.debug is False or args.id is None):
+ print("##################################################")
+ print("Error! You must not use --debug and --id together!")
+ print("##################################################")
+ parser.print_help()
+ sys.exit(1)
-if not (args.debug==False or args.skip==None):
- print("####################################################")
- print("Error! You must not use --debug and --skip together!")
- print("####################################################")
- parser.print_help()
- sys.exit(1)
+if not (args.debug is False or args.skip is None):
+ print("####################################################")
+ print("Error! You must not use --debug and --skip together!")
+ print("####################################################")
+ parser.print_help()
+ sys.exit(1)
print(args)
# Set values from argparse
-projectname=args.projectpath.strip('/')
-projectpath=args.projectpath
+projectname = args.projectpath.strip('/')
+projectpath = args.projectpath
# Check if project exists
try:
- project = renderlib.loadProject(projectname)
+ project = renderlib.loadProject(projectname)
except ImportError:
- print("you must specify a project-name as first argument, eg. './make.py sotmeu14'. The supplied value '{0}' seems not to be a valid project (there is no '{0}/__init__.py').\n".format(projectname))
- raise
+ print("you must specify a project-name as first argument, eg. './make.py sotmeu14'. The supplied value '{0}' seems not to be a valid project (there is no '{0}/__init__.py').\n".format(projectname))
+ raise
# using --debug skips the threading, the network fetching of the schedule and
# just renders one type of video
renderlib.debug = args.debug
renderlib.args = args
-#sys.exit(1)
+# sys.exit(1)
+
def render(infile, outfile, sequence, parameters={}, workdir=os.path.join(projectname, 'artwork')):
- task = renderlib.Rendertask(infile=infile, outfile=outfile, sequence=sequence, parameters=parameters, workdir=workdir)
- return renderlib.rendertask(task)
+ task = renderlib.Rendertask(infile=infile, outfile=outfile, sequence=sequence, parameters=parameters, workdir=workdir)
+ return renderlib.rendertask(task)
+
# debug-mode selected by --debug switch
if renderlib.debug:
- print("!!! DEBUG MODE !!!")
+ print("!!! DEBUG MODE !!!")
- # expose debug-render method
- project.render = render
-
- # call into project which calls render as needed
- project.debug()
-
- # exit early
- sys.exit(0)
+ # expose debug-render method
+ project.render = render
+ # call into project which calls render as needed
+ project.debug()
+ # exit early
+ sys.exit(0)
# threaded task queue
tasks = Queue()
-#initialize args.id and args.skip, if they are not given by the user
-if (args.id==None):
- args.id = []
+# initialize args.id and args.skip, if they are not given by the user
+if (args.id is None):
+ args.id = []
-if (args.skip==None):
- args.skip = []
+if (args.skip is None):
+ args.skip = []
# call into project which generates the tasks
project.tasks(tasks, projectpath, args.id, args.skip)
@@ -126,90 +126,93 @@ print("{0} tasks in queue, starting {1} worker threads".format(tasks.qsize(), nu
# put a sentinel for each thread into the queue to signal the end
for _ in range(num_worker_threads):
- tasks.put(None)
+ tasks.put(None)
# this lock ensures, that only one thread at a time is writing to stdout
# and avoids output from multiple threads intermixing
printLock = Lock()
+
+
def tprint(str):
- # aquire lock
- printLock.acquire()
+ # aquire lock
+ printLock.acquire()
- # print thread-name and message
- print(threading.current_thread().name+': '+str)
+ # print thread-name and message
+ print(threading.current_thread().name + ': ' + str)
- # release lock
- printLock.release()
+ # release lock
+ printLock.release()
# thread worker
def worker():
- # generate a tempdir for this worker-thread and use the artwork-subdir as temporary folder
- tempdir = tempfile.mkdtemp()
- workdir = os.path.join(tempdir, 'artwork')
+ # generate a tempdir for this worker-thread and use the artwork-subdir as temporary folder
+ tempdir = tempfile.mkdtemp()
+ workdir = os.path.join(tempdir, 'artwork')
- # save the current working dir as output-dir
- outdir = os.path.join(os.getcwd(), projectname)
+ # save the current working dir as output-dir
+ outdir = os.path.join(os.getcwd(), projectname)
- # print a message that we're about to initialize our environment
- tprint("initializing worker in {0}, writing result to {1}".format(tempdir, outdir))
+ # print a message that we're about to initialize our environment
+ tprint("initializing worker in {0}, writing result to {1}".format(tempdir, outdir))
- # copy the artwork-dir into the tempdir
- shutil.copytree(os.path.join(projectname, 'artwork'), workdir)
+ # copy the artwork-dir into the tempdir
+ shutil.copytree(os.path.join(projectname, 'artwork'), workdir)
- # loop until all tasks are done (when the thread fetches a sentinal from the queue)
- while True:
- # fetch a task from the queue
- task = renderlib.Rendertask.ensure(tasks.get())
+ # loop until all tasks are done (when the thread fetches a sentinal from the queue)
+ while True:
+ # fetch a task from the queue
+ task = renderlib.Rendertask.ensure(tasks.get())
- # if it is a stop-sentinal break out of the loop
- if task == None:
- break
+ # if it is a stop-sentinal break out of the loop
+ if task is None:
+ break
- # print that we're about to render a task
- tprint('rendering {0} from {1}'.format(task.outfile, task.infile))
+ # print that we're about to render a task
+ tprint('rendering {0} from {1}'.format(task.outfile, task.infile))
- # prepend workdir to input file
- task.infile = os.path.join(workdir, task.infile)
- task.outfile = os.path.join(outdir, task.outfile)
- task.workdir = workdir
+ # prepend workdir to input file
+ task.infile = os.path.join(workdir, task.infile)
+ task.outfile = os.path.join(outdir, task.outfile)
+ task.workdir = workdir
- # render with these arguments
- renderlib.rendertask(task)
+ # render with these arguments
+ renderlib.rendertask(task)
- # print that we're finished
- tprint('finished {0}, {1} tasks left'.format(task.outfile, max(0, tasks.qsize() - num_worker_threads)))
+ # print that we're finished
+ tprint('finished {0}, {1} tasks left'.format(task.outfile, max(0, tasks.qsize() - num_worker_threads)))
- # mark the task as finished
- tasks.task_done()
+ # mark the task as finished
+ tasks.task_done()
- # all tasks from the queue done, clean up
- tprint("cleaning up worker")
+ # all tasks from the queue done, clean up
+ tprint("cleaning up worker")
- # remove the tempdir
- shutil.rmtree(tempdir)
+ # remove the tempdir
+ shutil.rmtree(tempdir)
+
+ # mark the sentinal as done
+ tasks.task_done()
- # mark the sentinal as done
- tasks.task_done()
# List of running threads
threads = []
# generate and start the threads
for i in range(num_worker_threads):
- t = Thread(target=worker)
- t.daemon = True
- t.start()
- threads.append(t)
+ t = Thread(target=worker)
+ t.daemon = True
+ t.start()
+ threads.append(t)
# wait until they finished doing the work
# we're doing it the manual way because tasks.join() would wait until all tasks are done,
# even if the worker threads crash due to broken svgs, Ctrl-C termination or whatnot
while True:
- if tasks.empty() == True:
- break
+ if tasks.empty() is True:
+ break
- # sleep while the workers work
- time.sleep(1)
+ # sleep while the workers work
+ time.sleep(1)
print("all worker threads ended")
diff --git a/renderlib.py b/renderlib.py
index 1b42979..a4e9fda 100644
--- a/renderlib.py
+++ b/renderlib.py
@@ -1,10 +1,10 @@
#!/usr/bin/python3
+# vim: tabstop=4 shiftwidth=4 expandtab
import os
import sys
import re
import glob
-import math
import shutil
import errno
from lxml import etree
@@ -22,263 +22,267 @@ args = None
cssutils.ser.prefs.lineSeparator = ' '
cssutils.log.setLevel(logging.FATAL)
+
def loadProject(projectname):
- sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), projectname))
- return __import__(projectname)
+ sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), projectname))
+ return __import__(projectname)
+
def easeDelay(easer, delay, t, b, c, d, *args):
- if t < delay:
- return b
+ if t < delay:
+ return b
- if t - delay > d:
- return b+c
+ if t - delay > d:
+ return b + c
+
+ return easer(t - delay, b, c, d, *args)
- return easer(t - delay, b, c, d, *args)
class Rendertask:
- def __init__(self, infile, sequence, parameters={}, outfile=None, workdir='.'):
- if isinstance(infile, list):
- self.infile = infile[0]
- #self.audiofile = infile[1]
- else:
- self.infile = infile
- self.audiofile = None
- self.sequence = sequence
- self.parameters = parameters
- self.outfile = outfile
- self.workdir = workdir
+ def __init__(self, infile, sequence, parameters={}, outfile=None, workdir='.'):
+ if isinstance(infile, list):
+ self.infile = infile[0]
+ # self.audiofile = infile[1]
+ else:
+ self.infile = infile
+ self.audiofile = None
+ self.sequence = sequence
+ self.parameters = parameters
+ self.outfile = outfile
+ self.workdir = workdir
- def fromtupel(tuple):
- return Rendertask(tuple[0], tuple[2], tuple[3], tuple[1])
+ def fromtupel(tuple):
+ return Rendertask(tuple[0], tuple[2], tuple[3], tuple[1])
+
+ def ensure(input):
+ if isinstance(input, tuple):
+ return Rendertask.fromtupel(input)
+ elif isinstance(input, Rendertask):
+ return input
+ else:
+ return None
- def ensure(input):
- if isinstance(input, tuple):
- return Rendertask.fromtupel(input)
- elif isinstance(input, Rendertask):
- return input
- else:
- return None
# try to create all folders needed and skip, they already exist
def ensurePathExists(path):
- try:
- os.makedirs(path)
- except OSError as exception:
- if exception.errno != errno.EEXIST:
- raise
+ try:
+ os.makedirs(path)
+ except OSError as exception:
+ if exception.errno != errno.EEXIST:
+ raise
+
# remove the files matched by the pattern
def ensureFilesRemoved(pattern):
- for f in glob.glob(pattern):
- os.unlink(f)
+ for f in glob.glob(pattern):
+ os.unlink(f)
+
def rendertask(task):
- global args
- # in debug mode we have no thread-worker which prints its progress
- if debug:
- print("generating {0} from {1}".format(task.outfile, task.infile))
+ global args
+ # in debug mode we have no thread-worker which prints its progress
+ if debug:
+ print("generating {0} from {1}".format(task.outfile, task.infile))
- if not args.skip_frames and not 'only_rerender_frames_after' in task.parameters:
+ if args.skip_frames and 'only_rerender_frames_after' not in task.parameters:
if os.path.isdir(os.path.join(task.workdir, '.frames')):
shutil.rmtree(os.path.join(task.workdir, '.frames'))
- # make sure a .frames-directory exists in out workdir
- ensurePathExists(os.path.join(task.workdir, '.frames'))
+ # make sure a .frames-directory exists in out workdir
+ ensurePathExists(os.path.join(task.workdir, '.frames'))
- # open and parse the input file
- with open(os.path.join(task.workdir, task.infile), 'r') as fp:
- svgstr = fp.read()
- for key in task.parameters.keys():
- svgstr = svgstr.replace(key, xmlescape(str(task.parameters[key])))
+ # open and parse the input file
+ with open(os.path.join(task.workdir, task.infile), 'r') as fp:
+ svgstr = fp.read()
+ for key in task.parameters.keys():
+ svgstr = svgstr.replace(key, xmlescape(str(task.parameters[key])))
- parser = etree.XMLParser(huge_tree=True)
- svg = etree.fromstring(svgstr.encode('utf-8'), parser)
+ parser = etree.XMLParser(huge_tree=True)
+ svg = etree.fromstring(svgstr.encode('utf-8'), parser)
- #if '$subtitle' in task.parameters and task.parameters['$subtitle'] == '':
- # child = svg.findall(".//*[@id='subtitle']")[0]
- # child.getparent().remove(child)
+ # if '$subtitle' in task.parameters and task.parameters['$subtitle'] == '':
+ # child = svg.findall(".//*[@id='subtitle']")[0]
+ # child.getparent().remove(child)
- # frame-number counter
- frameNr = 0
+ # frame-number counter
+ frameNr = 0
- # iterate through the animation seqence frame by frame
- # frame is a ... tbd
- cache = {}
- for frame in task.sequence(task.parameters):
- skip_rendering = False
- # skip first n frames, to speed up rerendering during debugging
- if 'only_rerender_frames_after' in task.parameters:
- skip_rendering = (frameNr <= task.parameters['only_rerender_frames_after'])
+ # iterate through the animation seqence frame by frame
+ # frame is a ... tbd
+ cache = {}
+ for frame in task.sequence(task.parameters):
+ skip_rendering = False
+ # skip first n frames, to speed up rerendering during debugging
+ if 'only_rerender_frames_after' in task.parameters:
+ skip_rendering = (frameNr <= task.parameters['only_rerender_frames_after'])
- if args.skip_frames:
- skip_rendering = (frameNr <= args.skip_frames)
+ if args.skip_frames:
+ skip_rendering = (frameNr <= args.skip_frames)
- if args.only_frame:
- skip_rendering = (frameNr != args.only_frame)
+ if args.only_frame:
+ skip_rendering = (frameNr != args.only_frame)
- # print a line for each and every frame generated
- if debug and not skip_rendering:
- print("frameNr {0:3d} => {1}".format(frameNr, frame))
+ # print a line for each and every frame generated
+ if debug and not skip_rendering:
+ print("frameNr {0:3d} => {1}".format(frameNr, frame))
- frame = tuple(frame)
- if frame in cache:
- if debug:
- print("cache hit, reusing frame {0}".format(cache[frame]))
+ frame = tuple(frame)
+ if frame in cache:
+ if debug:
+ print("cache hit, reusing frame {0}".format(cache[frame]))
- framedir = task.workdir + "/.frames/"
- shutil.copyfile("{0}/{1:04d}.png".format(framedir, cache[frame]), "{0}/{1:04d}.png".format(framedir, frameNr))
+ framedir = task.workdir + "/.frames/"
+ shutil.copyfile("{0}/{1:04d}.png".format(framedir, cache[frame]), "{0}/{1:04d}.png".format(framedir, frameNr))
- frameNr += 1
- continue
- elif not skip_rendering:
- cache[frame] = frameNr
+ frameNr += 1
+ continue
+ elif not skip_rendering:
+ cache[frame] = frameNr
- # apply the replace-pairs to the input text, by finding the specified xml-elements by thier id and modify thier css-parameter the correct value
- for replaceinfo in frame:
- (id, type, key, value) = replaceinfo
+ # apply the replace-pairs to the input text, by finding the specified xml-elements by thier id and modify thier css-parameter the correct value
+ for replaceinfo in frame:
+ (id, type, key, value) = replaceinfo
- for el in svg.findall(".//*[@id='"+id.replace("'", "\\'")+"']"):
- if type == 'style':
- style = cssutils.parseStyle( el.attrib['style'] if 'style' in el.attrib else '' )
- style[key] = str(value)
- el.attrib['style'] = style.cssText
+ for el in svg.findall(".//*[@id='" + id.replace("'", "\\'") + "']"):
+ if type == 'style':
+ style = cssutils.parseStyle(el.attrib['style'] if 'style' in el.attrib else '')
+ style[key] = str(value)
+ el.attrib['style'] = style.cssText
- elif type == 'attr':
- el.attrib[key] = str(value)
+ elif type == 'attr':
+ el.attrib[key] = str(value)
- elif type == 'text':
- el.text = str(value)
+ elif type == 'text':
+ el.text = str(value)
- if not skip_rendering:
- # open the output-file (named ".gen.svg" in the workdir)
- with open(os.path.join(task.workdir, '.gen.svg'), 'w') as fp:
- # write the generated svg-text into the output-file
- fp.write( etree.tostring(svg, encoding='unicode') )
+ if not skip_rendering:
+ # open the output-file (named ".gen.svg" in the workdir)
+ with open(os.path.join(task.workdir, '.gen.svg'), 'w') as fp:
+ # write the generated svg-text into the output-file
+ fp.write(etree.tostring(svg, encoding='unicode'))
- if task.outfile.endswith('.ts') or task.outfile.endswith('.mov'):
- width = 1920
- height = 1080
- else:
- width = 1024
- height = 576
+ if task.outfile.endswith('.ts') or task.outfile.endswith('.mov'):
+ width = 1920
+ height = 1080
+ else:
+ width = 1024
+ height = 576
- # invoke inkscape to convert the generated svg-file into a png inside the .frames-directory
- cmd = 'cd {0} && inkscape --export-background=white --export-background-opacity=0 --export-width={2} --export-height={3} --export-png=$(pwd)/.frames/{1:04d}.png $(pwd)/.gen.svg 2>&1 >/dev/null'.format(task.workdir, frameNr, width, height)
- errorReturn = subprocess.check_output(cmd, shell=True, universal_newlines=True, stderr=subprocess.STDOUT)
- if errorReturn != '':
- print("inkscape exitted with error\n"+errorReturn)
- #sys.exit(42)
+ # invoke inkscape to convert the generated svg-file into a png inside the .frames-directory
+ cmd = 'cd {0} && inkscape --export-background=white --export-background-opacity=0 --export-width={2} --export-height={3} --export-png=$(pwd)/.frames/{1:04d}.png $(pwd)/.gen.svg 2>&1 >/dev/null'.format(task.workdir, frameNr, width, height)
+ errorReturn = subprocess.check_output(cmd, shell=True, universal_newlines=True, stderr=subprocess.STDOUT)
+ if errorReturn != '':
+ print("inkscape exitted with error\n" + errorReturn)
+ # sys.exit(42)
- # increment frame-number
- frameNr += 1
+ # increment frame-number
+ frameNr += 1
+ if args.only_frame:
+ task.outfile = '{0}.frame{1:04d}.png'.format(task.outfile, args.only_frame)
+ # remove the dv/ts we are about to (re-)generate
+ ensureFilesRemoved(os.path.join(task.workdir, task.outfile))
- if args.only_frame:
- task.outfile = '{0}.frame{1:04d}.png'.format(task.outfile, args.only_frame)
+ if task.outfile.endswith('.png'):
+ cmd = 'cd {0} && cp ".frames/{1:04d}.png" "{2}"'.format(task.workdir, args.only_frame, task.outfile)
+ # invoke avconv aka ffmpeg and renerate a lossles-dv from the frames
+ # if we're not in debug-mode, suppress all output
+ elif task.outfile.endswith('.ts'):
+ cmd = 'cd {0} && '.format(task.workdir)
+ cmd += 'ffmpeg -f image2 -i .frames/%04d.png '
+ if task.audiofile is None:
+ cmd += '-ar 48000 -ac 1 -f s16le -i /dev/zero -ar 48000 -ac 1 -f s16le -i /dev/zero '
+ else:
+ cmd += '-i {0} -i {0} '.format(task.audiofile)
- # remove the dv/ts we are about to (re-)generate
- ensureFilesRemoved(os.path.join(task.workdir, task.outfile))
+ cmd += '-map 0:0 -c:v mpeg2video -q:v 2 -aspect 16:9 '
- if task.outfile.endswith('.png'):
- cmd = 'cd {0} && cp ".frames/{1:04d}.png" "{2}"'.format(task.workdir, args.only_frame, task.outfile)
+ if task.audiofile is None:
+ cmd += '-map 1:0 -map 2:0 '
+ else:
+ cmd += '-map 1:0 -c:a copy -map 2:0 -c:a copy '
+ cmd += '-shortest -f mpegts "{0}"'.format(task.outfile)
+ elif task.outfile.endswith('.mov'):
+ cmd = 'cd {0} && '.format(task.workdir)
+ cmd += 'ffmpeg -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -f image2 -i .frames/%04d.png -r 25 -shortest -c:v qtrle -f mov "{0}"'.format(task.outfile)
+ else:
+ cmd = 'cd {0} && ffmpeg -ar 48000 -ac 2 -f s16le -i /dev/zero -f image2 -i .frames/%04d.png -target pal-dv -aspect 16:9 -shortest "{1}"'.format(task.workdir, task.outfile)
- # invoke avconv aka ffmpeg and renerate a lossles-dv from the frames
- # if we're not in debug-mode, suppress all output
- elif task.outfile.endswith('.ts'):
- cmd = 'cd {0} && '.format(task.workdir)
- cmd += 'ffmpeg -f image2 -i .frames/%04d.png '
- if task.audiofile is None:
- cmd += '-ar 48000 -ac 1 -f s16le -i /dev/zero -ar 48000 -ac 1 -f s16le -i /dev/zero '
- else:
- cmd += '-i {0} -i {0} '.format(task.audiofile)
+ if debug:
+ print(cmd)
- cmd += '-map 0:0 -c:v mpeg2video -q:v 2 -aspect 16:9 '
+ r = os.system(cmd + ('' if debug else '>/dev/null 2>&1'))
- if task.audiofile is None:
- cmd += '-map 1:0 -map 2:0 '
- else:
- cmd += '-map 1:0 -c:a copy -map 2:0 -c:a copy '
- cmd += '-shortest -f mpegts "{0}"'.format(task.outfile)
- elif task.outfile.endswith('.mov'):
- cmd = 'cd {0} && '.format(task.workdir)
- cmd += 'ffmpeg -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -f image2 -i .frames/%04d.png -r 25 -shortest -c:v qtrle -f mov "{0}"'.format(task.outfile)
- else:
- cmd = 'cd {0} && ffmpeg -ar 48000 -ac 2 -f s16le -i /dev/zero -f image2 -i .frames/%04d.png -target pal-dv -aspect 16:9 -shortest "{1}"'.format(task.workdir, task.outfile)
+ # as before, in non-debug-mode the thread-worker does all progress messages
+ if debug:
+ if r != 0:
+ sys.exit()
- if debug:
- print(cmd)
-
- r = os.system(cmd + ('' if debug else '>/dev/null 2>&1'))
-
- # as before, in non-debug-mode the thread-worker does all progress messages
- if debug:
- if r != 0:
- sys.exit()
-
- if not debug:
- print("cleanup")
-
-
- # remove the generated svg
- ensureFilesRemoved(os.path.join(task.workdir, '.gen.svg'))
+ if not debug:
+ print("cleanup")
+ # remove the generated svg
+ ensureFilesRemoved(os.path.join(task.workdir, '.gen.svg'))
# Download the Events-Schedule and parse all Events out of it. Yield a tupel for each Event
+
+
def events(scheduleUrl, titlemap={}):
- print("downloading schedule")
+ print("downloading schedule")
- # download the schedule
- response = urlopen(scheduleUrl)
+ # download the schedule
+ response = urlopen(scheduleUrl)
- # read xml-source
- xml = response.read()
+ # read xml-source
+ xml = response.read()
- # parse into ElementTree
- parser = etree.XMLParser(huge_tree=True)
- schedule = etree.fromstring(xml, parser)
+ # parse into ElementTree
+ parser = etree.XMLParser(huge_tree=True)
+ schedule = etree.fromstring(xml, parser)
- # iterate all days
- for day in schedule.iter('day'):
- # iterate all rooms
- for room in day.iter('room'):
- # iterate events on that day in this room
- for event in room.iter('event'):
- # aggregate names of the persons holding this talk
- personnames = []
- if event.find('persons') is not None:
- for person in event.find('persons').iter('person'):
- personname = re.sub( '\s+', ' ', person.text ).strip()
- personnames.append(personname)
+ # iterate all days
+ for day in schedule.iter('day'):
+ # iterate all rooms
+ for room in day.iter('room'):
+ # iterate events on that day in this room
+ for event in room.iter('event'):
+ # aggregate names of the persons holding this talk
+ personnames = []
+ if event.find('persons') is not None:
+ for person in event.find('persons').iter('person'):
+ personname = re.sub(r'\s+', ' ', person.text).strip()
+ personnames.append(personname)
- id = int(event.get('id'))
+ id = int(event.get('id'))
- if id in titlemap:
- title = titlemap[id]
- elif event.find('title') is not None and event.find('title').text is not None:
- title = re.sub( '\s+', ' ', event.find('title').text ).strip()
- else:
- title = ''
+ if id in titlemap:
+ title = titlemap[id]
+ elif event.find('title') is not None and event.find('title').text is not None:
+ title = re.sub(r'\s+', ' ', event.find('title').text).strip()
+ else:
+ title = ''
- if event.find('subtitle') is not None and event.find('subtitle').text is not None:
- subtitle = re.sub( '\s+', ' ', event.find('subtitle').text ).strip()
- else:
- subtitle = ''
+ if event.find('subtitle') is not None and event.find('subtitle').text is not None:
+ subtitle = re.sub(r'\s+', ' ', event.find('subtitle').text).strip()
+ else:
+ subtitle = ''
+
+ # yield a tupel with the event-id, event-title and person-names
+ yield {
+ 'id': id,
+ 'title': title,
+ 'subtitle': subtitle,
+ 'persons': personnames,
+ 'personnames': ', '.join(personnames),
+ 'room': room.attrib['name'],
+ 'track': event.find('track').text
+ }
- # yield a tupel with the event-id, event-title and person-names
- yield {
- 'id': id,
- 'title': title,
- 'subtitle': subtitle,
- 'persons': personnames,
- 'personnames': ', '.join(personnames),
- 'room': room.attrib['name'],
- 'track' : event.find('track').text
- }
try:
- from termcolor import colored
+ from termcolor import colored
except ImportError:
- def colored(str, col):
- return str
+ def colored(str, col):
+ return str