Skip to content

Instantly share code, notes, and snippets.

@nwithan8
Last active February 15, 2024 17:08
Show Gist options
  • Save nwithan8/b5282d4dc44b032909fafb11f1f48485 to your computer and use it in GitHub Desktop.
Save nwithan8/b5282d4dc44b032909fafb11f1f48485 to your computer and use it in GitHub Desktop.
Script to download songs from a Spotify playlist

This does NOT require Spotify Premium or logging into your Spotify account.

  1. Install dependencies with pip3 install -r requirements.txt
  2. Get Spotify playlist share URL by clicking the three dots next to a playlist -> Share -> Copy Link
  3. Run python3 spotify_download.py [URL] to download all playlist items to the local directory.
objectrest
pydantic
#!/bin/bash
from __future__ import annotations
from typing import Any, List
import argparse
import objectrest
from pydantic import BaseModel
class Track(BaseModel):
id: str
title: str
artists: str
cover: str
album: str
releaseDate: str
class TrackListData(BaseModel):
success: bool
nextOffset: Any
trackList: List[Track]
class Metadata(BaseModel):
id: str
artists: str
title: str
cover: str
album: str
releaseDate: str
class TrackDownloadSummary(BaseModel):
success: bool
metadata: Metadata
link: str
BASE_URL = "https://api.spotifydown.com"
HEADERS = {
'authority': 'api.spotifydown.com',
'accept': '*/*',
'accept-language': 'en-US,en;q=0.9',
'dnt': '1',
'origin': 'https://spotifydown.com',
'referer': 'https://spotifydown.com/',
'sec-ch-ua-mobile': '?0',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-site'
}
ALLOWED_FILENAME_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!?"
FILENAME_TEMPLATE = "{artist} - {album} - {title}.mp3"
def extract_playlist_id(playlist_url: str) -> str:
# remove any query parameters
playlist_url = playlist_url.split("?")[0]
# playlist id is the last part of the url
return playlist_url.split("/")[-1]
def get_track_list_url(playlist_id: str) -> str:
return f"{BASE_URL}/trackList/playlist/{playlist_id}"
def get_track_download_url(track_id: str) -> str:
return f"{BASE_URL}/download/{track_id}"
def format_track_filename(track: TrackDownloadSummary) -> str:
artist = clean_filename_element(track.metadata.artists)
album = clean_filename_element(track.metadata.album)
title = clean_filename_element(track.metadata.title)
return FILENAME_TEMPLATE.format(artist=artist, album=album, title=title)
def clean_filename_element(filename_element: str) -> str:
# remove any special characters
filename_element = "".join([c for c in filename_element if c == " " or c in ALLOWED_FILENAME_CHARACTERS]).rstrip()
# replace slashes with dashes
filename_element = filename_element.replace("/", "-")
return filename_element
# noinspection PyTypeChecker
def main(args: argparse.Namespace):
playlist_url = args.playlist_url
playlist_id = extract_playlist_id(playlist_url=playlist_url)
track_list_url = get_track_list_url(playlist_id=playlist_id)
track_list_data: TrackListData = objectrest.get_object(url=track_list_url, model=TrackListData, headers=HEADERS)
for track in track_list_data.trackList:
track_download_url = get_track_download_url(track_id=track.id)
track_download_summary: TrackDownloadSummary = objectrest.get_object(url=track_download_url,
model=TrackDownloadSummary,
headers=HEADERS)
track_name = format_track_filename(track=track_download_summary)
track_link = track_download_summary.link
print(f"Downloading {track_name}...")
with open(track_name, "wb") as f:
res = objectrest.get(url=track_link, headers=HEADERS)
f.write(res.content)
if __name__ == "__main__":
args_parser = argparse.ArgumentParser(description="Download Spotify playlist")
args_parser.add_argument("playlist_url", type=str, help="Spotify playlist URL")
args = args_parser.parse_args()
main(args=args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment