A powerful video preview generation service for Jellyfin
JellyPreviews automatically generates video preview clips for your Jellyfin media library, providing Netflix-style hover previews for an enhanced browsing experience.
JellyPreviews automatically generates video preview clips for your Jellyfin media library, providing Netflix-style hover previews for an enhanced browsing experience.
- Automatic preview generation with smart scene detection
- Hardware-accelerated encoding support (Intel QSV, NVIDIA NVENC, VA-API)
- Intelligent caching system for instant playback
- Webhook notifications for Discord and other services
- Customizable encoding presets for different quality/performance needs
- Multi-server support to manage multiple Jellyfin instances
This project was inspired by and uses partial code from BobHasNoSoul's jellyfin-video-previews. Special thanks to BobHasNoSoul for the original JavaScript implementation and the inspiration that made this project possible.
- Automatic Preview Generation: Create 3-5 second preview clips from movies and TV episodes
- On-Hover Playback: Previews play automatically when hovering over items in Jellyfin
- Smart Container Detection: Automatically handles TV Series and Seasons by fetching the first episode
- Multiple Format Support: WebM, MP4, and MKV output formats
- Hardware Acceleration: Optional GPU encoding (NVIDIA NVENC, Intel QuickSync, AMD VA-API)
- Intelligent Caching: Efficient storage with LRU cache management and automatic cleanup
- Library Management: Scan and batch-generate previews for entire libraries
- Webhook Integration: Auto-generate previews when new content is added to Jellyfin
- Discord Notifications: Send preview notifications to Discord with embedded video files
- Multi-Server Support: Connect to multiple Jellyfin servers simultaneously
- Real-time Monitoring: Web-based admin panel with job tracking and system metrics
- Customizable Settings: Configure duration, format, codec, bitrate, and more
- Docker or .NET 8.0 Runtime
- Jellyfin Server (10.8.0+)
- FFmpeg (included in Docker images)
Basic Setup:
docker run -d \
--name jellypreviews \
-p 8080:8080 \
-v ./cache:/data/cache \
-v ./config:/config \
-e JELLYFIN_SERVER_URL=http://jellyfin:8096 \
-e JELLYFIN_API_KEY=your-api-key-here \
jellypreviews:latestDocker Compose:
version: '3.8'
services:
jellypreviews:
image: jellypreviews:latest
container_name: jellypreviews
ports:
- "8080:8080"
volumes:
- ./data/cache:/data/cache
- ./data/config:/config
environment:
- JELLYFIN_SERVER_URL=http://jellyfin:8096
- JELLYFIN_API_KEY=your-api-key
- PREVIEW_DURATION_SECONDS=4
- PREVIEW_FORMAT=webm
- CONCURRENCY=2
restart: unless-stoppedWith GPU Support (NVIDIA):
services:
jellypreviews:
image: jellypreviews:latest
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=all
- FFMPEG_CODEC=h264_nvencJS Injector
Add this script with JS Injector:
// set the global variable first
window.PREVIEW_SERVICE_URL = "http://your-server:8080";
// create the script element
const script = document.createElement("script");
script.src = window.PREVIEW_SERVICE_URL + "/config/vidprev.js?version=multi";
script.async = true;
// append to page
document.head.appendChild(script);With custom settings:
// Set PREVIEW_CONFIG
window.PREVIEW_CONFIG = {
playbackSpeed: 1.0,
hoverDelay: 1000,
maxPollAttempts: 30,
pollInterval: 2000,
maxCacheSize: 30
};
// Set PREVIEW_SERVICE_URL
window.PREVIEW_SERVICE_URL = "http://your-server:8080";
// Dynamically load the script
const script = document.createElement("script");
script.src = "http://your-server:8080/config/vidprev.js?version=multi"; // <-- your JS URL
script.async = true;
document.head.appendChild(script);Direct Injection Directly in your Jellyfin index.html:
<script>
window.PREVIEW_SERVICE_URL = 'http://your-server:8080';
</script>
<script src="http://your-server:8080/config/vidprev.js?version=multi"></script>With Custom Settings
<script>
window.PREVIEW_CONFIG = {
playbackSpeed: 1.0,
hoverDelay: 1000,
maxPollAttempts: 30,
pollInterval: 2000,
maxCacheSize: 30
};
window.PREVIEW_SERVICE_URL="https://your-server:8080";
</script>
<script src="http://your-server:8080/config/vidprev.js?version=multi"></script>Navigate to: http://your-server:8080/
| Variable | Default | Description |
|---|---|---|
JELLYFIN_SERVER_URL |
- | Jellyfin server URL (required) |
JELLYFIN_API_KEY |
- | Jellyfin API key (required) |
PREVIEW_DURATION_SECONDS |
4 |
Preview clip duration (1-30) |
PREVIEW_FORMAT |
webm |
Output format (webm, mp4, mkv) |
FFMPEG_CODEC |
libx264 |
Video codec (see below) |
PREVIEW_BITRATE |
300k |
Target bitrate |
INCLUDE_AUDIO |
true |
Include audio in previews |
CONCURRENCY |
2 |
Concurrent generation jobs (1-16) |
CACHE_DIR |
/data/cache |
Cache directory path |
MAX_CACHE_SIZE |
10737418240 |
Max cache size (10GB) |
Software (CPU):
libx264- H.264 (most compatible)libx265- H.265 (better compression)libvpx-vp9- VP9 (best for WebM)
Hardware (GPU):
h264_nvenc- NVIDIA NVENCh264_qsv- Intel QuickSynch264_vaapi- AMD VA-API
Configuration is stored in /config/settings.json:
{
"previewDurationSeconds": 4,
"previewStartSeconds": 300,
"previewFormat": "webm",
"ffmpegCodec": "libx264",
"previewBitrate": "300k",
"includeAudio": true,
"concurrency": 2,
"jellyfinServers": [
{
"name": "My Server",
"url": "http://jellyfin:8096",
"apiKey": "your-api-key",
"enabled": true
}
],
"webhookEnabled": false,
"autoGenerateEnabled": false
}- Hover over any movie or TV show in Jellyfin
- Preview generates automatically on first hover (takes 20-30 seconds)
- Subsequent hovers play instantly from cache
- Open Admin Panel:
http://your-server:8080/ - Navigate to Library Management tab
- Select your Jellyfin server and library
- Click Generate All Previews
- Monitor progress in Jobs tab
- Install Jellyfin Webhook plugin
- Configure webhook to POST to:
http://jellypreviews:8080/api/webhook/jellyfin - Enable auto-generation in JellyPreviews settings
- Previews generate automatically when content is added
Docker Compose:
services:
jellypreviews:
runtime: nvidia
environment:
- NVIDIA_VISIBLE_DEVICES=all
- FFMPEG_CODEC=h264_nvenc
- HARDWARE_ACCELERATION=cudaRequirements:
- NVIDIA GPU (GTX 600+ or newer)
- NVIDIA Container Toolkit installed
- CUDA support
Docker Compose:
services:
jellypreviews:
devices:
- /dev/dri:/dev/dri
environment:
- FFMPEG_CODEC=h264_qsv
- HARDWARE_ACCELERATION=qsvRequirements:
- Intel CPU with QuickSync (6th gen+)
- iGPU enabled in BIOS
/dev/dridevice access
Docker Compose:
services:
jellypreviews:
devices:
- /dev/dri:/dev/dri
environment:
- FFMPEG_CODEC=h264_vaapi
- HARDWARE_ACCELERATION=vaapiRequirements:
- AMD GPU with VA-API support
- VA-API drivers installed
/dev/dridevice access
- ASP.NET Core Web API: REST API for preview generation and status
- Background Worker: Processes preview generation jobs
- Cache Service: Manages preview storage and retrieval
- Jellyfin Integration: Authenticates and fetches media metadata
- FFmpeg Service: Handles video encoding
- Admin Dashboard: Web-based management interface
- Client Script: JavaScript injection for Jellyfin hover functionality
┌──────────┐
│ User │
│ Hovers │
└────┬─────┘
│
▼
┌─────────────┐
│ vidprev.js │
└─────┬───────┘
│
├─► Check browser cache
│ ├─► HIT: Play immediately
│ └─► MISS: Continue
│
▼
┌──────────────┐
│ POST /preview│
└──────┬───────┘
│
▼
┌──────────────────┐
│ API Controller │
└──────┬───────────┘
│
├─► Check server cache
│ ├─► HIT: Return preview
│ └─► MISS: Continue
│
├─► Check job queue
│ ├─► EXISTS: Return job ID
│ └─► NONE: Create job
│
▼
┌──────────────┐
│ Job Queue │
└──────┬───────┘
│
▼
┌──────────────┐
│ Worker │
└──────┬───────┘
│
├─► Download from Jellyfin
├─► Run FFmpeg
├─► Save to cache
└─► Update job status
│
▼
┌──────────────┐
│ Cache │
└──────┬───────┘
│
▼
┌──────────────────┐
│ vidprev.js │
│ (polling status) │
└──────┬───────────┘
│
▼
┌──────────────┐
│ Play Preview │
└──────────────┘
GET /preview?id={itemId}- Get or generate previewGET /preview/status/{hash}- Check generation statusGET /preview/download/{hash}- Download preview file
GET /admin/api/status- Get system statusGET /admin/api/jobs- List all jobsPOST /admin/api/jobs/{jobId}/cancel- Cancel jobGET /admin/api/cache- List cached previewsDELETE /admin/api/cache/{hash}- Delete cached previewGET /admin/api/settings- Get configurationPUT /admin/api/settings- Update configuration
POST /api/webhook/jellyfin- Receive Jellyfin webhookGET /api/webhook/jellyfin/test- Test webhook endpoint
See API_Endpoints.md for complete API documentation.
| Scenario | Time per Preview | Throughput |
|---|---|---|
| Software Encoding (CPU) | 20-30s | ~120/hour |
| Hardware Encoding (GPU) | 3-5s | ~720/hour |
| 4K Content (CPU) | 60-90s | ~40/hour |
| 4K Content (GPU) | 10-15s | ~240/hour |
- Storage: ~150KB per preview (4s @ 300k bitrate)
- 1000 movies: ~150MB total cache
- 10000 movies: ~1.5GB total cache
- Hit Rate: 90%+ after initial generation
- CPU: 50-100% per job (software encoding)
- RAM: ~512MB base + ~100MB per concurrent job
- Storage: Variable (depends on cache size)
- Network: Minimal (only for Jellyfin API calls)
Check:
- Service is running:
docker ps | grep jellypreviews - Client script is loaded: Open browser console (F12), look for
[VidPrev]messages - Test endpoint:
curl http://localhost:8080/health - Check logs:
docker logs jellypreviews -f
Check:
- FFmpeg is available:
docker exec jellypreviews ffmpeg -version - Storage space:
df -h /data/cache - File permissions: Cache directory must be writable
- Video codec compatibility: Try different codec
Solutions:
- Enable hardware acceleration
- Lower preview quality:
PREVIEW_BITRATE=200k - Shorter duration:
PREVIEW_DURATION_SECONDS=3 - Increase concurrency:
CONCURRENCY=4
See Troubleshooting.md for detailed solutions.
- Getting Started - Quick start guide
- Functions and Features - Complete feature list
- Docker Build Guide - Build and deployment
- Environment Variables - Configuration reference
- FFmpeg Configuration - Encoding settings
- Webhook Setup - Automation guide
- Multi-Server Guide - Multiple servers
- API Endpoints - API reference
- Troubleshooting - Common issues
# Clone repository
git clone https://github.com/Angablade/JellyPreviews.git
cd JellyPreviews
# Build with .NET
dotnet build
# Run locally
dotnet run --project JellyPreviews
# Build Docker image
docker build -t jellypreviews:latest .- .NET 8.0 SDK
- FFmpeg (for local development)
- Jellyfin server for testing
JellyPreviews/
├── Controllers/ # API endpoints
├── Services/ # Business logic
├── Models/ # Data models
├── Workers/ # Background workers
├── wwwroot/ # Static files (admin panel)
│ ├── js/ # JavaScript modules
│ ├── css/ # Stylesheets
│ └── img/ # Images
├── Docs/ # Documentation
└── Dockerfile # Docker build configuration
- Jellyfin - Open source media server
- FFmpeg - Video processing framework
- ASP.NET Core - Web framework
- Docker - Containerization platform
- BobHasNoSoul - Original inspiration and codebase
- Issues: GitHub Issues
- Documentation: Docs Folder
- Admin Panel: Built-in documentation viewer`
Made with ❤️ for the Jellyfin community
