[GH-ISSUE #598] Avoid authentication every time I add song to library #355

Closed
opened 2026-02-27 23:22:10 +03:00 by kerem · 2 comments
Owner

Originally created by @tejas-kale on GitHub (Oct 26, 2020).
Original GitHub issue: https://github.com/spotipy-dev/spotipy/issues/598

Hi. I want to write a Flask application that can add the currently playing song to my library. The aim is to have a Keyboard Maestro trigger that will go to the URL http://localhost:5002/like_currently_playing_song. I am trying to address two problems:

  • Every time I call like_currently_playing_song, 2 browser tabs open and close (one for user-read-currently-playing OAuth and the other for user-library-modify, I guess). How can I avoid this behaviour? The intention behind writing this application is to like a song with a shortcut without having to change windows.
  • Can I make a call to http://localhost:5002/like_currently_playing_song in the background? With my current code, Safari comes into focus.

Pardon me if this is not the right place to post the question. Thanks.

Here is my code:

from flask import Flask, session
from spotipy import Spotify
from spotipy.oauth2 import SpotifyOAuth
from typing import Union

import json
import os
import spotipy
import uuid

currently_playing_conn: Union[None, spotipy.client.Spotify] = None
library_modify_conn: Union[None, spotipy.client.Spotify] = None


app = Flask(__name__)
app.config["SECRET_KEY"] = os.urandom(64)
app.config["SESSION_TYPE"] = "filesystem"
app.config["SESSION_FILE_DIR"] = "./.flask_session/"

# Session(app)

caches_dir = "./.spotify_caches/"
if not os.path.exists(caches_dir):
    os.makedirs(caches_dir)


def session_cache_path():
    return f"{caches_dir}{session.get('uuid')}"


@app.route("/")
def index():
    if not session.get("uuid"):
        session["uuid"] = str(uuid.uuid4())
    establish_connection()

    return "Connection established."


def get_auth_info(credential_fn) -> dict:
    with open(credential_fn, "r") as f:
        spotify_credentials = json.load(f)

    return spotify_credentials


def establish_connection(credential_fn: str = "spotify_credentials.json"):
    global currently_playing_conn
    global library_modify_conn
    spotify_info: dict = get_auth_info(credential_fn)
    currently_playing_conn = Spotify(auth_manager=SpotifyOAuth(scope="user-read-currently-playing",
                                                               cache_path=session_cache_path(),
                                                               client_id=spotify_info["client_id"],
                                                               client_secret=spotify_info["client_secret"],
                                                               redirect_uri=spotify_info["redirect_uri"]))
    library_modify_conn = Spotify(auth_manager=SpotifyOAuth(scope="user-library-modify",
                                                            cache_path=session_cache_path(),
                                                            client_id=spotify_info["client_id"],
                                                            client_secret=spotify_info["client_secret"],
                                                            redirect_uri=spotify_info["redirect_uri"]))


@app.route("/like_currently_playing_song")
def like_currently_playing_song():
    global currently_playing_conn
    global library_modify_conn
    if (currently_playing_conn is None) or (library_modify_conn is None):
        establish_connection()
    track_id = currently_playing_conn.current_user_playing_track()["item"]["id"]
    library_modify_conn.current_user_saved_tracks_add([track_id])

    return "Liked!"


if __name__ == '__main__':
    app.run(port=5002)
Originally created by @tejas-kale on GitHub (Oct 26, 2020). Original GitHub issue: https://github.com/spotipy-dev/spotipy/issues/598 Hi. I want to write a Flask application that can add the currently playing song to my library. The aim is to have a Keyboard Maestro trigger that will go to the URL `http://localhost:5002/like_currently_playing_song`. I am trying to address two problems: - Every time I call `like_currently_playing_song`, 2 browser tabs open and close (one for `user-read-currently-playing` OAuth and the other for `user-library-modify`, I guess). How can I avoid this behaviour? The intention behind writing this application is to like a song with a shortcut without having to change windows. - Can I make a call to `http://localhost:5002/like_currently_playing_song` in the background? With my current code, Safari comes into focus. Pardon me if this is not the right place to post the question. Thanks. Here is my code: ``` from flask import Flask, session from spotipy import Spotify from spotipy.oauth2 import SpotifyOAuth from typing import Union import json import os import spotipy import uuid currently_playing_conn: Union[None, spotipy.client.Spotify] = None library_modify_conn: Union[None, spotipy.client.Spotify] = None app = Flask(__name__) app.config["SECRET_KEY"] = os.urandom(64) app.config["SESSION_TYPE"] = "filesystem" app.config["SESSION_FILE_DIR"] = "./.flask_session/" # Session(app) caches_dir = "./.spotify_caches/" if not os.path.exists(caches_dir): os.makedirs(caches_dir) def session_cache_path(): return f"{caches_dir}{session.get('uuid')}" @app.route("/") def index(): if not session.get("uuid"): session["uuid"] = str(uuid.uuid4()) establish_connection() return "Connection established." def get_auth_info(credential_fn) -> dict: with open(credential_fn, "r") as f: spotify_credentials = json.load(f) return spotify_credentials def establish_connection(credential_fn: str = "spotify_credentials.json"): global currently_playing_conn global library_modify_conn spotify_info: dict = get_auth_info(credential_fn) currently_playing_conn = Spotify(auth_manager=SpotifyOAuth(scope="user-read-currently-playing", cache_path=session_cache_path(), client_id=spotify_info["client_id"], client_secret=spotify_info["client_secret"], redirect_uri=spotify_info["redirect_uri"])) library_modify_conn = Spotify(auth_manager=SpotifyOAuth(scope="user-library-modify", cache_path=session_cache_path(), client_id=spotify_info["client_id"], client_secret=spotify_info["client_secret"], redirect_uri=spotify_info["redirect_uri"])) @app.route("/like_currently_playing_song") def like_currently_playing_song(): global currently_playing_conn global library_modify_conn if (currently_playing_conn is None) or (library_modify_conn is None): establish_connection() track_id = currently_playing_conn.current_user_playing_track()["item"]["id"] library_modify_conn.current_user_saved_tracks_add([track_id]) return "Liked!" if __name__ == '__main__': app.run(port=5002) ```
kerem 2026-02-27 23:22:10 +03:00
  • closed this issue
  • added the
    question
    label
Author
Owner

@Peter-Schorn commented on GitHub (Oct 28, 2020):

@tejas-kale You're using the library wrong. Why did you create a separate instance of Spotify for each request? You only need once instance per Spotify user.

Furthermore, it looks like you're saving the authorization information for each instance of Spotify to the same file. These instances keep overwriting each other's authorization information and each instance is authorized for different scopes. This is why you have to keep logging in again.

<!-- gh-comment-id:717702262 --> @Peter-Schorn commented on GitHub (Oct 28, 2020): @tejas-kale You're using the library wrong. Why did you create a separate instance of `Spotify` for each request? You only need once instance per Spotify user. Furthermore, it looks like you're saving the authorization information for each instance of `Spotify` to the same file. These instances keep overwriting each other's authorization information and each instance is authorized for different scopes. This is why you have to keep logging in again.
Author
Owner

@tejas-kale commented on GitHub (Oct 28, 2020):

@Peter-Schorn Thanks for the clarification. I thought each SpotifyOAuth accepts a single scope so I created 2 instances. Changing the scope assignment to

scope="user-read-currently-playing user-library-modify"

got rid of the problem.

Pasting the corrected code just for reference:

from flask import Flask, session
from spotipy import Spotify
from spotipy.oauth2 import SpotifyOAuth
from typing import Union

import json
import os
import spotipy
import uuid

spotify_conn: Union[None, spotipy.client.Spotify] = None


app = Flask(__name__)
app.config["SECRET_KEY"] = os.urandom(64)
app.config["SESSION_TYPE"] = "filesystem"
app.config["SESSION_FILE_DIR"] = "./.flask_session/"

caches_dir = "./.spotify_caches/"
if not os.path.exists(caches_dir):
    os.makedirs(caches_dir)


def session_cache_path():
    return f"{caches_dir}{session.get('uuid')}"


@app.route("/")
def index():
    if not session.get("uuid"):
        session["uuid"] = str(uuid.uuid4())
    establish_connection()

    return "Connection established."


def get_auth_info(credential_fn) -> dict:
    with open(credential_fn, "r") as f:
        spotify_credentials = json.load(f)

    return spotify_credentials


def establish_connection(credential_fn: str = "spotify_credentials.json"):
    global spotify_conn
    spotify_info: dict = get_auth_info(credential_fn)
    spotify_conn = Spotify(auth_manager=SpotifyOAuth(scope="user-read-currently-playing user-library-modify",
                                                     cache_path=session_cache_path(),
                                                     client_id=spotify_info["client_id"],
                                                     client_secret=spotify_info["client_secret"],
                                                     redirect_uri=spotify_info["redirect_uri"]))


@app.route("/like_currently_playing_song")
def like_currently_playing_song():
    global spotify_conn
    if spotify_conn is None:
        establish_connection()
    track_id = spotify_conn.current_user_playing_track()["item"]["id"]
    spotify_conn.current_user_saved_tracks_add([track_id])

    return "Liked!"


if __name__ == '__main__':
    app.run(port=5002)
<!-- gh-comment-id:717750934 --> @tejas-kale commented on GitHub (Oct 28, 2020): @Peter-Schorn Thanks for the clarification. I thought each `SpotifyOAuth` accepts a single scope so I created 2 instances. Changing the scope assignment to `scope="user-read-currently-playing user-library-modify"` got rid of the problem. Pasting the corrected code just for reference: ``` from flask import Flask, session from spotipy import Spotify from spotipy.oauth2 import SpotifyOAuth from typing import Union import json import os import spotipy import uuid spotify_conn: Union[None, spotipy.client.Spotify] = None app = Flask(__name__) app.config["SECRET_KEY"] = os.urandom(64) app.config["SESSION_TYPE"] = "filesystem" app.config["SESSION_FILE_DIR"] = "./.flask_session/" caches_dir = "./.spotify_caches/" if not os.path.exists(caches_dir): os.makedirs(caches_dir) def session_cache_path(): return f"{caches_dir}{session.get('uuid')}" @app.route("/") def index(): if not session.get("uuid"): session["uuid"] = str(uuid.uuid4()) establish_connection() return "Connection established." def get_auth_info(credential_fn) -> dict: with open(credential_fn, "r") as f: spotify_credentials = json.load(f) return spotify_credentials def establish_connection(credential_fn: str = "spotify_credentials.json"): global spotify_conn spotify_info: dict = get_auth_info(credential_fn) spotify_conn = Spotify(auth_manager=SpotifyOAuth(scope="user-read-currently-playing user-library-modify", cache_path=session_cache_path(), client_id=spotify_info["client_id"], client_secret=spotify_info["client_secret"], redirect_uri=spotify_info["redirect_uri"])) @app.route("/like_currently_playing_song") def like_currently_playing_song(): global spotify_conn if spotify_conn is None: establish_connection() track_id = spotify_conn.current_user_playing_track()["item"]["id"] spotify_conn.current_user_saved_tracks_add([track_id]) return "Liked!" if __name__ == '__main__': app.run(port=5002) ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/spotipy#355
No description provided.