Connect Radarr, Sonarr, and Jellyfin

A full walk runs on the Library’s schedule, once an hour by default. A webhook rescans one folder as soon as the Library has no other Job running, so a new title shows on the screens without waiting for the next walk.

The address

Every existing Library reports its webhook address:

$ kubectl -n media get library movies -o jsonpath='{.status.webhook}'
http://library-operator.liken-system.svc/webhook/media/movies

The address names the operator’s own Service and the Library, so it holds for as long as the Library exists. The Service is a ClusterIP, reachable only inside the cluster, and the endpoint checks nothing but the method and the path. Give it only to tools that run in the same cluster.

What a POST does

The operator reads the body for the path of what changed, in this order: movieFile.path, episodeFile.path, movie.folderPath, movie.path, series.path, then a top-level Path. The first one found names the folder to rescan. A body with none of them, or no body at all, schedules a full walk. Every POST answers 204 No Content.

The operator holds the folder and starts one walk Job for it on its next pass. That Job walks the folder and runs every phase the Library’s sources serve over it, so the title gets its id, its .nfo file, its art, and its tiles in the same Job, as each phase before them finishes the title.

An upgrade imports a new file under the old name, and the download manager removes the old file’s thumbnail and tiles. The rescan compares the file’s size with its probe record, finds a new file, and opens the facts of that file: the probe, the marks, the tiles, and the thumbnail. The file keeps its first arrival. The scanning guide says what the walk opens and what it keeps.

A Library runs one Job at a time. A webhook that arrives while a Job runs waits for it, and every folder the webhooks named in the meantime goes into the next Job. So five imports during one run start one Job, not five. The trailer files stop starting new titles fifteen minutes into a run, so a Job that works through a backlog of them ends within about fifteen minutes and one title, and the folders that waited go next. Trickplay runs in a worker Job that runs no catalog agent, outside the Library’s one Job at a time, so no folder waits for a decode. Past 64 distinct folders held for one Library, the whole set collapses to a full walk.

Each pod of the trickplay worker sends the same request when its video is done, with the video’s title folder in a top-level path field, so the catalog reads the new tiles at once. The operator’s log counts each one as a webhook that named 1 folder.

Radarr

Add a connection of type Webhook. Set the URL to the movies Library’s address, and the method to POST. Enable the events that name a file: import, upgrade, and rename. An event with no path, such as the test, schedules a full walk. Saving a new connection runs the test on its own, and the Test button runs it again, so a new connection costs one full walk for each. Make the connection once and let the walks finish before you test it by hand.

Sonarr

The same, with the series Library’s address.

Jellyfin

Install Jellyfin’s Webhook plugin and add a generic destination with the Library’s address. An item-added notification has a top-level Path, which is what the operator reads.

One writer beside the media

Radarr and Sonarr write beside the media too. This operator reads the uniqueid in the Kodi .nfo they write, so keep that metadata option on. Turn off their image writes, so that the art facts are the only writers of images, and turn off their import of extra files. The Jellyfin guide lists the Jellyfin switches.

By hand

curl -X POST http://library-operator.liken-system.svc/webhook/media/movies

A body naming a folder narrows the walk to that folder:

curl -X POST http://library-operator.liken-system.svc/webhook/media/movies \
  -H 'Content-Type: application/json' \
  -d '{"movie":{"folderPath":"/media/movies/Example Movie (2019)"}}'