Search code examples
pythonvlcraspberry-pi4

Python VLC - Next track


I am writing a program in Python to run on a Raspberry Pi in order to control my Wurlitzer jukebox. The program current accepts the codes for "record selection" (A1, B1, C4, etc.), add those codes to a playlist, and executes the list. My issue is that once a song starts, I would like to be able to press a button ("Y" in the current code) to skip the currently playing song. I can't get this to work.

If I use "player.next()" I get an error: 'MediaPlayer' object has no attribute 'next'.

I tried to stop the player and restart it (thinking it would pick up the next song in the Playlist. This doesn't even stop the player.

I do not want to use subprocess if I can avoid it. I'd like to figure out a way within Python to do the skipping. How would one accomplish this?

import os, sys, csv, vlc, time, threading
from pynput.keyboard import Key, Listener

DefaultUSBPath="/media/pi"
PlayHistory="PlayHistory.csv"

#
# Declare variables
#
USBDrive = None
Action = None
Playlist = []
SelectionCount = []
Sel_char = None
#
# Find the USB Drive
#
for item in os.listdir(DefaultUSBPath):
    if os.path.isdir(os.path.join(DefaultUSBPath, item)):
        if USBDrive is None:
            USBDrive =  os.path.join(DefaultUSBPath, item)
        else:
            USBDrive = USBDrive + ";" + os.path.join(DefaultUSBPath, item)
if USBDrive is None:
    print ("Error(0) - No USB Drive detected")
    sys.exit()
elif ";" in USBDrive:
    print ("Error(1) - More than one USB Drive detected.")
    sys.exit()
#
# Adding to playlist - Returns directory contents and adds to playlist
#
def addplaylist(track):
    list = None
    if os.path.isdir(os.path.join(USBDrive, track)):
        files = [f for f in os.listdir(os.path.join(USBDrive, track)) if os.path.isfile(os.path.join(USBDrive, track, f))]
        for f in files:
            if list is None:
                list = os.path.join(USBDrive, track, f)
            else:
                list = list + ";" + os.path.join(USBDrive, track, f)
    else:
        print ("Error(2) - Selection is invalid")
    if list is not None:
        if ";" in list:
            list = list.split(";")
    else:
        print ("Error(3) - Selection has no media")
    return list
#
# MediaPlayer function
#
def MusicPlayer(P):
    global Playlist, player
    while len(Playlist) > 0:
        song=Playlist.pop(0)
        print("Song: ")
        print(song)
        player=vlc.MediaPlayer(song)
        player.play()
#
# Define keyboard actions
#
def on_press(key):
    global Action, Playlist, player
    try:
        Sel_char = int(key.char)
    except:
        try:
            Sel_char = str(key.char)
            Sel_char = Sel_char.upper()
        except:
            Sel_char = None
    if Sel_char == "Z":
        return False
    elif Sel_char == "Y":
        print("Skip")
        #player.next()     This line causes 'MediaPlayer' object has no attribute 'next'
        time.sleep(1)
        MusicPlayer(Playlist)
    elif type(Sel_char) == str:
        Action = Sel_char
    elif type(Sel_char) == int:
        Action = Action + str(Sel_char)
        print("Action: " + Action)
        Plist = addplaylist(Action)
        if Plist is not None:
            print("Added to playlist")
            Playlist.append(Plist)
            print(Plist)
            MusicPlayer(Playlist)
    else:
        pass
#
# Read keyboard input
#
with Listener(on_press=on_press) as listener:
    listener.join()
print ("")
print ("Have a nice day!")
print ("")
sys.exit()

Solution

  • The way you have it coded, I expect you'd have to stop it, remove the current media from the list, then re-start it.

    However you may be better off running with a media_list_player, see below for a bare bones version. Note, I'm on Linux and had to hack some code to get the key input, rather than using a specific library or spend time on it, but it should give you enough to work with.

    Edit

    I apologise, there is a much simpler method when using a media_list_player, although if you want finer control you should use the media_list_player where you control the list's index or for full control use a media_player_new() but that's beyond the scope of this question. (I'll leave the original answer below this code, as it may be useful)

    import vlc
    import time
    
    ## pinched from vlc for keyboard input
    import termios
    import tty
    import sys
    
    mymedia = ["vp.mp3","vp1.mp3","happy.mp3","V1.mp4"]
    
    def getch():  # getchar(), getc(stdin)  #PYCHOK flake
        fd = sys.stdin.fileno()
        old = termios.tcgetattr(fd)
        try:
            tty.setraw(fd)
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old)
        return ch
    ## end pinched code
    
    def jukebox():
        player.play_item_at_index(0)    
        while True:
            time.sleep(0.25)
            k = getch()
            if k == "n": #Next
                player.next()
            if k == "p": #Previous
                player.previous()
            if k == " ": #Pause
                player.pause()
            if k == "q": #Quit
                player.stop()
                return True
    
        player.next()
                
    vlc_instance = vlc.Instance('--no-xlib --quiet ') # no-xlib for linux and quiet don't complain
    player = vlc_instance.media_list_player_new()
    Media = vlc_instance.media_list_new(mymedia)
    player.set_media_list(Media)
    
    print("Welcome to Jukebox")
    print("Options - space = Play/Pause, n = Next, p = Previous, q = Quit")
    print("Media",mymedia)
    
    jukebox()
    

    Original code

    import vlc
    import time
    
    ## pinched from vlc for keyboard input
    import termios
    import tty
    import sys
    
    def getch():  # getchar(), getc(stdin)  #PYCHOK flake
        fd = sys.stdin.fileno()
        old = termios.tcgetattr(fd)
        try:
            tty.setraw(fd)
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old)
        return ch
    ## end pinched code
    
    
    vlc_instance = vlc.Instance()
    player = vlc_instance.media_list_player_new()
    mymedia = ["vp.mp3","vp1.mp3","happy.mp3"]
    Media = vlc_instance.media_list_new(mymedia)
    player.set_media_list(Media)
    for index, name in enumerate(mymedia):
        print("Playing:",name)
        player.play_item_at_index(index)    
        time.sleep(1)
        while player.get_state() != 6:
            time.sleep(1)
            k = getch()
            if k == "y":
                player.stop()
                break