Spotify Podcastプレイリストを自動更新する方法

公開のたびに、毎回Spotifyのプレイリストを手動で更新しないといけないのがしんどかったので、GASで自動化してみました。
ChatGPT先生ありがとう。記事もほぼ書いてもらいました


※2024.09.27追記
Xへの自動ポスト機能を追加した高機能拡張版を作りました。
こちらへどうぞ
https://note.com/higuchiki/n/na543c707d5ea


この記事では、Google Apps Script (GAS) と Spotify API を使って、Podcastエピソードの自動更新とメール通知を行う方法を、初心者向けに分かりやすく説明します。コードをコピーしてそのまま使うことができるようにし、各ステップを丁寧に説明していきます。

手順1: Google Apps Script(GAS)の準備

  1. Googleスプレッドシートを作成します。
    • A列にプレイリスト名を入力します。
    • C列に公開日(`YYYY-MM-DD`形式)を入力します。
画像
こんなかんじ
  1. スプレッドシート内で、「拡張機能」→「Apps Script」を選択し、スクリプトエディタを開きます。

手順2: スクリプトプロパティの設定

次に、Spotify APIを利用するためのクライアントIDやシークレットを設定します。

1. クライアントIDとクライアントシークレットの取得

Spotify for Developers でアカウントを作成し、アプリを登録してクライアントIDとクライアントシークレットを取得します。

2. スクリプトプロパティの設定

スクリプトエディタで、「ファイル」→「プロジェクトのプロパティ」→「スクリプトプロパティ」に次の項目を追加します。

  • `SPOTIFY_CLIENT_ID`: SpotifyのクライアントID
  • `SPOTIFY_CLIENT_SECRET`: Spotifyのクライアントシークレット
  • `DEPLOY_ID`: Google Apps ScriptのデプロイID
  • `PODCAST_SPOTIFY_ID`: 対象PodcastのSpotify ID
  • `SPOTIFY_PLAYLIST_ID`: プレイリストのSpotify ID
  • `NOTIFICATION_EMAIL`: 通知を受け取りたいメールアドレス

手順3: OAuthライブラリの追加

Spotify APIを利用するために、Google Apps ScriptにOAuth2ライブラリを追加する必要があります。

1. OAuth2ライブラリを追加

  1. スクリプトエディタで「ライブラリ」を選択します。
  2. 「ライブラリID」に以下のIDを入力して、OAuth2ライブラリを追加します。
1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF

これにより、Spotify APIに認証するためのOAuth2ライブラリがスクリプトに組み込まれます。

手順4: リダイレクトURIの設定

Spotify APIで正しく認証するには、Spotify for Developersのアプリ設定でリダイレクトURIを追加する必要があります。

  1. Spotify Dashboardでアプリを選択します。
  2. Redirect URIs」に以下のURLを追加します。
https://script.google.com/macros/s/{DEPLOY_ID}/usercallback

※ `{DEPLOY_ID}` はデプロイIDに置き換えてください。

手順5: 認証を実行

次に、Spotify APIにアクセスするための認証を行います。

  1. スクリプトエディタ内で `authorize` 関数を実行します。
  2. ログに表示されたURLを開き、Spotifyでアプリにアクセス権を与えてください。

手順6: スクリプトの自動実行設定

Podcastエピソードの更新を定期的にチェックするために、トリガーを設定します。

  1. スクリプトエディタで「時計のアイコン」から「トリガー」を開きます。
  2. `checkPodcastRSS` 関数を日単位または時間単位で実行するように設定します。

手順7: メール通知の仕組み

このスクリプトでは、最新エピソードがプレイリストに追加されると、指定したメールアドレスに通知が送信されます。通知にはエピソードタイトル、プレイリストタイトル、公開日が含まれます。


まとめ

この手順に従えば、Spotify APIGoogle Apps Scriptを使ってPodcastエピソードを自動でプレイリストに追加し、メールで通知を受け取る仕組みが作成できます。

以下にスクリプト全文を掲載しますので、そのままコピーして、冒頭の、

const SPREADSHEET_ID = '{スプレッドシートID}'; // スプレッドシートID
const SHEET_ID = '{シートID}'; // シートID

だけ書き換えて使ってください。


スクリプト全文:

// 定数定義: スプレッドシートのIDとシートID
const SPREADSHEET_ID = '{スプレッドシートID}'; // スプレッドシートID
const SHEET_ID = '{シートID}'; // シートID

// シートIDでシートを取得する関数
function getSheetById(spreadsheet, sheetId) {
  var sheets = spreadsheet.getSheets(); // すべてのシートを取得
  for (var i = 0; i < sheets.length; i++) {
    if (sheets[i].getSheetId() === sheetId) { // シートIDが一致するか確認
      return sheets[i];
    }
  }
  throw new Error('指定されたシートIDのシートが見つかりませんでした。'); // シートが見つからない場合のエラーハンドリング
}
// 公開日からプレイリストのタイトルを探す(本日の日付を基に)
function findPlaylistTitleByDate(currentDate) {
  var spreadsheet = SpreadsheetApp.openById(SPREADSHEET_ID);
  var sheet = getSheetById(spreadsheet, SHEET_ID); // シートIDでシートを取得
  
  var lastRow = sheet.getLastRow();
  var closestDate = null;
  var closestTitle = null;

  // 公開日(C列)を2行目から順に見ていき、一番近い日付を探す
  for (var i = 2; i <= lastRow; i++) {
    var publishDate = sheet.getRange(i, 3).getValue(); // C列が公開日

    // 本日の日付と一致または通過した場合、その日付を記録
    if (publishDate <= currentDate) {
      closestDate = publishDate;
      closestTitle = sheet.getRange(i, 1).getValue(); // A列にプレイリストタイトル
    }
  }
  
  // 最も近い日付を返す
  if (closestDate && closestTitle) {
    Logger.log('公開日が一致または近い行を発見: ' + closestDate + ' タイトル: ' + closestTitle);
    return { playlistTitle: closestTitle, publishDate: closestDate };
  } else {
    Logger.log('公開日が一致する行が見つかりませんでした。');
    return null;
  }
}


// Spotify API 認証用
function getSpotifyService() {
  var scriptProperties = PropertiesService.getScriptProperties();
  var clientId = scriptProperties.getProperty('SPOTIFY_CLIENT_ID'); 
  var clientSecret = scriptProperties.getProperty('SPOTIFY_CLIENT_SECRET'); 
  var deployId = scriptProperties.getProperty('DEPLOY_ID'); 
  
  var redirectUri = 'https://script.google.com/macros/s/' + deployId + '/usercallback'; 
  
  return OAuth2.createService('spotify')
    .setAuthorizationBaseUrl('https://accounts.spotify.com/authorize')
    .setTokenUrl('https://accounts.spotify.com/api/token')
    .setClientId(clientId)
    .setClientSecret(clientSecret)
    .setRedirectUri(redirectUri)
    .setPropertyStore(PropertiesService.getUserProperties())
    .setCallbackFunction('authCallback')
    .setScope('playlist-modify-public playlist-modify-private');
}

// 認証のトリガー
function authorize() {
  var service = getSpotifyService();
  if (service.hasAccess()) {
    Logger.log('Already authorized');
  } else {
    var authorizationUrl = service.getAuthorizationUrl();
    Logger.log('Open the following URL and authorize the app: %s', authorizationUrl);
  }
}

// 認証後のコールバック処理
function authCallback(request) {
  var service = getSpotifyService();
  var authorized = service.handleCallback(request); 
  if (authorized) {
    return HtmlService.createHtmlOutput('認証に成功しました。');
  } else {
    return HtmlService.createHtmlOutput('認証に失敗しました。');
  }
}


// SpotifyからPodcastエピソードを取得し、最新のものをプレイリストに追加
function checkPodcastRSS() {
  var service = getSpotifyService();
  var scriptProperties = PropertiesService.getScriptProperties();
  var podcastId = scriptProperties.getProperty('PODCAST_SPOTIFY_ID'); 
  var playlistId = scriptProperties.getProperty('SPOTIFY_PLAYLIST_ID'); 
  var lastPodcastGuid = scriptProperties.getProperty('PODCAST_GUID'); // 以前のエピソードのGUID
  var notificationEmail = scriptProperties.getProperty('NOTIFICATION_EMAIL'); // 通知先のメールアドレス
  
  if (!podcastId || !playlistId || !notificationEmail) {
    Logger.log('Podcast ID、プレイリストID、または通知先メールアドレスが設定されていません。');
    return;
  }

  if (service.hasAccess()) {
    // エピソード数を取得する
    var url = 'https://api.spotify.com/v1/shows/' + podcastId + '/episodes?market=JP&limit=1';
    var options = {
      method: 'GET',
      headers: {
        Authorization: 'Bearer ' + service.getAccessToken(),
        'Content-Type': 'application/json'
      }
    };
    var response = UrlFetchApp.fetch(url, options);
    var json = JSON.parse(response.getContentText());
    
    if (json && json.total > 0) {
      var totalEpisodes = json.total; // 全エピソード数を取得
      Logger.log('全エピソード数: ' + totalEpisodes);
      
      // 最新のエピソードを取得するためのoffsetを設定
      var latestEpisodeUrl = 'https://api.spotify.com/v1/shows/' + podcastId + '/episodes?market=JP&limit=1&offset=' + (totalEpisodes - 1);
      var latestEpisodeResponse = UrlFetchApp.fetch(latestEpisodeUrl, options);
      var latestEpisodeJson = JSON.parse(latestEpisodeResponse.getContentText());
      
      if (latestEpisodeJson && latestEpisodeJson.items && latestEpisodeJson.items.length > 0) {
        var latestEpisodeUri = latestEpisodeJson.items[0].uri; // 最新エピソードのURIを取得
        var latestEpisodeGuid = latestEpisodeJson.items[0].id; // SpotifyのエピソードID
        var latestEpisodeDateUTC = new Date(); // 本日の日時を取得

        // 本日の日付を取得
        Logger.log('本日の日時 (JST): ' + latestEpisodeDateUTC.toLocaleString('ja-JP'));

        var latestEpisodeTitle = latestEpisodeJson.items[0].name; // 最新エピソードのタイトルを取得
        
        // GUIDが以前のエピソードと同じ場合は処理を停止
        if (lastPodcastGuid === latestEpisodeGuid) {
          Logger.log('最新エピソードはすでに処理済みです。');
          return;
        }
        
        // 新しいエピソードを認識した場合、処理を進める
        Logger.log('新しいエピソード URI: ' + latestEpisodeUri + ' タイトル: ' + latestEpisodeTitle + ' 本日 (JST): ' + latestEpisodeDateUTC.toLocaleString('ja-JP'));
        
        // 新しいGUIDを保存
        scriptProperties.setProperty('PODCAST_GUID', latestEpisodeGuid);
        
        // 公開日の行をスプレッドシートで探す(本日の日付と比較)
        var playlistData = findPlaylistTitleByDate(latestEpisodeDateUTC);
        
        if (playlistData) {
          var playlistTitle = playlistData.playlistTitle;
          var publishDate = playlistData.publishDate;
          
          // プレイリストにエピソードを追加
          var playlistId = findOrCreatePlaylist(service, playlistTitle);
          addPodcastToPlaylist(service, playlistId, latestEpisodeUri, latestEpisodeTitle, playlistTitle, publishDate);

          // エピソードが追加されたことを通知
          sendEmailNotification(notificationEmail, latestEpisodeTitle, playlistTitle, publishDate);
        }
      } else {
        Logger.log('最新エピソードが見つかりませんでした。');
      }
    } else {
      Logger.log('エピソードが見つかりませんでした。');
    }
  } else {
    Logger.log('アクセストークンがありません。authorize()を実行して認証を行ってください。');
  }
}

// プレイリストが存在するか確認し、存在しなければ作成
function findOrCreatePlaylist(service, playlistTitle) {
  var options = {
    method: 'GET',
    headers: {
      Authorization: 'Bearer ' + service.getAccessToken(),
      'Content-Type': 'application/json'
    }
  };
  
  // プレイリストを探す(仮にユーザーのプレイリスト一覧から探すと仮定)
  var url = 'https://api.spotify.com/v1/me/playlists';
  var response = UrlFetchApp.fetch(url, options);
  var playlists = JSON.parse(response.getContentText()).items;
  
  for (var i = 0; i < playlists.length; i++) {
    if (playlists[i].name === playlistTitle) {
      Logger.log('既存のプレイリストを発見: ' + playlistTitle);
      return playlists[i].id;
    }
  }
  
  // 認証されたユーザーの情報を取得する
  var userResponse = UrlFetchApp.fetch('https://api.spotify.com/v1/me', options);
  var user = JSON.parse(userResponse.getContentText());
  var userId = user.id; // 認証ユーザーのIDを取得

  // 見つからなかった場合、新しいプレイリストを作成
  Logger.log('プレイリストが存在しないため、新しく作成します: ' + playlistTitle);
  var createOptions = {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + service.getAccessToken(),
      'Content-Type': 'application/json'
    },
    payload: JSON.stringify({
      name: playlistTitle,
      description: '自動作成プレイリスト',
      public: false
    })
  };
  
  // 認証ユーザーのためにプレイリストを作成
  var createResponse = UrlFetchApp.fetch('https://api.spotify.com/v1/users/' + userId + '/playlists', createOptions);
  var createdPlaylist = JSON.parse(createResponse.getContentText());
  return createdPlaylist.id;
}

// プレイリストにエピソードを追加
function addPodcastToPlaylist(service, playlistId, episodeUri, episodeTitle, playlistTitle, publishDate) {
  var url = 'https://api.spotify.com/v1/playlists/' + playlistId + '/tracks';
  var payload = {
    uris: [episodeUri] 
  };
  var options = {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + service.getAccessToken()
    },
    contentType: 'application/json',
    payload: JSON.stringify(payload)
  };
  var response = UrlFetchApp.fetch(url, options);
  var responseJson = JSON.parse(response.getContentText());

  // ログに追加したエピソードのタイトル、プレイリスト名、公開日を表示
  Logger.log('エピソードをプレイリストに追加しました。Response: ' + JSON.stringify(responseJson));
  Logger.log('追加されたエピソード: ' + episodeTitle + ' プレイリスト: ' + playlistTitle + ' 公開日: ' + publishDate);
}

// スクリプトプロパティ "PODCAST_GUID" を "0" に設定する関数
function setPodcastGuidToZero() {
  // スクリプトプロパティにアクセス
  var scriptProperties = PropertiesService.getScriptProperties();
  
  // プロパティ "PODCAST_GUID" の値を "0" に設定
  scriptProperties.setProperty('PODCAST_GUID', '0');
  
  // 確認のためのログ出力
  Logger.log('PODCAST_GUID has been set to 0');
}

// メール通知を送信する関数
function sendEmailNotification(emailAddress, episodeTitle, playlistTitle, publishDate) {
  var subject = 'Spotifyプレイリストが更新されました';
  var message = '最新のエピソードがプレイリストに追加されました:\n' +
                'エピソードタイトル: ' + episodeTitle + '\n' +
                'プレイリストタイトル: ' + playlistTitle + '\n' +
                '公開日: ' + publishDate;

  // メール送信
  MailApp.sendEmail(emailAddress, subject, message);
  Logger.log('通知メールを送信しました: ' + emailAddress);
}

この方法を活用して、Podcastエピソードの更新を自動化し、作業を効率化しましょう!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です