DEPRECATED / NOT WORKING
This plugin does not work as intended. The middleware approach to filter MediaSources from Jellyfin's API responses conflicts with Jellyfin's response compression, causing browser decoding errors.
Recommended Alternative: Use separate Jellyfin libraries for different quality tiers (e.g.,
/mediafor 4K/1080p,/media-720for 720p only), and assign users to specific libraries via Jellyfin's built-in Library Access settings.The intro provider functionality works, but the core version-filtering feature does not.
Intelligent media access control for Jellyfin
- Path-Based Policies -- Define granular access rules based on file path prefixes
- Per-User Assignments -- Assign different policies to different users
- Web Configuration -- Easy-to-use admin interface in Jellyfin dashboard
- Multi-Version Support -- Seamlessly filter available media versions per user
- Real-Time Enforcement -- Policies are enforced at playback time
- Detailed Logging -- Full audit trail of access decisions
This plugin is perfect for scenarios where you have:
| Scenario | Solution |
|---|---|
| Bandwidth Management | Restrict remote users to lower-bitrate versions |
| Tiered Access | Premium users get 4K, standard users get 1080p |
| Device Optimization | Mobile users automatically get mobile-optimized versions |
| Storage Tiers | Keep originals on slow storage, transcodes on fast storage |
/media/Movies/ ← High-quality originals (4K/Remux)
/media-transcoded/Movies/ ← Transcoded versions (1080p/720p)
Create a "Standard Access" policy allowing only /media-transcoded/ and assign it to users who should be restricted.
Add this repository to your Jellyfin instance for automatic updates:
- Go to Dashboard → Plugins → Repositories
- Click Add and enter:
- Name:
Quality Gate - URL:
https://raw.githubusercontent.com/GeiserX/jellyfin-quality-gate/main/manifest.json
- Name:
- Go to Catalog and install Quality Gate
- Restart Jellyfin
Docker
# Download the latest release
curl -L -o QualityGate.zip \
https://github.com/GeiserX/jellyfin-quality-gate/releases/latest/download/quality-gate.zip
# Extract to your plugins volume
unzip QualityGate.zip -d /path/to/jellyfin/plugins/QualityGate/
# Restart your container
docker restart jellyfinOr add to your docker-compose.yml:
volumes:
- ./plugins/QualityGate:/config/plugins/QualityGateLinux (Native)
# Download the latest release
curl -L -o QualityGate.zip \
https://github.com/GeiserX/jellyfin-quality-gate/releases/latest/download/quality-gate.zip
# Extract to plugins directory
sudo unzip QualityGate.zip -d /var/lib/jellyfin/plugins/QualityGate/
# Set permissions
sudo chown -R jellyfin:jellyfin /var/lib/jellyfin/plugins/QualityGate/
# Restart Jellyfin
sudo systemctl restart jellyfinWindows
- Download the latest release
- Extract to
%LOCALAPPDATA%\jellyfin\plugins\QualityGate\ - Restart Jellyfin from Services or the tray icon
macOS
# Download the latest release
curl -L -o QualityGate.zip \
https://github.com/GeiserX/jellyfin-quality-gate/releases/latest/download/quality-gate.zip
# Extract to plugins directory
unzip QualityGate.zip -d ~/.local/share/jellyfin/plugins/QualityGate/
# Restart JellyfinNavigate to Dashboard → Plugins → Quality Gate to configure the plugin.
Policies define which paths are allowed or blocked. Click "+ Add Policy" to create one.
| Field | Description |
|---|---|
| Policy Name | A descriptive name (e.g., "720p Only", "No 4K") |
| Allowed Path Prefixes | Paths users CAN access. One per line. |
| Blocked Path Prefixes | Paths that will be blocked. One per line. |
| Blocked Message | Custom message shown when playback is blocked |
| Enabled | Toggle policy on/off |
Choose a policy from the Default Policy dropdown. This applies to ALL users who don't have a specific override.
- Select (No default - Full Access) to allow unrestricted access by default
- Select a policy to restrict all users by default
Override the default for specific users:
- Select a user from the dropdown
- Select a policy (or "Full Access" for no restrictions)
- Click "Add Override"
The plugin evaluates paths in this order:
- Blocked Paths: If file path starts with any blocked prefix -- BLOCKED
- Allowed Paths: If allowed paths are defined and file doesn't match any -- BLOCKED
- Otherwise -- ALLOWED
| Allowed Paths | Blocked Paths | File Path | Result |
|---|---|---|---|
/transcodes/ |
-- | /transcodes/Movie.mkv |
Allowed |
/transcodes/ |
-- | /originals/Movie.mkv |
Blocked |
| (empty) | /originals/4K/ |
/originals/1080p/Film.mkv |
Allowed |
| (empty) | /originals/4K/ |
/originals/4K/Film.mkv |
Blocked |
/media/ |
/media/4K/ |
/media/Movies/Film.mkv |
Allowed |
/media/ |
/media/4K/ |
/media/4K/Film.mkv |
Blocked |
Tip: If no Allowed Paths are set, all paths are allowed except those explicitly blocked.
Use case: You have originals in /mnt/originals/ and 720p transcodes in /mnt/transcodes/. Restrict some users to only see transcoded versions.
Policy Name: 720p Only
Allowed Path Prefixes:
/mnt/transcodes/
/mnt/remotes/transcodes/
Blocked Path Prefixes:
(leave empty)
Use case: Allow access to everything except 4K content stored in a specific folder.
Policy Name: No 4K
Allowed Path Prefixes:
(leave empty - allows all by default)
Blocked Path Prefixes:
/media/4K/
/media/UHD/
/mnt/storage/4K/
Use case: You have a Jellyfin library with multi-version support where originals and transcodes are in the same folder. Originals come from /mnt/originals/ and transcodes come from /mnt/transcodes/.
Policy Name: Standard Quality
Allowed Path Prefixes:
/mnt/transcodes/
Blocked Path Prefixes:
/mnt/originals/
Blocked Message Header: Quality Restricted
Blocked Message Text: Please select the 720p version to play this content.
Then set this as the Default Policy and add Full Access overrides for admin users.
Use case: Premium users get full access, standard users get 1080p max.
-
Create policy "Standard (1080p max)":
Blocked Path Prefixes: /media/4K/ /media/2160p/ -
Create policy "Premium (Full Access)" or use the built-in Full Access
-
Set Default Policy to "Standard (1080p max)"
-
Add User Overrides for premium users → "Full Access"
-
Middleware Filtering: When Jellyfin returns media sources/versions to the client, the plugin filters out blocked versions so they don't appear in the UI.
-
Playback Interception: If a user somehow attempts to play a blocked version, the plugin intercepts the playback and stops it with a custom message.
-
Path Matching: The plugin matches the full file path of each media version against your policy prefixes.
To see what paths your files have:
- Go to a movie/show in Jellyfin
- Click the ⋮ menu → Media Info
- Look at the Path field for each version
Common path patterns:
- Docker:
/media/Movies/Title (2024)/Title.mkv - NFS mounts:
/mnt/nfs/media/Movies/... - Remote transcodes:
/mnt/remotes/server/transcodes/...
- .NET 9.0 SDK
- Git
git clone https://github.com/GeiserX/jellyfin-quality-gate.git
cd jellyfin-quality-gate/Jellyfin.Plugin.QualityGate
dotnet build -c ReleaseThe compiled plugin will be in bin/Release/net9.0/.
# Run with hot reload (for development)
dotnet watch build -c Debug
# Run tests
dotnet test- This plugin handles access control — review your policies carefully
- Only administrators can configure policies
- See SECURITY.md for vulnerability reporting
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the GPL-3.0 License — see the LICENSE file for details.
- Jellyfin — The Free Software Media System
- The Jellyfin plugin development community