Library Doctor for Jellyfin — guide

Installation

You need Jellyfin Server 12.2 or newer and a MediaLab Works account with a license that covers the plugin (an active subscription, a lifetime license within its update window, the Jellyfin bundle or the free 30-day trial).

From your plugin repository (recommended). Jellyfin then installs and updates the plugin by itself.

  1. In your MediaLab Works account open Licenses → Jellyfin plugin repository and click Create address. Copy it: it is private (it contains a token), so do not share it; you can regenerate or revoke it at any time.
  2. In Jellyfin open Dashboard → Plugins → Repositories, click +, give it a name (for example MediaLab Works), paste the address and save.
  3. In Dashboard → Plugins → Catalog find Library Doctor by MediaLab, click Install, then restart Jellyfin.

The repository lists only the plugins your licenses cover, and only the versions they allow. Its download links are signed and expire after a few hours, so Jellyfin always reads a fresh list.

From the public catalog. https://medialab.works/jellyfin/manifest.json lists every released MediaLab Works plugin with its public build; medialab Server adds it by itself. You can add it the same way to install a plugin before you have a key: the plugin then asks for the license, or starts the trial, on its License page. With a lifetime license whose update window has ended, keep to your private repository, which offers only the versions your license allows.

Manually, from a zip file.

  1. In Account → Licenses download jf-librarydoctor_x.y.z.zip (the SHA-256 of the file is shown next to the button).
  2. Extract it into a new folder Library Doctor by MediaLab_x.y.z.0 inside the Jellyfin plugins folder:
    • Linux (package): /var/lib/jellyfin/plugins/
    • Docker (official image): /config/plugins/ in the config volume
    • Windows: %ProgramData%\Jellyfin\Server\plugins\
  3. Restart Jellyfin.

License activation

  1. Open the plugin page in Dashboard → Plugins → Library Doctor by MediaLab and click License.
  2. Paste your license key and click Activate with key. The plugin contacts the license server, binds the key to this Jellyfin server and shows the license as Active.
  3. No key yet? On the same page enter your e-mail and start the 30-day free trial (card needed, nothing to pay today) or buy a license; the plugin activates itself when the payment is done.

The license is checked once a day and at every Jellyfin start. Without internet the plugin keeps working for 7 days after the last successful check. Without a valid license the plugin stops its work and only its License page keeps working (trial, key, renewal); nothing is deleted, and everything resumes as soon as the license is valid again. Move license (release this server) on the same page frees the key for another server.

Updates and rollback

  • From the repository: Jellyfin shows new versions in Dashboard → Plugins and can update the plugin automatically; restart Jellyfin to load the new version.
  • From the plugin's License page: with a valid license, Install update installs the newest version your license allows and Roll back to x.y.z goes back to the previous version (for example if something stopped working after an update). Restart Jellyfin after either. Both packages are checked against the checksum published by the license server.
  • A lifetime license receives the versions released during its update window; after it, the plugin keeps working on the last version it may use.
  • After Roll back to x.y.z, Jellyfin's automatic update for that plugin is turned off until the next Install update on the License page, so the scheduled task Update Plugins (at start-up and daily) does not bring the newer version back.
  • Lifetime license and automatic updates. The public Jellyfin catalog (https://medialab.works/jellyfin/manifest.json) always offers the newest version. When the update window of a lifetime license has ended, versions released after it are not licensed: if Jellyfin's automatic plugin updates install one, the plugin stops with "version not licensed" until you go back with Roll back to x.y.z on its License page or buy an update pass. To avoid it, don't let Jellyfin update this plugin automatically: add your private plugin repository, which offers only the versions your license allows, instead of the public catalog, or remove the triggers of the scheduled task Update Plugins in Dashboard → Scheduled Tasks (this stops automatic updates for every plugin).

Source code

The GPL-3.0 source code of each version is in Account → Licenses (Source code), next to the download, as a .tar.gz archive made from the exact commit of that version.

Configuration

Open Library Doctor in the Jellyfin dashboard menu (administrators only). There is nothing to set up: the page reads your movie and episode libraries each time it opens.

Before using the quarantine, give Jellyfin write access to the library folders. The quarantine creates a .medialab-de-sters (Romanian for “to delete”) folder at the root of each library folder and moves files out of their folders, so the user Jellyfin runs as (on Linux usually jellyfin) needs write permission on the library folders and the folders inside them. In Docker, mount the media volumes read-write (without :ro). Everything else in the plugin only reads.

Daily use

  1. The numbers at the top: video files, duplicate groups, the space to reclaim from duplicates, unmatched movies and the total size.

  2. Duplicates: each group shows the versions, the best one first and marked best (higher resolution first, then HDR, then a more efficient codec — AV1, HEVC, VP9 — then bitrate).

    • Movies are compared by their TMDb or IMDb id, episodes by series, season and episode number.
    • Movies without a TMDb or IMDb id are compared by title and year. Their groups are marked by title and year (no metadata id) — check: open both and make sure they really are the same movie before you group them or quarantine one (a remake can have the same title, and sometimes the same year).
    • Group as versions keeps all the files and makes them one title with a version choice at playback (Jellyfin's own version grouping). No file is moved.
    • Quarantine, on a weaker version, asks for confirmation and moves that file and its companions to quarantine (see below).
  3. Unmatched movies: movies with no metadata id. Click Open, then use Identify in Jellyfin to pick the right movie; it disappears from the list the next time you open the page.

  4. Missing, broken or truncated files: click Check the files. The check runs in the background, file by file, and shows its progress; on a large library it takes a while and you can leave the page. For each file it checks that the file exists, reads it with ffprobe (at most 30 seconds) and then decodes the last 3 seconds of the video with ffmpeg (at most 60 seconds). Each problem is listed with its cause:

    • missing from disk;
    • cannot be read, with the error;
    • no video track: the only "video" is an image (cover art does not count as video), as in a file of random bytes;
    • no duration (broken header);
    • zero duration;
    • the end is missing (incomplete download/copy): the last seconds cannot be decoded. A file cut off very close to its end (for example at about 98%) may still pass, because its last seconds can still contain decodable frames.

    The result is kept until Jellyfin restarts.

  5. Space: the space by library, by resolution and by codec, and the 20 largest files no user has watched.

The quarantine

  • What is moved: the video file and the files in the same folder whose name starts with the same name followed by . or - — for example Movie (2010).mkv with Movie (2010).en.srt, Movie (2010).nfo and Movie (2010)-poster.jpg. Other files in the folder stay where they are.
  • Where: into .medialab-de-sters at the root of the library folder that holds the file, with the same sub-folders (/media/movies/Movie (2010)/Movie (2010).mkv goes to /media/movies/.medialab-de-sters/Movie (2010)/Movie (2010).mkv). If a file of the same name is already in quarantine, the new one goes into a sub-folder with a unique name, so nothing is overwritten.
  • If a move fails half-way (for example one companion file cannot be moved): the files already moved are put back, and a copy left behind in .medialab-de-sters by an interrupted move between disks is removed, so you never end up with the same file in two places. If the files cannot be put back either, the entry stays in the Quarantine list and Restore brings back what was moved. The page shows the error.
  • After the move, Jellyfin rescans the library and the quarantined version disappears from it; Jellyfin does not import the .medialab-de-sters folder.
  • Restore: the Quarantine list shows each title with its original paths and size. Restore moves the files back exactly where they were and rescans the library. If a file is both in quarantine and at its original path (for example you added the movie again), the restore is refused and nothing is touched.
  • Final deletion is yours: the plugin never deletes. When you are sure, delete the .medialab-de-sters folder (or the files in it) from disk; the space is freed only then.
  • The list of quarantined files is kept in the plugin's data folder, so it survives a Jellyfin restart. Do not rename or move the .medialab-de-sters folder while something in it may still need restoring.

Troubleshooting

  • Quarantine, Restore or Group as versions shows "It could not be done": the message after it gives the reason.
  • Quarantine fails: usually Jellyfin has no write access to the library folder; see Configuration. Files outside the folders of your Jellyfin libraries, and files already in .medialab-de-sters, are also refused.
  • Restore fails: a file is already at the original path (for example you added the movie again). Move or delete that file, then restore.
  • Every file is reported "cannot be read": ffprobe was not found; install jellyfin-ffmpeg and check its path in Dashboard → Playback → Transcoding.
  • A group marked "by title and year — check" holds two different movies: do not group or quarantine anything; identify both movies in Jellyfin, and once they have their TMDb or IMDb ids they are no longer grouped.
  • A movie is in the library twice but not listed under duplicates: the two items have different ids, or one has an id and the other has none (then identify it).
  • A file that does not play is not reported: the damage may be in the middle of the file, which is not checked, or the file was cut off very close to its end.

The build is public; using the plugin requires a license or the free 30-day trial.