From 0731087dbbd403795dfe9c3ef3ea7650255c5f92 Mon Sep 17 00:00:00 2001 From: Thomas Howe Date: Sat, 26 Sep 2026 12:41:11 -0400 Subject: [PATCH] fix(wtf_transcribe): resolve common audio extensions without the OS MIME table mimetypes.guess_type() depends on the host's MIME table. Slim Linux images ship without /etc/mime.types, so "clip.ogg" resolved to None in CI's container and the dialog fell back to audio/wav, failing test_dialog_mediatype_resolution_order on main. Check a small table of common audio and video extensions first, then the OS table. Co-Authored-By: Claude Opus 5.5 --- conserver/links/wtf_transcribe/__init__.py | 21 +++++++++++++++++++ .../test_wtf_transcribe_unit.py | 12 +++++++++++ 2 files changed, 33 insertions(+) diff --git a/conserver/links/wtf_transcribe/__init__.py b/conserver/links/wtf_transcribe/__init__.py index 685e83a..90b9a6b 100644 --- a/conserver/links/wtf_transcribe/__init__.py +++ b/conserver/links/wtf_transcribe/__init__.py @@ -26,6 +26,7 @@ import json import logging import mimetypes +import os import requests from typing import Optional, Dict, Any @@ -181,6 +182,23 @@ def dialog_to_audio_binary(dialog, url_timeout): def dialog_filename(dialog, dialog_index): return dialog.get("filename", f"audio_{dialog_index}.wav") +# Common audio/video extensions, checked before the OS MIME table: slim Linux +# images ship without /etc/mime.types, where mimetypes.guess_type("x.ogg") +# returns None even though macOS resolves it. +_MEDIA_EXTENSIONS = { + ".wav": "audio/wav", + ".mp3": "audio/mpeg", + ".ogg": "audio/ogg", + ".oga": "audio/ogg", + ".opus": "audio/opus", + ".m4a": "audio/mp4", + ".aac": "audio/aac", + ".flac": "audio/flac", + ".webm": "audio/webm", + ".mp4": "video/mp4", +} + + def dialog_mediatype(dialog): """Resolve a dialog's media type. @@ -199,6 +217,9 @@ def dialog_mediatype(dialog): filename = dialog.get("filename") if filename: + known = _MEDIA_EXTENSIONS.get(os.path.splitext(filename)[1].lower()) + if known: + return known guessed_type, _ = mimetypes.guess_type(filename) if guessed_type and (guessed_type.startswith("audio/") or guessed_type.startswith("video/")): return guessed_type diff --git a/conserver/links/wtf_transcribe/test_wtf_transcribe_unit.py b/conserver/links/wtf_transcribe/test_wtf_transcribe_unit.py index 2102ca6..abff914 100644 --- a/conserver/links/wtf_transcribe/test_wtf_transcribe_unit.py +++ b/conserver/links/wtf_transcribe/test_wtf_transcribe_unit.py @@ -239,3 +239,15 @@ def test_run_happy_path_installs_defaults_and_saves( url_timeout=60, ) mock_save_vcon.assert_called_once_with(vcon, redis) + + +def test_dialog_mediatype_extension_table_without_os_mime_table(monkeypatch): + # Slim Linux images have no /etc/mime.types, so guess_type() knows little. + # The built-in extension table must still resolve common audio types. + import links.wtf_transcribe as wtf + + monkeypatch.setattr(wtf.mimetypes, "guess_type", lambda *_a, **_k: (None, None)) + assert wtf.dialog_mediatype({"filename": "clip.ogg"}) == "audio/ogg" + assert wtf.dialog_mediatype({"filename": "CALL.MP3"}) == "audio/mpeg" + assert wtf.dialog_mediatype({"filename": "x.opus"}) == "audio/opus" + assert wtf.dialog_mediatype({"filename": "notes.txt"}) == "audio/wav"