Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

VLSubSync

A standalone VLC Lua extension that adds a Resync current subtitles button. It aligns the most likely external subtitle beside the playing media using ffsubsync, writes a corrected subtitle without modifying the original, and loads it into VLC.

VLSubSync does not modify or depend on VLSub. Download or load subtitles normally, then open VLSubSync from VLC's extension menu.

V1 behavior

  1. Reads the current local media path from VLC.
  2. Finds related .srt, .ass, .ssa, or .vtt files beside the media.
  3. Prefers an exact filename match, then the selected VLC 4 subtitle-track name, then the newest related file.
  4. Refuses to guess if equally plausible candidates remain.
  5. Runs ffs, writes <subtitle>.synced.<ext> into a private per-user cache directory, and atomically publishes it without replacing existing filesystem objects.
  6. Loads the corrected subtitle into VLC while preserving the original.

VLC 3 does not expose selected subtitle-track metadata to Lua, so filename and modification time are used there. VLC 4 also uses the selected track name when available.

Requirements

  • Linux (V1 target)
  • VLC 3 or 4
  • Python 3.10+
  • ffsubsync (ffs command)
  • FFmpeg

Try without installing

nix develop
python -m unittest discover -s tests -v
lua tests/test_extension.lua

Manual installation

Experienced users can install VLSubSync with any preferred package or dotfile manager. This assumes python3, ffs, and ffmpeg are installed and available on the runtime PATH:

  1. Install the standalone CLI:

    install -Dm700 vlsubsync ~/.local/bin/vlsubsync
  2. Install the VLC extension:

    install -Dm600 extension/vlsubsync.lua \
      ~/.local/share/vlc/lua/extensions/vlsubsync.lua
  3. Restart VLC.

Keep the installed files and directories user-owned and not writable by group or others. For a different CLI location, set packaged_cli in extension/vlsubsync.lua to its absolute path.

Command-line usage

Let vlsubsync discover the most likely subtitle beside a video:

vlsubsync ~/Videos/Movie.mkv

Synchronize a specific subtitle:

vlsubsync ~/Videos/Movie.mkv --subtitle ~/Videos/Movie.en.srt

Provide a selected-track hint when filenames alone are insufficient:

vlsubsync ~/Videos/Movie.mkv --track-name "English [CC]"

On success, stdout contains only the corrected subtitle path, so it can be used by another command:

synced="$(vlsubsync ~/Videos/Movie.mkv)"
vlc --sub-file "$synced" ~/Videos/Movie.mkv

--protocol emits the strict encoded response consumed by the VLC extension and is intended for machine integration.

Home Manager (recommended)

Add VLSubSync as a flake input and follow your existing nixpkgs:

{
  inputs.vlsubsync = {
    url = "github:urchin-tidebot/vlsubsync";
    inputs.nixpkgs.follows = "nixpkgs";
  };
}

Import the module in your Home Manager configuration and enable it:

{ inputs, ... }:
{
  imports = [ inputs.vlsubsync.homeManagerModules.default ];

  programs.vlsubsync.enable = true;
}

For Home Manager embedded in a NixOS configuration:

{
  home-manager.users.shazow = {
    imports = [ inputs.vlsubsync.homeManagerModules.default ];
    programs.vlsubsync.enable = true;
  };
}

The module installs the CLI and declaratively links the extension to $XDG_DATA_HOME/vlc/lua/extensions/vlsubsync.lua. The Nix-built extension contains the CLI's absolute store path, so desktop-launched VLC does not need to inherit a particular PATH.

To override the package:

programs.vlsubsync.package = inputs.vlsubsync.packages.${pkgs.system}.vlsubsync;

Without adding a flake input

You can fetch the repository from an ordinary Home Manager module and wire the package and VLC extension directly. Because callPackage uses your existing pkgs, VLSubSync reuses the same pkgs.ffmpeg, pkgs.ffsubsync, and pkgs.python3 derivations selected by your configuration:

{ pkgs, ... }:

let
  src = pkgs.fetchFromGitHub {
    owner = "urchin-tidebot";
    repo = "vlsubsync";
    rev = "d7902ae177753b50e435389b38416001dd1dd3f9";
    hash = "sha256-wuTXphIeYBQEbbykwX7WxrfRw17io8y1uJFmKPwGkIg=";
  };

  vlsubsync = pkgs.callPackage "${src}/nix/package.nix" {
    inherit src;
  };
in
{
  home.packages = [
    pkgs.vlc
    pkgs.ffmpeg
    vlsubsync
  ];

  xdg.dataFile."vlc/lua/extensions/vlsubsync.lua".source =
    "${vlsubsync}/share/vlc/lua/extensions/vlsubsync.lua";
}

Update rev and hash together when upgrading. Importing package.nix from a fetchFromGitHub result uses import-from-derivation, which must be enabled in the evaluating Nix configuration.

Other flake outputs

The flake also exports:

  • packages.<system>.default and packages.<system>.vlsubsync
  • overlays.default, which adds pkgs.vlsubsync
  • homeManagerModules.default and homeManagerModules.vlsubsync

Package-only installation is available:

nix profile install github:urchin-tidebot/vlsubsync

However, VLC does not consistently scan profile-provided data directories for Lua extensions. Prefer the Home Manager module, or manually link the packaged extension into the per-user VLC extension directory.

Portable per-user install

With Python, ffs, and FFmpeg already on PATH:

./scripts/install-user

The installer copies the included CLI to ~/.local/bin/vlsubsync and installs the Lua extension. It does not install dependencies or record absolute paths for Python, ffs, or FFmpeg; those commands are resolved from PATH each time the CLI runs. Ensure that VLC inherits a trusted PATH containing them. The installer still refuses symlinked installation directories or destination files and supports only user-owned directories that are not writable by group or others under ~/.local.

Restart VLC, then choose View → VLSubSync and click Resync current subtitles. Depending on the desktop integration, VLC extensions may instead appear under Tools → Plugins and extensions.

Synchronization analyzes the media's audio and commonly takes tens of seconds. Playback can continue while it runs, although the extension dialog remains busy.

Safety

  • Media and subtitle inputs must be ordinary files; symbolic links, FIFOs, devices, and other special files are rejected.
  • Subtitle content is copied into the private cache before parsing, and the media inode is pinned through an open file descriptor during synchronization.
  • Original subtitle files are never overwritten. Generated output is published atomically without replacing any existing file, symlink, or other object.
  • Subtitle and generated-output sizes, CLI diagnostics, and Lua protocol responses are bounded; synchronization is terminated after 15 minutes.
  • Previously generated .synced files are excluded from candidate discovery.
  • Ambiguous candidates produce an error instead of a guess.
  • Only local media files are supported in V1.

ffmpeg and ffsubsync still parse untrusted media and subtitle content. Keep those dependencies updated. VLSubSync constrains their inputs, output, runtime, diagnostics, and lingering child processes, but it does not place them in an OS-level privilege sandbox; a successful parser code-execution vulnerability would therefore run with the user's access. Use an externally sandboxed VLC/CLI environment when processing media that requires stronger isolation.

Development

nix flake check
nix build

The CLI tests use Python's standard-library unittest; Lua tests run against Lua 5.1, matching VLC's embedded Lua version.

License

VLSubSync is MIT licensed. ffsubsync is a separate MIT-licensed dependency. VLC is distributed under GPL/LGPL terms depending on the component.

About

One-click subtitle synchronization for VLC

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages