Hey,
I tried for the last week to implement a set of scripts that allows to resume the last played track and I finally made it work - the Jukebox now saves the last played playlist, song and position and resumes after reboot of the pi. It's not perfect since the song starts for a second before resuming to the last played position. I'm still trying to tweak that with mute/unmute or working with shorter sleep() in milliseconds.
My appraoch (code see below): The VLC which the RPi-Jukebox uses offers a XML file, that shows all information on the playing track (like artist, title, current position, current track number of the playlist). I took those information and saved it into separate text files - (artist and title for a display that I plan for the future) as well as the current track position and playlist number. Since the "download" of this xml is pretty slow (1-3 seconds), I only use it when pressing the button for "pause" or "shutdown".
Current track position and playlist number I use in combination with the VLC remote functions "goto x" and "seek x" (seek is already integrated in the code with seek 0 as a possibility to start the track from beginning). "goto x" works similarly, it makes vlc jump to the specific item on the playlist and plays it.
To make it work, the playlist cannot be temporary like in the original project, so I changed the path in the rfid_trigger_play.sh to RPi-Jukebox-RFID\shared\playlists\ (First I chose the Folder containing the mp3s itself, however that did not work as all the files in the folder are used to create the playlist, including the m3u's. So I added the subfolder. At the sametime I exported the path of the m3u to a textfile to use after reboot.
Now I have all I need, the playlist and the text files containing current position and current track.
I then created a bash-script that runs after boot of the pi, starts the VLC, loads the playlist, jumps to the current track and plays a specific position.
Now to my code:
- To make VLC produce the xml change in the rfid_trigger_play.sh the line
cvlc --no-video --network-caching=10000 -I rc --rc-host localhost:4212 "$PLAYLISTPATH" &
to
cvlc --no-video --network-caching=10000 -I rc --extraintf=http --http-password 123 --rc-host localhost:4212 "$PLAYLISTPATH" &
- When already in rfid_trigger_play.sh also change the playlist from temporary to permanent:
instead of
PLAYLISTPATH="/tmp/$FOLDERNAME.m3u"
set
PLAYLISTPATH="$PATHDATA/../shared/playlists/$FOLDERNAME.m3u"
(not sure if you need to create the folder "playlists" in ./shared first or if the script does it for you - if you want to be sure, create it first with mkdir playlists
.
Additionally we want to export the path of the playlist to a textfile, to be able to remember the last used playlist at start of the pi:
Still in rfid_trigger_play.sh add
echo "$PATHDATA/../shared/playlists/$FOLDERNAME.m3u" > $PATHDATA/../shared/lastplayed.txt
after
find
"$PATHDATA/../shared/audiofolders/$FOLDERNAME" -type f | sort -n > "$PLAYLISTPATH"
- This is my script to exploit the VLC xml,intended to be run when button for "shutdown" or "pause" are pressed (Created with
nano /home/pi/RPi-Jukebox-RFID/scripts/getCurrentSongDetails.py
)
#!/usr/bin/python3
#/home/pi/RPi-Jukebox-RFID/scripts/getCurrentSongDetails.py
from bs4 import BeautifulSoup
import urllib.request
def get_currentSongDetails():
password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
top_level_url = "http://127.0.0.1:8080/requests/status.xml"
password_mgr.add_password(None, top_level_url, '', '123')
handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
opener = urllib.request.build_opener(handler)
u = opener.open(top_level_url)
soup = BeautifulSoup(u, 'html.parser')
currents = []
for info in soup.select('info'): # Selects the info category
if info['name'] == 'title': # Selects the title inside the info category
currents.append(info.text) # Writes title into the currents-array
for playid in soup.select('currentplid'): # Selects the current playlist-number of the track
currents.append(playid.text) # Writes the playlist-number in the currents-array
for current in soup.select('time'): #Selects the current position in the track
currents.append(current.text) # Writes the current position into current-array
return (currents)
currentSongDetails = get_currentSongDetails()
#with open ("/home/pi/RPi-Jukebox-RFID/shared/latestSong.txt", "w") as text_file: #for putting on a #display - later!
# for (cur) in currentSongDetails[0]:
# text_file.write('{}'.format(cur))
with open ("/home/pi/RPi-Jukebox-RFID/shared/latestPlid.txt", "w") as text_file2:
for (cur) in currentSongDetails[1]:
text_file2.write('{}'.format(cur))
with open ("/home/pi/RPi-Jukebox-RFID/shared/latesttime.txt", "w") as text_file3:
for (cur) in currentSongDetails[2]:
text_file3.write('{}'.format(cur))
Relevant output are the the textfiles latestPlid.txt and latesttime.txt that contain the last played playlist-item and the track position. (I don't know why, but the VLC playlists start with track position 4 - so the first song of the playlist has PLID 4, the second has 5 and so on). Here I'm pretty sure python does not creates the textfiles latestPlid.txt, latesttime.txt and latestsong.txt in ./shared for you - better create them yourself beforehand via nano latestPlid.txt
(then CTRL-O and Enter to save, CTRL-X to close; same for latesttime.txt, latestsong.txt).
- Now my script that should run while/after booting the pi:
#!/usr/bin/env bash
#/home/pi/RPi-Jukebox-RFID/scripts/lastplayed.sh
sudo pkill vlc #necessary?
VLCPLAYS=$(</home/pi/RPi-Jukebox-RFID/shared/lastplayed.txt)
cvlc --no-video --network-caching=10000 -I rc --extraintf=http --http-password 123 --rc-host localhost:4212 "$VLCPLAYS" &
sleep 5
PLID=$(</home/pi/RPi-Jukebox-RFID/shared/latestPlid.txt)
echo "goto $PLID" | nc.openbsd -w 1 localhost 4212
sleep 1
SONGTIME=$(</home/pi/RPi-Jukebox-RFID/shared/latesttime.txt)
echo "seek $SONGTIME" | nc.openbsd -w 1 localhost 4212
The service I use to start the script lies in /etc/systemd/system/ and looks like this:
(Just copy the code into a new file via nano /etc/systemd/system/resumesong.service
then sudo systemctl daemon-reload
to inform the system that there's a new service, then sudo systemctl enable resumesong.service
to start the service.)
[Unit]
Description=Resume Last Song Service
After=network.target iptables.service firewalld.service rfid-reader.service
[Service]
WorkingDirectory=/home/pi/RPi-Jukebox-RFID
ExecStart=/home/pi/RPi-Jukebox-RFID/scripts/lastplayed.sh
KillMode=process
SendSIGKILL=no
[Install]
WantedBy=multi-user.target
- To save playlist, track and position in the track I run the getCurrentSongDetails.py (see No. 2) when play/pause or shutdown buttons are pressed. Theoretically you can run it also when pressing next and/or previous buttons, however knowing my daughter hitting next-button a hundred times to find her favorite song - i really don't know what happens if the "download" of the vlc-xml is requested with the same staccato...
To run the script fetching the data you need to change the gpio-buttons.py in the following way:
add in headline
import time
and change
def def_shutdown():
check_call("/home/pi/RPi-Jukebox-RFID/scripts/playout_controls.sh -c=shutdown", shell=True)
to
def def_shutdown():
check_call("/home/pi/RPi-Jukebox-RFID/scripts/getCurrentSongDetails.py")
time.sleep( 3 )
check_call("/home/pi/RPi-Jukebox-RFID/scripts/playout_controls.sh -c=shutdown", shell=True)
Also change
def def_halt():
call("/home/pi/RPi-Jukebox-RFID/scripts/playout_controls.sh -c=playerpause", shell=True)
to
def def_halt():
call("/home/pi/RPi-Jukebox-RFID/scripts/playout_controls.sh -c=playerpause", shell=True)
check_call("/home/pi/RPi-Jukebox-RFID/scripts/getCurrentSongDetails.py")
Don't forget to make the new scripts and textfiles accessible with sudo chmod +x <file>
!
Done!
I started python only a week ago - so most of my code is stolen or the result of trial and error - if you have any recommendations for simplifying it, let me know.