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

Resume last played track now working! (especially for audiobooks) #47

Closed
Neponator opened this issue May 17, 2018 · 4 comments
Closed

Comments

@Neponator
Copy link

Neponator commented May 17, 2018

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:

  1. 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" &

  1. 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"

  1. 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).

  1. 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
  1. 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.

@MiczFlor
Copy link
Owner

Hi @Neponator

you started a good feature here, I haven't found the time to really test what you are aiming at. Still, some feedback (to keep your motivation up! We all want your feature!). When I have more time, I will join in the effort.

  • yes, if you want to keep the playlists intact after a reboot you can not use the tmp folder. So changing the path as you did is necessary.
  • there is a $ missing here? echo "$PATHDATA/../shared/playlists/$FOLDERNAME.m3u" > PATHDATA/../shared/lastplayed.txt the second PATHDATA should be $PATHDATA?
  • the best thing I can come up with without getting into the code and test it:

a) try to give each action in your script more time by adding sleep. Just for testing.

b) the bash rfid_trigger_play.sh creates a new playlist from the folder contents when it is run. Possibly that confuses the script? Timing wise? Try with commenting out the playlist creation?

c) sudo everything and then see if that does the trick... :/

@Neponator
Copy link
Author

Neponator commented May 19, 2018

You're my hero - a) already solved it - it's not only the time between calling the playlist and the track (min sleep 5) but also the time between calling track (goto x) and calling position (seek x) in the track, that requires a sleep 1, otherwise the requests confuse each other. It's running now - many thanks @MiczFlor ! I updated the code in post above. Will keep the issue open to create a manual that is easier to follow :-D

@Neponator Neponator changed the title Resume last played track (especially for audiobooks) Resume last played track now working! (especially for audiobooks) May 19, 2018
@MiczFlor
Copy link
Owner

Hi @Neponator
Glad it works and yes, please keep it open and a bit of documentation would be great.
I am a big fan of pull requests. They write you into the history of the phoniebox code base. And they open another thread to discuss suggestions and changes, see here for an example:
#42
From your github profile, I can't see how savy you are with git :) You might to read a bit

@MiczFlor
Copy link
Owner

I am closing this ticket, because of the launch of version 1.0 on master. If you have any issues, please try first if they are still happening on the latest version 1.0. If so, open another ticket, please.

The version 1.0 has not been tested on jessie

Version 1.0 is a major improvement (which will be full of bugs, for sure :) and I want to thank all the contributors and Phoniebox lovers with their input, pictures, bug fixes and suggestions.

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

No branches or pull requests

3 participants
@MiczFlor @Neponator and others