Skip to content

Instantly share code, notes, and snippets.

@tomty89
Last active January 19, 2024 17:12
Show Gist options
  • Save tomty89/e1ab603e7404f6b55e7c53331d146f96 to your computer and use it in GitHub Desktop.
Save tomty89/e1ab603e7404f6b55e7c53331d146f96 to your computer and use it in GitHub Desktop.
#!/bin/bash
# https://play.qobuz.com/resources/5.6.2-b016/bundle.js
# from bundle.js
app_id="" # base_url="https://play.qobuz.com"
# from bundle.js
initialSeed="" # window.utimezone.berlin
info="" # Europe/Berlin
extras="" # Europe/Berlin
app_secret=$(echo -n ${initialSeed}${info}${extras} | base64 -d)
app_secret=${app_secret:0:32}
if [ $# -lt 1 ]; then
echo "Pass one or more album id to the program"
exit
fi
echo "Enter email:"
read email
echo "Enter password:"
read -s password
password=($(echo -n "$password" | md5sum))
auth=$(curl "https://www.qobuz.com/api.json/0.2/user/login?email=${email}&password=${password}&app_id=${app_id}" | jq -r .user_auth_token)
output_prefix=/tmp/
for album in "$@"; do
tracks=($(curl "https://www.qobuz.com/api.json/0.2/album/get?album_id=${album}&app_id=${app_id}&user_auth_token=${auth}" | jq -r .tracks.items[].id))
format=27 # best: 27 / cd: 6 / hi-res(?): 7
if [ ${#tracks[@]} -gt 0 ]; then
mkdir "${output_prefix}${album}"
fi
for track in "${tracks[@]}"; do
time=$(date +%s)
sigsrc="trackgetFileUrlformat_id${format}intentstreamtrack_id${track}${time}${app_secret}"
sig=($(echo -n "$sigsrc" | md5sum))
url=$(curl "https://www.qobuz.com/api.json/0.2/track/getFileUrl?request_ts=${time}&request_sig=${sig}&track_id=${track}&format_id=${format}&intent=stream&app_id=${app_id}&user_auth_token=${auth}" | jq -r .url)
track_pp="${output_prefix}${album}/${track}"
curl -o "$track_pp".flac "$url"
bps=$(metaflac --show-bps "$track_pp".flac)
srate=$(metaflac --show-sample-rate "$track_pp".flac)
channels=$(metaflac --show-channels "$track_pp".flac)
flac -d --force-raw-format --endian little --sign signed "$track_pp".flac
rm "$track_pp".flac
flac --force-raw-format --endian little --sign signed --bps "$bps" --sample-rate "$srate" --channels "$channels" "$track_pp".raw
rm "$track_pp".raw
done
done
@tomty89
Copy link
Author

tomty89 commented Jul 13, 2023

I don't recall if it was the case when this script was written, but now it can be used to download your purchases in hi-res, i.e., even when you don't have a subscription. You'll need to change the intent to download though, since Qobuz "officially" prevent you (I mean like, on their own apps or web player, by using this intent switch to override the format_id passed) from streaming your purchases in hi-res. (Note that there are two stream that need to be changed.)

Ref.: https://help.qobuz.com/en/articles/10164-how-do-i-listen-to-my-purchases

I have no idea if setting the intent to download would cause downloads using a subscription to fail. What I do know is that with the download intent, format_id passed must match with the corresponding purchase. Apparently 27 does not work at all, and 7 will not work if it's not a hi-res purchase. The API does not fall back to CD quality download like it does when the intent is stream. It simply fails instead.

By the way, in the past Qobuz uses a different API or so on its purchase download page, which gives out FLAC with checksum, but now they seem to have "unified" things by using one same API, so even if you download "properly" (well, from the web page; not sure about their client programs / downloader), you'll be getting broken FLAC without checksum, since that has always been the case of the API used in this script (which is why this script "repacks" the downloaded files).

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