This is a web app that generates and serves random frames from a TV show or similar content, as both a curiosity and a guessing game. It is free software licensed under the AGPL, meaning anyone can set up their own instance for their favorite show, as long as they make the source code (if modified) freely available.
Note that this software is agnostic of what show is randomized from. The content served is the instance operator's sole responsibility. If you have concerns about the content served by a specific instance, contact the site operator instead of the maintainers of this project.
If you just want to set up an instance, you need to supply a JSON config file with information about your show, a recent version of Node.js, and have episodes accessible on disk. These instructions are for Linux, but it may be possible to set up a server on other platforms.
The built version doesn't require running npm install
, instead pre-bundling all dependencies needed thanks to Nuxt. However, the following dependencies aren't packaged with it.
- Node.js. Make sure that you have a recent enough version of Node.js available. At the time of this writing, the Debian stable version is
12
, while this project requires the Node.js18
. You can use a tool like Node Version Manager to prepare the right Node.js version. - ffmpeg, ffprobe.
ffmpeg
is invoked on-demand to extract frames.ffprobe
is used to determine the length of each episode, to be able to generate a random time in the episode. - ImageMagick identify.
identify
(from ImageMagick) is used to determine if a frame isn't interesting enough to show, for example, if it's a black screen/part of a fade, is just a single solid color, etc. If this feature is disabled, identify isn't used.
You need to supply a config file for the show. (Comments are not allowed in normal JSON; they are just used to document this example.) The episodes
section can be downloaded from TMDB using its API and the tool frame-randomizer-create
.
{
"commonTimings": {
// Common timings for all episodes.
"credits": {
// If there are credits that play until the end.
"start": "22:12"
}
},
// Fallback language if episode data is unavailable in the user's locale.
"defaultLanguage": "en",
// List of episodes.
"episodes": [
{
"episode": 1, // Season number.
"perLanguage": [
{
"language": "en",
"name": "My First Episode", // Episode name.
// Plot overview (synopsis).
"overview": "In the first episode, wacky hijinks occurred."
}
],
"season": 1, // Season number.
// Episode timing info, used to ignore sections. See Timings in utils/file.ts.
"timings": {
"intro": {
// If there is an intro that plays from the beginning.
"end": "1:10"
}
}
} // Rest of the episodes...
],
"name": {
// Default language name.
"name": "My Great TV Show",
// Per-language show names.
"perLanguage": [{ "language": "en", "name": "My Great TV Show" }]
}
}
Consult the RuntimeConfig
section of nuxt.config.ts
for all the settings that are available. Each will be configurable as environment variables. For example, you might create a simple script:
app.env
:
export NUXT_SHOW_DATA_PATH="/path/to/episode_config.json"
export NUXT_PUBLIC_INSTANCE_NAME="My Randomizer"
export NUXT_VIDEO_SOURCE_DIR="/path/to/video/files/"
# Include file contents in variable.
export NUXT_PUBLIC_INSTANCE_INFO_DATA="$(cat info.json)"
export PORT=3000
# Additional configs...
Once these configs are set, grab a built version from releases on Github or build a custom one using the development instructions. Inside, running server/index.mjs
will start the server.
# Simple version.
$ source app.env && node path/to/server/index.mjs
# Launch in a subshell so variables don't persist between runs. Otherwise, you
# may not realize that variables that you remove from app.env are still set in
# the current shell.
$ (source app.env && node path/to/server/index.mjs)
# sh version; don't have to (and can't) include "export" in app.env.
$ sh -ac '. ./app.env && node path/to/server/index.mjs'
There are a couple options for favicon in the base version without building a custom app.
- Use the default favicon (don't change anything).
- Generate an emoji favicon using the
FR_FAVICON_EMOJI
env param. This is intended to include an emoji; if there are multiple they will be drawn on top of one another.
Changing the favicon to a custom image requires building the app yourself. Unfortunately, moving new images into public/
in the prebuilt version won't work, because the file size of the image is hardcoded.
Currently, the software is in an unstable state; required options, option availability, and option interpretation can change between versions. Check the changelog or commit history when upgrading or merging from upstream.
One of the main constraints for cloud hosting is the amount of disk needed. For a long running TV show, SSD can be quite expensive. Settling for regular disk may be the pragmatic option. At the time of this writing, Oracle Cloud's free tier offers a relatively large amount of SSD (200 GB).
Depending on load, the server can also be quite CPU-intensive due to running ffmpeg. However, unless high load is constant, you may be able to get by with less than you think thanks to pregeneration evening out load.
This app is built on Nuxt 3; check out the Nuxt 3 documentation to learn more.
Make sure to install the dependencies:
npm install
Start the development server on http://localhost:3000
npm run dev
Note that there will be an error message:
ERROR
[vue-tsc] Found 0 errors. Watching for file changes.
This is a spurious error message/not an error; disregard it.
Build the application for production:
npm run build
Locally preview production build:
npm run preview
Check out the deployment documentation for more information.
Known issue: Nuxt appears to hardcode some paths at build time, so running npm run build
and then deploying on a server as a user with a different username will result in it looking for paths with the user it was originally built as. An easy, disgusting workaround is to symlink the entire user directory (/home/username
) to the real user.
A Husky precommit hook is set up to check the formatting and lint the code before committing. You can run npm run format
and npm run check
to format and lint your code periodically.