Skip to content

Replaced spotipy with a non-api library#2626

Open
TzurSoffer wants to merge 12 commits intospotDL:masterfrom
TzurSoffer:master
Open

Replaced spotipy with a non-api library#2626
TzurSoffer wants to merge 12 commits intospotDL:masterfrom
TzurSoffer:master

Conversation

@TzurSoffer
Copy link
Copy Markdown

@TzurSoffer TzurSoffer commented Mar 15, 2026

Title

Due to recent changes in Spotify, the api is no longer useful to non-premium users, therfore I have replaced it.

Description

I have made a wrapper to spotAPI, which functions as a drag-n-drop replacement to spotipy, it not perfect, but it has fixed spotdl.
To install working verison run pip install git+https://github.com/TzurSoffer/spotify-downloader
If this helped you a star to my fork and bump would be appreciated to help promote this fix.

Related Issue

#2621
#2617

Motivation and Context

It solves the problem of the app not working without Spotify Premium.

How Has This Been Tested?

I have tested both playlists and individual songs.

Types of Changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist

  • My code follows the code style of this project
  • My change requires a change to the documentation
  • I have updated the documentation accordingly
  • I have read the CONTRIBUTING document
  • I have added tests to cover my changes
  • All new and existing tests passed

@TzurSoffer
Copy link
Copy Markdown
Author

I currently have a hardcoded variable in the Spotify.py file, but this should be a config option. I tried to make it a config option, but I am unfamiliar with this code and couldn't.

@TzurSoffer
Copy link
Copy Markdown
Author

Hey, is there any progress towards merging this PR?

@DylanBPY
Copy link
Copy Markdown

This branch works on my system

@DylanBPY
Copy link
Copy Markdown

@TzurSoffer Have you seen this?

@TzurSoffer
Copy link
Copy Markdown
Author

if you see that error, you didnt install my fork as it does not use api.spotify.com...
if you do spotdl --version does it print 4.4.5 or 4.4.3?

@gripfastistech
Copy link
Copy Markdown

if you see that error, you didnt install my fork as it does not use api.spotify.com... if you do spotdl --version does it print 4.4.5 or 4.4.3?

4.4.5

I had too many pre-existing versions hanging around, was pointing back to the previous .local install.

After removing everything spotdl related, reinstalled again from the fork, and its processing, without the error so far.

Thanks!

@sidelkins
Copy link
Copy Markdown

Works on my system! Thanks!

@m111k333
Copy link
Copy Markdown

works very well,thank you!

@ciangottinim
Copy link
Copy Markdown

ciangottinim commented Mar 26, 2026

I tried it with the "artist" option and it doesn't work, I don't know if I'm doing something wrong or not; I run the command on a linux headless server, I attached the command and the error here:

Command: spotdl download artist:X --headless --output {artist}/{album}/{title}.{output-ext} --format m4a
Error: error.txt

@TzurSoffer
Copy link
Copy Markdown
Author

Artists are not yet supported, download a playlist with all his songs instead for now

@taco-mustard
Copy link
Copy Markdown

taco-mustard commented Mar 27, 2026

Thanks, it works pretty well! Just running into a couple errors at the moment (running this on mac):

  1. Trying to download spotify playlist from youtube playlist of same length via spotdl "{youtube playlist url}|{spotify playlist url}" results in AttributeError: 'Downloader' object has no attribute 'download_url'
  2. Downloading spotify playlists of over about 30 songs (after using command ulimit -n 10000) results in AudioProviderError: YT-DLP download error

@TzurSoffer
Copy link
Copy Markdown
Author

@taco-mustard Please provide the commands as well as the output from the terminal so I can fix replicate it

@taco-mustard
Copy link
Copy Markdown

taco-mustard commented Mar 27, 2026

This was the code that led to the AttributeError
spotdl "https://www.youtube.com/playlist?list=PL7fDRdzfbWcwVwup1gMdeO46S7-lMwFG2|https://open.spotify.com/playlist/2G42OAnMtsPSeUDmsC9TBt?si=UIpV6CXnRA-yrDnkf0bfhA"

This was the output

Found 121 songs in dl_songs (Playlist)                                                                                                                                                                                 
AttributeError: 'Downloader' object has no attribute 'download_url' ```

@TzurSoffer
Copy link
Copy Markdown
Author

TzurSoffer commented Mar 27, 2026

@taco-mustard It possible that youtube is bugged, I dont have time to fix it rn. for now, you can do
spotdl "https://open.spotify.com/playlist/2G42OAnMtsPSeUDmsC9TBt"
if you have trouble with yt-dlp, update it pip install -U yt-dlp

@taco-mustard
Copy link
Copy Markdown

changing candidate_urls = [self.download_url] to candidate_urls = [song.download_url] in line 716 of spotdl/download/downloader.py appears to fix the issue for me

@trotskylenin
Copy link
Copy Markdown

It works!
Thanks!!

@VagueKEK
Copy link
Copy Markdown

VagueKEK commented Apr 8, 2026

works like a charm!

@work-ham
Copy link
Copy Markdown

work-ham commented Apr 9, 2026

Thankyouu

@yonka2019
Copy link
Copy Markdown

someone got error "Could not get session auth tokens"?

@horzel02
Copy link
Copy Markdown

horzel02 commented Apr 9, 2026

thanks bro

@CrumplyLime
Copy link
Copy Markdown

someone got error "Could not get session auth tokens"?

Yeah I keep getting that too

@JonneSaloranta
Copy link
Copy Markdown

@yonka2019 @CrumplyLime are you trying to use Spotify API with auth tokens? if so, do you have an active Spotify subscription and made sure your "Spotify app" is not disabled? This is due to recent Spotify API changes which also disabled my "app"

@CrumplyLime
Copy link
Copy Markdown

@yonka2019 @CrumplyLime are you trying to use Spotify API with auth tokens? if so, do you have an active Spotify subscription and made sure your "Spotify app" is not disabled? This is due to recent Spotify API changes which also disabled my "app"

Yeah, I've got everything in order for my auth token except for the subscription, but I thought this new pull request was meant to address the subscription exclusivity and make it so non-premium members can also download songs?

I hope I haven't misinterpreted the purpose of this pr

@JonneSaloranta
Copy link
Copy Markdown

@yonka2019 @CrumplyLime are you trying to use Spotify API with auth tokens? if so, do you have an active Spotify subscription and made sure your "Spotify app" is not disabled? This is due to recent Spotify API changes which also disabled my "app"

Yeah, I've got everything in order for my auth token except for the subscription, but I thought this new pull request was meant to address the subscription exclusivity and make it so non-premium members can also download songs?

I hope I haven't misinterpreted the purpose of this pr

I think this PR is just a workaround the Spotify API changes by using different package which does not rely on Spotify. Try removing your auth tokens and see if it works.

@CrumplyLime
Copy link
Copy Markdown

@yonka2019 @CrumplyLime are you trying to use Spotify API with auth tokens? if so, do you have an active Spotify subscription and made sure your "Spotify app" is not disabled? This is due to recent Spotify API changes which also disabled my "app"

Yeah, I've got everything in order for my auth token except for the subscription, but I thought this new pull request was meant to address the subscription exclusivity and make it so non-premium members can also download songs?
I hope I haven't misinterpreted the purpose of this pr

I think this PR is just a workaround the Spotify API changes by using different package which does not rely on Spotify. Try removing your auth tokens and see if it works.

Unfortunately I still get the same error message, so I'm just gonna assume for the time being I'll need some valid auth tokens. I'm gonna leave my console output here though, hopefully it might be of use

An error occurred
╭───────────────────────────────────────── Traceback (most recent call last) ──────────────────────────────────────────╮
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotdl\console\entry_point.py:160 in        │
│ entry_point                                                                                                          │
│                                                                                                                      │
│   157 │   try:                                                                                                       │
│   158 │   │   # Pick the operation to perform                                                                        │
│   159 │   │   # based on the name and run it!                                                                        │
│ ❱ 160 │   │   OPERATIONS[arguments.operation](                                                                       │
│   161 │   │   │   query=arguments.query,                                                                             │
│   162 │   │   │   downloader=downloader,                                                                             │
│   163 │   │   )                                                                                                      │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotdl\console\download.py:25 in download   │
│                                                                                                                      │
│   22 │   """                                                                                                         │
│   23 │                                                                                                               │
│   24 │   # Parse the query                                                                                           │
│ ❱ 25 │   songs = get_simple_songs(                                                                                   │
│   26 │   │   query,                                                                                                  │
│   27 │   │   use_ytm_data=downloader.settings["ytm_data"],                                                           │
│   28 │   │   playlist_numbering=downloader.settings["playlist_numbering"],                                           │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotdl\utils\search.py:264 in               │
│ get_simple_songs                                                                                                     │
│                                                                                                                      │
│   261 │   │   │   )                                                                                                  │
│   262 │   │   │   songs.extend(full_lists)                                                                           │
│   263 │   │   elif "open.spotify.com" in request and "playlist" in request:                                          │
│ ❱ 264 │   │   │   lists.append(Playlist.from_url(request, fetch_songs=False))                                        │
│   265 │   │   elif "open.spotify.com" in request and "album" in request:                                             │
│   266 │   │   │   lists.append(Album.from_url(request, fetch_songs=False))                                           │
│   267 │   │   elif "open.spotify.com" in request and "artist" in request:                                            │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotdl\types\song.py:306 in from_url        │
│                                                                                                                      │
│   303 │   │   - The SongList object.                                                                                 │
│   304 │   │   """                                                                                                    │
│   305 │   │                                                                                                          │
│ ❱ 306 │   │   metadata, songs = cls.get_metadata(url)                                                                │
│   307 │   │   urls = [song.url for song in songs]                                                                    │
│   308 │   │                                                                                                          │
│   309 │   │   if fetch_songs:                                                                                        │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotdl\types\playlist.py:48 in get_metadata │
│                                                                                                                      │
│    45 │   │                                                                                                          │
│    46 │   │   spotify_client = SpotifyClient()                                                                       │
│    47 │   │                                                                                                          │
│ ❱  48 │   │   playlist = spotify_client.playlist(url)                                                                │
│    49 │   │   if playlist is None:                                                                                   │
│    50 │   │   │   raise PlaylistError("Invalid playlist URL.")                                                       │
│    51                                                                                                                │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\SpotipyFree\Spotify.py:198 in playlist      │
│                                                                                                                      │
│   195 │   │   return(self._addChunkInfo(merged, total, limit, offset, end))                                          │
│   196 │                                                                                                              │
│   197 │   def playlist(self, playlistId, limit=-1, offset=0, *args, **kwargs):                                       │
│ ❱ 198 │   │   playlist =                                                                                             │
│       spotapi.PublicPlaylist(playlistId).get_playlist_info()["data"]["playlistV2"]                                   │
│   199 │   │   playlist["owner"] = playlist["ownerV2"]["data"]                                                        │
│   200 │   │   playlist.pop("ownerV2", None)                                                                          │
│   201 │   │   playlist["owner"]["display_name"] = playlist["owner"]["name"]                                          │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotapi\types\annotations.py:47 in wrapper  │
│                                                                                                                      │
│    44 │   │   │   │   │   │   f"but got {format_type(type(arg_value))}"                                              │
│    45 │   │   │   │   │   )                                                                                          │
│    46 │   │                                                                                                          │
│ ❱  47 │   │   result: R = func(*args, **kwargs)                                                                      │
│    48 │   │                                                                                                          │
│    49 │   │   if return_type is not None and not is_instance_of(result, return_type):                                │
│    50 │   │   │   raise TypeError(                                                                                   │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotapi\playlist.py:72 in get_playlist_info │
│                                                                                                                      │
│    69 │   │   │   │   {                                                                                              │
│    70 │   │   │   │   │   "persistedQuery": {                                                                        │
│    71 │   │   │   │   │   │   "version": 1,                                                                          │
│ ❱  72 │   │   │   │   │   │   "sha256Hash": self.base.part_hash("fetchPlaylist"),                                    │
│    73 │   │   │   │   │   }                                                                                          │
│    74 │   │   │   │   }                                                                                              │
│    75 │   │   │   ),                                                                                                 │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotapi\types\annotations.py:47 in wrapper  │
│                                                                                                                      │
│    44 │   │   │   │   │   │   f"but got {format_type(type(arg_value))}"                                              │
│    45 │   │   │   │   │   )                                                                                          │
│    46 │   │                                                                                                          │
│ ❱  47 │   │   result: R = func(*args, **kwargs)                                                                      │
│    48 │   │                                                                                                          │
│    49 │   │   if return_type is not None and not is_instance_of(result, return_type):                                │
│    50 │   │   │   raise TypeError(                                                                                   │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotapi\client.py:214 in part_hash          │
│                                                                                                                      │
│   211 │                                                                                                              │
│   212 │   def part_hash(self, name: str) -> str:                                                                     │
│   213 │   │   if self.raw_hashes is _Undefined:                                                                      │
│ ❱ 214 │   │   │   self.get_sha256_hash()                                                                             │
│   215 │   │                                                                                                          │
│   216 │   │   if self.raw_hashes is _Undefined:                                                                      │
│   217 │   │   │   raise ValueError("Could not get playlist hashes")                                                  │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotapi\types\annotations.py:47 in wrapper  │
│                                                                                                                      │
│    44 │   │   │   │   │   │   f"but got {format_type(type(arg_value))}"                                              │
│    45 │   │   │   │   │   )                                                                                          │
│    46 │   │                                                                                                          │
│ ❱  47 │   │   result: R = func(*args, **kwargs)                                                                      │
│    48 │   │                                                                                                          │
│    49 │   │   if return_type is not None and not is_instance_of(result, return_type):                                │
│    50 │   │   │   raise TypeError(                                                                                   │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotapi\client.py:226 in get_sha256_hash    │
│                                                                                                                      │
│   223 │                                                                                                              │
│   224 │   def get_sha256_hash(self) -> None:                                                                         │
│   225 │   │   if self.js_pack is _Undefined:                                                                         │
│ ❱ 226 │   │   │   self.get_session()                                                                                 │
│   227 │   │                                                                                                          │
│   228 │   │   if self.js_pack is _Undefined:                                                                         │
│   229 │   │   │   raise ValueError("Could not get playlist hashes")                                                  │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotapi\types\annotations.py:47 in wrapper  │
│                                                                                                                      │
│    44 │   │   │   │   │   │   f"but got {format_type(type(arg_value))}"                                              │
│    45 │   │   │   │   │   )                                                                                          │
│    46 │   │                                                                                                          │
│ ❱  47 │   │   result: R = func(*args, **kwargs)                                                                      │
│    48 │   │                                                                                                          │
│    49 │   │   if return_type is not None and not is_instance_of(result, return_type):                                │
│    50 │   │   │   raise TypeError(                                                                                   │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotapi\client.py:170 in get_session        │
│                                                                                                                      │
│   167 │   │                                                                                                          │
│   168 │   │   self.client_version = self.server_cfg["clientVersion"]                                                 │
│   169 │   │   self.device_id = self.client.cookies.get("sp_t") or ""                                                 │
│ ❱ 170 │   │   self._get_auth_vars()                                                                                  │
│   171 │                                                                                                              │
│   172 │   def get_client_token(self) -> None:                                                                        │
│   173 │   │   if not (self.client_id and self.device_id and self.client_version):                                    │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotapi\types\annotations.py:47 in wrapper  │
│                                                                                                                      │
│    44 │   │   │   │   │   │   f"but got {format_type(type(arg_value))}"                                              │
│    45 │   │   │   │   │   )                                                                                          │
│    46 │   │                                                                                                          │
│ ❱  47 │   │   result: R = func(*args, **kwargs)                                                                      │
│    48 │   │                                                                                                          │
│    49 │   │   if return_type is not None and not is_instance_of(result, return_type):                                │
│    50 │   │   │   raise TypeError(                                                                                   │
│                                                                                                                      │
│ C:\Users\arsha\AppData\Local\Programs\Python\Python310\lib\site-packages\spotapi\client.py:134 in _get_auth_vars     │
│                                                                                                                      │
│   131 │   │   │   resp = self.client.get("https://open.spotify.com/api/token", params=query)                         │
│   132 │   │   │                                                                                                      │
│   133 │   │   │   if resp.fail:                                                                                      │
│ ❱ 134 │   │   │   │   raise BaseClientError(                                                                         │
│   135 │   │   │   │   │   "Could not get session auth tokens", error=resp.error.string                               │
│   136 │   │   │   │   )                                                                                              │
│   137                                                                                                                │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
BaseClientError: Could not get session auth tokens

@JonneSaloranta
Copy link
Copy Markdown

131 │ │ │ resp = self.client.get("https://open.spotify.com/api/token", params=query)

Yeah seems like it's still trying to use the Spotify API.
Are you using venv? Did you install the pip package with pip install git+https://github.com/TzurSoffer/spotify-downloader?

@CrumplyLime
Copy link
Copy Markdown

131 │ │ │ resp = self.client.get("https://open.spotify.com/api/token", params=query)

Yeah seems like it's still trying to use the Spotify API. Are you using venv? Did you install the pip package with pip install git+https://github.com/TzurSoffer/spotify-downloader?

Yeah, I used the recommended download command, the one you're referencing. And no, I'm running this out of a powershell on my native Win11 machine, no virtual envs

@JonneSaloranta
Copy link
Copy Markdown

JonneSaloranta commented Apr 10, 2026

Yeah that could cause the issue. Try creating a venv and installing the PR there. In case you are not familiar with it try creating a folder open terminal in the folder and run python (or python3 or py or full path for the python.exe) -m venv {folder name} then activate the venv
e.g open terminal here c:/user/documents/spotdl then run python -m venv venv and venv\Scripts\activate.bat and python pip install {package}
Here's the docs https://docs.python.org/3/library/venv.html

@CrumplyLime
Copy link
Copy Markdown

CrumplyLime commented Apr 10, 2026

Yeah that could cause the issue. Try creating a venv and installing the PR there. In case you are not familiar with it try creating a folder open terminal in the folder and run python (or python3 or py or full path for the python.exe) -m venv {folder name} then activate the venv e.g open terminal here c:/user/documents/spotdl then run python -m venv venv and venv\Scripts\activate.bat and python pip install {package} Here's the docs https://docs.python.org/3/library/venv.html

That was certainly interesting trying this out, never made a virtual env before. But yeah I still have the same error output on my machine; I followed your instructions, then I went ahead and ran spotdl with my playlist in the venv but I still get the BaseClientError: Could not get session auth tokens output

@JonneSaloranta
Copy link
Copy Markdown

JonneSaloranta commented Apr 10, 2026

Have you tried deleting your .spotdl folder from the c:/users/YOURACCOUNT/.spotdl? there might be a cache file which is causing this error. Make sure you have enabled "show hidden files"

@yonka2019
Copy link
Copy Markdown

the error isnt consistent

@CrumplyLime
Copy link
Copy Markdown

Have you tried deleting your .spotdl folder from the c:/users/YOURACCOUNT/.spotdl? there might be a cache file which is causing this error. Make sure you have enabled "show hidden files"

Yeah, even with that folder deleted the issue persists for me

@TzurSoffer
Copy link
Copy Markdown
Author

What does running spotdl --version return?

@CrumplyLime
Copy link
Copy Markdown

What does running spotdl --version return?

It should be the latest one at 4.4.6

@TzurSoffer
Copy link
Copy Markdown
Author

Then run spotdl --generate-config, it should reset any changes you made to the config. This WILL reset your config, so if you made any changes, I suggest you back it up

@CrumplyLime
Copy link
Copy Markdown

spotdl --generate-config

I went ahead and did that, after updating the newest version, but I'm still getting the same error

@TzurSoffer
Copy link
Copy Markdown
Author

Thats odd... What command are you using? Also, does running spotdl https://open.spotify.com/track/4PTG3Z6ehGkBFwjybzWkR8 work?

@CrumplyLime
Copy link
Copy Markdown

Thats odd... What command are you using? Also, does running spotdl https://open.spotify.com/track/4PTG3Z6ehGkBFwjybzWkR8 work?

Nope that fails also with the same error message, I've been using spotdl.exe https://open.spotify.com/playlist/7LZt4OxIPxsTwOcvrjdf5o?si=328f0d50f32943ef the entire time

@TzurSoffer
Copy link
Copy Markdown
Author

I didn't provide a compiled exe...

@JonneSaloranta
Copy link
Copy Markdown

Thats odd... What command are you using? Also, does running spotdl https://open.spotify.com/track/4PTG3Z6ehGkBFwjybzWkR8 work?

Nope that fails also with the same error message, I've been using spotdl.exe https://open.spotify.com/playlist/7LZt4OxIPxsTwOcvrjdf5o?si=328f0d50f32943ef the entire time

This is most likely the issue. You are updating the spotdl package but using old exe binary

@CrumplyLime
Copy link
Copy Markdown

CrumplyLime commented Apr 10, 2026

Oh, I'm sorry I was accidentally using the .exe binary I saw in one of the directories in the venv. But I tried it regularly as spotdl https://open.spotify.com/track/4PTG3Z6ehGkBFwjybzWkR8 in the venv main directory as well and the issue persists on my end

@pdao1
Copy link
Copy Markdown

pdao1 commented Apr 11, 2026

Ran your pip install git+https://github.com/TzurSoffer/spotify-downloader command and now spotdl <song/playlist url> works

you are the man/woman! <3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.