1

I writing an application that collects some various data about the song I currently listen to on Spotify. To do this I use Python, flask and spotipy running inside a docker container. The thing is that Spotify's API requires some user input to login, and when calling the login API a web page is open up for the user to enter the user credentials. This has to be done only once per server (I think). However, inside a docker container (running on my Linux server), I can't do that. How do I solve a problem like this?

I have set the callback on Spotify's developer page and registered an application. Also I have set the following environment variables (not all is used :-P)

        self.client_id = os.getenv("CLIENT_ID")
        self.client_secret = os.getenv("CLIENT_SECRET")
        self.oauth_token = os.getenv("OAUTH_TOKEN")
        self.username = os.getenv("USERNAME")
        self.redirect_uri = os.getenv("REDIRECT_URI")
        self.scope = os.getenv("SCOPE") 

And basically using the following code to authorize and get the song data from Spotify

from spotipy.oauth2 import SpotifyOAuth

            self.auth_manager = SpotifyOAuth(
                                    client_id=self.client_id,
                                    client_secret=self.client_secret,
                                    scope=self.scope,
                                    redirect_uri = self.redirect_uri,
                                    username= self.username,
                                )

            self.spotify = spotipy.Spotify(auth_manager=self.auth_manager)
            response = self.spotify.currently_playing()


I have tried tried to solve this using a headless selenium and webdriver but haven't got it to work. I have also tried to ditch spotipy and side load a token from another instance and got it to work, but that is not a sustainable solution, since I need to be able to run a new docker container without any manual work. (And I have other APIs that also need this kind of solution)

ZombieGoose
  • 37
  • 1
  • 3

1 Answers1

1

There are many obstacles to running Spotify with Flask on Docker Container.

Overview

Overview

#1 Local Flask Test

#1.1 Flask code - save as login.py

from flask import Flask, request, redirect
from requests_oauthlib import OAuth2Session
from requests.auth import HTTPBasicAuth
import requests
import os

app = Flask(__name__)
try:
    AUTH_URL=os.getenv("AUTH_URL")
    TOKEN_URL=os.getenv("TOKEN_URL")
    REDIRECT_URI=os.getenv("REDIRECT_URI")
    CLIENT_ID=os.getenv("CLIENT_ID")
    CLIENT_SECRET=os.getenv("CLIENT_SECRET")
    SCOPE=['user-read-currently-playing']
    # The checking environment variable is set
    print(AUTH_URL)
    print(TOKEN_URL)
except KeyError: 
   print ('Please set the environment variable for Spotify')

def get_headers(token):
    return {"Authorization": "Bearer " + token}

@app.route("/")
def root_message():
    app.logger.info('hello! root accessed')
    return 'I am a spotify server'

@app.route("/login")
def login():
    app.logger.info('login logged in successfully')
    spotify = OAuth2Session(CLIENT_ID, scope=SCOPE, redirect_uri=REDIRECT_URI)
    authorization_url, state = spotify.authorization_url(AUTH_URL)
    return redirect(authorization_url)

# Your redirect URI's path
# http://localhost:3000/callback?code=AQDTZDK66wl...se8A1YTe&state=kt4H....963Nd
@app.route("/callback", methods=['GET'])
def callback():
    # get access token
    code = request.args.get('code')
    resp = requests.post(TOKEN_URL,
        auth=HTTPBasicAuth(CLIENT_ID, CLIENT_SECRET),
        data={
            'grant_type': 'authorization_code',
            'code': code,
            'redirect_uri': REDIRECT_URI
        })
    access_token = resp.json()['access_token']

    # get current playing
    headers = get_headers(access_token)
    result1 = requests.get(url='https://api.spotify.com/v1/me/player/currently-playing', headers=headers)
    current_song = result1.json()
    return current_song

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=3000,debug=True) # your redirect URI's port

#1.2 Set environment variable

  • in Windows, if Linux/Mac use EXPORT instead of SET
SET AUTH_URL=https://accounts.spotify.com/authorize
SET TOKEN_URL=https://accounts.spotify.com/api/token
SET CLIENT_ID=<your client id>
SET CLIENT_SECRET=<your client secret>
SET REDIRECT_URI=http://localhost:3000/callback <- <your redirect URL>

#1.3 run it

python login.py

#1.4 access it and Spotify login with your credential

localhost:3000/login

Result - confirm local Flask is providing current play song

enter image description here

#2 Dockerfile

#2.1 Save requirements.txt for python dependency install.

click==8.1.3
colorama==0.4.5
Flask==2.2.2
gunicorn==20.1.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.1
python-dotenv==0.21.0
requests-file==1.5.1
requests-oauthlib==1.3.1
requests-toolbelt==0.9.1
requests==2.28.1
Werkzeug==2.2.2

#2.2 Dockerfile

I am using python:3.10 but if you want to more small size use python:3.10-slim

# start by pulling the python image
FROM python:3.10

# hardcoded envirenment - Causion no single quote or double quote
ENV AUTH_URL=https://accounts.spotify.com/authorize
ENV TOKEN_URL=https://accounts.spotify.com/api/token
ENV CLIENT_ID=<your client id>
ENV CLIENT_SECRET=<your client secret>
ENV REDIRECT_URI=http://localhost:3000/callback <- <your redirect URI>

# port expose to outside, your redirect URI's port
EXPOSE 3000

# copy the requirements file into the image
COPY ./requirements.txt /app/requirements.txt

# switch working directory
WORKDIR /app

# install the dependencies and packages in the requirements file
RUN /usr/local/bin/python -m pip install --upgrade pip
RUN pip install -r requirements.txt

# copy every content from the local file to the image
COPY . /app

# configure the container to run in an executed manner
ENTRYPOINT ["python"]

CMD ["login.py"]

#3 Docker image

From the terminal, to create docker image

$ docker image build -t local_spotify .

Result enter image description here

#4 Docker Container

I use port 3000 but it should be change your redirect PORT

docker run -p 3000:3000 --name spotify_login local_spotify

Make sure the AUTH_URL and ENV TOKEN_URL were displayed

enter image description here

#5 Access Flask Server

localhost:3000/login

enter image description here

debugging tips!

To delete all of running container

$ docker rm -f $(docker ps -aq)

Can go inside container for looking environment variable.

$ docker exec -it spotify_login bash
root@<container id>:/app# env

To delete docker image

$ docker rmi -f local_spotify:latest
Bench Vue
  • 5,257
  • 2
  • 10
  • 14
  • Thank you. Ditching spotipy (again) and using requests was way more simpler and also the callback was not called. Fixed that and now it works. Thanks again – ZombieGoose Mar 08 '23 at 11:47