import { onMounted, onUnmounted, ref, toValue, watch } from "vue"; //#region src/client/composables/audio.ts const mimeTypes = { "audio/flac": ["flac", "fla"], "audio/mpeg": ["mp3", "mpga"], "audio/mp4": ["mp4", "m4a"], "audio/ogg": ["ogg", "oga"], "audio/aac": ["aac", "adts"], "audio/x-ms-wma": ["wma"], "audio/x-aiff": [ "aiff", "aif", "aifc" ], "audio/webm": ["webm"] }; const playerList = []; function useAudioPlayer(source, options = {}) { let player = null; let unknownSupport = false; const isSupported = ref(false); const loaded = ref(false); const paused = ref(true); const currentTime = ref(0); const duration = ref(0); const volume = ref(1); function initialize() { player = document.createElement("audio"); player.className = "audio-player"; player.style.display = "none"; player.preload = options.autoplay ? "auto" : "none"; player.autoplay = options.autoplay ?? false; document.body.appendChild(player); playerList.push(player); player.onloadedmetadata = () => { duration.value = player.duration; currentTime.value = player.currentTime; volume.value = player.volume; loaded.value = true; }; player.oncanplay = (...args) => { loaded.value = true; if (unknownSupport) isSupported.value = true; options.oncanplay?.bind(player)(...args); }; player.onplay = (...args) => { paused.value = false; options.onplay?.bind(player)(...args); }; player.onpause = (...args) => { paused.value = true; options.onpause?.bind(player)(...args); }; player.ontimeupdate = () => { if (isValidDuration(player.duration)) { const lastBufferTime = getLastBufferedTime(); if (lastBufferTime <= player.duration) { options.ontimeupdate?.bind(player)(lastBufferTime); currentTime.value = lastBufferTime; options.onprogress?.bind(player)(lastBufferTime, player.duration); } } }; player.onvolumechange = () => { volume.value = player.volume; options.onvolume?.bind(player)(player.volume); }; player.onended = (...args) => { paused.value = true; options.onend?.bind(player)(...args); }; player.onplaying = options.onplaying; player.onload = options.onload; player.onerror = options.onerror; player.onseeked = options.onseeked; player.oncanplaythrough = options.oncanplaythrough; player.onwaiting = options.onwaiting; isSupported.value = isSupportType(); player.src = toValue(source); player.load(); } function isSupportType() { if (!player) return false; let type = toValue(options.type); if (!type) { const ext = toValue(source).split(".").pop() || ""; type = Object.keys(mimeTypes).filter((type$1) => mimeTypes[type$1].includes(ext))[0]; } if (!type) { unknownSupport = true; return false; } const isSupported$1 = player.canPlayType(type) !== ""; if (!isSupported$1) console.warn(`The specified type "${type}" is not supported by the browser.`); return isSupported$1; } function getBufferedRanges() { if (!player) return []; const ranges = []; const seekable = player.buffered || []; const offset = 0; for (let i = 0, length = seekable.length; i < length; i++) { let start = seekable.start(i); let end = seekable.end(i); if (!isValidDuration(start)) start = 0; if (!isValidDuration(end)) { end = 0; continue; } ranges.push({ start: start + offset, end: end + offset }); } return ranges; } function getLastBufferedTime() { const bufferedRanges = getBufferedRanges(); if (!bufferedRanges.length) return 0; const buff = bufferedRanges.find((buff$1) => buff$1.start < player.currentTime && buff$1.end > player.currentTime); if (buff) return buff.end; return bufferedRanges[bufferedRanges.length - 1].end; } function isValidDuration(duration$1) { if (duration$1 && !Number.isNaN(duration$1) && duration$1 !== Number.POSITIVE_INFINITY && duration$1 !== Number.NEGATIVE_INFINITY) return true; return false; } function destroy() { player?.pause(); player?.remove(); playerList.splice(playerList.indexOf(player), 1); player = null; } onMounted(() => { initialize(); watch([source, options.type], () => { destroy(); loaded.value = false; paused.value = true; currentTime.value = 0; duration.value = 0; initialize(); }); }); onUnmounted(() => destroy()); return { isSupported, paused, loaded, currentTime, duration, player, destroy, play: () => { if (options.mutex ?? true) { for (const p of playerList) if (p !== player) p.pause(); } player?.play(); }, pause: () => player?.pause(), seek(time) { if (player) player.currentTime = time; }, setVolume(volume$1) { if (player) player.volume = Math.min(1, Math.max(0, volume$1)); } }; } //#endregion export { useAudioPlayer };