Synchronize an SRT in any model-supported language against a correctly timed reference SRT, in the same or a different language, or an embedded SubRip track. Processing is local; the first analysis downloads the multilingual model. Original source and reference subtitles are kept unchanged.
- Windows x64 portable package
- Linux x64 portable package
- Windows/Linux portable SHA-256 sums
- GPU Docker image SHA-256
- GPU Docker parts SHA-256 sums
The v1.1.2 release does not include a prebuilt CPU-only Docker image; build
subtitle-sync:1.1.2-cpu locally as described in the Docker section, or use the
full GPU image, which also runs on CPU.
The full GPU Docker image is split into two release assets because of its size. Download part 1 and part 2, then verify the parts and reassemble the image:
sha256sum --check SubtitleSync-1.1.2-docker-amd64.parts.sha256
cat SubtitleSync-1.1.2-docker-amd64.tar.gz.part01 \
SubtitleSync-1.1.2-docker-amd64.tar.gz.part02 \
> SubtitleSync-1.1.2-docker-amd64.tar.gz
sha256sum --check SubtitleSync-1.1.2-docker-amd64.tar.gz.sha256
gzip -dc SubtitleSync-1.1.2-docker-amd64.tar.gz | docker loadThe desktop window/taskbar name is SubSync, using the app icon on Windows and
Linux. On Linux, desktop startup registers a per-user SubSync.desktop entry in
$XDG_DATA_HOME/applications (normally ~/.local/share/applications) so the dock
can associate the name and icon with the window. Restart the app after updating;
an old pinned Tk launcher may need to be removed and SubSync pinned again.
The top bar shows the app name on the left. On the right are Sync to audio, Speech to text, Analyze and synchronize, Cancel, the gear menu and the version badge, in that order. Sync to audio uses the same secondary colors as Speech to text in every theme. The secondary speech button and primary synchronization button follow the reference app's light/dark banner colors. The current Subtitle Sync version is v1.1.2. The gear dropdown is right-aligned with its button; its submenus open to the left when the window is at the right screen edge, including when maximized. Cancel sits between Analyze and synchronize and the gear, with the same secondary theme colors as Speech to text. It is enabled during synchronization. Cancellation waits for the current inference step, then stops without publishing partial output or replacing existing subtitles. Private ONNX GPU workers are terminated on cancellation; CPU/Vulkan analysis checks between inference steps. The header stays visible while the form can scroll on smaller displays; the initial window height is limited to the available screen height.
The gear offers the same menu categories as Bulk Rename Utility:
- Language: English, German, Greek, Estonian, French, Hindi, Hungarian, Italian, Japanese, Korean, Dutch, Polish, Portuguese (Brazil), Portuguese, Romanian, Russian, Turkish, Simplified Chinese and Traditional Chinese. Switching languages immediately translates menus, main controls, workflow instructions and common controls in the speech, cue-pairing and GPU windows. Paths, subtitle dialogue, backend/model identifiers and spoken-language selections are never translated or changed. Detailed technical guidance, progress logs and diagnostic messages can still be in English.
- Theme: Follow the system, Light or Dark. Popups, text areas and menus follow the selected theme. System mode checks Windows' application theme preference or the GNOME-compatible desktop color-scheme preference on Linux; systems without an available preference use the native light appearance. System mode refreshes when the main window regains focus.
- Help → About Subtitle Sync: displays this app's name and version.
Language, theme, processing-device choices and Whisper.cpp choices are saved automatically under
%APPDATA%\SubtitleSync\settings.json on Windows or
$XDG_CONFIG_HOME/SubtitleSync/settings.json on Linux (default:
~/.config/SubtitleSync/settings.json). Preference read/write failures are shown
explicitly. Interface changes preserve selected files, devices and manual sync
points.
The main processing device, inference backend, adapter ID, report checkbox and Hearing-impaired
filter level are restored after
restart. The speech window separately remembers the spoken language, model,
device, backend, adapter ID and audio stream index, including across application
restarts. Files and generated-output destinations are not saved. Restored hardware
choices do not guarantee the same GPU is available: recheck adapter IDs if your
hardware or driver configuration changes.
The Report and extra box, to the right of Processing device, has a Generate sync
report checkbox (off by default). Enable it before synchronizing or opening
Sync to audio... to save a UTF-8 text report beside the resulting SRT.
For example, Episode.pt.synced.srt or Episode.pt.audio-synced.srt produces
Episode.pt.sync.txt; the filename's language suffix is preserved, not inferred.
Repeating a synchronization replaces that output's previous report.
The report records the input/output paths, method and settings, completion time, processing log/warnings and output cue timings. Reference-based synchronization also includes matching diagnostics and timing anchors. When the original external SRT is available, each cue includes its original timings and start/end shifts. For embedded audio-sync subtitles, original cue timings are unavailable to the report writer; the engine log records the available correction details instead. Matching coverage is not a timing-accuracy score: review the result. Reports contain local file paths and diagnostic logs; review them before sharing. If writing a report fails, the app explicitly reports the problem and keeps the successfully saved SRT. Disabling the checkbox does not delete an older report.
The same box also offers Sync to audio after analysis (off by default). When checked, a valid video file is mandatory, even when a reference SRT is selected. After reference-based alignment, the app automatically aligns that temporary result to the video's first audio track, using CPU speech-activity matching with drift and section correction. For a different audio track, leave this option off and use the separate Sync to audio... window. Only the final combined result is saved to the selected output filename. Failure or cancellation in either stage leaves any existing output unchanged. The optional report covers both stages and the final cue timings. Out-of-order download/translation credits are excluded from dialogue matching without removing them or reordering the original subtitle file. Out-of-order dialogue still requires correction.
Final offset (ms) applies a uniform shift to every cue after Analyze and
synchronize, including after the optional audio pass. Enter a whole number:
500 or +500 delays subtitles by half a second; -500 advances them.
The default is 0 (no shift). This session-only setting also applies to all files
in SRT folder mode, but not to the separate audio-only workflows. Cue durations,
text and formatting are preserved. An advance that would create negative
timestamps is rejected without replacing the output. Reports include the offset
and final timings.
Enable Automatically select initial sync point in the Initial sync point box, then click Analyze and synchronize. You do not need to select matching cues first. This option is off by default and remembered across restarts. It uses the selected multilingual embedding backend to find several distinct, consistent dialogue matches across the subtitles, excluding credits and hearing-impaired captions. It does not assume the first cues correspond. The chosen source/reference cue numbers and reference time appear after success and are included in the optional report.
An external reference SRT is sufficient; video is only mandatory when using embedded reference subtitles or the separate automatic audio-pass option. With embedded references, the app still asks which subtitle track to use if there is more than one. Uncertain, ambiguous or insufficient matches stop the operation without replacing the output. Uncheck the option and select matching cues manually to proceed. Automatic selection is not a guarantee of accuracy: always review the result, especially across different edits of the video.
Analyze and synchronize automatically checks for a uniform timing-rate difference before detailed matching, with either a manually selected initial pair or automatic initial-point selection. This also applies to each pair in SRT folder mode and uses the same embeddings on all supported processing backends. No FPS selection or additional model download is required.
The preliminary search compares distinctive dialogue across the whole timeline,
without the local matching window. It estimates
reference time = source time * scale + offset, rejecting ambiguous or repeated
dialogue. The fit uses alternating matches and is checked against the held-out
matches, not just the points used to fit it. Acceptance requires at least ten
global matches, at least 80% agreement within 1.5 seconds, and supporting dialogue
spread across at least 60% of the source timeline and at least one minute.
Consecutive, same-direction timing deviations exceeding 4.5 seconds are treated
as evidence of different edits rather than a uniform rate change. Smaller
cue-boundary deviations do not by themselves trigger this section-cut veto;
the fit must still pass the 80% agreement and independent validation checks.
Tiny rate differences accumulating less than two
seconds across the source timeline are not applied.
An accepted scale guides the existing local dialogue matching so accumulated drift does not push the correct dialogue outside the search window. Manual sync pairs remain exact. Local corrections are still used for cue-level differences. Both cue starts and durations are retimed; the scale also extends beyond the first and last matching anchors. Source text, cue order and original files are unchanged, and newly introduced overlaps can still shorten a cue. When no reliable scale is found, the processing log explicitly says that local offset-based alignment is retained; uncertain automatic initial-point selection still fails rather than guessing.
The log and optional report record the accepted scale, supporting match count and held-out timing error. Reports also include candidate count and support percentage; these are evidence for the fit, not a guarantee of timing accuracy. SRT timestamps do not identify an original FPS, so the app reports a measured timing scale rather than claiming a specific FPS conversion. Speed differences can be corrected in either direction. Different video edits, malformed timestamps and missing dialogue are not reliably repaired by a global scale: always review the beginning, middle and end. Optional audio synchronization follows reference retiming; Final offset (ms) is applied last.
The options row contains SRT folder mode, Sync to audio folder mode, Initial sync point, and Hearing-impaired filter. Click Browse... to the right of the path in SRT folder mode, choose a folder, then choose the source and reference languages detected from its SRT filenames. The box shows the editable folder path and source/reference language dropdowns, whose choices come from that folder's filename suffixes. Change either language directly in the box. Delete the path to return to single-file mode. You can also type a different folder path and press Enter or leave the field to refresh the available languages. Only files directly inside the selected folder are scanned.
Language suffixes may be names or codes, for example Episode.Portuguese.srt
and Episode.en.srt. Names such as Portuguese/English are normalized to pt/en;
regional suffixes such as pt-pt and pt-br remain separate choices.
Subtitles are paired by the same filename/title before the language suffix.
Generated .synced.srt and .audio-synced.srt files are not used as new inputs.
Unrecognized suffixes are not guessed.
Folder mode uses the Automatically select initial sync point setting.
Leave it enabled for unattended processing. When disabled, a cue-pair popup opens
before analysis of each eligible source/reference pair. Select the same dialogue
moment in both files and confirm to process that file, then repeat for the next
pair. The popup title identifies both files. Cancelling or closing the popup
cancels the remaining batch; completed outputs are retained. Files with existing
outputs or invalid/missing pairs are skipped or reported without a popup.
You can toggle automatic initial-point selection while a folder batch is running.
Each file uses the setting at the start of its processing; changing it does not
interrupt the current analysis or close a cue-pair popup already open.
Click Analyze and synchronize to process every source file in the selected
language. Each output is saved next to its source as Episode.pt.synced.srt.
Existing outputs are skipped, not overwritten. Missing references, ambiguous
pairs and uncertain initial matches are recorded as failures, and processing
continues with the next file. Cancel stops further processing and retains outputs
already completed. The final scrollable report has one row per source file,
including success, failure, skipped/cancelled status, output path and details.
Optional per-file text reports follow the Generate sync report setting. If Sync to audio after analysis is enabled, each pair must have exactly one video with the same title in that folder; the first audio track is used. On mounted SMB/GVfs shares that do not support hard links, saving uses exclusive file creation instead. Existing files remain protected; the new output may be visible while it is being copied. Failed copies are removed.
Sync to audio folder mode, immediately left of Initial sync point, has
its own folder path, Browse button and Sync button. It does not require
reference subtitles, language choices, an initial anchor or the embedding model.
Each SRT is matched to a video with the same title (ignoring a recognized subtitle
language suffix). All available subtitle languages are processed, as are SRTs
without a language suffix. An existing .synced.srt can be processed too.
For example, Episode.pt.srt and Episode.en.srt both match Episode.mkv.
An exact filename-stem video match takes precedence over the language-stripped
title. More than one matching video is treated as ambiguous, not guessed.
Each output is saved beside its source with .audio-synced.srt in place of
.srt; originals are unchanged. Generated .audio-synced.srt files are excluded
from new inputs, and existing outputs are skipped. Processing uses the first
audio track, CPU speech-activity matching, drift correction and section correction.
Per-file failures do not stop the remaining files; Cancel stops further work.
The final table includes successes, failures, skipped and cancelled files.
Generate sync report also applies to this mode. The top-bar Analyze and
synchronize button continues to use the SRT folder; use the button inside the
audio folder box to run audio-only processing.
Click the Relaxed–Strict slider to focus it, then use Left/Down or Right/Up to decrease/increase the hearing-impaired filter by one point (bounded to 0–100).
Use Sync to audio... in the top bar, immediately left of Speech to text, when a video's subtitle track (or an external SRT) is early, late, or gradually drifts away from its audio. This is separate from Speech to text and does not need a correctly timed reference SRT or manually selected cue pairs.
- Choose the video/audio file, then click Inspect tracks.
- Select the correct audio track by its stream index and language.
- Select Embedded subtitles and a text subtitle track, or choose External SRT and browse to the existing SRT.
- Leave Correct gradual drift / frame-rate mismatch enabled for timing-rate differences. Leave Correct section-by-section timing shifts enabled when the delay changes during the video. Disable both for a constant offset only.
- Choose a new
.srtoutput path and click Synchronize to audio.
After a successful save, the result is automatically selected in the main window's Reference SRT box. Errors and cancellation leave that selection unchanged. The audio-sync window includes an explicit Close button, including in Docker's browser desktop. During work it offers cancellation; close it after cancellation finishes. Like Speech to text, it remains a normal resizable window with its own window-manager decorations, rather than inheriting the main window's fullscreen rule.
The CPU-only ffsubsync engine compares speech activity with subtitle timing. It does not recognize or translate words, download speech models, or use the GPU selected for semantic subtitle matching. FFmpeg reads the selected tracks, including the audio track's position on the video timeline. The offset search covers up to one minute in either direction with section correction enabled. Disable section correction for a global offset search up to ten minutes.
External SRT wording, cue numbers, formatting, line breaks and encoding are preserved; only timestamps change. Embedded text tracks are extracted to SRT; format-specific styling from formats such as ASS may be lost in conversion. Image subtitles such as PGS/DVD require OCR first and are not selectable. The video and source subtitles are never overwritten. Replacing an existing output requires confirmation; cancellation or failure does not publish partial output. The result is a standalone SRT; the video is not remuxed or re-encoded.
Section correction uses ffsubsync's penalized split alignment to find multiple timing offsets instead of applying one offset to the whole video. A split must improve speech overlap enough to avoid chasing every noisy cue. When the engine finds section changes, the log identifies the cue numbers to review. Speech activity alone may place a transition a few cues early or late; check several cues on both sides. Section correction is slower and uses more memory than global correction. Its bounded one-minute search also reduces the risk of matching unrelated scenes far away in the video.
This corrects global offset, timing-rate problems and changing section delays, but cannot recover missing dialogue or guarantee arbitrary per-line alignment across different edits. Detected low-quality matches or lost/reordered cues are reported as errors instead of being saved. A successful match is not a guarantee of accuracy: review the beginning, middle and end in your player. Same-language speech and subtitles from the same video cut work best.
Desktop launchers install ffsubsync automatically if it is missing. If a
previously selected Library updates environment lacks it, install updated
libraries or restore the base environment and restart with the desktop launcher.
On Linux, browsing input/output files uses a resizable, maximizable file picker with a details list. Click Name, Size or Modified to sort; click again to reverse the order. Folders stay first. Use Up/Home or type a folder path and press Enter to navigate. This picker also works in the Docker browser desktop. Windows retains its native file dialogs.
The main file controls use two rows: Video and Reference SRT on the first
row; Source SRT and Save synchronized SRT
are side by side on the second row. Source and reference are language-independent.
Selecting a source SRT also searches its folder for the same filename with a
different language suffix (for example, Episode.pt-pt.srt and Episode.en.srt,
or Episode.Portuguese.srt and Episode.English.srt). A single match is offered
in a themed chooser; multiple matches appear in its dropdown. Selecting a reference
first offers matching source subtitles in the same way. Accepting a source also
sets its default output filename. Cancel keeps the current selection.
Matching names do not prove correct timing: choose a reference that
is synchronized to the video. Only the final language suffix is varied; unrelated
release names and generated .synced outputs are not suggested automatically.
The About dialog includes the credit developed by SMAP. Below the processing controls, Initial sync point is on the left and Hearing-impaired filter is on the right.
The Linux picker has a Locations / mounted shares selector listing mounted
SMB/CIFS, NFS and desktop GVfs shares, along with /mnt and /media. Open the
selector or click Refresh after mounting a share. You can also type any
mounted path in the location bar. Mount and authenticate shares using the OS or
desktop first; the app does not connect directly to smb:// or nfs:// URLs.
Windows native dialogs support mapped drive letters and UNC paths such as
\\server\share\folder. Network permissions and availability still apply.
Extract the entire release archive into a writable folder, then run
Start_Subtitle_Sync.bat on Windows or
bash Start_Subtitle_Sync.sh from a terminal on Linux.
Windows requires PowerShell and tar.exe (included with current Windows 10/11).
Linux downloads require curl, tar and sha256sum.
These are first-launch-download portable releases, not offline bundles. The archives contain only app source, requirements, documentation and the platform launcher. They exclude test videos/subtitles, tests, Python environments, downloaded packages, models, GPU runtimes, caches and personal preferences.
- A compatible installed Python (3.10+, Tkinter, venv and ensurepip) is reused.
If none is available, the launcher asks before downloading private
CPython 3.12.15, pinned to python-build-standalone release 20261013 and
SHA-256-verified. This distribution includes Tkinter and pip, unlike Windows'
minimal embeddable Python package. Downloads are about 21 MB on Windows and
33 MB on Linux, extracted under
.pythoninside the app folder. - Python dependencies are installed on first launch into
.venvon Windows or.venv-linuxon Linux. Later launches check and reuse them. First setup needs internet and sufficient disk space; inference models and optional GPU runtimes are downloaded separately when needed. - The release's
PORTABLEmarker makes the launchers keep app preferences in.config/settings.jsonand the matching model cache in.model-cache, inside the extracted app folder. Speech models and GPU runtimes already live there. Existing user settings from a source installation are not copied into releases. Dependency tools can still use their normal OS download caches. - Linux still needs a graphical desktop and compatible system libraries (including X11/OpenGL). The launcher gives package guidance if needed; it never runs sudo or installs OS packages/drivers. Private Python targets glibc Linux x86_64, not Alpine/musl. Windows private Python targets x64, not ARM64.
- Extract into the intended final location before first launch. Do not copy a generated virtual environment between machines or operating systems. To move to a new computer/location, extract a fresh archive and copy only desired data, downloaded models and preferences; let its launcher recreate dependencies.
- Keep the launcher terminal open while the app runs so startup errors remain visible. No system-wide Python installation or PATH changes are performed.
Build clean archives with python3 build_portable.py. Its explicit allowlist
produces Windows ZIP and Linux tar.gz archives plus SHA256SUMS.txt in releases.
Unraid/Docker setup is described below; it is separate from these portable archives.
- Windows: run
Start_Subtitle_Sync.bat. A suitable installed Python is reused; otherwise you can approve a private runtime download. - Linux: run
bash Start_Subtitle_Sync.sh. System Python 3.10+, venv and Tkinter are preferred; a private x64 runtime download is offered if needed. On Debian/Ubuntu, system prerequisites includepython3-venv python3-tk.
The launchers use separate .venv (Windows) and .venv-linux (Linux) environments.
Both platforms have the same UI, including the hearing-impaired filter and initial
sync point. Embedded references use MKVToolNix for Matroska when available
(mkvmerge and mkvextract on PATH; Windows installation paths are also checked).
Otherwise the app uses FFmpeg, including its bundled executable, to inspect and
extract supported text subtitle tracks. Neither a separate FFmpeg installation
nor MKVToolNix is required.
Desktop startup checks for newer fastembed, imageio-ffmpeg and
onnxruntime versions and installed app-managed runtimes/models at most once
every 24 hours, in the background. Checks also repeat daily if the app stays open.
Initial
setup still installs the tested versions from requirements.txt. A successful
check with available updates opens gear -> Help -> Library updates; no
packages are installed without your confirmation. You can also open this window
to check manually, see running versions, install updates or restore the previous
library environment.
Install updates creates a separate Python environment in
.library-updates/linux or .library-updates/windows inside the app folder.
It installs the latest stable releases compatible with your Python, including
their required dependencies, then checks package consistency and app imports.
If any step fails, the current selection stays unchanged and the error is shown.
An import check cannot guarantee a newer library works correctly for every
subtitle or device; keep the previous environment until you have tested it.
Package checks/installation use pip's configured package index (PyPI by default).
Network failures are shown, not reported as "up to date"; automatic checks are
retried on a subsequent launch after 24 hours, or manually at any time.
After installation or restoration, close the app and reopen it using the desktop launcher to use the selected libraries. The working environment is not modified while the app is running. Restore previous versions switches back to the exact previous environment without downloading packages. The original environment is kept, and older managed environments are retained on disk. App preferences remain shared; updating desktop libraries does not change the separately selected models or private GPU runtimes. An update operation requires a writable app folder and may use substantial disk space.
If an update prevents the GUI from opening, restore from a terminal:
# Linux
bash Start_Subtitle_Sync.sh --restore-libraries:: Windows
Start_Subtitle_Sync.bat --restore-librariesThe recovery option switches to the previous library environment and starts the app.
Python itself and system OS packages, drivers, CUDA and ROCm installations are
never updated by this feature. SUBTITLE_SYNC_DESKTOP_UPDATES=0
disables the desktop check and update controls. Directly running the GUI with
an arbitrary Python does not activate a selected environment; use the launcher.
The Installed runtimes and models selector in the same update window checks only components already installed for this platform:
- Private NVIDIA, DirectML and OpenVINO environments, including their GPU ONNX distribution, fastembed and installed NVIDIA CUDA/cuDNN Python packages.
- The llama.cpp Vulkan embedding executable and its multilingual GGUF model.
- Installed Whisper.cpp CPU, Vulkan and CUDA executables.
- Downloaded Whisper model variants and the cached multilingual ONNX embedding model (Hugging Face snapshots, including older cache-name casing, or the legacy fastembed model folder).
Select a component, then Install updates or Restore previous versions.
Each component has its own active/previous selection. Updates use separate
directories under .component-updates/linux or .component-updates/windows;
original files are retained. These selections are used by GPU workers, speech
transcription and embedding inference, not just shown in the update window.
Restart after a change before continuing work. Model updates retain the same
model family and variant; they do not switch a tiny Whisper model to a larger
one or replace the alignment algorithm.
Runtime checks query the existing upstream repository and match the installed
OS/backend asset. Official llama.cpp and whisper.cpp numbered b... binary
builds are checked on their existing build channel even when GitHub marks them
as prereleases; unrelated prereleases are not selected. A newer source-only
release is not a usable binary update. Downloads require upstream SHA-256 asset
digests; models use immutable Hugging Face revisions and file checksums.
Archives must contain the expected executable, which is tested with --version.
Embedding snapshots are also tested for finite 384-dimensional CPU embeddings.
Whisper/GGUF models are checksum- and format-checked. GPU environments must
expose the required provider; this does not replace testing inference with your
actual GPU, driver and adapter ID.
If no matching downloadable asset or checksum is available, or an upstream check fails, the window reports it and leaves the working version alone. AMD Linux MIGraphX wheels depend on the installed OS/ROCm stack: newer packages are reported when discoverable, but automatic replacement is not offered. Use the existing GPU setup guidance for those custom wheels. No system installers are run.
If a component change prevents normal startup, restore all components that have a previous version without needing the GUI:
bash Start_Subtitle_Sync.sh --restore-componentsStart_Subtitle_Sync.bat --restore-componentsThis recovery operation does not switch desktop library environments; use
--restore-libraries separately if needed. Older versions remain on disk; no
runtime, model or environment history is deleted automatically.
Docker/Unraid is excluded: there are no library update checks, notifications,
or install/restore controls in containers. Bundled library updates require a
new image, built with revised dependency pins. This feature does not change
requirements.txt or build/release anything automatically.
For CPU users, build subtitle-sync:1.1.2-cpu; for GPU users build the full
subtitle-sync:1.1.2 image. The earlier slim variant is discontinued.
CPU-only includes Python, Tkinter/browser desktop, CPU ONNX Runtime, FFmpeg and
MKVToolNix. It excludes the optional Vulkan driver packages and NVIDIA CUDA/cuDNN
libraries. Only CPU processing is offered in matching and speech-to-text; GPU
runtime installation is disabled. CPU models and the CPU Whisper executable
still download on demand into persistent /config.
docker build --platform linux/amd64 --build-arg WITH_CUDA=0 --build-arg CPU_ONLY=1 -t subtitle-sync:1.1.2-cpu .
docker save subtitle-sync:1.1.2-cpu | gzip > SubtitleSync-1.1.2-docker-amd64-cpu.tar.gz
gzip -dc SubtitleSync-1.1.2-docker-amd64-cpu.tar.gz | docker loadUse unraid/subtitle-sync-cpu.xml, or change an
existing container's Repository to subtitle-sync:1.1.2-cpu after loading it
and Apply. Remove GPU Device entries and --gpus=all / --device=/dev/dri:/dev/dri
from Extra Parameters; retain --shm-size=1g. Keep existing /config and /data
mappings. Saved GPU selections are ignored in this variant and CPU is selected.
Do not run two instances against the same appdata directory.
Compose: docker compose -f compose.yml -f docker/compose.cpu.yml up -d --build.
No NVIDIA plugin or GPU passthrough is needed.
The image uses the same pinned jlesage/baseimage-gui browser-desktop base as
the Subtitle Edit Unraid reference.
Open port 5800 in a browser to use the normal Tkinter app; no desktop Python,
X server or installation script is needed on the client or at container startup.
Python, Tkinter, FFmpeg, MKVToolNix, Python dependencies, Mesa Vulkan drivers and
CUDA 12/cuDNN userspace libraries are installed when the image is built.
Models and app-specific Vulkan/Whisper executables are still downloaded and
checksum-verified on demand, and persist in /config.
The all-vendor image is substantially larger than the portable archives because
it includes NVIDIA userspace libraries even on CPU/AMD/Intel hosts.
The current all-vendor build is about 2.4 GB compressed and 7 GB in Docker image
storage (reported sizes vary with Docker's layer/compression accounting).
NVIDIA libraries alone occupy roughly 2.8 GB uncompressed, plus about 470 MB for
ONNX Runtime. The desktop, Python, media tools and Mesa libraries add to that.
Downloaded inference models use additional space in /config, not in the image.
MKVToolNix (mkvmerge and mkvextract) is included. The container uses the
built-in C.UTF-8 locale, including for child media tools. Older images inherited
an unavailable en_US.UTF-8 locale from the desktop base; that could crash
MKVToolNix with locale::facet::_S_create_c_locale name not valid.
Rebuild/load the corrected image and recreate the container with the existing
/config mount. As an immediate workaround on an older image, add an Unraid
Variable named LC_ALL with value C.UTF-8, then Apply to recreate the container.
Two images are available:
| Image | NVIDIA libraries | Best for |
|---|---|---|
subtitle-sync:1.1.2 (full GPU) |
Included | CPU, NVIDIA CUDA and AMD/Intel Vulkan GPU/APU users |
subtitle-sync:1.1.2-cpu |
Not included; GPU disabled | CPU-only users wanting the smaller build |
Both include the resizable/maximizable sortable file picker, synchronization Cancel button, cached-file download prompt fix, UTF-8 locale fix and single-pass Whisper zero-duration cue repair. Models and app-specific Whisper executables still download on demand. GPU host drivers/plugin and passthrough remain required for GPU use. Do not run two containers simultaneously against the same appdata.
For the full image:
docker build --platform linux/amd64 -t subtitle-sync:1.1.2 .
# Optional: transfer the image to a different machine.
docker save subtitle-sync:1.1.2 | gzip > SubtitleSync-1.1.2-docker-amd64.tar.gzOn Unraid, load the image with
gzip -dc SubtitleSync-1.1.2-docker-amd64.tar.gz | docker load.
Copy unraid/subtitle-sync.xml into
/boot/config/plugins/dockerMan/templates-user/my-subtitle-sync.xml, then use
Docker -> Add Container -> Template -> SubtitleSync. Set your media share,
appdata location, Web UI host port and optional GPU options before applying.
The template refers to the local subtitle-sync:1.1.2 image: it is not a
published Docker Hub/GHCR image and cannot be pulled from a registry.
To update it, rebuild/load the new image and recreate the container while keeping
the same /config mapping; Unraid's registry update check cannot update a local image.
The build context is an explicit allowlist in .dockerignore, and the image
copies only required application files: no test videos/SRTs, local environments,
downloaded runtimes/models, preferences or portable release archives are included.
For a normal Docker host, set SUBTITLE_SYNC_CONFIG and SUBTITLE_SYNC_MEDIA
to the desired host directories, and USER_ID/GROUP_ID to their owner.
On Unraid the defaults are 99:100 (nobody:users). Example CPU deployment:
SUBTITLE_SYNC_CONFIG=/mnt/user/appdata/subtitle-sync \
SUBTITLE_SYNC_MEDIA=/mnt/user/media docker compose up -d --buildAccess http://SERVER-IP:5800. Files are selected inside /data. Preferences,
matching/speech models, caches and downloaded GPU executables persist under
/config. Container recreation must reuse that mount. Only /config ownership
is managed automatically; /data must already be writable by the configured
user/group for saving subtitles. No privileged container or host-wide filesystem
mount is needed.
Both images default to UMASK=0002: synchronized and speech-to-text subtitles
are saved with -rw-rw-r-- (664) permissions on normal Linux filesystems.
To change this, add/edit the UMASK variable in Unraid and Apply (for example,
0022 gives 644). Host filesystem ACLs can also affect permissions.
The sync-output permissions fix requires rebuilding/loading the corrected image
and recreating the container with the same /config mapping. It applies to newly
saved outputs, including replacements; existing files are not changed retroactively.
| Hardware | Host/container setup | App choices |
|---|---|---|
| CPU | No devices or NVIDIA runtime required | CPU |
| AMD GPU or AMD APU | Host AMD kernel driver; map /dev/dri |
AMD or AMD APU (integrated), Recommended/Vulkan |
| Intel GPU or integrated graphics | Host i915/xe driver; map /dev/dri |
INTEL or INTEL integrated GPU, Recommended/Vulkan |
| NVIDIA | Unraid NVIDIA Driver plugin / NVIDIA Container Toolkit; --gpus=all |
NVIDIA, Recommended/CUDA; Vulkan is an alternative when exposed by the driver |
AMD/Intel in Compose:
docker compose -f compose.yml -f docker/compose.dri.yml up -d --buildNVIDIA in Compose (requires Compose 2.30+ and configured NVIDIA Container Toolkit):
docker compose -f compose.yml -f docker/compose.nvidia.yml up -d --buildBoth override files can be supplied together on a host with mixed GPUs. In Unraid, use Extra Parameters in Advanced View:
- CPU only:
--shm-size=1g - AMD/Intel GPU or APU:
--shm-size=1g --device=/dev/dri:/dev/dri - NVIDIA:
--shm-size=1g --gpus=all - Mixed NVIDIA and AMD/Intel:
--shm-size=1g --gpus=all --device=/dev/dri:/dev/dri
For NVIDIA, set NVIDIA_VISIBLE_DEVICES to the GPU UUID from the NVIDIA Driver
plugin (or all). Only add the DRI argument if the host has /dev/dri.
Do not leave a blank Device entry in the Unraid form: DockerMan emits
--device='', which fails with bad format for path. If using an older template,
remove its AMD/Intel GPU or APU Device entry entirely rather than clearing its
value, then use Extra Parameters as above.
A GPU bound to VFIO for a VM is not
available to this container.
The desktop base automatically adds normal non-root DRI device groups to the
application user. If needed, enter additional numeric groups in SUP_GROUP_IDS;
do not solve permission errors by using privileged mode or running the app as root.
Host kernel drivers cannot be installed into the app folder/container.
The image supplies userspace libraries, not Unraid kernel drivers. No ROCm
installation or /dev/kfd mapping is required for the recommended AMD Vulkan path.
Advanced MIGraphX/OpenVINO paths are not preinstalled in this image; use Vulkan.
GPU/APU compatibility varies; not every card/driver supports the required Vulkan
or CUDA features. The APU option uses its integrated GPU, not an NPU.
Select the device separately in the main window and Speech to text window. Use GPU setup -> Test GPU for matching, and device discovery/testing in the speech window before a full job. For NVIDIA, matching's ONNX CUDA provider is already installed in the main Python environment; no extra private ONNX install is needed. A listed provider is not proof of successful GPU inference. Whisper/Vulkan executables and models are downloaded through the app's setup. GPU errors remain visible and CPU mode stays available; a software Vulkan adapter such as llvmpipe/lavapipe is not hardware acceleration.
Useful diagnostics (replace CONTAINER with the container name/ID):
docker exec CONTAINER vulkaninfo --summary
docker exec CONTAINER nvidia-smi # NVIDIA hosts only
docker logs CONTAINERKeep the Web UI on a trusted LAN: it is a remote desktop with access to
mounted media. Do not forward port 5800/5900 to the Internet. For remote use,
use a VPN or authenticated TLS reverse proxy. The optional VNC_PASSWORD
(maximum eight characters) is not transport encryption. Direct VNC port 5900
is not published by the supplied Compose configuration/template.
-
Choose Source SRT (to synchronize) and a Reference SRT whose timestamps already match the video. With an external reference SRT, the video is optional.
-
Alternatively, leave Reference SRT blank and choose a video. The app checks embedded subtitles automatically, even before a source SRT is selected:
- No usable embedded text subtitles: Speech to text opens automatically so you can generate a reference SRT.
- One usable track: it is selected automatically.
- Multiple usable tracks: a popup lets you choose the reference track. All available SubRip languages are offered; English is not preferred. The FFmpeg path also converts supported ASS/SSA, WebVTT and MP4 text tracks to SRT. Image-only subtitles cannot be used without OCR; use speech to text if no usable text track is available. An already-filled reference SRT field bypasses this automatic check.
-
Click Select matching cues.... A popup displays both subtitles side by side with cue numbers, timestamps, searchable dialogue and full-text previews.
-
Select one cue in each subtitle that represents the same dialogue moment, then click Use selected pair. The source cue can be anywhere in the file. Its target start time comes directly from the reference cue; the app never replaces your choice with an automatic first-cue guess.
The initial sync point does not need to be at the beginning of the files. Choose any dialogue that you can clearly identify in both subtitles. A good choice is the place where automatic synchronization drifted or failed. In the example below, an automatic run was aligned everywhere except around minute 37. Source cue 585 (00:37:02,930) was paired with reference cue 945 (00:37:10,329), giving an initial offset of +7.399 s. Because the app still checks many other matching cues before and after that anchor, the resulting synchronized subtitle was aligned throughout the whole file.
-
Choose an output file and click Analyze and synchronize.
The selected source cue is a fixed anchor. Other reliable matches are found both before and after it; the reference timeline is not shifted to compensate for the source's existing offset. Changing either input requires selecting the pair again. Input-file changes during loading/analysis are detected and prevent output from being written using a stale selection. Saving over either input is prohibited. Cancelling cue selection does not start synchronization.
Click Speech to text... in the main window. This uses local Whisper.cpp, following Subtitle Edit's Whisper CPP/Vulkan approach, and works independently of subtitle matching. You can also generate a standalone transcript without selecting a source SRT.
- Choose a video/audio file and a separate generated reference SRT destination.
- Select the video's spoken language, or Auto detect. The output stays in that language: it is not translated to English. For videos containing multiple audio tracks, enter the desired zero-based audio stream index (0 = first audio stream, 1 = second audio stream, regardless of video/subtitle stream IDs).
- Choose tiny, base, small, medium, large-v3, or
large-v3-turbo. English-only
.envariants are also available for tiny/base/small/medium and require English, not Auto detect. Cantonese's dedicated language code requires a large-v3-family model. - Select CPU, NVIDIA, AMD, INTEL, AMD APU (integrated), or INTEL integrated GPU. Recommended uses CPU, CUDA for NVIDIA, and Vulkan for AMD/Intel GPUs and APUs. NVIDIA can also use Vulkan explicitly. These settings do not change the main window's subtitle-matching settings.
- For GPU/APU use, click Detect GPUs / APUs to run a short Whisper test and display actual adapter IDs/names. Enter the desired GPU adapter ID. IDs are specific to Whisper, not necessarily the same as the matching engine. Vendor/APU labels do not automatically select or filter the physical adapter.
- Click Generate reference SRT. Approve the first-time downloads. Download runtime / verify model is also available to prepare files before transcription. Later runs reuse cached files and can work offline.
- The generated SRT is saved and automatically selected as the main window's reference. Close the speech window, select your source SRT, then use Select matching cues... to choose the initial sync pair as before.
- The app downloads pinned, SHA-256-checked Whisper binaries/models under
.speech-to-textbeside the app. Model downloads range from about 78 MB (tiny) to 3.1 GB (large-v3); large-v3-turbo is about 1.6 GB. Runtime downloads range from roughly 5-23 MB for CPU/Vulkan to 259-685 MB for CUDA. - CPU and Windows CUDA packages come from the official Whisper.cpp b5454 release (v1.9.5). Linux CUDA and Windows/Linux Vulkan packages come from Subtitle Edit's whispercpp-195 release. CPU supports Windows/Linux x64 and ARM64; the bundled GPU packages support x64. Older Linux distributions may need newer system libraries to run these binaries.
- FFmpeg is used from PATH when available; otherwise the launchers install
imageio-ffmpeg, which supplies a private FFmpeg executable on supported Windows/Linux platforms. Audio is decoded to temporary mono 16 kHz PCM. Initial audio delays are retained relative to the media start. Temporary files are removed after completion, failure or cancellation. Whisper splits segments at word boundaries with an 84-character target to avoid paragraph-sized subtitle cues. Download confirmation is shown only for missing/incomplete runtime or model files, not every time cached files are used. GPU detection checks the tiny model it actually uses, independently of the transcription model selection. Cached models are still checksum-verified; damaged models are downloaded again. Whisper runs once. If word splitting produces zero-duration cues (seen with CUDA), each such word is joined to the nearest neighbouring line using only Whisper's own timestamps; empty cues (no dialogue) are removed. The log reports how many were repaired. Reversed timings, or a transcript with only zero-duration cues, are rejected with the offending cue named, and existing output remains unchanged. - Your existing system graphics driver is used. The app does not download or replace kernel/display drivers or require administrator access. Vulkan needs a working Vulkan loader/driver; CUDA needs a compatible NVIDIA driver and may report missing host runtime libraries. Resolve the reported dependency or use Vulkan/CPU; GPU failures are not silently accepted as CPU transcription.
- Start with tiny/base on older GPUs or APUs. Larger models may exhaust VRAM or shared memory. Flash attention is disabled for compatibility with older Vulkan hardware. GPU inference still uses CPU resources for some operations.
- Real CPU and AMD GPU (Linux RADV Vulkan) transcription have been verified. Windows, CUDA and other GPUs/APUs need testing on the respective hardware.
The window shows progress and an engine log, stays responsive, and supports Cancel. Cancellation can take up to the network timeout during a stalled download. Existing output requires explicit confirmation; inputs and the selected synchronized-output path are protected. An SRT is published only after successful transcription and validation; failures/cancellation do not replace it.
Whisper can mishear names, omit dialogue or hallucinate text during silence. Review generated text and timestamps before synchronizing. Spoken-language recognition coverage is broader than the matching model's language coverage; a selectable speech language does not guarantee reliable cross-language matching. Source cue durations in the final synchronized SRT are preserved unless a confidence-checked global timing scale is applied (see Automatic timing-scale detection), subject to the existing overlap/negative-timestamp corrections.
The multilingual MiniLM model covers roughly 50 languages, including English, Portuguese, Spanish, French, German, Arabic, Chinese, Japanese, Korean and Russian. No Portuguese/English translation pipeline or language setting is required. Unicode dialogue is retained when building candidates; a Latin alphabet is not required. Support and quality vary by language, especially for languages absent from the model's training set.
Matching compares semantic embeddings, not literal words. It evaluates short multi-cue phrases on both sides (up to five nearby cues and nine seconds; individual longer cues, including Whisper segments, are retained) to accommodate different sentence splits, and compares surrounding dialogue context to help disambiguate paraphrases and repeated expressions. Context and phrase similarity are combined with the initial offset, a local time search and chronological constraints. Cross-language character-length penalties are not used. Unreliable matches are rejected rather than treated as exact translations.
This is semantic similarity, not a reasoning/translation model: idioms, heavily rewritten scenes, missing dialogue or different edits can still defeat automatic matching. Review the output, especially where there are few confident matches. Hearing-impaired filtering uses language-independent brackets/music markers plus an English sound-description vocabulary; the latter is not exhaustive across languages. The user-selected anchor remains fixed even when its dialogue is paraphrased or would be excluded by that filter.
Select CPU, NVIDIA, AMD, INTEL, AMD APU (integrated), or INTEL integrated GPU in Processing device. CPU is the default and works with the standard installation. GPUs accelerate model inference; subtitle parsing, matching and retiming still use the CPU.
Leave Inference backend set to Recommended for these defaults:
| Selection | Windows backend | Linux backend |
|---|---|---|
| CPU | ONNX Runtime CPU | ONNX Runtime CPU |
| NVIDIA | CUDA | CUDA |
| AMD | Vulkan | Vulkan |
| INTEL | Vulkan | Vulkan |
| AMD APU (integrated) | Vulkan | Vulkan |
| INTEL integrated GPU | Vulkan | Vulkan |
Integrated selections use the GPU built into an AMD or Intel processor, not CPU inference. They share the Vulkan runtime; enter the integrated adapter's ID if a discrete GPU is also present. Selecting an integrated option does not automatically discover or choose that adapter. Intel HD/UHD, Iris and integrated Arc support depends on the processor generation, driver and Vulkan features. A successful GPU test is required to establish whether the selected hardware works with this runtime.
GPU support depends on your card, drivers, Python version and installed runtime; having a vendor's GPU alone is not sufficient. The UI reports whether the required runtime is installed, not whether a physical GPU or its drivers have been validated. Analysis reports an error if the selected backend cannot initialize. It does not silently switch to CPU. Vulkan startup checks that model layers are offloaded to GPU; CPU tokenization and some operations/buffers remain normal.
GPU adapter ID is zero-based and refers to the backend's device ordering.
Use Detect GPUs in Vulkan setup to see actual device names and IDs. These IDs
are backend-specific: do not reuse a Vulkan ID blindly for CUDA or DirectML.
Vulkan enumerates all vendors; choose the actual GPU/APU you want, not just the
first adapter. CUDA and MIGraphX enumerate their supported devices. OpenVINO uses GPU.0,
GPU.1, etc. DirectML enumerates all DXGI adapters, not only the selected
vendor: AMD and INTEL both use DirectML on Windows, so enter the adapter ID for
the actual card you want. The vendor selection does not detect or filter cards.
Use your runtime's device enumeration tools to determine IDs on multi-GPU systems.
- Select your GPU/APU under Processing device. For AMD/Intel, leave Inference backend on Recommended. For NVIDIA, select Vulkan explicitly if you prefer it to CUDA.
- Click GPU setup / drivers..., then Install / repair Vulkan support.
- Approve downloading about 275 MB. The app downloads the official llama.cpp
Vulkan binaries and a community GGUF F16 conversion of the same multilingual
MiniLM model family used by CPU/CUDA. Both downloads are pinned and
SHA-256-verified; files stay under
.gpu-runtimesin the app folder. - The app detects hardware adapters. Select yours in the list; its Vulkan ID is copied to the main window. Click Test GPU.
- When the test passes, close setup and synchronize as normal. No app restart, ROCm installation, CUDA toolkit or Python GPU package is needed for Vulkan.
If Vulkan already works, do not reinstall graphics drivers. The app uses the
existing system Vulkan driver/loader, like other Vulkan applications. A successful
Whisper.cpp Vulkan run is encouraging but not a guarantee: this is a different
runtime/model with its own required Vulkan features. If detection fails, check the
existing driver/loader (Linux: vulkaninfo); only update/install system components
when needed. The app never modifies system drivers or runs elevated commands.
The Vulkan path was tested on an AMD GPU with Linux RADV, including real multilingual embeddings and subtitle alignment. The earlier ROCm restriction applies only to the optional ONNX/MIGraphX path, not Vulkan. Other cards/drivers and Windows still need their own Test GPU check.
The Vulkan runtime is llama.cpp
with embedding support, not Whisper.cpp speech recognition. Its local server binds
only to 127.0.0.1, uses a random API key and is stopped after embeddings are
generated or an error occurs. Subtitle text is not sent to an internet service.
CPU and CUDA retain the existing FastEmbed/ONNX implementation. Vulkan's F16 GGUF
conversion/tokenizer can produce slightly different similarities and matches, so
review important synchronization results. Both paths use normalized 384-dimensional
vectors, mean pooling and a 128-token input limit.
Packages are available for Windows/Linux x64 and ARM64. Linux binaries are upstream Ubuntu builds and may require newer glibc/system libraries than an older distribution provides. A working Vulkan driver alone does not guarantee binary compatibility. Downloads/installation failures are shown explicitly; CPU remains available. Internet access is needed for initial setup only.
NVIDIA Recommended uses CUDA. To retain the previous AMD/Intel inference backends, select ONNX (advanced) explicitly:
| Selection | Windows ONNX backend | Linux ONNX backend |
|---|---|---|
| NVIDIA | CUDA | CUDA |
| AMD / AMD APU | DirectML | MIGraphX (ROCm) |
| Intel / Intel integrated | DirectML | OpenVINO GPU |
- Start the app normally and select your GPU vendor under Processing device.
- Click GPU setup / drivers....
- Use Official driver guide only if your system driver needs installation/updating. Linux users should follow the instructions for their distribution. Restart the computer if the driver installer asks. The app never runs elevated commands, changes system drivers, or downloads driver installers from unofficial sites.
- Click Install / repair GPU support and approve the download. The app creates
a private Python environment under
.gpu-runtimesin the app folder, downloads the runtime and its dependencies, and then runs a real subtitle-model inference test on the selected adapter. Keep the app open until it finishes. - After a successful test, close the setup window and synchronize as normal. No app restart or terminal commands are needed. Use Test GPU to retest after a driver update or when changing the adapter ID.
Automatic packages:
- NVIDIA Windows/Linux:
onnxruntime-gpu[cuda,cudnn]==1.26.0(CUDA 12; Python 3.10 falls back to 1.23.2), including packaged CUDA/cuDNN libraries, preloaded before model initialization. This does not include the system NVIDIA driver; CUDA 12 needs NVIDIA driver 525 or newer. Follow the error output and official guide. - AMD/Intel Windows:
onnxruntime-directml; requires DirectX 12-compatible hardware and its system graphics driver. Both vendors share the DirectML environment. - Intel Linux:
onnxruntime-openvino; requires Intel GPU drivers and device permissions configured according to Intel's guide. - AMD Linux ONNX: guided manual setup (below); use Vulkan to avoid ROCm.
GPU packages may require several GB of disk space. The setup log displays download, installation and test errors. Your Python version and architecture must have compatible wheels; if not, create the app's base environment with a Python version supported by the selected runtime, or continue using CPU. Setup uses the same Python version as the running app; it does not install Python or change OS packages.
The base environment and CPU mode remain unchanged. Each backend has a separate environment, and subtitle analysis automatically runs in that environment in a child process. Failed installation or testing does not silently switch analysis to CPU. Select CPU explicitly if desired. A runtime marked installed is not proof of working hardware: Test GPU runs inference and checks the requested provider remains active. It does not guarantee every operation was executed on GPU.
Select AMD, set Inference backend to ONNX (advanced), then open GPU setup / drivers.... The Linux setup panel lets you choose:
- Official AMD driver / ROCm: guidance for the system driver and compute stack.
- MIGraphX inference: guidance for the ONNX Runtime backend used by this app. MIGraphX is also an official AMD project, not an alternative system driver. These are complementary setup stages, not interchangeable inference backends.
Select or type your exact GPU model, distribution, and distribution release. The distribution/release are initially detected from Linux's OS-release information when available and remain editable. The GPU suggestions include Radeon, Ryzen and Instinct models; unlisted models can be typed. Suggestions do not indicate support. The expanded list includes RX 400/500, Vega, RX 5000/6000/7000/9000 and older R7/R9 cards. Selecting AMD APU (integrated) shows an APU-focused model list instead.
The panel shows selection-specific guidance, package-manager-family cautions, and copyable app-environment commands for the MIGraphX stage.
Click Compatibility matrix before installing. Open selected setup guide opens the official ROCm selector or MIGraphX documentation for the selected stage; repeat your exact GPU/OS selection on AMD's site (website fields are not prefilled). ONNX Runtime guide explains the required MIGraphX execution-provider build. The app does not verify support or choose a wheel based on the selections, and does not substitute another distribution's packages. Installation stays manual; Install / repair GPU support remains disabled for AMD Linux.
After installing the required system driver/ROCm components using those guides:
- Create
.gpu-runtimes/linux-amdwith a compatible Python:python3 -m venv .gpu-runtimes/linux-amd. - Install the app dependencies:
.gpu-runtimes/linux-amd/bin/python -m pip install -r requirements.txt. - Uninstall the CPU runtime:
.gpu-runtimes/linux-amd/bin/python -m pip uninstall -y onnxruntime. - Install AMD's compatible ONNX Runtime wheel/build with
MIGraphXExecutionProviderinto that environment using its Python and AMD's instructions. Do not install the standard PyPIonnxruntime-gpufor AMD. - Select AMD and ONNX (advanced) in the app, open GPU setup / drivers..., then click Test GPU. A successful test registers the environment for subsequent analysis.
If your GPU, distribution or Python version is unsupported, try Vulkan or use CPU rather than installing mismatched ROCm packages.
See the official ONNX Runtime execution provider documentation for current driver, runtime and platform compatibility requirements.
FastEmbed declares the CPU onnxruntime distribution as a dependency. The installer
installs base requirements, removes conflicting ONNX Runtime distributions in the
private GPU environment only, then installs the selected backend. Pip may report
the CPU distribution missing there even though the replacement supplies the same
module. Do not manually reinstall base requirements inside a configured GPU
environment; use Install / repair GPU support instead.
The model cache uses LOCALAPPDATA on Windows and XDG_DATA_HOME (default
~/.local/share) on Linux, under SubtitleSync/model-cache. Set
SUBTITLE_SYNC_MODEL_CACHE to override it.
Run PYTHON -m unittest -v test_subtitle_devices test_gpu_runtime test_vulkan_embeddings test_manual_sync test_speech_to_text test_ui_preferences using the
environment's Python (.venv-linux/bin/python on Linux,
.venv\Scripts\python.exe on Windows).
GUI tests need a display; on headless Linux use
xvfb-run -a .venv-linux/bin/python -m unittest -v test_subtitle_devices test_gpu_runtime test_vulkan_embeddings test_manual_sync test_speech_to_text test_ui_preferences.
The tests mock inference sessions and speech engines and do not require model
downloads or a GPU. Speech tests cover language/model validation, platform/backend
selection, audio-track mapping, timeline offsets, cancellation, output protection,
GPU-fallback rejection and the generated-reference GUI handoff.


