diff --git a/camp2023/config.ini b/camp2023/config.ini
index e8c7ab6..2072250 100644
--- a/camp2023/config.ini
+++ b/camp2023/config.ini
@@ -1,13 +1,15 @@
-[meta]
+[default]
schedule = https://pretalx.c3voc.de/camp2023/schedule/export/schedule.xml
template = intro.mp4
alpha = false
prores = false
-inout_type = t
+fontfile = true
+inout = t
[title]
in = 16
out = 24
+fontfamily = BeonRegular
fontfile = saira.ttf
fontsize = 100
fontcolor = #FB48C4
@@ -17,6 +19,7 @@ y = 100
[speaker]
in = 16
out = 24
+fontfamily = BeonRegular
fontfile = saira.ttf
fontsize = 70
fontcolor = #3FFF21
@@ -26,10 +29,11 @@ y = 800
[text]
in = 16
out = 24
+fontfamily = BeonRegular
fontfile = saira.ttf
fontsize = 45
fontcolor = #FB48C4
x = (w-text_w)/2
y = 1000
-text = Chaos Communication Camp 2023
+text = 'Chaos Communication Camp 2023'
diff --git a/cccamp19/config.ini b/cccamp19/config.ini
index d723d7d..181caed 100644
--- a/cccamp19/config.ini
+++ b/cccamp19/config.ini
@@ -1,13 +1,15 @@
-[meta]
+[default]
schedule = https://fahrplan.events.ccc.de/camp/2019/Fahrplan/schedule.xml
template = cccamp19_talks_intro_1080p.mov
alpha = true
prores = true
-inout_type = n
+fontfile = true
+inout = n
[title]
in = 193
out = 324
+fontfamily =
fontfile = Marvel-Bold.ttf
fontsize = 120
fontcolor = #c68100
@@ -17,6 +19,7 @@ y = 480
[speaker]
in = 233
out = 324
+fontfamily =
fontfile = Marvel-Regular.ttf
fontsize = 70
fontcolor = #c68100
@@ -26,10 +29,11 @@ y = 845
[text]
in = 242
out = 324
+fontfamily =
fontfile = Marvel-Regular.ttf
fontsize = 45
fontcolor = #c68100
x = (w-text_w)/2
y = 927
-text = chaos communication camp 2019
+text = 'chaos communication camp 2019'
diff --git a/denog11/config.ini b/denog11/config.ini
index 89c8096..6f403b5 100644
--- a/denog11/config.ini
+++ b/denog11/config.ini
@@ -1,13 +1,15 @@
-[meta]
+[default]
schedule = https://pretalx.denog.de/denog11/schedule/export/schedule.xml
template = denog11_intro_template.ts
alpha = false
prores = false
-inout_type = n
+fontfile = true
+inout = n
[title]
in = 1
out = 6.5
+fontfamily =
fontfile = DejaVuSans.ttf
fontsize = 100
fontcolor = #f9cc12
@@ -17,6 +19,7 @@ y = 200
[speaker]
in = 2
out = 6.5
+fontfamily =
fontfile = DejaVuSans.ttf
fontsize = 60
fontcolor = #ffffff
@@ -26,10 +29,11 @@ y = 900
[text]
in = 3
out = 6.5
+fontfamily =
fontfile = DejaVuSans.ttf
fontsize = 45
fontcolor = #ffffff
x = 640
y = 1000
-; text =
+text = ''
diff --git a/fossgis24/__init__.py b/fossgis24/__init__.py
new file mode 100644
index 0000000..86cc8e2
--- /dev/null
+++ b/fossgis24/__init__.py
@@ -0,0 +1,201 @@
+#!/usr/bin/python3
+
+import subprocess
+from renderlib import *
+from schedulelib import *
+from easing import *
+
+# URL to Schedule-XML
+scheduleUrl = 'https://pretalx.com/fossgis2024/schedule/export/schedule.xml'
+
+# For (really) too long titles
+titlemap = {
+ #708: "Neue WEB-Anwendungen des LGRB Baden-Württemberg im Überblick"
+}
+
+
+def outroFrames(params):
+ # 8 Sekunden
+
+ # 2 Sekunden Fadein Text
+ frames = 2*fps
+ for i in range(0, frames):
+ yield (
+ ('banderole', 'style', 'opacity', "%.4f" % easeOutCubic(i, 0, 1, frames) ),
+ ('license', 'style', 'opacity', 0)
+ )
+
+ # 2 Sekunde Fadein Lizenz-Logo
+ frames = 2*fps
+ for i in range(0, frames):
+ yield (
+ ('banderole', 'style', 'opacity', 1),
+ ('license', 'style', 'opacity', "%.4f" % (float(i)/frames))
+ )
+
+ # 4 Sekunde stehen bleiben
+ frames = 4*fps
+ for i in range(0, frames):
+ yield (
+ ('banderole', 'style', 'opacity', 1),
+ ('license', 'style', 'opacity', 1)
+ )
+
+def introFrames(params):
+ # 7 Sekunden
+
+ # 2 Sekunden Text 1
+ frames = 2*fps
+ for i in range(0, frames):
+ yield (
+ ('box-und-text1', 'style', 'opacity', "%.4f" % easeOutCubic(i, 0, 1, frames)),
+ ('url', 'style', 'opacity', "%.4f" % easeOutCubic(i, 0, 1, frames)),
+ ('text1', 'style', 'opacity', "%.4f" % 1),
+ ('text2', 'style', 'opacity', 0)
+ )
+
+ # 1 Sekunde Fadeout Text 1
+ frames = 1*fps
+ for i in range(0, frames):
+ yield (
+ ('box-und-text1', 'style', 'opacity', 1),
+ ('url', 'style', 'opacity', 1),
+ ('text1', 'style', 'opacity', "%.4f" % (1-(float(i)/frames))),
+ ('text2', 'style', 'opacity', 0)
+ )
+
+ # 2 Sekunden Text 2
+ frames = 2*fps
+ for i in range(0, frames):
+ yield (
+ ('box-und-text1', 'style', 'opacity', 1),
+ ('url', 'style', 'opacity', 1),
+ ('text1', 'style', 'opacity', 0),
+ ('text2', 'style', 'opacity', "%.4f" % easeOutCubic(i, 0, 1, frames))
+ )
+
+ # 2 Sekunden stehen bleiben
+ frames = 2*fps
+ for i in range(0, frames):
+ yield (
+ ('box-und-text1', 'style', 'opacity', 1),
+ ('url', 'style', 'opacity', 1),
+ ('text1', 'style', 'opacity', 0),
+ ('text2', 'style', 'opacity', 1)
+ )
+
+def pauseFrames(params):
+ # 12 Sekunden
+
+ # 2 Sekunden Text1 stehen
+ frames = 2*fps
+ for i in range(0, frames):
+ yield (
+ ('text1', 'style', 'opacity', 1),
+ ('text2', 'style', 'opacity', 0)
+ )
+
+ # 2 Sekunden Fadeout Text1
+ frames = 2*fps
+ for i in range(0, frames):
+ yield (
+ ('text1', 'style', 'opacity', "%.4f" % (1-easeOutCubic(i, 0, 1, frames))),
+ ('text2', 'style', 'opacity', 0)
+ )
+
+ # 2 Sekunden Fadein Text2
+ frames = 2*fps
+ for i in range(0, frames):
+ yield (
+ ('text1', 'style', 'opacity', 0),
+ ('text2', 'style', 'opacity', "%.4f" % easeOutCubic(i, 0, 1, frames))
+ )
+
+ # 2 Sekunden Text2 stehen
+ frames = 2*fps
+ for i in range(0, frames):
+ yield (
+ ('text1', 'style', 'opacity', 0),
+ ('text2', 'style', 'opacity', 1)
+ )
+
+ # 2 Sekunden Fadeout Text2
+ frames = 2*fps
+ for i in range(0, frames):
+ yield (
+ ('text1', 'style', 'opacity', 0),
+ ('text2', 'style', 'opacity', "%.4f" % (1-easeOutCubic(i, 0, 1, frames)))
+ )
+
+ # 2 Sekunden Fadein Text1
+ frames = 2*fps
+ for i in range(0, frames):
+ yield (
+ ('text1', 'style', 'opacity', "%.4f" % (easeOutCubic(i, 0, 1, frames))),
+ ('text2', 'style', 'opacity', 0)
+ )
+
+def debug():
+ render(
+ 'intro.svg',
+ '../intro.ts',
+ introFrames,
+ {
+ '$id': 904,
+ '$title': 'Was ist Open Source, wie funktioniert das?',
+ '$subtitle': 'Die Organisation der Open Geo- und GIS-Welt. Worauf man achten sollte.',
+ '$personnames': 'Arnulf Christl, Astrid Emde, Dominik Helle, Till Adams'
+ }
+ )
+
+ render(
+ 'outro.svg',
+ '../outro.ts',
+ outroFrames
+ )
+
+ render('pause.svg',
+ '../pause.ts',
+ pauseFrames
+ )
+
+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 ('Hörsaal 1 (Audimax 1)', 'Hörsaal 2 (Ditze H016)', 'Hörsaal 3 (K0506/ Audimax 2)', 'Hörsaal 4 (A.013)'):
+ print("skipping room %s (%s)" % (event['room'], event['title']))
+ continue
+
+
+ if (event['id'] in idlist or not idlist) and not 'intro' in skiplist:
+ # generate a task description and put them into the queue
+ queue.put(Rendertask(
+ infile = 'intro.svg',
+ outfile = str(event['id'])+".ts",
+ sequence = introFrames,
+ parameters = {
+ '$id': event['id'],
+ '$title': event['title'],
+ '$url': event['url'],
+ #'$subtitle': event['subtitle'],
+ '$personnames': event['personnames']
+ }
+ ))
+
+ if not 'outro' in skiplist:
+ # place a task for the outro into the queue
+ queue.put(Rendertask(
+ infile = 'outro.svg',
+ outfile = 'outro.ts',
+ sequence = outroFrames
+ ))
+
+ if not 'pause' in skiplist:
+ # place the pause-sequence into the queue
+ queue.put(Rendertask(
+ infile = 'pause.svg',
+ outfile = 'pause.ts',
+ sequence = pauseFrames
+ ))
+
+
diff --git a/fossgis24/artwork/001_camptocamp_logo.png b/fossgis24/artwork/001_camptocamp_logo.png
new file mode 100644
index 0000000..17be0fc
Binary files /dev/null and b/fossgis24/artwork/001_camptocamp_logo.png differ
diff --git a/fossgis24/artwork/002_WhereGroup.png b/fossgis24/artwork/002_WhereGroup.png
new file mode 100644
index 0000000..36f2cc7
Binary files /dev/null and b/fossgis24/artwork/002_WhereGroup.png differ
diff --git a/fossgis24/artwork/002_WhereGroup.psd b/fossgis24/artwork/002_WhereGroup.psd
new file mode 100644
index 0000000..bf28598
Binary files /dev/null and b/fossgis24/artwork/002_WhereGroup.psd differ
diff --git a/fossgis24/artwork/003_dataport.png b/fossgis24/artwork/003_dataport.png
new file mode 100644
index 0000000..4d1db84
Binary files /dev/null and b/fossgis24/artwork/003_dataport.png differ
diff --git a/fossgis24/artwork/FOSSGIS-Pause_DaVinci.drp b/fossgis24/artwork/FOSSGIS-Pause_DaVinci.drp
new file mode 100644
index 0000000..cd1bac3
Binary files /dev/null and b/fossgis24/artwork/FOSSGIS-Pause_DaVinci.drp differ
diff --git a/fossgis24/artwork/LOGO_FOSSGIS24_RGB_300dpi.png b/fossgis24/artwork/LOGO_FOSSGIS24_RGB_300dpi.png
new file mode 100644
index 0000000..eb28485
Binary files /dev/null and b/fossgis24/artwork/LOGO_FOSSGIS24_RGB_300dpi.png differ
diff --git a/fossgis24/artwork/Titelbild_FOSSGIS2024.png b/fossgis24/artwork/Titelbild_FOSSGIS2024.png
new file mode 100644
index 0000000..dba787d
Binary files /dev/null and b/fossgis24/artwork/Titelbild_FOSSGIS2024.png differ
diff --git a/fossgis24/artwork/Titelbild_FOSSGIS2024_fullhd.png b/fossgis24/artwork/Titelbild_FOSSGIS2024_fullhd.png
new file mode 100644
index 0000000..1f7f2a9
Binary files /dev/null and b/fossgis24/artwork/Titelbild_FOSSGIS2024_fullhd.png differ
diff --git a/fossgis24/artwork/by-sa.svg b/fossgis24/artwork/by-sa.svg
new file mode 100755
index 0000000..f850297
--- /dev/null
+++ b/fossgis24/artwork/by-sa.svg
@@ -0,0 +1,199 @@
+
+
+
diff --git a/fossgis24/artwork/fossgis24_logo.png b/fossgis24/artwork/fossgis24_logo.png
new file mode 100644
index 0000000..485560f
Binary files /dev/null and b/fossgis24/artwork/fossgis24_logo.png differ
diff --git a/fossgis24/artwork/intro.svg b/fossgis24/artwork/intro.svg
new file mode 100755
index 0000000..8010d23
--- /dev/null
+++ b/fossgis24/artwork/intro.svg
@@ -0,0 +1,243 @@
+
+
+
+
diff --git a/fossgis24/artwork/outro.svg b/fossgis24/artwork/outro.svg
new file mode 100755
index 0000000..924919f
--- /dev/null
+++ b/fossgis24/artwork/outro.svg
@@ -0,0 +1,350 @@
+
+
+
+
diff --git a/fossgis24/artwork/pause.svg b/fossgis24/artwork/pause.svg
new file mode 100755
index 0000000..55e7c7d
--- /dev/null
+++ b/fossgis24/artwork/pause.svg
@@ -0,0 +1,242 @@
+
+
+
+
diff --git a/god2024/__init__.py b/god2024/__init__.py
deleted file mode 100644
index c04c938..0000000
--- a/god2024/__init__.py
+++ /dev/null
@@ -1,145 +0,0 @@
-#!/usr/bin/python3
-
-from renderlib import *
-from schedulelib import *
-from easing import *
-
-# URL to Schedule-XML
-scheduleUrl = 'https://import.c3voc.de/schedule/god2024.xml?showall=yes'
-
-titlemap = {
-
-}
-
-def introFrames(p):
- givenFrame = 0
-
- nr = p['$id'];
-
- # 1 Sekunden nix
- frames = 1*fps
- for i in range(0, frames):
- givenFrame += 1
- yield (
- ('bg', 'attr', '{http://www.w3.org/1999/xlink}href', "given-frames/frame%04d.png" % (givenFrame)),
- ('layer1', 'style', 'opacity', "%.4f" % 0), # nix
- # ('text', 'attr', 'transform', 'translate(%.4f, 0)' % easeOutQuad(i, move, -move, frames)),
- )
-
- # 1 Sekunde Text Fadein
- frames = 1*fps
- for i in range(0, frames):
- givenFrame += 1
- yield (
- ('bg', 'attr', '{http://www.w3.org/1999/xlink}href', "given-frames/frame%04d.png" % (givenFrame)),
- ('layer1', 'style', 'opacity', "%.4f" % easeLinear(i, 0, 1, frames)),
- # ('text', 'attr', 'transform', 'translate(%.4f, 0)' % easeOutQuad(i, move, -move, frames)),
- )
-
- # 5 Sekunden Text
- frames = 5*fps
- for i in range(0, frames):
- givenFrame += 1
- yield (
- ('bg', 'attr', '{http://www.w3.org/1999/xlink}href', "given-frames/frame%04d.png" % (givenFrame)),
- ('layer1', 'style', 'opacity', "%.4f" %1),
- # ('text', 'attr', 'transform', 'translate(%.4f, 0)' % easeOutQuad(i, move, -move, frames)),
- )
-
-def outroFrames(p):
- xml = etree.parse('god2024/artwork/outro.svg').getroot()
-
- frames = int(5*fps)
- for i in range(0, frames):
- yield ()
-
-def pauseFrames(p):
- # 1 sekunden fade in
- frames = 1*fps
- for i in range(0, frames):
- yield (
- ('text1', 'style', 'opacity', "%.4f" % easeLinear(i, 0, 1, frames)),
- )
-
- # 1 sekunde sehen
- for i in range(0, frames):
- yield (
- ('text1', 'style', 'opacity', "%.4f" % 1),
- )
-
- # 1 sekunde fadeout
- for i in range(0, frames):
- yield (
- ('text1', 'style', 'opacity', "%.4f" % easeLinear(i, 1, -1, frames)),
- )
-
- # 1 sekunde bild
- for i in range(0, frames):
- yield (
- ('text1', 'style', 'opacity', "%.4f" % 0),
- )
-
-def debug():
- render(
- 'intro.svg',
- '../intro.ts',
- introFrames,
- {
- '$id': 65,
- '$title': 'OWASP Juice Shop 10th anniversary: Is it still fresh?'.upper(),
- '$subtitle': '',
- '$personnames': 'Jannik Hollenbach'.upper(),
- #'only_render_frame': 353
- 'only_rerender_frames_after': 225
- }
- )
-
- # render(
- # 'pause.svg',
- # '../pause.ts',
- # pauseFrames
- # )
-
-def tasks(queue, args, id_list, skip_list):
- if not 'outro' in skip_list:
- # place a task for the outro into the queue
- queue.put(Rendertask(
- infile = 'outro.svg',
- outfile = 'outro.ts',
- sequence = outroFrames
- ))
-
- if not 'pause' in skip_list:
- # place the pause-sequence into the queue
- queue.put(Rendertask(
- infile = 'pause.svg',
- outfile = 'pause.ts',
- sequence = pauseFrames
- ))
-
- # iterate over all events extracted from the schedule xml-export
- for event in events(scheduleUrl, titlemap):
-
- # skip events which will not be recorded
- if event['room'] not in ('Da Capo',) or event['track'] == 'Nomnom':
- print("skipping room %s (%s [%s])" % (event['room'], event['title'], event['id']))
- continue
-
- # when id_list is not empty, only render events which are in id_list
- if id_list and int(event['id']) not in id_list:
- print("skipping id (%s [%s])" % (event['title'], event['id']))
- continue
-
- # generate a task description and put them into the queue
- queue.put(Rendertask(
- infile = 'intro.svg',
- outfile = str(event['id']) + ".ts",
- sequence = introFrames,
- parameters = {
- '$id': event['id'],
- '$title': event['title'].upper(),
- '$subtitle': event['subtitle'],
- '$personnames': event['personnames'].upper(),
- }
- ))
-
diff --git a/god2024/artwork/intro.svg b/god2024/artwork/intro.svg
deleted file mode 100644
index 4a79ff0..0000000
--- a/god2024/artwork/intro.svg
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
-
diff --git a/god2024/artwork/outro.svg b/god2024/artwork/outro.svg
deleted file mode 100644
index 8097ff8..0000000
--- a/god2024/artwork/outro.svg
+++ /dev/null
@@ -1,131 +0,0 @@
-
-
-
-
diff --git a/god2024/artwork/pause.svg b/god2024/artwork/pause.svg
deleted file mode 100644
index 6dd0f4e..0000000
--- a/god2024/artwork/pause.svg
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
-
diff --git a/jh19-berlin/config.ini b/jh19-berlin/config.ini
index 73365a8..6a8740d 100644
--- a/jh19-berlin/config.ini
+++ b/jh19-berlin/config.ini
@@ -1,13 +1,15 @@
-[meta]
+[default]
schedule = https://projects.alpaka.space/media/jhber19-schedule.xml
template = intro-alpha.mov
alpha = true
prores = true
-inout_type = n
+fontfile = true
+inout = n
[title]
in = 175
out = 260
+fontfamily =
fontfile = SourceSansPro-Bold.otf
fontsize = 90
fontcolor = #ffffff
@@ -17,6 +19,7 @@ y = 450
[speaker]
in = 175
out = 260
+fontfamily =
fontfile = SourceSansPro-Regular.otf
fontsize = 36
fontcolor = #ffffff
@@ -26,10 +29,11 @@ y = 900
[text]
in = 200
out = 250
+fontfamily =
fontfile = SourceSansPro-Regular.otf
fontsize = 45
fontcolor = #c68100
x = (w-text_w)/2
y = 927
-; text =
+text = ''
diff --git a/jh20-jue/config.ini b/jh20-jue/config.ini
index 05878cf..163ca71 100644
--- a/jh20-jue/config.ini
+++ b/jh20-jue/config.ini
@@ -1,35 +1,37 @@
-[meta]
+[default]
schedule = https://releasing.c3voc.de/releases/jhjue2020/schedule-jhjue20.xml
; intro_template.ts was derived from a png which was derived from jhjue-20-intro.svg
; ffmpeg -loop 1 -i jh20-jue/jh20-jue-intro.png -ar 48000 -ac 2 -f s16le -i /dev/zero -c:v mpeg2video -pix_fmt:v yuv420p -qscale:v 2 -qmin:v 2 -qmax:v 7 -keyint_min 0 -bf 0 -g 0 -intra:0 -maxrate:0 90M -c:a mp2 -b:a 384k -t 5 jh20-jue/jh20-jue_intro_template.ts
template = jh20-jue_intro_template.ts
alpha = false
prores = false
+; enable using a font file
+fontfile = true
; in and out time format: t for seconds, n for frame number
inout = n
-;; Some font settings can have defaults, which can be overridden in the
-;; 'title', 'speaker' and 'text' sections below.
-[default]
-;; default font
-fontfile = ebisu.ttf
-;; default font color
-fontcolor = #ffffff
-
-;; fields for title and speaker names are empty in the template.ts, so we'll render them in via ffmpeg
+; fields for title and speaker names are empty in the template.ts, so we'll render them in via ffmpeg
[title]
; inframe for title
in = 20
-; outframe for title
+; outframe for totle
out = 225
+; title font (either font family or file, see default setting above)
+fontfamily =
+fontfile = ebisu.ttf
; title font size
fontsize = 70
+; title color
+fontcolor = #ffffff
+; title position from upper left corner
x = 600
y = 865
[speaker]
in = 40
out = 225
+fontfamily =
+fontfile = ebisu.ttf
fontsize = 40
fontcolor = #eeeeee
x = 600
@@ -39,10 +41,13 @@ y = 950
[text]
in = 3
out = 4
+fontfamily =
+fontfile = ebisu.ttf
fontsize = 45
+fontcolor = #ffffff
x = 640
y = 1000
-; text =
+text = ''
; build intros via
diff --git a/jh21-rn/config.ini b/jh21-rn/config.ini
index 626db55..a7870db 100644
--- a/jh21-rn/config.ini
+++ b/jh21-rn/config.ini
@@ -1,13 +1,15 @@
-[meta]
+[default]
schedule = https://pretalx.c3voc.de/jugend-hackt-rhein-neckar-2021/schedule/export/schedule.xml
template = jh21-rn-template.ts
alpha = false
prores = false
-inout_type = t
+fontfile = true
+inout = t
[title]
in = 1
out = 6.5
+fontfamily =
fontfile = SourceSansPro-Semibold.ttf
fontsize = 85
fontcolor = #ffffff
@@ -17,6 +19,7 @@ y = 877
[speaker]
in = 2
out = 6.5
+fontfamily =
fontfile = SourceSansPro-Semibold.ttf
fontsize = 45
fontcolor = #ffffff
@@ -26,10 +29,11 @@ y = 954
[text]
in = 3
out = 6.5
+fontfamily =
fontfile = SourceSansPro-Semibold.ttf
fontsize = 45
fontcolor = #ffffff
x = 1920
y = 1080
-; text =
+text = ''
diff --git a/jugendhackt/config.ini b/jugendhackt/config.ini
index 94180a5..2400940 100644
--- a/jugendhackt/config.ini
+++ b/jugendhackt/config.ini
@@ -1,52 +1,39 @@
-[meta]
-schedule = https://pretalx.c3voc.de/jhhh23/schedule/export/schedule.xml
-;; path to background video
-template = intro-background.ts
-;; whether background video uses transparency (needs to be .mov)
-alpha = false
-;; whether background video is prores 4444
-prores = false
-;; in and out time format: t for seconds, n for frame number
-inout_type = t
-;; fade-in duration (seconds), leave out or set to zero to disable
-;fade_duration = 0.5
-
-;; Some font settings can have defaults, which can be overridden in the
-;; 'title', 'speaker' and 'text' sections below.
[default]
-;; default font
-fontfile = SourceSansPro-Semibold.ttf
-;; default font color
-fontcolor = #ffffff
+schedule = https://pretalx.c3voc.de/jhhh23/schedule/export/schedule.xml
+template = intro-background.ts
+alpha = false
+prores = false
+fontfile = true
+inout = t
-;; fields for title and speaker names are empty in the template.ts, so we'll render them in via ffmpeg
-;; parameters are:
-;; - in: start frame/ time
-;; - out: end frame/ time
-;; - fontfile: font file
-;; - fontcolor: font color
-;; - fontsize: font size (pixel)
-;; - x: horizontal position (top left corner)
-;; - y: vertical position (top left corner)
[title]
in = 1
out = 9.5
+fontfamily =
+fontfile = SourceSansPro-Semibold.ttf
fontsize = 67
+fontcolor = #ffffff
x = 400
y = 870
[speaker]
in = 2
out = 9
+fontfamily =
+fontfile = SourceSansPro-Semibold.ttf
fontsize = 50
+fontcolor = #ffffff
x = 400
y = 950
-;; optional extra text, comment out "text" field to disable
+
[text]
in = 0
out = 0
+fontfamily =
+fontfile = SourceSansPro-Semibold.ttf
fontsize = 0
+fontcolor = #ffffff
x = 0
y = 0
-;text = some additional text
+text = ''
diff --git a/jugendhackt/source.xcf b/jugendhackt/source.xcf
deleted file mode 100644
index e01939d..0000000
Binary files a/jugendhackt/source.xcf and /dev/null differ
diff --git a/make-apple-motion.py b/make-apple-motion.py
index c2940e9..1d4c631 100755
--- a/make-apple-motion.py
+++ b/make-apple-motion.py
@@ -11,6 +11,8 @@ import sys
import os
import re
+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',
@@ -55,20 +57,6 @@ parser.add_argument('--num-audio-streams', dest='naudio', type=int, default=1, h
number of audio-streams to generate. defaults to 1
''')
-parser.add_argument('--no-cleanup', action='store_true', help='''
- keep temp-dir for debugging purposes
- ''')
-
-parser.add_argument('--snapshot-sec', type=int, default=3, help='''
- number of seconds into the final clip when to take a snapshot (for inspection purposes or as thumbnail)
- ''')
-
-parser.add_argument('--setting-path', default='hd1080p.compressorsetting', help='''
- filename in the script-dir (where this python script resides),
- the work-dir (where the .motn-file resides) or absolute path to
- a .compressorsetting file
- ''')
-
args = parser.parse_args()
@@ -113,22 +101,9 @@ def describe_event(event):
def event_print(event, message):
print("{} – {}".format(describe_event(event), message))
-def find_settingpath():
- artwork_dir = os.path.dirname(args.motn)
- setting_path = os.path.join(artwork_dir, args.setting_path)
- if os.path.exists(setting_path):
- return setting_path
-
- setting_path = os.path.join(os.path.dirname(__file__), args.setting_path)
- if os.path.exists(setting_path):
- return setting_path
-
- return args.setting_path
-
tempdir = tempfile.TemporaryDirectory()
print('working in ' + tempdir.name)
-settingpath = find_settingpath()
def fmt_command(command, **kwargs):
@@ -153,13 +128,6 @@ def run_output(command, **kwargs):
os.system(f'{cmd} >{t.name} 2>&1')
return t.read().decode('utf-8')
-def xmlescape(xml):
- xml = xml.replace("&", "&")
- xml = xml.replace("<", "<")
- xml = xml.replace(">", ">")
- xml = xml.replace("\"", """)
- xml = xml.replace("'", "'")
- return xml
def enqueue_job(event):
event_id = str(event['id'])
@@ -176,11 +144,10 @@ def enqueue_job(event):
fp.write(xmlstr)
compressor_info = run_output(
- '/Applications/Compressor.app/Contents/MacOS/Compressor -batchname {batchname} -jobpath {jobpath} -settingpath {settingpath} -locationpath {locationpath}',
+ '/Applications/Compressor.app/Contents/MacOS/Compressor -batchname {batchname} -jobpath {jobpath} -settingpath hd1080p.compressorsetting -locationpath {locationpath}',
batchname=describe_event(event),
jobpath=work_doc,
- locationpath=intermediate_clip,
- settingpath=settingpath)
+ locationpath=intermediate_clip)
match = re.search(r"", compressor_info)
if not match:
@@ -235,7 +202,6 @@ def finalize_job(job_id, event):
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')
- snapshot_file = os.path.join(os.path.dirname(args.motn), event_id + '.png')
shutil.copy(intermediate_clip, copy_clip)
@@ -245,11 +211,6 @@ def finalize_job(job_id, event):
vcodec=args.vcodec,
acodec=args.acodec)
- run('ffmpeg -y -hide_banner -loglevel error -i {input} -ss {snapshot_sec} -frames:v 1 -vf scale="iw*sar:ih" -f image2 -y -c png {output}',
- input=intermediate_clip,
- output=snapshot_file,
- snapshot_sec=str(args.snapshot_sec))
-
event_print(event, "finalized intro to " + final_clip)
@@ -289,9 +250,5 @@ while len(active_jobs) > 0:
finalize_job(job_id, event)
-if args.no_cleanup:
- print('all done, *NOT* cleaning up, *TEMPFILES REMAIN* in ' + tempdir.name)
-
-else:
- print('all done, cleaning up ' + tempdir.name)
- tempdir.cleanup()
+print('all done, cleaning up ' + tempdir.name)
+tempdir.cleanup()
diff --git a/make-ffmpeg-fade.py b/make-ffmpeg-fade.py
index 3c1eecd..f4e88a3 100755
--- a/make-ffmpeg-fade.py
+++ b/make-ffmpeg-fade.py
@@ -1,5 +1,329 @@
#!/usr/bin/env python3
# vim: tabstop=4 shiftwidth=4 expandtab
-print("ERROR: The functionality of this script was added to 'make-ffmpeg.py'!")
-print("Specify meta.fade_duration = 0.5 in the config.ini for the same effect.")
+import os
+import sys
+import subprocess
+import renderlib
+import argparse
+import shlex
+from PIL import ImageFont
+from configparser import ConfigParser
+import json
+
+# Parse arguments
+parser = argparse.ArgumentParser(
+ description='C3VOC Intro-Outro-Generator - Variant which renders only using video filters in ffmpeg',
+ usage="./make-ffmpeg.py yourproject/",
+ formatter_class=argparse.RawTextHelpFormatter)
+
+parser.add_argument('project', action="store", metavar='Project folder', type=str, help='''
+ Path to your project folder
+ ''')
+
+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.
+ This argument must not be used together with --id
+ Usage: ./make-ffmpeg.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
+ ''')
+
+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('--skip', nargs='+', action="store", type=str, help='''
+ Skip ID(s) not needed to be rendered.
+ Usage: ./make-ffmpeg.py yourproject/ --skip 4711 0815 4223 1337
+ ''')
+
+parser.add_argument('--force', action="store_true", default=False, help='''
+ Force render if file exists.
+ ''')
+
+args = parser.parse_args()
+
+if (args.skip is None):
+ args.skip = []
+
+
+def headline(str):
+ print("##################################################")
+ print(str)
+ print("##################################################")
+ print()
+
+
+def error(str):
+ headline(str)
+ parser.print_help()
+ sys.exit(1)
+
+
+cparser = ConfigParser()
+cparser.read(os.path.join(os.path.dirname(args.project), 'config.ini'))
+template = cparser['default']['template']
+alpha = cparser['default']['alpha']
+prores = cparser['default']['prores']
+
+fade_duration = 0.5
+
+title_in = float(cparser['title']['in'])
+title_out = float(cparser['title']['out'])
+title_duration = title_out - title_in
+title_font = cparser['title']['font']
+title_fontsize = int(cparser['title']['fontsize'])
+title_fontcolor = cparser['title']['fontcolor']
+title_x = int(cparser['title']['x'])
+title_y = int(cparser['title']['y'])
+
+speaker_in = float(cparser['speaker']['in'])
+speaker_out = float(cparser['speaker']['out'])
+speaker_duration = speaker_out - speaker_in
+speaker_font = cparser['speaker']['font']
+speaker_fontsize = int(cparser['speaker']['fontsize'])
+speaker_fontcolor = cparser['speaker']['fontcolor']
+speaker_x = int(cparser['speaker']['x'])
+speaker_y = int(cparser['speaker']['y'])
+
+text_in = float(cparser['text']['in'])
+text_out = float(cparser['text']['out'])
+text_duration = text_out - text_in
+text_font = cparser['text']['font']
+text_fontsize = int(cparser['text']['fontsize'])
+text_fontcolor = cparser['text']['fontcolor']
+text_x = int(cparser['text']['x'])
+text_y = int(cparser['text']['y'])
+text_text = cparser['text']['text']
+
+font_t = os.path.join(os.path.dirname(args.project), title_font)
+font_s = os.path.join(os.path.dirname(args.project), speaker_font)
+font_tt = os.path.join(os.path.dirname(args.project), text_font)
+
+fileformat = os.path.splitext(template)[1]
+infile = os.path.join(os.path.dirname(args.project), template)
+
+schedule = cparser['default']['schedule']
+
+if not (os.path.exists(os.path.join(args.project, template))):
+ error("Template file {} in Project Path is missing".format(template))
+
+for ffile in (title_font, speaker_font, text_font):
+ if not (os.path.exists(os.path.join(args.project, ffile))):
+ error("Font file {} in Project Path is missing".format(ffile))
+
+if not (os.path.exists(os.path.join(args.project, 'config.ini'))):
+ error("config.ini file in Project Path is missing")
+
+if alpha == 'true' and not fileformat == '.mov':
+ error("Alpha can only be rendered with .mov source files")
+
+if not args.project:
+ error("The Project Path is a required argument")
+
+if not args.debug and not schedule:
+ error("Either specify --debug or supply a schedule in config.ini")
+
+if args.debug:
+ persons = ['Thomas Roth', 'Dmitry Nedospasov', 'Josh Datko']
+ events = [{
+ 'id': 'debug',
+ 'title': 'wallet.fail',
+ 'subtitle': 'Hacking the most popular cryptocurrency hardware wallets',
+ 'persons': persons,
+ 'personnames': ', '.join(persons),
+ 'room': 'Borg',
+ }]
+
+else:
+ events = list(schedulelib.events(schedule))
+
+
+def describe_event(event):
+ return "#{}: {}".format(event['id'], event['title'])
+
+
+def event_print(event, message):
+ print("{} – {}".format(describe_event(event), message))
+
+
+def fmt_command(command, **kwargs):
+ args = {}
+ for key, value in kwargs.items():
+ args[key] = shlex.quote(value)
+
+ command = command.format(**args)
+ return shlex.split(command)
+
+
+def run(command, **kwargs):
+ return subprocess.check_call(
+ fmt_command(command, **kwargs),
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.DEVNULL)
+
+
+def fit_text(string: str, frame_width):
+ split_line = [x.strip() for x in string.split()]
+ lines = ""
+ w = 0
+ line_num = 0
+ line = ""
+ for word in split_line:
+ w, _ = translation_font.getsize(" ".join([line, word]))
+ print("{}, {}".format(w, line))
+ if w > (frame_width):
+ print("too wide, breaking")
+ lines += line.strip() + "\n"
+ line = ""
+
+ line += word + " "
+
+ lines += line.strip()
+ return lines
+
+
+def fit_title(string: str):
+ global translation_font
+ translation_font = ImageFont.truetype(font_t, size=title_fontsize-10, encoding="unic")
+ title = fit_text(string, 1080)
+
+ return title
+
+
+def fit_speaker(string: str):
+ global translation_font
+ translation_font = ImageFont.truetype(font_s, size=speaker_fontsize-10, encoding="unic")
+ speaker = fit_text(string, 1080)
+
+ return speaker
+
+
+def enqueue_job(event):
+ event_id = str(event['id'])
+ if event_id in args.skip:
+ event_print(event, "skipping " + str(event['id']))
+ return
+ 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
+
+ event_title = str(event['title'])
+ event_personnames = str(event['personnames'])
+ event_title = event_title.replace('"', '')
+ event_title = event_title.replace('\'', '')
+ event_personnames = event_personnames.replace('"', '')
+
+ t = fit_title(event_title)
+ s = fit_speaker(event_personnames)
+ print(s)
+
+ if args.debug:
+ print('Title: ', t)
+ print('Speaker: ', s)
+
+ outfile = os.path.join(os.path.dirname(args.project), event_id + '.ts')
+
+ videofilter = "drawtext=fontfile={fontfile}:fontsize={fontsize}:fontcolor={fontcolor}:x={x}:y={y}:text='{text}':".format(
+ fontfile=font_t,
+ fontsize=title_fontsize,
+ fontcolor=title_fontcolor,
+ x=title_x,
+ y=title_y,
+ text=t)
+ videofilter += "alpha='if(lt(t,{fade_in_start_time}),0,if(lt(t,{fade_in_end_time}),(t-{fade_in_start_time})/{fade_duration},if(lt(t,{fade_out_start_time}),1,if(lt(t,{fade_out_end_time}),({fade_duration}-(t-{fade_out_start_time}))/{fade_duration},0))))',".format(
+ fade_in_start_time=title_in,
+ fade_in_end_time=title_in + fade_duration,
+ fade_out_start_time=title_in + fade_duration + title_duration,
+ fade_out_end_time=title_in + fade_duration + title_duration + fade_duration,
+ fade_duration=fade_duration
+ )
+ videofilter += "drawtext=fontfile={fontfile}:fontsize={fontsize}:fontcolor={fontcolor}:x={x}:y={y}:text='{text}':".format(
+ fontfile=font_s,
+ fontsize=speaker_fontsize,
+ fontcolor=speaker_fontcolor,
+ x=speaker_x,
+ y=speaker_y,
+ text=s)
+ videofilter += "alpha='if(lt(t,{fade_in_start_time}),0,if(lt(t,{fade_in_end_time}),(t-{fade_in_start_time})/{fade_duration},if(lt(t,{fade_out_start_time}),1,if(lt(t,{fade_out_end_time}),({fade_duration}-(t-{fade_out_start_time}))/{fade_duration},0))))',".format(
+ fade_in_start_time=speaker_in,
+ fade_in_end_time=speaker_in + fade_duration,
+ fade_out_start_time=speaker_in + fade_duration + speaker_duration,
+ fade_out_end_time=speaker_in + fade_duration + speaker_duration + fade_duration,
+ fade_duration=fade_duration
+ )
+ videofilter += "drawtext=fontfile={fontfile}:fontsize={fontsize}:fontcolor={fontcolor}:x={x}:y={y}:text={text}:".format(
+ fontfile=font_tt,
+ fontsize=text_fontsize,
+ fontcolor=text_fontcolor,
+ x=text_x,
+ y=text_y,
+ text=text_text)
+ videofilter += "alpha='if(lt(t,{fade_in_start_time}),0,if(lt(t,{fade_in_end_time}),(t-{fade_in_start_time})/{fade_duration},if(lt(t,{fade_out_start_time}),1,if(lt(t,{fade_out_end_time}),({fade_duration}-(t-{fade_out_start_time}))/{fade_duration},0))))'".format(
+ fade_in_start_time=text_in,
+ fade_in_end_time=text_in + fade_duration,
+ fade_out_start_time=text_in + fade_duration + text_duration,
+ fade_out_end_time=text_in + fade_duration + text_duration + fade_duration,
+ fade_duration=fade_duration
+ )
+
+ if fileformat == '.mov':
+ if alpha == 'true':
+ if prores == 'true':
+ cmd = 'ffmpeg -y -i "{0}" -vf "{1}" -vcodec prores_ks -pix_fmt yuva444p10le -profile:v 4444 -shortest -movflags faststart -f mov "{2}"'.format(
+ infile, videofilter, outfile)
+ else:
+ cmd = 'ffmpeg -y -i "{0}" -vf "{1}" -shortest -c:v qtrle -movflags faststart -f mov "{2}"'.format(
+ infile, videofilter, outfile)
+ else:
+ cmd = 'ffmpeg -y -i "{0}" -vf "{1}" -map 0:0 -c:v mpeg2video -q:v 2 -aspect 16:9 -map 0:1 -c:a mp2 -b:a 384k -shortest -f mpegts "{2}"'.format(
+ infile, videofilter, outfile)
+ else:
+ cmd = 'ffmpeg -y -i "{0}" -vf "{1}" -map 0:0 -c:v mpeg2video -pix_fmt:v yuv420p -qscale:v 2 -qmin:v 2 -qmax:v 7 -keyint_min 0 -bf 0 -g 0 -maxrate:0 90M -aspect 16:9 -map 0:1 -c:a mp2 -b:a 384k -shortest -f mpegts "{2}"'.format(
+ infile, videofilter, outfile)
+
+ if args.debug:
+ print(cmd)
+
+ run(cmd)
+
+ return event_id
+
+
+if args.ids:
+ if len(args.ids) == 1:
+ print("enqueuing {} job".format(len(args.ids)))
+ else:
+ print("enqueuing {} jobs".format(len(args.ids)))
+else:
+ if len(events) == 1:
+ print("enqueuing {} job".format(len(events)))
+ else:
+ print("enqueuing {} jobs".format(len(events)))
+
+
+for event in events:
+ 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
+
+ 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
+
+
+print('all done')
diff --git a/make-ffmpeg.py b/make-ffmpeg.py
index 94d1514..fe4b745 100755
--- a/make-ffmpeg.py
+++ b/make-ffmpeg.py
@@ -1,167 +1,155 @@
#!/usr/bin/env python3
# vim: tabstop=4 shiftwidth=4 expandtab
-"""See jugendhackt/config.ini for some config file documentation."""
-
import os
import sys
import subprocess
-import argparse
-import ssl
-from configparser import ConfigParser
-from pathlib import PurePath
-import platform
-
-from PIL import ImageFont
import schedulelib
+import argparse
+import shlex
+from PIL import ImageFont
+from configparser import ConfigParser
+import json
+import platform
+import ssl
ssl._create_default_https_context = ssl._create_unverified_context
-FRAME_WIDTH = 1920
+# Parse arguments
+parser = argparse.ArgumentParser(
+ description='C3VOC Intro-Outro-Generator - Variant which renders only using video filters in ffmpeg',
+ usage="./make-ffmpeg.py yourproject/",
+ formatter_class=argparse.RawTextHelpFormatter)
+
+parser.add_argument('project', action="store", metavar='Project folder', type=str, help='''
+ Path to your project folder
+ ''')
+
+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.
+ This argument must not be used together with --id
+ Usage: ./make-ffmpeg.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
+ ''')
+
+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('--skip', nargs='+', action="store", type=str, help='''
+ Skip ID(s) not needed to be rendered.
+ Usage: ./make-ffmpeg.py yourproject/ --skip 4711 0815 4223 1337
+ ''')
+
+parser.add_argument('--force', action="store_true", default=False, help='''
+ Force render if file exists.
+ ''')
+
+args = parser.parse_args()
+
+if (args.skip is None):
+ args.skip = []
-class TextConfig:
- inpoint: float
- outpoint: float
- x: int
- y: int
-
- fontfile_path: str
- fontsize: int
- fontcolor: str
- bordercolor: str = None # border is added, if a color is set
-
- def uses_fontfile(self):
- return self.fontfile_path is not None
-
- def parse(self, cparser_sect, default_fontfile, default_fontcolor):
- self.inpoint = cparser_sect.getfloat('in')
- self.outpoint = cparser_sect.getfloat('out')
- self.x = cparser_sect.getint('x')
- self.y = cparser_sect.getint('y')
-
- self.fontcolor = cparser_sect.get('fontcolor', default_fontcolor)
-
- fontfile = cparser_sect.get('fontfile', default_fontfile)
- self.fontfile_path = str(PurePath(args.project, fontfile).as_posix())
-
- if not os.path.exists(self.fontfile_path):
- error("Font file {} in Project Path is missing".format(self.fontfile_path))
-
- self.fontsize = cparser_sect.getint('fontsize')
- self.bordercolor = cparser_sect.get('bordercolor', None)
-
- def fit_text(self, text: str) -> list[str]:
- if not text:
- return [""]
-
- font = ImageFont.truetype(
- self.fontfile_path, size=self.fontsize, encoding="unic")
-
- return fit_text(text, (FRAME_WIDTH-self.x-100), font)
-
- def get_ffmpeg_filter(self, inout_type: str, fade_time: float, text: list[str]):
- if not text:
- return ""
-
- text_duration = self.outpoint - self.inpoint
- filter_str = ""
- for idx, line in enumerate(text):
- filter_str += "drawtext=enable='between({},{},{})':x={}:y={}".format(
- inout_type, self.inpoint, self.outpoint, self.x, self.y + (idx*self.fontsize))
-
- filter_str += ":fontfile='{}':fontsize={}:fontcolor={}:text={}".format(
- self.fontfile_path, self.fontsize, self.fontcolor, ffmpeg_escape_str(line))
-
- if self.bordercolor is not None:
- filter_str += ":borderw={}:bordercolor={}".format(
- self.fontsize / 30, self.bordercolor)
-
- if fade_time > 0:
- filter_str += ":alpha='if(lt(t,{fade_in_start_time}),0,if(lt(t,{fade_in_end_time}),(t-{fade_in_start_time})/{fade_duration},if(lt(t,{fade_out_start_time}),1,if(lt(t,{fade_out_end_time}),({fade_duration}-(t-{fade_out_start_time}))/{fade_duration},0))))'".format(
- fade_in_start_time=self.inpoint,
- fade_in_end_time=self.inpoint + fade_time,
- fade_out_start_time=self.inpoint + fade_time + text_duration,
- fade_out_end_time=self.inpoint + fade_time + text_duration + fade_time,
- fade_duration=fade_time)
-
- filter_str += ","
-
- return filter_str[:-1]
-
-
-class Config:
- schedule: str
- template_file: str # video background
- alpha: bool = False
- prores: bool = False
- inout_type: str = "t" # in and out time format: t for seconds, n for frame number
- fade_duration: float = 0 # fade duration in seconds, 0 to disable
-
- fileext: str
-
- title: TextConfig
- speaker: TextConfig
- text: TextConfig
- extra_text: str = "" # additional text
-
-
-def parse_config(filename) -> Config:
- if not os.path.exists(filename):
- error("config.ini file in Project Path is missing")
-
- conf = Config()
-
- cparser = ConfigParser()
- cparser.read(filename)
-
- meta = cparser['meta']
- conf.schedule = meta.get('schedule')
- infile = PurePath(args.project, meta.get('template'))
- conf.template_file = str(infile)
- conf.alpha = meta.getboolean('alpha', conf.alpha)
- conf.prores = meta.getboolean('prores', conf.prores)
- conf.inout_type = meta.get('inout_type', conf.inout_type)
- conf.fade_duration = meta.getfloat('fade_duration', conf.fade_duration)
-
- defaults = cparser['default']
- default_fontfile = defaults.get('fontfile', None)
- default_fontcolor = defaults.get('fontcolor', "#ffffff")
-
- conf.title = TextConfig()
- conf.title.parse(cparser['title'], default_fontfile, default_fontcolor)
- conf.speaker = TextConfig()
- conf.speaker.parse(cparser['speaker'], default_fontfile, default_fontcolor)
- conf.text = TextConfig()
- conf.text.parse(cparser['text'], default_fontfile, default_fontcolor)
-
- conf.extra_text = cparser['text'].get('text', '')
-
- conf.fileext = infile.suffix
-
- if not os.path.exists(conf.template_file):
- error("Template file {} in Project Path is missing".format(conf.template_file))
-
- if conf.alpha and conf.fileext != '.mov':
- error("Alpha can only be rendered with .mov source files")
-
- if not args.project:
- error("The Project Path is a required argument")
-
- if not args.debug and not conf.schedule:
- error("Either specify --debug or supply a schedule in config.ini")
-
- return conf
-
-
-def error(err_str):
+def headline(str):
print("##################################################")
- print(err_str)
+ print(str)
print("##################################################")
print()
+
+
+def error(str):
+ headline(str)
parser.print_help()
sys.exit(1)
+cparser = ConfigParser()
+cparser.read(os.path.join(os.path.dirname(args.project), 'config.ini'))
+template = cparser['default']['template']
+alpha = cparser['default']['alpha']
+prores = cparser['default']['prores']
+fontfile = cparser['default']['fontfile'] # use a font file instead of a font family
+inout = cparser['default']['inout'] # in and out time format: t for seconds, n for frame number
+
+title_in = cparser['title']['in']
+title_out = cparser['title']['out']
+title_fontfamily = cparser['title']['fontfamily']
+title_fontfile = cparser['title']['fontfile']
+title_fontsize = cparser['title']['fontsize']
+title_fontcolor = cparser['title']['fontcolor']
+title_x = cparser['title']['x']
+title_y = cparser['title']['y']
+
+speaker_in = cparser['speaker']['in']
+speaker_out = cparser['speaker']['out']
+speaker_fontfamily = cparser['speaker']['fontfamily']
+speaker_fontfile = cparser['speaker']['fontfile']
+speaker_fontsize = cparser['speaker']['fontsize']
+speaker_fontcolor = cparser['speaker']['fontcolor']
+speaker_x = cparser['speaker']['x']
+speaker_y = cparser['speaker']['y']
+
+text_in = cparser['text']['in']
+text_out = cparser['text']['out']
+text_fontfamily = cparser['text']['fontfamily']
+text_fontfile = cparser['text']['fontfile']
+text_fontsize = cparser['text']['fontsize']
+text_fontcolor = cparser['text']['fontcolor']
+text_x = cparser['text']['x']
+text_y = cparser['text']['y']
+text_text = cparser['text']['text']
+
+font_t = os.path.join(os.path.dirname(args.project), title_fontfile)
+font_s = os.path.join(os.path.dirname(args.project), speaker_fontfile)
+font_tt = os.path.join(os.path.dirname(args.project), text_fontfile)
+
+fileformat = os.path.splitext(template)[1]
+infile = os.path.join(os.path.dirname(args.project), template)
+
+schedule = cparser['default']['schedule']
+
+if not (os.path.exists(os.path.join(args.project, template))):
+ error("Template file {} in Project Path is missing".format(template))
+
+for ffile in (title_fontfile, speaker_fontfile, text_fontfile):
+ if not (os.path.exists(os.path.join(args.project, ffile))):
+ error("Font file {} in Project Path is missing".format(ffile))
+
+if not (os.path.exists(os.path.join(args.project, 'config.ini'))):
+ error("config.ini file in Project Path is missing")
+
+if alpha == 'true' and not fileformat == '.mov':
+ error("Alpha can only be rendered with .mov source files")
+
+if not args.project:
+ error("The Project Path is a required argument")
+
+if not args.debug and not schedule:
+ error("Either specify --debug or supply a schedule in config.ini")
+
+if args.debug:
+ persons = ['Thomas Roth', 'Dmitry Nedospasov', 'Josh Datko',]
+ events = [{
+ 'id': 'debug',
+ 'title': 'wallet.fail',
+ 'subtitle': 'Hacking the most popular cryptocurrency hardware wallets',
+ 'persons': persons,
+ 'personnames': ', '.join(persons),
+ 'room': 'Borg',
+ }]
+
+else:
+ events = list(schedulelib.events(schedule))
+
+
def describe_event(event):
return "#{}: {}".format(event['id'], event['title'])
@@ -170,184 +158,164 @@ def event_print(event, message):
print("{} – {}".format(describe_event(event), message))
-def fit_text(string: str, max_width: int, font: ImageFont) -> list[str]:
- """Break text into array of strings which fit certain a width (in pixels) for the specified font."""
+def fmt_command(command, **kwargs):
+ args = {}
+ for key, value in kwargs.items():
+ args[key] = shlex.quote(value)
+ command = command.format(**args)
+ return shlex.split(command)
+
+
+def run(command, **kwargs):
+ return subprocess.check_call(
+ fmt_command(command, **kwargs),
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.DEVNULL)
+
+
+def fit_text(string: str, frame_width):
split_line = [x.strip() for x in string.split()]
- lines = []
+ lines = ""
w = 0
- line = []
+ line_num = 0
+ line = ""
for word in split_line:
- new_line = line + [word.rstrip(':')]
- w = font.getlength(" ".join(new_line))
- if w > max_width:
- lines.append(' '.join(line))
- line = []
+ left, top, right, bottom = translation_font.getbbox(" ".join([line, word]))
+ width, height = right - left, bottom - top
+ if width > (frame_width - (2 * 6)):
+ lines += line.strip() + "\n"
+ line = ""
- line.append(word.rstrip(':'))
-
- if word.endswith(':'):
- lines.append(' '.join(line))
- line = []
-
- if line:
- lines.append(' '.join(line))
+ line += word + " "
+ lines += line.strip()
return lines
-def ffmpeg_escape_str(text: str) -> str:
- # Escape according to https://ffmpeg.org/ffmpeg-filters.html#Notes-on-filtergraph-escaping
- # and don't put the string in quotes afterwards!
- text = text.replace(",", r"\,")
- text = text.replace(':', r"\\:")
- text = text.replace("'", r"\\\'")
+def fit_title(string: str, fontsize: int, x_offset: int):
+ global translation_font
+ translation_font = ImageFont.truetype(
+ font_t, size=fontsize, encoding="unic")
+ title = fit_text(string, (1920-x_offset-100))
- return text
+ return title
-def enqueue_job(conf: Config, event):
+def fit_speaker(string: str, fontsize: int, x_offset: int):
+ global translation_font
+ translation_font = ImageFont.truetype(
+ font_s, size=fontsize, encoding="unic")
+ speaker = fit_text(string, (1920-x_offset-100))
+
+ return speaker
+
+
+def enqueue_job(event):
event_id = str(event['id'])
-
- outfile = str(PurePath(args.project, event_id + '.ts'))
- outfile_mov = str(PurePath(args.project, event_id + '.mov'))
-
if event_id in args.skip:
event_print(event, "skipping " + str(event['id']))
return
- if (os.path.exists(outfile) or os.path.exists(outfile_mov)) and not args.force:
+ 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
event_title = str(event['title'])
event_personnames = str(event['personnames'])
+ event_title = event_title.replace('"', '\\"')
+ event_title = event_title.replace('\'', '')
+ event_personnames = event_personnames.replace('"', '\\"')
- title = conf.title.fit_text(event_title)
- speakers = conf.speaker.fit_text(event_personnames)
- extra_text = conf.text.fit_text(conf.extra_text)
+ t = fit_title(event_title, int(title_fontsize), int(title_x))
+ t = t.replace(':', "\:") # the ffmpeg command needs colons to be escaped
+ s = fit_speaker(event_personnames, int(speaker_fontsize), int(speaker_x))
if args.debug:
- print('Title: ', title)
- print('Speaker: ', speakers)
+ print('Title: ', t)
+ print('Speaker: ', s)
+
+ outfile = os.path.join(os.path.dirname(args.project), event_id + '.ts')
if platform.system() == 'Windows':
ffmpeg_path = './ffmpeg.exe'
+ font_t_win = "/".join(font_t.split("\\"))
+ font_s_win = "/".join(font_s.split("\\"))
+ font_tt_win = "/".join(font_tt.split("\\"))
else:
ffmpeg_path = 'ffmpeg'
- videofilter = conf.title.get_ffmpeg_filter(conf.inout_type, conf.fade_duration, title) + ","
- videofilter += conf.speaker.get_ffmpeg_filter(conf.inout_type,
- conf.fade_duration, speakers) + ","
- videofilter += conf.text.get_ffmpeg_filter(conf.inout_type, conf.fade_duration, extra_text)
-
- cmd = [ffmpeg_path, '-y', '-i', conf.template_file, '-vf', videofilter]
-
- if conf.fileext == '.mov' and conf.alpha:
- if conf.prores:
- cmd += ['-vcodec', 'prores_ks', '-pix_fmt', 'yuva444p10le', '-profile:v',
- '4444', '-shortest', '-movflags', 'faststart', '-f', 'mov', outfile_mov]
+ if fontfile == 'true':
+ if platform.system() == 'Windows':
+ videofilter = "drawtext=enable='between({8},{0},{1})':fontfile='{2}':fontsize={3}:fontcolor={4}:x={5}:y={6}:text='{7}',".format(
+ title_in, title_out, font_t_win, title_fontsize, title_fontcolor, title_x, title_y, t, inout)
+ videofilter += "drawtext=enable='between({8},{0},{1})':fontfile='{2}':fontsize={3}:fontcolor={4}:x={5}:y={6}:text='{7}',".format(
+ speaker_in, speaker_out, font_s_win, speaker_fontsize, speaker_fontcolor, speaker_x, speaker_y, s, inout)
+ videofilter += "drawtext=enable='between({8},{0},{1})':fontfile='{2}':fontsize={3}:fontcolor={4}:x={5}:y={6}:text='{7}'".format(
+ text_in, text_out, font_tt_win, text_fontsize, text_fontcolor, text_x, text_y, text_text, inout)
else:
- cmd += ['-shortest', '-c:v', 'qtrle', '-movflags',
- 'faststart', '-f', 'mov', outfile_mov]
+ videofilter = "drawtext=enable='between({8},{0},{1})':fontfile='{2}':fontsize={3}:fontcolor={4}:x={5}:y={6}:text='{7}',".format(
+ title_in, title_out, font_t, title_fontsize, title_fontcolor, title_x, title_y, t, inout)
+ videofilter += "drawtext=enable='between({8},{0},{1})':fontfile='{2}':fontsize={3}:fontcolor={4}:x={5}:y={6}:text='{7}',".format(
+ speaker_in, speaker_out, font_s, speaker_fontsize, speaker_fontcolor, speaker_x, speaker_y, s, inout)
+ videofilter += "drawtext=enable='between({8},{0},{1})':fontfile='{2}':fontsize={3}:fontcolor={4}:x={5}:y={6}:text='{7}'".format(
+ text_in, text_out, font_tt, text_fontsize, text_fontcolor, text_x, text_y, text_text, inout)
else:
- cmd += ['-map', '0:0', '-c:v', 'mpeg2video', '-q:v', '2', '-aspect', '16:9', '-map',
- '0:1', '-c:a', 'mp2', '-b:a', '384k', '-shortest', '-f', 'mpegts', outfile]
+ videofilter = "drawtext=enable='between({8},{0},{1})':font='{2}':fontsize={3}:fontcolor={4}:x={5}:y={6}:text='{7}',".format(
+ title_in, title_out, title_fontfamily, title_fontsize, title_fontcolor, title_x, title_y, t, inout)
+ videofilter += "drawtext=enable='between({8},{0},{1})':font='{2}':fontsize={3}:fontcolor={4}:x={5}:y={6}:text='{7}',".format(
+ speaker_in, speaker_out, speaker_fontfamily, speaker_fontsize, speaker_fontcolor, speaker_x, speaker_y, s, inout)
+ videofilter += "drawtext=enable='between({8},{0},{1})':font='{2}':fontsize={3}:fontcolor={4}:x={5}:y={6}:text='{7}'".format(
+ text_in, text_out, text_fontfamily, text_fontsize, text_fontcolor, text_x, text_y, text_text, inout)
+
+ if fileformat == '.mov':
+ if alpha == 'true':
+ if prores == 'true':
+ cmd = '{3} -y -i "{0}" -vf "{1}" -vcodec prores_ks -pix_fmt yuva444p10le -profile:v 4444 -shortest -movflags faststart -f mov "{2}"'.format(
+ infile, videofilter, outfile, ffmpeg_path)
+ else:
+ cmd = '{3} -y -i "{0}" -vf "{1}" -shortest -c:v qtrle -movflags faststart -f mov "{2}"'.format(
+ infile, videofilter, outfile, ffmpeg_path)
+ else:
+ cmd = '{3} -y -i "{0}" -vf "{1}" -map 0:0 -c:v mpeg2video -q:v 2 -aspect 16:9 -map 0:1 -c:a mp2 -b:a 384k -shortest -f mpegts "{2}"'.format(
+ infile, videofilter, outfile, ffmpeg_path)
+ else:
+ cmd = '{3} -y -i "{0}" -vf "{1}" -map 0:0 -c:v mpeg2video -q:v 2 -aspect 16:9 -map 0:1 -c:a mp2 -b:a 384k -shortest -f mpegts "{2}"'.format(
+ infile, videofilter, outfile, ffmpeg_path)
if args.debug:
print(cmd)
- subprocess.check_call(cmd,
- stderr=subprocess.STDOUT,
- stdout=subprocess.DEVNULL
- )
+ run(cmd)
return event_id
-if __name__ == "__main__":
- # Parse arguments
- parser = argparse.ArgumentParser(
- description='C3VOC Intro-Outro-Generator - Variant which renders only using video filters in ffmpeg',
- usage="./make-ffmpeg.py yourproject/",
- formatter_class=argparse.RawTextHelpFormatter)
-
- parser.add_argument('project', action="store", metavar='Project folder', type=str, help='''
- Path to your project folder
- ''')
-
- 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.
- This argument must not be used together with --id
- Usage: ./make-ffmpeg.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
- ''')
-
- 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('--skip', nargs='+', action="store", type=str, help='''
- Skip ID(s) not needed to be rendered.
- Usage: ./make-ffmpeg.py yourproject/ --skip 4711 0815 4223 1337
- ''')
-
- parser.add_argument('--force', action="store_true", default=False, help='''
- Force render if file exists.
- ''')
-
- args = parser.parse_args()
-
- if (args.skip is None):
- args.skip = []
-
- config = parse_config(PurePath(args.project, 'config.ini'))
-
- if args.debug:
- persons = ['Thomas Roth', 'Dmitry Nedospasov', 'Josh Datko',]
- events = [{
- 'id': 'debug',
- 'title': 'wallet.fail and the longest talk title to test if the template is big enough',
- 'subtitle': 'Hacking the most popular cryptocurrency hardware wallets',
- 'persons': persons,
- 'personnames': ', '.join(persons),
- 'room': 'Borg',
- }]
-
+if args.ids:
+ if len(args.ids) == 1:
+ print("enqueuing {} job".format(len(args.ids)))
else:
- events = list(schedulelib.events(config.schedule))
-
- if args.ids:
- if len(args.ids) == 1:
- print("enqueuing {} job".format(len(args.ids)))
- else:
- print("enqueuing {} jobs".format(len(args.ids)))
+ print("enqueuing {} jobs".format(len(args.ids)))
+else:
+ if len(events) == 1:
+ print("enqueuing {} job".format(len(events)))
else:
- if len(events) == 1:
- print("enqueuing {} job".format(len(events)))
- else:
- print("enqueuing {} jobs".format(len(events)))
+ print("enqueuing {} jobs".format(len(events)))
- for event in events:
- 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
+for event in events:
+ if args.ids and event['id'] not in args.ids:
+ continue
- event_print(event, "enqueued as " + str(event['id']))
+ if args.rooms and event['room'] not in args.rooms:
+ print("skipping room %s (%s)" % (event['room'], event['title']))
+ continue
- job_id = enqueue_job(config, event)
- if not job_id:
- event_print(event, "job was not enqueued successfully, skipping postprocessing")
- continue
+ event_print(event, "enqueued as " + str(event['id']))
- print('all done')
+ job_id = enqueue_job(event)
+ if not job_id:
+ event_print(event, "job was not enqueued successfully, skipping postprocessing")
+ continue
+
+
+print('all done')
diff --git a/mrmcd2019/config.ini b/mrmcd2019/config.ini
index fb74efc..165753c 100755
--- a/mrmcd2019/config.ini
+++ b/mrmcd2019/config.ini
@@ -1,14 +1,16 @@
-[meta]
+[default]
#schedule = https://talks.mrmcd.net/2019/schedule/export/schedule.xml
schedule = file:///home/thorti/git/c3voc/intro-outro-generator/mrmcd2019/schedule.xml
template = mrmcd2019.mov
alpha = false
prores = false
-inout_type = n
+fontfile = true
+inout = n
[title]
in = 50
out = 225
+fontfamily =
fontfile = Jura-Bold.ttf
fontsize = 80
fontcolor = #47acda
@@ -18,6 +20,7 @@ y = 540
[speaker]
in = 75
out = 225
+fontfamily =
fontfile = Jura-Regular.ttf
fontsize = 50
fontcolor = #094762
@@ -27,9 +30,10 @@ y = 950
[text]
in = 242
out = 324
+fontfamily =
fontfile = Jura-Regular.ttf
fontsize = 45
fontcolor = #c68100
x = (w-text_w)/2
y = 927
-; text =
+text = ''
diff --git a/osmodevcon24/__init__.py b/osmodevcon24/__init__.py
deleted file mode 100644
index 4264294..0000000
--- a/osmodevcon24/__init__.py
+++ /dev/null
@@ -1,148 +0,0 @@
-#!/usr/bin/python3
-
-from renderlib import *
-from schedulelib import *
-from easing import *
-
-# URL to Schedule-XML
-scheduleUrl = 'https://pretalx.sysmocom.de/osmodevcon2024/schedule/export/schedule.xml'
-
-def introFrames(args):
- #fade in title
- frames = 3*fps
- for i in range(0, frames):
- yield(
- ('title', 'style', 'opacity', easeInQuad(i, 0, 1, frames)),
- )
- # fade in subtitle and names
- frames = 1*fps
- for i in range(0, frames):
- yield(
- ('title', 'style', 'opacity', 1),
- ('subtitle', 'style', 'opacity', easeInQuad(i, 0, 1, frames)),
- ('personnames', 'style', 'opacity', easeInQuad(i, 0, 1, frames)),
- )
- #show whole image for 2 seconds
- frames = 2*fps
- for i in range(0, frames):
- yield(
- ('title', 'style', 'opacity', 1),
- ('personnames', 'style', 'opacity', 1),
- ('subtitle', 'style', 'opacity', 1),
- )
-
-def backgroundFrames(parameters):
- frames = 5*fps
- for i in range(0, frames):
- yield(
- ('logo', 'style', 'opacity', 1),
- )
-
-def outroFrames(args):
- frames = 2*fps
- for i in range(0, frames):
- yield(
- ('logo', 'style', 'opacity', 1),
- ('sublogo', 'style', 'opacity', 1),
- ('cclogo', 'style', 'opacity', 1),
- )
- # fade out
- frames = 3*fps
- for i in range(0, frames):
- yield(
- ('logo', 'style', 'opacity', "%.4f" % easeInCubic(i, 1, -1, frames)),
- ('sublogo', 'style', 'opacity', "%.4f" % easeInCubic(i, 1, -1, frames)),
- ('cclogo', 'style', 'opacity', "%.4f" % easeInCubic(i, 1, -1, frames)),
- )
-
-def pauseFrames(args):
- #fade in pause
- frames = 4*fps
- for i in range(0, frames):
- yield(
- ('pause', 'style', 'opacity', "%.4f" % easeInCubic(i, 0.2, 1, frames)),
- )
-
- # fade out
- frames = 4*fps
- for i in range(0, frames):
- yield(
- ('pause', 'style', 'opacity', "%.4f" % easeInCubic(i, 1, -0.8, frames)),
- )
-
-def debug():
- render('intro.svg',
- '../intro.ts',
- introFrames,
- {
- '$id': 7776,
- '$title': 'Configuring + running GPRS/EDGE data services with OsmoPCU, OsmoSGSN and OpenGGSN',
- '$subtitle': 'With some subtitle!',
- '$personnames': 'Alexander Chemeris + Harald Welte'
- }
- )
-
- render('outro.svg',
- '../outro.ts',
- outroFrames
- )
-
- render(
- 'background.svg',
- '../background.ts',
- backgroundFrames
- )
-
- render('pause.svg',
- '../pause.ts',
- pauseFrames
- )
-
-
-def tasks(queue, args, idlist, skiplist):
- # iterate over all events extracted from the schedule xml-export
- for event in events(scheduleUrl):
- 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 them 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'],
- '$personnames': 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
- ))
-
- # place the pause-sequence into the queue
- if not "pause" in skiplist:
- queue.put(Rendertask(
- infile = 'pause.svg',
- outfile = 'pause.ts',
- sequence = pauseFrames
- ))
-
- # place the background-sequence into the queue
- if not "bg" in skiplist:
- queue.put(Rendertask(
- infile = 'background.svg',
- outfile = 'background.ts',
- sequence = backgroundFrames
- ))
diff --git a/osmodevcon24/artwork/background.svg b/osmodevcon24/artwork/background.svg
deleted file mode 100644
index 3a87e75..0000000
--- a/osmodevcon24/artwork/background.svg
+++ /dev/null
@@ -1,946 +0,0 @@
-
-
-
-
diff --git a/osmodevcon24/artwork/intro.svg b/osmodevcon24/artwork/intro.svg
deleted file mode 100644
index 2a8b797..0000000
--- a/osmodevcon24/artwork/intro.svg
+++ /dev/null
@@ -1,1372 +0,0 @@
-
-
-
-
diff --git a/osmodevcon24/artwork/outro.svg b/osmodevcon24/artwork/outro.svg
deleted file mode 100644
index 1fbdc0e..0000000
--- a/osmodevcon24/artwork/outro.svg
+++ /dev/null
@@ -1,1479 +0,0 @@
-
-
-
-
diff --git a/osmodevcon24/artwork/pause.svg b/osmodevcon24/artwork/pause.svg
deleted file mode 100644
index e8d1e7b..0000000
--- a/osmodevcon24/artwork/pause.svg
+++ /dev/null
@@ -1,1208 +0,0 @@
-
-
-
-
diff --git a/requirements.txt b/requirements.txt
index efa6214..181482a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
-pillow>=8.0.0
+pillow
cssutils==1.0.2
-lxml~=5.3
-svg.path~=6.0
-Wand~=0.6.5
+lxml==4.9.1
+svg.path==4.0.2
+Wand==0.6.5
diff --git a/schedulelib.py b/schedulelib.py
index a986498..340e4d8 100644
--- a/schedulelib.py
+++ b/schedulelib.py
@@ -100,12 +100,6 @@ def events(scheduleUrl, titlemap={}):
url = event.find('url').text.strip()
else:
url = ''
-
- if event.find('track') is not None and event.find('track').text is not None:
- track = event.find('track').text
- else:
- track = ''
-
# yield a tupel with the event-id, event-title and person-names
yield {
'day': day.get('index'),
@@ -115,7 +109,7 @@ def events(scheduleUrl, titlemap={}):
'persons': personnames,
'personnames': ', '.join(personnames),
'room': room.attrib['name'],
- 'track': track,
+ 'track': event.find('track').text,
'url': url
}
diff --git a/vcfb24/Computerfont.ttf b/vcfb24/Computerfont.ttf
deleted file mode 100644
index 49f1c62..0000000
Binary files a/vcfb24/Computerfont.ttf and /dev/null differ
diff --git a/vcfb24/SourceSansPro-Semibold.ttf b/vcfb24/SourceSansPro-Semibold.ttf
deleted file mode 100644
index bf69cc2..0000000
Binary files a/vcfb24/SourceSansPro-Semibold.ttf and /dev/null differ
diff --git a/vcfb24/config.ini b/vcfb24/config.ini
deleted file mode 100644
index 712eff6..0000000
--- a/vcfb24/config.ini
+++ /dev/null
@@ -1,37 +0,0 @@
-[meta]
-schedule = http://vcfb.de/2024/schedule.xml
-# ffmpeg -loop 1 -i intro.png -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=44100 -c:v libx264 -tune stillimage -pix_fmt yuv420p -c:a aac -r 25 -t 10 intro.mp4
-template = intro.mp4
-alpha = false
-prores = false
-inout_type = t
-fade_duration = 0.5
-
-[title]
-in = 1
-out = 9.5
-fontfile = Computerfont.ttf
-fontsize = 100
-fontcolor = #ffffff
-x = 85
-y = 122
-
-[speaker]
-in = 2
-out = 9
-fontfile = SourceSansPro-Semibold.ttf
-fontsize = 65
-fontcolor = #ffffff
-x = 85
-y = 861
-
-
-[text]
-in = 0
-out = 0
-fontfile = Computerfont.ttf
-fontsize = 0
-fontcolor = #ffffff
-x = 0
-y = 0
-; text =
diff --git a/vcfb24/intro.svg b/vcfb24/intro.svg
deleted file mode 100644
index 30044ab..0000000
--- a/vcfb24/intro.svg
+++ /dev/null
@@ -1,15584 +0,0 @@
-
-
-
-
diff --git a/vcfb24/lower-third.svg b/vcfb24/lower-third.svg
deleted file mode 100644
index a70d3b4..0000000
--- a/vcfb24/lower-third.svg
+++ /dev/null
@@ -1,6231 +0,0 @@
-
-
-
-
diff --git a/vcfb24/outro.svg b/vcfb24/outro.svg
deleted file mode 100644
index 45f706d..0000000
--- a/vcfb24/outro.svg
+++ /dev/null
@@ -1,1668 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/vcfb24/pause.svg b/vcfb24/pause.svg
deleted file mode 100644
index 8ef961c..0000000
--- a/vcfb24/pause.svg
+++ /dev/null
@@ -1,6626 +0,0 @@
-
-
-
-
\ No newline at end of file