Youtubeの動画一覧をSpreadSheetの表にするGoogle Apps Script

最近,某ゲーム実況者の動画をよく見るようになったけれど,数が多すぎてYoutubeの画面では探しにくいのでSpreadSheetの表を作ることにした.
URLとサムネと投稿日時とタイトルはYoutube Data APIで取得できるので,勉強がてらGAS(Google Apps Script)を組んでみた.

// Youtube Data API側関数
/**********************************************************************/
// 特定のチャンネルの特定の期間内の動画リストを取得
async function getChannelVideos(channelID, publishedAfter, publishedBefore)
{
 let list = [];

 // 検索オプション
 let searchOps = {
   channelId: channelID,
   maxResults: 50,
   order: "date",
   pageToken: null,
   publishedAfter: dateFormat(publishedAfter),
   publishedBefore: dateFormat(publishedBefore),
   type: "video"
 };

 // 一度に取得できる件数に制限があるので,「maxResults」件ずつ繰り返す
 do
 {
   // 条件を満たす「maxResults」件を取得
   let result = await YouTube.Search.list("snippet", searchOps);
   // 続きがあればnullでないトークンが取得できる
   searchOps.pageToken = result.nextPageToken;

   list = list.concat(result.items.map(o => new {
     id:          o.id.videoId,                   // 動画ID
     title:       o.snippet.title,                // タイトル
     publishedAt: o.snippet.publishedAt,          // 投稿日時
     thumbnails:  o.snippet.thumbnails.medium.url // サムネURL
   }));
 }
 while(searchOps.pageToken != null); // 「nextPageToken」が取得できる限り続ける

 Logger.log("count:" + list.length);

 return list;
/**********************************************************************/
}
/**********************************************************************/
// Date型の日付をYoutube Data API用のフォーマット(2022-01-01T00:00:00Z)に変換
function dateFormat(date)
{
 if(date)
 {
   return Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy-MM-dd') + "T"
        + Utilities.formatDate(date, 'Asia/Tokyo', 'HH:mm:ss') + "Z";
 }
 else {
   return null;
 }
/**********************************************************************/
}
/**********************************************************************/
// チャンネルID又はユーザIDを使ってチャンネルIDと開設日時を取得
async function getChannelData(channel)
{
 // 取得する情報.チャンネルIDとsnippet
 let parts = [
   "id",
   "snippet"
 ];

 // 検索条件.ユーザIDかチャンネルIDが必要
 let filter = channel.id ? {id: channel.id}
                         : {forUsername: channel.userId};

 try
 {
   res = await YouTube.Channels.list(parts, filter).items[0];

   //とりあえずチャンネルIDと開設日時だけ
   channel.id = res.id;
   channel.publishedAt = res.snippet.publishedAt;

   return channel;
 }
 catch(e) {
   Logger.log(e);
 }
/**********************************************************************/
}
// SpreadSheet側関数
/**********************************************************************/
async function update(name)
{
 // チャンネル名をキーにして用意しておいたシートからチャンネル情報を取得
 let channel = getChannelFromSheet(name);
 
 if(channel == null) {
   return;
 }
 
 // チャンネルIDか開設日時が不明ならYoutube Data APIで取得
 if(channel.id == null || channel.publishedAt == null) {
   await getChannelData(channel);
 }

 Logger.log(channel);

 // 期間の起点にするために表内の一番新しい動画の投稿日時を調べる.無ければ開設日時を使う
 let latestDate = getLatestDate(name) || channel.publishedAt;
 let currentDate = new Date();

 // 1日の情報取得量に制限があるので,1年分ずつ期間を区切って取得する
 let d0 = new Date(latestDate);
 let d1 = new Date(latestDate);
 d1.setFullYear(d1.getFullYear() + 1);

 let list = [];
 while(d0 <= currentDate)
 {
   Logger.log(dateFormat(d0) + " -> " + dateFormat(d1));

   // 1年分の情報取得を試みて成功したら配列に追加,失敗したら諦めてそれまでの分だけ反映させる
   try
   {
     list = list.concat(await getChannelVideos(channel.id, d0, d1));

     // 次の1年
     d0 = d1;
     d0.setMilliseconds(d0.getMilliseconds() + 1);
     d1 = new Date(d0);
     d1.setFullYear(d1.getFullYear() + 1);
   }
   catch(e)
   {
     Logger.log(e);
     
     break;
   }
 }

 // 成功した年数分だけ新しいもの順に並び替えてシートに反映させる
 list.sort((o0, o1) =>
 {
   if(o1.publishedAt < o0.publishedAt)      return -1;
   else if(o1.publishedAt > o0.publishedAt) return 1;
   else                                     return 0;
 });
 setValues(name, list);
/**********************************************************************/
}
/**********************************************************************/
//シートからチャンネル情報を取得
function getChannelFromSheet(name)
{
 let source = SpreadsheetApp.getActiveSpreadsheet();
 let sheet = source.getSheetByName("_");
 let values = sheet.getRange("A2:D").getValues();

 let line = values.find(l => l[0] == name);

 if(line == undefined)
   return null;
 else
 {
   return {
     name: name,                                           // 名前
     userId:      line[1] == "" ? null : line[1],          // ユーザID
     id:          line[2] == "" ? null : line[2],          // チャンネルID
     publishedAt: line[3] == "" ? null : new Date(line[3]) // 開設日時
   };
 }
/**********************************************************************/
}
/**********************************************************************/
// シート名(投稿者名と同一)から表内の最新投稿日時を取得
function getLatestDate(sheetName)
{
 let source = SpreadsheetApp.getActiveSpreadsheet();
 let sheet = source.getSheetByName(sheetName);
 let range = sheet.getRange("B2:B");
 let values = range.getValues();

 let str = values.reduce((r0, r1) => r0[0] < r1[0] ? r1 : r0)[0];

 return str != "" ? new Date(str) : null;
/**********************************************************************/
}
/**********************************************************************/
// 動画のリストをシートに反映
function setValues(sheetName, list)
{
 if(list.length == 0) return;

 let values = list.map(o => [
   "=image(\"" + o.thumbnails + "\")",  // サムネイルを画像として挿入
   new Date(o.publishedAt),             // 投稿日時
   "http://youtu.be/" + o.id,           // 動画リンク
   o.title                              // タイトル
 ]);

 let source = SpreadsheetApp.getActiveSpreadsheet();
 let sheet = source.getSheetByName(sheetName);
 // ヘッダー行の下に必要なだけ挿入してから書き込み
 sheet.insertRowsBefore(2, list.length);
 sheet.getRange(2, 1, values.length, values[0].length).setValues(values);
/**********************************************************************/
}

作成したシート.サムネ,投稿日時,URL,タイトルまでは自動化できた.ゲームタイトルで絞り込みたいので動画タイトルから正規表現で抽出.イレギュラーなものは手動入力で対応する.
https://docs.google.com/spreadsheets/d/1g8Y5N0fG0WfUx7-LucrrBzV4DMdNyIaIAX2RSoWKQFA/edit?usp=sharing

※2022/02/05追記
どうもこれでは上手くいかない様子(´・ω・`)


この記事が気に入ったらサポートをしてみませんか?