Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions conserver/links/wtf_transcribe/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import json
import logging
import mimetypes
import requests
from typing import Optional, Dict, Any

Expand Down Expand Up @@ -180,8 +181,33 @@ def dialog_to_audio_binary(dialog, url_timeout):
def dialog_filename(dialog, dialog_index):
return dialog.get("filename", f"audio_{dialog_index}.wav")

def dialog_mimetype(dialog):
return dialog.get("mimetype", "audio/wav")
def dialog_mediatype(dialog):
"""Resolve a dialog's media type.

Prefers the modern ``mediatype`` field, then falls back to the legacy
``mimetype`` field (in case a caller passes a pre-compat dialog), then
derives it from the dialog's filename extension, and finally defaults
to audio/wav.
"""
mediatype = dialog.get("mediatype")
if mediatype:
return mediatype

mimetype = dialog.get("mimetype")
if mimetype:
return mimetype

filename = dialog.get("filename")
if filename:
guessed_type, _ = mimetypes.guess_type(filename)
if guessed_type and (guessed_type.startswith("audio/") or guessed_type.startswith("video/")):
return guessed_type

return "audio/wav"


# Backwards-compatible alias; prefer dialog_mediatype for new code.
dialog_mimetype = dialog_mediatype

def build_vfun_headers(api_key):
headers = {}
Expand All @@ -202,8 +228,8 @@ def maybe_decode_double_encoded_json(response_json):

def send_audio_to_vfun(audio_binary, dialog, dialog_index, vfun_server_url, api_key, diarize, language, vfun_timeout):
filename = dialog_filename(dialog, dialog_index)
mimetype = dialog_mimetype(dialog)
files = {"file-binary": (filename, audio_binary, mimetype)}
mediatype = dialog_mediatype(dialog)
files = {"file-binary": (filename, audio_binary, mediatype)}
headers = build_vfun_headers(api_key)
data = build_vfun_data(diarize, language)
response = requests.post(
Expand Down
24 changes: 22 additions & 2 deletions conserver/links/wtf_transcribe/test_wtf_transcribe_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
analysis_dialog_index,
analysis_is_wtf_transcription,
dialog_filename,
dialog_mimetype,
dialog_mediatype,
dialog_to_audio_binary,
dialog_to_binary,
dialog_to_index,
Expand Down Expand Up @@ -63,7 +63,27 @@ def test_analysis_and_dialog_helpers_cover_transcribed_and_recording_checks():
def test_file_and_remote_loaders_handle_success_and_failure():
assert is_file_url("file:///tmp/audio.wav") is True
assert dialog_filename({}, 3) == "audio_3.wav"
assert dialog_mimetype({}) == "audio/wav"
assert dialog_mediatype({}) == "audio/wav"


def test_dialog_mediatype_resolution_order():
# Modern adapters write "mediatype"; it takes priority.
assert dialog_mediatype({"mediatype": "audio/mpeg"}) == "audio/mpeg"

# Legacy "mimetype" is honoured when "mediatype" is absent.
assert dialog_mediatype({"mimetype": "audio/mpeg"}) == "audio/mpeg"

# "mediatype" wins over a legacy "mimetype" when both are present.
assert dialog_mediatype({"mediatype": "audio/mpeg", "mimetype": "audio/wav"}) == "audio/mpeg"

# Neither field present: derive from the filename extension.
assert dialog_mediatype({"filename": "clip.ogg"}) == "audio/ogg"

# A non-audio/video guess from the filename doesn't count; fall through to default.
assert dialog_mediatype({"filename": "clip.txt"}) == "audio/wav"

# Nothing at all gives the audio/wav default.
assert dialog_mediatype({}) == "audio/wav"

with patch("builtins.open", mock_open(read_data=b"file-bytes")):
assert maybe_load_file_url("file:///tmp/audio.wav") == b"file-bytes"
Expand Down
Loading