LoginSignup
1
0
個人開発エンジニア応援 - 個人開発の成果や知見を共有しよう!-

YouTube Liveの配信予定を見逃したくない人のための記事【非エンジニア・初心者向け】【詳細版】

Last updated at Posted at 2023-09-27

この記事を読むと出来るようになること

  • 記事中のコードをコピペするなどして、YouTube Liveの枠立ての通知をGoogleカレンダーを通じて自動で受け取れるようになる。
  • YouTube Liveの配信予定をカレンダー上でいつでも確認出来るようになる。
  • ひと手間加えればAppleカレンダーなど他のカレンダーアプリ経由でも通知を受け取れる

基本的にプログラミングの知識は不要です。

VTuberや実況者、その他YouTubeで活動されている配信者を追っている方にオススメです

0. 前提

0-1. 目標

  • YouTube Liveでの配信予定を見逃したくない
  • 枠立ての通知を事前に自動で受け取りたい。
  • 配信予定をカレンダー上でいつでも確認したい

0-2. 筆者が採った手法

  1. GAS (Google Apps Script) 上で YouTube Data APIGoogle Calendar API を利用出来るように準備する。
  2. インターネット上の情報を参考にしながら、ChatGPTと一緒にコーディングをする。
  3. 配信予定(枠立て)を通知したいYouTubeチャンネルのチャンネルID を控えておく。
  4. トリガーの基本的な設定をする。
  5. 配信枠がGoogleカレンダーに正しく予定として追加されていることを確認する。

0-3. この記事の簡易版について

noteでこの記事の簡易版を公開しています
難しいことやカスタマイズなどは抜きにしてまず使いたいという方は、そちらをご覧いただくことも可能です。

1. まず最初にすること

1-1. Googleドライブに飛んでファイルを作成する。

vivaldi_bHlI9d2Q3n_01.png
Googleドライブを開いたら適当なフォルダ上で「+ 新規」ボタンをクリック。
vivaldi_GPk39eZahE_01.png
「その他」→「Google Apps Script」からGAS(Google Apps Script)ファイルを作成します。

1-2. コードをコピペする

vivaldi_O6OkiTMR6V_01.png
作成したファイルを開くと「myFunction……」というコードが書かれているので削除してしまいましょう。
削除した部分に以下のコードをコピペしてください。

var apiKey = 'YOUR_API_KEY_HERE'; // ここにAPIキーを格納

// main関数の前回の実行時間を格納する変数
var lastMainFunctionExecutionTime = null;

function setTrigger() {
  const now = new Date();
  const currentHour = now.getHours();

  // 実行スケジュールを設定
  var triggerTime;
  if (currentHour >= 1 && currentHour < 11) {
    // 午前1時から午前11時までは2時間おき
    triggerTime = 2; // 2時間を時間単位で表現
  } else if (currentHour >= 11 && currentHour < 19) {
    // 午前11時から19時までは1時間おき
    triggerTime = 1; // 1時間を時間単位で表現
  } else if (currentHour >= 21 && currentHour < 24) {
    // 21時から24時までは15分おき
    triggerTime = 15; // 15分を分単位で表現
  }else {
    // それ以外では30分おき
    triggerTime = 30; // 30分を分単位で表現
  }

  // 前回のmain関数の実行時間が設定されている場合、実行頻度の変更を確認
  if (lastMainFunctionExecutionTime !== null) {
    var timeSinceLastExecution = now - lastMainFunctionExecutionTime;
    var minutesSinceLastExecution = timeSinceLastExecution / (1000 * 60); // ミリ秒を分に変換
    // 実行頻度が変更されていない場合、トリガーを削除しない
    if (minutesSinceLastExecution === triggerTime) {
      return;
    }
  }

  // 既存のトリガーを取得
  var existingTriggers = ScriptApp.getProjectTriggers();

  // main関数によるトリガーを削除
  for (var i = 0; i < existingTriggers.length; i++) {
    if (existingTriggers[i].getHandlerFunction() === 'main') {
      ScriptApp.deleteTrigger(existingTriggers[i]);
    }
  }

  // 新しいトリガーを設定
  var triggerBuilder = ScriptApp.newTrigger('main').timeBased();

  if (triggerTime >= 1 && triggerTime <= 14) {
    // 1から14の間ならeveryHoursを使う
    triggerBuilder.everyHours(triggerTime);
  } else {
    // それ以外の場合はeveryMinutesを使う
    triggerBuilder.everyMinutes(triggerTime);
  }

  triggerBuilder.create();
}

function delTrigger() {
  // すべてのトリガーを削除
  var triggers = ScriptApp.getProjectTriggers();
  for (var i = 0; i < triggers.length; i++) {
    ScriptApp.deleteTrigger(triggers[i]);
  }
}

function youtube_video_details(video_id) {
  try {
    // YouTube Data APIからビデオの詳細情報を取得するためのURLを構築
    var apiUrl = 'https://www.googleapis.com/youtube/v3/videos?part=snippet,liveStreamingDetails&id=' + video_id + '&key=' + apiKey;
    var response = UrlFetchApp.fetch(apiUrl);
    var data = response.getContentText();
    var videoData = JSON.parse(data);
    
    if (videoData.items && videoData.items.length > 0) {
      return videoData.items[0];
    } else {
      return null;
    }
  } catch (e) {
    console.error(e);
    return null;
  }
}

function youtube_search(channel_ids_str, max_results) {
  // YouTube Data APIを使用して、指定したチャンネルIDから動画情報を検索する処理
  var channel_ids = channel_ids_str.split(',');  // カンマで区切られたチャンネルIDをリストに変換
  var items = [];  // 検索結果を格納するリスト

  // 実行日時を取得
  var now = new Date();
  var formattedDate = now.toISOString();

  // 各チャンネルごとに動画を検索
  for (var i = 0; i < channel_ids.length; i++) {
    var channel_id = channel_ids[i];
    var search_response = YouTube.Search.list('id', {
      channelId: channel_id,
      part: 'id',
      order: 'date',
      maxResults: max_results,
      publishedAfter: formattedDate // 実行日時以降の動画を検索
    });
    items = items.concat(search_response.items);
  }

  return items;
}

function create_calendar_event(start_time, end_time, title, event_location, description, calendar_id) {
  // Googleカレンダーにイベントを追加する処理
  var calendar = CalendarApp.getCalendarById(calendar_id);
  if (!calendar) {
    throw new Error('Calendar not found');
  }
  
  var event = calendar.createEvent(title, new Date(start_time), new Date(end_time), {
    description: description,
    location: event_location,
    timeZone: 'Asia/Tokyo'
  });
  
  return event;
}

function main() {
  var channel_ids_str = 'channel_id_01,channel_id_02,channel_id_03'; // チャンネルIDをコンマ区切りで追加
  var max_results = 10;
  var targetCalendarId = 'YOUR_CALENDAR_ID_HERE'; // カレンダーIDを入力

  // 既存のカレンダーイベントを取得
  var existingEvents = CalendarApp.getCalendarById(targetCalendarId).getEvents(new Date(), new Date(new Date().getTime() + 7 * 24 * 60 * 60 * 1000)); // 1週間分の既存のイベントを取得

  var items = youtube_search(channel_ids_str, max_results);

  // 重複をチェックするためにビデオIDをトラッキング
  var trackedVideoIds = [];

  for (var i = 0; i < items.length; i++) {
    var video_id = items[i].id.videoId;

    // 既にトラッキングされたビデオIDかどうかを確認
    if (trackedVideoIds.includes(video_id)) {
      continue; // 既にトラッキングされていればスキップ
    }

    var details = youtube_video_details(video_id);
    if (!details) {
      continue;
    }

    if (details.liveStreamingDetails) {
      var scheduled_start_time_utc = new Date(details.liveStreamingDetails.scheduledStartTime);
      var scheduled_end_time_utc = new Date(scheduled_start_time_utc);
      scheduled_end_time_utc.setHours(scheduled_end_time_utc.getHours() + 2);

      // 既存のイベントと重複するか確認
      var isDuplicate = false;
      for (var j = 0; j < existingEvents.length; j++) {
        var existingEvent = existingEvents[j];
        if (existingEvent.getTitle() === details.snippet.title && existingEvent.getStartTime().getTime() === scheduled_start_time_utc.getTime()) {
          isDuplicate = true;
          break;
        }
      }

      if (!isDuplicate) {
        var video_title = details.snippet.title;
        var channel_title = details.snippet.channelTitle;
        var event_summary = video_title;
        var event_location = channel_title;
        var event_description = 'https://www.youtube.com/watch?v=' + video_id;

        create_calendar_event(scheduled_start_time_utc, scheduled_end_time_utc, event_summary, event_location, event_description, targetCalendarId);
      }

      // ビデオIDをトラッキングリストに追加
      trackedVideoIds.push(video_id);
    }
  }

  // main関数の実行完了後にlastMainFunctionExecutionTimeを更新
  lastMainFunctionExecutionTime = new Date();
}

なお、コーディングにあたっては以下のページを参考にさせていただきました。

1-3. サービスを追加する

vivaldi_Tkv7pE8aee_01.png
左側の「サービス」の項目から追加していきます。
無題544_20230927165147.png
一覧から 「Google Calendar API」「YouTube Data API」 を探し出して、それぞれ「追加」をクリックします。

2. APIキーの取得など

2-1. YouTube Data APIのAPIキーを取得する

以下の記事を参考にキーを取得してください。

2-2. APIキーをコードに貼り付ける

vivaldi_8OffvCCrGe_01.png
先程取得したキーをコードの1行目、**「YOUR_API_KE_HERE」**の部分に貼り付けてください。

3. カレンダーIDの取得など

3-1. カレンダーIDの取得

以下の記事を参考に、配信通知を予定として組み込みたいカレンダーカレンダーIDを取得してください。

3-2. カレンダーIDの貼り付け

vivaldi_VQ9H6b0uF8_01.png
コードの131行目「YOUR_CALENDAR_ID_HERE」 の部分に先程取得したカレンダーIDを貼り付けてください。

4. チャンネルIDの取得など

4-1. チャンネルIDの取得

以下の記事を参考に、通知させたいYouTubeチャンネルチャンネルIDをそれぞれ取得してください。

数はいくつでも構いませんが、APIのクォータ上限の都合上なるべく少なく抑えると正常に動作しやすいです。
参考までに、私は3つのチャンネルのみ取得させるようにしています。

4-2. チャンネルIDの貼り付け

vivaldi_BAOIBIbbnC_01.png
コードの129行目に、コンマ区切りでチャンネルIDをそれぞれ入力していきます

5. 動作の確認

CNXlpPAvbr_01.png

GAS画面上部のボタンをクリックし、「setTrigger」関数および「main」関数をそれぞれ「実行」していきます。
クリックする順番は上記の画像を参考にしてください。

ここでエラーが起きなければ問題ありません。

何か異常が発生した場合、ここまでの手順に間違いがないか、スキップしてしまった箇所はないか確認してください。

万が一それでも解決しない場合、以下のTwitter(X)アカウントにお気軽に連絡ください。
可能な限り対応させていただきます。

6. トリガーの設定

いよいよ最終局面です。

6-1. 「トリガー」画面への移動

vivaldi_wysJvP4yc9_01.png
GASの画面左側、目覚まし時計のアイコンをクリックし 「トリガー」画面 に移行します。
その後、画面右下の青い「トリガーを追加」ボタンをクリックします。

6-2. トリガー(定期実行)の設定

vivaldi_o1B0Qimyl9_01.png
トリガーの設定画面に移行したら、

  • 「実行する関数」「setTrigger」
  • 「時間ベースのトリガーのタイプ」「分ベース」
  • 「時間の間隔」「15分おき」

順番に設定していきます。
その後、「保存」を押して終了です。

7. その他

7-1. 経過観察

これで設定は完了です。あとは放置してスクリプトが正しく動いていくか確認することになります。
具体的には、GASの画面右側「実行数」タブでステータスが「失敗しました」などとなっていなければ問題ありません。
問題が発生するならば大抵はAPIのクォータ上限に引っかかっているがゆえのことだと思いますので、通知するチャンネルの数を減らすなどして調整してください。

7-2. 予定は追加されるが通知が届かない場合

以下のページを参考にカレンダーの通知設定を見直してください。

7-3. 他のカレンダーを通じての通知/予定の受け取り

AppleカレンダーWindows(Windows 10, Windows 11)標準カレンダーGoogleカレンダーを同期させることで、それぞれのアプリやソフトで予定や通知を確認することができます
以下の記事などを参考に操作を行ってください。

8. カスタマイズなどについて

以下はやや込み入った話になります。

8-1. 時間帯別・配信予定チェック頻度の変更について

setTrigger関数の「実行スケジュールを設定」以下、つまるところ13行目以下の内容を調整することで時間帯別の配信予定チェック頻度を変更することができます

分単位では1分、5分,10分,15分、30分ごと
時間単位では1時間、2時間、4時間、6時間、8時間、12時間ごとというユニットでしか設定できません。

8-2. YouTube Data APIのクォータ上限について

正直目の上のたんこぶです。
追っている配信者が多い方ほど困るだろうと思います。
一応Google Cloud Platform経由でAPIキーを取得させているので、以下の記事などを参考にして上限のアップを申し込んでも良いのかもしれません。

8-3. そもそもの話

そもそも筆者自身が非エンジニアで殆ど勘と他の言語の経験値でコーディングした代物です。
動作はしますが無茶苦茶な部分や意味不明な部分が多かろうとは思いますので、気になった点があれば下記のTwitter(X)アカウントなどへ連絡いただけますと幸いです。

1
0
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
1
0