mirror of
https://github.com/Kunsi/pretalx-plugin-broadcast-tools
synced 2024-12-04 18:52:42 +00:00
add downloadable lower thirds images to be used in voctomix
This commit is contained in:
parent
f459c6c498
commit
c0b3bdb55e
11 changed files with 651 additions and 83 deletions
|
@ -22,3 +22,4 @@ class PluginApp(AppConfig):
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
from . import signals # NOQA
|
from . import signals # NOQA
|
||||||
|
from . import tasks # NOQA
|
||||||
|
|
94
pretalx_broadcast_tools/assets/titilium-web-regular.LICENSE
Normal file
94
pretalx_broadcast_tools/assets/titilium-web-regular.LICENSE
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
Copyright (c) 2009-2011 by Accademia di Belle Arti di Urbino and students
|
||||||
|
of MA course of Visual design. Some rights reserved.
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
https://openfontlicense.org
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
BIN
pretalx_broadcast_tools/assets/titilium-web-regular.ttf
Normal file
BIN
pretalx_broadcast_tools/assets/titilium-web-regular.ttf
Normal file
Binary file not shown.
|
@ -16,12 +16,22 @@ class BroadcastToolsSettingsForm(I18nFormMixin, HierarkeyForm):
|
||||||
broadcast_tools_lower_thirds_info_string = I18nFormField(
|
broadcast_tools_lower_thirds_info_string = I18nFormField(
|
||||||
help_text=_(
|
help_text=_(
|
||||||
"Will only be shown if there's a talk running. You may use "
|
"Will only be shown if there's a talk running. You may use "
|
||||||
"the place holders mentioned below."
|
"the place holders mentioned below. The info line will be shown "
|
||||||
|
"on the bottom right side of the lower third. Setting this to an "
|
||||||
|
"empty string will hide the line entirely."
|
||||||
),
|
),
|
||||||
label=_("Info line"),
|
label=_("Info line"),
|
||||||
required=False,
|
required=False,
|
||||||
widget=I18nTextInput,
|
widget=I18nTextInput,
|
||||||
)
|
)
|
||||||
|
broadcast_tools_lower_thirds_export_voctomix = BooleanField(
|
||||||
|
help_text=_(
|
||||||
|
"If checked, pretalx will periodically generate voctomix-compatible "
|
||||||
|
"lower thirds images and make them available as <code>.tar.gz</code>."
|
||||||
|
),
|
||||||
|
label=_("Generate voctomix lower thirds"),
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
|
||||||
broadcast_tools_room_info_lower_content = ChoiceField(
|
broadcast_tools_room_info_lower_content = ChoiceField(
|
||||||
choices=(
|
choices=(
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-10-31 21:10+0100\n"
|
"POT-Creation-Date: 2024-11-03 13:40+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -74,16 +74,31 @@ msgstr "\"Derzeit kein Vortrag\"-Informationen"
|
||||||
#: forms.py:18
|
#: forms.py:18
|
||||||
msgid ""
|
msgid ""
|
||||||
"Will only be shown if there's a talk running. You may use the place holders "
|
"Will only be shown if there's a talk running. You may use the place holders "
|
||||||
"mentioned below."
|
"mentioned below. The info line will be shown on the bottom right side of the "
|
||||||
|
"lower third. Setting this to an empty string will hide the line entirely."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Wird nur angezeigt, wenn derzeit ein Vortrag läuft. Du kannst die oben "
|
"Wird nur angezeigt, wenn derzeit ein Vortrag läuft. Du kannst die oben "
|
||||||
"genannten Platzhalter benutzen."
|
"genannten Platzhalter benutzen. Die Info-Zeile wird unten rechts in den "
|
||||||
|
"Bauchbinden angezeigt. Wenn das Feld leer ist, wird die Zeile automatisch "
|
||||||
|
"ausgeblendet."
|
||||||
|
|
||||||
#: forms.py:21
|
#: forms.py:23
|
||||||
msgid "Info line"
|
msgid "Info line"
|
||||||
msgstr "Info-Zeile"
|
msgstr "Info-Zeile"
|
||||||
|
|
||||||
#: forms.py:37
|
#: forms.py:29
|
||||||
|
msgid ""
|
||||||
|
"If checked, pretalx will periodically generate voctomix-compatible lower "
|
||||||
|
"thirds images and make them available as <code>.tar.gz</code>."
|
||||||
|
msgstr ""
|
||||||
|
"Wenn aktiviert, wird pretalx automatisch voctomix-kompatible bauchbinden "
|
||||||
|
"generieren und diese als <code>.tar.gz</code> zur Verfügung stellen."
|
||||||
|
|
||||||
|
#: forms.py:32
|
||||||
|
msgid "Generate voctomix lower thirds"
|
||||||
|
msgstr "Erzeuge voctomix-Bauchbinden"
|
||||||
|
|
||||||
|
#: forms.py:47
|
||||||
msgid ""
|
msgid ""
|
||||||
"If a talk is running, the room info page will always show the talk title and "
|
"If a talk is running, the room info page will always show the talk title and "
|
||||||
"the list of speakers. The content below is configurable here."
|
"the list of speakers. The content below is configurable here."
|
||||||
|
@ -92,11 +107,11 @@ msgstr ""
|
||||||
"und die Liste der Vortragenden an. Der Inhalt unterhalb dessen ist hier "
|
"und die Liste der Vortragenden an. Der Inhalt unterhalb dessen ist hier "
|
||||||
"konfigurierbar."
|
"konfigurierbar."
|
||||||
|
|
||||||
#: forms.py:41
|
#: forms.py:51
|
||||||
msgid "lower content"
|
msgid "lower content"
|
||||||
msgstr "Unterer Inhalt"
|
msgstr "Unterer Inhalt"
|
||||||
|
|
||||||
#: forms.py:46
|
#: forms.py:56
|
||||||
msgid ""
|
msgid ""
|
||||||
"If no talk is running in the room, show the time and title of the next talk "
|
"If no talk is running in the room, show the time and title of the next talk "
|
||||||
"in the room."
|
"in the room."
|
||||||
|
@ -104,11 +119,11 @@ msgstr ""
|
||||||
"Wenn derzeit kein Vortrag läuft, soll die Startzeit und der Titel des "
|
"Wenn derzeit kein Vortrag läuft, soll die Startzeit und der Titel des "
|
||||||
"nächsten Vortrags angezeigt werden."
|
"nächsten Vortrags angezeigt werden."
|
||||||
|
|
||||||
#: forms.py:49
|
#: forms.py:59
|
||||||
msgid "Show next talk"
|
msgid "Show next talk"
|
||||||
msgstr "Zeige nächsten Vortrag"
|
msgstr "Zeige nächsten Vortrag"
|
||||||
|
|
||||||
#: forms.py:55
|
#: forms.py:65
|
||||||
msgid ""
|
msgid ""
|
||||||
"If checked, the value of the 'internal notes' field in a submission will get "
|
"If checked, the value of the 'internal notes' field in a submission will get "
|
||||||
"added to the pdf export."
|
"added to the pdf export."
|
||||||
|
@ -116,22 +131,22 @@ msgstr ""
|
||||||
"Wenn aktiviert, wird der Inhalt des Feldes 'Interne Notizen' im PDF-Export "
|
"Wenn aktiviert, wird der Inhalt des Feldes 'Interne Notizen' im PDF-Export "
|
||||||
"angezeigt."
|
"angezeigt."
|
||||||
|
|
||||||
#: forms.py:58
|
#: forms.py:68
|
||||||
msgid "Show internal notes in pdf export"
|
msgid "Show internal notes in pdf export"
|
||||||
msgstr "Zeige interne Notizen im PDF-Export"
|
msgstr "Zeige interne Notizen im PDF-Export"
|
||||||
|
|
||||||
#: forms.py:63
|
#: forms.py:73
|
||||||
msgid ""
|
msgid ""
|
||||||
"If checked, 'do not record' talks will not generate a page in the pdf export."
|
"If checked, 'do not record' talks will not generate a page in the pdf export."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Wenn aktiviert, werden Vorträge mit 'Zeichnet meinen Vortrag nicht auf'-Flag "
|
"Wenn aktiviert, werden Vorträge mit 'Zeichnet meinen Vortrag nicht auf'-Flag "
|
||||||
"keine Seite im PDF-Export generieren."
|
"keine Seite im PDF-Export generieren."
|
||||||
|
|
||||||
#: forms.py:66
|
#: forms.py:76
|
||||||
msgid "Ignore 'do not record' talks when generating pdf"
|
msgid "Ignore 'do not record' talks when generating pdf"
|
||||||
msgstr "Ignoriere 'Zeichnet meinen Vortrag nicht auf'-Vorträge im PDF-Export"
|
msgstr "Ignoriere 'Zeichnet meinen Vortrag nicht auf'-Vorträge im PDF-Export"
|
||||||
|
|
||||||
#: forms.py:71
|
#: forms.py:81
|
||||||
msgid ""
|
msgid ""
|
||||||
"Comma-Separated list of question ids to include in pdf export. If empty, no "
|
"Comma-Separated list of question ids to include in pdf export. If empty, no "
|
||||||
"questions will get added."
|
"questions will get added."
|
||||||
|
@ -139,11 +154,11 @@ msgstr ""
|
||||||
"Komma-separierte Liste von Fragen, die im PDF-Export eingebunden werden. "
|
"Komma-separierte Liste von Fragen, die im PDF-Export eingebunden werden. "
|
||||||
"Falls dieses Feld leer ist, werden keine Fragen im PDF angezeigt."
|
"Falls dieses Feld leer ist, werden keine Fragen im PDF angezeigt."
|
||||||
|
|
||||||
#: forms.py:74
|
#: forms.py:84
|
||||||
msgid "Questions to include"
|
msgid "Questions to include"
|
||||||
msgstr "Eingebundene Fragen"
|
msgstr "Eingebundene Fragen"
|
||||||
|
|
||||||
#: forms.py:79
|
#: forms.py:89
|
||||||
msgid ""
|
msgid ""
|
||||||
"Additional content to print onto the PDF export. Will get printed as-is. You "
|
"Additional content to print onto the PDF export. Will get printed as-is. You "
|
||||||
"may use the place holders mentioned below."
|
"may use the place holders mentioned below."
|
||||||
|
@ -151,7 +166,7 @@ msgstr ""
|
||||||
"Zusätzlicher Inhalt, der auf dem PDF-Export angezeigt wird. Wird wie hier "
|
"Zusätzlicher Inhalt, der auf dem PDF-Export angezeigt wird. Wird wie hier "
|
||||||
"eingegeben angezeigt. Du kannst die oben genannten Platzhalter benutzen."
|
"eingegeben angezeigt. Du kannst die oben genannten Platzhalter benutzen."
|
||||||
|
|
||||||
#: forms.py:83
|
#: forms.py:93
|
||||||
msgid "Additional text"
|
msgid "Additional text"
|
||||||
msgstr "Zusätzlicher Text"
|
msgstr "Zusätzlicher Text"
|
||||||
|
|
||||||
|
@ -167,36 +182,40 @@ msgstr ""
|
||||||
msgid "broadcasting tools"
|
msgid "broadcasting tools"
|
||||||
msgstr "Broadcasting-Tools"
|
msgstr "Broadcasting-Tools"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:14
|
#: templates/pretalx_broadcast_tools/orga.html:13
|
||||||
msgid "room"
|
msgid "room"
|
||||||
msgstr "Raum"
|
msgstr "Raum"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:15
|
#: templates/pretalx_broadcast_tools/orga.html:14
|
||||||
msgid "Feature"
|
msgid "Feature"
|
||||||
msgstr "Funktion"
|
msgstr "Funktion"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:22
|
#: templates/pretalx_broadcast_tools/orga.html:21
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Lower thirds"
|
#| msgid "Lower thirds"
|
||||||
msgid "Lower Thirds"
|
msgid "Lower Thirds"
|
||||||
msgstr "Bauchbinden"
|
msgstr "Bauchbinden"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:23
|
#: templates/pretalx_broadcast_tools/orga.html:22
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "Room info"
|
#| msgid "Room info"
|
||||||
msgid "Room Info"
|
msgid "Room Info"
|
||||||
msgstr "Raum-Information"
|
msgstr "Raum-Information"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:30
|
#: templates/pretalx_broadcast_tools/orga.html:27
|
||||||
|
msgid "Download voctomix-compatible lower thirds images"
|
||||||
|
msgstr "Lade voctomix-kompatible Bauchbinden-Bilder herunter"
|
||||||
|
|
||||||
|
#: templates/pretalx_broadcast_tools/orga.html:29
|
||||||
msgid "Placeholders"
|
msgid "Placeholders"
|
||||||
msgstr "Platzhalter"
|
msgstr "Platzhalter"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:31
|
#: templates/pretalx_broadcast_tools/orga.html:30
|
||||||
msgid ""
|
msgid ""
|
||||||
"pretalx will automatically replace some placeholders in your custom content:"
|
"pretalx will automatically replace some placeholders in your custom content:"
|
||||||
msgstr "pretalx ersetzt automatisch einige Platzhalter in deinen Texten:"
|
msgstr "pretalx ersetzt automatisch einige Platzhalter in deinen Texten:"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:34
|
#: templates/pretalx_broadcast_tools/orga.html:33
|
||||||
msgid ""
|
msgid ""
|
||||||
"talk code (<code>MUX9U3</code> for example) - most useful in combination "
|
"talk code (<code>MUX9U3</code> for example) - most useful in combination "
|
||||||
"with pretalx-proposal-redirects or something like that"
|
"with pretalx-proposal-redirects or something like that"
|
||||||
|
@ -204,54 +223,46 @@ msgstr ""
|
||||||
"Vortrags-Slug (zum Beispiel <code>MUX9U3</code>) - am hilfreichsten in "
|
"Vortrags-Slug (zum Beispiel <code>MUX9U3</code>) - am hilfreichsten in "
|
||||||
"Kombination mit pretalx-proposal-redirects oder ähnlichen Plugins"
|
"Kombination mit pretalx-proposal-redirects oder ähnlichen Plugins"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:37
|
#: templates/pretalx_broadcast_tools/orga.html:36
|
||||||
msgid "The event slug"
|
msgid "The event slug"
|
||||||
msgstr "Der Event-Slug"
|
msgstr "Der Event-Slug"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:40
|
#: templates/pretalx_broadcast_tools/orga.html:39
|
||||||
msgid "URL to the talk feedback page."
|
msgid "URL to the talk feedback page."
|
||||||
msgstr "Adresse der Vortrags-Feedback-Seite"
|
msgstr "Adresse der Vortrags-Feedback-Seite"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:43
|
#: templates/pretalx_broadcast_tools/orga.html:42
|
||||||
msgid "The talk slug"
|
msgid "The talk slug"
|
||||||
msgstr "Der Vortrags-Slug"
|
msgstr "Der Vortrags-Slug"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:46
|
#: templates/pretalx_broadcast_tools/orga.html:45
|
||||||
msgid "URL to the talk detail page."
|
msgid "URL to the talk detail page."
|
||||||
msgstr "Adresse der Vortrags-Seite"
|
msgstr "Adresse der Vortrags-Seite"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:48
|
#: templates/pretalx_broadcast_tools/orga.html:47
|
||||||
msgid "or"
|
msgid "or"
|
||||||
msgstr "oder"
|
msgstr "oder"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:49
|
#: templates/pretalx_broadcast_tools/orga.html:48
|
||||||
msgid "Track name in plain text or coloured using the track colour."
|
msgid "Track name in plain text or coloured using the track colour."
|
||||||
msgstr "Track-Name in einfachem Text oder in der Track-Farbe eingefärbt."
|
msgstr "Track-Name in einfachem Text oder in der Track-Farbe eingefärbt."
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:52
|
#: templates/pretalx_broadcast_tools/orga.html:51
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr "Einstellungen"
|
msgstr "Einstellungen"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:55
|
#: templates/pretalx_broadcast_tools/orga.html:54
|
||||||
msgid "Lower thirds"
|
msgid "Lower thirds"
|
||||||
msgstr "Bauchbinden"
|
msgstr "Bauchbinden"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:59
|
#: templates/pretalx_broadcast_tools/orga.html:62
|
||||||
msgid ""
|
|
||||||
"The info line will be shown on the bottom right side of your lower third. If "
|
|
||||||
"you set it to an empty string, it will automatically hide itself."
|
|
||||||
msgstr ""
|
|
||||||
"Die Info-Zeile wird unten rechts in den Bauchbinden angezeigt. Wenn das Feld "
|
|
||||||
"leer ist, wird die Zeile automatisch ausgeblendet."
|
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:63
|
|
||||||
msgid "Room info"
|
msgid "Room info"
|
||||||
msgstr "Raum-Information"
|
msgstr "Raum-Information"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:70
|
#: templates/pretalx_broadcast_tools/orga.html:69
|
||||||
msgid "PDF export"
|
msgid "PDF export"
|
||||||
msgstr "PDF-Export"
|
msgstr "PDF-Export"
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:86
|
#: templates/pretalx_broadcast_tools/orga.html:85
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr "Speichern"
|
msgstr "Speichern"
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-10-31 21:10+0100\n"
|
"POT-Creation-Date: 2024-11-03 13:40+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -71,69 +71,80 @@ msgstr ""
|
||||||
#: forms.py:18
|
#: forms.py:18
|
||||||
msgid ""
|
msgid ""
|
||||||
"Will only be shown if there's a talk running. You may use the place holders "
|
"Will only be shown if there's a talk running. You may use the place holders "
|
||||||
"mentioned below."
|
"mentioned below. The info line will be shown on the bottom right side of the "
|
||||||
|
"lower third. Setting this to an empty string will hide the line entirely."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:21
|
#: forms.py:23
|
||||||
msgid "Info line"
|
msgid "Info line"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:37
|
#: forms.py:29
|
||||||
|
msgid ""
|
||||||
|
"If checked, pretalx will periodically generate voctomix-compatible lower "
|
||||||
|
"thirds images and make them available as <code>.tar.gz</code>."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: forms.py:32
|
||||||
|
msgid "Generate voctomix lower thirds"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: forms.py:47
|
||||||
msgid ""
|
msgid ""
|
||||||
"If a talk is running, the room info page will always show the talk title and "
|
"If a talk is running, the room info page will always show the talk title and "
|
||||||
"the list of speakers. The content below is configurable here."
|
"the list of speakers. The content below is configurable here."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:41
|
#: forms.py:51
|
||||||
msgid "lower content"
|
msgid "lower content"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:46
|
#: forms.py:56
|
||||||
msgid ""
|
msgid ""
|
||||||
"If no talk is running in the room, show the time and title of the next talk "
|
"If no talk is running in the room, show the time and title of the next talk "
|
||||||
"in the room."
|
"in the room."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:49
|
#: forms.py:59
|
||||||
msgid "Show next talk"
|
msgid "Show next talk"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:55
|
#: forms.py:65
|
||||||
msgid ""
|
msgid ""
|
||||||
"If checked, the value of the 'internal notes' field in a submission will get "
|
"If checked, the value of the 'internal notes' field in a submission will get "
|
||||||
"added to the pdf export."
|
"added to the pdf export."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:58
|
#: forms.py:68
|
||||||
msgid "Show internal notes in pdf export"
|
msgid "Show internal notes in pdf export"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:63
|
#: forms.py:73
|
||||||
msgid ""
|
msgid ""
|
||||||
"If checked, 'do not record' talks will not generate a page in the pdf export."
|
"If checked, 'do not record' talks will not generate a page in the pdf export."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:66
|
#: forms.py:76
|
||||||
msgid "Ignore 'do not record' talks when generating pdf"
|
msgid "Ignore 'do not record' talks when generating pdf"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:71
|
#: forms.py:81
|
||||||
msgid ""
|
msgid ""
|
||||||
"Comma-Separated list of question ids to include in pdf export. If empty, no "
|
"Comma-Separated list of question ids to include in pdf export. If empty, no "
|
||||||
"questions will get added."
|
"questions will get added."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:74
|
#: forms.py:84
|
||||||
msgid "Questions to include"
|
msgid "Questions to include"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:79
|
#: forms.py:89
|
||||||
msgid ""
|
msgid ""
|
||||||
"Additional content to print onto the PDF export. Will get printed as-is. You "
|
"Additional content to print onto the PDF export. Will get printed as-is. You "
|
||||||
"may use the place holders mentioned below."
|
"may use the place holders mentioned below."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: forms.py:83
|
#: forms.py:93
|
||||||
msgid "Additional text"
|
msgid "Additional text"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -149,83 +160,81 @@ msgstr ""
|
||||||
msgid "broadcasting tools"
|
msgid "broadcasting tools"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:14
|
#: templates/pretalx_broadcast_tools/orga.html:13
|
||||||
msgid "room"
|
msgid "room"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:15
|
#: templates/pretalx_broadcast_tools/orga.html:14
|
||||||
msgid "Feature"
|
msgid "Feature"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:22
|
#: templates/pretalx_broadcast_tools/orga.html:21
|
||||||
msgid "Lower Thirds"
|
msgid "Lower Thirds"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:23
|
#: templates/pretalx_broadcast_tools/orga.html:22
|
||||||
msgid "Room Info"
|
msgid "Room Info"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:30
|
#: templates/pretalx_broadcast_tools/orga.html:27
|
||||||
|
msgid "Download voctomix-compatible lower thirds images"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/pretalx_broadcast_tools/orga.html:29
|
||||||
msgid "Placeholders"
|
msgid "Placeholders"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:31
|
#: templates/pretalx_broadcast_tools/orga.html:30
|
||||||
msgid ""
|
msgid ""
|
||||||
"pretalx will automatically replace some placeholders in your custom content:"
|
"pretalx will automatically replace some placeholders in your custom content:"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:34
|
#: templates/pretalx_broadcast_tools/orga.html:33
|
||||||
msgid ""
|
msgid ""
|
||||||
"talk code (<code>MUX9U3</code> for example) - most useful in combination "
|
"talk code (<code>MUX9U3</code> for example) - most useful in combination "
|
||||||
"with pretalx-proposal-redirects or something like that"
|
"with pretalx-proposal-redirects or something like that"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:37
|
#: templates/pretalx_broadcast_tools/orga.html:36
|
||||||
msgid "The event slug"
|
msgid "The event slug"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:40
|
#: templates/pretalx_broadcast_tools/orga.html:39
|
||||||
msgid "URL to the talk feedback page."
|
msgid "URL to the talk feedback page."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:43
|
#: templates/pretalx_broadcast_tools/orga.html:42
|
||||||
msgid "The talk slug"
|
msgid "The talk slug"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:46
|
#: templates/pretalx_broadcast_tools/orga.html:45
|
||||||
msgid "URL to the talk detail page."
|
msgid "URL to the talk detail page."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:48
|
#: templates/pretalx_broadcast_tools/orga.html:47
|
||||||
msgid "or"
|
msgid "or"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:49
|
#: templates/pretalx_broadcast_tools/orga.html:48
|
||||||
msgid "Track name in plain text or coloured using the track colour."
|
msgid "Track name in plain text or coloured using the track colour."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:52
|
#: templates/pretalx_broadcast_tools/orga.html:51
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:55
|
#: templates/pretalx_broadcast_tools/orga.html:54
|
||||||
msgid "Lower thirds"
|
msgid "Lower thirds"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:59
|
#: templates/pretalx_broadcast_tools/orga.html:62
|
||||||
msgid ""
|
|
||||||
"The info line will be shown on the bottom right side of your lower third. If "
|
|
||||||
"you set it to an empty string, it will automatically hide itself."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:63
|
|
||||||
msgid "Room info"
|
msgid "Room info"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:70
|
#: templates/pretalx_broadcast_tools/orga.html:69
|
||||||
msgid "PDF export"
|
msgid "PDF export"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: templates/pretalx_broadcast_tools/orga.html:86
|
#: templates/pretalx_broadcast_tools/orga.html:85
|
||||||
msgid "Save"
|
msgid "Save"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -0,0 +1,336 @@
|
||||||
|
import logging
|
||||||
|
import tarfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
from django_scopes import scope, scopes_disabled
|
||||||
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
|
from pretalx.agenda.management.commands.export_schedule_html import delete_directory
|
||||||
|
from pretalx.event.models import Event
|
||||||
|
|
||||||
|
from pretalx_broadcast_tools.utils.placeholders import placeholders
|
||||||
|
|
||||||
|
IMG_WIDTH = 1920 # px
|
||||||
|
IMG_HEIGHT = 1080 # px
|
||||||
|
|
||||||
|
FONT_SIZE_TITLE = 30 # px
|
||||||
|
FONT_SIZE_SPEAKER = 25 # px
|
||||||
|
FONT_SIZE_INFOLINE = 18 # px
|
||||||
|
FONT_FILE = (
|
||||||
|
Path(__file__).resolve().parent.parent.parent
|
||||||
|
/ "assets"
|
||||||
|
/ "titilium-web-regular.ttf"
|
||||||
|
)
|
||||||
|
|
||||||
|
BOX_PADDING = 10 # px
|
||||||
|
|
||||||
|
BOX_WIDTH = int(IMG_WIDTH * 0.8)
|
||||||
|
BOX_BOTTOM = int(IMG_HEIGHT - (IMG_HEIGHT * 0.1))
|
||||||
|
BOX_LEFT = int((IMG_WIDTH - BOX_WIDTH) / 2)
|
||||||
|
|
||||||
|
|
||||||
|
def get_export_path(event):
|
||||||
|
return settings.HTMLEXPORT_ROOT / event.slug / "broadcast-tools"
|
||||||
|
|
||||||
|
|
||||||
|
def get_export_targz_path(event):
|
||||||
|
return get_export_path(event).with_suffix(".voctomix.tar.gz")
|
||||||
|
|
||||||
|
|
||||||
|
def make_targz(generated_files, targz_path):
|
||||||
|
tmp_name = targz_path.with_suffix(".tmp")
|
||||||
|
tmp_name.unlink(missing_ok=True)
|
||||||
|
with tarfile.open(tmp_name, "w:gz") as tar:
|
||||||
|
for file in generated_files:
|
||||||
|
tar.add(file, arcname=file.name)
|
||||||
|
tmp_name.rename(targz_path)
|
||||||
|
|
||||||
|
|
||||||
|
class VoctomixLowerThirdsExporter:
|
||||||
|
def __init__(self, event, tmp_dir):
|
||||||
|
self.log = logging.getLogger(event.slug)
|
||||||
|
self.event = event
|
||||||
|
self.tmp_dir = tmp_dir
|
||||||
|
self.exported = set()
|
||||||
|
|
||||||
|
if event.primary_color:
|
||||||
|
self.primary_colour = self._hex2rgb(event.primary_color)
|
||||||
|
else:
|
||||||
|
# pretalx.settings.DEFAULT_EVENT_PRIMARY_COLOR
|
||||||
|
self.primary__color = (58, 165, 124)
|
||||||
|
|
||||||
|
self.infoline = event.settings.broadcast_tools_lower_thirds_info_string or ""
|
||||||
|
|
||||||
|
self.font_title = ImageFont.truetype(
|
||||||
|
FONT_FILE,
|
||||||
|
size=FONT_SIZE_TITLE,
|
||||||
|
encoding="unic",
|
||||||
|
)
|
||||||
|
self.font_speaker = ImageFont.truetype(
|
||||||
|
FONT_FILE,
|
||||||
|
size=FONT_SIZE_SPEAKER,
|
||||||
|
encoding="unic",
|
||||||
|
)
|
||||||
|
self.font_infoline = ImageFont.truetype(
|
||||||
|
FONT_FILE,
|
||||||
|
size=FONT_SIZE_INFOLINE,
|
||||||
|
encoding="unic",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _hex2rgb(self, hex_value):
|
||||||
|
hex_value = hex_value.lstrip("#")
|
||||||
|
# black insists this should have spaces around the :, but flake8
|
||||||
|
# complains about spaces around the :, soooooo ....
|
||||||
|
return tuple(int(hex_value[i : i + 2], 16) for i in (0, 2, 4)) # NOQA
|
||||||
|
|
||||||
|
def _fit_text(self, input_text, font, max_width):
|
||||||
|
words = [i.strip() for i in input_text.split()]
|
||||||
|
lines = []
|
||||||
|
line = []
|
||||||
|
for word in words:
|
||||||
|
new_line = " ".join([*line, word])
|
||||||
|
_, _, w, _ = font.getbbox(new_line)
|
||||||
|
if w > max_width:
|
||||||
|
# append old line to list of lines, then start new line with
|
||||||
|
# current word
|
||||||
|
lines.append(" ".join(line))
|
||||||
|
line = [word]
|
||||||
|
elif word.endswith(":"):
|
||||||
|
lines.append(new_line)
|
||||||
|
line = []
|
||||||
|
else:
|
||||||
|
line.append(word)
|
||||||
|
|
||||||
|
if line:
|
||||||
|
lines.append(" ".join(line))
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def _get_infoline(self, talk):
|
||||||
|
infoline = self.infoline.localize(self.event.locale).format(
|
||||||
|
**placeholders(
|
||||||
|
self.event,
|
||||||
|
talk,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
_, _, w, _ = self.font_infoline.getbbox(infoline)
|
||||||
|
return w, infoline
|
||||||
|
|
||||||
|
def export_speaker(self, talk, speaker):
|
||||||
|
img = Image.new(
|
||||||
|
mode="RGBA",
|
||||||
|
size=(IMG_WIDTH, IMG_HEIGHT),
|
||||||
|
color=(0, 0, 0, 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
speaker_text = self._fit_text(
|
||||||
|
speaker.get_display_name(),
|
||||||
|
self.font_speaker,
|
||||||
|
BOX_WIDTH,
|
||||||
|
)
|
||||||
|
infoline_width, infoline_text = self._get_infoline(talk)
|
||||||
|
|
||||||
|
y_pos = BOX_BOTTOM - BOX_PADDING
|
||||||
|
if speaker_text:
|
||||||
|
y_pos -= len(speaker_text) * FONT_SIZE_SPEAKER
|
||||||
|
if infoline_text:
|
||||||
|
y_pos -= FONT_SIZE_INFOLINE
|
||||||
|
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
draw.rectangle(
|
||||||
|
[
|
||||||
|
(BOX_LEFT - BOX_PADDING, y_pos),
|
||||||
|
(BOX_LEFT + BOX_WIDTH + BOX_PADDING, BOX_BOTTOM + BOX_PADDING),
|
||||||
|
],
|
||||||
|
fill=self.primary_colour,
|
||||||
|
)
|
||||||
|
if talk.submission.track and talk.submission.track.color:
|
||||||
|
draw.rectangle(
|
||||||
|
[
|
||||||
|
(BOX_LEFT - BOX_PADDING, BOX_BOTTOM + BOX_PADDING),
|
||||||
|
(
|
||||||
|
BOX_LEFT + BOX_WIDTH + BOX_PADDING,
|
||||||
|
BOX_BOTTOM + (BOX_PADDING * 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
fill=self._hex2rgb(talk.submission.track.color),
|
||||||
|
)
|
||||||
|
|
||||||
|
for line in speaker_text:
|
||||||
|
draw.text(
|
||||||
|
(BOX_LEFT, y_pos),
|
||||||
|
line,
|
||||||
|
font=self.font_speaker,
|
||||||
|
fill=(255, 255, 255),
|
||||||
|
)
|
||||||
|
y_pos += FONT_SIZE_SPEAKER
|
||||||
|
|
||||||
|
if infoline_text:
|
||||||
|
draw.text(
|
||||||
|
(BOX_LEFT + BOX_WIDTH - infoline_width, y_pos),
|
||||||
|
infoline_text,
|
||||||
|
font=self.font_infoline,
|
||||||
|
fill=(255, 255, 255),
|
||||||
|
)
|
||||||
|
|
||||||
|
filename = self.tmp_dir / f"event_{talk.submission_id}_person_{speaker.id}.png"
|
||||||
|
img.save(filename)
|
||||||
|
self.log.debug(
|
||||||
|
f"Generated single-speaker image for {speaker.get_display_name()!r} "
|
||||||
|
"of talk {talk.submission.title!r}, saved as {filename}"
|
||||||
|
)
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def export_talk(self, talk):
|
||||||
|
img = Image.new(
|
||||||
|
mode="RGBA",
|
||||||
|
size=(IMG_WIDTH, IMG_HEIGHT),
|
||||||
|
color=(0, 0, 0, 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
title_text = self._fit_text(talk.submission.title, self.font_title, BOX_WIDTH)
|
||||||
|
speaker_text = self._fit_text(
|
||||||
|
", ".join(
|
||||||
|
[person.get_display_name() for person in talk.submission.speakers.all()]
|
||||||
|
),
|
||||||
|
self.font_speaker,
|
||||||
|
BOX_WIDTH,
|
||||||
|
)
|
||||||
|
infoline_width, infoline_text = self._get_infoline(talk)
|
||||||
|
|
||||||
|
y_pos = BOX_BOTTOM - BOX_PADDING
|
||||||
|
if title_text:
|
||||||
|
y_pos -= len(title_text) * FONT_SIZE_TITLE
|
||||||
|
if speaker_text:
|
||||||
|
y_pos -= len(speaker_text) * FONT_SIZE_SPEAKER
|
||||||
|
if title_text and speaker_text:
|
||||||
|
y_pos -= BOX_PADDING
|
||||||
|
if infoline_text:
|
||||||
|
y_pos -= FONT_SIZE_INFOLINE
|
||||||
|
|
||||||
|
draw = ImageDraw.Draw(img)
|
||||||
|
draw.rectangle(
|
||||||
|
[
|
||||||
|
(BOX_LEFT - BOX_PADDING, y_pos),
|
||||||
|
(BOX_LEFT + BOX_WIDTH + BOX_PADDING, BOX_BOTTOM + BOX_PADDING),
|
||||||
|
],
|
||||||
|
fill=self.primary_colour,
|
||||||
|
)
|
||||||
|
if talk.submission.track and talk.submission.track.color:
|
||||||
|
draw.rectangle(
|
||||||
|
[
|
||||||
|
(BOX_LEFT - BOX_PADDING, BOX_BOTTOM + BOX_PADDING),
|
||||||
|
(
|
||||||
|
BOX_LEFT + BOX_WIDTH + BOX_PADDING,
|
||||||
|
BOX_BOTTOM + (BOX_PADDING * 2),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
fill=self._hex2rgb(talk.submission.track.color),
|
||||||
|
)
|
||||||
|
|
||||||
|
for line in title_text:
|
||||||
|
draw.text(
|
||||||
|
(BOX_LEFT, y_pos),
|
||||||
|
line,
|
||||||
|
font=self.font_title,
|
||||||
|
fill=(255, 255, 255),
|
||||||
|
)
|
||||||
|
y_pos += FONT_SIZE_TITLE
|
||||||
|
|
||||||
|
if title_text and speaker_text:
|
||||||
|
y_pos += BOX_PADDING
|
||||||
|
|
||||||
|
for line in speaker_text:
|
||||||
|
draw.text(
|
||||||
|
(BOX_LEFT, y_pos),
|
||||||
|
line,
|
||||||
|
font=self.font_speaker,
|
||||||
|
fill=(255, 255, 255),
|
||||||
|
)
|
||||||
|
y_pos += FONT_SIZE_SPEAKER
|
||||||
|
|
||||||
|
if infoline_text:
|
||||||
|
draw.text(
|
||||||
|
(BOX_LEFT + BOX_WIDTH - infoline_width, y_pos),
|
||||||
|
infoline_text,
|
||||||
|
font=self.font_infoline,
|
||||||
|
fill=(255, 255, 255),
|
||||||
|
)
|
||||||
|
|
||||||
|
filename = self.tmp_dir / f"event_{talk.submission_id}_persons.png"
|
||||||
|
img.save(filename)
|
||||||
|
self.log.debug(
|
||||||
|
f"Generated image for talk {talk.submission.title!r}, saved as {filename}"
|
||||||
|
)
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def export(self):
|
||||||
|
generated_files = set()
|
||||||
|
if not self.event.current_schedule:
|
||||||
|
raise CommandError(
|
||||||
|
f"event {self.event.slug} ({self.event.name}) does not have a schedule to be exported!"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.log.info(
|
||||||
|
f"Generating voctomix-compatible lower thirds for event {self.event.name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
for talk in self.event.current_schedule.talks.filter(
|
||||||
|
is_visible=True
|
||||||
|
).select_related("submission"):
|
||||||
|
if talk.id in self.exported:
|
||||||
|
# account for talks that are scheduled multiple times
|
||||||
|
self.log.warning(
|
||||||
|
f"Talk {talk.submission.title!r} was already generated, skipping. "
|
||||||
|
"(Possibly scheduled multiple times?)"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.log.info(f"Generating image(s) for talk {talk.submission.title!r}")
|
||||||
|
generated_files.add(self.export_talk(talk))
|
||||||
|
for speaker in talk.submission.speakers.all():
|
||||||
|
generated_files.add(self.export_speaker(talk, speaker))
|
||||||
|
self.exported.add(talk.id)
|
||||||
|
|
||||||
|
return generated_files
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
def add_arguments(self, parser):
|
||||||
|
super().add_arguments(parser)
|
||||||
|
parser.add_argument("event", type=str)
|
||||||
|
parser.add_argument("--no-delete-source-files", action="store_true")
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
event_slug = options.get("event")
|
||||||
|
|
||||||
|
with scopes_disabled():
|
||||||
|
try:
|
||||||
|
event = Event.objects.get(slug__iexact=event_slug)
|
||||||
|
except Event.DoesNotExist:
|
||||||
|
raise CommandError(f"could not find event with slug {event_slug!r}")
|
||||||
|
|
||||||
|
with scope(event=event):
|
||||||
|
logging.info(f"Exporting {event.name}")
|
||||||
|
|
||||||
|
export_dir = get_export_path(event)
|
||||||
|
targz_path = get_export_targz_path(event)
|
||||||
|
|
||||||
|
delete_directory(export_dir)
|
||||||
|
# for the first export of the conference, the "broadcast-tools"
|
||||||
|
# directory does not exist
|
||||||
|
export_dir.mkdir(parents=True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
exporter = VoctomixLowerThirdsExporter(event, export_dir)
|
||||||
|
generated_files = exporter.export()
|
||||||
|
make_targz(generated_files, targz_path)
|
||||||
|
except Exception:
|
||||||
|
logging.exception(f"Export of {event.name} failed")
|
||||||
|
else:
|
||||||
|
logging.info(
|
||||||
|
f"Export of {event.name} succeeded, export available at {targz_path}"
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
if not options.get("no_delete_source_files"):
|
||||||
|
delete_directory(export_dir)
|
78
pretalx_broadcast_tools/tasks.py
Normal file
78
pretalx_broadcast_tools/tasks.py
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import logging
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django_scopes import scope, scopes_disabled
|
||||||
|
from pretalx.celery_app import app
|
||||||
|
from pretalx.common.signals import periodic_task
|
||||||
|
from pretalx.event.models import Event
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.task(name="pretalx_broadcast_tools.export_voctomix_lower_thirds")
|
||||||
|
def export_voctomix_lower_thirds(*, event_id):
|
||||||
|
from django.core.management import call_command
|
||||||
|
|
||||||
|
with scopes_disabled():
|
||||||
|
event = Event.objects.filter(pk=event_id).first()
|
||||||
|
if not event:
|
||||||
|
LOG.error(f"Could not find event {event_id=} for export")
|
||||||
|
return
|
||||||
|
|
||||||
|
with scope(event=event):
|
||||||
|
if not event.current_schedule:
|
||||||
|
LOG.error(f"event {event.slug} does not have schedule, can't export")
|
||||||
|
return
|
||||||
|
|
||||||
|
call_command(
|
||||||
|
"export_voctomix_lower_thirds",
|
||||||
|
event.slug,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.task(name="pretalx_broadcast_tools.periodic_voctomix_export")
|
||||||
|
def task_periodic_voctomix_export(*, event_slug):
|
||||||
|
from pretalx_broadcast_tools.management.commands.export_voctomix_lower_thirds import (
|
||||||
|
get_export_targz_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
with scopes_disabled():
|
||||||
|
event = Event.objects.filter(slug=event_slug).first()
|
||||||
|
|
||||||
|
with scope(event=event):
|
||||||
|
if (
|
||||||
|
not event.settings.broadcast_tools_lower_thirds_export_voctomix
|
||||||
|
or not event.current_schedule
|
||||||
|
):
|
||||||
|
return
|
||||||
|
|
||||||
|
targz_path = get_export_targz_path(event)
|
||||||
|
needs_rebuild = False
|
||||||
|
last_rebuild = event.cache.get("broadcast_tools_last_voctomix_export")
|
||||||
|
_now = now()
|
||||||
|
if not targz_path.exists():
|
||||||
|
needs_rebuild = True
|
||||||
|
if not last_rebuild or _now - last_rebuild >= timedelta(hours=1):
|
||||||
|
needs_rebuild = True
|
||||||
|
if event.cache.get("broadcast_tools_force_new_voctomix_export"):
|
||||||
|
needs_rebuild = True
|
||||||
|
|
||||||
|
if needs_rebuild:
|
||||||
|
event.cache.delete("broadcast_tools_force_new_voctomix_export")
|
||||||
|
event.cache.set("broadcast_tools_last_voctomix_export", _now, None)
|
||||||
|
export_voctomix_lower_thirds.apply_async(
|
||||||
|
kwargs={"event_id": event.id}, ignore_result=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(periodic_task)
|
||||||
|
def periodic_event_services(sender, **kwargs):
|
||||||
|
for event in Event.objects.all():
|
||||||
|
with scope(event=event):
|
||||||
|
if (event.date_to + timedelta(days=2)) < now().date():
|
||||||
|
continue
|
||||||
|
task_periodic_voctomix_export.apply_async(
|
||||||
|
kwargs={"event_slug": event.slug}, ignore_result=True
|
||||||
|
)
|
|
@ -24,6 +24,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<p><a href="{% url 'plugins:pretalx_broadcast_tools:lowerthirds_voctomix_download' request.event.slug %}">{% translate "Download voctomix-compatible lower thirds images" %}</a></p>
|
||||||
|
|
||||||
<h2>{% translate "Placeholders" %}</h2>
|
<h2>{% translate "Placeholders" %}</h2>
|
||||||
<p>{% translate "pretalx will automatically replace some placeholders in your custom content:" %}</p>
|
<p>{% translate "pretalx will automatically replace some placeholders in your custom content:" %}</p>
|
||||||
|
@ -54,7 +55,7 @@
|
||||||
</legend>
|
</legend>
|
||||||
{{ form.broadcast_tools_lower_thirds_no_talk_info.as_field_group }}
|
{{ form.broadcast_tools_lower_thirds_no_talk_info.as_field_group }}
|
||||||
{{ form.broadcast_tools_lower_thirds_info_string.as_field_group }}
|
{{ form.broadcast_tools_lower_thirds_info_string.as_field_group }}
|
||||||
<p>{% translate "The info line will be shown on the bottom right side of your lower third. If you set it to an empty string, it will automatically hide itself." %}</p>
|
{{ form.broadcast_tools_lower_thirds_export_voctomix.as_field_group }}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>
|
<legend>
|
||||||
|
|
|
@ -5,6 +5,7 @@ from .views.orga import BroadcastToolsOrgaView
|
||||||
from .views.qr import BroadcastToolsFeedbackQrCodeSvg, BroadcastToolsPublicQrCodeSvg
|
from .views.qr import BroadcastToolsFeedbackQrCodeSvg, BroadcastToolsPublicQrCodeSvg
|
||||||
from .views.schedule import BroadcastToolsScheduleView
|
from .views.schedule import BroadcastToolsScheduleView
|
||||||
from .views.static_html import BroadcastToolsLowerThirdsView, BroadcastToolsRoomInfoView
|
from .views.static_html import BroadcastToolsLowerThirdsView, BroadcastToolsRoomInfoView
|
||||||
|
from .views.voctomix_export import BroadcastToolsLowerThirdsVoctomixDownloadView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(
|
path(
|
||||||
|
@ -26,6 +27,11 @@ urlpatterns = [
|
||||||
BroadcastToolsLowerThirdsView.as_view(),
|
BroadcastToolsLowerThirdsView.as_view(),
|
||||||
name="lowerthirds",
|
name="lowerthirds",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"lower-thirds.voctomix.tar.gz",
|
||||||
|
BroadcastToolsLowerThirdsVoctomixDownloadView.as_view(),
|
||||||
|
name="lowerthirds_voctomix_download",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"feedback-qr/<talk>.svg",
|
"feedback-qr/<talk>.svg",
|
||||||
BroadcastToolsFeedbackQrCodeSvg.as_view(),
|
BroadcastToolsFeedbackQrCodeSvg.as_view(),
|
||||||
|
|
22
pretalx_broadcast_tools/views/voctomix_export.py
Normal file
22
pretalx_broadcast_tools/views/voctomix_export.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from django.http import FileResponse, Http404
|
||||||
|
from django.views import View
|
||||||
|
from pretalx.common.text.path import safe_filename
|
||||||
|
from pretalx.common.views.mixins import EventPermissionRequired
|
||||||
|
|
||||||
|
from pretalx_broadcast_tools.management.commands.export_voctomix_lower_thirds import (
|
||||||
|
get_export_targz_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BroadcastToolsLowerThirdsVoctomixDownloadView(EventPermissionRequired, View):
|
||||||
|
permission_required = "agenda.view_schedule"
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
targz_path = get_export_targz_path(self.request.event)
|
||||||
|
if not targz_path.exists():
|
||||||
|
raise Http404()
|
||||||
|
response = FileResponse(open(targz_path, "rb"), as_attachment=True)
|
||||||
|
response["Content-Disposition"] = (
|
||||||
|
f"attachment; filename={safe_filename(targz_path.name)}"
|
||||||
|
)
|
||||||
|
return response
|
Loading…
Reference in a new issue