6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Spotify APIを使用して毎月の「いいねリスト」を作ってみた

Posted at

はじめに

元々Apple Musicに加入していましたが、あとで聴きたい曲や刺さった曲に「いいね」できる機能はあっても、
「いいね」した曲を蓄積するプレイリストを作ってくれず、毎回自分で作成したプレイリストに入れていました。
(スマートプレイリストという機能を使えばできるそうです...当時は知りませんでした。 apple musicのラブ済みの曲を集めるには?

それもあり「いいね」を自動で蓄積してくれる機能を持つSpotifyに乗り換えました。

たまに学生時代ヘビーローテーションしていた曲を聴きかえして、当時の思い出を鮮明に思い出したくなるんです。
「あの時こんな曲聴いていたなぁ〜」とか「あの時辛かったなぁ〜」とか。

そこでSpotifyが提供するWeb APIを使用して毎月「いいね」したリストを作るくんを作成してみたので紹介します。

導入や初期設定等は公式がご丁寧に用意されているのでそちらを。
日本語で要点だけみたい方は以下記事が参考になるかと思います。

Spotify Developer Platform: Spotify APIアクセスしてデータ取得してみてみた

仕組み

  • 【準備】Quick Startを使ってAppの作成と認証トークンの発行
  • GASを使って以下処理(毎時実行)
    1. あらかじめ生成しておいたリフレッシュトークンを使用してアクセストークンを発行
    2. 1時間以内にいいねされた曲リストを取得
    3. 取得した曲リストを今月のプレイリストに追加(今月のプレイリストがなければ作成して追加)
  • GASの設定

【準備】Quick Start使ってAppの作成と認証トークンの発行

Web APIにリクエストを投げるに当たり、認可されたアクセストークンが必要です。
公式のQuick Startに従い、Appの作成とトークンの作成をおこないます。
App作成したらClient IDClient Secretをメモしておきましょう。
(handle-liked-musicはApp名です。好きな名前でOKっす。)

スクリーンショット 2020-12-26 17.22.41.png

トークンに以下スコープを許可しましょう。

  • user-library-read いいねした曲の取得に必要
  • playlist-modify-public 公開プレイリスト操作(作成や更新)に必要
  • playlist-modify-private 非公開プレイリスト操作(作成や更新)に必要
  • playlist-read-private 追加対象のプレイリスト取得に必要

うまくいけばこんなページが表示されるのでRefresh tokenをメモしておきましょう。

スクリーンショット 2020-12-26 17.12.13.png

GASを使って以下処理(毎時実行)

全体のコードです。
サクッと作ったためミュータブルなコードですがご了承ください。


var refresh_token = "{メモったRefresh token}"
var username = "{Idに記される名前(私の場合tuaaasa)}"

// アクセストークンの発行
function getToken() {
  var endpoint = 'https://accounts.spotify.com/api/token'
  
  var encode = Utilities.base64Encode("{メモったClient ID}:{メモったClient Secret}", Utilities.Charset.UTF_8)
  var options = {
    'method': 'post',
    'contentType': 'application/x-www-form-urlencoded',
    'headers': {
      'Authorization': 'Basic ' + encode
    },
    'payload': {
      'grant_type': 'refresh_token',
      'refresh_token': refresh_token
    },
    'muteHttpExceptions': true
  }

  var response = UrlFetchApp.fetch(endpoint, options)
  var content = response.getContentText("UTF-8")
  return JSON.parse(content).access_token
}

// 今月のプレイリストを作成
function makePlaylist(yyyymm, token) {
  var endpoint = 'https://api.spotify.com/v1/users/' + username + '/playlists';

  var data = {
    'name': yyyymm,
    'description': 'liked list in ' + yyyymm,
    'public': false
  }
  var options = {
    'method': 'post',
    'contentType': 'application/json',
    'headers': {
      'Authorization': 'Bearer ' + token
    },
    'payload': JSON.stringify(data)
  }

  var response = UrlFetchApp.fetch(endpoint, options)
  return JSON.parse(response).id
}

// 今月のプレイリストを取得
function getPlaylistThisMonth(token) {
  var endpoint = 'https://api.spotify.com/v1/users/'+ username +'/playlists?offset=0&limit=20';
  var options = {
    'method': 'get',
    'headers': {
      'Authorization': 'Bearer ' + token
    }
  }

  var response = UrlFetchApp.fetch(endpoint, options)

  var date = new Date()
  var yyyymm = String(date.getFullYear()) + String(date.getMonth()+ 1)

  var targetPlaylist = JSON.parse(response).items.filter(item => item.name === yyyymm)

  if(targetPlaylist[0]){
    return targetPlaylist[0].id
  }else{
    return makePlaylist(yyyymm, token)
  }
}

// 直近1時間以内にいいねした曲を取得
function getNewSongs(token) {
  var endpoint = 'https://api.spotify.com/v1/me/tracks?offset=0&limit=20';
  var options = {
    'method': 'get',
    'headers': {
      'Authorization': 'Bearer ' + token
    }
  }

  var response = UrlFetchApp.fetch(endpoint, options)

  var now = new Date()
  now.setMinutes(now.getHours() - 1)

  var targetItems = JSON.parse(response).items.filter(
    item => {
      var itemDate = new Date(item.added_at)
      return itemDate > now
    })

  return targetItems.map(item => item.track.uri)
}

// 曲をプレイリストに追加
function addNewSongs(playlist_id, uris, token) {
  var endpoint = 'https://api.spotify.com/v1/playlists/' + playlist_id + '/tracks';
  
  var data = {
    'uris': uris,
    'position': 0
  }
  var options = {
    'method': 'post',
    'contentType': 'application/json',
    'headers': {
      'Authorization': 'Bearer ' + token
    },
    'payload': JSON.stringify(data)
  }

  var response = UrlFetchApp.fetch(endpoint, options)
}

// メイン関数
function main() {
  var token = getToken()
  var uris = getNewSongs(token)
  if(uris.length >= 1){
    var playlistId = getPlaylistThisMonth(token)
    addNewSongs(playlistId, uris, token)
  }
}

あらかじめ生成しておいたリフレッシュトークンを使用してアクセストークンを発行

本処理のコードです。

// アクセストークンの発行
function getToken() {
  var endpoint = 'https://accounts.spotify.com/api/token'
  
  var encode = Utilities.base64Encode("{メモったClient ID}:{メモったClient Secret}", Utilities.Charset.UTF_8)
  var options = {
    'method': 'post',
    'contentType': 'application/x-www-form-urlencoded',
    'headers': {
      'Authorization': 'Basic ' + encode
    },
    'payload': {
      'grant_type': 'refresh_token',
      'refresh_token': refresh_token
    },
    'muteHttpExceptions': true
  }

  var response = UrlFetchApp.fetch(endpoint, options)
  var content = response.getContentText("UTF-8")
  return JSON.parse(content).access_token
}

Basic認証のため、Client IDClient Secretをエンコードします。

  var encode = Utilities.base64Encode("{メモったClient ID}:{メモったClient Secret}", Utilities.Charset.UTF_8)

1時間以内にいいねされた曲リストを取得

本処理のコードです。

// 直近1時間以内にいいねした曲を取得
function getNewSongs(token) {
  var endpoint = 'https://api.spotify.com/v1/me/tracks?offset=0&limit=20';
  var options = {
    'method': 'get',
    'headers': {
      'Authorization': 'Bearer ' + token
    }
  }

  var response = UrlFetchApp.fetch(endpoint, options)

  var now = new Date()
  now.setMinutes(now.getHours() - 1)

  var targetItems = JSON.parse(response).items.filter(
    item => {
      var itemDate = new Date(item.added_at)
      return itemDate > now
    })

  return targetItems.map(item => item.track.uri)
}

GASで毎時実行するので、1時間以内を検索対象に指定します。

  var now = new Date()
  now.setHours(now.getHours() - 1)

フィルタ部です。
曲をプレイリストに追加する際uriで曲を指定するため、uriの配列を返します。

var targetItems = JSON.parse(response).items.filter(
    item => {
      var itemDate = new Date(item.added_at)
      return itemDate > now
    })

  return targetItems.map(item => item.track.uri)

取得した曲リストを今月のプレイリストに追加(今月のプレイリストがなければ作成して追加)

本処理のコードです。

// 曲をプレイリストに追加
function addNewSongs(playlist_id, uris, token) {
  var endpoint = 'https://api.spotify.com/v1/playlists/' + playlist_id + '/tracks';

  var data = {
    'uris': uris,
    'position': 0
  }
  var options = {
    'method': 'post',
    'contentType': 'application/json',
    'headers': {
      'Authorization': 'Bearer ' + token
    },
    'payload': JSON.stringify(data)
  }

  var response = UrlFetchApp.fetch(endpoint, options)
}

ここで曲を指定しています。
positionはプレイリスト中に曲を挿入する位置です。
0は先頭を指定しています。

var data = {
    'uris': uris,
    'position': 0
  }

GASの設定

Apps Scriptで新規プロジェクトを作成。コーディング。
実行許可を行うため一度動作確認しましょう。

トリガータブ > トリガーを追加から以下設定。
時間の間隔を選択(時間) 欄を1時間おきにしましょう。

スクリーンショット 2020-12-26 17.54.16.png

結果

上手にできました!

スクリーンショット 2020-12-26 17.56.40.png

さいごに

これで思い出すのが楽になりました。
Spotify以外のサービスで曲を聴くこともあるので、できたら他のサービスでもやってみたいです。
(SoundCloudもAPIあるっぽい、作るぞ...!!!)

最後まで読んでいただきありがとうございました〜。良い音楽ライフを〜♪

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?