Admin access
Unlock your archive
Sign in with your admin username and password to browse your library, import Trakt history, and review webhook appends.
Now Playing
Live playback sessions across Plex, Emby, and JellyfinWatch history
Recently watched TV shows and moviesTV Shows
Movies
Most Active Playback Platforms
Ranked by stored watch eventsTop 5 Most Watched TV Series
Top 5 by distinct logged episodesWatch Activity
Monthly archive volumeMovies
Library view from watched historyAdmin Login
Local username and password sessionLogin & Security
Environment options used by the self-hosted portalADMIN_USERNAME
Sets the initial dashboard username. Saving the form above stores an app-managed replacement.
ADMIN_PASSWORD
Sets the initial dashboard password. Form changes are stored as a secure hash and are never shown.
API_KEY
Authenticates webhooks and external integrations. A random key is generated on first boot when omitted.
SESSION_SECRET
Signs the dashboard session cookie. A random secret is generated on first boot when omitted.
API Endpoints
Webhook listener and in-process background worker/api/webhook
Point Plex, Emby, and Jellyfin played/scrobble webhooks here. Valid watched events are saved to SQLite and synced to other platforms.
/api/cron-sync
The background scheduler runs every minute inside Plembfin. This endpoint is also available as an authenticated manual trigger for diagnostics.
Database Status
Local SQLite backendPlex Setup
Server URL, token, and usernameEmby Setup
API key, URL, and user IDJellyfin Setup
API key, URL, and user IDTMDB
Optional — enables poster artwork, cast, trailers, and metadata for movies and TV showsTMDB (The Movie Database) is a free community database used to fetch posters, cast lists, trailers, and descriptions. To get an API key:
- Create a free account at themoviedb.org
- Go to Settings → API in your account menu
- Click Request an API Key and choose Developer
- Fill in the form (personal use is fine) — the key is issued instantly
- Copy the API Key (v3 auth) value and paste it below
YouTube
Optional — fetches descriptions, publish dates, and duration for YouTube-only contentWithout this key, YouTube thumbnails still work in Fix Match and Edit Image — only the description and video metadata require it. The free quota (10,000 units/day) is more than enough for personal use. To get a key:
- Go to console.cloud.google.com and sign in with your Google account
- Create a new project (or select an existing one)
- In the left menu go to APIs & Services → Library
- Search for YouTube Data API v3 and click Enable
- Go to APIs & Services → Credentials and click Create Credentials → API Key
- Copy the generated key and paste it below
- Optionally click Edit API Key and restrict it to the YouTube Data API v3 for security
Save Configuration
Commits server fields to the server-only Firestore settings documentConfiguration not loaded yet.
Backup & Transfer
Export this installation or import a Plembfin backup
Idle
Use Export on the old installation, then import the downloaded JSON file here. The backup includes watch data, settings, and cache metadata, but not stored artwork files. Treat it as sensitive because saved media-server credentials and API keys may be included.
Backup & Transfer
Export this installation or import a Plembfin backupDownload all supported data from this installation.
Import replaces every collection included in the file. The local admin username, password, and active sessions are not changed.
[idle] Select Export or choose a backup file.
Full Sync Watchstates
Push your entire watch archive to every configured media server
Idle
Reads every record in your Firestore watchHistory collection and pushes the watched or unwatched state to each enabled media server. Use this after setting up a new Plex, Emby, or Jellyfin server, after a library rebuild that wiped playstates, or after restoring a server from backup. The sync respects your enabled and disabled flags from Apps settings. Progress is streamed to the log in real time and each server is processed independently, so a failure on one does not block the others.
Full Sync Watchstates
Push your entire watch archive to every configured media serverwatchHistory collection and pushes the watched or unwatched state to each enabled media server. Use this after setting up a new Plex, Emby, or Jellyfin server, after a library rebuild that wiped playstates, or after restoring a server from backup. The sync respects your enabled and disabled flags from Apps settings. Progress is streamed to the log in real time and each server is processed independently, so a failure on one does not block the others.[idle] Waiting for restore request.
Trakt History Importer
Bulk-import your Trakt watch history from exported files
Idle
Accepts the CSV and JSON files from a Trakt data export (unzipped). Files are parsed entirely in your browser and sent to /api/import in batches of 100 records. Each imported entry is written to watchHistory with the source field set to trakt_import, so it is always distinguishable from live webhook records. Duplicate detection is performed on the server — records that already exist are skipped, not overwritten. After importing, use Backfill Trakt Imports below to fetch poster artwork for the new rows.
Trakt History Importer
Bulk-import your Trakt watch history from exported files/api/import in batches of 100 records. Each imported entry is written to watchHistory with the source field set to trakt_import, so it is always distinguishable from live webhook records. Duplicate detection is performed on the server — records that already exist are skipped, not overwritten. After importing, use Backfill Trakt Imports below to fetch poster artwork for the new rows.[idle] Waiting for files.
System Integrity Check
Health probe across Firestore, Functions, and every media server
Runs a live diagnostic against every backend dependency the dashboard relies on. Checks include: Firestore read and write access, the Firebase Functions API reachability, and a connection test against each configured Plex, Emby, and Jellyfin server using your saved credentials. Results show the latency and status of each probe individually so you can pinpoint which component is failing. Run this before making configuration changes, after a deployment, or when diagnosing unexpected sync failures or missing data.
System Integrity Check
Health probe across Firestore, Functions, and every media server
Repair History Rows
Correct mislabelled media types and refresh missing poster URLs
Queries TMDB for each record in watchHistory that may have an incorrect type value — for example, a movie that was stored as a TV episode because the webhook payload was ambiguous. When TMDB returns a different type, the stored record is updated and the poster URL cache is refreshed. Only rows where the lookup disagrees with the stored type are modified, so it is safe to run at any time without risk of data loss. The repair log streams each corrected row in real time.
Repair History Rows
Correct mislabelled media types and refresh missing poster URLswatchHistory that may have an incorrect type value — for example, a movie that was stored as a TV episode because the webhook payload was ambiguous. When TMDB returns a different type, the stored record is updated and the poster URL cache is refreshed. Only rows where the lookup disagrees with the stored type are modified, so it is safe to run at any time without risk of data loss. The repair log streams each corrected row in real time.Idle
Backfill Trakt Imports
Fetch missing poster artwork for imported Trakt rows via TMDB
Scans every watchHistory row where the source is trakt_import and the poster URL is empty, then looks up artwork from TMDB and writes the URL back to Firestore. Runs in configurable batches with a per-call rate-limit delay to stay within TMDB's free-tier request quota. Only imported rows are touched — live webhook records are never modified. Run this once after a Trakt import, or again if poster artwork is still missing after a previous partial run.
Backfill Trakt Imports
Fetch missing poster artwork for imported Trakt rows via TMDBwatchHistory row where the source is trakt_import and the poster URL is empty, then looks up artwork from TMDB and writes the URL back to Firestore. Runs in configurable batches with a per-call rate-limit delay to stay within TMDB's free-tier request quota. Only imported rows are touched — live webhook records are never modified. Run this once after a Trakt import, or again if poster artwork is still missing after a previous partial run.Idle
Refresh All TMDB Metadata
Pre-cache cast, trailers, reviews, posters and artwork for every item in your library
Fetches full TMDB detail pages — cast and crew, trailers, reviews, and related titles — for every movie and TV show currently in your watchHistory, writes the results into the tmdbMetadataCache Firestore collection, downloads each poster and backdrop into Storage, and stamps the canonical poster back onto every watch record. This brings existing media to full parity with newly-added media: both detail pages and grid thumbnails are instant rather than waiting for a live API call. Run this after a large import or periodically. The cache is also updated automatically when a title is added or first viewed.
Refresh All TMDB Metadata
Pre-cache cast, trailers, reviews, posters and artwork for every item in your librarywatchHistory, writes the results into the tmdbMetadataCache Firestore collection, downloads each poster and backdrop into Storage, and stamps the canonical poster back onto every watch record. This brings existing media to full parity with newly-added media: both detail pages and grid thumbnails are instant rather than waiting for a live API call. Run this after a large import or periodically. The cache is also updated automatically when a title is added or first viewed.Idle
Clean Duplicate History Rows
Remove redundant watchHistory records, keeping the newest per item
Scans every document in watchHistory, groups them by mediaKey (the canonical title and ID combination), and deletes all but the most recently logged entry for each unique item. Duplicates can accumulate when webhooks fire multiple times for a single watch event, after re-importing a Trakt export that partially overlaps existing records, or after a database migration that ran twice. No watched data is lost — only the older copies of an already-recorded item are removed. Safe to run at any time.
Clean Duplicate History Rows
Remove redundant watchHistory records, keeping the newest per itemwatchHistory, groups them by mediaKey (the canonical title and ID combination), and deletes all but the most recently logged entry for each unique item. Duplicates can accumulate when webhooks fire multiple times for a single watch event, after re-importing a Trakt export that partially overlaps existing records, or after a database migration that ran twice. No watched data is lost — only the older copies of an already-recorded item are removed. Safe to run at any time.Idle
Automatic Watch History Backups
A local backup is always kept; add Backblaze B2 or another destination below to also copy each backup off-box. Use either or both.Backups are always written to data/backups/watch-history first, then mirrored to any enabled remote destinations below. Artwork, TMDB caches, credentials, settings, logs, and active sessions are excluded.
Remote Destinations
Mirror backups to OneDrive, Dropbox, WebDAV, or S3-compatible storage. The local copy is always kept.Available Backups
Download or validate a backup before restoring itRestore & disaster recovery
What to keep so you can restore from local or the cloud — even on a fresh installBackups are independent — restoring from local or from the cloud both produce the same result. You can rely on either, or keep both for safety.
Restore from a local backup
Local backups live on the Plembfin server at data/backups/watch-history. To restore, use the Available Backups list above → Merge Restore (keeps newest on conflict) or Replace (wipes current history first).
To save for recovery: back up that folder (it's just .json.gz files), or trust your normal server/volume backups. Dropping a valid plembfin-watch-history-*.json.gz file into that folder makes it appear in the list.
Restore from Backblaze B2 (or any cloud destination)
Open the destination above → Restore from here → pick a backup → Merge Restore or Replace. Plembfin downloads it, verifies it, and restores it.
To save for recovery (write these down — the secret key is shown only once): Region, Bucket name, Key prefix (if you set one), keyID, and applicationKey. With those five you can re-add the destination on any fresh install and restore.
Restore onto a brand-new install
- Install Plembfin and log in.
- Settings → Backups → Add destination → re-enter the saved cloud details above → Save → Test.
- Restore from here → choose the newest backup → Replace (a fresh install has nothing to merge with).
That's it — watch history, playstate, and resume progress are back. (Artwork and caches rebuild on their own.)
Sync
Outstanding watched-state sync jobs and target diagnostics[idle] Waiting for force sync.
Sync history
Recent watched-state and resume propagation attemptsDebug Logs
Local diagnostic stream for connection tests and now-playing polling