a slide generator - intended to be used for sunday services with OBS

Noah Vogt 8f52789b70 fully integrate ssync + update docs 2 年之前
config 8f52789b70 fully integrate ssync + update docs 2 年之前
input 8f52789b70 fully integrate ssync + update docs 2 年之前
media 8f52789b70 fully integrate ssync + update docs 2 年之前
slides 8f52789b70 fully integrate ssync + update docs 2 年之前
sync d1f270e8fd add initial ssync integration 2 年之前
utils d1f270e8fd add initial ssync integration 2 年之前
.gitignore 34e468cb33 use configuration via python files containing constants 2 年之前
LICENSE bab82a9d11 init commit 2 年之前
README.md 8f52789b70 fully integrate ssync + update docs 2 年之前
slidegen.py d1f270e8fd add initial ssync integration 2 年之前
ssync.py d1f270e8fd add initial ssync integration 2 年之前
template.txt bab82a9d11 init commit 2 年之前

README.md

slidegen

As the name may partially imply, slidegen.py generates song slides as images - out of a plain text input file - intended for use with [OBS]() to livestream a typical (contemporary) Sunday church service.

This program is also intended to be used in conjunction with ssync.py, which is basically a wrapper script that automatically syncs a local copy with the remote slide repository, removes the old obs slides and lets the user interactively choose the new slides with a smart fuzzy finder. It stands for slidesync, by the way.

Standalone use of slidegen.py is possible and can sure fit other use cases.

Why this program exists

To add song slides to OBS or similar software as input sources, there exist the following obvious options:

  • generating song slides via a text or presentation documents, exported in printable form and converted to images afterwards
  • generating images through image manipulation or designing software and export to images

Both of these processes have major downsides: They are hard to automate, take a long time to create the slides, have very limited to support for bulk operations on the song repository (like wanting to change the theme and layout of all slides or changing the metadata shared by a lot of songs) and maintaining is a lot harder because of bad portability and complex source files.

The only upside they have is that can be more intuitive for inexperienced computer users, but changing a text file template and uploading to a remote storage should not be too hard to manage and worth it as it has none of the above mentioned downsides.

Usage

Commandline Interface

As mentioned above, this program is not made to be executed directly. Therefore the commandline interface is not yet fully stable. Generally, the syntax is as follows

./slidegen.py SRC_PATH DEST_DIR PROMPT_INPUT

with SRC_PATH being the path to the song plain text file, DEST_DIR the output directory where the slide image file output is placed and PROMPT_INPUT

Here a short example:

./slidegen.py "../songrepo/Stille Nacht.txt" "~/Documents/Song Slides 1"

The wrapper script doesn't have any additional arguments, as all the relevant variables are to be defined in the configuration file, but more on that below. Thus, execute ssync like this:

./ssync.py

Source File Layout

The file is divided into two what we will here call parts that are divided with at least one \n character and an arbitrary amount of lines that are either empty or only contain whitespace:

  • the metadata header (top of the file)
  • the text body (bottom of the file)

1. Part: The Metadata Header

As the top of the file are these five metadata entries. We call them metadata strings:

  • title
  • book
  • text
  • melody
  • structure

As for the syntax, each line starts with the metadata string, followed by ": ", and ends with a non-empty string that acts as the value of the metadata string, which we call metadata value. The structure is allowed only R and non-negative integers values separated by one comma each, which represents the structure or order in which to song is to be played by dividing the song into refrain and verses. The values in between the commas are called structure elements.

Hence to check if a line in the header has the correct syntax use the following regular expression: ^(?!structure)\S+: .+|^structure: ([0-9]+|R)(,([0-9]+|R))*$. What is not captured syntactically by this regex is that each metadata string and value pair must appear exactly once in the header.

The semantics of the other metadata values can be pretty much whatever you want, as long as they adhere to the syntax. Note also that the arrangement of lines doesn't have to match the list above, e.g. the line with the book metadata string can be above the line containing the title metadata, but note that it is probably better to not mix this up anyway for quicker manual editing's sake.

Example of a metadata header:

title: Amazing Grace
book: Pretty much every protestant songbook
text: John Newton (1779)
melody: American folk tune
structure: 1,2,3

2. Part: The Text Body

To start off with the syntax, every unique structure element, will be placed on a separate line between an opening square bracket directly to the left and an closing square bracket directly to the right, e.g. [R] or [3]. We call these lines structure element identifiers. After each of those lines must follow a finite amount of lines that semantically carry the song text of the corresponding refrain or verse indicated by the structure element. The lines which are empty or only contain whitespace after the last line that doesn't meet these two requirements before either the next structure element identifier or the end of the file are ignored, while the lines that are empty or only contain whitespace before that last line who doesn't meet these two requirements are not ignored, as they semantically could act as separators intended by the user. Note that such ignored lines are not syntactically necessary, but are heavily encouraged for their clearer separation they provide semantically.

Here is a example of a text body using the first three verses of 'Amazing Grace' written by John Newton found on Hymnary.org:

[1]
Amazing grace (how sweet the sound)
that saved a wretch like me!
I once was lost, but now am found,
was blind, but now I see.

[2]
'Twas grace that taught my heart to fear,
and grace my fears relieved;
how precious did that grace appear
the hour I first believed!

[3]
Through many dangers, toils and snares
I have already come:
'tis grace has brought me safe thus far,
and grace will lead me home.

Configuration

The configuration of both slidegen.py and ssync.py is handled via constants in *.py files. The default configuration is stored in config/default_config.py, and in the same form a custom user-defined configuration can optionally - except for ssync.py which needs user-defined variables - be placed in config/config.py. You don't have to specify all the constants present in the default config, only the one's you want to change.

For example, if you want to change the text color to green and the file extension to "jpeg", your config/config.py could look like this:

TEXT_COLOR = "green"
FILE_EXTENSION = "jpeg"

Now for explanation of the individual entries.

File Format and Naming

IMAGE_FORMAT forces a specific file format when writing the files in formats accepted by ImageMagick. The individual slides get named in this form: ${FILE_NAMEING}${SLIDE_NUMBER}${FILE_EXTENSION}. Hence with the default config of

IMAGE_FORMAT = "jpeg"
FILE_EXTENSION = "jpg"
FILE_NAMEING = "slide-"

the slides would be named slide-1.jpg, slide-2.jpg, slide-3.jpg etc.

Dimensions

WIDTH and HEIGHT present the output resolution of the slide in pixels. Meaning for 4K slides you would have to use

WIDTH = 3840
HEIGHT = 2160

General Colors

Now let us look at the start slide. With BG_COLOR we can set the background for all slides and FG_COLOR sets the color of the what we call titlebar. Note the color values are again in the typical form accepted by ImageMagick.

BG_COLOR = "white"
FG_COLOR = "#6298a4"

Titlebar

TITLE_COLOR = "#d8d5c4"
MAX_TITLE_FONT_SIZE = 70
MIN_TITLE_FONT_SIZE = 20
TITLE_FONT_SIZE_STEP = 10
TITLE_HEIGHT = 160
TITLEBAR_Y = 65
TITLEBAR_TRIANGLE_WIDTH = 80
TITLEBAR_TRIANGLE_HEIGTH = 160

Both TITLEBAR_Y and TITLE_HEIGHT are transformations given in pixels as shown in the following image. THE TITLE_COLOR gives a color of the song title (in this example, "Amazing Grace") in the accepted color format of ImageMagick, same for the font sizes. More in detail, the MAX_TITLE_FONT_SIZE is applied when the song title is not too long, but when is the case, slidegen automatically shrinks down the font size in steps of TITLE_FONT_SIZE_STEP until it reaches the minimum font size specified by MIN_TITLE_FONT_SIZE. The dimensions of the triangle on the right of the titlebar can be give in pixel as the height by TITLEBAR_TRIANGLE_HEIGTH and as the width by TITLEBAR_TRIANGLE_WIDTH.

Titlebar Explanation

Infodisplay

INFODISPLAY_FONT_SIZE = 25
INFODISPLAY_ITEM_WIDTH = 20
INFODISPLAY_X = 1650
INFODISPLAY_Y = 1000

INFODISPLAY_X and INFODISPLAY_Y are the coordinates given as pixels which define where the top-left pixel of the infodisplay is located on the song slides. INFODISPLAY_FONT_SIZE just gives the font size in a format acceptable by ImageMagick. Closely related, INFODISPLAY_ITEM_WIDTH gives the width in pixels of both the letter of a structure element in the infodisplay and the whitespace that follows before the next structure to the right.

Infodisplay Explanation

Player

PLAYER_WIDTH = 560
PLAYER_HEIGHT = 315

The player is intended to be another OBS video source that displays someone or multiple people playing their instruments while the song is playing. The player is anchored on the top right of the slides. In pixels, you can change the dimensions of the player by width via PLAYER_WIDTH and by height via PLAYER_HEIGHT.

This is so that when the text width on maximum font size collides with the player, the font size is gradually decreased - once again only down to the defined minimum font size - until there is no overlap anymore. Hence if you don't intend to use a player just set the dimensions to 0.

Player Explanation

Font Configuration

Slidegen uses two font styles, normal and bold. BOLD_FONT_PATH should point the respecting ttf/otf file for the bold style font and FONT_PATH for the normal style font. FONT and BOLD_FONT should be defined as the name of the font as accepted by ImageMagick. This is as some submodules for ImageMagick - or at least their wand python bindings - cannot reliably convert between name and path of the font and has sometimes problems accessing the system font config. Hence we use these four variables. The default config

BOLD_FONT_PATH = "/usr/share/fonts/TTF/century-gothic/CenturyGothicBold.ttf"
FONT_PATH = "/usr/share/fonts/TTF/century-gothic/CenturyGothic.ttf"
FONT = "Century-Gothic"
BOLD_FONT = "Century-Gothic-Bold"

is made to use the ttf-century-gothic font on the AUR on Arch Linux (-based distros). For different operating systems you may need to use another naming convention for FONT and FONT_BOLD like using spaces instead of dashes, but check out the ImageMagick documentation for more details.

Metadata

METADATA_FONT_SIZE = 36
METADATA_X = 70
METADATA_VALUE_CHAR_LIMIT = 100
BOOK_Y = 260
ATTRIBUTIONS_Y = 930

You can change the font size off the three metadata lines with an acceptable format by ImageMagick using METADATA_FONT_SIZE. The metadata is separated into two block on the start slide: The top part contains the book info and the bottom part contains the attributions. You can set the top part placement in pixels with BOOK_Y and the respecting bottom part with ATTRIBUTIONS_Y. You can also set the maximum character limit of these metadata lines with METADATA_VALUE_CHAR_LIMIT.

Text Canvas

STRUCTURE_ELEMENT_X = 80
STRUCTURE_ELEMENT_Y = 400

On the top left of the Text Canvas, there is a indicator for the structure element of the current slide. The structure element is followed by a dot. Note that the chorus or R is not displayed. You can control the placement in pixels with its X coordinate with STRUCTURE_ELEMENT_X and its Y coordinate with STRUCTURE_ELEMENT_Y.

TEXT_CANVAS_X = 160
TEXT_CANVAS_Y = 400
TEXT_CANVAS_WIDTH = 1600
TEXT_CANVAS_HEIGHT = 600

The Text canvas placement is defined in pixels with its coordinates via TEXT_CANVAS_X and TEXT_CANVAS_Y. Also in pixels are the dimensions in width with TEXT_CANVAS_WIDTH and height with TEXT_CANVAS_HEIGHT.

STRUCTURE_ELEMENT_PER_LINE_CHAR_LIMIT = 85
STRUCTURE_ELEMENT_MAX_LINES = 8
MAX_CANVAS_FONT_SIZE = 55
MIN_CANVAS_FONT_SIZE = 35
CANVAS_FONT_SIZE_STEP = 5
INTERLINE_SPACING = 30

The character limit of each line is defined with STRUCTURE_ELEMENT_PER_LINE_CHAR_LIMIT. The maximum number of lines is set by STRUCTURE_ELEMENT_MAX_LINES. Now all the following variables in this paragraph are defined in a format accepted by ImageMagick. MAX_CANVAS_FONT_SIZE sets the maximum font size in the text canvas, that gets reduced by a delta of CANVAS_FONT_SIZE_STEP until the minimum of MIN_CANVAS_FONT_SIZE is reached. The interline spacing is indicated by INTERLINE_SPACING.

Roadmap

These are some issues and possible changes that will be addressed or at least considered by our future development efforts:

  • prevent all crashes:
    • safe PROMPT_INPUT parsing
    • handle possibly incorrect or insensible configurations safely
  • asynchronous slide generation
  • use caching, with checksum checks for changes in the source file and the PROMPT_INPUT
  • provide ssync with the song structure, display it to the user and prevent him from entering a prompt that would slidegen cause to terminate unsuccessfully
  • add more optional metadata strings
  • use a more typical commandline argument system
  • add more documentation, especially explaining the slide generation and its configuration, but also dependencies and deployment
  • better handling of font path Configuration
  • add tests

Licensing

slidegen is free (as in “free speech” and also as in “free beer”) Software. It is distributed under the GNU General Public License v3 (or any later version) - see the accompanying LICENSE file for more details.