Search code examples
htmlamazon-s3sveltehtml5-audio

HTML audio element is disabled even though src is correct


I have an HTML audio element with src pointing to a valid URL, but the audio element is greyed out and disabled. Right next to the audio element, I have a link that goes to directly the src URL, which correctly points to the mp3 file on S3.

I can't figure out why the audio player is in a disabled state. It's implemented in Svelte code below. Happening in Chrome and Safari browsers.

How can I remove the disabled state from this audio player?

<script>
    import { onMount } from 'svelte';
    import { slide } from 'svelte/transition';
    import { BACKEND_URL } from '$lib/constants';
    import { isRtlLanguage } from '$lib/utils';
    import Ping from '$lib/components/loading/Ping.svelte';

    export let text = 'hello';
    export let language = 'English';

    let isLoading = true;
    let audioUrl;

    onMount(async () => {
        await handleSubmit();
    });

    async function handleSubmit() {
        isLoading = true;
        audioUrl = null;
        const response = await fetch(BACKEND_URL + 'text-to-speech', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ text, language })
        });
        const r = await response.json();
        audioUrl = r.outputUri;
        isLoading = false;
    }
</script>

<div in:slide out:slide class="flex {isRtlLanguage(language) ? 'justify-end' : ''}">
    {#if isLoading}
        <div class="flex space-x-3">
            <div class="pt-1">
                <Ping />
            </div>
            <div class="text-sm text-gray">Generating voice response...</div>
        </div>
    {:else if audioUrl}
        <div class="flex space-x-4">
            <div class="rounded-full border border-lightgray">
                <audio controls>
                    <source src={audioUrl} type="audio/mp3">
                </audio>
            </div>
            <div class="pt-4">
                <a href={audioUrl} target="_blank" class="hovertext">
                    <i class="bi bi-box-arrow-right"></i>
                </a>
            </div>
        </div>
    {/if}
</div>

enter image description here


Solution

  • The problem is fixed if I add a small delay between when the S3 audio file URL is generated and when I mount the audio player: setTimeout(() => {isLoading = false}, 2000);.

    This gives enough time for the audio URL to become accessible by the audio player.

    <script>
        import { onMount } from 'svelte';
        import { slide } from 'svelte/transition';
        import { BACKEND_URL } from '$lib/constants';
        import { isRtlLanguage } from '$lib/utils';
        import Ping from '$lib/components/loading/Ping.svelte';
    
        export let text = 'hello';
        export let language = 'English';
    
        let isLoading = true;
        let audioUrl;
    
        onMount(() => {
            handleSubmit();
        });
    
        async function handleSubmit() {
            isLoading = true;
            const response = await fetch(BACKEND_URL + 'text-to-speech', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ text, language })
            });
            const r = await response.json();
            audioUrl = r.outputUri;
    
            // give short delay until audio is ready.
            // this is to remedy a bug with the
            // S3 source on the audio player.
            setTimeout(() => {
                isLoading = false;
            }, 2000);
        }
    </script>
    
    <div in:slide out:slide class="flex {isRtlLanguage(language) ? 'justify-end' : ''}">
        {#if isLoading}
            <div class="flex space-x-3">
                <div class="pt-1">
                    <Ping />
                </div>
                <div class="text-sm text-gray">Generating voice response...</div>
            </div>
        {:else}
            <div class="rounded-full border border-lightgray">
                <audio controls>
                    <source src={audioUrl} type="audio/mp3" />
                </audio>
            </div>
        {/if}
    </div>