Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to detect when a song ends #43

Open
rufogongora opened this issue Oct 30, 2018 · 10 comments
Open

How to detect when a song ends #43

rufogongora opened this issue Oct 30, 2018 · 10 comments
Labels

Comments

@rufogongora
Copy link

Is there a way we can detect when a song ends?

I'm trying to create some sort of custom queue, as I couldn't find any way to modify the queue, any pointers as to how this can be achieved?

Thanks

@Areyana
Copy link

Areyana commented Oct 30, 2018

Maybe we can just subscribe to player state and check when we'll get new track with new track id or name.

@rufogongora
Copy link
Author

Maybe we can just subscribe to player state and check when we'll get new track with new track id or name.

The problem is I would like to play something before the next track starts

@Areyana
Copy link

Areyana commented Oct 31, 2018

Maybe we can just subscribe to player state and check when we'll get new track with new track id or name.

The problem is I would like to play something before the next track starts

I can suggest you to research example repo of app-remote. In this example you can find TrackProgress bar, that we can use for our needs.

@Brockify
Copy link

Brockify commented Feb 4, 2019

I'm really curious, is there any solution to this? Was hoping it was going to be relatively straight forward by subscribing to a state, but I'm starting to see it isn't that easy huh? 👎

@paulgoetze
Copy link

paulgoetze commented Mar 27, 2019

There is a similar issue for the web-playback-sdk: spotify/web-playback-sdk#35

A track seems to be ended if the position is 0 and if it is paused. However this can also appear in the beginning of a track, so you need to check whether it’s not this starting-state.
For me it works when using an intermediate trackWasStarted variable and checking this one in addition to the flags provided by the SDK (Kotlin code):

private var trackWasStarted = false

// get the player state
spotifyAppRemote.playerApi.subscribeToPlayerState().setEventCallback {
    handleTrackEnded(it)
}

// ...

private fun handleTrackEnded(playerState: PlayerState) {
    setTrackWasStarted(playerState)

    val isPaused = playerState.isPaused
    val position = playerState.playbackPosition
    val hasEnded = trackWasStarted && isPaused && position == 0L

    if (hasEnded) {
        trackWasStarted = false
        // ... do whatever you want to do if track ended
    }
}

private fun setTrackWasStarted(playerState: PlayerState) {
    val position = playerState.playbackPosition
    val duration = playerState.track.duration
    val isPlaying = !playerState.isPaused

    if (!trackWasStarted && position > 0 && duration > 0 && isPlaying) {
        trackWasStarted = true
    }
}

@Kilemonn
Copy link

I agree with @paulgoetze, an event is generated "most" times with the song that has ended along with the position set to 0.
But after extensive testing on my end there are scenarios where this specific event is never fired when auto play is enabled on Spotify and the player will move to the next auto-play song, without a specific event telling me that the previous song has end.

Have you come across this issue aswell @paulgoetze, or has your solution provided a suitable work around for your needs? Also during your tests has auto-play been enabled or disabled in the spotify app?

@paulgoetze
Copy link

@KyleGonzalez I also had issues when auto-play was enabled (see #90) and did not find a work-around for this. Also, I haven't tried whether the event is reliably generated when queued tracks end.

@mdelolmo
Copy link
Contributor

Maybe we can just subscribe to player state and check when we'll get new track with new track id or name.

The problem is I would like to play something before the next track starts

Hi, there's no specific functionality for this, but have you tried listening to Player State and checking track.duration - playbackPosition? If that difference is below certain threshold (1 sec?), you can probably achieve that functionality.

@JoshTechLee
Copy link

Maybe we can just subscribe to player state and check when we'll get new track with new track id or name.

The problem is I would like to play something before the next track starts

Hi, there's no specific functionality for this, but have you tried listening to Player State and checking track.duration - playbackPosition? If that difference is below certain threshold (1 sec?), you can probably achieve that functionality.

but how would one go about doing this without a timer running in parallel to sync playback position? cause it seems pretty janky to do it that way...

@niehusst
Copy link

niehusst commented Oct 1, 2020

I found a workaround for getting app-remote to do what you want at the end of a song, based on paulgoetze suggestion above. (granted it is a bit messy, and sometimes you'll hear another song play for a blip before the correct action kicks in)

spotifyAppRemote.playerApi.subscribeToPlayerState().setEventCallback { // it is PlayerState!
    setTrackWasStarted(it) // this func from paulgoetze above

    val isPaused = it.isPaused
    val position = it.playbackPosition
    val hasEnded = trackWasStarted && isPaused && position == 0L
    val songPlayingIsNotRight = it.track?.uri != /* expectedSong.uri or null if you want to stop */
            
    if (hasEnded) {
        trackWasStarted = false
        // update val for the expected song
        val nextSong = /* expectedSong.uri or null if you want to stop */
        if (nextSong == null) {
            // pause before Spotify autoplay starts a random song
            spotifyAppRemote.playerApi.pause()
        } else {
            spotifyAppRemote.playerApi.play( /* expectedSong.uri */ )
        }
    } else if(songPlayingIsNotRight && !it.isPaused) {
        /* Sometimes Spotify misses the end-of-song event, or something goes wrong with
         * playing the next song and Spotify starts playing a random song from autoplay.
         * To remedy this, we're just going to hammer app-remote w/ the correct command
         * until it gets it right.
         */
        val correctCurrSong = /* expectedSong.uri or null if you want to stop */
        if (correctCurrSong == null) {
            // pause the currently playing Spotify autoplay random song
            spotifyAppRemote.playerApi.pause()
        } else {
            spotifyAppRemote.playerApi.play(correctCurrSong.uri)
        }
    }
}
// ...
private fun setTrackWasStarted(playerState: PlayerState) {
    val position = playerState.playbackPosition
    val duration = playerState.track.duration
    val isPlaying = !playerState.isPaused

    if (!trackWasStarted && position > 0 && duration > 0 && isPlaying) {
        trackWasStarted = true
    }
}

This is not an exact substitute for reliable end-of-song event generation, but hopefully it will help someone struggling with this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants