Files
CodeNomad/packages/ui/src/lib/speech-playback-support.ts
2026-03-26 22:59:30 +00:00

59 lines
2.0 KiB
TypeScript

import type { SpeechCapabilitiesResponse } from "../../../server/src/api-types"
import type { SpeechPlaybackMode, SpeechTtsFormat } from "../stores/preferences"
export interface SpeechPlaybackSupportResult {
available: boolean
reason?: "unsupported-environment" | "provider-streaming-unavailable" | "browser-streaming-unavailable"
}
export function formatToMimeType(format: SpeechTtsFormat): string {
if (format === "wav") return "audio/wav"
if (format === "opus") return getSupportedMimeType(format)
if (format === "aac") return "audio/aac"
return "audio/mpeg"
}
export function getCandidateMimeTypes(format: SpeechTtsFormat): string[] {
if (format === "wav") return ["audio/wav"]
if (format === "opus") {
return ['audio/ogg; codecs="opus"', 'audio/webm; codecs="opus"', "audio/opus"]
}
if (format === "aac") return ["audio/aac", "audio/mp4", 'audio/mp4; codecs="mp4a.40.2"']
return ["audio/mpeg"]
}
export function getSupportedMimeType(format: SpeechTtsFormat): string {
const candidates = getCandidateMimeTypes(format)
if (typeof MediaSource === "undefined") {
return candidates[0]
}
return candidates.find((candidate) => MediaSource.isTypeSupported(candidate)) ?? candidates[0]
}
export function getSpeechPlaybackSupport(options: {
playbackMode: SpeechPlaybackMode
ttsFormat: SpeechTtsFormat
capabilities?: SpeechCapabilitiesResponse | null
}): SpeechPlaybackSupportResult {
if (typeof window === "undefined" || typeof window.Audio === "undefined") {
return { available: false, reason: "unsupported-environment" }
}
if (options.playbackMode !== "streaming") {
return { available: true }
}
if (!options.capabilities?.supportsStreamingTts) {
return { available: false, reason: "provider-streaming-unavailable" }
}
if (
typeof MediaSource === "undefined" ||
!getCandidateMimeTypes(options.ttsFormat).some((candidate) => MediaSource.isTypeSupported(candidate))
) {
return { available: false, reason: "browser-streaming-unavailable" }
}
return { available: true }
}