fix(subtitles): strip X-TIMESTAMP-MAP so WebVTT cues are not offset on conversion

This commit is contained in:
imSp4rky
2026-08-05 11:07:38 -06:00
parent 041ba5293b
commit 02fd7b9387
2 changed files with 34 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
from unshackle.core.tracks.subtitle import Subtitle
def test_strips_timestamp_map_keeps_cues():
text = "WEBVTT\nX-TIMESTAMP-MAP=MPEGTS:900000,LOCAL:00:00:00.000\n\n00:00:22.690 --> 00:00:24.710\nHi\n"
out = Subtitle.strip_webvtt_timestamp_map(text)
assert "X-TIMESTAMP-MAP" not in out
assert "00:00:22.690 --> 00:00:24.710" in out
assert out.startswith("WEBVTT\n")
def test_strips_lowercase_timestamp_map():
# SubtitleEdit honours the header case-insensitively, so the strip must too
text = "WEBVTT\nx-timestamp-map=MPEGTS:900000,LOCAL:00:00:00.000\n\n00:00:22.690 --> 00:00:24.710\nHi\n"
out = Subtitle.strip_webvtt_timestamp_map(text)
assert "x-timestamp-map" not in out.lower()
assert "00:00:22.690 --> 00:00:24.710" in out
def test_no_timestamp_map_is_untouched():
text = "WEBVTT\n\n00:00:01.000 --> 00:00:02.000\nHi\n"
assert Subtitle.strip_webvtt_timestamp_map(text) == text
+12
View File
@@ -257,6 +257,7 @@ class Subtitle(Track):
self.convert(Subtitle.Codec.WebVTT)
elif self.codec == Subtitle.Codec.WebVTT:
text = self.path.read_text("utf8")
text = Subtitle.strip_webvtt_timestamp_map(text)
if self.descriptor == Track.Descriptor.DASH:
if len(self.data["dash"]["segment_durations"]) > 1:
text = merge_segmented_webvtt(
@@ -303,6 +304,17 @@ class Subtitle(Track):
# Keep the sanitized version even if parsing failed
self.path.write_text(text, encoding="utf8")
@staticmethod
def strip_webvtt_timestamp_map(text: str) -> str:
"""
Remove X-TIMESTAMP-MAP header lines (RFC 8216 §3.5, HLS only).
Cue times are already absolute, but SubtitleEdit reads the header and offsets
every cue by it during conversion.
"""
# IGNORECASE: SubtitleEdit matches the header case-insensitively, so must the strip
return re.sub(r"^[ \t]*X-TIMESTAMP-MAP[^\n]*\n?", "", text, flags=re.MULTILINE | re.IGNORECASE)
@staticmethod
def sanitize_webvtt_timestamps(text: str) -> str:
"""