Bart Simons

ripping

A 1 post collection


Ripping Spotify songs on macOS

 •  Filed under spotify, ripping, rip, songs, macos

Ever wanted to rip songs of Spotify on your Mac? Since there are no tools available that allow you to directly download songs from Spotify, I decided to create a so-called 'duct tape' solution that might help you out with ripping songs.

So, it works as follows: AppleScript bindings allow users to automate Spotify in the following way for example:

osascript -e "tell application \"Spotify\"" -e "play track \"spotify:track:21cp8L9Pei4AgysZVihjSv\"" -e "end tell"  

This will play the song Child In Time by Deep Purple for example. There are so many ways to control Spotify with AppleScript: lots of examples and/or AppleScripts are available on the internet.

To summarize: we are now able to control and automate Spotify with AppleScript. But, we also want to record songs. I came across a brilliant application called Piezo. Piezo is a simple to use app that allows Mac users to record the audio of a specific application, which makes it a perfect solution since we only want to record Spotify audio, right?

We also want to update the song's ID3 tags afterwards, since Spotify also makes song information available through AppleScript.

I decided to write my script in Python to help better integrate the 3 main tasks:

  • Creating a recording in Piezo
  • Start and stop playing the song in Spotify
  • Update ID3 tag information

I have included comments in my Python script so that you can easily understand what each part of the code does:

# recordsong.py - record a song on Spotify with the help of Piezo

# Example usage:
#
# python3 recordsong.py spotify:track:21cp8L9Pei4AgysZVihjSv

import subprocess, sys, os, time, shutil, eyed3  
from urllib.request import urlopen

# Setup variables
piezoStorageLocation = '/Users/bart/Music/Piezo/'  
ripStorageLocation   = '/Users/bart/Music/Ripped/'

# Clear all previous recordings if they exist
for f in os.listdir(piezoStorageLocation):  
    os.remove(os.path.join(piezoStorageLocation,f))

# Tell Spotify to pause, tell Piezo to record, tell Spotify to play a specified song
subprocess.Popen('osascript -e "tell application \\"Spotify\\" to pause"', shell=True, stdout=subprocess.PIPE).stdout.read()  
time.sleep(.300)  
subprocess.Popen('osascript -e "activate application \\"Piezo\\"" -e "tell application \\"System Events\\"" -e "keystroke \\"r\\" using {command down}" -e "end tell"', shell=True, stdout=subprocess.PIPE).stdout.read()  
subprocess.Popen('osascript -e "tell application \\"Spotify\\"" -e "play track \\"'+sys.argv[1]+'\\"" -e "end tell"', shell=True, stdout=subprocess.PIPE).stdout.read()

time.sleep(1)

# Get the artist name, track name, album name and album artwork URL from Spotify
artist  = subprocess.Popen('osascript -e "tell application \\"Spotify\\"" -e "current track\'s artist" -e "end tell"', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8').rstrip('\r\n')  
track   = subprocess.Popen('osascript -e "tell application \\"Spotify\\"" -e "current track\'s name" -e "end tell"', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8').rstrip('\r\n')  
album   = subprocess.Popen('osascript -e "tell application \\"Spotify\\"" -e "current track\'s album" -e "end tell"', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8').rstrip('\r\n')  
artwork = subprocess.Popen('osascript -e "tell application \\"Spotify\\"" -e "current track\'s artwork url" -e "end tell"', shell=True, stdout=subprocess.PIPE).stdout.read().decode('utf-8').rstrip('\r\n')

# Download album artwork
artworkData = urlopen(artwork).read()

# Check every 500 milliseconds if Spotify has stopped playing
while subprocess.Popen('osascript -e "tell application \\"Spotify\\"" -e "player state" -e "end tell"', shell=True, stdout=subprocess.PIPE).stdout.read() == b"playing\n":  
    time.sleep(.500)

# Spotify has stopped playing, stop the recording in Piezo
subprocess.Popen('osascript -e "activate application \\"Piezo\\"" -e "tell application \\"System Events\\"" -e "keystroke \\"r\\" using {command down}" -e "end tell"', shell=True, stdout=subprocess.PIPE).stdout.read()

time.sleep(.500)

# Create directory for the artist
if not os.path.exists(ripStorageLocation+artist):  
    os.makedirs(ripStorageLocation+artist)

# Create directory for the album
if not os.path.exists(ripStorageLocation+artist+"/"+album):  
    os.makedirs(ripStorageLocation+artist+"/"+album)

# Move MP3 file from Piezo folder to the folder containing rips.
for f in os.listdir(piezoStorageLocation):  
        if f.endswith(".mp3"):
            shutil.move(piezoStorageLocation+f, ripStorageLocation+artist+"/"+album+"/"+track+".mp3")

# Set and/or update ID3 information
musicFile = eyed3.load(ripStorageLocation+artist+"/"+album+"/"+track+".mp3")  
musicFile.tag.images.set(3, artworkData, "image/jpeg", sys.argv[1])  
musicFile.tag.artist = artist  
musicFile.tag.album  = album  
musicFile.tag.title  = track

musicFile.tag.save()  

Please note that this script should run in Python 3, not Python 2. Also, make sure that you have all dependencies installed:

pip3 install eyed3  
brew install libmagic  

Now, we can rip songs from Spotify without having to deal with the circumvention of DRM. Enjoy your music!