Open moritz1000 opened 4 years ago
I g uess both Volumio and Mopidy being music player daemons basically means they hog the audio ouput device and don't give things like raspotify a look in.
You should probably just install raspotify onto a fresh Raspbian and see how you go with that: https://github.com/dtcooper/raspotify
All of the display code we've got working thus far is Mopidy/mpd-centric and wont work with Raspotify. There surely must be a way to query Librespot for the currently playing song/album art/etc though.
Okay it looks like librespot (the grunt behind raspotify) supports an --onevent
option that lets you configure it with an endpoint for handling track start/change/stop events: https://github.com/librespot-org/librespot/issues/185
The event type and releated track IDs are saved to environment variables:
So all that remains - okay so this isn't exactly trivial - is to write a small Python handler that:
This all said. VolumIO should work- I guess you set up the DAC in the System settings? I think the missing link is the enable pin. If you can edit /boot/config.txt
then adding the line gpio=25=op,dh
should get you up and running.
Thank you for talking your time and writing such a detailed response.
I guess you set up the DAC in the System settings?
I looked for something called "dtoverlay=hifiberry-dac" as used for pirate audio but did not add gpio=25=op,dh
to /boot/config.txt
. There was a list of many different hifiberry dacs in VolumIO and I tried some of them.
First of all I will try to get Rasbian + raspotify + sound output working and then look into the rest but I guess this goes way over my head.
I've been curious about this too and just had a quick go at getting Spotify Connect playing through the Pirate Audo DAC via Raspotify.
Turns out all I needed to do run the "Easy" installation instructions as detailed in the Raspotify project page and then change the options in the config file to use the correct audio device, which in my case was:
OPTIONS="--device hw:1,0"
The LCD and buttons don't function doing it this way, but to anyone who just wants a basic way of controlling what Pirate Audio is playing via Spotify Connect, it doesn't seem like a bad solution. This also doesn't touch Mopidy, but this might be moot as what's playing can now be controlled from the Spotify client on any desktop or mobile device.
Did you add OPTIONS="--device hw:1,0"
in /etc/default/raspotify
? Thank you for testing this.
Hey, no probs. That's where I added it, yep.
+1
Does raspotify show the media info on the LCD?
Does raspotify show the media info on the LCD?
Sadly no. I still think that a real Spotify Connect integration would be so useful.
+1 I actually think this would be a great feature.
I bought a Pirate Audio device because I thought it supports Spotify Connect but it turns out I can stream Spotify to it but no via Spotify Connect.
Oh no, same happened to me. I thought it would support Spotify Conect, too. It would be awesome if it would.
I think full Spotify Connect integration is probably something that needs to be handled in Mopidy Spotify. See this- somewhat dated (and locked)- thread here: https://github.com/mopidy/mopidy-spotify/issues/14
This slightly more current thread suggests that Raspotify and Mopidy can potentially better co-exist by pausing MPD when Raspotify is active. Although this would not include any visual indication of what's playing on screen (as mentioned above)- https://discourse.mopidy.com/t/mopidy-spotify-connect-raspotify/2397
That's, at least, a good start but getting Raspotify "now playing" to co-exist on a screen that's actively being driven by Mopidy will be quite tricky.
I'm imaging a new SD card to set up Mopidy and Raspotify side-by-side to see how bad it is. Least I can do is some discovery on how much of a headache this might be. I'm a Spotify user myself and can appreciate the desire to "beam" music to it 😆
Nothing I do will be as slick as direct Mopidy integration though.
Welp! I just wasted about an hour discovering that Mopidy's mpd
plugin is no longer installed by default...
BUT I have come up with the most devious and contrived of scripts:
#!/bin/bash
if [ "$PLAYER_EVENT" = "start" ]; then
mpc clear
mpc add spotify:track:$TRACK_ID
mpc play
sleep 1
mpc pause
elif [ "$PLAYER_EVENT" = "change" ]; then
mpc clear
mpc add spotify:track:$TRACK_ID
mpc play
sleep 1
mpc pause
elif [ "$PLAYER_EVENT" = "stop" ]; then
mpc play
elif [ "$PLAYER_EVENT" = "volume_set" ]; then
mpc_volume=$(($VOLUME * 100 / 65536))
mpc volume $mpc_volume
fi
You will need to sudo apt install mopidy-mpd mpc
and test that mpc
is working by running mpc play
or something on the command line. It should show something like:
pi@raspberrypi:~ $ mpc play
Adagio - I'll Possess You
[playing] #1/1 0:00/5:48 (0%)
volume: 50% repeat: off random: off single: off consume: off
Now that's working I'll explain the ridiculous script above...
mpc clear
- clear the current playlistmpc add spotify:track:$TRACK_ID
- add the track currently being played by Raspotify to the Mopidy playlistmpc play
- PLAY the track so that it shows up on the LCDsleep 1
- Give it time to tick over and actually plaympc pause
- Pause it, so it doesn't play alongside the raspotify track and clash!You will need to put this somewhere like /home/pi/raspotify.sh
make sure you chmod +x /home/pi/raspotify.sh
and then:
sudo nano /etc/default/raspotify
And add: --onevent /home/pi/raspotify.sh
to your OPTIONS=""
line.
Mine looks like this because I'm playing audio out of the Pi's headphone jack in both cases:
OPTIONS="--device hw:1,0 --onevent /home/pi/raspotify.sh"
Now when you play a song via Spotify Connect, Raspotify will call this hook, add it to Mopidy, play it so that it shows on the LCD and then pause it. I've tested this with my Spotify account set up on a Pi 4 and it appears to work. In fact I can play on Mopidy and Raspotify simultaneously if I want a clashing horrible racket.
I'll type this up more coherently when it's not 11pm, but I feel I'm onto something here.
Edit: I have updated the script so that it syncs Raspotify volume changes over to Mopidy, alas I don't think it's possibly to sync the other way, but this does mean your Raspotify volume shows on the LCD.
It's not very robust! I've been trying to sync the playing track better... but it really doesn't work well. People who just want to use Spotify Connect and not Mopidy should speak up, because I suspect I can build something leaner that doesn't involve Mopidy at all. The big difficulty there is sourcing the album art and track information, however. Right now Mopidy (even for those who don't necessarily want to use it directly) is a really good way to grab full details from a spotify Track ID. Rolling that from scratch wont be so fun.
Thank you so much for your help @Gadgetoid !
I would'nt need Mopidy to be happy with my setup. :) Using my Raspi and the Pirate Audio as a Spotify Connect device with visible Track details and being able to control the volume etc. would be awesome for me.
Okay, take 2- no Mopidy edition. This is VERY BETA and needs A LOT OF WORK.
Right now it displays track info and album art (caching art locally) but does not show volume/transport information.
It will probably never support the buttons, since that requires a much more complex Spotify authentication context- but maybe that's feasible as a bigger, separate project. (I think this whole script could run totally separate of anything else and just use current_user_playing()
to get the currently playing song on a poll.)
You will need to (possibly incomplete):
Go to https://developer.spotify.com/dashboard and set up a new app. Grab your Client ID and Secret and keep them handy.
sudo pip3 install spotipy pidi_display_st7789
mkdir -p /home/pi/spotipy/cache/
touch /home/pi/spotipy/hook.log
sudo chown raspotify:raspotify/home/pi/spotipy/hook.log
sudo usermod -a -G gpio raspotify
sudo usermod -a -G spi raspotify
Make sure your /etc/default/raspotify
options contains --onevent /home/pi/spotipy/hook.sh
, here's mine:
OPTIONS="--device hw:1,0 --onevent /home/pi/spotipy/hook.sh"
Make sure you restart raspotify
:
sudo systemctl restart raspotify
Save this script in /home/pi/spotipy/hook.sh
#!/bin/bash
exec >> /home/pi/spotipy/hook.log
exec 2>&1
echo "Changing track: $TRACK_ID"
if [ "$PLAYER_EVENT" = "start" ]; then
echo $TRACK_ID > /tmp/spotify_album_display
elif [ "$PLAYER_EVENT" = "change" ]; then
echo $TRACK_ID > /tmp/spotify_album_display
elif [ "$PLAYER_EVENT" = "stop" ]; then
echo $TRACK_ID > /tmp/spotify_album_display
elif [ "$PLAYER_EVENT" = "volume_set" ]; then
sleep 0.1
fi
Save this script in /home/pi/spotipy/art.py
:
import time
import os
import requests
from pathlib import Path
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
import spotipy.util as util
import unittest.mock as mock
import argparse
import sys
FIFO_NAME = "/tmp/spotify_album_display"
class Display():
"""Base class to represent a pidi display output."""
# pylint: disable=too-many-instance-attributes
def __init__(self, args=None):
"""Initialise a new display."""
self._size = args.size
self._title = ''
self._shuffle = False
self._repeat = False
self._state = ''
self._volume = 0
self._progress = 0
self._elapsed = 0
self._title = ''
self._album = ''
self._artist = ''
def update_album_art(self, input_file):
"""Update the display album art."""
raise NotImplementedError
def update_overlay(self, shuffle, repeat, state, volume,
progress, elapsed, title, album, artist):
"""Update the display transport information."""
# pylint: disable=too-many-arguments
self._shuffle = shuffle
self._repeat = repeat
self._state = state
self._volume = volume
self._progress = progress
self._elapsed = elapsed
self._title = title
self._album = album
self._artist = artist
def redraw(self):
"""Redraw the display."""
raise NotImplementedError
def add_args(argparse): # pylint: disable=no-self-argument
"""Expand argparse instance with display-specific args."""
try:
os.unlink(FIFO_NAME)
except (IOError, OSError):
pass
os.mkfifo(FIFO_NAME)
os.chmod(FIFO_NAME, 0o777)
mock_display = mock.Mock()
mock_display.Display = Display
sys.modules["pidi.display"] = mock_display
import pidi_display_st7789
parser = argparse.ArgumentParser()
pidi_display_st7789.DisplayST7789.add_args(parser)
args = parser.parse_args()
args.size = 240
display = pidi_display_st7789.DisplayST7789(args)
auth_manager = SpotifyClientCredentials()
spotify = spotipy.Spotify(auth_manager=auth_manager)
with open(FIFO_NAME, "r") as fifo:
while True:
track_id = fifo.read().strip()
if len(track_id) == 0:
time.sleep(0.1)
continue
track = spotify.track(track_id)
image_url = None
album_id = track["album"]["id"]
album_name = track["album"]["name"]
artist_name = track["album"]["artists"][0]["name"]
track_name = track["name"]
for image in track["album"]["images"]:
if image["height"] == 300:
image_url = image["url"]
image_cache_path = Path(f"/home/pi/spotipy/cache/{album_id}.png")
if not image_cache_path.is_file():
print("Missing cached image, loading now..")
image = requests.get(image_url)
with open(image_cache_path, "wb") as f:
f.write(image.content)
display.update_album_art(image_cache_path)
display.update_overlay(
False,
False,
"play",
1,
0,
0,
track_name,
album_name,
artist_name
)
# Crossfade album art fudge
for x in range(30):
display.redraw()
time.sleep(1.0 / 30)
Now grab your Client ID and Secret from before and set them on your env, or save them into a script for easier setup:
export SPOTIPY_CLIENT_ID="YOUR CLIENT ID"
export SPOTIPY_CLIENT_SECRET="YOUR CLIENT SECRET"
Run art.py
to test:
python3 /home/pi/spotipy/art.py
The display should light up with whatever was last written to it.
Test that the front end works by firing a track ID into the fifo:
echo "2yLMuUJ8xXBOzdbRE5ZkJW" > /tmp/spotify_album_display
You should see the album art and track info update on Pirate Audio with the song Ember by Kubbi.
Now connect to Raspotify and change some tracks to see if it works.
That's awesome @Gadgetoid . I really appreciate your help.
To be honest the whole RPi, Linux, ssh etc. thing is fairly new to me. But I will definetely try to make it work and want to use the whole project to get a deeper understanding of everything.
What I did:
I've set up the RPi completely from scratch and installed Raspotify first. Afterwards I did all the steps you described. The only error I've encountered:
sudo chown raspotify:raspotify/home/pi/spotipy/hook.log chown: missing operand after ‘raspotify:raspotify/home/pi/spotipy/hook.log’ Try 'chown --help' for more information.
I can't connect through Spotify Connect when "--onevent /home/pi/spotipy/hook.sh" is added. When I add only OPTIONS="--device hw:1,0" I can connect properly. BUT: I can hear no audio through lineout.
Do I need to install audio/video drivers for the Pirate Audio which normally would be included in pirate-audio/mopidy? Before starting from scratch it worked fine with Raspotify and Mopidy combined.
Also, when I run art.py:
$ python3 /home/pi/spotipy/art.py Traceback (most recent call last): File "/home/pi/spotipy/art.py", line 80, in <module> display = pidi_display_st7789.DisplayST7789(args) File "/usr/local/lib/python3.7/dist-packages/pidi_display_st7789/__init__.py", line 21, in __init__ spi_speed_hz=args.spi_speed_mhz * 1000 * 1000 File "/usr/local/lib/python3.7/dist-packages/ST7789/__init__.py", line 124, in __init__ self._spi = spidev.SpiDev(port, cs) FileNotFoundError: [Errno 2] No such file or directory
Ah looks like you need to run sudo raspi-config
and enable SPI.
Edit: Done a lot of work on this, and new code incoming shortly. Was important to me that volume/transport at least kinda matched and that the album art crossfade worked properly.
Updated code replaces hook.sh
and art.py
documented above- https://github.com/pimoroni/pidi-spotify
For the time being you will have to run it like so:
git clone https://github.com/pimoroni/pidi-spotify
cd pidi-spotify
python3 -m pidi_spotify
It will need the same auth as documented above.
This has:
I have no idea how librespot/Raspotify volume works. We could do local volume control but it would not sync up with Spotify Connect volume... ie if you turn the volume down locally you'll never be able to turn it back up again.
I feel like the A/B X/Y buttons should do something but since they can't control Spotify at all... I'm really not sure what that should be.
Wow - I am really grateful and impressed how quickly you can hack something like this. I've already learned a lot today. I hope it is okay, to ask so many questions.
Is started with adding
dtoverlay=hifiberry-dac
gpio=25=op,dh
to /boot/config.txt. Afterwards I executed the following:
sudo pip3 install spotipy pidi_display_st7789
mkdir -p /home/pi/spotipy/cache/
touch /home/pi/spotipy/hook.log
sudo chown raspotify:raspotify /home/pi/spotipy/hook.log
sudo usermod -a -G gpio raspotify
sudo usermod -a -G spi raspotify
Then I added
OPTIONS="--device hw:1,0 --onevent /home/pi/spotipy/hook.sh"
and executed
sudo systemctl restart raspotify
Afterwards I executed:
cd pidi-spotify
python3 -m pidi_spotify
Somehow the Terminal gets unresponsive at this point
pi@raspberrypi:~/pidi-spotify $ python3 -m pidi_spotify
open 5
Also the problem persists, that I can't connect through Spotify Connect when "--onevent /home/pi/spotipy/hook.sh" is added. When I add only OPTIONS="--device hw:1,0" I can connect properly.
You're most of the way there! I should mention that to test pidi_spotify
you will need a second terminal Window, because the running application will block that terminal. You can exit with Ctrl+C. I haven't turned it into a system-service-like-thingy just yet.
I encountered the problem with adding a hook, where I wasn't able to log in. It could be that Raspotify cannot run hook.sh
Try: chmod +x /home/pi/spotipy/hook.sh
And make sure your hook.sh
is updated ot the new one included with pidi-spotify
.
Edit: I should clarify that chmod +x
sets the execute bit on that script, allowing Raspotify to run it.
Shouldn't the new script generate the new hook.sh? Or do I still have to sudo nano generate it?
Is it correct that "python3 -m pidi_spotify" kind of freezes with open 5?
pi@raspberrypi:~/spotipy $ chmod +x /home/pi/spotipy/hook.sh
chmod: changing permissions of '/home/pi/spotipy/hook.sh': Operation not permitted
You'll have to copy the "hook.sh" from the pidi-spotify
directory copied down with git.
So assumping you're somewhere like: /home/pi/pidi-spotify
you can cp hook.sh /home/pi/spotipy/hook.sh
And try: sudo chmod +x /home/pi/spotipy/hook.sh
... the Pi likes this simon-says nonsense 😆
(and yes python3 -m pidi_spotify
will kind of freeze with "open 5" ... I should probably have put a more helpful message in there before pushing it up for testing!)
oh boy! that did it. and it works really good. 😍
So at the moment the whole thing only works while "python3 -m pidi_spotify" is running, right?
This is what you meant when you wrote that you haven't turned it into a system-service-like-thingy just yet, right?
That's correct! Yes. I need to create a way to configure pidi_spotify and write a script to run it as a service.
You can probably create a script called /home/pi/spotipy/start.sh
and put something like this in it (changing values and/or directories as needed):
#!/bin/bash
cd /home/pi/pidi_spotify
export SPOTIPY_CLIENT_ID="YOUR CLIENT ID"
export SPOTIPY_CLIENT_SECRET="YOUR CLIENT SECRET"
python3 -m pidi_spotify
Then add it to your crontab by typing:
crontab -e
And then adding:
@reboot /home/pi/spotipy/start.sh
When it's finished I hope pidi_spotify
will just be a program you can run either as a system service, or as a utility to control that system service, so we can get rid of hook.sh
altogether and just have a command like pidi_spotify --raspotify
do everything that hook does.... in theory anyway! This is all an elaborate proof of concept.
Edit (again): thanks for running through these steps and trying this early, it really helps me identify the sticking points and build a better application. Thanks to you I've no doubt (once I've actually finished this thing anyway) a lot of people will be enjoying slick album art display soon!
Right I've added instructions to the PiDi Spotify repository. Any other questions/issues/suggestions/requests then please raise them in a new issue over there: https://github.com/pimoroni/pidi-spotify
For those wanting to use Raspotify/Spotify Connect with Mopidy then you will still need to use my first soution above which I will re-iterate here:
sudo apt install mopidy-mpd mpc
Then use the below script as your --onevent
hook for Raspotify, by editing /etc/default/raspotify
and making your options line look something like this:
OPTIONS="--device hw:1,0 --onevent /home/pi/raspotify.sh"
Save this as /home/pi/raspotify.sh
and ensure it's executable with sudo chmod +x /home/pi/raspotify.sh
:
#!/bin/bash
if [ "$PLAYER_EVENT" = "start" ]; then
mpc clear
mpc add spotify:track:$TRACK_ID
mpc play
sleep 1
mpc pause
elif [ "$PLAYER_EVENT" = "change" ]; then
mpc clear
mpc add spotify:track:$TRACK_ID
mpc play
sleep 1
mpc pause
elif [ "$PLAYER_EVENT" = "stop" ]; then
mpc play
elif [ "$PLAYER_EVENT" = "volume_set" ]; then
mpc_volume=$(($VOLUME * 100 / 65536))
mpc volume $mpc_volume
fi
pidi-spotify and raspotify are now all I need for Spotify on my pirate-audio-enabled Pi 2; that and shairport-sync for Airplay.
Thank you @Gadgetoid!
I bought a Pirate Audio device because I thought it supports Spotify Connect but it turns out I can stream Spotify to it but no via Spotify Connect.
I could not find a way to add Spotify Connect to Mopidy.
I tried achieving this with Volumio since it supports external displays and GPIO control via plugins and also Spotify Connect but I could not even get the sound output to work.
To summarize I think Spotify Connect would be a great feature but I am unable to get it working.