A Jellyfin plugin that creates an "infinite library" by displaying content from TVDB, TMDB, or Stremio addons even when media files don't exist locally. Search results appear as virtual items that can be browsed for metadata or streamed via configurable providers.
- Jellyfin 10.11.x or later
- At least one metadata source:
- TVDB API key - for TV shows and anime
- TMDB API key - for movies
- Stremio catalog addon URL (e.g., Cinemeta, AIOMetadata)
- (Optional) Stream provider for playback:
- AIOStreams addon (recommended), OR
- Embedarr instance, OR
- Direct URL templates to your streaming service
- In Jellyfin, go to Dashboard > Plugins > Repositories
- Click Add and enter:
- Repository Name: Dynamic Library
- Repository URL:
https://pythcon.github.io/jellyfin-dynamic-library/manifest.json
- Click Save
- Go to the Catalog tab and find "Dynamic Library"
- Click Install
- Restart Jellyfin
- Download the latest release from GitHub Releases
- Extract the ZIP to your Jellyfin plugins directory:
- Linux:
/var/lib/jellyfin/plugins/DynamicLibrary/ - Docker:
/config/plugins/DynamicLibrary/(mapped volume) - Windows:
C:\ProgramData\Jellyfin\Server\plugins\DynamicLibrary\
- Linux:
- Restart Jellyfin
- Go to Dashboard > Plugins > Dynamic Library
- Choose a Catalog Provider:
- Stremio Addon: Enter a Stremio catalog URL (e.g.,
https://v3-cinemeta.strem.io) - Direct: Enter your TVDB and/or TMDB API keys
- Stremio Addon: Enter a Stremio catalog URL (e.g.,
- (Optional) Configure a Stream Provider for playback:
- AIOStreams: Enter your AIOStreams addon URL
- Direct: Configure URL templates
- Embedarr: Enter your Embedarr instance URL
- (Optional) Enable Persistence to save items to your library
- Save and restart Jellyfin
- Search for any movie or TV show - results will include virtual items from the configured sources
- Search returns results from your configured catalog source alongside your existing library
- Full metadata including posters, backdrops, cast, descriptions, and ratings
- Virtual items are distinguished by a
DynamicLibraryprovider ID
- Stremio Addon: Use Cinemeta, AIOMetadata, or any compatible Stremio addon
- Direct APIs: Query TVDB/TMDB directly for metadata
- None: Browse-only mode for metadata exploration
- AIOStreams: Use your configured AIOStreams Stremio addon for multi-source streaming with version selection
- Embedarr: Automatic STRM generation via Embedarr API
- Direct: Custom URL templates with placeholder support
- Optionally save virtual items as real Jellyfin library items (.strm files)
- Full playback tracking, watched status, and resume support
- Automatic detection of new episodes for ongoing series
- Optional sub/dub track selection for anime content
- Configurable audio track options (e.g., "sub,dub")
- Works with Direct mode URL templates
- Fetches subtitles from OpenSubtitles for virtual items
- Supports multiple languages
- Caches subtitles locally for performance
- Display metadata in your preferred language
- Supports 20+ languages via ISO 639-2/3 codes
| Setting | Default | Description |
|---|---|---|
Enabled |
true | Enable/disable the plugin |
CacheTtlMinutes |
60 | How long to cache API responses |
| Setting | Options | Description |
|---|---|---|
CatalogProvider |
StremioAddon, Direct | Source for metadata and search results |
StremioCatalogUrl |
- | Stremio addon URL (when using StremioAddon mode) |
Stremio Addon Examples:
# Cinemeta (official Stremio catalog)
https://v3-cinemeta.strem.io
# AIOMetadata (your configured instance)
https://your-aiometadata-instance.com/xxxxx
| Setting | Description |
|---|---|
TvdbApiKey |
TVDB v4 API key for TV shows and anime |
TmdbApiKey |
TMDB v3 API key for movies |
MovieApiSource |
API source for movies: None, TMDB, or TVDB |
TvShowApiSource |
API source for TV shows: None, TVDB, or TMDB |
MaxMovieResults |
Maximum movie search results (default: 20) |
MaxTvShowResults |
Maximum TV show search results (default: 20) |
| Setting | Description |
|---|---|
StreamProvider |
None (browse only), AIOStreams, Embedarr, or Direct |
| Setting | Description |
|---|---|
AIOStreamsUrl |
Full AIOStreams addon URL including encrypted config (e.g., https://aiostreams.elfhosted.com/E2_xxxxx) |
EnableHlsProbing |
Probe HLS streams for accurate duration (helps with scrubbing on Android TV). Disable if experiencing playback issues with token-based streams. Default: false |
| Setting | Description |
|---|---|
EmbedarrUrl |
Embedarr instance URL |
EmbedarrApiKey |
Embedarr API key (if required) |
CreateMediaOnView |
Pre-trigger Embedarr when viewing item details (default: false) |
| Setting | Description |
|---|---|
DirectMovieUrlTemplate |
URL template for movie streams |
DirectTvUrlTemplate |
URL template for TV show streams |
DirectAnimeUrlTemplate |
URL template for anime streams |
ShowUnreleasedStreams |
Generate stream URLs for unreleased content (default: false) |
Template Placeholders:
| Placeholder | Description |
|---|---|
{id} |
Preferred provider ID based on config |
{imdb} |
IMDB ID (tt1234567) |
{tmdb} |
TMDB ID |
{tvdb} |
TVDB ID |
{anilist} |
AniList ID (anime only) |
{season} |
Season number |
{episode} |
Episode number |
{absolute} |
Absolute episode number |
{audio} |
Audio track (sub/dub) |
{title} |
URL-encoded title |
Example Templates:
# Movies
https://stream.example.com/movie/{imdb}
# TV Shows
https://stream.example.com/tv/{imdb}/{season}/{episode}
# Anime with audio selection
https://stream.example.com/anime/{anilist}/{episode}?audio={audio}
Control which ID is used for stream lookups:
| Setting | Options | Default |
|---|---|---|
MoviePreferredId |
IMDB, TMDB | IMDB |
TvShowPreferredId |
IMDB, TVDB | IMDB |
AnimePreferredId |
IMDB, TVDB, AniList | IMDB |
| Setting | Default | Description |
|---|---|---|
EnableAnimeAudioVersions |
false | Show audio track selector for anime |
AnimeAudioTracks |
"sub,dub" | Comma-separated track options |
| Setting | Options | Description |
|---|---|---|
LanguageMode |
Default, Override | Default uses original language from API |
PreferredLanguage |
ISO 639-2 code | Language code (eng, jpn, spa, fra, deu, etc.) |
| Setting | Default | Description |
|---|---|---|
EnableSubtitles |
false | Enable automatic subtitle fetching |
OpenSubtitlesApiKey |
- | API key from OpenSubtitles |
SubtitleLanguages |
"en" | Comma-separated ISO 639-1 codes (e.g., "en,es,fr") |
MaxSubtitlesPerLanguage |
1 | Maximum subtitles to download per language |
UseJellyfinOpenSubtitlesCredentials |
true | Use credentials from Jellyfin's OpenSubtitles plugin |
SubtitleCachePath |
/tmp/jellyfin-dynamiclibrary-subtitles | Local path for cached subtitle files |
Enable persistence to save virtual items as real Jellyfin library items with full playback tracking.
| Setting | Default | Description |
|---|---|---|
EnablePersistence |
false | Enable persistent library items (.strm files) |
PersistentLibraryPath |
/dynamic-library | Root folder for persistent items (add as a Jellyfin library) |
TriggerLibraryScan |
true | Automatically scan library after creating new items |
CreateUnreleasedMedia |
false | Create media for unreleased content |
CheckForUpdatesOnView |
true | Check for new episodes when viewing persisted series |
Setting Up Persistence:
- Enable
EnablePersistencein plugin settings - Set
PersistentLibraryPathto a folder (e.g.,/media/dynamic-library) - Add this folder as a Jellyfin library (Movies and/or Shows)
- When you click on a virtual item, it creates a .strm file in this folder
- Library scan adds it as a real item with full tracking
Search Query
|
v
SearchActionFilter intercepts request
|
v
Query catalog source (Stremio addon or TVDB/TMDB)
|
v
Create virtual BaseItemDto objects with unique GUIDs
|
v
Cache items in memory
|
v
Return results to Jellyfin UI
|
v
User clicks item -> ItemLookupFilter returns cached DTO
|
v
(Optional) PersistenceService creates .strm files
|
v
User plays item -> PlaybackInfoFilter gets stream URL
|
v
(Optional) SubtitleFilter serves cached subtitles
Virtual items use deterministic GUIDs generated from a unique prefix (jellyfin-dynamiclibrary-plugin:) to prevent collisions with real library items.
- Virtual items (without persistence) are ephemeral and exist only in memory cache
- Playback requires a configured stream provider (AIOStreams, Embedarr, or Direct URLs)
- Some metadata may be incomplete depending on API availability
- Episode translations may not be available for all languages in TVDB
- Android TV may have quirks with version selection (the plugin includes workarounds)
The plugin includes workarounds for Android TV's version selection quirks. If you're still experiencing issues, try:
- Clear the Jellyfin app cache on Android TV
- Ensure you're on the latest plugin version
- Check that your stream provider is correctly configured
- For AIOStreams, verify your addon URL is valid and not expired
- For Direct mode, ensure URL templates include all required placeholders
- Verify OpenSubtitles API key is correct
- Check that the language codes in
SubtitleLanguagesare valid ISO 639-1 codes - Subtitles may not be available for all content
- Issues: GitHub Issues
- Discussions: GitHub Discussions
dotnet build Jellyfin.Plugin.DynamicLibrary/Jellyfin.Plugin.DynamicLibrary.csproj -c Release# Start test Jellyfin container
cd jellyfin-test && docker-compose up -d
# Build and install plugin
./install-plugin.sh jellyfin-
Update version in
Jellyfin.Plugin.DynamicLibrary/Jellyfin.Plugin.DynamicLibrary.csproj:<Version>X.Y.Z</Version> <AssemblyVersion>X.Y.Z.0</AssemblyVersion> <FileVersion>X.Y.Z.0</FileVersion>
-
Update version in
.github/workflows/build.yaml:meta.jsonversion field- ZIP filename (
dynamic-library-X.Y.Z.zip)
-
Commit changes:
git add -A && git commit -m "Bump version to X.Y.Z"
-
Create and push version tag:
git tag vX.Y.Z && git push && git push --tags
-
GitHub Actions will automatically:
- Build the plugin
- Create a GitHub Release with the ZIP
- Update
manifest.jsonon gh-pages
MIT - See LICENSE file for details.