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)
There are many obstacles to running Spotify with Flask on Docker Container.
#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
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
#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"]
From the terminal, to create docker image
$ docker image build -t local_spotify .
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
localhost:3000/login
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