zotify-dev / zotify

A fast and customizable music and podcast downloader.
zlib License
1.03k stars 79 forks source link

Failed fetching audio key! #186

Open Cameron1391 opened 2 weeks ago

Cameron1391 commented 2 weeks ago

Hello. This software has been working perfect for me up until today. I get multiple errors trying to download albums and tracks. "Failed fetching audio key!

Failed fetching audio key - gid: 34a47982845a47808079a3c77396e6ae, fileId: f50977596f4be8ce0e3f918d51009daef606fb9c

        [∙∙∙] Preparing download... Traceback (most recent call last):
  File "C:\ProgramData\anaconda3\Lib\site-packages\zotify\track.py", line 224, in download_track
    stream = Zotify.get_content_stream(track, Zotify.DOWNLOAD_QUALITY)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\ProgramData\anaconda3\Lib\site-packages\zotify\zotify.py", line 53, in get_content_stream
    return cls.SESSION.content_feeder().load(content_id, VorbisOnlyAudioQuality(quality), False, None)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\ProgramData\anaconda3\Lib\site-packages\librespot\audio\__init__.py", line 739, in load
    return self.load_track(playable_id, audio_quality_picker, preload,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\ProgramData\anaconda3\Lib\site-packages\librespot\audio\__init__.py", line 800, in load_track
    return self.load_stream(file, track, None, preload, halt_listener)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\ProgramData\anaconda3\Lib\site-packages\librespot\audio\__init__.py", line 754, in load_stream
    return CdnFeedHelper.load_track(self.__session, track, file,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\ProgramData\anaconda3\Lib\site-packages\librespot\audio\__init__.py", line 339, in load_track
    key = session.audio_key().get_audio_key(track.gid, file.file_id)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\ProgramData\anaconda3\Lib\site-packages\librespot\audio\__init__.py", line 277, in get_audio_key
    return self.get_audio_key(gid, file_id, False)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\ProgramData\anaconda3\Lib\site-packages\librespot\audio\__init__.py", line 278, in get_audio_key
    raise RuntimeError(
RuntimeError: Failed fetching audio key! gid: 34a47982845a47808079a3c77396e6ae, fileId: f50977596f4be8ce0e3f918d51009daef606fb9c

This now happens on full albums and individual tracks. I haven't touched or changed anything. Does anyone know what the problem is. Thanks.

BionztheGod commented 2 weeks ago

I have the same problem, but it only occurs when I use the download a playlist feature. If I just download the song everything works fine.

Cameron1391 commented 2 weeks ago

I seemed to have fixed the problem. I reinstalled Anaconda3 and put it in a new folder in ProgramFIles. It downloads again after that.

BionztheGod commented 2 weeks ago

I never installed anaconda3 what am I supposed to do now?

Cameron1391 commented 2 weeks ago

interesting must be a bug in the app

LineSurfer commented 2 weeks ago

i'm having the same issue. it will download a few songs then this error will occur.

x528491x commented 2 weeks ago

I seemed to have fixed the problem. I reinstalled Anaconda3 and put it in a new folder in ProgramFIles. It downloads again after that.

I doubt it's related to that. I'm facing the same bug on a zotify clone that I wrote in rust.

It appears to be caused by some rate limiting introduced by Spotify.

Try throttling the speed of the download and you won't face the errors.

Edit:

I guess the --download-real-time flag is a necessity now, not just for preventing bans, but for actually being able to download the songs.

x528491x commented 2 weeks ago

I supposed a decent fix at the moment would be to either

  1. Somehow figure out the ratelimit by trial and error and download just under that limit (this must be higher than --real-time), or

  2. When the playlist fails to download due to this rate limit, retry with --download-real-time, and then download at normal speed for the next song onwards. This should be able to squeeze out maximum throughput and replenish the ratelimit with a slower download when the ratelimit is hit.

BionztheGod commented 2 weeks ago

Is there anyway to enter a buffer so it doesn't download that fast? The bulk time thing doesn't work for me

Cameron1391 commented 2 weeks ago

I seemed to have fixed the problem. I reinstalled Anaconda3 and put it in a new folder in ProgramFIles. It downloads again after that.

I doubt it's related to that. I'm facing the same bug on a zotify clone that I wrote in rust.

It appears to be caused by some rate limiting introduced by Spotify.

Try throttling the speed of the download and you won't face the errors.

Edit:

I guess the --download-real-time flag is a necessity now, not just for preventing bans, but for actually being able to download the songs.

Thank you i'll also give that a go

tig3rmast3r commented 2 weeks ago

if they have implemented a bandwidth check maybe we just need a throttle on the download loop, like a simple configurable pause between each download, like 1 minute or so... next time i need it i'll do some tests..

MichaelDevon commented 2 weeks ago

This can either be fixed with the--download-real-time=True flag, or changing your IP with a VPN.

LineSurfer commented 2 weeks ago

I can confirm. Using --download-real-time=True makes the downloads just fine. Downside is, downloading 150 songs will take you for about more than 6 hours.

BionztheGod commented 2 weeks ago

I just let it run once, then wait for a moment and then let it run over again.

LineSurfer commented 2 weeks ago

I just let it run once, then wait for a moment and then let it run over again.

may i know what's your config?

BionztheGod commented 2 weeks ago

I haven't really changed anything besides the retry attempts, I set those to 5 and to skip existing and previously downloaded to true

iGurkz commented 1 week ago

I tried all the things in the thread. Has anyone found a way to make full playlists download again. I get about half before I get the error message. I am on Mac OS. Any help is appreciated :)

BionztheGod commented 1 week ago

I think I may have found a solution, but it wont be as fast as originally. So first of all the explanation: if there are to many request hitting the Spotify API, wich Zotify uses, you get an error code. To solve that we add a little timer so Zotify doesn't download to fast. To do that you go into C:/users/(your name)/pipx/venvs/zotify/Lib/site-packages/zotify/track.py. At least that was for me the case. When you open the track.py file, there should be a time.sleep command in line 153 with the value 0. I changed it to 5 for now but i think you should be able to lower it. Safe the file and start Zotify and try downloading. The value in time.sleep is corrospondend to your average song length, so if you download anything longer you can turn it down. I couldnt find the max amount for the API so i had to guess something.

yodaluca23 commented 1 week ago

I think I may have found a solution, but it wont be as fast as originally. So first of all the explanation: if there are to many request hitting the Spotify API, wich Zotify uses, you get an error code. To solve that we add a little timer so Zotify doesn't download to fast. To do that you go into C:/users/(your name)/pipx/venvs/zotify/Lib/site-packages/zotify/track.py. At least that was for me the case. When you open the track.py file, there should be a time.sleep command in line 153 with the value 0. I changed it to 5 for now but i think you should be able to lower it. Safe the file and start Zotify and try downloading. The value in time.sleep is corrospondend to your average song length, so if you download anything longer you can turn it down. I couldnt find the max amount for the API so i had to guess something.

I am on macos and the their was no sleep command already, but I added a sleep right after the if not Zotify.CONFIG.get_bulk_wait_time() if statement, and it works, so far, and most importantly does not sleep if it is supposed to skip it...

yodaluca23 commented 1 week ago

Another alternative solution would be to allow for multiple accounts, if one gets rate limited switch to another till it gets rate limited...

x528491x commented 1 week ago

Another alternative solution would be to allow for multiple accounts, if one gets rate limited switch to another till it gets rate limited...

I'd assume that the rate limiting would also be done based on the IP the requests are coming from and not just the account.

yodaluca23 commented 1 week ago

Another alternative solution would be to allow for multiple accounts, if one gets rate limited switch to another till it gets rate limited...

I'd assume that the rate limiting would also be done based on the IP the requests are coming from and not just the account.

Hmmm, then maybe I shouldn't be using a VPN lool

Edit: Can confirm that I am getting like 0 ratelimits with the 5 sec sleep and my VPN off...

x528491x commented 1 week ago

Just ran some tests, the requests for the raw audio files do not seem to be factored into their rate limiting algorithm at all. I was able to download 10,000 raw oggs without a single error.

The rate limiting gets triggered only for the audio keys.

I have a wild proposal that might work to overcome this issue:

Crowdsourced open database of audio keys.

When a user tries downloading a track, it queries this database first for the key.

If no key is found, it then queries Spotify's servers and then submits the key to the database for the community to use later.

There are obviously problems with this proposal in regards to legal risk, costs, anonymity of submissions, etc.

NotFractured33 commented 1 week ago

Hey, where can I find track.py on ubuntu?

MichaelDevon commented 1 week ago

@x528491x Banger idea! However the Zotify userbase might be a bit small for this.

joor241 commented 1 week ago

I think I may have found a solution, but it wont be as fast as originally. So first of all the explanation: if there are to many request hitting the Spotify API, wich Zotify uses, you get an error code. To solve that we add a little timer so Zotify doesn't download to fast. To do that you go into C:/users/(your name)/pipx/venvs/zotify/Lib/site-packages/zotify/track.py. At least that was for me the case. When you open the track.py file, there should be a time.sleep command in line 153 with the value 0. I changed it to 5 for now but i think you should be able to lower it. Safe the file and start Zotify and try downloading. The value in time.sleep is corrospondend to your average song length, so if you download anything longer you can turn it down. I couldnt find the max amount for the API so i had to guess something.

I am on macos and the their was no sleep command already, but I added a sleep right after the if not Zotify.CONFIG.get_bulk_wait_time() if statement, and it works, so far, and most importantly does not sleep if it is supposed to skip it...

You mean in between the "()"? Sorry I'm a nooby image

Draxxx commented 1 week ago

I think I may have found a solution, but it wont be as fast as originally. So first of all the explanation: if there are to many request hitting the Spotify API, wich Zotify uses, you get an error code. To solve that we add a little timer so Zotify doesn't download to fast. To do that you go into C:/users/(your name)/pipx/venvs/zotify/Lib/site-packages/zotify/track.py. At least that was for me the case. When you open the track.py file, there should be a time.sleep command in line 153 with the value 0. I changed it to 5 for now but i think you should be able to lower it. Safe the file and start Zotify and try downloading. The value in time.sleep is corrospondend to your average song length, so if you download anything longer you can turn it down. I couldnt find the max amount for the API so i had to guess something.

I am on macos and the their was no sleep command already, but I added a sleep right after the if not Zotify.CONFIG.get_bulk_wait_time() if statement, and it works, so far, and most importantly does not sleep if it is supposed to skip it...

You mean in between the "()"? Sorry I'm a nooby image

Yeah I see the same. @yodaluca23 - Could you please show us your sleep command, and where exactly it goes? I'm a complete amateur with Python. Thanks! :)

briefer666 commented 1 week ago

I added:

time.sleep(5)

after

Printer.print(PrintChannel.DOWNLOADS, f'### Downloaded "{song_name}" to "{Path(filename).relative>

at ~/.local/pipx/venvs/zotify/lib/python3.10/site-packages/zotify/track.py (I am using linux)

this delay seems to mitigate somehow the error. I am also downloading the files as mp3, so that the conversion process add more time between downloads.

JaredIsOffline commented 1 week ago

I think I may have found a solution, but it wont be as fast as originally. So first of all the explanation: if there are to many request hitting the Spotify API, wich Zotify uses, you get an error code. To solve that we add a little timer so Zotify doesn't download to fast. To do that you go into C:/users/(your name)/pipx/venvs/zotify/Lib/site-packages/zotify/track.py. At least that was for me the case. When you open the track.py file, there should be a time.sleep command in line 153 with the value 0. I changed it to 5 for now but i think you should be able to lower it. Safe the file and start Zotify and try downloading. The value in time.sleep is corrospondend to your average song length, so if you download anything longer you can turn it down. I couldnt find the max amount for the API so i had to guess something.

(If you don't want to do all of this, or don't know anything and are afraid to break something, scroll and read on how the audio keys work and read the conclusion.)

Where to put the code. I think I found a fix for it based on this comment. Like the comment said, you gotta go to C:/users/(your name)/pipx/venvs/zotify/Lib/site-packages/zotify/track.py. In that script is all the code for downloading and even converting songs. There isn't a time.sleep() command in line 153 to begin with. What I think he was saying is YOU need to put a time.sleep() in line 153 or in between prepare_download_loader = Loader(PrintChannel.PROGRESS_INFO, "Preparing download...") and prepare_download_loader.start(). When I did this, it successfully buffered the download speed.

How many seconds should I put in? What I don't recommend putting for the amount of seconds is anything that is 9 seconds or lower, this for me does NOT fix the audio key issue. (I'll talk about how I THINK the audio key issue works in a bit.) If you REALLY aren't patient, go for 10-12 seconds, this doesn't completely fix it, but it's enough to push a few more downloads. If you want to be a bit more safe, go for 15-20 seconds. This gives you way more time to download and should keep the cooldown low enough to not activate the error. (I'll explain about that cooldown thing in a little bit.) If you want to be REALLY safe, go for 25 or more. In my opinion, this is just too slow, BUT it is faster than real time.

How I THINK the audio key issue might work. I think the audio key issue might be going off how many songs you're downloading in a set time, think of it like an unknown cooldown. From what I tested, (Trying to download 4 albums at once.) the amount of songs you can download without using a time.sleep() is somewhere around 50 songs. (These songs are around 3 minutes long.) If you try closing powershell or whatever you are using and running Zotify again, you will just keep getting the same audio key error. In order for it to stop you have to wait around 10-30 minutes so you can continue WITHOUT the time.sleep(). Again, it's very similar to a video game cooldown. The time.sleep() is only TRYING to keep you from reaching the cooldown, which is the reason why putting anything under 9 seconds doesn't work very well. Increasing the number of seconds will result in less or even no audio errors, depending on many seconds you put.

Conclusion. You are most likely bound to run to this error since Spotify has limited the amount of audio keys Zotify can request. This is so far the only way I know to bypass the audio key error. If you ever do get this error, just take a 10-30 minute break from downloading before continuing to avoid it as much as possible. To the people who don't want to do all of this, just use download in real time. It will most likely remove the audio key error completely, but the only downside to this is downloading 50 songs takes like 1 to 3 hours.

alr im gonna go get some wingstop, making an essay for fun gets you really hungry fr

Cybernatus commented 1 week ago

While we are striving to find a long-term solution regarding this issue, I would also like to share my own workaround method, which is more on the "Give me my songs, damnit!" point of view than waiting a certain time before a download could be manually tried again.

If you don't wanna Zotify to shutdown on encountering the infamous Audio Key Error, but instead wait a determined time window before continuously trying to resume the task you were doing, then it's possible to do so. The Audio Key Error does not seems to impose you a temporary ban, rather limiting the success rate of a request for a key.

Knowing this, you can adapt the __init__.py file in "/zotify/lib/python3.10/site-packages/librespot/audio" or wherever this specific file of Librespot is located, by changing the get_audio_key function as follow :

def get_audio_key(self,
                      gid: bytes,
                      file_id: bytes,
                      retry: bool = True) -> bytes:
        seq: int
        with self.__seq_holder_lock:
            seq = self.__seq_holder
            self.__seq_holder += 1
        out = io.BytesIO()
        out.write(file_id)
        out.write(gid)
        out.write(struct.pack(">i", seq))
        out.write(self.__zero_short)
        out.seek(0)
        self.__session.send(Packet.Type.request_key, out.read())
        callback = AudioKeyManager.SyncCallback(self)
        self.__callbacks[seq] = callback
        key = callback.wait_response()
        if key is None:
            if retry:
                time.sleep(15)
                return self.get_audio_key(gid, file_id, True)
            raise RuntimeError(
                "Failed fetching audio key! gid: {}, fileId: {}".format(
                    util.bytes_to_hex(gid), util.bytes_to_hex(file_id)))
        return key

This will give Zotify a cooldown when this error raise, then the task will continue and so on. You can adapt time.sleep(15) to how many seconds you'd like to wait between each try.

I, for myself and using a waiting time of 10 seconds, got this result in the scope of a bot using Zotify : gc_screen_librespot_adapt

briefer666 commented 6 days ago

Also, for downloading large playlists, I recommend to make a copy of it in spotify and remove the songs in the playlist already downloaded, before retrying.

p3w-p3w commented 6 days ago

The solution suggested by @Cybernatus worked for a time, maybe until I hit about 28-30 downloads from a playlist - and then it seems the rate limit cooldown (with a wait time of 15 seconds) began to increase exponentially. 2 retries on the first error, then 6, then I Ctrl+c'd at 14. Changed wait time to 60 seconds and it helped at least keep things moving, but I still think I'm bumping up against that exponential rate limit.

After changing to time.sleep(60):

After typing all that, been crawling along with one error either every 1-2 or 3-4 downloads, either alternating, at random, or somewhere in between.

Only included this detail for more data, in the event that someone feels particularly inspired to deduce what the rate limit may be. though no telling if that's based on payload size or # requests within X time period.

p3w-p3w commented 6 days ago

Thinking out loud - might be worth playing around with throttling DL speed and see if that improves overall downloads per period of time, notably in larger playlist runs.

x528491x commented 6 days ago

The solution suggested by @Cybernatus worked for a time, maybe until I hit about 28-30 downloads from a playlist - and then it seems the rate limit cooldown (with a wait time of 15 seconds) began to increase exponentially. 2 retries on the first error, then 6, then I Ctrl+c'd at 14. Changed wait time to 60 seconds and it helped at least keep things moving, but I still think I'm bumping up against that exponential rate limit.

After changing to time.sleep(60):

  • First audio key error
  • 5 more downloads
  • One error
  • 4 more downloads
  • 3 more audio key errors
  • one download
  • one error
  • 4 downloads
  • one error
  • 3 downloads
  • one error
  • two downloads
  • another error

After typing all that, been crawling along with one error either every 1-2 or 3-4 downloads, either alternating, at random, or somewhere in between.

Only included this detail for more data, in the event that someone feels particularly inspired to deduce what the rate limit may be. though no telling if that's based on payload size or # requests within X time period.

I'm working on exactly that - deducing the rate limit. 😅

Wrote a tool to connect to Spotify and just download the audio key in a loop whilst not making any call to any other endpoint in the process.

Running the tests with sleeps in the loop as well to test if it helps.

I'll publish my tests with Unix timestamps for us to be able to detect a pattern.

x528491x commented 5 days ago

So, here is the rust code to run the test I just mentioned:

    let mut count = 0;
    let sleep = time::Duration::from_secs(30);
    loop {
        count += 1;
        let now = SystemTime::now();
        let epoch = now.duration_since(UNIX_EPOCH).unwrap();
        match &session.audio_key().request(track_id, file_id).await {
            Ok(_) => println!("{} --> {:?}", count, epoch),
            Err(_) => {
                eprintln!("✖ --> {:?}", epoch);
                count = 0;
            }
        }
        thread::sleep(sleep);
    }

I have compiled the results for:

  1. No sleep
  2. Sleep of 1 second
  3. Sleep of 5 seconds

There appears to be at first a hard rate limit (where no requests go through at all) and then a soft one after which requests go through roughly every 30 seconds.

I'll cite the line where the soft limit is reached in each of the above tests

speedrun.txt L43 1sec.txt L44 5sec.txt L48

The prefix numbers indicate how many requests successfully went through consecutively.

x528491x commented 5 days ago

Someone ought to try modifying the underlying librespot to see if the audio key endpoint returns a retry-after header like Spotify does for its most other rate limited endpoints.

I do not know python or else I would have tried it. And as for the rust librespot, it cannot be done because the requests are made in a way that no headers are received at all, making it a deadend.

So, I suggest python developers to take a look at https://github.com/kokarare1212/librespot-python

briefer666 commented 5 days ago

I have been downloading today for 6 hours now and I have not have the same error again, it is working flawlessly

x528491x commented 5 days ago

I have been downloading today for 6 hours now and I have not have the same error again, it is working flawlessly

Are you running stock zotify or added some sleep timers?

briefer666 commented 5 days ago

I added the delay I explained above and also the one from Cybernatus

x528491x commented 5 days ago

I added the delay I explained above and also the one from Cybernatus

Strange, I am still running into errors in just 5 mins.

How many tracks did you end up downloading in 6 hours? Also, try removing those modifications to check if Spotify is slowly rolling back the ratelimit it pushed out?

yodaluca23 commented 5 days ago

Not sure if this is factual, I don't even know if audio keys are used when normally streaming. But I think I might of hit it just natively listening to Spotify, I skipped like a bunch of songs (like probably 50+) because I was board, and suddenly it wouldn't work anymore, I couldn't skip or unskip...

briefer666 commented 5 days ago

I added the delay I explained above and also the one from Cybernatus

Strange, I am still running into errors in just 5 mins.

How many tracks did you end up downloading in 6 hours? Also, try removing those modifications to check if Spotify is slowly rolling back the ratelimit it pushed out?

I have downloaded 500 files today. I may try with the roll back when I finish downloading the current playlist, but not today.

x528491x commented 5 days ago

Not sure if this is factual, I don't even know if audio keys are used when normally streaming. But I think I might of hit it just natively listening to Spotify, I skipped like a bunch of songs (like probably 50+) because I was board, and suddenly it wouldn't work anymore, I couldn't skip or unskip...

They are indeed used for streaming too, whether on the native client or librespot. Only encrypted files are on spotify's server, and whatever client you use needs to fetch the audio key to decrypt it for playback/download.

What you described seems like Spotify's rate limit is indiscriminate even towards their own official client. I would imagine them to be so because if there existed some special unrestricted method for it, it would be targeted for reverse engineering like the rest.

Hazeybxx commented 2 days ago

Hey guys, what are the good fixes to this issue? can anyone assist me please? i'm a newbie

exhale96 commented 1 day ago

Hey guys, what are the good fixes to this issue? can anyone assist me please? i'm a newbie

The fix from Cybernatus is definitely helpful and pretty easy to do, but I don't think its a permanent fix. Just don't use tab to indent the additional line of code, you have to use spaces. I'm also a newbie and made that error.