2025-10-21, 09:13 PM
I am trying to do the same thing with some success. If you check the box to save trickplay data in the media folder then you can pre-generate the trickplay files and copy them to
My setup also uses read-only directories for jellyfin and I have found that this trickplay setup does not work automatically all the time. I might be missing something but I have not found a way to tell jellyfin that the trickplay files exist, without having it try to generate trickplay files for all media that does not have the trickplay files.
The only way I was able to get it to work was to manually modify the database for a specific media file to include the trickplay config settings, which is not ideal and probably the wrong way to do this.
This is the bash script I have been using to generate the trickplay thumbnails.
<media-name-without-ext>.trickplay/320 10x10/0.jpg (assuming you are generating trickplay preview files of 10x10 grids at 320px wide.My setup also uses read-only directories for jellyfin and I have found that this trickplay setup does not work automatically all the time. I might be missing something but I have not found a way to tell jellyfin that the trickplay files exist, without having it try to generate trickplay files for all media that does not have the trickplay files.
The only way I was able to get it to work was to manually modify the database for a specific media file to include the trickplay config settings, which is not ideal and probably the wrong way to do this.
This is the bash script I have been using to generate the trickplay thumbnails.
bash
#!/bin/bash
# USAGE: ./generate_trickplay.sh "Your Video File.mp4"
set -e
INPUT="$1"
if [ -z "$INPUT" ]; then
echo "Usage: $0 <video-file>"
exit 1
fi
# ------------------- CONFIGURATION -------------------
FPS="1/10" # One thumbnail every 10 seconds
WIDTH=320 # Thumbnail width
TILE_COLS=10 # Thumbnails per row in sprite
MAX_THUMBS_PER_SPRITE=100 # Max thumbnails per JPEG sprite (adjust as needed)
# ------------------------------------------------------
BASENAME=$(basename "$INPUT")
FILENAME="${BASENAME%.*}"
OUTPUT_DIR=$(dirname "$INPUT")"/$FILENAME.trickplay"
OUTPUT_DIR_FOR_STITCHFILES="${OUTPUT_DIR}/320 - 10x10"
TMPDIR=$(mktemp -d)
THUMBS_DIR="$TMPDIR/thumbs"
mkdir -p "$THUMBS_DIR"
mkdir -p "$OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR_FOR_STITCHFILES"
echo "Extracting thumbnails every 10s..."
echo "temporary dir: $THUMBS_DIR"
# ffmpeg -i "$INPUT" -vf "fps=$FPS,scale=$WIDTH:$HEIGHT" "$THUMBS_DIR/thumb%05d.jpg" -hide_banner -loglevel error
ffmpeg -threads 8 -noautorotate -i file:"$INPUT" -an -sn -vf "fps=$FPS,setparams=color_primaries=bt709:color_trc=bt709:colorspace=bt709,scale=trunc(min(max(iw\,ih*(a*sar))\,$WIDTH)/2)*2:trunc(ow/(a*sar)/2)*2,format=yuv420p" -threads 8 -c:v mjpeg -qscale:v 4 -f image2 "$THUMBS_DIR/thumb%08d.jpg"
THUMB_PATH="$THUMBS_DIR/thumb00000001.jpg"
HEIGHT=$(identify -format "%h" "$THUMB_PATH")
WIDTH=$(identify -format "%w" "$THUMB_PATH")
TOTAL_THUMBS=$(ls "$THUMBS_DIR" | wc -l)
TOTAL_SPRITES=$(( (TOTAL_THUMBS + MAX_THUMBS_PER_SPRITE - 1) / MAX_THUMBS_PER_SPRITE ))
echo "Total thumbnails: $TOTAL_THUMBS"
echo "Generating $TOTAL_SPRITES sprite images..."
VTT_FILE="$OUTPUT_DIR/index.txt"
> "$VTT_FILE"
echo "WEBVTT" >> "$VTT_FILE"
echo "" >> "$VTT_FILE"
COUNTER=0
SPRITE_INDEX=0
convert -size ${WIDTH}x${HEIGHT} xc:black "$THUMBS_DIR/black.jpg"
for ((i=0; i<TOTAL_THUMBS; i++)); do
if (( i % MAX_THUMBS_PER_SPRITE == 0 )); then
SPRITE_FILES=()
for ((j=0; j<MAX_THUMBS_PER_SPRITE && i+j<TOTAL_THUMBS; j++)); do
PADDED_NUM=$(printf "%08d" $((i + j + 1)))
SPRITE_FILES+=("$THUMBS_DIR/thumb${PADDED_NUM}.jpg")
done
if (( j < MAX_THUMBS_PER_SPRITE )); then
for ((;j<MAX_THUMBS_PER_SPRITE;j++)); do
SPRITE_FILES+=("$THUMBS_DIR/black.jpg")
done
fi
# Create sprite
PADDED_NUM=$(printf "%08d" $((i/100 + 1)))
SPRITE_NAME="$((i/100)).jpg"
montage "${SPRITE_FILES[@]}" -tile ${TILE_COLS}x -geometry ${WIDTH}x${HEIGHT}+0+0 "$OUTPUT_DIR_FOR_STITCHFILES/$SPRITE_NAME"
SPRITE_INDEX=$((SPRITE_INDEX + 1))
fi
done

