Skip to content

Forced external subtitle files count as full coverage, while forced embedded tracks correctly do not #358

Description

@coaxk

Summary

ignore_forced_subtitles is honoured for embedded subtitle streams but not for external subtitle files. A forced sidecar such as Movie.en.forced.srt satisfies the English coverage check, so the file is skipped and there is no way to obtain a full subtitle for it.

Observed on 2026.08.1, but the code path looks unchanged for a long time.

Why this looks like a bug rather than a choice

has_internal_subtitle_in_language deliberately excludes forced tracks, and its docstring states the rationale:

Forced subtitle tracks are excluded when ignore_forced_subtitles is enabled, because a forced track only covers a small portion of dialogue and should not be treated as full subtitle coverage.

That reasoning is about what a forced subtitle contains, not about where it is stored, so it applies identically to a sidecar. But has_external_subtitle_in_language has no equivalent screen.

Line references on 2026.08.1:

  • subtitle_exists_in_language line 2071, which ORs internal and external
  • has_internal_subtitle_in_language line 2091, forced excluded at line 2116 (if ignore_forced_subtitles and is_forced: continue)
  • has_external_subtitle_in_language line 2128, filename split at line 2160, language match at line 2174, with no forced handling anywhere between

Reproduction

Blue Planet II S01E01.mkv
Blue Planet II S01E01.en.forced.srt      <- forced only, no full English sub

subtitle_exists_in_language(video, LanguageCode.ENGLISH) returns True with IGNORE_FORCED_SUBTITLES=True, so the file is skipped. Move the same forced subtitle into the container as an embedded forced track and it correctly returns False.

At line 2160 the filename is split on ., so Movie.en.forced yields ['en', 'forced'], is_valid_subtitle_language sees en, and the file matches. Note this is independent of naming order: Movie.forced.en yields ['forced', 'en'] and matches just the same, so both common conventions are affected.

Suggested fix

Screen the parts for a forced marker before the language match, mirroring what the embedded path already does:

if ignore_forced_subtitles and any(part.lower() == "forced" for part in subtitle_parts):
    logging.debug(f"Skipping forced subtitle file {file_name} for {video_file}")
    continue

Checking the parts list rather than the raw filename keeps it order-agnostic and avoids matching a title that happens to contain the word.

Impact

Any workflow that produces forced-only sidecars is affected. In our case it is self-inflicted in an obvious way: the tool generates .en.forced.srt for foreign-dialogue segments and then subgen declines to produce the full subtitle, having counted that sidecar as coverage.

One behaviour note if you take this: installs whose only subtitle for a file is a forced sidecar will start transcribing those files rather than skipping them. That is the intended semantics and matches embedded behaviour, but it will produce new work on affected libraries, so it may be worth a release note.

Downstream status

We have patched this in our fork (coaxk/subarr-subgen v4.24) so our users are unblocked, and we would rather drop the patch than carry it. Happy to open a PR against upstream if the approach above looks right to you.

Reported to us as coaxk/subarr#483.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions