Subtitle Edit 5 plugins are standalone executables that communicate with Subtitle Edit through JSON files. Subtitle Edit writes a request file, launches the plugin process, waits for it to exit, then reads a response file the plugin wrote. The plugin is free to show its own user interface while it runs.
This model is a clean break from the Subtitle Edit 4 plugin API (in-process WinForms DLLs), which cannot run on the cross-platform Avalonia build. SE4 plugins are not compatible with SE5 and must be ported.
Status: the JSON contract, the host-side runner/catalog, the Plugins menu, the plugin manager (Plugins → Manage plugins… — enable/disable/remove and open the plugins folder), and the online installer (Get plugins online… — download and update plugins from the index) are implemented. Installing, enabling, disabling, and removing plugins update the Plugins menu live (no restart needed). A startup update notification is still to come.
The Plugins menu is hidden by default; turn it on via Options → Settings → Appearance → Show Plugins menu. The menu must be enabled there to run or manage plugins from the main window.
request.json to a fresh temp folder.response.json to the path given in the request
(responseFilePath).0.ok it creates an undo point and replaces
the subtitle; on cancelled it does nothing; on error it shows the message.Cancelling from Subtitle Edit kills the plugin process. A non-zero exit code, a missing response file, or invalid JSON is reported to the user as an error and the subtitle is left unchanged.
Each plugin lives in its own folder under the Plugins directory in the Subtitle
Edit data folder (next to Settings.json; in portable installs that is the
Subtitle Edit program folder). Every plugin folder must contain a plugin.json
manifest:
Plugins/
MyPlugin/
plugin.json
MyPlugin.exe (Windows)
MyPlugin (Linux/macOS)
icon.png (optional)
Install a plugin either by copying its folder here manually, or via Plugins → Manage plugins… → Get plugins online…, which downloads it from the plugin index (see below).
Get plugins online… reads a JSON index and offers each listed plugin for
download. The index lives at the URL in PluginConstants.OnlineIndexUrl
(https://raw.githubusercontent.com/SubtitleEdit/plugins/main/se5-plugins.json):
{
"plugins": [
{
"name": "Uppercase Selected Lines",
"description": "Converts the text of the selected lines to UPPERCASE.",
"version": "1.0.0",
"author": "Jane Doe",
"url": "https://github.com/example/se-uppercase-plugin",
"date": "2026-05-01",
"minSeVersion": "5.0.0",
"downloads": {
"win-x64": "https://github.com/example/se-uppercase-plugin/releases/download/v1.0.0/Uppercase-win-x64.zip",
"win-arm64": "https://github.com/example/se-uppercase-plugin/releases/download/v1.0.0/Uppercase-win-arm64.zip",
"linux-x64": "https://github.com/example/se-uppercase-plugin/releases/download/v1.0.0/Uppercase-linux-x64.zip",
"linux-arm64": "https://github.com/example/se-uppercase-plugin/releases/download/v1.0.0/Uppercase-linux-arm64.zip",
"osx-x64": "https://github.com/example/se-uppercase-plugin/releases/download/v1.0.0/Uppercase-osx-x64.zip",
"osx-arm64": "https://github.com/example/se-uppercase-plugin/releases/download/v1.0.0/Uppercase-osx-arm64.zip"
}
}
]
}
downloads is a map of platform key → zip URL. Each key has the form
{os}-{arch} where os is win, linux, or osx and arch is x64,
arm64, x86, or arm. Keys are matched case-insensitively..zip containing the plugin’s folder (with
plugin.json inside). Subtitle Edit unpacks it into the Plugins directory.name.version with the
installed manifest version and shows Update available when the index is newer.The manifest lets Subtitle Edit list the plugin in its menus without launching it.
{
"apiVersion": 1,
"name": "Uppercase Selected Lines",
"description": "Converts the text of the selected lines to UPPERCASE.",
"version": "1.0.0",
"author": "Jane Doe",
"url": "https://github.com/example/se-uppercase-plugin",
"menu": "Tools",
"shortcut": "Control+Shift+U",
"minSeVersion": "5.0.0",
"icon": "icon.png",
"executables": {
"windows": "MyPlugin.exe",
"linux": "MyPlugin",
"macos": "MyPlugin"
}
}
| Field | Required | Notes |
|---|---|---|
apiVersion |
yes | JSON contract version. Currently 1. |
name |
yes | Menu text. |
description |
no | Shown in the plugin manager. |
version |
no | The plugin’s own version. |
author |
no | |
url |
no | Project/home page. |
menu |
no | Intended menu group: Tools (default), File, Sync, Translate, SpellCheck, or Assa. Currently every plugin is listed under a single top-level Plugins menu; menu-based routing is planned. |
shortcut |
no | Suggested keyboard shortcut, e.g. Control+Shift+U (modifiers Control/Ctrl, Shift, Alt, Win/Cmd plus one key). Applied as the default binding the first time the plugin is seen, unless the chord is already taken. Users can change it in Options > Shortcuts > Plugins. |
minSeVersion |
no | Minimum supported Subtitle Edit version. |
icon |
no | Icon file name, relative to the plugin folder. |
executables |
yes* | Native executable file names per OS, relative to the plugin folder. |
runtime |
no | Set to "dotnet" to launch via the shared .NET runtime instead of a native exe. |
entry |
no | Entry .dll (relative to the plugin folder), used with runtime: "dotnet". |
* Provide either executables or runtime + entry. A dotnet plugin is
launched as dotnet <entry> <requestFilePath>.
{
"apiVersion": 1,
"requestType": "run",
"responseFilePath": "C:\\Users\\...\\Temp\\SubtitleEditPlugins\\<id>\\response.json",
"tempDirectory": "C:\\Users\\...\\Temp\\SubtitleEditPlugins\\<id>",
"pluginDataDirectory": "C:\\Users\\...\\Subtitle Edit\\Plugins\\Data\\Uppercase Selected Lines",
"subtitle": {
"format": "Advanced Sub Station Alpha",
"fileName": "C:\\videos\\episode01.ass",
"native": "[Script Info]\n...full subtitle in its original format...",
"subRip": "1\n00:00:01,000 --> 00:00:03,000\nHello world\n\n...",
"header": "[Script Info]\n...",
"paragraphs": [
{ "startMs": 1000, "endMs": 3000, "text": "Hello world", "style": "Default",
"actor": "", "marginV": "0", "layer": 0, "isComment": false }
]
},
"subtitleEncoding": "UTF-8 with BOM",
"selectedIndices": [3, 4, 5],
"videoFileName": "C:\\videos\\episode01.mkv",
"frameRate": 23.976,
"videoDurationSeconds": 1432.5,
"videoPositionSeconds": 142.0,
"videoWidth": 1920,
"videoHeight": 1080,
"uiLanguage": "English",
"theme": "Dark",
"themeColors": {
"isDark": true,
"backgroundColor": "#FF212121",
"foregroundColor": "#FFDCDCDC",
"accentColor": "#631E90FF",
"backgroundColorLighter": "#FF262626",
"backgroundColorHeader": "#FF303030",
"bookmarkColor": "#FFFFD700"
},
"seVersion": "5.0.0",
"rules": {
"subtitleMinimumDisplayMilliseconds": 1000,
"subtitleMaximumDisplayMilliseconds": 8000,
"subtitleMaximumCharactersPerSeconds": 25,
"subtitleOptimalCharactersPerSeconds": 15,
"subtitleLineMaximumLength": 43,
"maxNumberOfLines": 2,
"minimumMillisecondsBetweenLines": 24,
"minimumFramesBetweenLines": 2,
"useFrameMode": false,
"ebuStlTeletextUseBox": true,
"ebuStlTeletextUseDoubleHeight": true,
"videoOffsetMs": 0
},
"settings": { "lastUsedOption": true },
"settingsVersion": 2
}
subtitle.native is the subtitle serialized in its original format;
subtitle.subRip is the same content as SubRip (.srt) — use whichever is
easier for your plugin.subtitle.header is the subtitle’s in-memory header: the EBU STL GSI block (1024
characters, single-byte code page), the ASSA [Script Info]/styles sections, etc.subtitle.paragraphs are the lines exactly as Subtitle Edit holds them, including
fields SubRip cannot carry (style, actor, layer, marginL/R/V, region,
effect, extra, isComment, forced, newSection, bookmark). Times are in
milliseconds. For EBU STL / DVB Teletext, marginV is the teletext row (vertical
position) the line starts on, and colors/box/alignment are in the text as
<font color="...">, <box> and {\anN} tags.subtitle.native is empty —
use header + paragraphs.rules is a read-only snapshot of the user’s subtitle rules (durations, CPS, line
length, minimum gap, frame mode, EBU teletext box/double height, video offset).
Paragraph times are video-relative; the grid and saved file show time +
videoOffsetMs. Changes to rules are not read back — keep your own options in
settings. Subtitle Edit does not hand out the path to its Settings.json: the file
is rewritten by Subtitle Edit on exit and its layout is internal.subtitleEncoding is the display name of the encoding the file was loaded with
(e.g. UTF-8 with BOM). Empty when unknown / on older SE versions.pluginDataDirectory is a persistent, writable folder private to your plugin
(under Plugins/Data/<plugin name>). Unlike tempDirectory it survives between
runs, and unlike your install folder it is not wiped when the plugin is
updated — use it for caches, downloaded models, dictionaries, etc. Subtitle Edit
creates it before the run. May be empty on older SE versions.selectedIndices are zero-based line indices selected in the grid (empty if none).settings is whatever JSON your plugin returned in its previous response;
it is null on first run. Use it to persist plugin-private settings.settingsVersion is the schema version your plugin attached to settings in
its previous response, handed back unchanged. Use it to migrate or reset old
settings when you change your own schema. Null on first run, and on older SE
versions that don’t track it.videoDurationSeconds, videoPositionSeconds, videoWidth, videoHeight
describe the loaded video; videoPositionSeconds is the current playhead
position. Null when no video is loaded (or on older SE versions). Saves you from
re-opening the video file just to read these.theme and uiLanguage let your plugin’s own UI match Subtitle Edit.themeColors carries the active theme’s colors as #AARRGGBB hex strings so
your plugin’s own UI can match Subtitle Edit. May be omitted in older SE
versions — fall back to your platform’s defaults when it is missing.Write this file to responseFilePath before exiting with code 0.
{
"apiVersion": 1,
"status": "ok",
"message": "Converted 3 lines to uppercase.",
"subtitle": {
"format": "SubRip",
"native": "1\n00:00:01,000 --> 00:00:03,000\nHELLO WORLD\n\n..."
},
"settings": { "lastUsedOption": true },
"settingsVersion": 2,
"undoDescription": "Uppercase Selected Lines 1.0.0"
}
status |
Effect |
|---|---|
ok |
Subtitle Edit re-parses subtitle.native (as subtitle.format), makes an undo point, and replaces the current subtitle. |
cancelled |
Nothing changes. Use this when the user closed your plugin’s window. |
error |
message is shown to the user; the subtitle is left unchanged. |
status: "ok" set either subtitle.format + subtitle.native, or
subtitle.paragraphs. Return the format you find most convenient — SubRip is
always safe for text-based subtitles.subtitle.paragraphs is non-empty it wins over native: the lines replace the
current ones as-is (no re-parse) and the current format is kept — the way to change a
binary format such as EBU STL without losing its rows and header. Set
subtitle.header to replace the header too; omit it (or null) to keep it.subtitle.native cannot be parsed, Subtitle Edit aborts with an error and
makes no changes.settings is stored by Subtitle Edit and handed back in the next request.settingsVersion is stored alongside settings and handed back via
request.settingsVersion on the next run. Bump it whenever you change the
shape of settings so you can detect (and migrate or discard) stale data
written by an older build of your plugin. Optional; if you don’t write it,
SE clears any previously-stored version for this plugin.undoDescription is optional; it labels the undo-history entry.tempDirectory) is yours to use for scratch files and is deleted after the run.apiVersion at 1. If a future version introduces breaking changes the
number will be bumped and Subtitle Edit will tell the user to update the plugin.