React-native-track-player: How to implement playlist controls like loop, shuffle, and repeat?

Created on 17 Jan 2018  Â·  24Comments  Â·  Source: react-native-kit/react-native-track-player

Does this library come with an API to control how to music plays? I've read through the docs and since I wasn't able to find it, I would appreciate some suggestions as to how to implement this behavior. I am using redux and redux-saga to handle state/effects. I've thought about implementing this logic within the listeners whenever events are triggered. But a lot of the time the app will be in the background or the screen will be locked. Does the JS still execute in those cases? Still a bit new, Thanks.

Enhancement

Most helpful comment

Here's what I ended up doing. I have an event handler to get all of the track changed events. If repeat is turned on then it checks if the next track is the one we are repeating. If so it does nothing. If not, it will pause the player, then do 'skip' to the track to be repeated, then play again.

    TrackPlayer.registerEventHandler((data)=>{
        console.log("got event",data.type,data)
        TrackPlayer.getCurrentTrack().then((track_id)=>{
            this.currentIndex = this.tracks.findIndex(t => t.id === track_id)
        })

        if(data.type === 'playback-track-changed') {
            if(this.repeat) {
                //don't mess with anything if already on the track to repeat
                if(data.nextTrack === this.trackToRepeat) return
                if(!data.track && data.nextTrack && this.trackToRepeat === null) {
                    this.trackToRepeat = data.nextTrack
                } else {
                    TrackPlayer.pause()
                    TrackPlayer.skip(this.trackToRepeat).then(()=>TrackPlayer.play())
                }
            }
        }

All 24 comments

I've had the same question. I'm building an music player that should still loop even if it's in the background.

I've found this from Apple's iOS docs:

https://developer.apple.com/documentation/mediaplayer/mpremotecommandcenter/1649694-changerepeatmodecommand

It looks like we could update the RNTrackPlayer.update function to set a target for the changeRepeatModeCommand and changeShuffleModeCommand objects. This should make it repeat and shuffle. However, once we are getting these events, I don't know how to use them (yet).

Add an event listener whenever the track ends and then call play with the
songs I'd.. it works even in the bg

On Wed, May 2, 2018, 6:01 PM Josh Marinacci notifications@github.com
wrote:

I've had the same question. I'm building an music player that should still
loop even if it's in the background.

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/react-native-kit/react-native-track-player/issues/122#issuecomment-386135208,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AEpTRaWcSl4sO4xBloVCl2omZdRE2bGxks5tuiylgaJpZM4Rh5hX
.

It looks like the react native code is wrapping RNTrackPlayer which wraps MediaWrapper which wraps AudioPlayer from the vendor dir. This class calls AVPlayer. Unfortunately this class doesn't have shuffle. It's designed to only manage one media object at a time. There is an AVPlayerQueue class, but I'm guessing it would require updating a lot of code to use this.

https://developer.apple.com/documentation/avfoundation/avplayer

The queue is managed by ourselves, we will add those features eventually.

@Alanz2223 can you please share a snippet of code that you use?

Here's what I ended up doing. I have an event handler to get all of the track changed events. If repeat is turned on then it checks if the next track is the one we are repeating. If so it does nothing. If not, it will pause the player, then do 'skip' to the track to be repeated, then play again.

    TrackPlayer.registerEventHandler((data)=>{
        console.log("got event",data.type,data)
        TrackPlayer.getCurrentTrack().then((track_id)=>{
            this.currentIndex = this.tracks.findIndex(t => t.id === track_id)
        })

        if(data.type === 'playback-track-changed') {
            if(this.repeat) {
                //don't mess with anything if already on the track to repeat
                if(data.nextTrack === this.trackToRepeat) return
                if(!data.track && data.nextTrack && this.trackToRepeat === null) {
                    this.trackToRepeat = data.nextTrack
                } else {
                    TrackPlayer.pause()
                    TrackPlayer.skip(this.trackToRepeat).then(()=>TrackPlayer.play())
                }
            }
        }

I maintain queue on js side and feed tracks one by one when queue ended event fires. Not sure about performance yet though.

There are some issues issue with observing playback-queue-ended and feeding items one by one. On lock screen there is a noticeable blink until next item is supplied. And new item does not get current time on lock screen updated. Here is my asset update code:

    if (currentFile && !!currentFile.name) {
      await trackPlayer.remove(currentFile.name)
    }
    await trackPlayer.add(trackPlayerAsset)

Any ideas how to make it smoother? With native queue there are no such issues. I am testing on iOS only so far.

Moving making track addition and playing track before doing anything else (e.g. updating UI) did the trick.

@iThinker how were you able to fix the blinking issue on lock screen? I wasn't sure since the UI is managed by this package

@jdrorrer my code is ugly. But basically the issue was too much code doing unrelated stuff before playing next track.
Here is my current code:

const shouldRemoveCurrentFile = currentFile && !!currentFile.name
if (shouldRemoveCurrentFile && isAndroid) {
  await trackPlayer.remove(currentFile.name)
}

const trackPlayerAsset = {
  id: file.name,
  url: file.source,
  title: file.name,
  artist: ''
}
await trackPlayer.add(trackPlayerAsset)

if (autoplay) {
  await trackPlayer.play()
}

if (shouldRemoveCurrentFile && isAndroid == false) {
  await trackPlayer.remove(currentFile.name)
}

+1

I tried this to shuffle Queue

shuffleArray = (array) => {
        let currentIndex = array.length, temporaryValue, randomIndex;
        // While there remain elements to shuffle...
        while (0 !== currentIndex) {
            // Pick a remaining element...
            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex -= 1;
            // And swap it with the current element.
            temporaryValue = array[currentIndex];
            array[currentIndex] = array[randomIndex];
            array[randomIndex] = temporaryValue;
        }
        return array;
    };
    shuffle = async () => {
        const queue = await TrackPlayer.getQueue();
        const shuffledQueue = this.shuffleArray(queue);
        await TrackPlayer.destroy()
        await TrackPlayer.setupPlayer();
        await TrackPlayer.add(shuffledQueue);
        await TrackPlayer.play();
    };

@PiyushGaurav it will reset if we make the shuffle off again?

Currently I am able to add duplicate songs to queue. Is there a possibility to remove duplicates from queue without writing to write JavaScript code ?

Is it not better to handle queue functionality from JavaScript ?

+1 on getting loop/shuffle please.

Managing queue in JS means background play stops working (at least it does for me).

+1

+1

+1

+1

+1

+1

Was this page helpful?
0 / 5 - 0 ratings