Make Spotify better
Tags: javascript, opensource, showdev, appsscript
Published: 2022-11-13 16:44:36

Hello everyone! This is my first post here. I would like to tell you about my hobby project that make Spotify experience more and more better.

The worst part of Spotify is repeat and repeat and.. Maybe I want something new or even too old that I forgot. You can say: "Explore billions playlists". By hand? Then I will erase phone screen due constant surfers through pages and track skips. 

What I offer? Let we say **_how to X_** and replace **_X_** to our dreams: 

- ...remove history tracks? 
- ...find forgot liked tracks? 
- ...collect multiple playlists to one?
- ...mine tracks from search result by keywords?
- ...get only new releases by favorite artists?
- ...calculate custom top any time?
- ...remove duplicate?
- ...filter by meta-data?
- ...import tracks from other platforms by shedule?
- ...even last.fm?
- ...and repeat such and other dreams every day without hand control

Probably you know some of this or other dreams by separate API services. What about one place?

## Say hello to..

_goofy_ - JavaScript library for Google Apps Script (GAS). It is powerful extension above Spotify API that runs on Google servers for free by custom shedule. Plus your trigger to run scripts can be [Tasker](https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm) on android (buttons, voice, location and other events).

Before you read next, after many advantages, you must to know one disadvantage: need to use translator in order to read goofy docs on English. For example, [Google Translate](https://chrome.google.com/webstore/detail/google-translate/aapbdbdomjkkjkaonfhkkikfgjllcleb).

## Get started

[Configure](https://chimildic.github.io/goofy/#/install) goofy step by step. You will copy GAS project and setup API keys from Spotify Dashboard.

> Note: many of scripts must to contain filter in order to remove history tracks, but at the start goofy don't know what you are listening. Because Spotify API return only last 50 tracks. But goofy begin tracking your history immediately after configure.

Let's go to realize dreams and, for example, mine synthwave tracks from search result. Just copy/paste the next function and run (or create GAS trigger):

```js
function mineSynthTracks() {
  let tracks = Source.mineTracks({
    keyword: ['synthwave', 'synthpop'],
    itemCount: 5,
    requestCount: 3,
    popularity: 51,
  })

  Filter.matchOriginalOnly(tracks)
  Filter.dedupTracks(tracks)

  Playlist.saveWithReplace({
    name: 'Synth from search',
    tracks: tracks
  })
}
```

What about week releases from saved artists?

```js
function collectWeekReleases() {
  let weekReleases = Source.getReleasesByArtists({
    artists: Source.getArtists({ followed_include: true }),
    date: { sinceDays: 7, beforeDays: 0 },
    type: ['album', 'single'],
  })

  Filter.matchExceptMix(weekReleases)
  Filter.dedupArtists(weekReleases)

  Playlist.saveWithReplace({
    name: 'Week releases',
    tracks: weekReleases
  })
}
```

New sound?

```js
function updateMyRecommendation() {
  let savedTracks = Source.getSavedTracks()

  Filter.replaceWithSimilar({
    origin: Selector.sliceRandom(savedTracks, 40),
    replace: [savedTracks, RecentTracks.get()],
    isRemoveOriginArtists: true,
  })

  Playlist.saveWithReplace({
    name: 'Only new sound',
    tracks: savedTracks
  })
}
```

## Advance level

Besides Tasker for android, goofy can be fire by Web and hotkeys on Windows. This are addons that you can find on [docs site](https://chimildic.github.io/goofy/#/addon) and [github forum](https://github.com/Chimildic/goofy/discussions/categories/избранное).

The last example for today, let's find tracks that located in albums with our likes, but we never hear their. Remember about note above.

```js
function updateDiscoveryAlbumsSpotify() {
  const LIMIT_TRACKS = 25
  const LIMIT_ALB_TRACK = 1

  let recentTracks = RecentTracks.get(2500)
  let savedTracks = Source.getSavedTracks()
  let banTracks = Combiner.push([], recentTracks, savedTracks)

  let tracks = savedTracks
  Order.shuffle(tracks)

  let recomTracks = [];
  for (let i = 0; i < tracks.length; i++) {
    if (tracks[i].album.album_type == 'compilation'
      || tracks[i].album.total_tracks == 1) {
      continue;
    }
    let albumTracks = Source.getAlbumTracks(tracks[i].album)
    Filter.matchOriginalOnly(albumTracks)
    Filter.removeTracks(albumTracks, banTracks)
    if (albumTracks.length == 0) {
      continue
    }

    Order.sort(albumTracks, 'meta.popularity', 'desc')
    Selector.keepFirst(albumTracks, LIMIT_ALB_TRACK)
    Combiner.push(recomTracks, albumTracks)

    Filter.dedupTracks(recomTracks)
    if (recomTracks.length >= LIMIT_TRACKS) {
      break;
    }
  }

  Selector.keepFirst(recomTracks, LIMIT_TRACKS)
  Playlist.saveWithAppend({
    name: 'Discovery albums',
    tracks: recomTracks
  })
}
```

If you have any question about goofy, welcome to [github discussions](https://github.com/Chimildic/goofy/discussions).