We frequently set up a collection of 10 to 20 slides as a pre-show before our programming for 30 or so minutes using the Slideshow module. However, we encounter the slideshow limitation: once all the slides have been displayed once, the slideshow ends, regardless of the total duration set. To achieve our desired length, we duplicate and manually number the slides, which is quite cumbersome when looking to keep the same rotation order over and over. Would it be possible to add a “loop” option to
I've been working on a system that generates movie-specific content on the fly every time you launch a PreShow sequence. Instead of static slides, PSE now dynamically creates a "Tonight's Feature" title card and (optionally) 30 custom trivia slides tailored to whatever film you're about to watch. I wanted to share the approach in case others want to build on it.
Here's what it looks like in practice: you press Start PreShow on "Fargo," and a few seconds later your sequence opens with a custom title card reading "FARGO" on a theater marquee background, followed by 30 slides of trivia, behind-the-scenes facts, and Q&A specific to that film — all generated fresh every time.
The Problem
PSE's slideshow module is great for static trivia packs, but I wanted content that adapts to whatever movie I'm watching. The challenge is that PSE caches slideshow content when it builds the sequence, so you can't just generate images asynchronously and hope they show up in time.
I tried a lot of approaches that didn't work before finding one that does:
Action module with Python script — PSE runs Python actions inside Kodi's embedded Python, which doesn't have libraries like Pillow installed. Also runs asynchronously — PSE moves to the next module before the script finishes.
Generating images and hoping the slideshow picks them up — PSE caches the slide directory contents at sequence build time. If your images aren't there yet, they won't appear.
Overwriting existing placeholder images — PSE caches the actual image data, not the file reference. Overwriting the file after caching has no effect.
Skin button interception — Tried adding a RunScript call before PSE launches from the skin's context menu. Unreliable timing.
The Solution: Hooking into experience.py
The key insight is that PSE resolves the feature film's metadata (title, genre, year, cast, etc.) before it builds and caches the sequence. There's a window between "PSE knows what movie you picked" and "PSE scans the slideshow directories" where we can inject code.
The hook goes into PSE's
experience.py, specifically in the method that resolves the feature film from Kodi's library. After PSE appends the feature to its internal list, we write the metadata to temp files and call external Python scripts that generate our content:The critical detail is
subprocess.call— this blocks PSE until the script finishes. PSE doesn't proceed to sequence building until our content is generated and sitting in the slideshow directory.Important: We call
/usr/bin/python3(system Python) rather than running inside Kodi's Python. This gives us access to any pip-installed library — Pillow for image compositing, requests for API calls, whatever you need. Kodi's embedded Python is limited to its bundled packages.Finding the Right Spot
To find where to inject, look for this pattern in
experience.py:Search for
self.features.append(feature)nearfeatureFromId— there's one instance in the method that handles the "Start PreShow" button press. On my install it's around line 1060.After editing, you must delete the bytecode cache or Kodi will keep running the old version:
Then restart Kodi.
What You Can Build With This
Once you have the hook in place, the feature metadata is available to any external script. Here's what's in the
self.features[-1]object:title — Movie title
year — Release year
genres — List of genres (Action, Horror, Drama, etc.)
directors — Director names
cast — Cast list with names and roles
rating — MPAA rating
runtime — Runtime in seconds
studios — Studio names
tags — Kodi library tags
With this data you could:
Generate a custom "Tonight's Feature" title card with the movie name
Create genre-themed trivia (horror trivia for horror films, sci-fi for sci-fi)
Select decade-appropriate trailers by dynamically populating a trailer directory
Generate MPAA-style rating cards
Pick studio-specific intro bumpers
Call an AI API to generate movie-specific trivia and Q&A slides
Create "on this day in cinema history" slides based on the current date and film era
Example 1: Dynamic Title Card
My first use case was a "Tonight's Feature" title card — a theater marquee background with the movie title composited in a vintage font. The script reads the title from the temp file, uses Pillow to render text onto a template image, and saves it to a slideshow directory.
The core of the compositing script:
The real script has auto-sizing logic (shrinks the font for long titles), word wrapping, and handles the CIFS font loading issue (Pillow can't load fonts from network shares — copy to
/tmp/first). But the concept is this simple.In the PSE sequence, a Slideshow module points at the
Tonightdirectory and displays whatever image is in there.Runtime: About 1.4 seconds on a Vero 5. Barely noticeable in the PSE launch flow.
Example 2: AI-Generated Trivia Slides
The more ambitious extension calls the Claude API with the movie's metadata and asks it to generate trivia facts and Q&A pairs. The response is parsed and rendered into 30 slides — 10 trivia facts plus 10 question/answer pairs (20 slides).
The flow:
Each slide gets rendered with a vintage cinema aesthetic — dark background, golden category headers, cream body text, decorative borders. Question slides use blue headers, answer slides use green.
The trivia generation is opt-in — I have a toggle script that creates/removes a flag file (
/tmp/pse_trivia_enabled). The generation script checks for this file and skips if it's not there. A Kodi notification confirms the toggle state.Runtime: About 8-10 seconds for the API call plus slide rendering. A progress notification shows during generation so you know it's working.
Tips for Implementation
CIFS/Network Share Gotcha: If your PSE content lives on a network share, Pillow's FreeType library cannot load fonts from CIFS mounts. Always copy fonts and templates to
/tmp/before opening them with Pillow.Bytecode Cache: Kodi caches compiled Python as
.pycfiles. Every time you editexperience.py, delete the cache and restart Kodi or your changes won't take effect.PSE Updates: When PSE updates,
experience.pygets overwritten. Keep a backup of your modified version and re-apply the hook after updates. The hook is small enough that re-applying takes 30 seconds.Testing: You can test your generation scripts independently from the command line:
Then check the output directory for your generated slides.
Keep Scripts Fast: Since
subprocess.callblocks PSE initialization, your scripts need to finish in a reasonable time. The title card takes ~1.4 seconds, the AI trivia takes ~8-10 seconds. Much longer than that and the delay before the sequence starts becomes noticeable.Sequence Setup
My PSE sequence order:
Slideshow —
Tonightdirectory (dynamic title card, 30-60 second duration)Slideshow —
Triviadirectory (AI trivia slides, 10 second per slide)Trailer modules
Audio Format Bumper
Action — Enable refresh rate switching
Feature
The two slideshow modules point at directories that get populated fresh every time by the hook scripts. Everything else in the sequence is standard PSE configuration.
What's Next
I'm exploring a few more ideas with this system: dynamically selecting trailer folders based on the feature's genre/decade, generating "cinema history" slides that tie the film to its cultural moment, and creating custom intermission cards for double features. The hook pattern is flexible enough that any script with access to the movie metadata can feed content into the sequence.
If anyone builds on this or has questions about the implementation, happy to help. The hardest part was figuring out the right injection point — once that's solved, the rest is just Python scripting.