Allow rendering inserts
Includes an example from Forum Open Education 2019
154
forumoe19/__init__.py
Normal file
|
@ -0,0 +1,154 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
from renderlib import *
|
||||
from easing import *
|
||||
|
||||
# URL to Schedule-XML
|
||||
scheduleUrl = 'https://gist.githubusercontent.com/danimo/3cf27a2198da2fbc7c1fb138c13506ce/raw/forumoe19-schedule.xml'
|
||||
|
||||
personmap = {
|
||||
1: 'Margit Stumpp (Fraktion B. 90/Die Grünen)',
|
||||
2: 'Marja-Liisa Völlers (SPD-Fraktion)',
|
||||
3: 'Dr. Jens Brandenburg (FDP-Fraktion)',
|
||||
}
|
||||
|
||||
taglinemap = {
|
||||
1: "Bildungspolitische Sprecherin",
|
||||
2: "Expertin für frühk., schul. und berufl. Bildung",
|
||||
3: "Sprecher für Studium und Bildung",
|
||||
4: "mediale.pfade",
|
||||
}
|
||||
|
||||
|
||||
def bounce(i, min, max, frames):
|
||||
if i == frames - 1:
|
||||
return 0
|
||||
|
||||
if i <= frames/2:
|
||||
return easeInOutQuad(i, min, max, frames/2)
|
||||
else:
|
||||
return max - easeInOutQuad(i - frames/2, min, max, frames/2)
|
||||
|
||||
def introFrames(parameters):
|
||||
# 1 Sekunde Text Fadein
|
||||
frames = 1*fps
|
||||
for i in range(0, frames):
|
||||
yield (
|
||||
('text', 'style', 'opacity', "%.4f" % easeLinear(i, 0, 1, frames)),
|
||||
)
|
||||
|
||||
# 4 Sekunden stehen lassen
|
||||
frames = 4*fps
|
||||
for i in range(0, frames):
|
||||
yield ()
|
||||
|
||||
def outroFrames(p):
|
||||
# 5 Sekunden stehen bleiben
|
||||
frames = 5*fps
|
||||
for i in range(0, frames):
|
||||
yield []
|
||||
|
||||
def bbFrames(parameters):
|
||||
# 1 Sekunde Text Fadein
|
||||
frames = 1*fps
|
||||
for i in range(0, frames):
|
||||
yield (
|
||||
('bg', 'style', 'opacity', "%.4f" % easeLinear(i, 0, 1, frames)),
|
||||
('text', 'style', 'opacity', "%.4f" % easeLinear(i, 0, 1, frames)),
|
||||
)
|
||||
|
||||
# 3 Sekunden stehen lassen
|
||||
frames = 3*fps
|
||||
for i in range(0, frames):
|
||||
yield ()
|
||||
|
||||
frames = 1*fps
|
||||
for i in range(0, frames):
|
||||
yield (
|
||||
('bg', 'style', 'opacity', "%.4f" % easeLinear(i, 1, -1, frames)),
|
||||
('text', 'style', 'opacity', "%.4f" % easeLinear(i, 1, -1, frames)),
|
||||
)
|
||||
|
||||
|
||||
|
||||
def debug():
|
||||
# render(
|
||||
# 'intro.svg',
|
||||
# '../intro.ts',
|
||||
# introFrames,
|
||||
# {
|
||||
# '$ID': 4711,
|
||||
# '$TITLE': "Long Long Long title is LONG",
|
||||
# '$SUBTITLE': 'Long Long Long Long subtitle is LONGER',
|
||||
# '$SPEAKER': 'Long Name of Dr. Dr. Prof. Dr. Long Long'
|
||||
# }
|
||||
# )
|
||||
|
||||
render(
|
||||
'insert.svg',
|
||||
'../insert.mkv',
|
||||
bbFrames,
|
||||
{
|
||||
'$PERSON': "Prof. Bernhard Birnbaum",
|
||||
'$TAGLINE': "Leiter des rennomierten Birnbaum-Instituts",
|
||||
}
|
||||
)
|
||||
|
||||
# render(
|
||||
# 'pause.svg',
|
||||
# '../pause.ts',
|
||||
# pauseFrames
|
||||
# )
|
||||
#
|
||||
# render(
|
||||
# 'outro.svg',
|
||||
# '../outro.ts',
|
||||
# outroFrames
|
||||
# )
|
||||
|
||||
def tasks(queue, args, idlist, skiplist):
|
||||
# iterate over all events extracted from the schedule xml-export
|
||||
for event in events(scheduleUrl):
|
||||
if event['room'] not in ('ecdf'):
|
||||
print("skipping room %s (%s [%s])" % (event['room'], event['title'], event['id']))
|
||||
continue
|
||||
if not (idlist==[]):
|
||||
if 000000 in idlist:
|
||||
print("skipping id (%s [%s])" % (event['title'], event['id']))
|
||||
continue
|
||||
if int(event['id']) not in idlist:
|
||||
print("skipping id (%s [%s])" % (event['title'], event['id']))
|
||||
continue
|
||||
|
||||
# generate a task description and put it into the queue
|
||||
queue.put(Rendertask(
|
||||
infile = 'intro.svg',
|
||||
outfile = str(event['id'])+".ts",
|
||||
sequence = introFrames,
|
||||
parameters = {
|
||||
'$ID': event['id'],
|
||||
'$TITLE': event['title'],
|
||||
'$SUBTITLE': event['subtitle'],
|
||||
'$SPEAKER': event['personnames']
|
||||
}
|
||||
))
|
||||
|
||||
# place a task for the outro into the queue
|
||||
if not "out" in skiplist:
|
||||
queue.put(Rendertask(
|
||||
infile = 'outro.svg',
|
||||
outfile = 'outro.ts',
|
||||
sequence = outroFrames
|
||||
))
|
||||
|
||||
for person in persons(scheduleUrl, personmap, taglinemap):
|
||||
queue.put(Rendertask(
|
||||
infile = 'insert.svg',
|
||||
outfile = "insert_{}.mkv".format(person['person'].replace("/", "_")),
|
||||
sequence = bbFrames,
|
||||
parameters = {
|
||||
'$PERSON': person['person'],
|
||||
'$TAGLINE': person['tagline'],
|
||||
}
|
||||
))
|
||||
|
BIN
forumoe19/artwork/5_Intro-Video.png
Normal file
After Width: | Height: | Size: 100 KiB |
12
forumoe19/artwork/CC-Zero-badge.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Basic//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="88" height="31" viewBox="0 0 88 31" baseProfile="basic" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(66.666667%,69.803922%,67.058824%);fill-opacity:1;" d="M 2.300781 0.570313 L 85.429688 0.71875 C 86.589844 0.71875 87.628906 0.546875 87.628906 3.039063 L 87.527344 30.414063 L 0.203125 30.414063 L 0.203125 2.9375 C 0.203125 1.707031 0.324219 0.570313 2.300781 0.570313 Z "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 86.351563 0.199219 L 1.648438 0.199219 C 0.738281 0.199219 0 0.9375 0 1.847656 L 0 30.628906 C 0 30.832031 0.167969 31 0.371094 31 L 87.628906 31 C 87.832031 31 88 30.832031 88 30.628906 L 88 1.847656 C 88 0.9375 87.261719 0.199219 86.351563 0.199219 Z M 1.648438 0.945313 L 86.351563 0.945313 C 86.851563 0.945313 87.253906 1.347656 87.253906 1.847656 C 87.253906 1.847656 87.253906 13.46875 87.253906 21.851563 L 26.515625 21.851563 C 24.296875 25.863281 20.023438 28.585938 15.117188 28.585938 C 10.207031 28.585938 5.933594 25.863281 3.714844 21.851563 L 0.746094 21.851563 C 0.746094 13.46875 0.746094 1.847656 0.746094 1.847656 C 0.746094 1.347656 1.148438 0.945313 1.648438 0.945313 Z "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 46.753906 23.984375 L 50.832031 23.984375 L 50.832031 24.738281 L 48.230469 27.886719 L 50.90625 27.886719 L 50.90625 28.832031 L 46.679688 28.832031 L 46.679688 28.074219 L 49.28125 24.925781 L 46.753906 24.925781 L 46.753906 23.984375 M 51.8125 23.984375 L 55.183594 23.984375 L 55.183594 24.925781 L 53.0625 24.925781 L 53.0625 25.832031 L 55.058594 25.832031 L 55.058594 26.773438 L 53.0625 26.773438 L 53.0625 27.886719 L 55.253906 27.886719 L 55.253906 28.832031 L 51.8125 28.832031 L 51.8125 23.984375 M 58.132813 26.132813 C 58.394531 26.132813 58.582031 26.082031 58.695313 25.984375 C 58.808594 25.890625 58.867188 25.730469 58.867188 25.503906 C 58.867188 25.285156 58.808594 25.125 58.695313 25.03125 C 58.582031 24.9375 58.394531 24.890625 58.132813 24.890625 L 57.605469 24.890625 L 57.605469 26.132813 L 58.132813 26.132813 M 57.605469 26.996094 L 57.605469 28.832031 L 56.355469 28.832031 L 56.355469 23.984375 L 58.265625 23.984375 C 58.902344 23.984375 59.371094 24.089844 59.667969 24.304688 C 59.964844 24.519531 60.117188 24.855469 60.117188 25.320313 C 60.117188 25.640625 60.039063 25.902344 59.882813 26.109375 C 59.730469 26.316406 59.496094 26.464844 59.183594 26.5625 C 59.355469 26.601563 59.507813 26.691406 59.640625 26.832031 C 59.777344 26.964844 59.914063 27.175781 60.054688 27.453125 L 60.734375 28.832031 L 59.402344 28.832031 L 58.8125 27.625 C 58.691406 27.382813 58.570313 27.21875 58.445313 27.128906 C 58.324219 27.039063 58.164063 26.996094 57.960938 26.996094 L 57.605469 26.996094 M 63.6875 24.800781 C 63.304688 24.800781 63.011719 24.941406 62.800781 25.222656 C 62.589844 25.503906 62.484375 25.902344 62.484375 26.410156 C 62.484375 26.921875 62.589844 27.316406 62.800781 27.597656 C 63.011719 27.878906 63.304688 28.019531 63.6875 28.019531 C 64.070313 28.019531 64.367188 27.878906 64.578125 27.597656 C 64.785156 27.316406 64.890625 26.921875 64.890625 26.410156 C 64.890625 25.902344 64.785156 25.503906 64.578125 25.222656 C 64.367188 24.941406 64.070313 24.800781 63.6875 24.800781 M 63.6875 23.894531 C 64.464844 23.894531 65.078125 24.117188 65.519531 24.5625 C 65.960938 25.011719 66.179688 25.625 66.179688 26.410156 C 66.179688 27.195313 65.960938 27.808594 65.519531 28.253906 C 65.078125 28.703125 64.464844 28.925781 63.6875 28.925781 C 62.910156 28.925781 62.300781 28.703125 61.855469 28.253906 C 61.414063 27.808594 61.195313 27.195313 61.195313 26.410156 C 61.195313 25.625 61.414063 25.011719 61.855469 24.5625 C 62.300781 24.117188 62.910156 23.894531 63.6875 23.894531 "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 25.121094 14.578125 C 25.125 20.097656 20.648438 24.578125 15.125 24.582031 C 9.605469 24.585938 5.125 20.113281 5.121094 14.589844 C 5.121094 14.585938 5.121094 14.582031 5.121094 14.578125 C 5.117188 9.054688 9.59375 4.578125 15.113281 4.574219 C 20.636719 4.570313 25.117188 9.042969 25.121094 14.566406 C 25.121094 14.570313 25.121094 14.574219 25.121094 14.578125 Z "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 23.25 6.433594 C 25.464844 8.652344 26.574219 11.367188 26.574219 14.578125 C 26.574219 17.789063 25.484375 20.476563 23.304688 22.636719 C 20.992188 24.910156 18.261719 26.046875 15.105469 26.046875 C 11.992188 26.046875 9.304688 24.921875 7.050781 22.664063 C 4.792969 20.410156 3.667969 17.714844 3.667969 14.578125 C 3.667969 11.441406 4.792969 8.726563 7.050781 6.433594 C 9.246094 4.214844 11.933594 3.109375 15.105469 3.109375 C 18.316406 3.109375 21.03125 4.214844 23.25 6.433594 Z M 8.542969 7.925781 C 6.667969 9.820313 5.730469 12.035156 5.730469 14.582031 C 5.730469 17.125 6.660156 19.324219 8.511719 21.175781 C 10.367188 23.03125 12.578125 23.960938 15.140625 23.960938 C 17.703125 23.960938 19.929688 23.023438 21.824219 21.148438 C 23.621094 19.410156 24.519531 17.21875 24.519531 14.582031 C 24.519531 11.960938 23.605469 9.738281 21.78125 7.910156 C 19.953125 6.085938 17.742188 5.171875 15.140625 5.171875 C 12.539063 5.171875 10.339844 6.089844 8.542969 7.925781 Z M 13.476563 13.460938 C 13.1875 12.835938 12.761719 12.523438 12.1875 12.523438 C 11.175781 12.523438 10.671875 13.207031 10.671875 14.566406 C 10.671875 15.925781 11.175781 16.605469 12.1875 16.605469 C 12.855469 16.605469 13.332031 16.277344 13.617188 15.613281 L 15.019531 16.359375 C 14.351563 17.542969 13.351563 18.136719 12.015625 18.136719 C 10.984375 18.136719 10.15625 17.824219 9.539063 17.191406 C 8.917969 16.558594 8.609375 15.6875 8.609375 14.578125 C 8.609375 13.488281 8.925781 12.621094 9.566406 11.980469 C 10.207031 11.335938 11.003906 11.015625 11.957031 11.015625 C 13.371094 11.015625 14.378906 11.574219 14.992188 12.6875 Z M 20.066406 13.460938 C 19.78125 12.835938 19.359375 12.523438 18.808594 12.523438 C 17.777344 12.523438 17.261719 13.207031 17.261719 14.566406 C 17.261719 15.925781 17.777344 16.605469 18.808594 16.605469 C 19.476563 16.605469 19.945313 16.277344 20.210938 15.613281 L 21.644531 16.359375 C 20.976563 17.542969 19.976563 18.136719 18.644531 18.136719 C 17.613281 18.136719 16.789063 17.824219 16.171875 17.191406 C 15.550781 16.558594 15.242188 15.6875 15.242188 14.578125 C 15.242188 13.488281 15.554688 12.621094 16.183594 11.980469 C 16.8125 11.335938 17.613281 11.015625 18.585938 11.015625 C 19.996094 11.015625 21.003906 11.574219 21.613281 12.6875 Z "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(100%,100%,100%);fill-opacity:1;" d="M 60.03125 10.878906 C 60.03125 15.128906 58.308594 18.578125 56.183594 18.578125 C 54.054688 18.578125 52.332031 15.128906 52.332031 10.878906 C 52.332031 6.625 54.054688 3.179688 56.183594 3.179688 C 58.308594 3.179688 60.03125 6.625 60.03125 10.878906 Z "/>
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(0%,0%,0%);fill-opacity:1;" d="M 56.429688 2.417969 C 51.582031 2.417969 50.359375 6.996094 50.359375 10.875 C 50.359375 14.757813 51.582031 19.332031 56.429688 19.332031 C 61.28125 19.332031 62.5 14.757813 62.5 10.875 C 62.5 6.996094 61.28125 2.417969 56.429688 2.417969 Z M 56.429688 5.609375 C 56.628906 5.609375 56.808594 5.636719 56.976563 5.679688 C 57.324219 5.980469 57.496094 6.394531 57.160156 6.976563 L 53.929688 12.914063 C 53.832031 12.160156 53.816406 11.425781 53.816406 10.875 C 53.816406 9.167969 53.9375 5.609375 56.429688 5.609375 Z M 58.847656 8.34375 C 59.019531 9.257813 59.042969 10.207031 59.042969 10.875 C 59.042969 12.585938 58.925781 16.144531 56.429688 16.144531 C 56.234375 16.144531 56.054688 16.125 55.886719 16.082031 C 55.851563 16.074219 55.824219 16.0625 55.792969 16.050781 C 55.742188 16.035156 55.6875 16.019531 55.640625 16 C 55.082031 15.765625 54.734375 15.335938 55.238281 14.582031 Z "/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.1 KiB |
BIN
forumoe19/artwork/Logo_Bündnis_Freie_Bildung_hochkant.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
forumoe19/artwork/insert.png
Normal file
After Width: | Height: | Size: 19 KiB |
97
forumoe19/artwork/insert.svg
Normal file
After Width: | Height: | Size: 30 KiB |
588
forumoe19/artwork/intro.svg
Normal file
After Width: | Height: | Size: 131 KiB |
BIN
forumoe19/artwork/logo-okfn.png
Normal file
After Width: | Height: | Size: 11 KiB |
583
forumoe19/artwork/outro.svg
Normal file
After Width: | Height: | Size: 135 KiB |
0
forumoe19/artwork/pause.svg
Normal file
BIN
forumoe19/artwork/wmde.png
Normal file
After Width: | Height: | Size: 28 KiB |
44
renderlib.py
|
@ -162,7 +162,7 @@ def rendertask(task):
|
|||
# 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'):
|
||||
if task.outfile.endswith('.ts') or task.outfile.endswith('.mov') or task.outfile.endswith('.mkv'):
|
||||
width = 1920
|
||||
height = 1080
|
||||
else:
|
||||
|
@ -216,6 +216,8 @@ def rendertask(task):
|
|||
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)
|
||||
elif task.outfile.endswith('.mkv'):
|
||||
cmd = 'cd {0} && ffmpeg -ar 48000 -ac 2 -f s16le -i /dev/zero -f image2 -i .frames/%04d.png -aspect 16:9 -c copy -shortest "{1}"'.format(task.workdir, 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)
|
||||
|
||||
|
@ -238,7 +240,7 @@ def rendertask(task):
|
|||
# Download the Events-Schedule and parse all Events out of it. Yield a tupel for each Event
|
||||
|
||||
|
||||
def events(scheduleUrl, titlemap={}):
|
||||
def downloadSchedule(scheduleUrl):
|
||||
print("downloading schedule")
|
||||
|
||||
# download the schedule
|
||||
|
@ -249,8 +251,41 @@ def events(scheduleUrl, titlemap={}):
|
|||
|
||||
# parse into ElementTree
|
||||
parser = etree.XMLParser(huge_tree=True)
|
||||
schedule = etree.fromstring(xml, parser)
|
||||
return etree.fromstring(xml, parser)
|
||||
|
||||
def persons(scheduleUrl, personmap={}, taglinemap={}):
|
||||
schedule = downloadSchedule(scheduleUrl)
|
||||
# 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
|
||||
persons_seen = []
|
||||
if event.find('persons') is not None:
|
||||
for person in event.find('persons').iter('person'):
|
||||
id = int(person.get("id"))
|
||||
person = re.sub(r'\s+', ' ', person.text).strip()
|
||||
match = re.search(r'\((.*?)\)', person)
|
||||
tagline = ''
|
||||
if not match is None:
|
||||
tagline = match.group(1)
|
||||
person = person.split(" (")[0]
|
||||
if id in taglinemap:
|
||||
tagline = taglinemap[id]
|
||||
if id in personmap:
|
||||
person = personmap[id]
|
||||
if not id in persons_seen:
|
||||
persons_seen.append(id)
|
||||
yield {
|
||||
'id': id,
|
||||
'person': person,
|
||||
'tagline': tagline
|
||||
}
|
||||
|
||||
def events(scheduleUrl, titlemap={}):
|
||||
schedule = downloadSchedule(scheduleUrl)
|
||||
# iterate all days
|
||||
for day in schedule.iter('day'):
|
||||
# iterate all rooms
|
||||
|
@ -277,7 +312,6 @@ def events(scheduleUrl, titlemap={}):
|
|||
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,
|
||||
|
@ -287,7 +321,7 @@ def events(scheduleUrl, titlemap={}):
|
|||
'personnames': ', '.join(personnames),
|
||||
'room': room.attrib['name'],
|
||||
'track': event.find('track').text,
|
||||
'url': event.find('url').text
|
||||
#'url': event.find('url').text
|
||||
}
|
||||
|
||||
|
||||
|
|