Jellyfin 10.11 plugin

Fix a drifting subtitle without leaving Jellyfin

Subtitle Sync installs into your Jellyfin server, listens to where speech actually happens in a film or episode, and writes a corrected copy of the subtitle track beside the media. No downloading the video, no hunting for a better release, no editing timestamps by hand.

The same algorithm as the browser version of this site, running inside your dashboard against the files you already have.

What it does

A Jellyfin film detail page for a movie called Sample Clip, showing its video, audio and subtitle tracks, with the overflow menu open on the right. The menu lists the client's usual entries - Add to collection, Download, Edit metadata, Edit subtitles, Identify, Media Info - and directly beneath Edit subtitles is an extra entry, Sync subtitles..., added by this plugin.
Jellyfin's own overflow menu, with the plugin's entry sitting under Edit subtitles. Note that this is the shortcut and not the main route: it is the one entry point that also needs the File Transformation plugin. Without it everything below still works from Dashboard > Plugins > Subtitle Sync, which is why that is the path this page calls primary.

SRT timestamps are plain wall-clock times and carry no framerate, so a subtitle authored against a different release does not just sit at a fixed offset: it drifts further out the deeper into the episode you get. Subtitle Sync corrects both.

  1. 1

    The server decodes the audio

    Jellyfin's own ffmpeg turns the audio track you pick into 16 kHz mono PCM and streams it to your browser. The video is never uploaded anywhere.

  2. 2

    Your browser finds the speech

    A WebAssembly build of WebRTC VAD marks every 30 ms frame as speech or silence, producing a picture of when people are talking.

  3. 3

    It matches the subtitles against that

    Cross-correlation over a set of candidate framerate ratios picks the ratio and offset that line the cues up with the speech.

  4. 4

    You save the corrected track

    The result is written next to the media file and Jellyfin re-scans the item, so the new track shows up in the player within seconds.

The heavy lifting happens in your browser, not on the server. A NAS with a modest CPU only has to decode audio.

Requirements

Jellyfin 10.11 or newer
The plugin targets the 10.11 plugin API and runs on net9.0, which is what 10.11 ships. There is no upper bound: the manifest declares 10.11 as a minimum, so newer servers are offered the plugin too. It will not load on 10.10 or earlier.
An administrator account
In principle the API splits permissions: analysing a track needs the Subtitle Management permission and saving into a library needs an administrator. In practice the 10.11 web client puts every plugin page behind an admin-level route guard, so only an administrator can reach the page at all, whatever their subtitle permission is set to.
Media that lives on disk, as a local file
Saving writes a sibling file next to the video, so the library folder has to be a real, writable path on the server. Network streams and disc folders have nowhere to put the result.
File Transformation (optional)
Only needed for the Sync subtitlesentry inside the Subtitles menu on a detail page. Everything works without it from Dashboard > Plugins > Subtitle Sync. Jellyfin 10.11 has no plugin dependency mechanism, so we cannot install it for you - see below.

Install

1. Add the repository (recommended)

In your Jellyfin dashboard, go to Plugins > Repositories, add a repository with any name you like, and paste this URL:

https://subtitlesync.sircen.dev/jellyfin/manifest.json

Then open Plugins > Catalogue, find Subtitle Sync under Subtitles, and install it. Restart Jellyfin: it only scans for plugins at startup, so the restart is not optional. The dashboard should then list Subtitle Sync as Active. Installing from the repository is also what makes future versions show up as updates.

2. Or install the zip by hand

If your server cannot reach this site, download subtitle-sync_<version>.zip from the GitHub releases page, create a folder for it inside your Jellyfin data directory under plugins/, and extract the zip contents straight into that folder - the assemblies and meta.json sit at the zip root with no wrapping directory. Restart Jellyfin afterwards. A hand-installed copy will not receive updates automatically.

3. Optional: File Transformation, for the menu item

The Sync subtitlesentry in a film or episode's Subtitles menu is added by injecting a small script into the web client, which needs the third-party File Transformation plugin. Jellyfin 10.11 has no way for one plugin to require another, so you have to add its repository yourself:

https://www.iamparadox.dev/jellyfin/plugins/manifest.json

Its plugin ID is 5e87cc92-571a-4d8d-8d98-d2d4147f9f90, which is worth checking against if you are not sure whether you already have it installed. This step is genuinely optional, and skipping it costs you one shortcut and nothing else.

Using it

There are two ways in.

The reliable path

Dashboard > Plugins > Subtitle Sync

Opens the plugin's settings page, which has a button through to the sync page. The sync page starts on a picker listing your most recently added items, with a search box. Nothing else is required for this to work.

The shortcut

A film or episode > Subtitles > Sync subtitles

Jumps straight into the sync page with that item already loaded. Needs File Transformation, and is the part most likely to stop working after a Jellyfin upgrade.

Then

  1. 1

    Pick the version, subtitle track and audio track

    Any track Jellyfin lists works, external file or embedded. Tracks that can never be synced are shown disabled with the reason.

  2. 2

    Press Sync subtitles and wait

    The page reports what it is doing: reading the track, checking the signal cache, streaming audio, detecting speech, correlating. Cancel stops the server decoding immediately.

  3. 3

    Read the result

    You get the winning framerate ratio and offset, a score table for every candidate ratio, the first corrected cue as a preview, and any warnings verbatim. Warnings are the honest signal - read them before saving.

  4. 4

    Nudge if it is close but not right

    Adjust the offset or pick a different ratio by hand. The correction is re-applied instantly with no re-analysis and no further audio transfer.

  5. 5

    Save to the library, or download the .srt

    Save writes the file beside the media and queues a re-scan. Download gives you exactly the same file to place yourself.

The plugin's sync page after a completed run. It reports a best match of ratio 1.0, offset only, at minus 3.200 seconds with a score of 0.9094, 0.7% clear of the runner-up, and that 10 cues will be re-timed. A warning says the top two candidates scored similarly and are worth double-checking. Below it a table lists all six candidate ratios with the offset and score each one reached. Below that, an Adjust by hand block holds the recovered offset and ratio in editable fields, a preview reading “First cue moves from 00:00:04,220 to 00:00:01,020”, and buttons to save as a new track or download the .srt.
A genuine run, not a staged one: this is the repository's Structured Clip fixture, whose subtitle track is displaced by exactly -3.2 s, and the page recovered -3.200 s. The warning is the page doing its job rather than a fault - two ratios landed within a percent of each other on a very short clip, and it says so instead of presenting the winner as certain.

Where the file goes, and how to undo it

Saving writes a new file next to the video, named after it:

<video file name>.<language>.synced.srt

So Movie (2019).mkv gains Movie (2019).en.synced.srt, and it appears in the player's track picker as synced - English - SRT - External. If a file of that name already exists, a numbered suffix is added rather than replacing it.

The original is never touched. The subtitle you synced from is left exactly as it was, so undoing a sync is a matter of deleting the .synced.srt file. The one exception is the Overwrite the original subtitle filesetting on the plugin's configuration page, which is off by default. Turn it on only if you are comfortable with a destructive edit that has no undo.

What it will not do

None of these are bugs waiting to be fixed. Knowing them up front is cheaper than discovering them halfway through a season.

The browser tab has to stay open
The analysis runs in your browser, not on the server. Navigating away or closing the tab cancels the run, and the server stops decoding as soon as the connection closes.
A first run transfers real bandwidth
Decoded audio is roughly 115 MB per hour of runtime. Once the server has cached the speech signal for that file, later runs fetch about 45 KB per hour instead, so re-running with different settings or on a second track is nearly instant. Fine over a LAN, painful over a slow remote link.
Image-based subtitles can never work
PGS, VOBSUB and DVB tracks are sequences of pictures. There is no text to correlate against speech, and no conversion produces any. Those tracks are disabled in the picker rather than left to fail.
One track at a time
There is no batch mode and no season-wide sync. Each episode is its own run, deliberately: the correct offset differs per file, and a wrong answer applied silently across a season is worse than no answer.
Styling is lost on non-SRT tracks
An ASS or SSA track is converted to SRT, so positioning and styling do not survive. The timings are what get fixed. The page warns you before you start.
The menu item is the fragile part
File Transformation works by patching the server's startup path at runtime, so a Jellyfin server or web client update can stop the Sync subtitles entry appearing. The plugin itself is unaffected: the Dashboard route keeps working, which is why it is the one we call primary.

Troubleshooting

There is no Sync subtitles entry in the Subtitles menu
Check that File Transformation is installed and Active in Dashboard > Plugins, and that you restarted Jellyfin after installing either plugin. If it was working and stopped after an upgrade, that is the known fragility above rather than a broken install: go to Dashboard > Plugins > Subtitle Sync instead, which does the same job from a picker.
Saving fails and says the folder is not writable
The plugin writes into the same folder as the video, so the account Jellyfin runs as needs write permission there. In Docker this is usually a library mounted read-only: change the volume to read-write and restart the container. Otherwise check the folder's ownership and permissions. Until then, use Download the .srt and copy the file into place yourself - it is byte for byte what the save would have written.
Saving says it needs an administrator
Analysing and saving are separate permissions, and only an administrator can write into a library. Your result is not lost: download it, or ask an administrator to run the save.
The result looks wrong, or a warning says confidence is low
Check the preview of the first corrected cue before saving, and compare the top two scores in the candidate table. If two ratios sit very close together the correct one may still have won, which is common on short items. If the answer is clearly off, try a different audio track (a commentary track will not match the dialogue), raise the maximum search offset if the drift is large, or nudge the offset by hand and check the preview again. A track that is a translation of a different cut will not correlate no matter what you set.
The saved file does not appear in the player
A re-scan is queued automatically after a save, but if it could not be queued the track appears at the next library scan instead. Refresh the item's metadata from its detail page to force it.