The software that powers the sPot: a 4th generation

Overview

sPot

This code is meant to accompany this project in which a Spotify client is built into an iPod "Classic" from 2004. Everything is meant to run on a Raspberry Pi Zero W.

Since we are using the lite version of raspbian, some extra packages need to be installed:

Instructions

  1. Install updates
sudo apt-get update 
sudo apt-get upgrade
  1. Install Required Packages.

Installation for python3-pip, raspotify, python3-tk, openbox


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-get install python3-tk 

sudo apt-get install redis-server

sudo apt-get install openbox

sudo apt install xorg

sudo apt-get install lightdm

sudo apt-get install x11-xserver-utils

  1. Install Dependencies
pip3 install -r requirements.txt
  1. Install pi-btaudio
git clone https://github.com/bablokb/pi-btaudio.git
cd pi-btaudio
sudo tools/install
  1. Install PiGPIO
wget https://github.com/joan2937/pigpio/archive/master.zip
unzip master.zip
cd pigpio-master
make
sudo make install
  1. Setup Spotify API

First Create an App at https://developer.spotify.com/dashboard/applications/

https://accounts.spotify.com/authorize?client_id=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX&response_type=code&redirect_uri=http%3A%2F%2F127.0.0.1&scope=user-read-playback-state%20user-modify-playback-state%20user-read-currently-playing%20	app-remote-control%20streaming%20playlist-modify-public%20playlist-modify-private%20playlist-read-private%20playlist-read-collaborative
  1. raspi-config

sudo raspi-config

Console Autologin

Display Option -> Screen Blanking -> Off if you want to avoid the screen turning black after a few seconds.

  1. bash_profile

In .bash_profile added the following (if the file is not htere, you must create it)

#!/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
  1. 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 ;)
  1. Run "spotifypod.py" with autostart

sudo nano /etc/xdg/openbox/autostart

and add the following command to launch spotifypod.py:

cd /home/pi/fork/retro-ipod-spotify-client/frontend/

sudo -H -u pi python3 spotifypod.py &

sudo /home/pi/fork/retro-ipod-spotify-client/clickwheel/click &

Make sure that the paths are ok with your setup!!

in sudo nano /etc/xdg/openbox/environment all the variables needed to run spotifypod.py are set( SPOTIPY_CLIENT_ID, SPOTIPY_CLIENT_SECRET,SPOTIPY_REDIRECT_URI)

export SPOTIPY_CLIENT_ID='your_SPOTIPY_CLIENT_ID'

export SPOTIPY_CLIENT_SECRET='your_SPOTIPY_CLIENT_SECRET'

export SPOTIPY_REDIRECT_URI='your_SPOTIPY_REDIRECT_URI'
  1. 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 eceute refresh.devices. This will make the boot up way slower! but it will synchronize every single time you switch on :). If you dont run at least once refresh_data() no playlist, artist or anything related with your account will be displayed!

  1. Configure Raspotify

sudo nano /etc/default/raspotify

Uncomment and fill the following line:

OPTIONS="--username --password "

And maybe you want also to consider the following:

# 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"

Wiring

Here is the wiring of the hardware, as of revision 1. Note that the pin numbers correlate to those referenced in click.c

Wiring Diagram

Comments
  • Can't play music (error no devices)

    Can't play music (error no devices)

    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?

    opened by XEROxMEXICANO 87
  • Q: High click.c CPU usage

    Q: High click.c CPU usage

    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.

    click.c code
    // 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.

    opened by tomaculum 9
  • PodCast Entry and functionality

    PodCast Entry and functionality

    Hey, I'm probably not alone when saying this, but I'm a podcast fan, and thought that maybe you guys would like this functionality.

    So I created an entry in the menu for podcasts.

    Check it out, and see if its to your liking.

    (First contribution to a repo that isn't mine, so if you see anything that isnt quite up to spec, let me know!)

    image

    image

    image

    opened by ElCapitanDre 8
  • Search infinitely loading.

    Search infinitely loading.

    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.

    opened by MattDB101 3
  • TclError: no display name and no $DISPLAY environment variable

    TclError: no display name and no $DISPLAY environment variable

    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

    opened by chonnymon 3
  • Enable Discussions Tab

    Enable Discussions Tab

    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.

    opened by tomaculum 2
  • Compiling click.c warnings

    Compiling click.c warnings

    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.

    opened by Archipollo 2
  • Can't connect to Spotify API

    Can't connect to Spotify API

    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 :smile:

    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.

    opened by sguldemond 2
  • Black screen issues & Raspotify - Pi Zero 1

    Black screen issues & Raspotify - Pi Zero 1

    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

    opened by TheBasedDoge 1
  • How I fixed [Errno 98], NoneType, Scope, and StartX blank screen

    How I fixed [Errno 98], NoneType, Scope, and StartX blank screen

    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.

    [Errno 98] Address already in use

    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 (https://github.com/dupontgu/retro-ipod-spotify-client/issues/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.

    Scope Errors

    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.

    NoneType Error

    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 (https://github.com/dupontgu/retro-ipod-spotify-client/pull/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!

    StartX

    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 (https://github.com/dupontgu/retro-ipod-spotify-client/pull/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

    opened by CtrlAltWilson 1
  • No License?

    No License?

    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 😄

    opened by Seshpenguin 1
  • How to add a new option on the menu page

    How to add a new option on the menu page

    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: pair_head with this code Code_head run_head When I click on it, it freezes the program and I have to restart and it's back to normal. Any ideas?

    opened by XEROxMEXICANO 1
  • How I got everything working

    How I got everything working

    Installing SpotifyPod (The code is modified for Ricardo's Alternate Build)

    What you need to do before installing SpotifyPod

    You need raspberry pi lite os legacy version We will also be using raspotify version 0.14 which we will be installing later

    IMPORTANT This code works 2inch LCD Module ST7789V controller 240 x 320 Pixels For more hardware instructions please follow Ricardo's Alternate Build)

    Installing updates

    sudo apt-get update 
    sudo apt-get upgrade
    

    Install Required Packages

    sudo apt install python-setuptools python3-setuptools
    
    sudo apt install python3-pip
    
    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 
    
    sudo apt-get install redis-server
    
    sudo apt-get install openbox
    
    sudo apt install xorg
    
    sudo apt-get install lightdm
    
    sudo apt-get install x11-xserver-utils
    
    sudo apt-get install git
    

    Change directory to home

    cd ~
    

    Clone this repo and Install Dependencies

    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
    

    Installing Pi-btaudio

    git clone https://github.com/bablokb/pi-btaudio.git
    
    cd pi-btaudio
    
    sudo tools/install
    

    Install PigPio

    wget https://github.com/joan2937/pigpio/archive/master.zip
    
    unzip master.zip
    
    cd pigpio-master
    
    make
    
    sudo make install
    

    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

    https://developer.spotify.com/dashboard/applications/ Create_app

    Add an app name and description can be anything

    Add_app_name Add_host_uri Add_uri_2 add_uri_3 add_rui_4

    Save Client id and Save Secret id

    Secret_and_client

    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 ~/.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
    
    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
    

    Configure Xintric

    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 ;)`
    

    Run "spotifypod.py" with Autostart

    sudo nano /etc/xdg/openbox/autostart
    

    Then paste this before exit

    #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 &
    

    Please comment this out until I remind you to uncomment it, save the file and exit, then we are now going to make the the program that makes the clickwheel work an executable

    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
    

    when were running "gcc -Wall -pthread -o click click.c -lpigpio -lrt" just leave it alone till you see a blinking text box

    sudo chmod +x click
    

    Spotify credentials

    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'
    

    Synchronize 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

    sudo nano view_model.py
    

    go to line 16

    #spotify_manager.refresh_devices()
    
    spotify_manager.refresh_data()
    

    Configure Raspotify

    sudo nano /etc/default/raspotify
    

    Uncomment and fill the following line:

    OPTIONS="--username XXXXXXXX --password XXXXXXXXX"
    

    Enter your spotify login details, In the first XXX add your email address, and in the second XXX add your password

    Ex:

    OPTIONS="--username [email protected] --password thisismypassword"
    

    Paste the following

    # 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"
    

    Entering Device id

    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

    Post Install

    Generate .cache files

    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:

    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

    before reboot do the following:

    sudo raspi-config
    
    system options --> boot / auto login --> console autologin
    

    Turn on vnc or connect a mouse to the pi, then reboot

    Then you can run this program:

    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.

    Now move your cache file to the one in spotifypod

    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

    Install Display code

    ssh into the pi and edit the config file

    sudo nano /boot/config.txt
    

    Add the code below:

    DIsplay code

    hdmi_group=2 hdmi_mode=87 hdmi_cvt=320 240 60 1 0 0 0 hdmi_force_hotplug=1

    Also you have to comment out this code:

    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 &
    

    Uncomment the Autostart things:

    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 &
    

    Bluetooth

    For bluetooth I these instructions: To install blue-alsa link To set up auto connect link

    Use this to pair bluetooth device:

    pairing

    Please only follow the configuration instructions you already downloaded it in the previous link. link

    When Done

    Reboot and whenever you need to use the pod connect to it from your phone/ computer on the device list as if it were a speaker. It might take a while but it should connect eventually then you're free to exit the app and play songs from the pod.

    Issues

    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 😄

    Resources:

    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 https://github.com/dupontgu/retro-ipod-spotify-client/issues/41 https://github.com/dupontgu/retro-ipod-spotify-client/issues/22 https://github.com/dupontgu/retro-ipod-spotify-client/issues/34 https://github.com/dupontgu/retro-ipod-spotify-client/issues/65 https://github.com/dupontgu/retro-ipod-spotify-client/issues/23 https://github.com/dupontgu/retro-ipod-spotify-client/issues/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

    opened by XEROxMEXICANO 73
  • Song only plays first few seconds & How to use bluetooth audio?

    Song only plays first few seconds & How to use bluetooth audio?

    So I almost have everything fully working now. I am down to two issues.

    1. When selecting a song, it "plays" the first 3 or so seconds and then pauses on 00:03. Hitting play wont do anything and i just have to select the song again.
    2. When it's playing I don't get any audio. Bluetooth is connected and shows the following for status: [Galaxy Buds2 (C0C4)]# info 60:3A:AF:4D:C0:C4 Device 60:3A:AF:4D:C0:C4 (public) Name: Galaxy Buds2 (C0C4) Alias: Galaxy Buds2 (C0C4) Class: 0x00240404 Icon: audio-card Paired: yes Trusted: no Blocked: no Connected: yes LegacyPairing: no UUID: Serial Port (00001101-0000-1000-8000-00805f9b34fb) UUID: Audio Sink (0000110b-0000-1000-8000-00805f9b34fb) UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb) UUID: A/V Remote Control (0000110e-0000-1000-8000-00805f9b34fb) UUID: Handsfree (0000111e-0000-1000-8000-00805f9b34fb) UUID: PnP Information (00001200-0000-1000-8000-00805f9b34fb) UUID: Vendor specific (2e73a4ad-332d-41fc-90e2-16bef06523f2) UUID: Vendor specific (a23d00bc-217c-123b-9c00-fc44577136ee) UUID: Vendor specific (b4a9d6a0-b2e3-4e40-976d-a69f167ea895) UUID: Vendor specific (e7ab2241-ca64-4a69-ac02-05f5c6fe2d62) UUID: Vendor specific (f8620674-a1ed-41ab-a8b9-de9ad655729d)

    Also, how to auto connect bluetooth on startup? I have to manually connect via terminal over SSH each time.

    opened by TheBasedDoge 3
  • Search and Get_Playlist() Issues

    Search and Get_Playlist() Issues

    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!

    opened by hushdauihdhi 1
  • click wheel question

    click wheel question

    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.

    opened by montagsmodell 1
Owner
Guy Dupont
Guy Dupont
OpenStickFirmware is open source software designed to handle any and all tasks required in a custom Fight Stick

OpenStickFirmware is open source software designed to handle any and all tasks required in a custom Fight Stick. It can handle being the brains of your entire stick, or just handling the bells and whistles while your Brook board talks to your console.

Sleep Unit 23 Nov 24, 2022
Inykcal is a software written in python for selected E-Paper displays.

Inykcal is a software written in python for selected E-Paper displays. It converts these displays into useful information dashboards. It's open-source, free for personal use, fully modular and user-friendly. Despite all this, Inkycal can run well even on the Raspberry Pi Zero W. Oh, and it's open for third-party modules! Hooray!

Ace 727 Jan 2, 2023
Poupool is an overflow swimming pool control software

Poupool - The swimming pool controller Poupool is a swimming pool control software. It is based on Transitions, Pykka and Paho MQTT. The user interfac

Cyril Jaquier 8 Jul 18, 2022
Mycodo is open source software for the Raspberry Pi that couples inputs and outputs in interesting ways to sense and manipulate the environment.

Mycodo Environmental Regulation System Latest version: 8.12.9 Mycodo is open source software for the Raspberry Pi that couples inputs and outputs in i

Kyle Gabriel 2.3k Dec 29, 2022
Create a low powered, renewable generation forecast display with a Raspberry Pi Zero & Inky wHAT.

GB Renewable Forecast Display This Raspberry Pi powered eInk display aims to give you a quick way to time your home energy usage to help balance the g

Andy Brace 32 Jul 2, 2022
The Open edX platform, the software that powers edX!

This is the core repository of the Open edX software. It includes the LMS (student-facing, delivering courseware), and Studio (course authoring) compo

edX 6.2k Jan 1, 2023
A tool to convert AWS EC2 instances back and forth between On-Demand and Spot billing models.

ec2-spot-converter This tool converts existing AWS EC2 instances back and forth between On-Demand and 'persistent' Spot billing models while preservin

jcjorel 152 Dec 29, 2022
ckan 3.6k Dec 27, 2022
The project that powers MDN.

Kuma Kuma is the platform that powers MDN (developer.mozilla.org) Development Code: https://github.com/mdn/kuma Issues: P1 Bugs (to be fixed ASAP) P2

MDN Web Docs 1.9k Dec 26, 2022
The source code that powers readthedocs.org

Welcome to Read the Docs Purpose Read the Docs hosts documentation for the open source community. It supports Sphinx docs written with reStructuredTex

Read the Docs 7.4k Dec 25, 2022
evtx-hunter helps to quickly spot interesting security-related activity in Windows Event Viewer (EVTX) files.

Introduction evtx-hunter helps to quickly spot interesting security-related activity in Windows Event Viewer (EVTX) files. It can process a high numbe

NVISO 116 Dec 29, 2022
Learning source code review, spot vulnerability, find some ways how to fix it.

Learn Source Code Review Learning source code review, spot vulnerability, find some ways how to fix it. WordPress Plugin Authenticated Stored XSS on C

Shan 24 Dec 31, 2022
A quick script to spot the usage of Unicode Bidi (bidirectional) characters that could lead to an Invisible Backdoor

Invisible Backdoor Detector is a little Python script that allows you to spot and remove Bidi characters that could lead to an invisible backdoor. If you don't know what that is you should check the related paragraph.

SecSI 28 Dec 29, 2022
Official implementation of "Can You Spot the Chameleon? Adversarially Camouflaging Images from Co-Salient Object Detection" in CVPR 2022.

Jadena Official implementation of "Can You Spot the Chameleon? Adversarially Camouflaging Images from Co-Salient Object Detection" in CVPR 2022. arXiv

Qing Guo 13 Nov 29, 2022
Kaggle Lyft Motion Prediction for Autonomous Vehicles 4th place solution

Lyft Motion Prediction for Autonomous Vehicles Code for the 4th place solution of Lyft Motion Prediction for Autonomous Vehicles on Kaggle. Discussion

null 44 Jun 27, 2022
4th place solution to datafactory challenge by Intermarché.

Solution to Datafactory challenge by Intermarché. 4th place solution to datafactory challenge by Intermarché. The objective of the challenge is to pre

Raphael Sourty 11 Mar 19, 2022
BirdCLEF 2021 - Birdcall Identification 4th place solution

BirdCLEF 2021 - Birdcall Identification 4th place solution My solution detail kaggle discussion Inference Notebook (best submission) Environment Use K

tattaka 42 Jan 2, 2023
atmaCup #11 の Public 4th / Pricvate 5th Solution のリポジトリです。

#11 atmaCup 2021-07-09 ~ 2020-07-21 に行われた #11 [初心者歓迎! / 画像編] atmaCup のリポジトリです。結果は Public 4th / Private 5th でした。 フレームワークは PyTorch で、実装は pytorch-image-m

Tawara 12 Apr 7, 2022
The training code for the 4th place model at MDX 2021 leaderboard A.

The training code for the 4th place model at MDX 2021 leaderboard A.

Chin-Yun Yu 32 Dec 18, 2022
4th place solution for the SIGIR 2021 challenge.

SIGIR-2021 (Tinkoff.AI) How to start Download train and test data: https://sigir-ecom.github.io/data-task.html Place it under sigir-2021/data/. Run py

Tinkoff.AI 4 Jul 1, 2022