Skip to content

c-kick/vpro-cinema-plex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

126 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VPRO Cinema Metadata Provider for Plex

A custom metadata provider that supplies Dutch film descriptions from VPRO Cinema to Plex Media Server.

Note: As of v4.0.0, this provider only supports movies. TV series support has been removed because VPRO's data sources don't provide complete series metadata (seasons, episodes, episode descriptions), which caused Plex scanning failures.

See how it works.

Example of the Metadata Provider in action!

License: MIT Python 3.11+ Docker Plex 1.40+

Features

  • 🇳🇱 Dutch film reviews/descriptions from VPRO Cinema's database
  • 🎬 Movies only (TV series not supported — see note above)
  • 🔞 Kijkwijzer content ratings (Dutch age classification: AL, 6, 9, 12, 14, 16, 18)
  • 🔍 Direct NPO POMS API access
  • 🌍 Smart title matching via TMDB — works in both directions (Translated → Original and Original → Translated)
  • 🔎 Cinema.nl fallback with IMDB verification when POMS API returns no results
  • 📦 TMDB fallback metadata — movies not in VPRO can still be added to Plex with basic info
  • 💾 Persistent caching (with short 1-hour TTL for not-found entries)
  • 🐳 Docker-ready with health checks
  • 🔗 Combines with other providers (returns description + content rating by default)
  • ⚙️ Configurable: optionally return VPRO images and/or ratings

Background

For years I wanted to automatically pull the excellent Dutch film reviews from VPRO Cinema (formerly vprogids.nl/cinema) into Plex. I made several attempts over the years, but without an official NPO API, I never got it to work. After getting tired of manually copying descriptions into Plex — only to have them overwritten by the next metadata refresh — I teamed up with Claude to build a proper solution. After some experimentation (first with scraping, then reverse-engineering the NPO's internal POMS API), I finally got a working Plex agent! I decided to share it with the community, hoping it can help others too.

Feel free to use, fork, and contribute, but note that the API is not officially supported by NPO, so the approach is technically a bit dodgy and may break at any time. Though it has been working excellently for me, so far!

Prerequisites

Required:

Recommended:

  • TMDB API Key — Enables smart alternate title lookup. Many films are indexed in VPRO Cinema under their original ( often French, German, or Dutch) title rather than the English title. With a TMDB API key, the provider automatically discovers and tries alternate titles in both directions:

    • English → Original: "Downfall" → "Der Untergang" (via IMDB ID from Plex)
    • Original → English: "Der Untergang" → "Downfall" (via TMDB title search)

    Get your free API key at: https://www.themoviedb.org/settings/api

Quick Start

1. Clone and configure

git clone https://github.com/c-kick/vpro-cinema-plex.git
cd vpro-cinema-plex
cp env.example .env

Edit .env to add your TMDB API key (optional but recommended):

TMDB_API_KEY=your_tmdb_api_key_here

2. Build and run

docker-compose up -d

3. Verify

curl "http://localhost:5100/health"
curl "http://localhost:5100/test?title=Apocalypse+Now&year=1979"
Alternative: Add to existing Docker stack (Portainer)

Portainer often can't access local build contexts. Build the image on your server first:

cd /path/to/vpro-cinema-plex
docker build -t vpro-plex-provider:latest .

Add to your stack:

vpro-plex-provider:
  image: vpro-plex-provider:latest
  pull_policy: never
  container_name: vpro-plex-provider
  restart: unless-stopped
  ports:
    - "5100:5100"
  environment:
    - TZ=Europe/Amsterdam
    - LOG_LEVEL=INFO
    - TMDB_API_KEY=your_tmdb_api_key_here  # Optional but recommended
    - CACHE_DIR=/app/cache
    - POMS_CACHE_FILE=/app/cache/credentials.json
    - VPRO_RETURN_SUMMARY=true        # Dutch descriptions (main feature)
    - VPRO_RETURN_CONTENT_RATING=true # Kijkwijzer age ratings
    - VPRO_RETURN_IMAGES=false        # Set to true to use VPRO posters
    - VPRO_RETURN_RATING=false        # Experimental: VPRO ratings (see limitations)
  volumes:
    - /path/to/vpro-cinema-plex/cache:/app/cache
  networks:
    - your-plex-network  # Must be on same network as Plex

Note: if you use the plex network, you can use 'vpro-plex-provider' (instead of localhost, as in the examples below) to directly reference the agent in the provider URL in Plex: http://vpro-plex-provider:5100/movies

Plex Configuration

Important: Replace localhost with your server's IP if Plex runs on a different host.

Endpoint Provider Name Use For
http://localhost:5100/movies VPRO Cinema (Dutch Summaries) Movies

Register the provider

  1. In Plex, go to SettingsMetadata AgentsMetadata Providers
  2. Click + Add Provider, paste http://localhost:5100/movies, save
image

Create the agent

Movie Agent:

  1. Under Metadata Agents, click + Add Agent
  2. Title: "VPRO + Plex Movie"
  3. Primary provider: VPRO Cinema (Dutch Summaries)
  4. Add "Plex Movie" as additional provider (click +)
  5. Optionally add "Plex Local Media"
  6. Save
image

Configure your libraries

  1. SettingsManage Libraries → click ... next to library → Edit Library
  2. Advanced tab → Agent → select your new agent
  3. Save and repeat for other movie libraries

Refresh metadata

For existing content: Select items → ...Refresh Metadata

New content will automatically use the provider on scan.

How It Works

When configured as a Metadata Provider alongside Plex Movie (see Plex Configuration), the VPRO agent runs a lookup cascade while Plex Movie fetches its metadata in parallel. Results are merged with VPRO as primary — meaning Dutch summaries and Kijkwijzer ratings take precedence. Everything VPRO doesn't return (posters, cast, genres, etc.) is filled in by Plex Movie automatically.

vpro-how-it-works drawio (2) drawio (2)

Testing & Debugging

CLI search (no caching)

# Basic search
docker exec vpro-plex-provider python vpro_lookup.py "Apocalypse Now" --year 1979

# With IMDB ID + verbose output
docker exec vpro-plex-provider python vpro_lookup.py "Downfall" --year 2004 --imdb tt0363163 -v

# Test cinema.nl fallback directly (bypass POMS API)
docker exec vpro-plex-provider python vpro_lookup.py "Der Untergang" --year 2004 --skip-poms -v

HTTP endpoints (with caching)

# Test search
curl "http://localhost:5100/test?title=Le+dernier+métro&year=1980"

# Test cinema.nl fallback directly (bypass POMS API)
curl "http://localhost:5100/test?title=Der+Untergang&year=2004&skip_poms=1"

# Plex metadata endpoint
curl "http://localhost:5100/movies/library/metadata/vpro-apocalypse-now-1979-tt0078788-m"

# Cache operations
curl "http://localhost:5100/cache"
curl "http://localhost:5100/cache?key=vpro-apocalypse-now-1979-tt0078788-m"
curl -X POST "http://localhost:5100/cache/clear"
curl -X POST "http://localhost:5100/cache/delete?key=vpro-apocalypse-now-1979-tt0078788-m"
curl -X POST "http://localhost:5100/cache/delete?pattern=apocalypse"

Credential management

The POMS API uses hardcoded default credentials that have been working reliably.

# View cached credentials
docker exec vpro-plex-provider cat cache/credentials.json

Note: Credential auto-refresh from vprogids.nl is no longer functional since the migration to cinema.nl, but the default credentials continue to work with the POMS API.

Logs

docker-compose logs -f

Environment Variables

Variable Default Description
PORT 5100 Server port
LOG_LEVEL INFO DEBUG, INFO, WARNING, ERROR
CACHE_DIR ./cache Cache directory path
TMDB_API_KEY (none) TMDB API key for alternate title lookup
POMS_CACHE_FILE ./credentials.json Path to cached POMS credentials
VPRO_RETURN_SUMMARY true Return VPRO Dutch summary/description
VPRO_RETURN_CONTENT_RATING true Return Kijkwijzer content rating (AL, 6, 9, 12, 14, 16, 18)
VPRO_RETURN_IMAGES false Return VPRO images (recommended for Dutch films — better poster coverage than TMDB)
VPRO_RETURN_RATING false Return VPRO rating (experimental, see Limitations)

API Reference

Endpoint Method Description
/movies GET Movie provider info (type 1)
/movies/library/metadata/<key> GET Plex metadata lookup for movies
/movies/library/metadata/matches POST Plex match endpoint for movies
/movies/library/metadata/<key>/images GET Returns VPRO images if enabled, otherwise empty
/movies/library/metadata/<key>/extras GET Returns empty (no extras)
/health GET Simple health check (version only)
/health/ready GET Detailed health with checks, cache stats, config
/health/live GET Liveness probe (always returns ok)
/test GET Test search: ?title=X&year=Y&imdb=ttZ&skip_poms=1&skip_tmdb=1
/cache GET List cached entries or view specific: ?key=X
/cache/clear POST Clear all cached entries (preserves credentials)
/cache/delete POST Delete specific entries: ?key=X or ?pattern=X

File Structure

vpro-cinema-plex/
├── docker-compose.yml          # Docker Compose config
├── Dockerfile                  # Container definition
├── env.example                 # Environment template (copy to .env)
├── requirements.txt            # Python dependencies
│
├── vpro_metadata_provider.py   # Flask HTTP server for Plex
├── vpro_lookup.py              # Search orchestrator + CLI
├── poms_client.py              # NPO POMS API + TMDB clients
├── vpro_scraper.py             # Cinema.nl fallback + page scraper
├── models.py                   # Shared data models (VPROFilm)
│
├── cache.py                    # Disk cache with sharding
├── credentials.py              # POMS credential management
├── http_client.py              # HTTP session factory
├── text_utils.py               # Title matching utilities
├── logging_config.py           # Logging configuration
├── metrics.py                  # Simple metrics collection
├── constants.py                # Shared constants
│
├── LICENSE                     # MIT License
└── README.md                   # This file

Troubleshooting

Problem Solution
Provider not in Plex Verify running: curl http://localhost:5100/health. Check network from Plex to provider.
No Dutch descriptions Test film exists: docker exec vpro-plex-provider python vpro_lookup.py "TITLE" --year YEAR -v. Check logs: docker-compose logs --tail=100. Clear cache and retry.
Metadata not updating after port change Restart Plex server (known Plex bug with URL changes).
POMS auth errors Default credentials should work. If issues persist, check NPO API availability
Film not found Try original title: "Der Untergang" instead of "Downfall". Or provide IMDB ID: --imdb tt0363163
TMDB alternate titles not working Verify "configured": true and "status": "ok" under tmdb in /health/ready response.
Still it's not working Restart your Plex server.

Updating

Standalone docker-compose:

git pull && docker-compose down && docker-compose build --no-cache && docker-compose up -d

Portainer stack:

cd /path/to/vpro-cinema-plex
git pull
docker build -t vpro-plex-provider:latest .
# Then redeploy the stack in Portainer

Verify: curl http://localhost:5100/health

Cache and .env are preserved during updates.

Upgrade notes for specific versions

v4.0.0 — TV series support removed (breaking change)

TV series support has been removed. Only movies are now supported.

Migration:

  1. Remove the series provider from Plex (http://localhost:5100/series)
  2. Remove any TV Show agents that used VPRO as primary provider
  3. For TV libraries, switch to standard Plex Series agent
  4. Clear cache: curl -X POST "http://localhost:5100/cache/clear"

v3.1.0 — Breaking URL changes

Old New
http://localhost:5100/ http://localhost:5100/movies
http://localhost:5100/tv http://localhost:5100/series

Provider names also changed (added - Movies / - Series suffix).

Migration: Remove old providers in Plex, add new URLs, update agents, restart Plex.

v3.0.0 — series support (two-provider architecture)

Single provider → two providers (/movies and /series). Required by Plex API for proper secondary provider combining.

Migration: Remove old provider, add both new URLs, create separate TV Show agent.

Changelog

v4.1.0 — Improved poster and image handling

  • Poster extraction from Cinema.nl — Now extracts the main movie poster from the top of Cinema.nl pages (marked as PROMO_PORTRAIT), in addition to stills from the Afbeeldingen section. Uses alt-text matching and URL heuristics to identify posters reliably.
  • Fixed dead vprogids.nl image URLs — POMS API returns old vprogids.nl URLs that now return HTTP 410 Gone. The provider now converts these to cinema.nl URLs and scrapes for fresh images.vpro.nl URLs.
  • Proper image embedding in metadata — Images are now embedded directly in the metadata response (thumb, art, Image array) per the Plex TMDB example, not just via the /images endpoint.
  • Conditional images feature — The provider only declares the "images" feature when VPRO_RETURN_IMAGES=true. When disabled, Plex correctly falls back to secondary agents (Plex Movie) for artwork.
  • Rating array support — VPRO ratings are now returned in the Rating array format with cinemanl://image.rating scheme. Note: Plex UI only displays ratings with recognized schemes (imdb://, rottentomatoes://), so the rating is stored but won't show an icon.
  • WebP to JPEG conversion — Image URLs are converted from .webp to .jpg for better Plex compatibility.
  • Correct Plex image types — Fixed image type mapping to use Plex's expected values (coverPoster, background) instead of incorrect ones (poster, art).

v4.0.0 — Movies only (breaking change)

  • ⚠️ TV series support removed — VPRO's data sources don't provide complete series metadata (seasons, episodes, episode descriptions), which caused Plex scanning failures. This provider now only supports movies.
  • TMDB fallback metadata — When a movie isn't found in VPRO sources, the provider now returns basic metadata from TMDB (title, year, IMDB ID) so Plex can still add the movie. The description is omitted to let secondary providers (Plex Movie) fill it in.
  • Reduced not-found cache TTL — Changed from 7 days to 1 hour to allow quicker retries for newly indexed content
  • Simplified API — Removed /series endpoint and all TV-related routes

Migration from v3.x:

  1. Remove the series provider from Plex (http://localhost:5100/series)
  2. Remove any TV Show agents that used VPRO as primary provider
  3. For TV libraries, switch to standard Plex Series agent
  4. Clear cache: curl -X POST "http://localhost:5100/cache/clear"

v3.4.0

  • Cinema.nl direct scraper — Replaced DuckDuckGo/Startpage web search with direct cinema.nl scraping
  • IMDB verification — Cinema.nl fallback now verifies matches using IMDB IDs for reliable matching
  • Image extraction — Cinema.nl fallback extracts high-resolution images from the Afbeeldingen section
  • Migration complete — vprogids.nl/cinema has fully migrated to cinema.nl
  • Search optimizations — Year-in-query and model=cinema parameter for better search ranking
  • Circuit breaker — Prevents hammering cinema.nl after repeated failures
  • Credential refresh deprecated — Auto-refresh from vprogids.nl no longer works (site returns 404)

v3.3.0

  • Kijkwijzer content ratings — Dutch age classification (AL, 6, 9, 12, 14, 16, 18) now extracted from POMS API
  • Configurable metadata fields — New environment variables to control what metadata is returned:
    • VPRO_RETURN_SUMMARY (default: true) — Dutch descriptions
    • VPRO_RETURN_CONTENT_RATING (default: true) — Kijkwijzer ratings
    • VPRO_RETURN_IMAGES (default: false) — VPRO poster images
    • VPRO_RETURN_RATING (default: false) — VPRO appreciation ratings (experimental, see Limitations)
  • Fix Match thumbnails — Images now display in Plex's Fix Match dialog when VPRO_RETURN_IMAGES=true
  • Health endpoint improvements/health/ready now shows configured feature flags
  • Selective cache deletion — New /cache/delete endpoint for targeted cache management

v3.2.0

  • Added debug logging for troubleshooting
  • Docker environment variable passthrough improvements

v3.1.0

  • Breaking URL changes: //movies, /tv/series
  • Provider name suffix changes (- Movies / - Series)

v3.0.0

  • Two-provider architecture for proper Plex secondary agent combining
  • Added series/TV show support (removed in v4.0.0)

Limitations

  • Movies only — TV series support was removed in v4.0.0 because VPRO's data sources don't provide complete series metadata
  • POMS API is undocumented — Not officially supported by NPO; may change without notice
  • Not all content covered — Only films reviewed by VPRO Cinema; movies not found get basic TMDB fallback metadata
  • Artwork optional — Disabled by default; enable VPRO_RETURN_IMAGES or use Plex Movie fallback
  • Ratings display limited — Plex's Custom Metadata Provider API may store audienceRating values, but the rating icon displayed in the UI is controlled by the library's "Ratings Source" setting (Rotten Tomatoes, IMDb, or TMDb), not by the provider. Custom ratingImage URI schemes are not supported. This is a Plex architectural limitation — see the Plex Dev/API Forum for updates
  • Credential refresh broken — vprogids.nl/cinema has migrated to cinema.nl; auto-refresh no longer works but default credentials still function
  • Cinema.nl fallback — Direct scraping; may break if site structure changes

License

MIT — Do whatever you want with it.

Credits

  • VPRO Cinema for the Dutch film reviews
  • TMDB for alternate title data
  • Klaas (c_kick/hnldesign) — Original idea and development
  • Claude (Anthropic) — Implementation assistance

About

Plex Metadata Provider that fetches Dutch movie descriptions from VPRO Cinema. Uses NPO POMS API and TMDB alternate title lookup.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors