Google Apps ScriptでPodcastエピソードのメタ情報を自動取得

Podcastの編集を業務委託する OR される方が増えてきてます。
そんな中、ギャラの計算方法も「1月あたりいくら」「1本あたりいくら」「1分あたりいくら」など多様なパターンがあるんじゃないかと。

ちなみに僕は、受注時も発注時も分数計算でやることが多いんですが、この計算がめちゃくちゃ面倒だったので、自動計算してくれるアプリを作りました。

こんな感じになります。

画像

もしよかったら、記事の一番下から投げ銭できるんで、投げ銭ください。

では、ChatGPTくんによる説明をご覧ください。


こんにちは!
今回は、Google Apps Scriptを使ってPodcastのエピソード情報をRSSフィードから自動取得し、Googleスプレッドシートに自動的に記載する方法をご紹介します。

1. このスクリプトの特徴

・RSSフィードからデータ取得

RSSフィードに記載されたPodcastエピソードの情報(GUID、タイトル、公開日時、再生時間など)を取得します。

・期間指定によるフィルタリング

スクリプトプロパティに設定した開始日時(START_DATE)と終了日時(END_DATE)をもとに、対象期間内のエピソードだけをシートに反映します。

  • 両方未設定の場合: 全エピソード取得
  • 開始日時のみ設定: 指定日時以降のエピソード取得
  • 終了日時のみ設定: 指定日時以前のエピソード取得

・柔軟な秒数丸め設定

エピソードの再生時間(RSSで取得した形式)を分単位に計算する際、丸め方をスクリプトプロパティの ROUNDING_RULE で指定可能です。
設定できる丸めルールは以下の3種類:

  1. ルール1:30秒未満は切り捨て、30秒以上は切り上げ
    例:1分29秒 → 1分、1分36秒 → 2分
  2. ルール2:秒は常に切り捨て
    例:1分50秒 → 1分
  3. ルール3:秒が0でなければ切り上げ
    例:1分00秒 → 1分、1分01秒 → 2分

スクリプトプロパティに設定がなければ、デフォルトはルール1が適用されます。

・重複防止&視覚的な補助

既に取得済みのエピソードはGUIDで判定してスキップ。
また、偶数月のエピソード行には背景色を付け、視認性を向上させています。

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

スクリプトエディタの「ファイル」→「プロパティ」→「スクリプトのプロパティ」で、以下のキーと値を設定してください。

  • RSS_URL:RSSフィードのURL
  • START_DATE(任意):例 “20250101”(開始日時)
  • END_DATE(任意):例 “20250131”(終了日時)
  • ROUNDING_RULE(任意):1, 2, または 3(設定しなければデフォルトは 1)

3. スプレッドシートの準備

シート名は 「EP LIST」 にしてください。
また、以下のような列構成がおすすめです。

  • A1セル:実行時の「更新日時」が記録される
  • 行2(ヘッダー行)
    • A列:GUID
    • B列:タイトル
    • C列:配信日(yyyy/MM/dd 形式)
    • D列:再生時間(分)(丸めルールに基づいて計算)
    • E列:元の再生時間(例:00:30:00)
  • 行3以降:各エピソードのデータが追加されます

4. 完成したコード全文

function findLastRow(sheet) {
  const range = sheet.getRange('A2:E');
  const values = range.getValues();
  
  for (let row = 0; row < values.length; row++) {
    if (values[row].every(cell => cell === '')) {
      return row + 1; // 空行が見つかったら、その一つ前の行を最終行として返す
    }
  }
  
  return values.length + 1; // 全て埋まっている場合は最終行を返す
}

function calculateDuration(parts, roundingMode) {
  let baseMinutes, seconds;
  if (parts.length === 3) {
    baseMinutes = parts[0] * 60 + parts[1];
    seconds = parts[2];
  } else if (parts.length === 2) {
    baseMinutes = parts[0];
    seconds = parts[1];
  } else {
    return 0;
  }
  
  switch (roundingMode) {
    case 1: // 30秒未満は切り捨て、30秒以上は切り上げ
      return seconds >= 30 ? baseMinutes + 1 : baseMinutes;
    case 2: // 秒は常に切り捨て
      return baseMinutes;
    case 3: // 秒が0でなければ切り上げ
      return seconds > 0 ? baseMinutes + 1 : baseMinutes;
    default:
      return seconds >= 30 ? baseMinutes + 1 : baseMinutes;
  }
}

function fetchAndUpdatePodcastData() {
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("EP LIST");
  if (!sheet) {
    Logger.log('シート「EP LIST」が見つかりません');
    return;
  }
  
  // スクリプトプロパティから各プロパティを取得
  const scriptProperties = PropertiesService.getScriptProperties();
  const url = scriptProperties.getProperty("RSS_URL");
  if (!url) {
    Logger.log("RSS_URLがスクリプトプロパティに設定されていません。");
    return;
  }
  
  // 開始日時と終了日時(例:"20250101"の形式)を取得
  const startDateStr = scriptProperties.getProperty("START_DATE");
  const endDateStr = scriptProperties.getProperty("END_DATE");
  let startDate = null;
  let endDate = null;
  
  if (startDateStr) {
    startDate = new Date(startDateStr.substr(0,4) + '-' + startDateStr.substr(4,2) + '-' + startDateStr.substr(6,2));
  }
  
  if (endDateStr) {
    endDate = new Date(endDateStr.substr(0,4) + '-' + endDateStr.substr(4,2) + '-' + endDateStr.substr(6,2));
  }
  
  // ROUNDING_RULE:1, 2, または 3(設定がなければデフォルトは1)
  const roundingRuleStr = scriptProperties.getProperty("ROUNDING_RULE");
  let roundingRule = 1;
  if (roundingRuleStr) {
    roundingRule = parseInt(roundingRuleStr, 10);
    if (isNaN(roundingRule) || roundingRule < 1 || roundingRule > 3) {
      roundingRule = 1;
    }
  }
  
  // A〜E列の最終行を見つける
  const lastRow = findLastRow(sheet);
  
  // スプレッドシートの既存のGUIDを取得(重複登録防止)
  let existingGUIDs = [];
  if (lastRow > 2) {
    existingGUIDs = sheet.getRange(3, 1, lastRow - 2).getValues().flat();
  }
  
  const response = UrlFetchApp.fetch(url);
  const xml = response.getContentText();
  const document = XmlService.parse(xml);
  const root = document.getRootElement();
  const ns = XmlService.getNamespace("itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd");
  const items = root.getChild("channel").getChildren("item");
  
  let newData = [];
  
  items.forEach(item => {
    const guid = item.getChildText("guid");
    
    // 既存のGUIDがある場合はスキップ
    if (existingGUIDs.includes(guid)) return;
    
    const title = item.getChildText("title");
    const pubDateText = item.getChildText("pubDate");
    const pubDate = new Date(pubDateText);
    
    // 開始日時、終了日時によるフィルタリング
    if (startDate && pubDate < startDate) return;
    if (endDate && pubDate > endDate) return;
    
    const formattedDate = Utilities.formatDate(pubDate, Session.getScriptTimeZone(), "yyyy/MM/dd");
    
    const durationElement = item.getChild("duration", ns);
    let durationInMinutes = 0;
    let originalDuration = '';
    
    if (durationElement) {
      originalDuration = durationElement.getText();
      const parts = originalDuration.split(':').map(Number);
      durationInMinutes = calculateDuration(parts, roundingRule);
    }
    
    // 新しいデータを配列に追加
    newData.push([guid, title, formattedDate, durationInMinutes, originalDuration, pubDate]);
  });
  
  // 配信日時(pubDate)で昇順に並び替え
  newData.sort((a, b) => a[5] - b[5]);
  
  // 新しいデータが存在する場合のみシートに書き込む
  if (newData.length > 0) {
    const startRow = lastRow + 1;
    const range = sheet.getRange(startRow, 1, newData.length, 5);
    range.setValues(newData.map(row => [row[0], row[1], row[2], row[3], row[4]]));
    
    // 偶数月の行に背景色を付ける
    newData.forEach((row, index) => {
      const pubDate = new Date(row[5]);
      const month = pubDate.getMonth() + 1;
      if (month % 2 === 0) {
        const lastColumn = sheet.getLastColumn();
        sheet.getRange(startRow + index, 1, 1, lastColumn).setBackground('#A7C6ED');
      }
    });
  }
  
  // 最終更新日時をA1セルに記入
  const now = new Date();
  const formattedNow = Utilities.formatDate(now, Session.getScriptTimeZone(), "yyyy/MM/dd HH:mm:ss");
  sheet.getRange('A1').setValue('更新日時: ' + formattedNow);
}

5. おわりに

このスクリプトを利用することで、RSSフィードからPodcastのエピソードデータを自動で取得し、指定した期間・丸めルールに基づいてGoogleスプレッドシートに反映できます。

ぜひお試しください!ご不明な点や改善のアイディアがあれば、コメントでシェアしていただけると嬉しいです。

コメントを残す

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