見たいYouTubeの動画を見逃さないために

初めまして、17のバンル( @17_bnr )です。

この記事はrogy Advent Calendar 2020の12日目になってます。

ところでみなさん,声優の夏川椎菜さんを知っていますか?
ハイスクール・フリートの岬明乃

©AIS/海上安全整備局

アイドルマスターミリオンライブの望月杏奈

©窪岡俊之 ©BANDAI NAMCO Entertainment Inc.

などそれ以外にも多くの作品に出演しています。
本業の声優以外にもtrysailというユニットを組んでいたり,
個人アーティストとしても活動しています。
(ところで来年1月6日に新曲,クラクトリトルプライドが出ますね!
サブスクなどでは先行配信されているのでぜひ聴いてみてください)

それ以外にも自信のブログを書いたり,自身のYouTubeチャンネルに毎週動画を配信したりと多方面で活躍しています。

その中で特に自分が好きなのはファミ通TUBEで配信されているゲーム実況です!

これだけは見逃したくないのですが,少し問題がありまして,
ファミ通TUBEは,下の画像にあるように幅広くゲーム関連の動画を配信しています。

そのため,YouTubeの通知機能だとファミ通チャンネルの動画がアップロードされるごとに通知が来てしまいます。
他の動画も面白いのですが,個人的には
ファミ通TUBE夏川椎菜さんの動画だけの通知が欲しい!
でもない……
”ないなら作ればいい”

ってことで作成開始です。

※夏川椎菜さんはファミ通.comでもゲーム実況と同じタイトルのGAMEISCOOLというブログを書いていますので,よければそちらも読んでみてください。

準備と設計

使用したハード:
ラズパイ3

使用言語:
Python3

作ったプログラムの流れ:
YouTubeで新作動画を検索する
→新作動画があればタイトルごとgmailで自分に送信する

これを毎日ラズパイにやってもらいます。

必要なものは先にインストールしておきます。

pip3 install google-api-python-client
sudo pip3 install --upgrade oauth2client

コードの最初にこれらを書いておきます。

#!/usr/bin/python
#-*- coding:utf-8 -*-
from apiclient.discovery import build
from apiclient.errors import HttpError
from oauth2client.tools import argparser
from datetime import datetime,timedelta,timezone
import pytz

import httplib2
import os
import apiclient
import oauth2client.tools
import oauth2client.file
import argparse

flags = argparse.ArgumentParser(
    parents=[oauth2client.tools.argparser]
).parse_args()

import base64
from email.mime.text import MIMEText
from email.utils import formatdate
import traceback

YouTubeのAPI取得と検索の設定

API取得

YouTubeのAPIはGoogle Cloud Platformから簡単に手に入ります。
下のサイトを参考に取得しました。
https://qiita.com/iroiro_bot/items/1016a6a439dfb8d21eca

検索の設定

公式リファレンスに沿って,検索条件を確認していきます。

今回はファミ通TUBEに新しい夏川椎菜さんの動画があるかを確認したいので,

part="id,snippet"
channnelId="UCtPyU9cJgvvgH03PQKgaucA"
q="夏川椎菜"
publishedAfter=(昨日の日付。ここは後で)
order="date" #時系列順に並べ替える

ちなみにchannelIdはチャンネルのURLの最後の文字列です。
(ファミ通TUBEのURLはhttps://www.youtube.com/channel/UCtPyU9cJgvvgH03PQKgaucAなので,channelIdはUCtPyU9cJgvvgH03PQKgaucAになります)
取得したデータは

videos.append("%s " % search_result["snippet"]["title"])

でvideosというリストにタイトルを格納します。

よって,時間を引数にしてvideosを返す関数を作成します。

# Set DEVELOPER_KEY to the API key value from the APIs & auth > Registered apps
# tab of
#   https://cloud.google.com/console
# Please ensure that you have enabled the YouTube Data API for your project.
DEVELOPER_KEY = "APIで得た鍵"
YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3"
def youtube_search(time):
  youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION,
    developerKey=DEVELOPER_KEY)

  # Call the search.list method to retrieve results matching the specified
  # query term.
  search_response = youtube.search().list(
    q="夏川椎菜",
    part="id,snippet",
    maxResults=25,
    channelId="UCtPyU9cJgvvgH03PQKgaucA",
    order="date",
    publishedAfter=time
  ).execute()

  videos = []

  # Add each result to the appropriate list, and then display the lists of
  # matching videos.
  for search_result in search_response.get("items", []):
    if search_result["id"]["kind"] == "youtube#video":
      videos.append("%s " % search_result["snippet"]["title"])
                             
  return videos

日付の取得

YouTubeのAPIはRFC3339形式(1970-01-01T00:00:00Z)のstringである必要があるので,pythonのdatetimeから取得した時刻を変換する必要があります。
また,YouTubeの動画データに入っている時間のタイムゾーンはutcであるため,こっちもutcで作ります。

#時刻の取得
today = datetime.utcnow()
#日付のみを使用する
today_date = today.date()
#日付を昨日に変更する
one_day_ago = today_date - timedelta(days=1)
#stringの型にして,RFC3339形式にするために,"T12:00:00Z"にする
str(week_ago)+"T12:00:00Z"

この方法だと,時間までは取得できませんが,今回は困らないのでこのまま続けます。
(pythonで時間までを含めたデータをRFC3339形式にする方法があったら教えてほしい)

Gmailで送信

GmailのAPI関連は下のサイトを参考にしました。
https://qiita.com/muuuuuwa/items/822c6cffedb9b3c27e21
実際のpythonコードは下のサイトを参考にしました。
https://thinkami.hatenablog.com/entry/2016/06/10/065731

OAuth2の認証

OAuth2の認証を行うと,.jsonファイルがもらえると思うので,これをsecret_id.jsonというファイル名に変更しておきます。
認証部分の関数を作ります。
この関数で.credentailsというフォルダを作り,認証関係を入れておくことで,1度認証すれば連続で送信ができます。

# If modifying these scopes, delete your previously saved credentials
# at ~/.credentials/gmail-python-quickstart.json

SCOPES = "https://www.googleapis.com/auth/gmail.send"
CLIENT_SECRET_FILE = "secret_id.json"
APPLICATION_NAME = "GAMEISCOOLbot"
def get_credentials():
    script_dir =os.path.abspath(os.path.dirname(__file__)) 
    credential_dir = os.path.join(script_dir, ".credentials")

    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir,
                                   "my-gmail-sender.json")

    store = oauth2client.file.Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = oauth2client.client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        credentials = oauth2client.tools.run_flow(flow, store, flags)
        print("Storing credentials to " + credential_path)
    return credentials

メールの作成

自分に送るためのメールを作成します。
夏川椎菜さんがアップロードした動画のタイトルを本文に入れたいため,動画タイトルを引数にします。

def create_message(movie_title):
    #メール本文の作成
    body = "夏川椎菜さんが\n「" + movie_title + "」\nをアップロードしたよ"
    message = MIMEText(body)
    message["from"] = "メールを送るメールアドレス"
    message["to"] = "メールを受け取るメールアドレス"
    #メールのタイトル
    message["subject"] = "夏川椎菜のGAMEISCOOL!"
    message["Date"] = formatdate(localtime=True)

    byte_msg = message.as_string().encode(encoding="UTF-8")
    byte_msg_b64encoded = base64.urlsafe_b64encode(byte_msg)
    str_msg_b64encoded = byte_msg_b64encoded.decode(encoding="UTF-8")

    return {"raw": str_msg_b64encoded}

メールの送信

実際に作ったメールを送信します。

def send(movie_title):
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = apiclient.discovery.build("gmail", "v1", http=http)

    try:
        result = service.users().messages().send(
            userId="メールを送るメールアドレス",
            body=create_message(movie_title)
        ).execute()

        print("Message Id: {}".format(result["id"]))

    except apiclient.errors.HttpError:
        print("------start trace------")
        traceback.print_exc()
        print("------end trace------")

動画タイトルがメール本文に入ったメールを作成し,送信します。

統合

main関数

現在の時間から昨日に日付を作成し,昨日から現在までにアップロードされている動画を探します。
もし動画があれば,その動画タイトルが入ったメールを送信します。

if __name__ == "__main__":
  #昨日の日付を計算
  today = datetime.utcnow()
  today_date = today.date()
  week_ago = today_date - timedelta(days=2)

  try:
      #videosのリストに検索結果を格納
      videos = youtube_search(str(week_ago)+"T12:00:00Z")
  except HttpError as e:
    print("An HTTP error %d occurred:\n%s" % (e.resp.status, e.content))
  #videosのリストが1つ以上なら,メールを送信
  if len(videos)>0:
    send(videos[0])

メールを受信

実際にメールが来ました。動いてよかったです。

また,実際にメールで来たタイトルと同じ動画がアップロードされていました。

さいごに

プログラムを少し書けると,この記事みたいに便利なものが作れて,生活が豊かになりますね

明日はLoopさんの記事です。

ばいなーんす!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です