Skip to content

Instantly share code, notes, and snippets.

@varenc
Last active March 1, 2025 17:39
Show Gist options
  • Save varenc/c31e461a16bcf3266a5bcf375659220a to your computer and use it in GitHub Desktop.
Save varenc/c31e461a16bcf3266a5bcf375659220a to your computer and use it in GitHub Desktop.
Suno AI song generation, download songs from suno.com (this script requires 'zsh')
# extra comments for you arthur! but FYI you need 'pup' or 'htmlq' you can get with `brew install htmlq`
function sunoScrape() {
# interate over every url passed in as an argument. Each url will be in the variable '$u'
for u in $@; do
# get the title of the song by extracting the '<meta property="og:title" content="...">' tag from the html of the page
local TITLE="";
[[ $+commands[pup] ]] && {
TITLE="$(curl -sS "$u" | pup 'meta[property="og:title"] attr{content}' | perl -MHTML::Entities -pe 'decode_entities($_)' )";
} || { [[ $+commands[htmlq] ]] && {
TITLE="$(curl -sS "$u" | htmlq -a content "meta[property=\"og:title\"]" )";
} || { echo "You need to install either 'pup' or 'htmlq' to run this script to extract the title of the song from the URL";
TITLE="UNKNOWN TITLE $RANDOM"
# return 1
}
}
[[ -z "$TITLE" ]] && {
echo "Could not extract the title of the song from the URL '$u'...";
return 1
}
# convert the title to a safe filename by replacing all non-alphanumeric characters with underscores
local TITLE_SAFE="$(sed 's/[^a-zA-Z0-9\n]\+/_/g' <<<"$TITLE")";
# extract the song id, something like '3447b429-afc7-41c0-8123-3a08f0b04f5b', from the URL
# it extracts it just by finding the first string of exactly 36 chars that is made up of numbers and letters in the A-F range (hex)
local ID="$(grep -E -o '[0-9a-f\-]{36}' <<<"$u")";
local SONG_MP3_URL="https://cdn1.suno.ai/${ID}.mp3";
local SONG_FILENAME="${TITLE_SAFE}__full_song.mp3";
echo "Got song '$TITLE' with ID:'$ID'";
echo "Will download to '$SONG_FILENAME' from '$SONG_MP3_URL'";
sleep 0.5
curl -sS "$SONG_MP3_URL" -o "$SONG_FILENAME" || { echo "FAILURE! cURL failed while downloading '$SONG_MP3_URL' to '$SONG_FILENAME'... aborting"; return 1; }
done
}
ARTHUR_URLS=( https://suno.com/song/3447b429-afc7-41c0-8123-3a08f0b04f5b https://suno.com/song/cd289a8a-f9d8-4363-824b-409520c8d34b https://suno.com/song/a1db6e68-fdb4-41e9-aacf-9933dc849aca https://suno.com/song/dd85f590-9d73-4327-a077-e742a0f34d39 https://suno.com/song/a8658ec7-2605-4092-ae8a-c1092a565492 https://suno.com/song/c58adfca-1d6c-40ba-ab9d-6d80eb2830a2 https://suno.com/song/473e38fa-01e6-46c3-bece-8da1ed16a643 https://suno.com/song/3af75abe-f826-4b54-b8f5-5b25eda45dfc https://suno.com/song/d53be60f-eb37-4d80-bd3a-38a234c55ad4 https://suno.com/song/02414f68-8ab4-4914-9e7d-e15ac5efdefc https://suno.com/song/3764382e-bc15-41d6-884d-1af7624dd38c https://suno.com/song/64ad215d-21fe-4835-920c-b94fc7357ab2 https://suno.com/song/0a33b690-7587-4da7-8df9-4cb8772c6ba0 )
sunoScrape $ARTHUR_URLS # <--- this passes in each song in the array as a seperate argument. Mercifully the URLs need no escaping
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment