This commit is contained in:
Leif Niemczik 2025-02-15 09:41:22 +01:00 committed by GitHub
commit 9f4535166c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 211404 additions and 153 deletions

View file

@ -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()

205866
mrmcd2024/intro.aepx Normal file

File diff suppressed because one or more lines are too long

29
mrmcd2024/intro.jsx Normal file
View file

@ -0,0 +1,29 @@
var projectFile = new File("$filename")
app.open(projectFile)
var comp
for (var i = 1; i <= app.project.numItems; i++) {
if (
app.project.item(i) instanceof CompItem &&
app.project.item(i).name === "intro"
) {
comp = app.project.item(i)
break
}
}
var layer_title = comp.layer("intro_title")
var textProp_title = layer_title.property("Source Text")
var textDocument_title = textProp_title.value
var layer_persons = comp.layer("intro_personnames")
var textProp_persons = layer_persons.property("Source Text")
var textDocument_persons = textProp_persons.value
textDocument_title.text = "$title"
textProp_title.setValue(textDocument_title)
textDocument_persons.text = "$personnames"
textProp_persons.setValue(textDocument_persons)
app.project.save()
// app.quit()

BIN
mrmcd2024/intro.scpt Normal file

Binary file not shown.

BIN
mrmcd2024/rubik-bold.ttf Normal file

Binary file not shown.

BIN
mrmcd2024/rubik-regular.ttf Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

5217
voc_ae/intro.aepx Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,23 +1,29 @@
var comp;
for (var i = 1; i <= app.project.numItems; i ++) {
if ((app.project.item(i) instanceof CompItem) && (app.project.item(i).name === 'intro')) {
comp = app.project.item(i);
break;
var projectFile = new File("$filename")
app.open(projectFile)
var comp
for (var i = 1; i <= app.project.numItems; i++) {
if (
app.project.item(i) instanceof CompItem &&
app.project.item(i).name === "intro"
) {
comp = app.project.item(i)
break
}
}
var layer_title = comp.layer('intro_title');
var textProp_title = layer_title.property("Source Text");
var textDocument_title = textProp_title.value;
var layer_title = comp.layer("intro_title")
var textProp_title = layer_title.property("Source Text")
var textDocument_title = textProp_title.value
var layer_persons = comp.layer('intro_personnames');
var textProp_persons = layer_persons.property("Source Text");
var textDocument_persons = textProp_persons.value;
var layer_persons = comp.layer("intro_personnames")
var textProp_persons = layer_persons.property("Source Text")
var textDocument_persons = textProp_persons.value
textDocument_title.text = "$title";
textProp_title.setValue(textDocument_title);
textDocument_title.text = "$title"
textProp_title.setValue(textDocument_title)
textDocument_persons.text = "$personnames";
textProp_persons.setValue(textDocument_persons);
textDocument_persons.text = "$personnames"
textProp_persons.setValue(textDocument_persons)
app.project.save();
app.quit();
app.project.save()
app.quit()