fix: don't crash on a file with no subtitle tracks #15

Merged
nugget merged 1 commit from fix/no-subtitle-tracks-crash into main 2026-10-22 18:52:59 -05:00
Collaborator

tag_episode.py died with IndexError: list index out of range on any file
carrying no subtitle tracks, before printing even the audio plan it had already
built. An empty list has no middle element:

counts = sorted(cue.get(s["index"], 0) for s in sub)
median = counts[len(counts) // 2] or 1      # IndexError when sub == []

max(len(p[3]) for p in plan) raises the same way on a file with no audio
either.

This is not an edge case. A Jellyfin audit on 2026-10-22 found 3,201 of 8,052
library files carry no subtitle stream, so it is the normal shape of a streaming
pull.

Changes

Two guards, no behaviour change for files that do have subtitles:

  • median = (counts[len(counts) // 2] if counts else 0) or 1 — the existing
    or 1 already handled a zero median, so the fallback path was there already.
  • w = max((len(p[3]) for p in plan), default=0)

Verification

ast.parse and the --help gate pass across lib/*.py. Three real-media runs
against clips cut from ~/Movies/Rips:

case before after
video + audio, no subtitles IndexError 1 audio, 0 subtitle tracks
video only IndexError 0 audio, 0 subtitle tracks
video + audio + 2 subtitles plan printed unchanged

--apply also exercised end-to-end on the zero-subtitle file: exit 0, container
title written, audio track named.

🤖 Generated with Claude Code

`tag_episode.py` died with `IndexError: list index out of range` on any file carrying no subtitle tracks, before printing even the audio plan it had already built. An empty list has no middle element: ```python counts = sorted(cue.get(s["index"], 0) for s in sub) median = counts[len(counts) // 2] or 1 # IndexError when sub == [] ``` `max(len(p[3]) for p in plan)` raises the same way on a file with no audio either. This is not an edge case. A Jellyfin audit on 2026-10-22 found **3,201 of 8,052** library files carry no subtitle stream, so it is the normal shape of a streaming pull. ## Changes Two guards, no behaviour change for files that do have subtitles: - `median = (counts[len(counts) // 2] if counts else 0) or 1` — the existing `or 1` already handled a zero median, so the fallback path was there already. - `w = max((len(p[3]) for p in plan), default=0)` ## Verification `ast.parse` and the `--help` gate pass across `lib/*.py`. Three real-media runs against clips cut from `~/Movies/Rips`: | case | before | after | |---|---|---| | video + audio, no subtitles | `IndexError` | `1 audio, 0 subtitle tracks` | | video only | `IndexError` | `0 audio, 0 subtitle tracks` | | video + audio + 2 subtitles | plan printed | unchanged | `--apply` also exercised end-to-end on the zero-subtitle file: exit 0, container title written, audio track named. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
An empty subtitle list has no median, so indexing the middle of an empty
list raised IndexError and the script died before printing anything --
including the audio plan it had already built. max() over an empty plan
raises the same way on a video-only file.

Streaming pulls routinely carry no subtitle track at all; a Jellyfin
audit put 3,201 of 8,052 library files in that state, so this is the
common case rather than an edge one.
nugget merged commit f8b081189f into main 2026-10-22 18:52:59 -05:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
nugget/media-import-tooling!15
No description provided.