Skip to main content

media: rather than preserving the UTF-8 BOM, just discard it

ID
cefb712
date
2026-07-17 21:26:15+00:00
author
Alex Chan <alex@alexwlchan.net>
parent
108ddb5
message
media: rather than preserving the UTF-8 BOM, just discard it
changed files
4 files, 40 additions, 10 deletions

Changed files

CHANGELOG.md (6148) → CHANGELOG.md (6291)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a1e39f3..701f066 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # CHANGELOG
 
+## v47 - 2026-07-17
+
+Fix a bug in the byte order mark handling in v47.
+Rather than preserving the UTF-8 byte order mark, discard it entirely.
+
 ## v46 - 2026-07-16
 
 Change the `browser` fixture to be session-scoped, so the browser only needs to be started once per test suite.

src/chives/__init__.py (391) → src/chives/__init__.py (391)

diff --git a/src/chives/__init__.py b/src/chives/__init__.py
index eec5a4d..fe49ddd 100644
--- a/src/chives/__init__.py
+++ b/src/chives/__init__.py
@@ -11,4 +11,4 @@ I share across multiple sites.
 
 """
 
-__version__ = "46"
+__version__ = "47"

src/chives/media.py (12113) → src/chives/media.py (11912)

diff --git a/src/chives/media.py b/src/chives/media.py
index 015ae55..8a58113 100644
--- a/src/chives/media.py
+++ b/src/chives/media.py
@@ -427,7 +427,7 @@ def convert_srt_to_vtt(srt_path: Path) -> Path:
             f"convert_srt_to_vtt can only take .srt files, got {srt_path!r}"
         )
 
-    with open(srt_path) as srt_file:
+    with open(srt_path, encoding="utf-8-sig") as srt_file:
         srt_lines = srt_file.readlines()
 
     if len(srt_lines) == 0:
@@ -436,13 +436,7 @@ def convert_srt_to_vtt(srt_path: Path) -> Path:
     vtt_path = srt_path.with_suffix(".vtt")
     assert vtt_path != srt_path, vtt_path
 
-    with open(vtt_path, "w") as vtt_file:
-        # Check for a byte order mark in the first line; if so, preserve
-        # it in the converted VTT file.
-        if srt_lines[0].startswith("\ufeff"):
-            vtt_file.write("\ufeff")
-            srt_lines[0] = srt_lines[0][1:]
-
+    with open(vtt_path, "w", encoding="utf-8") as vtt_file:
         # Write the required WEBVTT header and blank line
         vtt_file.write("WEBVTT\n\n")
 

tests/test_media.py (17704) → tests/test_media.py (18739)

diff --git a/tests/test_media.py b/tests/test_media.py
index 5109adb..88b6e9c 100644
--- a/tests/test_media.py
+++ b/tests/test_media.py
@@ -492,7 +492,7 @@ class TestConvertSrtToVtt:
         assert not srt_path.exists()
 
         assert vtt_path.read_text() == (
-            "\ufeffWEBVTT\n"
+            "WEBVTT\n"
             "\n"
             "00:00:01.001 --> 00:00:10.010\n"
             "Somebody said the first thing\n"
@@ -501,6 +501,37 @@ class TestConvertSrtToVtt:
             "Somebody else said the second thing\n"
         )
 
+    def test_converting_srt_with_bom_on_later_number(self, tmp_path: Path) -> None:
+        """
+        If an SRT file starts with a byte order mark but the caption
+        number doesn't start at 1, the BOM is removed.
+        """
+        srt_path = tmp_path / "captions_with_bom.en.srt"
+        srt_path.write_text(
+            "\ufeff2\n"
+            "00:00:02,002 --> 00:00:20,020\n"
+            "Who said the first thing?\n"
+            "\n"
+            "3\n"
+            "00:03:00,003 --> 00:30:00,030\n"
+            "A ghost said the first thing\n"
+        )
+
+        vtt_path = convert_srt_to_vtt(srt_path)
+        assert vtt_path == (tmp_path / "captions_with_bom.en.vtt")
+        assert vtt_path.exists()
+        assert not srt_path.exists()
+
+        assert vtt_path.read_text() == (
+            "WEBVTT\n"
+            "\n"
+            "00:00:02.002 --> 00:00:20.020\n"
+            "Who said the first thing?\n"
+            "\n"
+            "00:03:00.003 --> 00:30:00.030\n"
+            "A ghost said the first thing\n"
+        )
+
     def test_convert_empty_file_is_error(self, tmp_path: Path) -> None:
         """
         Converting an empty file is an error.