Personal fork of Agregarr for testing changes before submitting upstream. Automatically builds to bitr8/agregarr:develop on Docker Hub.
Available on Docker Hub as bitr8/agregarr. Both develop and latest tags track the develop branch and include all fork-only features plus open upstream PRs. The image rebuilds automatically on every push to develop.
amd64 only — no arm64/Apple Silicon builds.
Switching from upstream? Replace the image line in your existing compose file — config volumes are compatible:
- image: agregarr/agregarr:latest
+ image: bitr8/agregarr:developservices:
agregarr:
image: bitr8/agregarr:develop
container_name: agregarr
volumes:
- /path/to/config:/app/config
- /path/to/placeholder/movies:/data/movies # Optional: Coming Soon
- /path/to/placeholder/tv:/data/tv # Optional: Coming Soon
environment:
- TZ=Australia/Sydney
ports:
- 7171:7171
restart: unless-stoppedFor general Agregarr configuration (services, collections, overlays etc.), see the upstream docs — note that they reference the upstream image, not this fork.
Features in this fork that aren't in upstream Agregarr. Some have open PRs, others are fork-only. These exist because upstream has scaling pain points when running many collections, and placeholder lifecycle has gaps that leave orphaned entries in Plex.
Overlay jobs on large libraries can run 30+ minutes with no feedback. This adds live dashboard status showing progress, item counts, ETA, and a stop button for each library.
Upstream Agregarr makes individual API calls per item, per rating source, per cache miss. With 40+ collections and 10k+ items, syncs take hours and hammer external APIs. These changes reduce that to minutes.
| Fix | Why | Impact |
|---|---|---|
| Batch IMDb Prefetch | Upstream fetches IMDb ratings one item at a time | Thousands of API calls reduced to tens |
| Adaptive TTL Caching | All cached ratings expire at the same fixed interval | New releases: 12h, older content: up to 30 days |
| Configurable Rating Cache | No way to tune cache duration | ratingsCacheMaxDays in settings.json (default: 30) |
| Collection Sync Cache | getAllCollections() called on every loop iteration |
Cached with mutation-based invalidation. Saves ~25-30s |
| Batch Overlay Metadata | Plex metadata fetched one item at a time | Batches of 200 per API call. Falls back on failure |
| AniList Retry Cap | parseInt NaN bug causes infinite tight retry loops |
Capped at 5 attempts |
| Release Date TTL Cap | Stale cache shows wrong overlay for new releases | Items within 3 days of release: max 2h TTL |
Persistent TMDB Resolution Cache -- Letterboxd collections require resolving titles to TMDB IDs. Upstream re-resolves every item on every sync (6 TMDB API calls each). This caches results in SQLite with adaptive TTL.
| Metric | First Sync (cold cache) | Second Sync (warm cache) |
|---|---|---|
| TMDB API calls | ~33,000 | 0 |
| Resolution time | ~42 min | < 1 sec (all cache hits) |
| Cache entries | 5,656 created (53 negative) | 5,656 served |
Plain HTTP for Letterboxd (letterboxdUsePlainHttp) -- Upstream launches headless Chromium (Playwright) for every Letterboxd page fetch. This was added to bypass Cloudflare, but Letterboxd list pages return full HTML without JS rendering. Plain HTTP (axios) is sufficient.
| Playwright | Plain HTTP | |
|---|---|---|
| Per page | ~10,500ms | ~280ms |
| 142 pages | ~25 min | ~40 sec |
| Cloudflare blocks | 0 | 0 |
To enable, add to settings.json:
{
"main": {
"letterboxdUsePlainHttp": true
}
}Defaults to false (Playwright) for safety. Flip back if Cloudflare starts blocking.
Upstream placeholder cleanup has gaps that leave orphaned entries in Plex and don't respond to filter changes.
Retroactive Filter Application -- Upstream placeholder filters (year, language, country, genre, keyword) only apply when placeholders are created. Filters added after creation have no effect on existing placeholders. This fork evaluates existing placeholders against the current filter config during cleanup and removes those that no longer pass. Rating filters are skipped for retroactive evaluation since unreleased content has no ratings.
Self-Healing for Stuck Records -- If a placeholder file is deleted externally (disk issue, manual cleanup), the DB record blocks re-creation. This fork detects missing files for active items and clears the DB record so the creation flow can recreate the placeholder on next sync. Only triggers on confirmed file-not-found (ENOENT), not transient filesystem errors.
Direct Plex Deletion -- Plex ignores empty directories during library scans. When a placeholder file is deleted and its folder becomes empty, scanLibrary() + emptyTrash() won't remove the stale database entry. This fork deletes stale items directly via DELETE /library/metadata/{ratingKey}, matching by exact file path. Falls back to scan+trash when direct deletion can't find matches.
TV Episode Cleanup -- TV placeholders create an S00E00 episode that persists in Plex after the placeholder file and DB record are cleaned up. Upstream's findItemsByFilePaths queries shows (not episodes), so TV paths never match. Config cleanup also never explicitly deletes the Plex episode. This fork pre-resolves episode ratingKeys (type=4) before file deletion, and navigates show > Season 00 > Episode 0 to delete stale episodes during config cleanup.
Sonarr Folder Naming -- Agregarr creates placeholders at /tv/Show (2024)/ but Sonarr uses /tv/Show (2024) [imdbid-tt1234567]/. When real content arrives, Plex sees them as different shows, leaving orphaned entries. This fork extracts the folder name from Sonarr's series path. Falls back to standard naming if the show isn't in Sonarr.
Download Status Awareness -- Upstream doesn't check whether content has already been downloaded in Radarr/Sonarr. This fork queries *arr download status in batch, skips placeholder creation for items already downloaded, and uses download status as a cleanup signal. Prevents unnecessary placeholders for content that's about to arrive.
Post-Sync Hub Verification -- After collection sync completes, queries each filtered hub and applies missing trailer-placeholder labels to any items that slipped through. A safety net that catches label leaks regardless of which pipeline stage failed to apply them.
| PR | Description | Depends On |
|---|---|---|
| #526 | Retroactive placeholder filter evaluation during cleanup | - |
| #516 | Check *arr download status + Sonarr folder naming | - |
| #515 | Remove vm2 sandbox dependency | - |
| #514 | Fix SVG sanitisation bypass | - |
| #513 | Fix export path traversal | - |
| #503 | Fix TV placeholders leaking into filtered hubs | - |
| #498 | Deduplicate hub identifiers to prevent convergence failures | - |
| #492 | Title fallback for TV placeholders without TMDB GUID | #491 |
| Feature | Why Fork-Only |
|---|---|
| Direct Plex API deletion for stale placeholders | Requires "Allow media deletion" in Plex |
| Post-sync hub verification for label leaks | Safety net for fork's label-based filtering |
Legacy Cleanup: TV placeholders created before the Sonarr folder naming fix may not match Sonarr's naming convention. If orphaned placeholders appear after real content arrives, delete the placeholder folder and let the next sync recreate it correctly.
| PR | Description |
|---|---|
| #504 | Support Maintainerr v3 API (mediaServerId rename) |
| #491 | Handle Plex returning TV seasons as Children.Directory |
| #481 | Guard splice in arrangeCollectionItemsInOrder |
| #483 | Parallelise collection membership check in overlay test |
| #482 | Index MAL IDs for constant-time lookups |
| #467 | Scan all placeholder-enabled libraries for discovery |
| #459 | Pass rating filters and seasonGrabOrder to multi-source collections |
| #456 | Separate placeholder filters independent of auto-request filters |
| #454 | Resolve Letterboxd items via film page TMDB links (#448) |
| #453 | Re-apply placeholder markers during global discovery (#414) |
| #452 | Disambiguate TMDB person search for person spotlight |
| #450 | Use episode air date for TV recently released filtered hubs |
| #446 | Add date format options for US and UK/AU locales |
| #445 | Persist applyOverlaysDuringSync for pre-existing collections |
| #444 | Use correct Plex API endpoint for collection title updates |
| #413 | Pass options to ExternalAPI constructor correctly |
| #405 | Fix Letterboxd title extraction from data-item-name |
| #400 | Empty Plex trash after placeholder cleanup |
| #387 | Skip date filtering for non-Coming-Soon with includeAllReleasedItems |
| #358 | IMDb Top 250 English Movies collection type |
| #356 | Handle 404 gracefully when deleting hub items |
| #350 | Validate SVG icon dimensions and file type |
| #349 | Don't double-estimate digital release dates |
| #348 | Fix scheduler startNow immediate sync and deadlock bugs |
| #345 | Multi-source label regex for collection matching |
| #340 | Handle Jellyfin trickplay directories during cleanup |
| #332 | Trigger Plex scan after placeholder cleanup |
| #321 | Surface per-collection sync errors to UI |
| #306 | Uniform scaling for non-standard poster aspect ratios |
| #305 | Downgrade library mismatch message to debug level |
| #304 | Sync networksCountry to sources array on change |
| #303 | Fetch Maintainerr collections in overlay test route |
| #302 | Return episodeNumber from fetchReleaseDateInfo |
| #300 | Harden API clients and file operations |
| #282 | Sanitize error responses |
| #278 | Filter daily shows from Coming Soon collections |
| #277 | TMDB poster caching and race condition fixes |
GPL-3.0, same as upstream.
All the real work is by the Agregarr team.
