2

I have a Next.js project. I have an audio player, but when the page is left or reloaded audio stops playing. How can I make an audio play in the background? I thought iframe would be a great fit, but it asks me where I want to download my audio. Here's the player I have now:

const getCurrDuration = (e) => {
    const percent = ((e.currentTarget.currentTime / e.currentTarget.duration) * 100).toFixed(2)
    const time = e.currentTarget.currentTime

    setPercentage(+percent)
    setCurrentTime(time.toFixed(2))
}

const play = () => {
    const audio = audioRef.current
    if (typeof window !== "undefined") {
        audio.volume = localStorage.getItem('volume') ? localStorage.getItem('volume') : 1
        audio.playbackRate = localStorage.getItem('playbackRate') ? localStorage.getItem('playbackRate') : 1

        if (!isPlaying) {
            setIsPlaying(true)
            audio.play()
        }

        if (isPlaying) {
            setIsPlaying(false)
            audio.pause()
        }
    }
}

const startPlaying = () => {
    setIsPlaying(true)
    const audio = audioRef.current
    if (typeof window !== "undefined") {
        audio.volume = localStorage.getItem('volume') ? localStorage.getItem('volume') : 1
        audio.playbackRate = localStorage.getItem('playbackRate') ? localStorage.getItem('playbackRate') : 1
    }

    if (router.query.time !== null && router.query.time > 0) {
        const audio = audioRef.current

        if (audio) {
            audio.currentTime = router.query.time
            setPercentage(router.query.time)
            setCurrentTime(router.query.time)
            setTimeIsFromUrl(false)
        }
    }
}

const stopPlaying = () => {
    setIsPlaying(false)
}

const onPlayingCapture = () => {
    const audio = audioRef.current

    if (currentTime > 2) {
        localStorage.setItem(props.audio._id, currentTime)
    }
    if (props.time > 0 && props.time <= duration) {
        audio.currentTime = props.time

        props.setTimeToZero()
    } else if (props.time > 0 && props.time >= duration) {
        audio.currentTime = duration

        props.setTimeToZero()
    }
}

 <audio
    autoPlay={true}
    id="audio"
    onPlay={startPlaying}
    ref={audioRef}
    onPlayingCapture={onPlayingCapture()}
    onPause={() => stopPlaying()}
    onTimeUpdate={getCurrDuration}
    src={props.audio.filePath}
    onLoadedData={(e) => {
       setDuration(e.currentTarget.duration.toFixed(2))
    }}
/>

My question is: How can I play audio in the background, despite the that the page is reloaded or left?

juliomalves
  • 42,130
  • 20
  • 150
  • 146
  • you cannot auto play videos/audio. User need to interact with the web page before video/audio can be played. e.g. - https://developers.google.com/web/updates/2017/09/autoplay-policy-changes – Someone Special May 09 '21 at 10:54
  • You can however, make use of onMouseOver or onClick events to activate/play audio – Someone Special May 09 '21 at 10:59
  • 1
    @SomeoneSpecial I think he knows that part - but the issue is that the page keep reloading whenever a user clicks on a link because in the background the site just auto reloads the next page. – Russell Harrower Jul 22 '21 at 09:43
  • I have seen this in soundcloud.com. On searching I found this [How do soundcloud keep music playing on when navigating?](https://stackoverflow.com/questions/18479191/how-do-soundcloud-keep-music-playing-on-when-navigating) – kiranvj Mar 12 '23 at 09:24

1 Answers1

1

The solution i found was to create a layout with next.js, see the documentation.

https://nextjs.org/docs/basic-features/layouts

Then in the layout which is shared by all the app. I inject all i need shared audios in a template. (not instantiate new Audio() in a useRef).

This template is put in an useMemo. That make songs available in all pages because this part of DOM do not refresh all the time.

Finally in your components use selector as HTMLAudioElement(typescript type) with the target audio id then play() it in useEffect() or in an event handler function.

Pay attention that using useMemo template will display and charge all the source audio link in the browser DOM, in another hand it's ready to use by the browser.

  • But once the app is built, during each route change the Layout will reload right? – kiranvj Mar 12 '23 at 09:23
  • 1
    In my case useMemo in a layout is cashing the templates in the DOM. So the part of template do not disappear from the DOM when refresh or routing. You can see it when you open the inspector tool it's not blinking. If the web api of the browser already launch the playing song on precedent page and the source is still available in the DOM of the browser the music wont stop because this the browser api witch handle the playing. You will probably need a state management (context, or redux) to manage the isPlaying state and give a new instruction to the browser when need. – Cyril Vassallo CyrilV Mar 12 '23 at 09:50
  • 1
    const audios = useMemo(() => (
    ) ,[])
    – Cyril Vassallo CyrilV Mar 12 '23 at 09:58
  • 1
    I just checked, after the build on vercel it work for me. – Cyril Vassallo CyrilV Mar 12 '23 at 10:12