/heartify-cli

A CLI tool to export and filter out your Spotify liked songs into playlists. With support for filters like ranges of date added, release date, genre, audio features, and more.

Primary LanguageJavaScriptMozilla Public License 2.0MPL-2.0

heartify-cli

A CLI tool to export and filter out your Spotify liked songs into playlists. With support for filters like ranges of date added, release date, genres, audio features, and more.

Motivation

The "Liked Songs" list on Spotify can quickly grow into chaos the more you use it. Over time, it becomes increasingly harder to find songs to fit the moment, while keeping all of your liked songs in one place. It's also easier to forget your older songs if only a few songs make it to the Spotify shuffle. It would be handy if there was a way to keep "liking" songs, while also being able to filter them out into playlists of just your favourites without having to do it manually.

With this little tool, you can create fine-tuned playlists with just the songs you like, and share them 🎉 One step from chaos -> organised chaos 🗃️

Features

✔️ Export to a playlist to share your liked songs

✔️ Filter by the year you liked the song - make a 'My Top Songs 2023' playlist, for example

✔️ Filter by genre(s) to make genre-mixes out of your liked songs

✔️ Make monthly playlists - no more adding songs manually to monthly playlists, simply filter by ranges of dates added

✔️ Decade mixes - filter by release date

✔️ Filter by audio features - make a workout playlist of songs in a certain bpm range, for example

No library size limits

There's no (known) limit to the number of songs Heartify can fetch, so bring along your massive library of 7000 liked songs (or more?)! It would just take a bit longer with the measures in place to account for Spotify's rate limits.

Fully local

Heartify collects no data. All data is stored locally on your device where heartify is installed, and can be deleted by running heartify logout. You can also revoke access at any time from your Spotify account page at Spotify Account > Security and privacy > Manage apps.

Installation

npm install -g heartify-cli

Authorisation

Run the following command from any directory, all data is stored locally where Heartify is installed. Then follow the instructions to authorise access to your Spotify library.

  heartify init

This command needs to be run just once, and you're logged in until you revoke permissions from your account page, logout, or until Spotify automatically revokes permissions from time-to-time (in which case, run heartify init again). Heartify uses the OAuth 2.0 Authorization Code with PKCE flow, and refreshes the access token automatically until it's revoked.

Basic Examples

Export All

Export all liked songs to a playlist (replace 'My Liked Songs' for your playlist name, wrapped in quotes if it contains spaces)

heartify export 'My Liked Songs'

New playlists are not displayed on your profile by default. To display it on your profile, use the --on-profile flag or its short form -p

heartify export 'My Liked Songs' -p

Filter by year added

Export your liked songs from a specific year (2023 in the example). You can use --added-from or -f and --added-to or -t (docs), but there's also a convenience option --year or -Y available, used here:

heartify export 'Liked Songs 2023' -Y 2023

Filter by genre

There are several filters available, and one of them is genre. To see all the genres detected in your liked songs that you can filter by, run

heartify show-genres

Then pick a genre and filter (replace '<genre name>'):

heartify export 'My <genre name> mix' --filter 'genre=<genre name>'

You can also filter by multiple genres:

heartify export 'My multi-genre mix' --filter 'genre=<genre name>' 'genre=<another genre name>'

If genre names contain spaces or special characters, either wrap it in quotes, or wrap the entire field-value pair in quotes. For example, this command would make (a rather interesting) playlist out of songs which fall into at least one of the following genres: 'disco', 'conscious hip hop', 'k-indie', and 'escape room'

heartify export 'My multi-genre mix' --filter genre=disco 'genre=conscious hip hop' genre='k-indie' genre='escape room'

Custom range of dates added

Filter by custom range of date added using the --added-from or -f option, the --added-to or -t option, or both

heartify export 'Liked Songs, November 2023' -f 2023-11-01 -t 2023-11-30
heartify export 'Liked Songs from July 1st 2023 onwards' -f 2023-07-01

Some things to keep in mind about the dates:

  • Dates for the start of the range (the 'from' values) are converted to the timestamp at midnight at the start of the day
  • Dates for the end of the range (the 'to' values) are converted to the timestamp at the last second before midnight at the end of the day
  • Dates are taken with reference to the user's current time zone (machine time zone). They're internally converted to UTC, but respect time zones.

Valid date formats include:

  • YYYYMMDD: 20230101, 20230730
  • YYYY-MM-DD: 2023-01-01, 2023-07-30
  • YYYY: 2023, 2022 (NOTE: this is converted to midnight at the start of the year if it's a 'from' value, and the timestamp at the last second before midnight of January 1st if it's a 'to' value)

Filter by audio features

Filter by other features of the tracks, like date released, key, or tempo (see the list of supported features here)

 heartify export 'Liked Songs, 120bpm' --filter tempo=120

Ranges

DateTime filters and Number filters (docs) support ranges. Ranges are written as field=[from,to]. Either the 'from' or the 'to' value can be omitted for a range unbounded on one side

 heartify export 'Liked Songs, 100-120bpm' --filter tempo=[100,120]

Combine options and filters

 heartify export 'Liked Songs 2023, 100-120bpm, danceable' -Y 2023 --filter tempo=[100,120] danceability=[0.7,]

Detailed Docs (TODO)

--filter

Filters are of the form field=value and accept any field from the list of supported fields below.

The value can be either individual values like in genre=disco and time_signature=4, or ranges like in tempo=[100,120] (only DateTime and Number fields accept ranges at the time of writing)

Ranges must have at least the start or the end specified. The search includes the end points.

# songs with tempo from 100 BPM to 120 BPM (both inclusive)
--filter tempo=[100,120]

# songs with tempo >= 100 BPM
--filter tempo=[100,]

# songs with tempo <= 120 BPM
--filter tempo=[,120]

If multiple filters are given for the same field, they are joined by OR in the search

# songs in the genres 'pop' or 'rock'
--filter genre=pop genre=rock

# songs released in the 60's or in the 80's
--filter release_date=[1960-01-01,1969-12-31] release_date=[1980-01-01,1989-12-31]

Filters for different fields are joined by AND

# songs in the genre 'pop' released in the year 1990
--filter genre=pop release_date=[1990-01-01,1990-12-31]

# songs by BTS released since 2020
--filter artist=BTS release_date=[2020-01-01,]

# the above search can also be written like this as 'from 2020' is parsed as 'from midnight of 2020-01-01'
--filter artist=BTS release_date=[2020,]

Filters do not need to be wrapped in quotes as long as they do not contain ANY spaces. Wrap them in single/double quotes when they contain whitespace.

# without spaces
--filter artist=BTS genre=k-pop release_date=[2016-01-01,2018-12-31]

# quotes can be around the whole field-value pair or just the string that has spaces
--filter artist='J. Cole' 'artist = BTS' 'artist = Epik High'

# quotes are needed if you prefer writing ranges with a space before/after the comma or the brackets
# the following filters are valid and return the same result:
--filter 'tempo=[60, 80]'
--filter tempo='[60, 80]'
--filter tempo=[60,' 80']
--filter tempo=[60, '80']
--filter tempo=['60', '80']
--filter 'tempo = [ 60, 80 ]'

String fields

  • artist
  • genre

String fields accept only individual values (and not ranges).

For example, --filter 'artist=[BTS,Epik High]' would mean artists 'from BTS to Epik High' and is not a supported search

DateTime fields

  • release_date

Number fields

These fields correspond to those returned by the Spotify Web API. The descriptions here are excerpts of the full descriptions, which can be found in the Spotify Web Api docs for Audio Features.

field description range
danceability How suitable a track is for dancing based on
a combination of musical elements including tempo,
rhythm stability, beat strength,and overall regularity
0.0 to 1.0
(float)
energy Represents a perceptual measure of intensity
and activity
0.0 to 1.0
(float)
key The key the track is in.
Integers map to pitches using standard Pitch Class
notation. E.g. 0 = C, 1 = C♯/D♭, 2 = D, and so on.
If no key was detected, the value is -1.
-1 to 11
(integer)
loudness The overall loudness of a track in decibels (dB).
Loudness values are averaged across the entire track and
are useful for comparing relative loudness of tracks
-60 to 0
(float)
mode Mode indicates the modality (major or minor) of a track.
Major is represented by 1 and minor is 0.
1 or 0
(integer)
speechiness Detects the presence of spoken words in a track 0.0 to 1.0
(float)
acousticness A confidence measure from 0.0 to 1.0 of whether the
track is acoustic
0.0 to 1.0
(float)
instrumentalness Predicts whether a track contains no vocals 0.0 to 1.0
(float)
liveness Detects the presence of an audience in the recording.
A value above 0.8 provides strong likelihood that the track
is live.
0.0 to 1.0
(float)
valence Tracks with high valence sound more positive
(e.g. happy, cheerful, euphoric), while tracks with
low valence sound more negative (e.g. sad, depressed, angry)
0.0 to 1.0
(float)
tempo The overall estimated tempo of a track in beats per minute (BPM) -
(float)
duration_ms The duration of the track in milliseconds -
(integer)
time_signature An estimated time signature. The time signature (meter)
is a notational convention to specify how many beats
are in each bar (or measure). The time signature ranges
from 3 to 7 indicating time signatures of "3/4", to "7/4".
3 to 7
(integer)


Roadmap

A few features I plan on adding in future updates 🎯

  • Options to add songs/update an existing playlist instead of creating a new playlist on export
  • Command to like all songs from a playlist - like an import rather than an export.
    • Could come in handy if your songs got unliked for some reason one day but you had them backed up in another playlist
    • Also useful if migrating to Spotify and you used another service to have your favourites exported to Spotify playlists
  • More export formats - csv, txt, JSON
  • A query command to see search results in a tabulated format before running an export
  • Support for regular expressions in string filters

Feedback, suggestions, and bug reports welcome! 👐

This is a small project I've been making on the side as I'm learning JS. Feel free to open an issue for a new feature you'd like to have, suggestions, bug reports, or anything else.

💜