JellyfinUpscalerPlugin
See the codeBuilt with Claude Opus 5.5 — this plugin is developed with Anthropic's Claude models: Opus 5.5 since v1.8.3.33 (including the review of v1.8.3.31–v1.8.3.32 before it), Opus 5 for v1.8.3.13–v1.8.3.30, Fable 5 for v1.8.3.5–v1.8.3.12 and Opus 4.8 before that. v1.8.3.31 and v1.8.3.32 were prepared with OpenAI Codex. Code, Dockerfiles, CI workflows and documentation are written in a pair-programming style with the model; the maintainer (Kuschel-code) reviews, tests and publishes every change. Commits made with Claude carry a
Co-Authored-By: Claudetrailer as disclosure.
AI-powered video upscaling for Jellyfin. Upscale SD content to HD/4K using neural networks, with AI inference in a Docker service and video decoding/encoding on the Jellyfin host.
Release status (2026-10-04): v1.8.3.36 is released for Jellyfin 12.0+ / .NET 10. Library jobs now wait out a busy AI service (HTTP 429/503) instead of failing the whole video, and the AI service explains why an Intel/AMD GPU is not used (see issue #90: pass the host's numeric render group ID, not the name render). Target-server acceptance was not run; actual playback, GPU and HDR target-hardware behavior remain unverified.
Previous release status (2026-09-26): v1.8.3.35 is released for Jellyfin 12.0+ / .NET 10. The player button no longer needs write access to Jellyfin's web folder, and since v1.8.3.34 real-time upscaling starts again in the web client. Jellyfin 10.11 users retain v1.8.3.31, which has both defects. Target-server acceptance was not run; actual playback, GPU and HDR target-hardware behavior remain unverified.
Fixed in v1.8.3.35 (reported in GitHub issue #75):
index.html: on a default Windows install (the server runs as NetworkService under Program Files), with the Debian package, snaps and read-only containers. It showed only in a tab that had opened the plugin's settings. The plugin now adds its script to the page as Jellyfin serves it; the file on disk is left alone./jellyfin), the Lanczos, Anime4K and WebGPU real-time engines never loaded: they were requested without the base URL, and Jellyfin redirected the request to its start page. They now load relative to the web client.Fixed in v1.8.3.34 (reported in GitHub issues #86 and #87 on 2026-09-24):
Fixed in v1.8.3.33 (found in a review of v1.8.3.31–v1.8.3.32 on 2026-09-24):
smpte170m/bt470bg and untagged 10-bit files. HDR now needs real evidence (a PQ/HLG transfer, an HDR range, dynamic metadata, or BT.2020 without an SDR transfer).HDR requires PQ/ST.2084.New in v1.8.3.33: a redesigned in-player menu (live frame-rate readout, filter previews on the playing frame, phone and TV layouts) and settings page (keyboard- and remote-friendly tabs).
Docker Images (released in lockstep with the plugin, both at v1.8.3.36): All seven regular version pins and docker7 tags are published and registry-verified, including ten platform configurations. NVIDIA latest also points to v1.8.3.35. The AI service itself is unchanged since v1.8.3.33. Release notes.
kuscheltier/jellyfin-ai-upscaler:docker7 (NVIDIA CUDA + cuDNN 9)kuscheltier/jellyfin-ai-upscaler:docker7-amd (AMD ROCm)kuscheltier/jellyfin-ai-upscaler:docker7-intel (Intel Arc/iGPU OpenVINO)kuscheltier/jellyfin-ai-upscaler:docker7-apple (Apple Silicon Docker — CPU inference, multi-arch amd64/arm64)kuscheltier/jellyfin-ai-upscaler:docker7-vulkan (Vulkan/ncnn — AMD pre-RDNA2, Intel iGPU)kuscheltier/jellyfin-ai-upscaler:docker7-cpu (CPU Only — multi-threaded ONNXRuntime, multi-arch)kuscheltier/jellyfin-ai-upscaler:docker7-converter (CPU + pth→ONNX converter for OpenModelDB community models — opt-in)Download sizes range from 0.27 GB (docker7-cpu) to 20 GB (docker7-amd, ROCm base) — see docs/DOCKER-IMAGES.md for the full table, the converter's RAM guidance and why the AMD stack is frozen.
Report bugs: GitHub Issues
Contents: Architecture · How It Works · Installation · Features · AI Models · Configuration · Docker Tags · Changelog · Troubleshooting · Support
Download Jellyfin 12 release v1.8.3.35 (requires Jellyfin 12.0+, five runtime DLLs plus meta.json).
Jellyfin's plugin system tries to load ALL .dll files as .NET assemblies. Native C++ libraries (ONNX Runtime, CUDA, OpenCV) caused BadImageFormatException crashes in older versions. The solution: a Docker microservice architecture where the plugin (only ~1.6 MB) communicates with an external AI container via HTTP.
┌──────────────────────────────────────────┐
│ Jellyfin Server │
│ ┌────────────────────────────────────┐ │
│ │ AI Upscaler Plugin v1.8.3.36 │ │
│ │ ~1.6 MB — No native DLLs │ │
│ │ Sends frames via HTTP │ │
│ └──────────────┬─────────────────────┘ │
└─────────────────┼────────────────────────┘
│ HTTP POST /upscale
▼
┌──────────────────────────────────────────┐
│ AI Upscaler Docker Container │
│ ┌────────────────────────────────────┐ │
│ │ Python + FastAPI + OpenCV DNN │ │
│ │ CUDA / ROCm / OpenVINO / CPU │ │
│ │ Real-ESRGAN, SPAN, SwinIR, DAT2 │ │
│ │ EDVR-M, RealBasicVSR, AnimeSR │ │
│ │ EDSR, FSRCNN, ESPCN (76+ models) │ │
│ │ Web UI for Model Management │ │
│ └────────────────────────────────────┘ │
└──────────────────────────────────────────┘
The plugin supports four upscaling modes:
The Scheduled Task ("Scan & Upscale Library") runs daily at 3 AM and:
_upscaled suffix)Movie_upscaled.mkv)This is ideal for users without powerful servers — upscaling happens overnight.
The Scheduled Task ("Scan & Upscale Library Images") runs weekly on Sunday at 4 AM and:
POST /api/upscaler/upscale-images/{itemId}Auto mode is on by default. For every video it answers three questions and tells you the answer:
| It decides | From | What it will not do |
|---|---|---|
| Which model | Content type (genre), source resolution, whether multi-frame models are loaded, and the hardware class the AI service reports | Override a model you selected yourself under Preferred Anime / Live-Action Model — that is annotated, never replaced |
| Which scale | The source size. SD gets a full 4x restore; 720p and 1080p get 2x; a source that is already 4K is cleaned up, not enlarged | Overshoot that target silently — going past it is always a reported substitution with a reason you can read |
| Which filter | Content type | Apply one. Ever. As of v1.8.3.20 auto never writes a filter preset - it offers one in the player's Auto tab and the sidebar with an Apply button, and only when its opinion differs from yours. Filters are taste; models are technique |
The reason is visible wherever the decision is: the dashboard, the in-player Auto tab, and the library-scan log (at Information level, because batch runs happen when nobody is watching).
If the AI service is unreachable, auto does not block — it runs uncapped with the hardware class marked unknown.
In the player, the panel's first tab is Auto: the decision for the file that is playing, plus live switches for auto mode, video filters, face restoration and real-time upscaling. Everything there takes effect immediately, without opening the full configuration page.
When you press play, the plugin enhances the video in real-time. It offers several honest tiers (pick one, or let Auto choose) — each labelled for what it actually is, not marketing:
_upscaled result — and reach the TVs/phones nothing else can.Pair it with what you already have: on a desktop browser you can also use your GPU's own VSR (NVIDIA RTX Video Super Resolution / Intel VSR); for mpv there's mpv-shim + Anime4K. This plugin is the hub that brings anime/AI upscaling to every web/TV/mobile client and batch-upscales your whole library.
How it decides: At playback start, a benchmark runs against the Docker service. If the server can process frames fast enough (≥80% of video FPS), it uses Server AI mode. Otherwise, it falls back to WebGL. If the server cannot keep up during playback (under half the video's frame rate for 5 s) it switches to Lanczos (WebGL); while the service asks it to wait, it hides the stale frame and resumes when the server recovers.
Visual indicators:
Covers things you do not want on screen. It exists because of discussion #11: a user's dog loses her mind whenever a dog or cat appears on TV.
Setup (three steps):
animals by default), save, then press Load Detection Model.It replaces real-time upscaling while it runs. Two full inference passes per frame — upscale and detect — do not keep up with playback on any realistic server, so the stream does one or the other. Batch upscaling is unaffected.
You can also drive it directly, without the player:
# load a detector you imported earlier (none ships - see below)
curl -X POST "$BASE/models/load-detector" -H "X-Api-Token: $TOKEN" \
-F model_name=tiny-yolov3 -F input_size=416
# cover every animal in a frame with a solid box
curl -X POST "$BASE/detect-mask?classes=animals&mode=box&pad=12" \
-H "X-Api-Token: $TOKEN" --data-binary @frame.jpg -o masked.jpg
classes takes COCO names or the animals group, mode is box or blur, and pad grows each box — a detector's box hugs the animal, and the ears sticking out set a dog off just as well. The reply carries X-Detections.
Why not ffmpeg's dnn_detect, which is what was asked for: jellyfin-ffmpeg is built without any DNN backend — 46 --enable-* flags and not one of libopenvino / libtensorflow / libtorch, with the string dnn absent from the build entirely. Those filters cannot run on the ffmpeg Jellyfin ships, whatever the plugin passes them. Accepting arbitrary -vf would also be a security hole rather than a feature: ffmpeg filter syntax includes movie= and subtitles=, both of which read files, so it would hand every authenticated user a file-read primitive.
The detector is downloaded, not bundled. Since v1.8.3.26 tiny-yolov3 is a catalog entry with a verified sha256, so the Models tab fetches and checks it exactly like an upscaler. That pin was computed from the bytes of the file itself, after checking its sha384 against the OpenVINO model zoo's own model.yml — until that was done, the honest answer was "bring your own", because inventing a pin would break the guarantee the importer exists to provide. You can still import any other detector through the upload path, which pins and verifies it the same way. Both families work: single-head exports (YOLOv5/v7/v8/v9) and the NMS-head ONNX YOLOv3 exports. The service tells them apart by reading the loaded model's own inputs and outputs, and rejects one it does not recognise at load time — a misread tensor does not raise, it paints boxes over the wrong part of the picture.
The in-player button lets you:
The v1.8.3.35 release targets Jellyfin.Controller 12.0.0 / .NET 10. Jellyfin 12.1 is also published; see the official releases. Build and runtime evidence is tracked in docs/JELLYFIN-12-READINESS.md; GPU, real-player and HDR target-hardware acceptance remain separate.
Choose the command that matches your GPU:
NVIDIA GPU (recommended):
docker run -d \
--name jellyfin-ai-upscaler \
--gpus all \
-p 5000:5000 \
-v ai-models:/app/models \
kuscheltier/jellyfin-ai-upscaler:docker7
Intel GPU (Arc / Iris):
docker run -d \
--name jellyfin-ai-upscaler \
--device=/dev/dri \
--group-add=render \
-p 5000:5000 \
-v ai-models:/app/models \
kuscheltier/jellyfin-ai-upscaler:docker7-intel
AMD GPU (ROCm):
docker run -d \
--name jellyfin-ai-upscaler \
--device=/dev/kfd --device=/dev/dri \
-p 5000:5000 \
-v ai-models:/app/models \
kuscheltier/jellyfin-ai-upscaler:docker7-amd
Vulkan GPU (AMD RX 5700, Intel iGPU, etc.):
docker run -d \
--name jellyfin-ai-upscaler \
--device=/dev/dri \
--group-add=render \
-p 5000:5000 \
-v ai-models:/app/models \
kuscheltier/jellyfin-ai-upscaler:docker7-vulkan
CPU Only (any platform):
docker run -d \
--name jellyfin-ai-upscaler \
-p 5000:5000 \
-v ai-models:/app/models \
kuscheltier/jellyfin-ai-upscaler:docker7-cpu
CPU + Model Converter (adds .pth community-model conversion, ~2 GB):
docker run -d \r
--name jellyfin-ai-upscaler \r
-p 5000:5000 \r
-v ai-models:/app/models \r
kuscheltier/jellyfin-ai-upscaler:docker7-converter
Verify the container is running: curl http://YOUR_SERVER_IP:5000/health
https://raw.githubusercontent.com/Kuschel-code/JellyfinUpscalerPlugin/main/repository-jellyfin.json
http://YOUR_SERVER_IP:5000After installation, play any video in a web browser (Chrome, Edge, Firefox). You will see an AI upscaler button (sparkle icon) in the player controls. Click it to access:
Note: The player button only works in web browsers. It does NOT appear in native Jellyfin apps (Windows, Android, iOS, TV).
Note (Docker users): If the button doesn't appear immediately, visit the plugin config page once — this activates the player script for the current session.
To batch-upscale your low-resolution content:
:5000 dashboard — sha256-pinned, ZIP-aware; the opt-in docker7-converter image converts .pth models to ONNX with output verification; installs land in the ★ Favorites card/docs and /redoc endpoints for API exploration/metrics endpoint with per-model jobs, failures, frames, timing/health/detailed with GPU health, circuit breaker state, model infohttp://YOUR_SERVER_IP:5000| Category | Models | Scale | Speed | Best For |
|---|---|---|---|---|
| Real-ESRGAN | realesrgan-x4, x4-256, x2-plus, animevideo-x4 | 2-4x | Slow | Best overall quality |
| SPAN | span-x2, span-x4 | 2-4x | Fast | Real-time video |
| SwinIR | swinir-x4, swinir-small-x2/x4 | 2-4x | Medium | Photos & live-action |
| APISR | apisr-x3, apisr-anime-x2 | 2-3x | Medium | CVPR 2024, general & anime |
| Video Real-Time | clearreality-x4, nomosuni-compact-x2, lsdir-compact-x4 | 2-4x | Fast | Low-latency video |
| Video Quality | ultrasharp-v2-x4, nomos2-dat2-x4, nomos2-realplksr-x4 | 4x | Slow | Maximum detail |
| Film Restoration | fsdedither-x4, nomos8k-hat-x4 | 4x | Medium | DVD/VHS cleanup |
| Anime | anime-compact-x4 | 4x | Fast | Lightweight anime |
| Multi-Frame VSR | edvr-m-x4, realbasicvsr-x4, animesr-v2-x4 | 4x | Slow | Temporal consistency (5 frames) |
| OpenCV Classic | edsr-x2/x3/x4, lapsrn-x2/x4/x8, fsrcnn-x2/x3/x4, espcn-x2/x3/x4 | 2-8x | Fast-Medium | CPU-only, lightweight |
| Vulkan/ncnn | realesrgan-x4-vulkan, realesrgan-anime-x4-vulkan, span-x4-vulkan | 4x | Fast | AMD pre-RDNA2, Intel iGPU |
Beyond the curated catalog, the Importable models page lists 660+ OpenModelDB community models — import them one-click from the plugin config page or the :5000 dashboard (.pth models convert automatically with the docker7-converter image).
After installation, find settings under Dashboard → Plugins → AI Upscaler Plugin.
| Setting | Description |
|---|---|
| AI Service URL | URL to Docker container (e.g., http://192.168.1.100:5000) |
| Enable Plugin | Global on/off switch |
| AI Model | Choose upscaling model (auto = intelligent selection per content) |
| Scale Factor | 2x, 3x, or 4x |
| Min Resolution | Threshold for scheduled task (default: 1920x1080) |
| Model Fallback Chain | Comma-separated fallback models (e.g., realesrgan-x4,span-x4,edsr-x4) |
| Preferred Anime Model | Model for anime content (empty = let the heuristic decide). A value here is treated as a deliberate override: it is exempt from the hardware cap and the scale logic |
| Preferred Live-Action Model | Model for live-action content (empty = let the heuristic decide). Same override semantics as above |
| Enable Processing Queue | Priority queue with pause/resume (default: true) |
| Max Queue Size | Maximum items in queue (default: 100) |
| Pause Queue During Playback | Pause processing when user is watching (default: true) |
| Webhook URL | HTTP POST notifications on job complete/failure |
| Enable Health Monitoring | Circuit breaker + health checks (default: true) |
| Circuit Breaker Threshold | Consecutive failures before circuit opens (default: 5) |
| Model Disk Quota MB | Max disk space for cached models (default: 2048) |
| Enable Model Auto Cleanup | LRU cleanup of unused models (default: true) |
| Enable Quality Metrics | Compute PSNR/SSIM scores after upscaling (default: true) |
| Enable Face Enhancement | Detect and enhance faces via GFPGAN ONNX or fallback (default: true) |
| Face Enhance Strength | Blend ratio 0.0–1.0 for face enhancement (default: 0.7) |
| Enable Grain Management | Film grain removal/re-addition pipeline (default: true) |
| Grain Denoise Strength | NL-means filter strength 1–30 (default: 5) |
| Grain Re-add Intensity | Gaussian noise sigma 0–50 for re-grain (default: 0) |
| Enable Custom Model Upload | Allow uploading custom ONNX models at runtime (default: true) |
| Enable API Docs | Toggle /docs and /redoc Swagger endpoints (default: true) |
| Player Button | Show/hide AI button in video player |
| Real-Time Upscaling | Enable/disable real-time enhancement during playback |
| Output Codec | Codec for upscaled videos: H.264, H.265, or copy |
| MaxItemsPerScan | Limit items per scan run (default: unlimited) |
| Tag | GPU | Use Case |
|---|---|---|
:docker7 | NVIDIA CUDA 12.8 (TensorRT opt-in) | RTX 50/40/30/20, GTX 16/10 |
:docker7-amd | AMD ROCm 6.2 | RX 7000, RX 6000 |
:docker7-intel | Intel OpenVINO 2025.4 | Arc A-Series, Iris Xe, iGPU |
:docker7-apple | ARM64 Optimized (multi-arch) | Apple M1–M5 (Docker=CPU, native=CoreML) |
:docker7-vulkan | Vulkan (ncnn) | AMD pre-RDNA2, Intel iGPU, any Vulkan GPU |
:docker7-cpu | Multi-threaded CPU (multi-arch) | Any platform (amd64/arm64) |
:docker7-converter | CPU + Torch/Spandrel | Supported community-model conversion to ONNX |
Each tag is published three ways so you can pin precisely:
:docker7 — rolling tag family (Watchtower auto-updates):docker7-v1.8.3.35 — NVIDIA pin for the currently published release:v1.8.3.35-<backend> — published backend pin (e.g. :v1.8.3.35-cpu):rc-v<version>[-backend] — release-candidate images, published ahead of a release when one is wanted; :rc-v<version>-<commit>[-backend] pins the exact build. The last candidates were :rc-v1.8.3.32…; v1.8.3.33 to v1.8.3.35 went straight to release.CUDA is the default: keep SKIP_TENSORRT=true. Enable TensorRT only with compatible libraries in the image. See Docker setup and controlled updates.
The full version history lives on the website and the release pages — this README no longer duplicates it:
Release: v1.8.3.35 — the player button no longer needs write access to Jellyfin's web folder (Windows installs, Debian package, snaps, read-only containers), and the browser real-time engines load behind a base URL; all seven Docker variants. v1.8.3.31 remains available for Jellyfin 10.11.
The button only appears in web browsers (Chrome, Edge, Firefox, Brave), not in the native apps (Windows app, mobile, TV). Open Jellyfin via http://YOUR_IP:8096.
Since v1.8.3.35 the plugin adds its script to the web client's page as Jellyfin serves it, so a read-only web folder no longer matters (Windows installer, Debian package, snap, read-only containers). After a restart and the first page load, the log (Dashboard → Logs) shows Player script injected via … or Player script added to index.html as Jellyfin serves it.
Neither line? Then Jellyfin is not serving the web client itself (for example, a separate web server serves jellyfin-web), and the plugin cannot reach the page.
On v1.8.3.34 or older, including v1.8.3.31 for Jellyfin 10.11, the plugin can only edit the file itself. If the log says Could not inject player script into index.html, give Jellyfin write access to the index.html it names, then restart Jellyfin:
Program Files. In an administrator Command Prompt, run icacls "C:\Program Files\Jellyfin\Server\jellyfin-web\index.html" /grant *S-1-5-20:M and restart the "Jellyfin Server" service. If you run the tray app instead of the service, use /grant "%USERNAME%":M.sudo chown jellyfin /usr/share/jellyfin/web/index.html, then sudo systemctl restart jellyfin.Until then, opening the plugin's settings page loads the button into that browser tab only; reloading the tab removes it again. A Jellyfin update replaces index.html, so repeat the step if the warning returns.
There is no "Upscale" button on item pages: whole libraries are upscaled by the scheduled task "Scan & Upscale Library" (Dashboard → Scheduled Tasks).
# Check logs
docker logs jellyfin-ai-upscaler --tail 50
# Health check
curl http://YOUR_SERVER_IP:5000/health
# GPU diagnostics
curl http://YOUR_SERVER_IP:5000/gpu-verify
# Check GPU (NVIDIA)
docker run --rm --gpus all nvidia/cuda:12.2.2-base-ubuntu22.04 nvidia-smi
:docker7 image. Compute capability sm_120 auto-detected.nvidia-container-toolkit, use --gpus all. TensorRT is skipped by default — set SKIP_TENSORRT=false only when the image includes compatible TensorRT libraries. CUDA remains the default.:docker7-intel tag with --device=/dev/dri --group-add=render. Check diagnostics: curl http://YOUR_SERVER_IP:5000/gpu-verify:docker7-amd tag with --device=/dev/kfd --device=/dev/dricd docker-ai-service && chmod +x install-native-macos.sh && ./install-native-macos.sh
/dev/dxg + WSL2-driver mount — see docker-ai-service/docker-compose.yml WSL2 section. NVIDIA: use NVIDIA Container Toolkit. FP16 mismatch (Issue #67) is auto-detected in v1.7.4+ based on the loaded ONNX model's input type.# Add to LXC config (/etc/pve/lxc/<id>.conf):
lxc.cgroup2.devices.allow: c 226:* rwm
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
# Inside LXC, use Docker with:
--device=/dev/dri --group-add=render
# Verify GPU visibility:
docker exec jellyfin-ai-upscaler curl http://localhost:5000/gpu-verify
docker ps --filter name=jellyfin-ai-upscalercurl http://YOUR_SERVER_IP:5000/healthcurl http://YOUR_SERVER_IP:5000/gpu-verifyThe plugin repository auto-updates checksums via CI. If you see a mismatch:
MIT License - See LICENSE for details.
C#
35.1%
HTML
22.5%
Python
21.4%
JavaScript
16.5%
CSS
2.0%
PowerShell
1.7%
JellyfinUpscalerPlugin
See the codeBuilt with Claude Opus 5.5 — this plugin is developed with Anthropic's Claude models: Opus 5.5 since v1.8.3.33 (including the review of v1.8.3.31–v1.8.3.32 before it), Opus 5 for v1.8.3.13–v1.8.3.30, Fable 5 for v1.8.3.5–v1.8.3.12 and Opus 4.8 before that. v1.8.3.31 and v1.8.3.32 were prepared with OpenAI Codex. Code, Dockerfiles, CI workflows and documentation are written in a pair-programming style with the model; the maintainer (Kuschel-code) reviews, tests and publishes every change. Commits made with Claude carry a
Co-Authored-By: Claudetrailer as disclosure.
AI-powered video upscaling for Jellyfin. Upscale SD content to HD/4K using neural networks, with AI inference in a Docker service and video decoding/encoding on the Jellyfin host.
Release status (2026-10-04): v1.8.3.36 is released for Jellyfin 12.0+ / .NET 10. Library jobs now wait out a busy AI service (HTTP 429/503) instead of failing the whole video, and the AI service explains why an Intel/AMD GPU is not used (see issue #90: pass the host's numeric render group ID, not the name render). Target-server acceptance was not run; actual playback, GPU and HDR target-hardware behavior remain unverified.
Previous release status (2026-09-26): v1.8.3.35 is released for Jellyfin 12.0+ / .NET 10. The player button no longer needs write access to Jellyfin's web folder, and since v1.8.3.34 real-time upscaling starts again in the web client. Jellyfin 10.11 users retain v1.8.3.31, which has both defects. Target-server acceptance was not run; actual playback, GPU and HDR target-hardware behavior remain unverified.
Fixed in v1.8.3.35 (reported in GitHub issue #75):
index.html: on a default Windows install (the server runs as NetworkService under Program Files), with the Debian package, snaps and read-only containers. It showed only in a tab that had opened the plugin's settings. The plugin now adds its script to the page as Jellyfin serves it; the file on disk is left alone./jellyfin), the Lanczos, Anime4K and WebGPU real-time engines never loaded: they were requested without the base URL, and Jellyfin redirected the request to its start page. They now load relative to the web client.Fixed in v1.8.3.34 (reported in GitHub issues #86 and #87 on 2026-09-24):
Fixed in v1.8.3.33 (found in a review of v1.8.3.31–v1.8.3.32 on 2026-09-24):
smpte170m/bt470bg and untagged 10-bit files. HDR now needs real evidence (a PQ/HLG transfer, an HDR range, dynamic metadata, or BT.2020 without an SDR transfer).HDR requires PQ/ST.2084.New in v1.8.3.33: a redesigned in-player menu (live frame-rate readout, filter previews on the playing frame, phone and TV layouts) and settings page (keyboard- and remote-friendly tabs).
Docker Images (released in lockstep with the plugin, both at v1.8.3.36): All seven regular version pins and docker7 tags are published and registry-verified, including ten platform configurations. NVIDIA latest also points to v1.8.3.35. The AI service itself is unchanged since v1.8.3.33. Release notes.
kuscheltier/jellyfin-ai-upscaler:docker7 (NVIDIA CUDA + cuDNN 9)kuscheltier/jellyfin-ai-upscaler:docker7-amd (AMD ROCm)kuscheltier/jellyfin-ai-upscaler:docker7-intel (Intel Arc/iGPU OpenVINO)kuscheltier/jellyfin-ai-upscaler:docker7-apple (Apple Silicon Docker — CPU inference, multi-arch amd64/arm64)kuscheltier/jellyfin-ai-upscaler:docker7-vulkan (Vulkan/ncnn — AMD pre-RDNA2, Intel iGPU)kuscheltier/jellyfin-ai-upscaler:docker7-cpu (CPU Only — multi-threaded ONNXRuntime, multi-arch)kuscheltier/jellyfin-ai-upscaler:docker7-converter (CPU + pth→ONNX converter for OpenModelDB community models — opt-in)Download sizes range from 0.27 GB (docker7-cpu) to 20 GB (docker7-amd, ROCm base) — see docs/DOCKER-IMAGES.md for the full table, the converter's RAM guidance and why the AMD stack is frozen.
Report bugs: GitHub Issues
Contents: Architecture · How It Works · Installation · Features · AI Models · Configuration · Docker Tags · Changelog · Troubleshooting · Support
Download Jellyfin 12 release v1.8.3.35 (requires Jellyfin 12.0+, five runtime DLLs plus meta.json).
Jellyfin's plugin system tries to load ALL .dll files as .NET assemblies. Native C++ libraries (ONNX Runtime, CUDA, OpenCV) caused BadImageFormatException crashes in older versions. The solution: a Docker microservice architecture where the plugin (only ~1.6 MB) communicates with an external AI container via HTTP.
┌──────────────────────────────────────────┐
│ Jellyfin Server │
│ ┌────────────────────────────────────┐ │
│ │ AI Upscaler Plugin v1.8.3.36 │ │
│ │ ~1.6 MB — No native DLLs │ │
│ │ Sends frames via HTTP │ │
│ └──────────────┬─────────────────────┘ │
└─────────────────┼────────────────────────┘
│ HTTP POST /upscale
▼
┌──────────────────────────────────────────┐
│ AI Upscaler Docker Container │
│ ┌────────────────────────────────────┐ │
│ │ Python + FastAPI + OpenCV DNN │ │
│ │ CUDA / ROCm / OpenVINO / CPU │ │
│ │ Real-ESRGAN, SPAN, SwinIR, DAT2 │ │
│ │ EDVR-M, RealBasicVSR, AnimeSR │ │
│ │ EDSR, FSRCNN, ESPCN (76+ models) │ │
│ │ Web UI for Model Management │ │
│ └────────────────────────────────────┘ │
└──────────────────────────────────────────┘
The plugin supports four upscaling modes:
The Scheduled Task ("Scan & Upscale Library") runs daily at 3 AM and:
_upscaled suffix)Movie_upscaled.mkv)This is ideal for users without powerful servers — upscaling happens overnight.
The Scheduled Task ("Scan & Upscale Library Images") runs weekly on Sunday at 4 AM and:
POST /api/upscaler/upscale-images/{itemId}Auto mode is on by default. For every video it answers three questions and tells you the answer:
| It decides | From | What it will not do |
|---|---|---|
| Which model | Content type (genre), source resolution, whether multi-frame models are loaded, and the hardware class the AI service reports | Override a model you selected yourself under Preferred Anime / Live-Action Model — that is annotated, never replaced |
| Which scale | The source size. SD gets a full 4x restore; 720p and 1080p get 2x; a source that is already 4K is cleaned up, not enlarged | Overshoot that target silently — going past it is always a reported substitution with a reason you can read |
| Which filter | Content type | Apply one. Ever. As of v1.8.3.20 auto never writes a filter preset - it offers one in the player's Auto tab and the sidebar with an Apply button, and only when its opinion differs from yours. Filters are taste; models are technique |
The reason is visible wherever the decision is: the dashboard, the in-player Auto tab, and the library-scan log (at Information level, because batch runs happen when nobody is watching).
If the AI service is unreachable, auto does not block — it runs uncapped with the hardware class marked unknown.
In the player, the panel's first tab is Auto: the decision for the file that is playing, plus live switches for auto mode, video filters, face restoration and real-time upscaling. Everything there takes effect immediately, without opening the full configuration page.
When you press play, the plugin enhances the video in real-time. It offers several honest tiers (pick one, or let Auto choose) — each labelled for what it actually is, not marketing:
_upscaled result — and reach the TVs/phones nothing else can.Pair it with what you already have: on a desktop browser you can also use your GPU's own VSR (NVIDIA RTX Video Super Resolution / Intel VSR); for mpv there's mpv-shim + Anime4K. This plugin is the hub that brings anime/AI upscaling to every web/TV/mobile client and batch-upscales your whole library.
How it decides: At playback start, a benchmark runs against the Docker service. If the server can process frames fast enough (≥80% of video FPS), it uses Server AI mode. Otherwise, it falls back to WebGL. If the server cannot keep up during playback (under half the video's frame rate for 5 s) it switches to Lanczos (WebGL); while the service asks it to wait, it hides the stale frame and resumes when the server recovers.
Visual indicators:
Covers things you do not want on screen. It exists because of discussion #11: a user's dog loses her mind whenever a dog or cat appears on TV.
Setup (three steps):
animals by default), save, then press Load Detection Model.It replaces real-time upscaling while it runs. Two full inference passes per frame — upscale and detect — do not keep up with playback on any realistic server, so the stream does one or the other. Batch upscaling is unaffected.
You can also drive it directly, without the player:
# load a detector you imported earlier (none ships - see below)
curl -X POST "$BASE/models/load-detector" -H "X-Api-Token: $TOKEN" \
-F model_name=tiny-yolov3 -F input_size=416
# cover every animal in a frame with a solid box
curl -X POST "$BASE/detect-mask?classes=animals&mode=box&pad=12" \
-H "X-Api-Token: $TOKEN" --data-binary @frame.jpg -o masked.jpg
classes takes COCO names or the animals group, mode is box or blur, and pad grows each box — a detector's box hugs the animal, and the ears sticking out set a dog off just as well. The reply carries X-Detections.
Why not ffmpeg's dnn_detect, which is what was asked for: jellyfin-ffmpeg is built without any DNN backend — 46 --enable-* flags and not one of libopenvino / libtensorflow / libtorch, with the string dnn absent from the build entirely. Those filters cannot run on the ffmpeg Jellyfin ships, whatever the plugin passes them. Accepting arbitrary -vf would also be a security hole rather than a feature: ffmpeg filter syntax includes movie= and subtitles=, both of which read files, so it would hand every authenticated user a file-read primitive.
The detector is downloaded, not bundled. Since v1.8.3.26 tiny-yolov3 is a catalog entry with a verified sha256, so the Models tab fetches and checks it exactly like an upscaler. That pin was computed from the bytes of the file itself, after checking its sha384 against the OpenVINO model zoo's own model.yml — until that was done, the honest answer was "bring your own", because inventing a pin would break the guarantee the importer exists to provide. You can still import any other detector through the upload path, which pins and verifies it the same way. Both families work: single-head exports (YOLOv5/v7/v8/v9) and the NMS-head ONNX YOLOv3 exports. The service tells them apart by reading the loaded model's own inputs and outputs, and rejects one it does not recognise at load time — a misread tensor does not raise, it paints boxes over the wrong part of the picture.
The in-player button lets you:
The v1.8.3.35 release targets Jellyfin.Controller 12.0.0 / .NET 10. Jellyfin 12.1 is also published; see the official releases. Build and runtime evidence is tracked in docs/JELLYFIN-12-READINESS.md; GPU, real-player and HDR target-hardware acceptance remain separate.
Choose the command that matches your GPU:
NVIDIA GPU (recommended):
docker run -d \
--name jellyfin-ai-upscaler \
--gpus all \
-p 5000:5000 \
-v ai-models:/app/models \
kuscheltier/jellyfin-ai-upscaler:docker7
Intel GPU (Arc / Iris):
docker run -d \
--name jellyfin-ai-upscaler \
--device=/dev/dri \
--group-add=render \
-p 5000:5000 \
-v ai-models:/app/models \
kuscheltier/jellyfin-ai-upscaler:docker7-intel
AMD GPU (ROCm):
docker run -d \
--name jellyfin-ai-upscaler \
--device=/dev/kfd --device=/dev/dri \
-p 5000:5000 \
-v ai-models:/app/models \
kuscheltier/jellyfin-ai-upscaler:docker7-amd
Vulkan GPU (AMD RX 5700, Intel iGPU, etc.):
docker run -d \
--name jellyfin-ai-upscaler \
--device=/dev/dri \
--group-add=render \
-p 5000:5000 \
-v ai-models:/app/models \
kuscheltier/jellyfin-ai-upscaler:docker7-vulkan
CPU Only (any platform):
docker run -d \
--name jellyfin-ai-upscaler \
-p 5000:5000 \
-v ai-models:/app/models \
kuscheltier/jellyfin-ai-upscaler:docker7-cpu
CPU + Model Converter (adds .pth community-model conversion, ~2 GB):
docker run -d \r
--name jellyfin-ai-upscaler \r
-p 5000:5000 \r
-v ai-models:/app/models \r
kuscheltier/jellyfin-ai-upscaler:docker7-converter
Verify the container is running: curl http://YOUR_SERVER_IP:5000/health
https://raw.githubusercontent.com/Kuschel-code/JellyfinUpscalerPlugin/main/repository-jellyfin.json
http://YOUR_SERVER_IP:5000After installation, play any video in a web browser (Chrome, Edge, Firefox). You will see an AI upscaler button (sparkle icon) in the player controls. Click it to access:
Note: The player button only works in web browsers. It does NOT appear in native Jellyfin apps (Windows, Android, iOS, TV).
Note (Docker users): If the button doesn't appear immediately, visit the plugin config page once — this activates the player script for the current session.
To batch-upscale your low-resolution content:
:5000 dashboard — sha256-pinned, ZIP-aware; the opt-in docker7-converter image converts .pth models to ONNX with output verification; installs land in the ★ Favorites card/docs and /redoc endpoints for API exploration/metrics endpoint with per-model jobs, failures, frames, timing/health/detailed with GPU health, circuit breaker state, model infohttp://YOUR_SERVER_IP:5000| Category | Models | Scale | Speed | Best For |
|---|---|---|---|---|
| Real-ESRGAN | realesrgan-x4, x4-256, x2-plus, animevideo-x4 | 2-4x | Slow | Best overall quality |
| SPAN | span-x2, span-x4 | 2-4x | Fast | Real-time video |
| SwinIR | swinir-x4, swinir-small-x2/x4 | 2-4x | Medium | Photos & live-action |
| APISR | apisr-x3, apisr-anime-x2 | 2-3x | Medium | CVPR 2024, general & anime |
| Video Real-Time | clearreality-x4, nomosuni-compact-x2, lsdir-compact-x4 | 2-4x | Fast | Low-latency video |
| Video Quality | ultrasharp-v2-x4, nomos2-dat2-x4, nomos2-realplksr-x4 | 4x | Slow | Maximum detail |
| Film Restoration | fsdedither-x4, nomos8k-hat-x4 | 4x | Medium | DVD/VHS cleanup |
| Anime | anime-compact-x4 | 4x | Fast | Lightweight anime |
| Multi-Frame VSR | edvr-m-x4, realbasicvsr-x4, animesr-v2-x4 | 4x | Slow | Temporal consistency (5 frames) |
| OpenCV Classic | edsr-x2/x3/x4, lapsrn-x2/x4/x8, fsrcnn-x2/x3/x4, espcn-x2/x3/x4 | 2-8x | Fast-Medium | CPU-only, lightweight |
| Vulkan/ncnn | realesrgan-x4-vulkan, realesrgan-anime-x4-vulkan, span-x4-vulkan | 4x | Fast | AMD pre-RDNA2, Intel iGPU |
Beyond the curated catalog, the Importable models page lists 660+ OpenModelDB community models — import them one-click from the plugin config page or the :5000 dashboard (.pth models convert automatically with the docker7-converter image).
After installation, find settings under Dashboard → Plugins → AI Upscaler Plugin.
| Setting | Description |
|---|---|
| AI Service URL | URL to Docker container (e.g., http://192.168.1.100:5000) |
| Enable Plugin | Global on/off switch |
| AI Model | Choose upscaling model (auto = intelligent selection per content) |
| Scale Factor | 2x, 3x, or 4x |
| Min Resolution | Threshold for scheduled task (default: 1920x1080) |
| Model Fallback Chain | Comma-separated fallback models (e.g., realesrgan-x4,span-x4,edsr-x4) |
| Preferred Anime Model | Model for anime content (empty = let the heuristic decide). A value here is treated as a deliberate override: it is exempt from the hardware cap and the scale logic |
| Preferred Live-Action Model | Model for live-action content (empty = let the heuristic decide). Same override semantics as above |
| Enable Processing Queue | Priority queue with pause/resume (default: true) |
| Max Queue Size | Maximum items in queue (default: 100) |
| Pause Queue During Playback | Pause processing when user is watching (default: true) |
| Webhook URL | HTTP POST notifications on job complete/failure |
| Enable Health Monitoring | Circuit breaker + health checks (default: true) |
| Circuit Breaker Threshold | Consecutive failures before circuit opens (default: 5) |
| Model Disk Quota MB | Max disk space for cached models (default: 2048) |
| Enable Model Auto Cleanup | LRU cleanup of unused models (default: true) |
| Enable Quality Metrics | Compute PSNR/SSIM scores after upscaling (default: true) |
| Enable Face Enhancement | Detect and enhance faces via GFPGAN ONNX or fallback (default: true) |
| Face Enhance Strength | Blend ratio 0.0–1.0 for face enhancement (default: 0.7) |
| Enable Grain Management | Film grain removal/re-addition pipeline (default: true) |
| Grain Denoise Strength | NL-means filter strength 1–30 (default: 5) |
| Grain Re-add Intensity | Gaussian noise sigma 0–50 for re-grain (default: 0) |
| Enable Custom Model Upload | Allow uploading custom ONNX models at runtime (default: true) |
| Enable API Docs | Toggle /docs and /redoc Swagger endpoints (default: true) |
| Player Button | Show/hide AI button in video player |
| Real-Time Upscaling | Enable/disable real-time enhancement during playback |
| Output Codec | Codec for upscaled videos: H.264, H.265, or copy |
| MaxItemsPerScan | Limit items per scan run (default: unlimited) |
| Tag | GPU | Use Case |
|---|---|---|
:docker7 | NVIDIA CUDA 12.8 (TensorRT opt-in) | RTX 50/40/30/20, GTX 16/10 |
:docker7-amd | AMD ROCm 6.2 | RX 7000, RX 6000 |
:docker7-intel | Intel OpenVINO 2025.4 | Arc A-Series, Iris Xe, iGPU |
:docker7-apple | ARM64 Optimized (multi-arch) | Apple M1–M5 (Docker=CPU, native=CoreML) |
:docker7-vulkan | Vulkan (ncnn) | AMD pre-RDNA2, Intel iGPU, any Vulkan GPU |
:docker7-cpu | Multi-threaded CPU (multi-arch) | Any platform (amd64/arm64) |
:docker7-converter | CPU + Torch/Spandrel | Supported community-model conversion to ONNX |
Each tag is published three ways so you can pin precisely:
:docker7 — rolling tag family (Watchtower auto-updates):docker7-v1.8.3.35 — NVIDIA pin for the currently published release:v1.8.3.35-<backend> — published backend pin (e.g. :v1.8.3.35-cpu):rc-v<version>[-backend] — release-candidate images, published ahead of a release when one is wanted; :rc-v<version>-<commit>[-backend] pins the exact build. The last candidates were :rc-v1.8.3.32…; v1.8.3.33 to v1.8.3.35 went straight to release.CUDA is the default: keep SKIP_TENSORRT=true. Enable TensorRT only with compatible libraries in the image. See Docker setup and controlled updates.
The full version history lives on the website and the release pages — this README no longer duplicates it:
Release: v1.8.3.35 — the player button no longer needs write access to Jellyfin's web folder (Windows installs, Debian package, snaps, read-only containers), and the browser real-time engines load behind a base URL; all seven Docker variants. v1.8.3.31 remains available for Jellyfin 10.11.
The button only appears in web browsers (Chrome, Edge, Firefox, Brave), not in the native apps (Windows app, mobile, TV). Open Jellyfin via http://YOUR_IP:8096.
Since v1.8.3.35 the plugin adds its script to the web client's page as Jellyfin serves it, so a read-only web folder no longer matters (Windows installer, Debian package, snap, read-only containers). After a restart and the first page load, the log (Dashboard → Logs) shows Player script injected via … or Player script added to index.html as Jellyfin serves it.
Neither line? Then Jellyfin is not serving the web client itself (for example, a separate web server serves jellyfin-web), and the plugin cannot reach the page.
On v1.8.3.34 or older, including v1.8.3.31 for Jellyfin 10.11, the plugin can only edit the file itself. If the log says Could not inject player script into index.html, give Jellyfin write access to the index.html it names, then restart Jellyfin:
Program Files. In an administrator Command Prompt, run icacls "C:\Program Files\Jellyfin\Server\jellyfin-web\index.html" /grant *S-1-5-20:M and restart the "Jellyfin Server" service. If you run the tray app instead of the service, use /grant "%USERNAME%":M.sudo chown jellyfin /usr/share/jellyfin/web/index.html, then sudo systemctl restart jellyfin.Until then, opening the plugin's settings page loads the button into that browser tab only; reloading the tab removes it again. A Jellyfin update replaces index.html, so repeat the step if the warning returns.
There is no "Upscale" button on item pages: whole libraries are upscaled by the scheduled task "Scan & Upscale Library" (Dashboard → Scheduled Tasks).
# Check logs
docker logs jellyfin-ai-upscaler --tail 50
# Health check
curl http://YOUR_SERVER_IP:5000/health
# GPU diagnostics
curl http://YOUR_SERVER_IP:5000/gpu-verify
# Check GPU (NVIDIA)
docker run --rm --gpus all nvidia/cuda:12.2.2-base-ubuntu22.04 nvidia-smi
:docker7 image. Compute capability sm_120 auto-detected.nvidia-container-toolkit, use --gpus all. TensorRT is skipped by default — set SKIP_TENSORRT=false only when the image includes compatible TensorRT libraries. CUDA remains the default.:docker7-intel tag with --device=/dev/dri --group-add=render. Check diagnostics: curl http://YOUR_SERVER_IP:5000/gpu-verify:docker7-amd tag with --device=/dev/kfd --device=/dev/dricd docker-ai-service && chmod +x install-native-macos.sh && ./install-native-macos.sh
/dev/dxg + WSL2-driver mount — see docker-ai-service/docker-compose.yml WSL2 section. NVIDIA: use NVIDIA Container Toolkit. FP16 mismatch (Issue #67) is auto-detected in v1.7.4+ based on the loaded ONNX model's input type.# Add to LXC config (/etc/pve/lxc/<id>.conf):
lxc.cgroup2.devices.allow: c 226:* rwm
lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir
# Inside LXC, use Docker with:
--device=/dev/dri --group-add=render
# Verify GPU visibility:
docker exec jellyfin-ai-upscaler curl http://localhost:5000/gpu-verify
docker ps --filter name=jellyfin-ai-upscalercurl http://YOUR_SERVER_IP:5000/healthcurl http://YOUR_SERVER_IP:5000/gpu-verifyThe plugin repository auto-updates checksums via CI. If you see a mismatch:
MIT License - See LICENSE for details.
C#
35.1%
HTML
22.5%
Python
21.4%
JavaScript
16.5%
CSS
2.0%
PowerShell
1.7%