diff --git a/make-adobe-after-effects.py b/make-adobe-after-effects.py index dcda85c..65eae76 100755 --- a/make-adobe-after-effects.py +++ b/make-adobe-after-effects.py @@ -12,84 +12,171 @@ import os import platform from shutil import copyfile -titlemap = ( - {'id': "000000", 'title': "Short title goes here"}, -) +titlemap = ({"id": "000000", "title": "Short title goes here"},) # Parse arguments parser = argparse.ArgumentParser( - description='C3VOC Intro-Outro-Generator - Variant to use with Adobe After Effects Files', + 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 filename_of_intro.aepx", - formatter_class=argparse.RawTextHelpFormatter) + formatter_class=argparse.RawTextHelpFormatter, +) -parser.add_argument('project', action="store", metavar='Project folder', type=str, help=''' +parser.add_argument( + "project", + action="store", + metavar="Project folder", + type=str, + help=""" Path to your project folder with After Effects Files - ''') -parser.add_argument('schedule', action="store", metavar='Schedule-URL', type=str, nargs='?', help=''' + """, +) +parser.add_argument( + "schedule", + action="store", + metavar="Schedule-URL", + type=str, + nargs="?", + help=""" URL or Path to your schedule.xml - ''') + """, +) -parser.add_argument('introfile', action="store", metavar='Name of intro source file', default='intro.aepx', type=str, nargs='?', help=''' +parser.add_argument( + "introfile", + action="store", + metavar="Name of intro source file", + default="intro.aepx", + type=str, + nargs="?", + help=""" Filename of the intro source file inside the project folder - ''') + """, +) -parser.add_argument('--debug', action="store_true", default=False, help=''' +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 - ''') + """, +) -parser.add_argument('--id', dest='ids', nargs='+', action="store", type=int, help=''' +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 - ''') + """, +) -parser.add_argument('--room', dest='rooms', nargs='+', action="store", type=str, help=''' +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" - ''') + """, +) -parser.add_argument('--day', dest='days', nargs='+', action="store", type=str, help=''' +parser.add_argument( + "--day", + dest="days", + nargs="+", + action="store", + type=str, + help=""" Only render from your projects schedule for the given days. This argument must not be used together with --debug Usage: ./make-adobe-after-effects.py yourproject/ --day "1" "3" - ''') + """, +) -parser.add_argument('--pause', action="store_true", default=False, help=''' +parser.add_argument( + "--pause", + action="store_true", + default=False, + help=""" Render a pause loop from the pause.aepx file in the project folder. - ''') + """, +) -parser.add_argument('--alpha', action="store_true", default=False, help=''' +parser.add_argument( + "--alpha", + action="store_true", + default=False, + help=""" Render intro/outro with alpha. - ''') + """, +) -parser.add_argument('--force', action="store_true", default=False, help=''' +parser.add_argument( + "--force", + action="store_true", + default=False, + help=""" Force render if file exists. - ''') + """, +) -parser.add_argument('--no-finalize', dest='nof', action="store_true", default=False, help=''' +parser.add_argument( + "--no-finalize", + dest="nof", + action="store_true", + default=False, + help=""" Skip finalize job. - ''') + """, +) -parser.add_argument('--outro', action="store_true", default=False, help=''' +parser.add_argument( + "--outro", + action="store_true", + default=False, + help=""" Render outro from the outro.aepx file in the project folder. - ''') + """, +) -parser.add_argument('--bgloop', action="store_true", default=False, help=''' +parser.add_argument( + "--bgloop", + action="store_true", + default=False, + help=""" Render background loop from the bgloop.aepx file in the project folder. - ''') + """, +) -parser.add_argument('--keep', action="store_true", default=False, help=''' +parser.add_argument( + "--keep", + action="store_true", + default=False, + help=""" Keep source file in the project folder after render. - ''') - -parser.add_argument('--mp4', action="store_true", default=False, help=''' + """, +) + +parser.add_argument( + "--mp4", + action="store_true", + default=False, + help=""" Also create a MP4 for preview. - ''') + """, +) args = parser.parse_args() @@ -109,53 +196,67 @@ def error(str): if not args.project: 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: +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") if args.debug: # persons = ['blubbel'] - persons = ['Vitor Sakaguti', 'Sara', 'A.L. Fehlhaber'] - events = [{ - 'id': 11450, - 'title': 'PQ Mail: Enabling post quantum secure encryption for email communication', - 'subtitle': '', - 'persons': persons, - 'personnames': ', '.join(persons), - 'room': 'rc1', - }] + persons = ["Vitor Sakaguti", "Sara", "A.L. Fehlhaber"] + events = [ + { + "id": 11450, + "title": "PQ Mail: Enabling post quantum secure encryption for email communication", + "subtitle": "", + "persons": persons, + "personnames": ", ".join(persons), + "room": "rc1", + } + ] 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(schedulelib.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)) -tempdir = tempfile.TemporaryDirectory() -print('working in ' + tempdir.name) +tempdir = tempfile.TemporaryDirectory(dir=os.getcwd()) +print("working in " + tempdir.name) def fmt_command(command, **kwargs): @@ -171,13 +272,14 @@ def run(command, **kwargs): return subprocess.check_call( fmt_command(command, **kwargs), stderr=subprocess.STDOUT, - stdout=subprocess.DEVNULL) + stdout=subprocess.DEVNULL, + ) def run_output(command, **kwargs): return subprocess.check_output( - fmt_command(command, **kwargs), - stderr=subprocess.STDOUT) + fmt_command(command, **kwargs), stderr=subprocess.STDOUT + ) def run_once(command, **kwargs): @@ -189,35 +291,43 @@ def run_once(command, **kwargs): stdout=None, stderr=None, close_fds=True, - creationflags=DETACHED_PROCESS) + creationflags=DETACHED_PROCESS, + ) 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'])) + 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 + '.aepx') - 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') + work_doc = os.path.join(tempdir.name, event_id + ".aepx") + 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 + '.aepx', work_doc) - if platform.system() == 'Darwin': - run(r'/Applications/Adobe\ After\ Effects\ 2024/aerender -project {jobpath} -comp {comp} -mp -output {locationpath}', + if event_id == "pause" or event_id == "outro" or event_id == "bgloop": + copyfile(args.project + event_id + ".aepx", work_doc) + if platform.system() == "Darwin": + run( + r"/Applications/Adobe\ After\ Effects\ 2024/aerender -project {jobpath} -comp {comp} -mp -output {locationpath}", jobpath=work_doc, comp=event_id, - locationpath=intermediate_clip) + locationpath=intermediate_clip, + ) - if platform.system() == 'Windows': - run(r'C:/Program\ Files/Adobe/Adobe\ After\ Effects\ 2024/Support\ Files/aerender.exe -project {jobpath} -comp {comp} -mp -output {locationpath}', + if platform.system() == "Windows": + run( + r"C:/Program\ Files/Adobe/Adobe\ After\ Effects\ 2024/Support\ Files/aerender.exe -project {jobpath} -comp {comp} -mp -output {locationpath}", jobpath=work_doc, comp=event_id, - locationpath=intermediate_clip) + locationpath=intermediate_clip, + ) else: - with open(args.project + 'intro.jsx', 'r') as fp: + with open(args.project + "intro.jsx", "r") as fp: scriptstr = fp.read() scriptstr = scriptstr.replace("$filename", work_doc.replace("\\", "/")) @@ -225,91 +335,114 @@ def enqueue_job(event): value = str(value).replace('"', '\\"') scriptstr = scriptstr.replace("$" + str(key), value) - with open(script_doc, 'w', encoding='utf-8') as fp: + with open(script_doc, "w", encoding="utf-8") as fp: fp.write(scriptstr) - copyfile(args.project+args.introfile, work_doc) + copyfile(args.project + args.introfile, work_doc) - if platform.system() == 'Darwin': - copyfile(args.project+'intro.scpt', ascript_doc) + if platform.system() == "Darwin": + copyfile(args.project + "intro.scpt", ascript_doc) - run('osascript {ascript_path} {scriptpath}', + run( + "osascript {ascript_path} {scriptpath}", scriptpath=script_doc, - ascript_path=ascript_doc) + ascript_path=ascript_doc, + ) # run('osascript {ascript_path} {jobpath} {scriptpath}', # jobpath=work_doc, # scriptpath=script_doc, # ascript_path=ascript_doc) - run(r'/Applications/Adobe\ After\ Effects\ 2024/aerender -project {jobpath} -comp "intro" -mp -output {locationpath}', + run( + r'/Applications/Adobe\ After\ Effects\ 2024/aerender -project {jobpath} -comp "intro" -mp -output {locationpath}', jobpath=work_doc, - locationpath=intermediate_clip) + locationpath=intermediate_clip, + ) - if platform.system() == 'Windows': - run_once(r'C:/Program\ Files/Adobe/Adobe\ After\ Effects\ 2024/Support\ Files/AfterFX.exe -noui -r {scriptpath}', - scriptpath=script_doc) + if platform.system() == "Windows": + run_once( + r"C:/Program\ Files/Adobe/Adobe\ After\ Effects\ 2024/Support\ Files/AfterFX.exe -noui -r {scriptpath}", + scriptpath=script_doc, + ) time.sleep(5) - run(r'C:/Program\ Files/Adobe/Adobe\ After\ Effects\ 2024/Support\ Files/aerender.exe -project {jobpath} -comp "intro" -mfr on 100 -output {locationpath}', + run( + r'C:/Program\ Files/Adobe/Adobe\ After\ Effects\ 2024/Support\ Files/aerender.exe -project {jobpath} -comp "intro" -mfr on 100 -output {locationpath}', jobpath=work_doc, - locationpath=intermediate_clip) + locationpath=intermediate_clip, + ) if args.debug or args.keep: path = tempdir.name dirs = os.listdir(path) for file in dirs: print(file) - copyfile(work_doc, args.project + event_id + '.aepx') - copyfile(script_doc, args.project + event_id + '.jsx') - copyfile(intermediate_clip, args.project + event_id + '.mov') + copyfile(work_doc, args.project + event_id + ".aepx") + copyfile(script_doc, args.project + event_id + ".jsx") + copyfile(intermediate_clip, args.project + event_id + ".mov") 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') - preview = os.path.join(os.path.dirname(args.project), event_id + '.mp4') + 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") + preview = os.path.join(os.path.dirname(args.project), event_id + ".mp4") if args.alpha: - ffprobe = run_output('ffprobe -i {input} -show_streams -select_streams a -loglevel error', - input=intermediate_clip) + ffprobe = run_output( + "ffprobe -i {input} -show_streams -select_streams a -loglevel error", + input=intermediate_clip, + ) if ffprobe: - run('ffmpeg -threads 0 -y -hide_banner -loglevel error -i {input} -c:v qtrle -movflags faststart -aspect 16:9 -c:a mp2 -b:a 384k -shortest -f mov {output}', + run( + "ffmpeg -threads 0 -y -hide_banner -loglevel error -i {input} -c:v qtrle -movflags faststart -aspect 16:9 -c:a mp2 -b:a 384k -shortest -f mov {output}", input=intermediate_clip, - output=final_clip) + output=final_clip, + ) else: - run('ffmpeg -threads 0 -y -hide_banner -loglevel error -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=48000 -i {input} -c:v qtrle -movflags faststart -aspect 16:9 -c:a mp2 -b:a 384k -shortest -f mov {output}', + run( + "ffmpeg -threads 0 -y -hide_banner -loglevel error -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=48000 -i {input} -c:v qtrle -movflags faststart -aspect 16:9 -c:a mp2 -b:a 384k -shortest -f mov {output}", input=intermediate_clip, - output=final_clip) + output=final_clip, + ) else: - ffprobe = run_output('ffprobe -i {input} -show_streams -select_streams a -loglevel error', - input=intermediate_clip) + ffprobe = run_output( + "ffprobe -i {input} -show_streams -select_streams a -loglevel error", + input=intermediate_clip, + ) if ffprobe: event_print(event, "finalize with audio from source file") - run('ffmpeg -threads 0 -y -hide_banner -loglevel error -i {input} -c:v mpeg2video -q:v 2 -aspect 16:9 -c:a mp2 -b:a 384k -shortest -f mpegts {output}', + run( + "ffmpeg -threads 0 -y -hide_banner -loglevel error -i {input} -c:v mpeg2video -q:v 2 -aspect 16:9 -c:a mp2 -b:a 384k -shortest -f mpegts {output}", input=intermediate_clip, - output=final_clip) + output=final_clip, + ) else: event_print(event, "finalize with silent audio") - run('ffmpeg -threads 0 -y -hide_banner -loglevel error -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=48000 -i {input} -c:v mpeg2video -q:v 2 -aspect 16:9 -c:a mp2 -b:a 384k -shortest -f mpegts {output}', + run( + "ffmpeg -threads 0 -y -hide_banner -loglevel error -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=48000 -i {input} -c:v mpeg2video -q:v 2 -aspect 16:9 -c:a mp2 -b:a 384k -shortest -f mpegts {output}", input=intermediate_clip, - output=final_clip) + output=final_clip, + ) - if event_id == 'pause' or event_id == 'outro' or event_id == 'bgloop': + 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.mp4: - run('ffmpeg -threads 0 -y -hide_banner -loglevel error -i {input} {output}', + run( + "ffmpeg -threads 0 -y -hide_banner -loglevel error -i {input} {output}", input=final_clip, - output=preview) + output=preview, + ) event_print(event, "created mp4 preview " + preview) + if args.ids: if len(args.ids) == 1: print("enqueuing {} job into aerender".format(len(args.ids))) @@ -322,25 +455,25 @@ else: print("enqueuing {} jobs into aerender".format(len(events))) for event in events: - if args.ids and event['id'] not in args.ids: + 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'])) + if args.rooms and event["room"] not in args.rooms: + print("skipping room %s (%s)" % (event["room"], event["title"])) continue - if args.days and event['day'] not in args.days: - print("skipping day %s (%s)" % (event['day'], event['title'])) + if args.days and event["day"] not in args.days: + print("skipping day %s (%s)" % (event["day"], event["title"])) continue for item in titlemap: - if str(item['id']) == str(event['id']): - title = item['title'] + if str(item["id"]) == str(event["id"]): + title = item["title"] event_print(event, "titlemap replacement") - event_print(event, "replacing title %s with %s" % (event['title'], title)) - event['title'] = title + event_print(event, "replacing title %s with %s" % (event["title"], title)) + event["title"] = title - 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: @@ -351,19 +484,19 @@ for event in events: event_print(event, "finalizing job") finalize_job(job_id, event) else: - event_id = str(event['id']) + 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') + 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') + 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) if args.debug or args.keep: - print('keeping source files in ' + args.project) + print("keeping source files in " + args.project) else: - print('all done, cleaning up ' + tempdir.name) + print("all done, cleaning up " + tempdir.name) tempdir.cleanup() diff --git a/voc_ae/intro.jsx b/voc_ae/intro.jsx index 330046d..b9d0f8f 100644 --- a/voc_ae/intro.jsx +++ b/voc_ae/intro.jsx @@ -26,4 +26,4 @@ textDocument_persons.text = "$personnames" textProp_persons.setValue(textDocument_persons) app.project.save() -// app.quit() +app.quit()