Skip to content

Add a Sonarr/Radarr webhook that applies cached artwork when new media is imported#58

Draft
Thoroslives wants to merge 5 commits into
mscodemonkey:mainfrom
Thoroslives:import-webhook-pr
Draft

Add a Sonarr/Radarr webhook that applies cached artwork when new media is imported#58
Thoroslives wants to merge 5 commits into
mscodemonkey:mainfrom
Thoroslives:import-webhook-pr

Conversation

@Thoroslives

@Thoroslives Thoroslives commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Important

Blocked by #57. This webhook reads the cached user index that #57 adds, so please merge #57 first. Until then this PR's diff includes #57's commit; once #57 is merged I will rebase so it shows only the webhook changes.

With cache_user_scrapes enabled (#57), the app already knows every poster your favourite ThePosterDB users have uploaded. This adds an opt-in webhook endpoint that Sonarr and Radarr can call when they import something, so a newly added movie or episode gets its artwork within a minute or so instead of waiting for the next scheduled run.

On an import event the title is looked up in the cached index (you configure which users to apply from, in order of preference, first match wins); if one of them covers it, that single artwork is applied through exactly the same processing path as a normal scrape, so artwork labels, skips and Kometa asset mode all behave as usual, and nothing is fetched or applied when no cached artwork matches. Imports usually reach the webhook before Plex has scanned the new file, so there is a configurable delay before the first attempt plus a short retry, after which it gives up and leaves the item to the next scheduled run.

  • enable_webhooks (default false, so the endpoints return 404 when disabled), webhook_token (required; sent as an X-Webhook-Token header, the connection's Basic password, or a ?token= query parameter), webhook_tpdb_users, and webhook_apply_delay (default 30 seconds) in config.json, plus a Webhook settings section in the web UI
  • Endpoints POST /webhook/radarr and POST /webhook/sonarr; the apps' Test button is acknowledged so the connection can be saved; only "On File Import" events do any work, everything else is acknowledged and ignored
  • Sonarr imports apply the show cover and the covers for the imported seasons; Radarr imports apply the movie poster; ambiguous title matches (for example same-name remakes) are skipped rather than guessed
  • No new dependencies; unit tests for the payload parsing and index lookup under tests/
  • README updated with setup steps for both apps

Depends on #57.

@Thoroslives
Thoroslives force-pushed the import-webhook-pr branch 2 times, most recently from 582c2e6 to af94055 Compare July 16, 2026 07:31
When ThePosterDB's upload counter overshoots the pages it lists, the crawl used
to run on into empty pages; the parse error on those marked the crawl unclean,
which blocked both the crawl-state record and the tombstone pass, so an affected
user did a full crawl every run and never reconciled.

Stop the crawl at the first page that fetches cleanly but lists nothing, and
don't treat that empty page as a failure. Guard the tombstone pass so a full
crawl that was cut short (a markup change parsing to zero posters) never deletes
what it didn't reach: only reconcile when the crawl covered most of the user's
reported uploads, and otherwise leave the full-crawl timestamp unset so the next
run retries. Also surface how many new assets a run added to the cache.
@Thoroslives

Copy link
Copy Markdown
Contributor Author

Rebased onto v0.8.8 for clean mergeability.

Thoroslives and others added 2 commits July 21, 2026 16:14
… label

_hydrate_from_cache built the artwork dicts that feed the upload processor
with the key "type", but get_posters and the processor use "file_type".
ARTWORK_ID_MAP.get(None) then returns None and PlexUploader.set_artwork does
None + md5(url), raising "unsupported operand type(s) for +: NoneType and str".
Latent until a cached crawl meets an unlocked in-library item.

Regression test drives _hydrate_from_cache for real (the existing cache tests
stub it out) and asserts every produced dict carries a file_type that
ARTWORK_ID_MAP resolves.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The import webhook's _artwork_dict keyed the artwork type as "type", but the
upload processor reads "file_type" to resolve the Plex label prefix. It came
out None and PlexUploader.set_artwork did None + md5(url), so every Radarr/
Sonarr import with cached artwork died with "unsupported operand type(s) for
+: NoneType and str" (seen in production on a Disclosure Day (2026) import).

Regression test builds the webhook artwork dict and runs it through the same
label path the processor uses, asserting it no longer crashes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant