dupontgu / retro-ipod-spotify-client Goto Github PK
View Code? Open in Web Editor NEWThe software that powers the sPot: a 4th generation "Click Wheel" iPod with a full Spotify client.
License: Apache License 2.0
The software that powers the sPot: a 4th generation "Click Wheel" iPod with a full Spotify client.
License: Apache License 2.0
Hey, I saw your video on YouTube. Really cool project!
I remember my iPod mini fondly :) I remember flashing some alternative OS on it as a kid, probably breaking it with that, but that was so cool. I would write notes on it to cheat on a topography test ๐
So I wanted to take a look at your sPod interface. But I can't seem to connect to the Spotify API.
I set the spotipy variables correctly I think:
export SPOTIPY_CLIENT_ID=123 (not my real id)
export SPOTIPY_CLIENT_SECRET=abc (not my real secret)
export SPOTIPY_REDIRECT_URI=http://foo.bar
But getting stuck after pasting the redirect URL:
$ python spotifypod.py
Couldn't read cache at: .cache
Couldn't read cache at: .cache
Enter the URL you were redirected to: http://foo.bar/?code=AQB95fJ1ctCgK2sst8VCmMvxGQOdU878Ob6l9hHodiv54CICwbVFXw8ZLz00chlVDl34mCU-udeEdWSsrw5_rxW3RGVN45k-iMq-0AuBcLshVbIet-h6DROk17QiXrb8KbJR9IjeXGFjPCMBn2V4q_gOye-EXU6YxMbDKQI91PIHmsa-q54zs5sME8nMnANU26uCKYOCxg
Enter the URL you were redirected to: HTTP Error for GET to https://api.spotify.com/v1/me/player/devices returned 401 due to Permissions missing
Traceback (most recent call last):
File "/home/stan/Misc/retro-ipod-spotify-client/frontend/venv/lib/python3.8/site-packages/spotipy/client.py", line 245, in _internal_call
response.raise_for_status()
File "/home/stan/Misc/retro-ipod-spotify-client/frontend/venv/lib/python3.8/site-packages/requests/models.py", line 943, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: https://api.spotify.com/v1/me/player/devices
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "spotifypod.py", line 12, in <module>
from view_model import *
File "/home/stan/Misc/retro-ipod-spotify-client/frontend/view_model.py", line 16, in <module>
spotify_manager.refresh_devices()
File "/home/stan/Misc/retro-ipod-spotify-client/frontend/spotify_manager.py", line 129, in refresh_devices
results = sp.devices()
File "/home/stan/Misc/retro-ipod-spotify-client/frontend/venv/lib/python3.8/site-packages/spotipy/client.py", line 1633, in devices
return self._get("me/player/devices")
File "/home/stan/Misc/retro-ipod-spotify-client/frontend/venv/lib/python3.8/site-packages/spotipy/client.py", line 291, in _get
return self._internal_call("GET", url, payload, kwargs)
File "/home/stan/Misc/retro-ipod-spotify-client/frontend/venv/lib/python3.8/site-packages/spotipy/client.py", line 261, in _internal_call
raise SpotifyException(
spotipy.exceptions.SpotifyException: http status: 401, code:-1 - https://api.spotify.com/v1/me/player/devices:
Permissions missing, reason: None
Do you have any idea what could be wrong here? The .cache
file is being created and populated correctly as far as I can see.
For whatever reason when I run python3 spotifypod.py I get the error: No module named 'spotipy'
I was able to run spotifypod.py before and was even able to get spotify to recognize everything but for some reason now the spotipy module wont show up.
I am super confused and also fairly new to this so forgive my ignorance.
Thanks
Hey everyone,
I want to show everyone what I did to resolve some of the major issues I've been running into during the installation @dupontgu's amazing project.
Some background: I don't have an iPod or anything, and this was all done on my Raspberry Pi 4 in a PiBoy DMG. My plan for this is to be able to launch spotipy via RetroPie.
After going through the installation I tried running spotipy.py, but I ran into a couple of issues with the [Errno 98] Address in use. I went out of my mind thinking it was related to the UDP_PORT or even the OAuth port. Maybe it was the server address or redis? Nah, throw all of that out the window. This issue is because of a .cache not being found.
python3 spotifypod.py
Traceback (most recent call last):
File "spotifypod.py", line 12, in
from view_model import *
File "/home/pi/retro-ipod-spotify-client/frontend/view_model.py", line 17, in
spotify_manager.refresh_data()
File "/home/pi/retro-ipod-spotify-client/frontend/spotify_manager.py", line 159, in refresh_data
results = sp.current_user_saved_tracks(limit=pageSize, offset=0)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/client.py", line 1183, in current_user_saved_tracks
return self._get("me/tracks", limit=limit, offset=offset)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/client.py", line 291, in _get
return self._internal_call("GET", url, payload, kwargs)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/client.py", line 221, in _internal_call
headers = self._auth_headers()
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/client.py", line 212, in _auth_headers
token = self.auth_manager.get_access_token(as_dict=False)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/oauth2.py", line 481, in get_access_token
"code": code or self.get_auth_response(),
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/oauth2.py", line 436, in get_auth_response
return self._get_auth_response_local_server(redirect_port)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/oauth2.py", line 402, in _get_auth_response_local_server
server = start_local_http_server(redirect_port)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/oauth2.py", line 1300, in start_local_http_server
server = HTTPServer(("127.0.0.1", port), handler)
File "/usr/lib/python3.7/socketserver.py", line 452, in init
self.server_bind()
File "/usr/lib/python3.7/http/server.py", line 137, in server_bind
socketserver.TCPServer.server_bind(self)
File "/usr/lib/python3.7/socketserver.py", line 466, in server_bind
self.socket.bind(self.server_address)
OSError: [Errno 98] Address already in use
sebakitzing's post (#22) led me to the right path with this issue.
You can verify if the .cache folder exists by going to /retro-ipod-spotify-client/frontend and typing ls -a
I was able to authorize spotipy through Midori browser, but for some reason, the .cache file would not create. I looked around and found Perelin's solution for Spotipy's OAuth (https://github.com/perelin/spotipy_oauth_demo). With a little modification to the spotipy_oauth_demo, I was able to create a cache folder to the /home/pi/ folder:
Please note: you will have to add your own client ID and secret from your Spotify Developer Dashboard. You will also need to add http://localhost:8080 to your Redirect URIs in the dashboard. I will explain the scoping modifications in the next topic.
SPOTIPY_CLIENT_ID = 'XXXXXXX'
SPOTIPY_CLIENT_SECRET = 'XXXXXX'
SPOTIPY_REDIRECT_URI = 'http://localhost:8080'
SCOPE = "user-follow-read," \
"user-library-read," \
"user-library-modify," \
"user-modify-playback-state," \
"user-read-playback-state," \
"user-read-currently-playing," \
"app-remote-control," \
"playlist-modify," \
"playlist-read-private," \
"playlist-read-collaborative," \
"playlist-modify-public," \
"playlist-modify-private," \
"streaming," \
"user-follow-modify," \
"user-follow-read"
CACHE = '/home/pi/.spotipyoauthcache'
sp_oauth = oauth2.SpotifyOAuth( SPOTIPY_CLIENT_ID, SPOTIPY_CLIENT_SECRET,SPOTIPY_REDIRECT_URI,scope=SCOPE,cache_path=CACHE)
With this modification, a cache folder was successfully made! I then
sudo cp /home/pi/.spotipyoauthcache /retro-ipod-spotify-client/frontend/.cache
This will copy the cache folder into the folder to allow spotipypod.py to work. This will also bypass the "Enter the URL you were redirected to:" prompt.
Note: I use cp instead of mv because I wanted to have a backup of the cache folder in case of a disaster.
After copying this cache folder over, I ran into another issue where spotipypod.py cannot write into the cache folder due to permission issues. This can easily be resolved with
sudo chomod 777 /retro-ipod-spotify-client/frontend/.cache
This command opens the permission rights to the .cache folder.
After that, I was able to run spotipypod.py...into another issue.
Another issue occurred when running spotipypod.py, somewhere along the line of:
https://api.spotify.com/v1/me/following?type=artist&limit=50
spotipy.client.SpotifyException: http status: 403, code:-1 - https://api.spotify.com/v1/me/albums?limit=100&offset=0:
Insufficient client scope
This can be resolved by adding more scopes into spotify_manager.py
scope = "user-follow-read," \
"user-library-read," \
"user-library-modify," \
"user-modify-playback-state," \
"user-read-playback-state," \
"user-read-currently-playing," \
"app-remote-control," \
"playlist-modify," \
"playlist-read-private," \
"playlist-read-collaborative," \
"playlist-modify-public," \
"playlist-modify-private," \
"streaming," \
"user-follow-modify," \
"user-follow-read"
This fixed pretty much my scope issues.
This part was tricky. As a person of many many... many playlists, some tracks become unavailable or null. spotipypod.py doesn't know what to do with these null tracks and will end up in an error before even starting. I had to modify some code around spotify_manager.py to skip null tracks. Here are the instances and their code:
!!Oh yeah before doing this, make sure to fix the playlist order first according to HerrEurobeat's post (#26).
def get_playlist_tracks(id):
tracks = []
results = sp.playlist_tracks(id, limit=pageSize)
while(results['next']):
for _, item in enumerate(results['items']):
track = item['track']
if track is None:
continue
else:
tracks.append(UserTrack(track['name'], track['artists'][0]['nam$
results = sp.next(results)
for _, item in enumerate(results['items']):
if item['track'] is None:
continue
else:
track = item['track']
tracks.append(UserTrack(track['name'], track['artists'][0]['name'],$
return tracks
def refresh_data():
<--code skip--->
results = sp.current_user_playlists(limit=pageSize)
totalindex = 0 # variable to preserve playlist sort index when calling offset loop down below
while(results['next']):
offset = results['offset']
for idx, item in enumerate(results['items']):
if item['uri'] is None:
continue
else:
tracks = get_playlist_tracks(item['id'])
DATASTORE.setPlaylist(UserPlaylist(item['name'], totalindex, item['uri'], len(tracks)), tracks, inde$
totalindex = totalindex + 1
results = sp.next(results)
We're not done yet!
I kept running into an issue with the Xauth: xauth: timeout in locking authority file /home/pi/.Xauthority
. I was not sure what could be causing it, but I read that this was not really needed and safe to deleted. So I did, I ran sudo rm /home/pi/.Xauthority
I ran into another issue where when using startx, it will not launch and returns an error
Cannot Open /dev/tty0 (Permission Denied)
to resolve this, I had to use sudo chown pi /dev/tty0
. This works, but resets when after rebooting. So I created a script:
sudo chown pi /dev/tty0
sudo chown pi /dev/tty1
sudo chown pi /dev/tty2
sudo chown pi /dev/tty3
sudo chown pi /dev/tty4
sudo chown pi /dev/tty5
sudo chown pi /dev/tty6
sudo chown pi /dev/tty7
startx
The reason why I repeated this step all the way until tty7 was because of the virtual consoles. If you already have a startx console running, using startx again will create another virtual console, which is a different ttyX set. Setting chown for 8 tty will make it safe to load in any one of these. I know this is a bit of a mess, but I have not run into any issues as of yet. I am open for optimizations and improvements.
make sure to use sudo chmod +x [yourscript].sh
to allow running ./[yourscript].sh
!!Please note using sudo startx
will launch it in root, which will not launch spotipypod.py at all. It'll just give you a blank screen. Just remove sudo
and it should launch correctly.
After tackling these issues, I was able to load spotipypod.py at last.
Side note: During this process, I followed HerrEurobeat's other post (#24) on removing emoticons from playlist names so it doesn't error. I have a couple of playlists with this and for 'future-proofing this definitely did the job.
Cheers
I have been over this very carefully and my ipod just boots to a command line. I cant get it to run anything else. Anyone have any ideas?
Hello, I've been working on a pcb design. This design is untested and I'm unsure if works, this pcb cost around $40 - $50. The 3.7v to 5v might not work, I'm going to provide the gerber file if you'd like to take a look at it. I want this to be a starting point if anyone wants to modify it go for it!
First of all, @dupontgu thank you for this amazing project.
I have setup env vars but still getting this error
โฏ python3 spotifypod.py
Couldn't read cache at: .cache
Couldn't read cache at: .cache
Enter the URL you were redirected to: http://google.com
Enter the URL you were redirected to: no ints
Couldn't read cache at: .cache
Hi, Trying to get the project working on my Pi z W with waveshare screen.
I followed the instructions in the readme, but I had to install the older version of raspotify:
raspotify_0.31.8.1.librespot.v0.3.1-54-gf4be9bb_armhf
First issue, on step 12 this file does not exist
sudo nano /etc/default/raspotify
However it does seem raspotify is running because I can see it as a a device in my spotify app. But not sure how to edit the config now.
Second issue:
I get a black screen when the desktop loads, which then eventually crashes back to desktop. When i SSH in I get this:
xset: unable to open display ""
xset: unable to open display ""
When I try to run sudo -H -u pi python3 spotifypod.py I get this:
pi@pipod:~/retro-ipod-spotify-client/frontend$ sudo -H -u pi python3 spotifypod.py Traceback (most recent call last): File "/home/pi/retro-ipod-spotify-client/frontend/spotifypod.py", line 12, in <module> from view_model import * File "/home/pi/retro-ipod-spotify-client/frontend/view_model.py", line 1, in <module> import spotify_manager File "/home/pi/retro-ipod-spotify-client/frontend/spotify_manager.py", line 102, in <module> sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope)) File "/home/pi/.local/lib/python3.9/site-packages/spotipy/oauth2.py", line 332, in __init__ self.client_id = client_id File "/home/pi/.local/lib/python3.9/site-packages/spotipy/oauth2.py", line 94, in client_id self._client_id = _ensure_value(val, "client_id") File "/home/pi/.local/lib/python3.9/site-packages/spotipy/oauth2.py", line 70, in _ensure_value raise SpotifyOauthError(msg) spotipy.oauth2.SpotifyOauthError: No client_id. Pass it or set a SPOTIPY_CLIENT_ID environment variable.
I did set the IDs in /etc/xdg/openbox/environment
Not sure what to do next
Where you would usually find playlists, albums, and podcasts I want to make a new one called Pair Audio so when it's clicked it will run subprocess.run(["btaudio-connect"]) which will run a bash command in python that connects my headphones.
My headphones already connect upon boot, But sometimes they don't or they are connected to my phone so I wanted an easy option to connect my headphones. I already have the code displaying the option but can't get it to run when clicked, I have no experience in python just messing around and looking at how the other classes get processed.
Here's what I already got working:
with this code
When I click on it, it freezes the program and I have to restart and it's back to normal. Any ideas?
Hello when I start my pi up the display is black. I think I've looked every on the page and can't seem to find an answer
have any suggestions?
Hello, Firstly thanks for sharing this awesome project.
I receive the error below after trying to run python3 spotifypod.py
This error appeared after I entered the redirected URL
Can anyone point me in the right direction to get past this?
Traceback (most recent call last): File "spotifypod.py", line 543, in <module> app = tkinterApp() File "spotifypod.py", line 88, in __init__ tk.Tk.__init__(self, *args, **kwargs) File "/usr/lib/python3.7/tkinter/__init__.py", line 2023, in __init__ self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use) _tkinter.TclError: no display name and no $DISPLAY environment variable
Thanks
Hi, I have the following problem: when I run de comand sudo ./click to see if I recive feedback from the clickwheel I only recive when the menu button or the center button are pressed nothing happens with the others. Any suggestions?, thank you very much!
When I Spotifypod.py manually it in the terminal it just spams 'no ints' and I've looked on every issue and read the comments and I can't seem to find a solution for this problem any help im kind of a noob to all this.
After battling for a weekend I'm finally nearing the end of the software side of things.
The only thing that I cannot seem to get working is the search function.
I was originally having issues with actually playing any songs from my playlists until I modified the scope array in spotify_manager.py. Am I missing anything from my scope that is causing my search issues?
Here's my current scope:
scope = "user-follow-read," \
"user-library-read," \
"user-library-modify," \
"user-modify-playback-state," \
"user-read-playback-state," \
"user-read-currently-playing," \
"app-remote-control," \
"playlist-read-private," \
"playlist-read-collaborative," \
"playlist-modify-public," \
"playlist-modify-private," \
"streaming," \
"user-read-private"
I am able to play music from my playlists, and at this stage, I'm happy to just leave it at that, but if I could get the search function working too, that would be great.
Hey,
Unless I missed it, there is no open source license for the code?
If that's the case then the code is technically proprietary (I assume that's not what you want). I am not a lawyer but from what I know right now people can't legally modify, distribute, or use your code (incl. to make contributions) without your written consent each time (See this github doc).
Personally, I'd use the GNU GPL (so people can't take your code and create a closed source version) but you may prefer something else ๐
Hi everyone,
I noticed a very high CPU usage if I run just click
even without the UI. Did anyone else notice this and did anyone find a better solution?
So far I have tried to rewrite the click.c
code into a python class which could be used by the frontend directly, which was much worse performance wise.
Afterwards I have tried to adjust the sampling rate of the gpios but this resulted in only detecting up and down commands:
gpioCfgClock(8, 0, 0);
My latest effort was to make use of the pigpio daemon sudo pigpiod
(add this command to /etc/xdg/openbox/autostart
). And the following rewritten code but it does not seem to be any better.
// To compile on the pi (after installing pigpio):
// gcc -Wall -pthread -o click click.c -lpigpiod_if2 -lrt
#include <pigpiod_if2.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define CLOCK_PIN 23
#define DATA_PIN 17
#define HAPTIC_PIN 26
#define BIT_COUNT 32
#define PORT 9090
#define MAXLINE 1024
#define CENTER_BUTTON_BIT 7
#define LEFT_BUTTON_BIT 9
#define RIGHT_BUTTON_BIT 8
#define UP_BUTTON_BIT 11
#define DOWN_BUTTON_BIT 10
#define WHEEL_TOUCH_BIT 29
#define BUFFER_SIZE 3
#define BUTTON_INDEX 0
#define BUTTON_STATE_INDEX 1
#define WHEEL_POSITION_INDEX 2
// used to store the current packet
uint32_t bits = 0;
// used to store the previous full packet
uint32_t lastBits = 0;
uint8_t bitIndex = 0;
uint8_t oneCount = 0;
uint8_t recording = 0;
// indicates whether the data pin is high or low
uint8_t dataBit = 1;
uint8_t lastPosition = 255;
int pi = -1;
int hapticWaveId = -1;
char buttons[] = {
CENTER_BUTTON_BIT,
LEFT_BUTTON_BIT,
RIGHT_BUTTON_BIT,
UP_BUTTON_BIT,
DOWN_BUTTON_BIT,
WHEEL_TOUCH_BIT
};
// all valid click wheel packets start with this
const uint32_t PACKET_START = 0b01101;
int sockfd;
char buffer[BUFFER_SIZE];
char prev_buffer[BUFFER_SIZE];
struct sockaddr_in servaddr;
// helper function to print packets as binary
void printBinary(uint32_t value) {
for(uint8_t i = 0; i < 32; i++) {
if (value & 1)
printf("1");
else
printf("0");
value >>= 1;
}
printf("\n");
}
// parse packet and broadcast data
void sendPacket() {
if ((bits & PACKET_START) != PACKET_START) {
return;
}
for (size_t i = 0; i < BUFFER_SIZE; i++) {
buffer[i] = -1;
}
for (size_t i = 0; i < sizeof(buttons); i++) {
char buttonIndex = buttons[i];
if ((bits >> buttonIndex) & 1 && !((lastBits >> buttonIndex) & 1)) {
buffer[BUTTON_INDEX] = buttonIndex;
buffer[BUTTON_STATE_INDEX] = 1;
printf("button pressed: %d\n", buttonIndex);
} else if (!((bits >> buttonIndex) & 1) && (lastBits >> buttonIndex) & 1) {
buffer[BUTTON_INDEX] = buttonIndex;
buffer[BUTTON_STATE_INDEX] = 0;
printf("button released: %d\n", buttonIndex);
}
}
uint8_t wheelPosition = (bits >> 16) & 0xFF;
// send haptics every other position. too sensitive otherwise
if (wheelPosition != lastPosition && wheelPosition % 2 == 0) {
if (hapticWaveId != -1) {
wave_send_once(pi, hapticWaveId);
}
lastPosition = wheelPosition;
}
buffer[WHEEL_POSITION_INDEX] = wheelPosition;
if (memcmp(prev_buffer, buffer, BUFFER_SIZE) == 0) {
return;
}
printf("position %d\n", wheelPosition);
lastBits = bits;
sendto(sockfd, (const char *)buffer, BUFFER_SIZE,
MSG_CONFIRM, (const struct sockaddr *) &servaddr,
sizeof(servaddr));
memcpy(prev_buffer, buffer, BUFFER_SIZE);
}
// Function to set the kth bit of n
int setBit(int n, int k) {
return (n | (1 << (k - 1)));
}
// Function to clear the kth bit of n
int clearBit(int n, int k) {
return (n & (~(1 << (k - 1))));
}
void onClockEdge(int pi, unsigned int gpio, unsigned int edge, unsigned int tick) {
if (!edge) {
// only care about rising edge
return;
}
if (dataBit == 0) {
recording = 1;
oneCount = 0;
} else {
// 32 1's in a row means we're definitely not in the middle of a packet
if (++oneCount >= BIT_COUNT) {
recording = 0;
bitIndex = 0;
}
}
// in the middle of the packet
if (recording == 1) {
if (dataBit) {
bits = setBit(bits, bitIndex);
} else {
bits = clearBit(bits, bitIndex);
}
// we've collected the whole packet
if (++bitIndex == 32) {
bitIndex = 0;
sendPacket();
}
}
}
void onDataEdge(int pi, unsigned int gpio, unsigned int edge, unsigned int tick) {
dataBit = edge;
}
int main(int argc, char** argv){
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY;
pi = pigpio_start(0, 0);
// haptic waveform - just a simple on-off pulse
set_mode(pi, HAPTIC_PIN, PI_OUTPUT);
gpioPulse_t pulse[2];
pulse[0].gpioOn = (1<<HAPTIC_PIN);
pulse[0].gpioOff = 0;
pulse[0].usDelay = 8000;
pulse[1].gpioOn = 0;
pulse[1].gpioOff = (1<<HAPTIC_PIN);
pulse[1].usDelay = 2000;
wave_add_new(pi);
wave_add_generic(pi, 2, pulse);
hapticWaveId = wave_create(pi);
set_pull_up_down(pi, CLOCK_PIN, PI_PUD_UP);
set_pull_up_down(pi, DATA_PIN, PI_PUD_UP);
callback(pi, CLOCK_PIN, RISING_EDGE, onClockEdge);
callback(pi, DATA_PIN, EITHER_EDGE, onDataEdge);
while(1) {
};
pigpio_stop(pi);
}
Is there a better or more efficient way to read the serial data from our clickwheel?
On the one hand I am a bit concerned by the heat generated by the CPU even while not even running anything else but click
on the other hand I kind of want to avoid adding another component as such as an Arduino if it is not necessary.
Hey everyone,
I was holding off on posting here for a while, but I've banged my head against the wall for so long that I need a bit of guidance.
Like Guy, I had been working on a similar project since last summer, but decided to try to switch over to the PocketBeagle instead of a Raspberry Pi (mainly due to it's alleged support for very low power sleep modes).
Getting things like Wi-Fi/Bluetooth/SPI displays working on this was a battle and a half, but I managed to work through them. One thing I'm still stuck on, however, is the clickwheel code.
From what I can tell, he's got a few callback functions triggered to run whenever either the clock or data line changes, then does a bit of bit-banging. What I'm struggling with is the switch to the IOBB library to get similar behavior up and running on a PocketBeagle.
// Nate added these for PocketBeagle to make hack-y callback functions
int CURRENTCLOCKSTATE = 0;
int CURRENTDATASTATE = 0;
int PASTCLOCKSTATE = 0;
int PASTDATASTATE = 0;
iolib_init();
iolib_setdir(1, 28, DigitalIn); // Set clock pin GPIO13 (P1,28) as input
iolib_setdir(1, 26, DigitalIn); // Set data pin GPIO12 (P1,26) as input
// Back to Guy's code
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY;
// I also removed the pulses/haptic code, as I wasn't planning on integrating that
while(running) {
CURRENTCLOCKSTATE = (is_low(1, 28)) ? 0 : 1;
CURRENTDATASTATE = (is_low(1, 26)) ? 0 : 1;
//printf("Did clock state change?");
if(CURRENTCLOCKSTATE != PASTCLOCKSTATE) {
printf("Clock state changed!\n");
onClockEdge(28, CURRENTCLOCKSTATE); // Runs his original functions
PASTCLOCKSTATE = CURRENTCLOCKSTATE;
}
//printf("Did data state change?");
if(CURRENTDATASTATE != PASTDATASTATE) {
printf("Data state changed!\n");
onDataEdge(26, CURRENTDATASTATE); // Runs his original functions
PASTDATASTATE = CURRENTDATASTATE;
}
}
The idea is that I'm creating my own callback functions if the current data/clock pin state changes compared to the previous one. However, it perpetually reads the state of those pins as 0, which makes me think I'm just not polling correctly.
I'm not a programmer by trade (especially not this low level of programming), and unfortunately I've exhausted my list of resources for i2c reading/bit-banging.
Just a few notes on how I got things working, mostly based on what I have read in various issue threads. Credit to #22 #30
/boot/config.txt if you bought the screen from the tutorial
comment out hdmi_force_hotplug=1
#hdmi_force_hotplug=1
uncomment sdtv_mode
sdtv_mode=0
uncomment disable overscan
disable_overscan=1
also want to put in your wpa_supplicant.conf in the boot directory as well at this time
Install git:
Step 2.
Add sudo apt install git
Clone repo
Step 3
add git clone https://github.com/dupontgu/retro-ipod-spotify-client
before installing requirements
add env variables to bash profile
Step 8
sudo nano ~/.bash_profile
#!/bin/bash
[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && startx -- -nocursor
# Disable any form of screen saver / screen blanking / power management
xset s off
xset s noblank
export SPOTIPY_CLIENT_ID='your_SPOTIPY_CLIENT_ID'
export SPOTIPY_CLIENT_SECRET='your_SPOTIPY_CLIENT_SECRET'
export SPOTIPY_REDIRECT_URI='http://localhost:8080'
export DISPLAY=:0.0
pass env variables to spotify ipod script
Step 10
replace sudo -H -u pi python3 spotifypod.py &
with sudo -H -u pi --preserve-env=SPOTIPY_REDIRECT_URI,SPOTIPY_CLIENT_ID,SPOTIPY_CLIENT_SECRET python3 spotifypod.py &
Also need to compile click.c
cd /home/pi/retro-spotify-client/clickwheel
gcc -Wall -pthread -o click click.c -lpigpio -lrt
Post install
We need to generate a .cache file for the spotify token. I suggest doing this on a pi 3 or 4 and using VNC.
sudo apt install midori
git clone https://github.com/perelin/spotipy_oauth_demo
sudo apt install python python-pip
cd ~/spotipy_oauth_demo
pip install -r requirements.txt
modify spotipy_oauth_demo.py and add your client id and secret
run the oauth demo with python spotipy_oauth_demo.py
using Midori, navigate to http://localhost:8080, you should see a hyperlink that says login to spotify, click it, you will be redirected to spotify login, just login and accept the terms
cp ~/.spotipyautocache ~/retro-ipod-spotify/frontend/.cache
chmod 777 ~/retro-ipod-spotify/frontend/.cache
Run raspi-config
one more time to disable VNC from the interface menu, reboot and now you should be good.
As a side note, be careful with the ipod case being metal it is really easy to short the various solder points you have made. I lined mine with electrical tape to avoid it, in the future I plan on 3d printing something to hold the various components in place and keep them from shorting.
So I almost have everything fully working now. I am down to two issues.
Also, how to auto connect bluetooth on startup? I have to manually connect via terminal over SSH each time.
What a great project. I saw it featured on hackaday and have got it mostly setup and running locally (on my PC running uBuntu 20.04 via WSL2 in Windows).
When browsing playlist tracks, are you supposed to be able to hit the "play/pause" button a track (or hit next on the track item) to start playing that instead of whatever is currently playing?
When I do this nothing happens, it just pushes in the Now Playing view and the existing track that is currently playing will just continue. See GIF for demonstration of what I mean:
Just wondering if this is expected, or if it is indeed meant to start playing the selected track? I'm not using Raspotify as a Connect Device as I'm running local on my desktop for now. Just a standard Spotify Windows App (also tried with spotify web player), but I'm not sure if this should make a difference or not.
BTW, a couple of things I found along the way that might trip up others:
xev
to test what codes were used for my directional arrows and P / N which I assigned to previous/next.I have some hardware on order and will attempt a full build once it arrives :]
Thanks!
Do not take the following steps below as reference, since all these steps are probably not 100% ok. The right steps have been updated in the main Readme file!
Hi there, I am having a hard time trying to setup Openbox tu show the Tkinter output. Has anyone managend to configure this properly on a raspberry pi zero?
So far I have manage to get running a small 2." SPI display using fbcp-ili9341
This is what I have done so far:
Since I am using the lite version of raspbien, I installed some extra packages:
sudo apt install xorg
sudo apt-get install lightdm
In raspi-config: Console Autologin
In rc.local added before exit 0 the following line:
startx &
Configure xinitrc
sudo nano /etc/X11/xinit/xinitrc
Comment this line
. /etc/X11/xsession
and add this line to start openbox
exec openbox-session
Run "spotifypod.py" with autostart
sudo nano /etc/xdg/openbox/autostart
and add the following command to launch spotifypod.py
sudo -H -u pi python3 /somewhere/over/the/rainbow/spotifypod.py &
in sudo nano /etc/xdg/openbox/environment
I set all the variables needed to run spotifypod.py ( SPOTIPY_CLIENT_ID, SPOTIPY_CLIENT_SECRET,SPOTIPY_REDIRECT_URI)
As result I get the following:
X.Org X Server 1.20.4
X Protocol Version 11, Revision 0
Build Operating System: Linux 5.4.0-54-generic armv8l Raspbian
Current Operating System: Linux raspberrypi 5.10.11+ #1399 Thu Jan 28 12:02:28 GMT 2021 armv6l
Kernel command line: coherent_pool=1M 8250.nr_uarts=0 snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1 bcm2708_fb.fbwidth=320 bcm2708_fb.fbheight=240 bcm2708_fb.fbswap=1 smsc95xx.macaddr=HI:TH:ER:EC:DD:E9 vc_mem.mem_base=0x1ec00000 vc_mem.mem_size=0x20000000 console=ttyS0,115200 console=tty1 root=PARTUUID=2386ac2f-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
Build Date: 15 January 2021 02:03:57PM
xorg-server 2:1.20.4-1+rpt2+deb10u3 (https://www.debian.org/support)
Current version of pixman: 0.36.0
Before reporting problems, check http://wiki.x.org
to make sure that you have the latest version.
Markers: (--) probed, (**) from config file, (==) default setting,
(++) from command line, (!!) notice, (II) informational,
(WW) warning, (EE) error, (NI) not implemented, (??) unknown.
(==) Log file: "/var/log/Xorg.1.log", Time: Mon Feb 8 22:27:43 2021
(==) Using system config directory "/usr/share/X11/xorg.conf.d"
InitSPI done
Relevant source display area size with overscan cropped away: 320x240.
Source GPU display is 320x240. Output SPI display is 320x240 with a drawable area of 320x240. Applying scaling factor horiz=1.00x & vert=1.00x, xOffset: 0, yOffset: 0, scaledWidth: 320, scaledHeight: 240
Creating dispmanX resource of size 320x240 (aspect ratio=1.333333).
GPU grab rectangle is offset x=0,y=0, size w=320xh=240, aspect ratio=1.333333
All initialized, now running main loop...
Openbox-Message: Unable to find a valid menu file "/var/lib/openbox/debian-menu.xml"
** (process:627): WARNING **: 22:27:46.829: xdg-autostart.vala:125: Error: Error opening directory ?/root/.config/autostart?: No such file or directory
** Message: 22:27:46.838: xdg-autostart.vala:39: Processing /etc/xdg/autostart/at-spi-dbus-bus.desktop file.
** Message: 22:27:46.855: xdg-autostart.vala:94: Launching: /usr/lib/at-spi2-core/at-spi-bus-launcher --launch-immediately (at-spi-dbus-bus.desktop)
** Message: 22:27:46.858: xdg-autostart.vala:39: Processing /etc/xdg/autostart/xdg-user-dirs.desktop file.
** Message: 22:27:46.896: xdg-autostart.vala:94: Launching: xdg-user-dirs-update (xdg-user-dirs.desktop)
Failed to launch bus: Failed to connect to session busNo protocol specified
No protocol specified
All what I get is a black screen with a mouse pointer... In my PC I have managed to make it run and get to see the manues an navigate. With tthe RPI Zero, it looks like I am missing one or several pconfigurations to link Tkinter properly with openbox..
Any help would be very appreciated!!
You did a great job congratulations! , I was wondering if you can add connection schemes so maybe we can too.
About at my wits end here. Not sure where to go, here's my issue. My pi boots into a debian login screen. If I do enter my login information the prompt disappears and nothing else happens. This is all before I even login to the pi over ssh. When I run spotifypod.py, my output is as follows:
`
Traceback (most recent call last):
File "spotifypod.py", line 12, in
from view_model import *
File "/home/pi/retro-ipod-spotify-client/frontend/view_model.py", line 17, in
spotify_manager.refresh_data()
File "/home/pi/retro-ipod-spotify-client/frontend/spotify_manager.py", line 15
9, in refresh_data
results = sp.current_user_saved_tracks(limit=pageSize, offset=0)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/client.py", line 122
6, in current_user_saved_tracks
return self._get("me/tracks", limit=limit, offset=offset, market=market)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/client.py", line 291
, in _get
return self._internal_call("GET", url, payload, kwargs)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/client.py", line 221
, in _internal_call
headers = self._auth_headers()
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/client.py", line 212
, in _auth_headers
token = self.auth_manager.get_access_token(as_dict=False)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/oauth2.py", line 518
, in get_access_token
"code": code or self.get_auth_response(),
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/oauth2.py", line 473
, in get_auth_response
return self._get_auth_response_local_server(redirect_port)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/oauth2.py", line 439
, in _get_auth_response_local_server
server = start_local_http_server(redirect_port)
File "/home/pi/.local/lib/python3.7/site-packages/spotipy/oauth2.py", line 130
3, in start_local_http_server
server = HTTPServer(("127.0.0.1", port), handler)
File "/usr/lib/python3.7/socketserver.py", line 452, in init
self.server_bind()
File "/usr/lib/python3.7/http/server.py", line 137, in server_bind
socketserver.TCPServer.server_bind(self)
File "/usr/lib/python3.7/socketserver.py", line 466, in server_bind
self.socket.bind(self.server_address)
OSError: [Errno 98] Address already in use
I attached a picture of the debian screen as well. Sorry in advance if my code copy/paste is hard to read, this is literally my first github post haha. Thanks!!
`
As the title suggests. The ability to mix local music with the Spotify library would make for a fantastic addition.
Hello i have a question i want to use your nice project in my diy bluetooth boombox
But for that i want to use a clickable potentiometer (Incremental encoders( instead of the normal ipod wheel how can i do that
Thanks in Advance
I make the click.c file, and then run sudo ./click
, but I got nothing when touching or clicking the clickwheel.
I connect the clickwheel and the gpio as the picture in README.md, am I missing the steps, or the clickwheel I bought is broken
I have no idea to figure out this problem, please give some suggestions, thank you very much!
Hey, I've been working on this project for a while now and am almost all the way through. I have only two major issues left on the software side and wanted to see if anyone could help me solve them.
1: When I try to use the search feature I get the following error and on the frontend the screen in just left with "loading..."
requests.exceptions.HTTPError: 400 Client Error: Bad Request for url: https://api.spotify.com/v1/search?q=&limit=5&offset=0&type=track
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.9/threading.py", line 954, in _bootstrap_inner
self.run()
File "/usr/lib/python3.9/threading.py", line 892, in run
self._target(*self._args, **self._kwargs)
File "/home/pi/retro-ipod-spotify-client/frontend/view_model.py", line 155, in
spotify_manager.run_async(lambda: self.run_search(self.live_render.query))
File "/home/pi/retro-ipod-spotify-client/frontend/view_model.py", line 150, in run_search
self.live_render.results = spotify_manager.search(query)
File "/home/pi/retro-ipod-spotify-client/frontend/spotify_manager.py", line 418, in search
track_results = sp.search(query, limit=5, type='track')
File "/home/pi/.local/lib/python3.9/site-packages/spotipy/client.py", line 553, in search
return self._get(
File "/home/pi/.local/lib/python3.9/site-packages/spotipy/client.py", line 297, in _get
return self._internal_call("GET", url, payload, kwargs)
File "/home/pi/.local/lib/python3.9/site-packages/spotipy/client.py", line 267, in _internal_call
raise SpotifyException(
spotipy.exceptions.SpotifyException: http status: 400, code:-1 - https://api.spotify.com/v1/search?q=&limit=5&offset=0&type=track:
No search query, reason: None
That was me searching just 'q'
2: I can never fetch any of my playlists
I'm not sure what else to put here but the only lead I have are that, if I'm actively listening to a playlist while booting it up it will have it saved. I have no clue what is the problem here but it seems like there were a lot more steps that needed to be taken around the coding for the playlist function after looking at the comments next to them.
If anyone could help fill me in on anything I may be missing or doing wrong please let me know and thank you in advance!
Hi @dupontgu,
could you please enable the Discussions tab in the repository settings? Instead of creating new issues, this would be a better place to ask questions.
Got everything mostly working (even the clickwheel) but I get nothing but a blank screen after X11 starts.
While I'm usually fine using X11, this openbox stuff is all new to me and it just doesn't seem to be working.
If I startx manually I get the following:
** (process:879): WARNING **: 20:40:13.509: xdg-autostart.vala:125: Error: Error opening directory ?/home/pi/.config/autostart?: No such file or directory
** Message: 20:40:13.544: xdg-autostart.vala:39: Processing /etc/xdg/autostart/gnome-keyring-secrets.desktop file.
** Message: 20:40:13.550: xdg-autostart.vala:64: Not found in OnlyShowIn list, aborting.
** Message: 20:40:13.555: xdg-autostart.vala:39: Processing /etc/xdg/autostart/gnome-keyring-ssh.desktop file.
** Message: 20:40:13.559: xdg-autostart.vala:64: Not found in OnlyShowIn list, aborting.
** Message: 20:40:13.564: xdg-autostart.vala:39: Processing /etc/xdg/autostart/at-spi-dbus-bus.desktop file.
** Message: 20:40:13.584: xdg-autostart.vala:94: Launching: /usr/lib/at-spi2-core/at-spi-bus-launcher --launch-immediately (at-spi-dbus-bus.desktop)
** Message: 20:40:13.587: xdg-autostart.vala:39: Processing /etc/xdg/autostart/xdg-user-dirs.desktop file.
** Message: 20:40:13.630: xdg-autostart.vala:94: Launching: xdg-user-dirs-update (xdg-user-dirs.desktop)
** Message: 20:40:13.638: xdg-autostart.vala:39: Processing /etc/xdg/autostart/gnome-keyring-pkcs11.desktop file.
** Message: 20:40:13.640: xdg-autostart.vala:64: Not found in OnlyShowIn list, aborting.
2021-03-15 20:40:13 initInitialise: Can't lock /var/run/pigpio.pid
Any help is appreciated.
hi
so my issues at this juncture
so i end up running - python3 Spotifypod.py
and each time i will get the same series of errors
ERROR 1- it will say the access token has expired
so i take the steps to "https://accounts.spotify.com/authorize?client_id=....." in PI BROWSER - it returns the callback to the REDIRECT URL - and I can ENTER THE URL into the Spotifypod.py when it requests "what is the URL Spotify redirected you to"
I have a .CACHE file - and it reads logically - showing the Access Token, Refresh Token, Timeout, Scopes.
i have also generated an access token through "Curl -h Authorization" direct to spotify/api/token
this will deliver an access token as well - and i can replace the contents of the .cache file in FRONTEND folder and it will resolve the expired token issue
but again - within the hour i will experience the same error - "invalid access token"
ERROR 2 - once i am past the invalid token error - i will get an error that states i must declare my CLIENT ID
here is the error
"spotipy.oauth2.SpotifyOauthError: No client_id. Pass it or set a SPOTIPY_CLIENT_ID environment variable."
so set these AGAIN
export SPOTIPY_CLIENT_ID='XXXXX'
export SPOTIPY_CLIENT_SECRET='XXXXX'
export SPOTIPY_REDIRECT_URI='XXXXX'
so why does it forget each time i do the SPOTIFYPOD.PY?
ERROR 3 - tkinter cannot display on localhost:10.1
so i AGAIN enter the following
"export DISPLAY=:0.0"
and this resolves this issue
ERROR 4 - so now i run the Spotifypod.py
and the SPOT screen renders and i see the following - see screenshot
this is great - i finally get this SPOT to function and display
however when you navigate to the any of these items - for example PLAYLISTS - everything is EMPTY
there is nothing underneath
meanwhile - the Spotifypod.py is still running in PUTTY
and
these errors are generating
"return SinglePlaylistPage(self.playlists[index], self)
IndexError: list index out of range"
or other times
"no ints
no ints
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.7/tkinter/init.py", line 1705, in call
return self.func(*args)
File "spotifypod.py", line 454, in onKeyPress
onSelectPressed()
File "spotifypod.py", line 517, in onSelectPressed
page = page.nav_select()
File "/home/pi/retro-ipod-spotify-client/frontend/view_model.py", line 260, in nav_select
return self.page_at(self.index)
File "/home/pi/retro-ipod-spotify-client/frontend/view_model.py", line 384, in page_at
artist = spotify_manager.DATASTORE.getArtist(index)
File "/home/pi/retro-ipod-spotify-client/frontend/datastore.py", line 103, in getArtist
return pickle.loads(pickled_pl)
TypeError: a bytes-like object is required, not 'NoneType'
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.7/tkinter/init.py", line 1705, in call
return self.func(*args)
File "spotifypod.py", line 454, in onKeyPress
onSelectPressed()
File "spotifypod.py", line 517, in onSelectPressed
page = page.nav_select()
File "/home/pi/retro-ipod-spotify-client/frontend/view_model.py", line 260, in nav_select
return self.page_at(self.index)
File "/home/pi/retro-ipod-spotify-client/frontend/view_model.py", line 384, in page_at
artist = spotify_manager.DATASTORE.getArtist(index)
File "/home/pi/retro-ipod-spotify-client/frontend/datastore.py", line 103, in getArtist
return pickle.loads(pickled_pl)
TypeError: a bytes-like object is required, not 'NoneType'"
does anyone have any clues as to how to resolve the above ?
thanks in advance
paulo
Following the documentation I got it running in a few minutes (congrats on the documentation!) https://www.clockworkpi.com/ is a pi compute module based device with a 320x240 screen.
As you can see in the picture there probably will be some tweaks need to get it working properly. But just wanted to let you (and the community) know that I'm hacking away at this... You might see some patches coming from me if I manage to modify the code.
Greetings! I'm excited about this project and I'm attempting to ensure the software is running smoothly before starting on the hardware. This means I have the raspberry pi zero outputting display to a monitor, and I'm either testing using an attached keyboard or via SSH.
I followed the base README instructions including the following suggested steps:
.cache
file when launching "sPot" failed (see #30)export DISPLAY=:0.0
to .bash_profile
to stop it complaining about the xset
commands in the console ๐คทpreserve-env
argument to the autostart
command because ENV variables were not being set (see #21)In addition I have the following changes:
SPOTIPY_REDIRECT_URI
is set to http://127.0.0.1
with no port specified because I can't determine what else is correct.spotify_manager.refresh_devices()
with spotify_manager.refresh_data()
in view_model.py
as the instructions suggested, but found out that this caused "sPot" to take exactly 5 minutes to start at launch! SO - I commented out both commands for now.So these are already somewhat stated, but I've run into a couple of issues that I can't seem to resolve and I'm open to suggestion. They all seem to be related to authentication, but I'm really not certain.
spotify_manager.refresh_data()
, but this is not a long term solution. My guess is that this request has to time out before the interface displays eventually.spotifypod.py
from the desktop, I get the error "spotipy.oauth2.SpotifyOauthError: No client_id". I can't run from SSH at all.SPOTIPY
vars set by sourcing /etc/xdg/openbox/environment
manually. Even then, it doesn't seem to affect anything - I get the same client_id
error.I feel like I'm close to a correctly functioning program, but I'm just missing a detail or two. Any feedback here would be most assuredly appreciated. Cheers and many thanks to @dupontgu for the challenge!
First when I manually run spotifypod.py I get error
spotipy.oauth2.SpotifyOauthError: No client_id. Pass it or set a SPOTIPY_CLIENT_ID environment variable.
I already set up my variables and I can't get it to run, also i'm trying to get this to run on a raspberry pi zero w on a monitor as a test.
Another thing is I set this to autorun but it just boots into the terminal. Any help? This is my first project.
Edit: fixed it all i did was in issue #41 theres a program that Authenticates the client id and secret id
When I was done with the settings and i reset it takes me to a sign in to debian i'm using raspberry pi os lite, and i'm using ssh. This is my first ever project and I can't get this working. Any help?
Not really an issue with the code, just a question, if somebody explored this idea:
Does anybody know if you can fit the click wheel of a 4g classic into the front case of a 5g? Do these two share the same dimensions? I'm thinking that I might 3d print some kind of inner holder, but then I could use the 5g case with the buttons of the 4g.
Hello, So I am trying to get this going on my Pi 4b and I had everything going ok(ish) last night.
I was able to get the gui going and go into playlists, artists, albums, etc. when I tried clicking on a song the "now playing" screen would appear with the progress bar in the middle and it would kick back a 401 spotify api error.
I couldn't figure out what was causing that so I decided to start from scratch with a fresh sd card.
I reinstall everything, set everything up and get to the point I have gotten to before where i reboot my pi to the command line.
openbox starts up on reboot (I assume it doesnt launch spotifypod.py automatically because of the errors i am getting?) and I have to rightclick on the black screen and pull up the terminal. When i navigate to my /retro-ipod-spotify-client/frontend folder and run python3 spotifypod.py I see it fetch my tracks, artists, playlists, albums and new releases, it sees my device and then it kicks back this error:
File "spotifypod.py", line 13, in
from PIL import ImageTK, Image
ImportError: cannot import name 'ImageTK' from 'PIL' (/usr/lib/python3/dist-packages/PIL/init.py)
Did I install something in the wrong place? I am a bit confused as to where I should be installing some of the required software, specifically pigpio and pi-btaudio.
By the way, I am not using ssh. (some people have said that that can cause issues.
Thanks in advance for any guidance.
sudo apt-get update -y
sudo apt-get upgrade -y
sudo apt install python-setuptools python3-setuptools -y
sudo apt install python3-pip -y
sudo wget https://github.com/dtcooper/raspotify/releases/download/0.14.0/raspotify_0.14.0.librespot.20200130T014147Z.3672214_armhf.deb
sudo apt-get install python3-tk -y
sudo apt-get install redis-server -y
sudo apt-get install openbox -y
sudo apt install xorg -y
sudo apt-get install lightdm -y
sudo apt-get install x11-xserver-utils -y
sudo apt-get install git -y
cd ~
sudo dpkg -i raspotify_0.14.0.librespot.20200130T014147Z.3672214_armhf.deb
git clone https://github.com/dupontgu/retro-ipod-spotify-client
cd retro-ipod-spotify-client/frontend
pip3 install -r requirements.txt
git clone https://github.com/bablokb/pi-btaudio.git
cd pi-btaudio
sudo tools/install
wget https://github.com/joan2937/pigpio/archive/master.zip
unzip master.zip
cd pigpio-master
make
sudo make install
https://developer.spotify.com/dashboard/applications/
sudo raspi-config
Display Option > Screen Blanking > OFF / Disable
sudo nano ~/.bash_profile
'#!/bin/bash'
[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && startx -- -nocursor
# Disable any form of screen saver / screen blanking / power management
xset s off
xset s noblank
export SPOTIPY_CLIENT_ID='your_SPOTIPY_CLIENT_ID'
export SPOTIPY_CLIENT_SECRET='your_SPOTIPY_CLIENT_SECRET'
export SPOTIPY_REDIRECT_URI='http://localhost:8080'
export DISPLAY=:0.0
sudo nano /etc/X11/xinit/xinitrc
#!/bin/sh
# /etc/X11/xinit/xinitrc
# global xinitrc file, used by all X sessions started by xinit (startx)
# invoke global X session script
#. /etc/X11/Xsession'
exec openbox-session #-> This is the one that launches Openbox ;)`
sudo nano /etc/xdg/openbox/autostart
#cd /home/pi/retro-ipod-spotify-client/frontend/
#sudo -H -u pi --preserve-env=SPOTIPY_REDIRECT_URI,SPOTIPY_CLIENT_ID,SPOTIPY_CLIENT_SECRET python3 spotifypod.py &
#sudo /home/pi/retro-ipod-spotify-client/clickwheel/click &
cd /home/pi/retro-ipod-spotify-client/clickwheel
nano click.c
change #define DATA_PIN 25 to #define DATA_PIN 5
gcc -Wall -pthread -o click click.c -lpigpio -lrt
sudo chmod +x click
sudo nano /etc/xdg/openbox/environment
export SPOTIPY_CLIENT_ID='your_SPOTIPY_CLIENT_ID'
export SPOTIPY_CLIENT_SECRET='your_SPOTIPY_CLIENT_SECRET'
export SPOTIPY_REDIRECT_URI='http://localhost:8080'
sudo nano view_model.py
#spotify_manager.refresh_devices()
spotify_manager.refresh_data()
sudo nano /etc/default/raspotify
OPTIONS="--username XXXXXXXX --password XXXXXXXXX"
OPTIONS="--username [email protected] --password thisismypassword"
# The displayed device type in Spotify clients.
# Can be "unknown", "computer", "tablet", "smartphone", "speaker", "tv",
# "avr" (Audio/Video Receiver), "stb" (Set-Top Box), and "audiodongle".
DEVICE_TYPE="smartphone"
To obtain your device id you first have to go to https://developer.spotify.com/console/get-users-available-devices/
when you go the the website your ipod may not appear connect to it using the spotify app on the device list then retry the website and it should show up
cd /home/pi/retro-ipod-spotify-client/frontend/
nano spotify_manager.py
Go to line 173 using ctrl+shift+_ and type 173 You can comment out the entire refresh_device def and replace it with
def refresh_devices():
device = UserDevice('xxxxxxxxxxxxxxxxxxxxxxxxxxxxx','raspotify', True)
DATASTORE.setUserDevice(device)
Replace the XXXXXXX with your device id
Ex:
def refresh_devices():
device = UserDevice(โ123456789','raspotify', True)
DATASTORE.setUserDevice(device)
After that you want to go save and exit the file then
cd ~/.local/lib/python3.7/site-packages/spotipy/
nano client.py
Go to line 1734 using ctrl+shift+_
Paste this in:
data = {"device_ids": ["xxxxxxxxxxxxxxxxxxxxxxxxxx"], "play": force_play}
return self._put("me/player", payload=data)
Replace the xxxxxx with your device id
sudo apt install midori
cd ~
git clone https://github.com/perelin/spotipy_oauth_demo
cd ~/spotipy_oauth_demo
sudo apt-get install python-pip python-dev
sudo apt-get install python3-pip python-dev
pip install -r requirements.txt
Before running it modify the client id and secret also your scopes in with:
nano spotipy_oauth_demo.py
SCOPE = "user-follow-read," \
"user-library-read," \
"user-library-modify," \
"user-modify-playback-state," \
"user-read-playback-state," \
"user-read-currently-playing," \
"app-remote-control," \
"playlist-modify," \
"playlist-read-private," \
"playlist-read-collaborative," \
"playlist-modify-public," \
"playlist-modify-private," \
"streaming," \
"user-follow-modify," \
"user-follow-read"
Now you can save and exit the file
sudo raspi-config
system options --> boot / auto login --> console autologin
python spotipy_oauth_demo.py
Recommend using a Pi 4 or anything faster than a zero w, but if this is the only pi you have you should be fine but it will take a while. Your pi should be displaying a black screen then right click and click on browser then go to internet/ browser (midori). Once you did that go to http://localhost:8080/ There should be a spotify sign in page, sign in, once signed in it should redirect you to a page with gibberish code and spotify credits. Once you're on that screen exit the page and in your terminal stop running the program.
cp ~/spotipy_oauth_demo/.spotipyoauthcache ~/retro-ipod-spotify-client/frontend/.cache
chmod 777 ~/retro-ipod-spotify-client/frontend/.cache
Once your done you can turn off vnc
If you get ERNO98 already in use go to issue 30
ssh into the pi and edit the config file
sudo nano /boot/config.txt
Add the code below:
hdmi_group=2
hdmi_mode=87
hdmi_cvt=320 240 60 1 0 0 0
hdmi_force_hotplug=1
dtoverlay=vc4-kms-v3d
Then save it, after that download the display code:
sudo apt install cmake git
cd ~
git clone https://github.com/juj/fbcp-ili9341.git
cd fbcp-ili9341
Then we want to change the native resolution to take advantage of the whole display.
nano st7735r.h
Go to line 18 which is ST7789
Change DISPLAY_NATIVE_HEIGHT from 240 to 320
After that save it and then nano into st7735r.cpp
nano st7735r.cpp
Go to line 95 with ctrl+shift+_
Comment out the line beginning with SPI_TRANSFER with // then below it add this:
SPI_TRANSFER(0x37, 0, 0);
Then exit the file then run this command:
mkdir build
cd build
cmake -DST7789=ON -DGPIO_TFT_DATA_CONTROL=24 -DGPIO_TFT_RESET_PIN=25 -DSPI_BUS_CLOCK_DIVISOR=30 -DSTATISTICS=0 -DDISPLAY_BREAK_ASPECT_RATIO_WHEN_SCALING=ON -DUSE_DMA_TRANSFERS=OFF ..
Once finished run:
make -j
After that is finished we want to make it run on boot:
sudo nano /etc/rc.local
Enter this before exit:
sudo /home/pi/fbcp-ili9341/build/fbcp-ili9341 &
sudo nano /etc/xdg/openbox/autostart
cd /home/pi/retro-ipod-spotify-client/frontend/
sudo -H -u pi --preserve-env=SPOTIPY_REDIRECT_URI,SPOTIPY_CLIENT_ID,SPOTIPY_CLIENT_SECRET python3 spotifypod.py &
sudo /home/pi/retro-ipod-spotify-client/clickwheel/click &
For bluetooth I these instructions:
To install blue-alsa link
To set up auto connect link
Please only follow the configuration instructions you already downloaded it in the previous link. link
Trying to recreate the Bluetooth settings (If you want to try and figure it out, I suggest going to #66 near the bottom there should be some links doris put)
If you have any issues please feel free to comment them below ๐
Couldn't have done this without doris, they've helped me out so much.
https://hackaday.io/project/177034-spot-spotify-in-a-4th-gen-ipod-2004
https://github.com/dupontgu/retro-ipod-spotify-client
https://www.youtube.com/watch?v=KciKqGX8g94
#41
#22
#34
#65
#23
#66
http://rsflightronics.com/spotifypod
https://sigmdel.ca/michel/ha/rpi/bluetooth_n_buster_01_en.html
https://github.com/dtcooper/raspotify/wiki/Play-via-Bluetooth-Speaker
https://github.com/bablokb/pi-btaudio
So it appears they dropped support for the raspberry pi zero. So that step does not work. Can someone help me solve this?
Hello I've finally got Spot to auto start and a bunch of other problems fixed but when I try to play a song it gives me this error
"playing spotify:album:6Bp2ojsBc8CtNaYipdxCLt spotify:track:3ZcLmonVVD1MTQtmQb8yf3
error! no devices"
I saw in the Github issue #4 someone had this error but they fixed it so I applied the fix to mine and it didn't do anything. Got a solution or an idea?
You are wondering on how to get a SPI display to work? Yeah me too so I hope this quick guide will help you.
I did this using a Waveshare 320x240 LCD IPS screen. This is the pinout I used which is provided by Waveshare themselves.
I'm sorry if an issue isn't the right place but I figured most people with this question would see it here first. If it fits into the README (as a shortened version) I would gladly open a pull request.
If I'm missing something or got something wrong please comment down below.
Stuff on your computer:
First of all open config.txt
which is located in the boot
partition while having the SD card mounted on your computer.
I didn't need to follow these few steps which tell you to remove any dtoverlay
or dtparam=spi=on
parameter as I didn't have them in my config.
As described here, add these lines to your config.txt
(or uncomment and modify the existing ones but hdmi_cvt
didn't exist for me yet):
hdmi_group=2
hdmi_mode=87
hdmi_cvt=320 240 60 1 0 0 0
hdmi_force_hotplug=1
Note: You will need to change the first two
hdmi_cvt
values based on the resolution of your screen. As I mentioned already, I used a 320x240 screen in this example.
Stuff on the pi:
You can now eject your SD card and boot the pi.
When you are in a terminal (for example using ssh), follow these steps as documented here:
sudo apt install cmake git
cd ~
git clone https://github.com/juj/fbcp-ili9341.git
cd fbcp-ili9341
Open st7735r.h
using a text editor:
nano st7735r.h
Go to line 18 which defines the values for the controller the display in this example uses: ST7789
Change DISPLAY_NATIVE_HEIGHT
from 240
to 320
as described here.
Then save (CTRL+S) and exit (CTRL+X).
Yes, in my experience you need to change height and not width even though the width should be 320. Idk, this is a bit weird but maybe I just don't understand it correctly.
Now we need to compile the driver.
Create a build folder:
mkdir build
and open it:
cd build
If you have used the same pinout as in the image in the beginning then these values should be right.
If not then you need to change TFT_DATA_CONTROL
and TFT_RESET_PIN
. Remember that these numbers are the GPIO pin names and not counted. Image to see which GPIO Pin has which number
Note: The
BUS_CLOCK_DIVISOR
is not a pin but a value that defines the speed of the clock together with the value60
we set in theconfig.txt
. I don't really understand this value myself but I read that most people recommend using 30 here first before changing it and it worked for me just fine.
cmake -DST7789=ON -DGPIO_TFT_DATA_CONTROL=25 -DGPIO_TFT_RESET_PIN=27 -DSPI_BUS_CLOCK_DIVISOR=30 -DSTATISTICS=0 -DUSE_DMA_TRANSFERS=OFF ..
Since I had to mount my display with the ribbon cable on the right side, so basically upside down from the original orientation the manufacturer intended, I had to flip the image by including this option before the two dots at the end: -DDISPLAY_ROTATE_180_DEGREES=ON
Now wait for that to finish and then type:
make -j
When that is done we are almost done. You can test the driver now by running sudo ./fbcp-ili9341
but since the driver should start automatically on boot we have to do one last change.
Open /etc/rc.local
with a text editor with sudo privileges:
sudo nano /etc/rc.local
Add these lines before exit
:
# Start display driver
/home/pi/fbcp-ili9341/build/fbcp-ili9341 &
Thats it!
I hope this helped you to figure out on how to get your display running. I had many questions for a few days but these were all the steps I had to take to get my display up and running. I credited every source where I got my information from in this post.
(I hope I didn't forget anything, wasn't the only one having this problem and that it works for you.)
Hi all,
what are the minimum scopes that are required for this codebase?
it looks like it requires more than
scope = "user-library-read,user-follow-read"
Insufficient client scope, reason: None
Thanks
Nick
the schematic posted on the github was SUPER useful
i am a novice to the soldering aspect of this project
in the attached image - my adafruit powerboost overheats - the little 6R8 component gets wickedly hot
then the power boost low power light flickers
and i am never able to get the screen nor the PI to actually turn on
in the photo
you will see that the white and black cables from the charger to the power boost
have been cut
obviously when connected
the overheating occurs
what am i missing in my attempt to mimic the schematic?
do i have some polarity issue
am i using a proper charger (using a different usb charger than in dupontgu's setup)
sincerely
and thanks in advance to all the comments and such here
wonderful project
paulo
Hi there. After following some guides from other posts, I have gotten to the part where you need to let the Spotipy program have access to my Spotify account. After loading up a VNC and going to the localhost:8080 site, and allowing it access to my account, it takes me to a site that Midori cannot load. I was told to paste this into the console, but there is no prompt to do so, and nothing happens when I do. Any leads? Here is a screenshot of after I authorize it through spotify:
First of all thank you for your intersting project.
What do you think to modify your source code about .py scritps to have external strings that enable a multilanguage project?
Thanks.
So, I've spent more nights than I should have working on this, my first Raspberry Pi project.
Here are the things I've done following the README file and trying to make it work for myself...
For the ones that don't know, you can code everything from your PC/Mac terminal and you can copy/paste the codes. I didn't know for the first days.
Details on what works and what doesn't at the end of this...
So:
Install Raspberry Pi OS Lite to your Pi and then configure your WiFi on first boot
Install Updates
sudo apt-get update
sudo apt-get upgrade
Install Dependencies
sudo apt install python-setuptools python3-setuptools
sudo apt install python3-pip
sudo curl-sL https://dtcooper.github.io/raspotify/install.sh | sh
sudo apt install install xorg
sudo apt-get install python3-tk
sudo apt-get install redis-server
sudo apt-get install openbox
sudo apt-get install lightdm
sudo apt-get install x11-xserver-utils
Install Dependencies
pip3 install -r requirements.txt
This command didn't work. So I ran the following:
sudo apt-get install git
git clone https://github.com/dupontgu/retro-ipod-spotify-client
After this:
cd /home/pi/retro-ipod-spotify-client/frontend/
To finally run this...
pip3 install -r requirements.txt
Install pi-btaudio
git clone https://github.com/bablokb/pi-btaudio.git
cd pi-btaudio
sudo tools/install
All worked fine. For bluetooth pairing and instructions, go to https://github.com/bablokb/pi-btaudio
wget https://github.com/joan2937/pigpio/archive/master.zip
unzip master.zip
cd pigpio-master
make
sudo make install
I don't know if it works because I haven't been able to use the Clickwheel. Still don't know it it's a hardware or software issue...
Setup Spotify API
Create a Spotify Developer Account, then create an app where you will get your Client ID, Client Secret, and create a Redirect URI.
I recommend watching this video and try to make something like the link on README. I think it hasn't worked 100% right for me though...
Disable Screen Blanking
sudo raspi-config
Display Option > Screen Blanking > OFF / Disable
Disable Cursor and Screen Savers
Add or Create and Add this:
sudo nano /root/.bash_profile
Then:
'#!/bin/bash'
[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && startx -- -nocursor
# Disable any form of screen saver / screen blanking / power management
xset s off
xset s noblank
Configure xinitrc
sudo nano /etc/X11/xinit/xinitrc
Inside, make sure the following is there:
#!/bin/sh
# /etc/X11/xinit/xinitrc
# global xinitrc file, used by all X sessions started by xinit (startx)
# invoke global X session script
#. /etc/X11/Xsession'
exec openbox-session #-> This is the one that launches Openbox ;)`
Run "spotifypod.py" with Autostart
sudo nano /etc/xdg/openbox/autostart
and add the following command to launch spotifypod.py:
I made some changes and didn't use the /fork/ folder as in README
cd /home/pi/retro-ipod-spotify-client/frontend/
sudo -H -u pi python3 spotifypod.py &
sudo /home/pi/retro-ipod-spotify-client/clickwheel/click &
There is no click
so in order to get that:
cd /home/pi/retro-spotify-client/clickwheel
gcc -Wall -pthread -o click click.c -lpigpio -lrt
It returns some warnings but everything is fine. I guess...
Spotify Credentials
'sudo nano /etc/xdg/openbox/environment'
Add you credentials:
export SPOTIPY_CLIENT_ID='your_SPOTIPY_CLIENT_ID'
export SPOTIPY_CLIENT_SECRET='your_SPOTIPY_CLIENT_SECRET'
export SPOTIPY_REDIRECT_URI='your_SPOTIPY_REDIRECT_URI'
Syncronize Spotify Data
Synchronizing Spotify data! Last but not least, if you want to make sure all your playlists artists, etc are synchronized every time you turn on your Spotypod, you can simply modify the script view_model.py
with the following at line 16:
#spotify_manager.refresh_devices()
spotify_manager.refresh_data()
instead of calling refresh_device
, you can execute refresh_data
. This will sync all your data and then will execute refresh.devices
. This will make the boot up way slower! but it will synchronize every single time you switch on :). If you don't run at least once refresh_data()
no playlist, artist or anything related with your account will be displayed!
I had errors with this part so I left it as it was...
sudo nano /etc/default/raspotify
OPTIONS="--username <USERNAME> --password <PASSWORD>"
# The displayed device type in Spotify clients.
# Can be "unknown", "computer", "tablet", "smartphone", "speaker", "tv",
# "avr" (Audio/Video Receiver), "stb" (Set-Top Box), and "audiodongle".
DEVICE_TYPE="smartphone"
All good. I also changed the Device Name
sudo nano /boot/config.txt
sdtv_mode=2
hdmi_force_hotplug=1
hdmi_ignore_hotplug=1
After this if it throws error for displaying spotifypod.py
try the following command:
export DISPLAY=:0.0
So that's what I've got.
The app doesn't autostart. When I start it I get the frontend screen that shows NOW PLAYING when I play a song on my phone. I still haven't been able to work with the clickwheel, it is running with no errors apparently, but it doesn't work with my clickwheel at least so I haven't tried the app on itself.
I connected the Pi to my bluetooth speaker and from my phone I used the Pi as a Remote Device and I got Spotify audio out of it. Is it able to choose it's own songs and play them, I still don't know.
I hope this works for someone and if anybody has any info on how to solve the rest of this, please share hahaha.
Cheers and thanks @dupontgu for this fun project.
would a 5th generation ipod classic be possible?
i would guess the newer clickwheel would be the only obstacle.
All I get is "x set" errors each time I run it. I've tried this countless times, word for word and it just isn't working.
Hello, so I tried compiling the click.c with the command supplied in the file :'gcc -Wall -pthread -o click click.c -lpigpio -lrt'.
The problem is I'm getting two error messages and I don't know anything about C, so I don't know how to fix them.
The errors are:
click.c:160:5: warning: first argument of 'main' should be 'int' [-Wmain]
int main(void *args){
^~~~
click.c:160:5: warning: 'main' takes only zero or two arguments [-Wmain]
Can somebody help me, Pigpio is installed and I'm guessing it has to do something with the main function, but I don't know how to fix this.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.