見出し画像

YouTubeの推しの配信予約をDiscordに通知するアプリ


1.経緯

YouTubeで活動している推しが配信枠を作った時、通知してくれるものが欲しかった。仕事でPythonを使うことが偶にあるため勉強ついでにも丁度よかった。

2024/6/30 執筆時点での内容です。


2.環境

言語: Python 3.10
API: YouTube Data API v3、Discord.py
運用: dockerコンテナ化してGCEの無料枠に乗せてちんまりと


3.事前準備

必要なものは以下の通り。

  • YouTube Data API key
    YouTube Data APIはリクエストの上限があるので注意。(申請無しで10000/日だが、searchリクエスト1回で100消費する)
    参考:

  • Discord Bot Token(Discord API Key)
    参考:

  • 推しのYouTubeチャンネルID
    チャンネルトップの概要を開き、チャンネルを共有をクリックするとチャンネルIDをコピーとあるので、このIDを使用する。
    参考までに私の推しのAqua Ch. 湊あくあの例

  • 通知するDiscordのチャンネルID
    Discord Bot Tokenを作成する際にどこかのサーバーに参加させているはず。そのサーバー内のテキストチャンネルのIDをコピーする。


4.アプリ

大まかなフローおよびコードは以下のような感じで作成。本職はコード書く人では無いので実際のコードを見せるのが恥ずかしいので…

  • Discord APIを使い、送信するテキストチャンネル情報を取得

channel = self.get_channel(discord_channel_id)
  • YouTube Data APIを使い、チャンネル情報からvideoIdリストを取得する。
    参考: 

search_url = f"https://www.googleapis.com/youtube/v3/search?key={youtube_api_key}&channelId={youtube_channel_id}&part=id&order=date"
  • 前回取得結果とvideoIdリストが違ったら差分があるvideoIdの詳細情報を取得する。
    参考:

video_id_detail_url = f"https://www.googleapis.com/youtube/v3/videos?key={youtube_api_key}&id={video_id}&part=liveStreamingDetails"
  • videoIdの詳細情報が空(Itemsが[])なら後述の配信予約リストからvideoIdを削除。また、Discordのチャンネル履歴からvideoIdを検索し、メッセージを取得。メッセージに配信枠が削除されたことを返信する。

if video_id_detail_request_result['items'] == []:
    del sent_to_discord_video_id_list[video_id]
    send_to_discord_message = "配信枠が削除されました。"
    messages = [message async for message in channel.history(limit=50)]

    for message in messages:
        if video_id in message.content:
            await message.reply(send_to_discord_message)
  • liveStreamingDetails内のactualStartTime(実配信開始時間)が入っていなければ配信開始前なのでscheduledStartTime(配信予約時間)を取得する。

if 'actualStartTime' not in video_id_details['liveStreamingDetails']:
    scheduled_start_time = datetime.datetime.strptime(video_id_details['liveStreamingDetails']['scheduledStartTime'], '%Y-%m-%dT%H:%M:%SZ')
  • videoIdをURL(https://www.youtube.com/watch?v={videoId})に加工、scheduledStartTimeをJSTにしてDiscordのテキストチャンネルに送信

new_video_url = f"https://www.youtube.com/watch?v={video_id}"
scheduled_start_time_jst = scheduled_start_time + datetime.timedelta(hours=9)
send_to_discord_message = "配信枠が作成されました。\n" + scheduled_start_time_jst.strftime('%Y年%m月%d日 %H:%M 開始') + "\n" + new_video_url
await channel.send(send_to_discord_message)
  • videoIdとscheduledStartTimeは配信予約リストとしてデータ保持しておく。

sent_to_discord_video_id_list[video_id] = scheduled_start_time.strftime('%Y%m%d %H:%M:%S')
  • 配信が開始されていたら配信予約リストから削除する。

if 'actualStartTime' in video_id_details['liveStreamingDetails']:
    del sent_to_discord_video_id_list[video_id]
  • 配信予約リスト内にあるvideoIdのscheduledStartTimeも確認し、配信予約時間が変更されていたらDiscordのチャンネル履歴からvideoIdを検索し、メッセージを取得。メッセージに変更後の配信予約時間を返信する。

scheduled_start_time_jst = scheduled_start_time + datetime.timedelta(hours=9)

if sent_to_discord_video_id_list.get(f'{video_id}', "") != scheduled_start_time.strftime('%Y%m%d %H:%M:%S'):
    send_to_discord_message = "配信開始時間が変更されました。\n" + scheduled_start_time_jst.strftime('%Y年%m月%d日 %H:%M 開始')
    messages = [message async for message in channel.history(limit=50)]
    for message in messages:
        if video_id in message.content:
            await message.reply(send_to_discord_message)
    sent_to_discord_video_id_list[video_id] = scheduled_start_time.strftime('%Y%m%d %H:%M:%S')

出来たらアプリの動作確認。起動したらDiscord Botがオンラインになる。
コーディングについてはこちらを参考。

(他にもサンプルが色々ある)


5.dockerで動かす

運用基盤が整っている人はここで終わり。

このままでも完成ではあるが、dockerの知識が多少なりとあるならコンテナ化したいってもの。
docker hubにあるpython3.XXを使ってもいいかもだが、日本語化したかったのでubuntuから作っていく。

FROM ubuntu:22.04

USER root

RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

RUN apt update -y && apt install -y python3-pip libffi-dev libnacl-dev python3-dev locales tzdata && localedef -f UTF-8 -i ja_JP ja_JP.UTF-8 && apt clean && rm -rf /var/lib/apt/lists/*

ENV LANG=ja_JP.UTF-8
ENV LANGUAGE=ja_JP:ja
ENV LC_ALL=ja_JP.UTF-8
ENV TZ=Asia/Tokyo
ENV TERM=xterm
ENV YOUTUBE_API_KEY=MY_YOUTUBE_API_KEY
ENV YOUTUBE_CHANNEL_ID=MY_YOUTUBE_CHANNEL_ID
ENV DISCORD_API_KEY=MY_DISCORD_API_KEY
ENV DISCORD_CHANNEL_ID=MY_DISCORD_CHANNEL_ID
ENV FETCH_INTERVAL_SEC=900

RUN groupadd -g 1000 user1 && useradd -u 1000 user1 -g 1000 -m -s /bin/bash

RUN mkdir -m 755 /data/ /var/log/MyApp/ && chown user1:user1 /data/ /var/log/MyApp/

COPY ./MyApp.py /data/
COPY ./log_config.json /data/

RUN chown user1:user1 /data/*

USER user1

RUN python3 -m pip install -U discord.py requests

ENTRYPOINT ["python3", "-u", "/data/MyApp.py"]

loggerで使うlog_config.jsonはこちらを参考。

docker-config.ymlの設定はこんな感じ。ログをホストに持ってきたかったりの場合は適宜volumesでマウント。

services:
  MyApp:
    image: MyApp:1.0.0
    container_name: 'my_oshi_stream_starttime_notify'
    restart: unless-stopped
    environment:
      YOUTUBE_API_KEY: "MY_YOUTUBE_API_KEY"
      YOUTUBE_CHANNEL_ID: "OSHI_YOUTUBE_CHANNEL_ID"
      DISCORD_API_KEY: "MY_DISCORD_API_KEY"
      DISCORD_CHANNEL_ID: 1234567890123456789
      FETCH_INTERVAL_SEC: 900 # 15 min
      TZ: "Asia/Tokyo"

Dockerfileをビルド。

docker image build -t MyApp:1.0.0 --progress=plain .

dockerコンテナを起動。

docker compose up -d

アプリの動作確認時と同様に動作確認する。


6.Google Compute Engine上で動かす

せっかくここまで作ったし、Discordサーバーもあるのだから他の人も参加させてあげたいと思うこともある。ただし、自宅サーバーじゃ安定性がなぁということで今のところ期限無しの無料枠があるGoogle Compute Engineに乗せちゃえば基盤周りの悩みが無くなるという考え。
無料枠のセットアップはこちらを参考。


以上。

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