Spotify Caching

November 07, 2017

This is a small little project I built in about winter '16 - right after I bought my first (and current !!!) used MacBook Air. I keep my Mac nice and lean and try to keep at least 50-70 gb free, but after a week I noticed it lost about 5 gb without having installed anything. I used a couple of du commands to track down the offending folders, and realized it was a ballooned Spotify cache that was taking up the space. I used Spotify to stream my carefully selected tunes on Windows pretty much since it launched, so when I got my Mac Spotify was one of the first things I installed.

I knew that, at least on Windows, there was a setting for controlling the cache size, so I smashed ⌘+, to bring up preferences and searched for the cache settings. It took me about 2 seconds and 3 google searches to realize there wasn't any option for limiting the cache size on Spotify Mac, so I got bummed for a sec.

Then I decided to write a cronjob to delete the cache once it passes a certain threshold. It's a simple Python 3.x script that sizes up and (maybe) deletes the cache directory when run.

#! usr/bin/env/python
# Cache Enforcer (for Spotify)
# Spotify took away the option for controlling the cache size, so I'm doing it for them.
# Written by Josh Chan (@joshpaulchan)

import os

import shutil

CONSTANTS

TARGET_DIR = "~/Library/Caches/com.spotify.client/Data/" # currently, unix only

SIZE_ALLOWANCE = 1 * ( 1024 ^ 3 ) # 4 gb

DEV = bool(os.environ.get('DEV', False))

def log(statement):
    """
    `log(statement)`

    Logs something if in development.

    @params: statement: str: statement to log
    @returns: none
    """
    if DEV: print(statement)

def total_size(dir_path):
    """
    `total_size(dir_path)`

    Calculates the size of a directory recursively

    @params: dir_path: str: the directory to calculate the size of
    @returns: total_size: int: int containing the size of the directory in bytes
    """
    total_size = 0
    for path, dirnames, filenames in os.walk(dir_path):
        for f in filenames:
            fp = os.path.join(path, f)
            sz = os.path.getsize(fp)
            total_size += sz
            # log("{}, {} bytes".format(f, sz))
    return total_size

def rmdir(dir_path):
    """
    `rmdir(dir_path)`

    Recursively remove the directory @ dir_path

    @params: dir_path: str: The directory to remove
    @returns: _ : dict: dict containing meta information
        files_removed: int: number of files removed total
        bytes_removed: int: number of bytes removed total
    """
    shutil.rmtree(dir_path, onerror=log)

def main():

    sz = total_size(TARGET_DIR)
    if sz > SIZE_ALLOWANCE:
        rmdir(TARGET_DIR)
        print("Removed {} GB from Spotify's caches".format(sz / (1024 ** 3)))

if name == 'main':
    main()

It's been pretty great at regulating my cache size - it's a shame this functionality isn't built into Spotify anymore, though. Check out the github to download/clone/fork around!

I'm open to a limited number of consultancy projects; if you think I can help you out, let's chat! When I'm free, I write about engineering, technology, HCI and the future of computing. If you like what I have to say, feel free to buy me a coffee and/or subscribe below for updates.