Search code examples
gochannellibvlctui

Implement previous with libvlc playback


I'm using libvlc (go binding) to play music in a TUI. Instead of using the media_list_player, which has Next and Previous methods, I followed the advice of this answer https://stackoverflow.com/a/44647523/4443226 to use the regular media_player and a loop:

import vlc
import time
my_list = ['vp1.mp3','happy.mp3']
instance = vlc.Instance()
player = instance.media_player_new()
playing = set([1,2,3,4])
for i in my_list:
    player.set_mrl(i)
    player.play()
    play=True
    while play == True:
        time.sleep(1)
        play_state = player.get_state()
        if play_state in playing:
            continue
        else:
            play = False

This has the benefit that I can get the index of the current song and I can get the position and duration of the current playing song.

I implemented it in Go, and one of the problems, is that I am unable to implement (effectively) Next and Previous song.

Part of the problem is that this playback loop must be in a separate goroutine than the UI thread. I use chan to send signals for stopping the goroutine and skipping the song.

func playAlbum(p *vlc.Player, a Album, l *tui.List, s *tui.StatusBar, done, next, prev chan struct{}) (err error) {
    playlist := make([]*vlc.Media, 0)
    for _, path := range a.Paths {
        media, err := vlc.NewMediaFromPath(path)
        // check eturn err
        playlist = append(playlist, media)
    }

    for idx := range playlist {
        p.SetMedia(playlist[idx])
        err = p.Play()
        // check return err

        status, err := p.MediaState()
        // check return err

    PlaybackLoop:
        for status != vlc.MediaEnded {
            status, err = p.MediaState()
            // continue with err

            l.SetSelected(idx) // TUI list of songs

            song := songStatus(a, l.Selected())
            s.SetPermanentText(song) // TUI status bar

            select {
            case <-next:
                break PlaybackLoop
            case <-prev:
                continue // TODO: implement previous
            case <-done:
                return err
            default:
                time.Sleep(50 * time.Millisecond)
            }
        }
    }
    return err
}

I am unable to implement Previous, because I can't just go back in the for loop.

Ideally I think I would like to use the libvlc media_list_player. However, if I can't get the song duration and length as well as the index of the song in the media_list, I would rather do this method.

If I must use the media_player instead, is there a better way to handle playback than using nested loops and channels? Something that can use previous?


Solution

  • Based on the comments and discussion with the OP It was determined that a linked list was probably the best route in trying to control how to go forward and backwards with a bit less effort. Since Go is being used and the modules that are being used require goroutines the linked list also would have to be safe to use within goroutines.