spotipy-dev / spotipy

A light weight Python library for the Spotify Web API
http://spotipy.readthedocs.org
MIT License
5.04k stars 959 forks source link

Enter the URL you were redirected to: <class 'EOFError'> #632

Open bpezzullo opened 3 years ago

bpezzullo commented 3 years ago

I am working on moving a small app using spotipy to Heruko. If is a flask based app and I am able to have it work when hosted on my machine. When I push it to Heroku I get this error and figure it has something to do with my redirect URI, but can't figure it out.

2021-01-02T16:58:54.820818+00:00 app[web.1]: Enter the URL you were redirected to: <class 'EOFError'> 2021-01-02T16:58:54.820865+00:00 app[web.1]: EOF when reading a line

The code I am using is

    sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope,
                                                    client_id=CLIENT_ID,
                                                    client_secret=CLIENT_SECRET,
                                                    redirect_uri=REDIRECT_URI,
                                                    username = user))

where the REDIRET_URI is set to https://127.0.0.1:8080. I have tried https://top100-search.herokuapp.com/redirect but can't figure out what is happening.

Any thoughts would be helpful.

Thanks

Peter-Schorn commented 3 years ago

You are getting an error because spotipy is attempting to read from standard input, which probably isn't supported in the environment that the app is running in (I've never used Heruko, so I can't confirm this). See this example.

bpezzullo commented 3 years ago

Thanks,  I ended up re-configuring a few things and have been able to get the basics working.  I appreciate the response. Regards,-Bill 

From: Peter SchornSent: Saturday, January 2, 2021 5:28 PM To: plamere/spotipy Cc: Bill Pezzullo; AuthorSubject: Re: [plamere/spotipy] Enter the URL you were redirected to: <class 'EOFError'> (#632) 

You are getting an error because spotipy is attempting to read from standard input, which probably isn't supported in the environment that the app is running in (I've never used Heruko, so I can't confirm this).—You are receiving this because you authored the thread.Reply to this email directly, view it on GitHub, or unsubscribe. 

ucalyptus2 commented 3 years ago

@Peter-Schorn so colab also would cause the same problem. Based on this article https://medium.com/@kshitijvijay271199/flask-on-google-colab-f6525986797b , do you think making the small tweak in examples/app.py would do the trick?

Peter-Schorn commented 3 years ago

@forkbabu I don't know what you're referring to. Please be more specific.

youngmulllababy commented 3 years ago

@bpezzullo Would you mind please sharing the solution for this, as I'm struggling with this as well.

JosephJHowlett commented 3 years ago

@bpezzullo me too, please share! thanks!

jtyson728 commented 2 years ago

Hi I am also getting this same issue, and not sure how to resolve based on the example @Peter-Schorn posted. @bpezzullo would you be able to share what you had to reconfigure?

stephanebruckert commented 2 years ago

@jtyson728 so we can help please share a minimal reproducible code example, and info about your environment, as well as where you are deploying

jtyson728 commented 2 years ago
from spotipy.oauth2 import SpotifyOAuth
spotify_client_id = os.environ['SPOTIPY_CLIENT_ID']
spotify_client_secret = os.environ['SPOTIPY_CLIENT_SECRET']
scope = "playlist-modify-public user-library-read user-modify-playback-state"
redirect_uri = os.environ['SPOTIPY_REDIRECT_URI']
spotify_username = os.environ['SPOT_USERNAME']

spot_token=SpotifyOAuth(username=spotify_username,client_id=spotify_client_id,
                        client_secret=spotify_client_secret,
                        redirect_uri=redirect_uri,
                        scope=scope,
                        show_dialog=True)
sp = spotipy.Spotify(auth_manager=spot_token)

I am running this from a python program, bot.py. Recently, I deployed this to Heroku hosting platform, and added environment variables to spotify client id, secret, redirect_uri, and spotify username into Heroku environment.

The problem is when you first connect via oAuth, running on my local I remember it redirecting to a browser page to authorize sign in, and then prompting in terminal "Enter the URL you were redirected to:". The problem is in Heroku, it is not starting this authorization flow.

The redirect URI is set to https://lacreme-bot.herokuapp.com/callback in both the Spotify application and in environment variables. Error output below Error Output

stephanebruckert commented 2 years ago

@jtyson728 this is expected as Heroku isn't a terminal like you have on your computer, so it won't be able to redirect or let you input the resulting URL. Therefore you'll have to generate your personal token locally and pass it to Heroku as an environment variable:

sp = spotipy.Spotify(auth=os.environ['SPOTIFY_TOKEN'])

https://spotipy.readthedocs.io/en/2.19.0/#spotipy.client.Spotify.init

This will work if the token age isn't older than 1hr, however it won't be able to refresh it as it is currently the case locally. To retrieve a long-lived token (lasts more than a year I think), you could instead get a token following https://github.com/enriquegh/spotify-webplayer-token. Although it feels a bit hacky, it can be a great solution for quick scripts or bots.


A better way to solve the refreshing token problem is to use an appropriate CacheHandler. Instead of the default FileCacheHandler, you could use MemoryCacheHandler so Heroku doesn't look for the token on its filesystem:

spot_token=SpotifyOAuth(username=spotify_username,
                        client_id=spotify_client_id,
                        client_secret=spotify_client_secret,
                        redirect_uri=redirect_uri,
                        scope=scope,
                        show_dialog=False, # Make this False or just remove the line so it doesn't open a browser
                        cache_handler=MemoryCacheHandler(
                             token_info=TOKEN_INFO
                        ))

To know what TOKEN_INFO should look like, print it using spot_token.get_access_token() locally and pass it as an ENV in Heroku.

https://spotipy.readthedocs.io/en/2.19.0/?highlight=cache#customized-token-caching

I agree documentation should be improved on this front, so feel free to update us where it blocks and we'll help.

jtyson728 commented 2 years ago

This has been excellent help!! I tried the 1st method (https://github.com/enriquegh/spotify-webplayer-token), however that token seems to be expiring after an hour. I believe I might've done something wrong grabbing the cookies.

Nevertheless, I would rather implement the MemoryCacheHandler solution anyways. When I add in:

cache_handler=MemoryCacheHandler(token_info=TOKEN_INFO)

it says that

Traceback (most recent call last):
  File "C:\Users\Jeremy Tyson\Desktop\laCreme-bot\bot.py", line 52, in <module>
    cache_handler=MemoryCacheHandler(token_info=spot_token_info))
NameError: name 'MemoryCacheHandler' is not defined

Is there another example of the MemoryCacheHandler that I can see, my apologies I might just be using it wrong! :/

stephanebruckert commented 2 years ago

Make sure you upgrade to 2.19

pip install spotipy --upgrade
jtyson728 commented 2 years ago

Hi so I upgraded to latest version, unfortunately I am still geting EOFError when I start up the app on Heroku. Here is what I did:

  1. Print the access token of the SpotifyOAuth spot_token locally, and then pass that into environment variable in my Heroku settings.
    spot_token=SpotifyOAuth(username=spotify_username,client_id=spotify_client_id,
                        client_secret=spotify_client_secret,
                        redirect_uri=redirect_uri,
                        scope=scope)
    print(f'This is access token----> {spot_token.get_access_token(as_dict=False)}')    #copied this token value to heroku
  2. Added Memory cache handler to spotifyOAuth object that takes in stored access token from local run into Heroku code
    cache_handler = spotipy.cache_handler.MemoryCacheHandler(token_info=spot_token_info)
    spot_token=SpotifyOAuth(username=spotify_username,client_id=spotify_client_id,
                        client_secret=spotify_client_secret,
                        redirect_uri=redirect_uri,
                        scope=scope,
                        cache_handler=cache_handler)
    sp = spotipy.Spotify(auth_manager=spot_token, requests_timeout=15, retries=10)
  3. Ran app via Heroku, noticed in logs that it is still trying to read the redirect url from input.
    2021-12-27T17:51:44.313820+00:00 app[worker.1]: Enter the URL you were redirected to: Unhandled exception in internal background task 'clear_weekly'.

I'm not exactly sure how avoid the redirect URL aspect while still using the SpotifyOAuth auth_manager flow, or what part exactly is messing it up.

Also added update, the 1-year spotify access token retrieved using https://github.com/enriquegh/spotify-webplayer-token is only lasting for 1 hour, and then expiring. I have posted this as an issue to his git repo, no response yet.

Peter-Schorn commented 2 years ago

@jtyson728 MemoryCacheHandler, as well as all the other cache handlers, need more than just the access token. They need a dictionary that contains the access token, expiration date, and, optionally, the refresh token and scopes. Go through the authorization process again locally using MemoryCacheHandler. Then print the MemoryCacheHandler.token_info instance property. You will see that it is a dictionary containing the aforementioned properties. You need to pass this same value back into the initializer for MemoryCacheHandler.

Addimator commented 2 years ago

That was what helped me, thank you!!! A bit more detailed described:

  1. After authenticating print locally the token (As @Peter-Schorn said you have to use the full dictionary:)

token = SpotifyOAuth(scope=scope, username=username, client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, show_dialog=False) print(token.get_access_token(as_dict=True))

The token dictionary should look like this:

{'access_token': 'BQB9j0gAfP3xEkZ6gr9KsOi03ufLc6Utx_pcnjOWTIDYNqRYMCyFivb49zd5Vj8yIjn6jqWJUSqJ-ipg1_7kxxhYlUDOoDs32qIT_zToYzJw40htJHZhQic4KOEGsjJRSFVzOXMHNijbAz3G9DbTjVw0j9KrTKDj0Kb8CPSrugwFV6QUwoQDIAbAxItgE0JYzKBxIckBzw5celmF-SrX8C8-dIFr8W8dj2ommSq6oRhwWy6E_DN_JgGW6BYOYLg4hBuTSaNw3c9OepMLLcL9uUs9uQ_9FCCo', 'token_type': 'Bearer', 'expires_in': 3600, 'scope': 'user-read-recently-played ugc-image-upload user-read-playback-state user-modify-playback-state user-read-currently-playing user-read-private user-read-email user-follow-modify user-follow-read user-library-modify user-library-read streaming app-remote-control user-read-playback-position user-top-read user-read-recently-played playlist-modify-private playlist-read-collaborative playlist-read-private playlist-modify-public', 'expires_at': 1641915649, 'refresh_token': 'AQB-hyj7zwgwwcYV9JnUYxUGd8cuNW53BjonxlgWUm9tvyOIUMl8wt1o2WtCdDmayIWNBckc-X7uK_3BUldn3glDe48DGx4Lsh4ZFdHEDAanTUc5ZusiYX2vSqPR_uyI5Pw'}

  1. Copy the dictionary in the global variable token_info. Change the authentification to:

token = SpotifyOAuth(scope=scope, username=username, client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, show_dialog=False, cache_handler=MemoryCacheHandler(token_info=token_info))

  1. Now it should work. Since the app now internally opens the URL you have to give it access to the internet. You do this by adjusting your buildozer.spec file by:

# (list) Permissions android.permissions = INTERNET

This is how it worked for me. Thank you @stephanebruckert and @Peter-Schorn

Addimator commented 2 years ago

My problem is now, that it is not possible to do this for different users in the app (via login), since one needs a new access token every time. Has anyone an idea how to deal with this problem?

bpezzullo commented 2 years ago

I haven’t followed what you are trying to do, but I ask the user to log into Spotify every time they start up the App. Then they can transfer a playlist that they created from a set of top 100 songs over the years than I have. They can’t play the songs.

Not sure if that helps.

From: Addimator @.> Sent: Tuesday, January 11, 2022 11:10 AM To: plamere/spotipy @.> Cc: Bill Pezzullo @.>; Mention @.> Subject: Re: [plamere/spotipy] Enter the URL you were redirected to: <class 'EOFError'> (#632)

My problem is now, that it is not possible to do this for different users in the app (via login), since one needs a new access token every time. Has anyone an idea how to deal with this problem?

— Reply to this email directly, view it on GitHub https://github.com/plamere/spotipy/issues/632#issuecomment-1010117398 , or unsubscribe https://github.com/notifications/unsubscribe-auth/APVZN7XJ2QVHEKTYF6GZXRDUVRI4NANCNFSM4VRGC7LQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub . You are receiving this because you were mentioned. https://github.com/notifications/beacon/APVZN7ROTVSNJLNZTVFJ5T3UVRI4NA5CNFSM4VRGC7L2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOHQ2SWFQ.gif Message ID: @. @.> >

Addimator commented 2 years ago

But how do you do the authentification then? Doesn't it redirect you to your redirect URL and than throws the error described above?

Zhiming77 commented 1 year ago

I solved the initial EOFError that brought me here, using the following code:

sp = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id=client_id,
                                                 client_secret=client_secret,
                                                 redirect_uri=redirect_uri,
                                                 scope=scope,
                                                show_dialog=False,
                                               open_browser=False,
                                               cache_handler=MemoryCacheHandler(token_info=token_info)
                                               ).get_access_token(as_dict=True, check_cache=True))

The bot is "successfully" hosted on Heroku, and I can use commands that don't access the Spotify API. However, now when I try to access the API in any manner, for example, playlists = sp.current_user_playlists()

I get the following error:

2023-07-12T12:29:03.090777+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.11/site-packages/spotipy/client.py", line 338, in _put 2023-07-12T12:29:03.090777+00:00 app[web.1]: return self._internal_call("PUT", url, payload, kwargs) 2023-07-12T12:29:03.090777+00:00 app[web.1]: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2023-07-12T12:29:03.090778+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.11/site-packages/spotipy/client.py", line 247, in _internal_call 2023-07-12T12:29:03.090778+00:00 app[web.1]: headers = self._auth_headers() 2023-07-12T12:29:03.090778+00:00 app[web.1]: ^^^^^^^^^^^^^^^^^^^^ 2023-07-12T12:29:03.090778+00:00 app[web.1]: File "/app/.heroku/python/lib/python3.11/site-packages/spotipy/client.py", line 238, in _auth_headers 2023-07-12T12:29:03.090779+00:00 app[web.1]: token = self.auth_manager.get_access_token(as_dict=False) 2023-07-12T12:29:03.090779+00:00 app[web.1]: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2023-07-12T12:29:03.090780+00:00 app[web.1]: AttributeError: 'dict' object has no attribute 'get_access_token'

Why is self.auth_manager appearing as a dictionary herein (it would seem based on the spotipy code that it should not be appearing as a dictionary)?

Thank you for any and all assistance. It's really making my head spin!