Skip to content
Swiftly Music Blog Discord GitHub
  1. Swiftly Player
  2. Blog

Jellyfin subtitles guide: formats, burn-in, forced, downloads (2026)

How Jellyfin 12 handles SRT, ASS, PGS and VobSub subtitles, when it burns them in, how forced flags work, and how to set up the OpenSubtitles plugin.

By Swiftly Player10 min read

Disclosure: we build Swiftly Player, one of the clients in this article. We point out where other apps do better.

A file that should direct play can still transcode because of its subtitles. This guide covers what Jellyfin does with each subtitle format, how to name external files, what the forced and default flags change, and how to download subtitles automatically. Everything was checked against Jellyfin 12.1 and jellyfin-web 12.1.

TL;DR

  • SRT and ASS/SSA are text. A client can draw them itself, so the server can usually send them without touching the video.
  • PGS and VobSub are pictures. If the client cannot draw them, the server has no text to convert. It burns them into the video, which means a full video transcode.
  • External files go next to the video: Film.en.srt, Film.en.forced.srt, Film.default.srt.
  • Forced marks a track for foreign-language parts only. Default marks the track the file author wants selected.
  • Subtitle mode per user has five options: Default, Smart, Only Forced, Always Play and None.
  • Automatic downloads use the official OpenSubtitles plugin, an OpenSubtitles.com account, and the "Subtitle Downloads" section of each library.

The four formats and why two of them cause transcoding

Jellyfin's codec support page lists the subtitle formats and what kind of data they hold:

FormatTypeTypical sourceMKVMP4
SubRip (SRT)TextDownloads, fansubsYesPartly
ASS/SSAFormatted textAnime releasesYesNo
PGS (PGSSUB)PictureBlu-rayYesNo
VobSub (SUB + IDX)PictureDVDYesYes

A text subtitle is a list of lines with start and end times. The client draws the text over the video. If the client does not accept the format, the server can often convert it to another text format.

A picture subtitle is a series of small bitmap images. There is no text inside to convert. The client either draws the images itself, or the server draws them into every video frame. The same docs page calls this burn-in "the most intensive method of transcoding", because the server decodes the video, paints the subtitle on top and encodes the video again.

How the server decides

Each client's device profile lists subtitle formats with a delivery method, defined in SubtitleDeliveryMethod.cs:

MethodWhat it means
EmbedThe subtitle stays inside the file or stream
ExternalThe subtitle is served as a separate file
HlsThe subtitle is served as a separate HLS stream
EncodeThe subtitle is burned into the video track
DropThe subtitle is dropped

The selection logic is in GetSubtitleProfile in StreamBuilder.cs. In short:

  1. The server looks for an Embed profile with the same format.
  2. It looks for an External or Hls profile with the same format.
  3. For text subtitles only, it looks for a profile it can convert to. The conversion check in MediaStream.cs returns false for anything that is not text. It also refuses to convert from or to ASS and SSA.
  4. If nothing matches, the result is Encode: burn-in.

So PGS and VobSub burn in on any client that does not list them. ASS does too, because the server will not turn ASS into SRT or WebVTT.

Settings that affect burn-in

In the web client, Settings > Subtitles has a Burn subtitles option with four choices: Auto, Only Image Formats (VobSub, PGS, SUB), All Complex Formats (ASS, SSA, VobSub, PGS, SUB, IDX, …) and All. Its help text says Auto burns "image based formats" and "certain ASS or SSA subtitles". The same page has Experimental PGS and VobSub subtitle rendering, which draws those formats in the browser instead of burning them in. Both labels come from jellyfin-web's en-us strings.

On the server, Dashboard > Playback > Transcoding has Allow subtitle extraction on the fly. Its help text says embedded subtitles can be extracted and sent to clients as plain text "in order to help prevent video transcoding".

Naming external subtitle files

External subtitles sit in the same folder as the video. The file name starts with the video's file name, followed by flags separated by dots. This is the example from Jellyfin's movie naming docs:

Movies
└── Film (1986)
    ├── Film.mkv
    ├── Film.default.srt
    ├── Film.default.en.forced.ass
    └── Film.en.sdh.srt

For episodes, the show naming docs use the full episode file name, for example Series Name A (2021) S01E01 Title.ja.ass.

The recognized flags are:

TypeFlag in file name
Defaultdefault
Forcedforced, foreign
Hearing impairedsdh, cc, hi

Three rules from the same page are easy to miss:

  • hi alone means Hindi. Film.en.hi.srt means English, hearing impaired.
  • Text that is neither a language nor a flag becomes the track title, unless the file already has a title.
  • Flags are ignored on containers with more than one stream.

After adding files, scan the library so Jellyfin picks them up.

Forced and default flags

These are two separate yes/no attributes on a subtitle track. For embedded tracks, Jellyfin reads them from the container's disposition data when it probes the file (ProbeResultNormalizer.cs). For external files, it reads them from the file name flags above.

  • Forced means the track only covers dialogue in another language, or signs and text that were not translated. The codec support page quotes that definition. The web client shows the flag as "Forced" in media info.
  • Default means the file's author wants this track picked.

How a client should act on these flags depends on the user's subtitle mode. Jellyfin's own logic is in MediaStreamSelector.cs. Only Forced picks a forced track in the preferred language. Always Play falls back to one when there is no full track. Smart uses one when the audio is already in a preferred language. Default mode selects forced tracks too. The default flag only decides the selection on its own in Default mode. In the other modes it only moves a track up the list among matching tracks.

Per-user subtitle settings

Each user sets this in the web client under Settings > Subtitles. The server stores it in the user configuration as SubtitleLanguagePreference and SubtitleMode (UserConfiguration.cs). The page has Preferred subtitle language and Subtitle mode.

The modes come from the SubtitlePlaybackMode enum. The behavior column follows the selection code in MediaStreamSelector.cs:

Label in the web clientAPI valueWhat the server selects
DefaultDefaultThe first track that is external, flagged default or flagged forced. External tracks rank first
SmartSmartIf the audio is not in a preferred language: a subtitle in the preferred language. Otherwise it acts like Only Forced
Only ForcedOnlyForcedA forced track in the preferred language, or a forced track with no language set
Always PlayAlwaysA non-forced track in the preferred language. If there is none, it acts like Only Forced
NoneNoneNothing. Subtitles can still be turned on during playback

One detail in Default mode surprises people. An external .srt without any flags is still picked, because Default mode accepts any external track.

This is what the server suggests. Each client decides whether to follow it.

Downloading subtitles automatically with OpenSubtitles

The subtitle downloader documented by Jellyfin is the Open Subtitles plugin. Its source is jellyfin/jellyfin-plugin-opensubtitles, maintained under the Jellyfin organization. Version 25 was released on 8 September 2026. It downloads from OpenSubtitles.com, so you need an account there. The plugin's settings page also links an import tool for old opensubtitles.org accounts.

  1. Create an account at opensubtitles.com if you do not have one.
  2. In Jellyfin, open Dashboard > Plugins.
  3. Filter the list by Available, open Open Subtitles and choose Install.
  4. Restart Jellyfin. The plugin page notes that a restart is needed after installing a plugin.
  5. Go back to Dashboard > Plugins, open Open Subtitles and choose Settings.
  6. Enter your OpenSubtitles.com Username and Password, then Save.
  7. Open Dashboard > Libraries, and choose Manage library for a movie or show library.
  8. In Subtitle Downloads, tick the Download languages you want.
  9. Under Subtitle downloaders, enable Open Subtitles. If you have more than one downloader, put them in order of priority.
  10. Choose the options below, then save the library.

The options in Subtitle Downloads are:

  • Only download subtitles that are a perfect match for video files. Fewer results, better timing.
  • Skip if the default audio track matches the download language.
  • Skip if the video already contains embedded subtitles.
  • Save subtitles into media folders. Stores the files next to the videos.

The downloads run in the scheduled task Download missing subtitles, found under Dashboard > Scheduled Tasks. In SubtitleScheduledTask.cs its default trigger is every 24 hours. Run it by hand once to test your login.

The plugin requests subtitles in SRT format (OpenSubtitleDownloader.cs), so they are text. The same file logs a message and returns no results for the automated task once your account's daily download limit is reached.

What different clients do with subtitles

The table only contains what each project states in its own docs or code. "Not listed" means we found no source, not that the feature is missing.

ClientASS/SSAPGSVobSubSource
Jellyfin Web 12.1Drawn in the browser with @jellyfin/libass-wasmBurned in, or drawn in the browser with "Experimental PGS and VobSub subtitle rendering"Same as PGSpackage.json, htmlVideoPlayer
Swiftfin, VLC playerSupportedEmbedded in MKV: supported. External file: server-side encodingSame as PGSplayers.md
Swiftfin, Native (AVPlayer)Not supportedServer-side encodingServer-side encodingplayers.md
Jellyfin for Android TV 0.19.10Client-side with "Enable Advanced SubStation Alpha subtitle client rendering", otherwise burned inEmbedded direct play with "Direct play PGS subtitles" (on by default), otherwise burned inEmbedded, burn-in as fallbackdeviceProfile.kt, UserPreferences.kt
Swiftly PlayerDrawn by VLC on the device, external .ass files tooDrawn by VLC on the device, no burn-inNot listedPlays through VLC with Direct Play

Swiftfin's docs add that subtitle track selection is not currently supported in the Native player.

FAQ

Why does my Blu-ray rip transcode only when subtitles are on?

The subtitle is most likely PGS, a picture format. If your client does not list PGS in its device profile, the server burns it into the video.

Can Jellyfin convert PGS to SRT?

Not during playback. The server's conversion check only accepts text subtitles, and PGS is a picture format. It either sends PGS as it is or burns it in.

How do I mark an external subtitle file as forced?

Add forced or foreign to the file name, for example Film.en.forced.srt. Then scan the library.

Why does a subtitle show up even though I never turned it on?

In Default mode, the server selects any external subtitle file, and any track flagged default or forced. Switch to Only Forced or None under Settings > Subtitles if you do not want that.

Does the OpenSubtitles plugin need an account?

Yes. The plugin downloads from OpenSubtitles.com and asks for your username and password on its settings page.

Summary

Text subtitles are cheap for the server, picture subtitles are not. Keep SRT or ASS files next to your Blu-ray and DVD rips where you can, name them after the video, and let the OpenSubtitles plugin fill the gaps. Then check which formats your clients draw themselves.

Related articles

All articles