電波ビーチ

☆(ゝω・)v

Google Apps ScriptでYoutube動画の公開設定を管理する

Google Apps ScriptでYoutube Data APIを気軽に使える(別にアクセストークンとかキーの発行とか要らない!)らしい。ということでデモ的に触ってみて、ごく簡易な管理シートを作ってみた。

github here (MITライセンスにしてる)

Sheetはこちら -> Youtube MyVideo Status Manager - SpreadSheet。コピーを作成して適当にどうぞ

使い方

カスタムメニューから適当にエントリーする。

custom menu

Sheet更新で、ログインしているGoogleアカウントのYoutubeのマイビデオをSheet上にリストで表示する。自分の場合は60本くらいが数十秒くらいで終わった。

Sheetはなんの飾りっ気もないです

なお、ブランドアカウントをもっているとログインは促されるものの何も生じない。ブランドアカウントのビデオを取得する方法はわからなかった。残念。

ブランドアカウントでログインは促されるものの使えない

取得できたら、やれることは公開状況更新予約ボタンから「特定の動画の公開設定を予約して変更する」ができるだけ。それだけなので別にSheet使う必要はないんだがまあ初めてYoutube APIを使うってことでこんなもんでしょう。IDは手入力してもいいし選んでもいい

なんの素っ気もないモーダル。css当てるまでもないので...

あとはGoogle Apps Script側でそれなりのことをする。ちなみに以下のような機能には対応していない。

  • 予約のキャンセル
  • 単一の動画に対する複数の予約状況の確認
    • 表示はされないので確認はできないけど、やりたければ「n時m分に公開」「x時y分に非公開」というのは可能ではある
    • まあオンラインエディタのトリガーのとこからは確認できるけど

モーダルも味も素っ気もないごくシンプルなただのhtmlなので、「選択した動画の状態によって公開/限定公開/非公開の提示を切り替える」とかそういうあってたり前な機能もない。単純にそこまでやる気力は無いというだけですが

Youtube API

ここからは内部の話。Youtube Data APIGoogle Apps Scriptで使う、というサンプルは公式が提供していたのでまずこれを適当に読んだ。

Youtube Data API v3 - Google Apps Script Quickstart

それから、quotaというのがあるというのを知った。要はこれこれのAPIを使うにはこれだけのコストがかかるよ、というもので、通常は10,000quota/1dayらしい。APIごとに、一回叩くとどれくらい消費するかのリストがあった。

Youtube Data API v3 - Quota Calculator

今回は自分の投稿した動画(videoId)を拾ってくるのにYouTube.Search.list、動画ごとに公開状況を取得するのにYouTube.Videos.listをまず使っている。前者は一度で最大50本取得でき、一回ごとに100quotaの消費。後者は1回ごとに1quotaの消費である。はちゃめちゃに投稿数が多いなら、一覧してSheetに表示するSheet更新では途中でquotaを食いつぶすことになるが、1000本の動画を投稿していたとしても3000quota程度だし、まあ超過する場合は考えないでおく。超過する可能性があるなら、数日に分けてTriggerで実行するようにしておけばいい。面倒だが自分でvideoIDを手打ちしてもいい。ヘビーな使い方をしている人なら既にそれなりの手段や正当な対策を施しているだろう。これは超ライトユーザー向けである。

取得する部分はこのような感じ

const retrieveMyVideos = () => {
    let nextPageToken = "";
    const videoData = [];
    while(nextPageToken !== null){
      const videos = YouTube.Search.list("id,snippet", {
        forMine: true,
        maxResults: 50,
        type: "video",
        order: "date",
        pageToken: nextPageToken,
      });
      nextPageToken = videos.nextPageToken === undefined ? null : videos.nextPageToken;
      // とりあえずvideo IDだけあればいい
      // statusを取得するにはVideo.listを叩く。そのときについてくるデータを使う
      const videosId = videos.items.map(v=>v.id.videoId);
      const videosInfo = YouTube.Videos.list("snippet,status", {id: videosId.join(",")});
      videosInfo.items.forEach(v=>{
        const myVideo = new MyVideo(v.snippet.publishedAt, v.id, v.snippet.title, v.snippet.thumbnails.default.url, `http://www.youtube.com/watch?v=${v.id}`, v.status.privacyStatus);
        videoData.push(myVideo);
      });
    }
    return videoData;
};

サムネイルが取ってこれるのでついでに取得して、Sheetのセル上に表示するようにしてみた。CellImageというのを初めて使った。

Google Apps Script Referenc - Class CellImage

      const builder = SpreadsheetApp.newCellImage();
      for(let [idx, row] of result.entries()){
        const thumbnailUrl = row[thumbnailIdx];
        const title = row[titleIdx];
        const img = builder.setSourceUrl(thumbnailUrl).setAltTextTitle(title).toBuilder().build();
        row[thumbnailIdx] = img;
        result[idx] = row;
      }
      fieldRange.setValues(result);

なお、quotaはGoogleアカウントごとに計算されるもので、別のサービスでquotaを消費するものを使用していたり、別のGoogle Apps ScriptでYoutube APIを使用している場合もそれらを合算して消費される。また、申請して採用されれば上限数を増やしてくれるそうである

YouTube API Services - Audit and Quota Extension Form

公開状況更新予約で公開設定の予約をするときはYouTube.Videos.listYouTube.Videos.updateを使う。一回の変更あたり51quota消費する。これはまあ、一覧さえ取得されていればそうそう問題になることはないだろう。たぶん。現在の公開状態はprivacyStatusで取得でき、これを指定のステータスで更新してYoutube.Videos.updateに投げて動画情報を更新した。

  const propsObj = JSON.parse(jsonizedTriggerProp);
  const curStatus = YouTube.Videos.list("status",{
    id: propsObj.videoIdInput,
  });
  const newStatus = curStatus;
  newStatus.privacyStatus = propsObj.nextStatus;
  const resource = {
    status: newStatus,
    id: propsObj.videoIdInput,
  };
  YouTube.Videos.update(resource, 'id,status');

また、通常の手段ではGoogle Cloud PlatformからYoutube APIを使うプロジェクトを作成し、APIキーを取得して使用するが、その場合は消費したquotaはGoogle Cloud Consoleから確認できるそうである。しかしGoogle Apps ScriptからYoutube APIライブラリを追加して使用するとCloud Consoleでは確認することができず、というか確認する手段があるのかわからない。

Triggerによる投機実行

この時間にトリガーを起動したい、そしてそのトリガーでは特定の動画IDに対して特定のステータスで上書きする。という機能を考える。Triggerに引数を与えたい、が、不可能である。Apps Script内でトリガーを作成するとき、関数名での指定であり、引数を渡せる仕様にはなっていない。

どうしようかなと思ってたら、各TriggerにはTrigger IDが存在しており、それをキーにしてPropertyServiceで値を管理する方法があるそうだ。

teratail - GASのトリガーで関数に引数を渡したい

ハレルヤ!知らんかった。作成したTriggerのIDをgetUniqueID()で取得し、プロパティに保存しておいて、

  const properties = PropertiesService.getScriptProperties();
  const trig = ScriptApp.newTrigger("setStatusTrigger_").timeBased().at(new Date(schedule)).create();
  const trigUID = trig.getUniqueId();
  properties.setProperty(`args_${trigUID}`, JSON.stringify(formDataObj));

発火されたTriggerの自身のイベントオブジェクトにはtriggerUidプロパティがあるのでこれをキーに使い、先程保存しておいた値を取り出す。

Google Apps Script Reference - Event Objects (Time-driven events)

const setStatusTrigger_ = (triggerObj) =>{
  const triggerId = triggerObj.triggerUid;
  const properties = PropertiesService.getScriptProperties();
  const jsonizedTriggerProp = properties.getProperty(`args_${triggerId}`);
  if(jsonizedTriggerProp === null){
    console.error(`can't find "args_${triggerId}" in script properties.\nthis trigger don't be fired.`);
    return;
  }
  const propsObj = JSON.parse(jsonizedTriggerProp);
 .
 .
 .
}

非常に便利。まったく知らなかったがこういう方法があるのか。知らなかったので他にもデモを書いた。並列でTriggerを実行するサンプルである。いろいろな使い方ができそう

github - halllllll/google-apps-script-sample/blob/master/trigger-passed-variables

勉強になりました

Google Apps ScriptからYoutube APIをお気軽に触れてたいへんに楽である。ほかにもなんかアイディアがあったら手を動かしてみよう。

リファレンスちゃんと読もうね(戒め)