[GH-ISSUE #715] How to suppress opening browser for command line tool? #428

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

Originally created by @joebonneau on GitHub (Aug 8, 2021).
Original GitHub issue: https://github.com/spotipy-dev/spotipy/issues/715

Hi there,

First and foremost, thanks so much for developing this package. Having a lot of fun using it!

I'm developing a CLI and I want to suppress the browser from opening without prompting the user for the URL. When authenticating using SpotifyOAuth and specifying open_browser=True, the browser opens a new tab and immediately closes it (assuming that I already gave the API permission to that function on my account, otherwise that appears and persists).

To avoid that, I tried specifying open_browser=False - here is what I get with this input (my application is called spoticli):

▶ spoticli play 
Playback resumed.
Go to the following URL: https://accounts.spotify.com/authorize?client_id=520cdb9e1e8e401480625d095e3ad2ed&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8887%2Fcallback&scope=user-read-playback-state
Enter the URL you were redirected to: 

Neither outcome is desirable for what I'd ideally want. Ideally, assuming the user has already given Spotify the proper permissions, the entire process is headless and seamless. Any thoughts on how to achieve this?

Thanks!

Originally created by @joebonneau on GitHub (Aug 8, 2021). Original GitHub issue: https://github.com/spotipy-dev/spotipy/issues/715 <!--- Please make sure you've: - read the FAQ https://github.com/plamere/spotipy/blob/master/FAQ.md - read the documentation https://spotipy.readthedocs.io/en/latest/ - searched older issues If your question is about code, please share the code you are using ---> Hi there, First and foremost, thanks so much for developing this package. Having a lot of fun using it! I'm developing a CLI and I want to suppress the browser from opening without prompting the user for the URL. When authenticating using `SpotifyOAuth` and specifying `open_browser=True`, the browser opens a new tab and immediately closes it (assuming that I already gave the API permission to that function on my account, otherwise that appears and persists). To avoid that, I tried specifying `open_browser=False` - here is what I get with this input (my application is called `spoticli`): ``` ▶ spoticli play Playback resumed. Go to the following URL: https://accounts.spotify.com/authorize?client_id=520cdb9e1e8e401480625d095e3ad2ed&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8887%2Fcallback&scope=user-read-playback-state Enter the URL you were redirected to: ``` Neither outcome is desirable for what I'd ideally want. Ideally, assuming the user has already given Spotify the proper permissions, the entire process is headless and seamless. Any thoughts on how to achieve this? Thanks!
kerem 2026-02-27 23:22:34 +03:00
  • closed this issue
  • added the
    question
    label
Author
Owner

@stephanebruckert commented on GitHub (Aug 8, 2021):

I'm not totally sure I understand. When signing in a user from a Spotify app (here, your CLI), the user has to get redirected to the Spotify login window at least once to prove authentication and generate a token. Are you trying to avoid that?

This particular feature (open_browser) is useful in case the machine doesn't have a browser installed (for example you ssh into a remote machine). But the user still has to open the browser somewhere else, on another machine.

<!-- gh-comment-id:894818585 --> @stephanebruckert commented on GitHub (Aug 8, 2021): I'm not totally sure I understand. When signing in a user from a Spotify app (here, your CLI), the user has to get redirected to the Spotify login window at least once to prove authentication and generate a token. Are you trying to avoid that? This particular feature (`open_browser`) is useful in case the machine doesn't have a browser installed (for example you ssh into a remote machine). But the user still has to open the browser somewhere else, on another machine.
Author
Owner

@joebonneau commented on GitHub (Aug 8, 2021):

the user has to get redirected to the Spotify login window at least once to prove authentication and generate a token. Are you trying to avoid that?

My intent was to have the user be redirected one time to generate the token and then avoid any browser involvement on the user end beyond that. I'm thinking the issue I'm running into here is related to the way that I'm implementing the authentication. For some context, I submitted this question on Stack Overflow yesterday because I was having trouble passing the auth object to other commands. To get around that, I created a function connect that is called with every command:

def connect(
    scope: str,
    client_id: str = SPOTIFY_CLIENT_ID,
    client_secret: str = SPOTIFY_CLIENT_SECRET,
    redirect_uri: str = SPOTIFY_REDIRECT_URI,
) -> sp.Spotify:
    try:
        auth = sp.Spotify(
            auth_manager=SpotifyOAuth(
                scope=scope,
                client_id=client_id,
                client_secret=client_secret,
                redirect_uri=redirect_uri,
            )
        )

        return auth
    except:
        click.secho(
            "API authorization failed! Did you remember to set the environment variables?",
            fg="red",
        )

where the specific scope required for the action is passed in. Example below:

def next_track():
    sp_auth = connect(scope=USER_MODIFY_PLAYBACK_STATE)
    sp_auth.next_track()

    get_current_playback(display=True)

After reading your response, I'm guessing that my issue is due to the fact that a new authentication is happening with each call which requires the browser to open. Maybe the real question here is how to pass around the auth object so that I can change the implementation in the app.

<!-- gh-comment-id:894828665 --> @joebonneau commented on GitHub (Aug 8, 2021): > the user has to get redirected to the Spotify login window at least once to prove authentication and generate a token. Are you trying to avoid that? My intent was to have the user be redirected one time to generate the token and then avoid any browser involvement on the user end beyond that. I'm thinking the issue I'm running into here is related to the way that I'm implementing the authentication. For some context, I submitted [this question on Stack Overflow](https://stackoverflow.com/questions/68688243/python-click-runtimeerror-when-passing-object-into-command?noredirect=1#comment121390153_68688243) yesterday because I was having trouble passing the auth object to other commands. To get around that, I created a function `connect` that is called with every command: ```python def connect( scope: str, client_id: str = SPOTIFY_CLIENT_ID, client_secret: str = SPOTIFY_CLIENT_SECRET, redirect_uri: str = SPOTIFY_REDIRECT_URI, ) -> sp.Spotify: try: auth = sp.Spotify( auth_manager=SpotifyOAuth( scope=scope, client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, ) ) return auth except: click.secho( "API authorization failed! Did you remember to set the environment variables?", fg="red", ) ``` where the specific `scope` required for the action is passed in. Example below: ```python def next_track(): sp_auth = connect(scope=USER_MODIFY_PLAYBACK_STATE) sp_auth.next_track() get_current_playback(display=True) ``` After reading your response, I'm guessing that my issue is due to the fact that a new authentication is happening with each call which requires the browser to open. Maybe the real question here is how to pass around the `auth` object so that I can change the implementation in the app.
Author
Owner

@stephanebruckert commented on GitHub (Aug 8, 2021):

Normally passing around sp_auth is the way to do it, so you would do:

sp_auth = connect(scope=USER_MODIFY_PLAYBACK_STATE)  # only once
next_track(sp_auth)
next_track(sp_auth)
next_track(sp_auth)

def next_track(sp_auth):
    sp_auth.next_track()
    get_current_playback(display=True)

So there is no need to connect multiple times, even though that still shouldn't open a new browser window every time, since the token is stored on the hard drive and spotipy is able to find the previous one. It doesn't seem to be the case. Can you verify that a hidden .cache file is stored in the same folder of your script? What OS are you using? Something tells me that your script is not allowed to create files, but it should. Can you see any error message/warning in the logs when running the script?

<!-- gh-comment-id:894830181 --> @stephanebruckert commented on GitHub (Aug 8, 2021): Normally passing around `sp_auth` is the way to do it, so you would do: sp_auth = connect(scope=USER_MODIFY_PLAYBACK_STATE) # only once next_track(sp_auth) next_track(sp_auth) next_track(sp_auth) def next_track(sp_auth): sp_auth.next_track() get_current_playback(display=True) So there is no need to connect multiple times, even though that still shouldn't open a new browser window every time, since the token is stored on the hard drive and spotipy is able to find the previous one. It doesn't seem to be the case. Can you verify that a hidden `.cache` file is stored in the same folder of your script? What OS are you using? Something tells me that your script is not allowed to create files, but it should. Can you see any error message/warning in the logs when running the script?
Author
Owner

@joebonneau commented on GitHub (Aug 8, 2021):

The method you mentioned was how I initially assumed I'd accomplish what I was trying to do, but it wasn't working in context of the click package I'm using to design the CLI. Maybe I need to go back to the drawing board on that piece.

There is a hidden .cache file that is generated and stored in the same folder as my script. I'm running on an Ubuntu machine. I don't see any specific error or warning other than that a GET or POST request was denied if authentication was unsuccessful.

<!-- gh-comment-id:894834971 --> @joebonneau commented on GitHub (Aug 8, 2021): The method you mentioned was how I initially assumed I'd accomplish what I was trying to do, but it wasn't working in context of the `click` package I'm using to design the CLI. Maybe I need to go back to the drawing board on that piece. There is a hidden `.cache` file that is generated and stored in the same folder as my script. I'm running on an Ubuntu machine. I don't see any specific error or warning other than that a `GET` or `POST` request was denied if authentication was unsuccessful.
Author
Owner

@stephanebruckert commented on GitHub (Aug 8, 2021):

To confirm whether the issue is in spotipy or not, you could try this example https://github.com/plamere/spotipy/blob/master/examples/my_playlists.py

Just run it multiple times with:

SPOTIPY_CLIENT_ID=replace SPOTIPY_CLIENT_SECRET=replace SPOTIPY_REDIRECT_URI=http://localhost:8080 python3 my_playlists.py

and it should only open the browser once.

<!-- gh-comment-id:894835561 --> @stephanebruckert commented on GitHub (Aug 8, 2021): To confirm whether the issue is in spotipy or not, you could try this example https://github.com/plamere/spotipy/blob/master/examples/my_playlists.py Just run it multiple times with: SPOTIPY_CLIENT_ID=replace SPOTIPY_CLIENT_SECRET=replace SPOTIPY_REDIRECT_URI=http://localhost:8080 python3 my_playlists.py and it should only open the browser once.
Author
Owner

@joebonneau commented on GitHub (Aug 8, 2021):

Was able to confirm that it's not a Spotipy issue at least!

Is there an example of how to make a new authorization if one doesn't exist or it's expired? Thinking I would both need to check if .cache exists and then also validate the token information within.

<!-- gh-comment-id:894836772 --> @joebonneau commented on GitHub (Aug 8, 2021): Was able to confirm that it's not a Spotipy issue at least! Is there an example of how to make a new authorization if one doesn't exist or it's expired? Thinking I would both need to check if `.cache` exists and then also validate the token information within.
Author
Owner

@stephanebruckert commented on GitHub (Aug 8, 2021):

an example of how to make a new authorization if one doesn't exist or it's expired

The my_playlists.py example does that, as by default spotipy looks for an existing token and refresh it if it is expired.

It only gets more complicated when looking to deal with multiple users, in which case the API/web example is probably interesting as it does more "manual" stuff https://github.com/plamere/spotipy/blob/master/examples/app.py

But let me know your use case and I might be able to help

<!-- gh-comment-id:894837576 --> @stephanebruckert commented on GitHub (Aug 8, 2021): > an example of how to make a new authorization if one doesn't exist or it's expired The `my_playlists.py` example does that, as by default spotipy looks for an existing token and refresh it if it is expired. It only gets more complicated when looking to deal with multiple users, in which case the API/web example is probably interesting as it does more "manual" stuff https://github.com/plamere/spotipy/blob/master/examples/app.py But let me know your use case and I might be able to help
Author
Owner

@joebonneau commented on GitHub (Aug 8, 2021):

I was able to fix the browser issue by changing my implementation of the authorization and how that object is being passed around the various commands. It was mostly an issue with how I set things up, but your insight about how things should work was super helpful and led me to the answer.

Thanks for taking the time to help me out - I really appreciate it!

<!-- gh-comment-id:894873510 --> @joebonneau commented on GitHub (Aug 8, 2021): I was able to fix the browser issue by changing my implementation of the authorization and how that object is being passed around the various commands. It was mostly an issue with how I set things up, but your insight about how things should work was super helpful and led me to the answer. Thanks for taking the time to help me out - I really appreciate it!
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#428
No description provided.