見出し画像

【簡単】推しの配信を”絶対に”見逃したくない人のための記事【非エンジニア・初心者向け】

☆ この記事を読んで出来るようになること:

  • 記事中のコードをコピペするだけで、YouTube Liveの枠立ての通知をGoogleカレンダーを通じて自動で受け取れるようになる

  • 推しの配信予定をカレンダー上でいつでも確認出来るようになる

  • ひと手間加えればAppleカレンダーなど他のカレンダーアプリ経由でも通知を受け取れる

⚠ プログラミングの知識は不要です。
VTuber実況者、その他YouTubeで活動されている配信者を追っている方にオススメです

目標:
- 推しのYouTube Liveでの配信予定を見逃したくない

- 枠立ての通知を事前に自動で受け取りたい
- 推しの配信予定をカレンダー上でいつでも確認したい

なお、Qiitaでカスタマイズのやり方などについて触れた同名の記事も投稿しております
Javascriptやプログラミング全般について知識のある方などはそちらの方が参考になるかもしれませんので、お好みの版をご覧いただければ幸いです。



1. まず最初にすること

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

Googleドライブを開いたら適当なフォルダ上で「+ 新規」ボタンをクリック

その他」→「Google Apps Script」からGAS(Google Apps Script)ファイルを作成します。

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

作成したファイルを開くと「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. サービスを追加する

左側の「サービス」の項目から追加していきます。

一覧から「Google Calendar API」「YouTube Data API」を探し出して、それぞれ「追加」をクリックします。

2. APIキーの取得など

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

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

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

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

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

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

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

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

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

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

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

以下の記事を参考に、通知させたいYouTubeチャンネルチャンネルIDをそれぞれ取得してください。
数はいくつでも構いませんが、APIの上限の都合上なるべく少なく抑えるとエラー=取得漏れが起こりにくいです。
参考までに、私は3つのチャンネルのみ取得させるようにしています。

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

コードの129行目に、コンマ区切りでチャンネルIDをそれぞれ入力していきます。
コンマとチャンネルID以外にスペースなどは入力しないほうが良いかなと。

5. 動作の確認

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

ここでエラーが起きなければ問題ありません
何か異常が発生した場合、ここまでの手順に間違いがないかスキップしてしまった箇所はないか確認してください。

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

Shawn Norman(@RealSh4wn) / Twitter
https://twitter.com/RealSh4wn

6. トリガーの設定

いよいよ最終局面です。

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

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

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

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

  1. 実行する関数」を「setTrigger」

  2. 時間ベースのトリガーのタイプ」を「分ベース」

  3. 時間の間隔」を「15分おき」

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

7. その他

7-1. 経過観察

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

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

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


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

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


7-4. 予定取得頻度・時間帯の変更

Qiitaに載せた私の同名の記事でより詳細に記述しています。
以下のリンクから参照してください。


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