🎥

Youtube DATA APIを使って、動画のチャプターを取得してみた話

2023/07/28に公開

はじめに

こんにちは!
ポートでエンジニアをしている、@shoya.iikawaです。

先日、Youtube DATA APIを使って実装する案件があり、いくつか工夫が必要だったので、そのお話を書いていきたいと思います!
特に、動画チャプターを取得・整形する箇所が面倒な要件でした🤮

要件

WPで作成した記事内にYoutubeの動画を埋め込んでる場合、当該記事ページの<head />タグ内に自動でSchemaマークアップとして動画情報を出力してほしい。
必要な情報は以下画像の赤枠

【必要情報】

  1. 動画タイトル
  2. 動画時間
  3. 動画公開日
  4. 各チャプターの「開始時間・終了時間・チャプタータイトル」

要件詳細

  1. APIを使うためのAPI KEYを用意
  2. 記事内に埋め込まれたYoutubeの埋め込みコードから、YoutubeのIDを取得
    • 埋め込みコードは0個以上で上限は無い
  3. API KEYとYoutube IDを利用して、APIからデータを取得
    • 「動画タイトル/時間/公開日」はAPI叩いたら一発で取得可能
    • チャプター情報については、明確にAPIから提供はされず、コメント欄のテキストから判別しなくてはいけない
    • チャプター情報の「開始時間/終了時間」の判定の為に、ISO8601形式とH:i:s形式の時間を秒数に変換する必要がある
  4. 取得したデータをSchemaマークアップの形式にして出力する

1. APIを使うためのAPI KEYを用意

Youtube Data APIのAPI KEYはGoogle Cloud Console上で発行可能です。
Google CloudでのAPI KEYの発行手順は世の中に星の数ほど出回っているので詳細は割愛して、参考リンクだけ貼らせていただきます!

https://developers.google.com/youtube/v3/getting-started?hl=ja
https://qiita.com/shinkai_/items/10a400c25de270cb02e4

2. 記事内に埋め込まれたYoutubeの埋め込みコードから、YoutubeのIDを取得

さて、まずはAPIを使って情報を取得したい動画のIDを、記事内のソースコードから特定していく事になります。
ここでいうソースコードはHTMLとして出力される前段階の、WordPressの記事エディター内のソースコードです。
これがちょっと癖もので、以下のように同じ「Youtubeの埋め込み」であっても、記事の執筆者の操作方法によって、ソース上ではいくつかパターン想定されました。
https://github.com/port-iikawa/youtube-data-api/blob/add-get_youtube_id/sample-source-code.txt

大きく分類すると、以下の3つを探すとIDが取得出来そうです。

そこで、今回のアプローチとしては正規表現のグループマッチングを利用して、一気に特定を進めようと思います。
https://github.com/port-iikawa/youtube-data-api/blob/add-get_youtube_id/youtube-data-api.php#L14-L35

こうすると、マッチした場合の配列はこんな感じになります。

$matches = [
    0 => [
        //マッチしたURL全て
    ],
    1 => [
        // /embed/動画IDでマッチしたID
    ],
    2 => [
        // /watch?v=動画IDでマッチしたID
    ],
    3 => [
        // youtube.be/動画IDでマッチしたID
    ]
];

var_dumpするとこんな感じです。

上手い事動画IDだけの配列が用意出来てますね。
とは言え、$matches[1]~$matches[3]まで、歯抜けがあったり重複があったりと、このままでは使えないので、ちょっと整形しましょう。
※正規表現を工夫すればこの辺の整形がいらなくなりそうなものですが、パターンを考えるのがしんどかったので、取得->整形の2段構えにしました💦

https://github.com/port-iikawa/youtube-data-api/blob/add-get_youtube_id/youtube-data-api.php#L37-L54

そういえば、44行目で利用しているアンパック(スプレッド構文)ですが、PHP7.4から使えるようになって便利ですよね。
しかしこいつが、「配列のキーが数字のもの」しか使えないのがちょっと不満だったのですが、PHP8.1からキーが文字列の場合もサポートされてました。
さよならarray_merge👋

3. API KEYとYoutube IDを利用して、APIからデータを取得

さて、ここからが本題です。
API KEYと動画IDを使って、特定の動画の情報を取得していきましょう。

まずは、コードの全容から。
https://github.com/port-iikawa/youtube-data-api/blob/add-generate_schema/youtube-data-api.php

こちらのコードの結果、14行目で出力されるSchemaデータをリッチリザルトで検証するとこうなります。
4つの動画の情報が全て綺麗に取得出来てますね。

という事で、細かい説明は以下です!

まずはSchemaマークアップの土台

マークアップに含める動画の数が1個or2個以上で形式が変わるので、そこの調整を担ってます。
実際の動画情報の取得は、続くgenerate_video_schema()にお任せ。
https://github.com/port-iikawa/youtube-data-api/blob/add-generate_schema/youtube-data-api.php#L59-L73

APIを叩く

CURLを使って、Youtube DATA APIのURLを叩きます。
https://github.com/port-iikawa/youtube-data-api/blob/add-generate_schema/youtube-data-api.php#L75-L95

この時、必要なデータのみを取得する事でリソースの節約が可能で、それを行なっているのが85行目と86行目です。
ざっくり言うと

  • 大分類(part)としてsnippetとcontentDetailsを取得
  • 更にその内、細かく分類(fields)して、contentDetailsに含まれるdurationと、snippetに含まれるpublishedAt,title,description,thumbnailsを取得
    となります。

結果、取得できたデータはこうなります。

APIから取得したデータをパース

さらにさらに、取得出来たデータをSchemaに適合した形にパースしていきます。
https://github.com/port-iikawa/youtube-data-api/blob/add-generate_schema/youtube-data-api.php#L97-L115

コードを見ていただけると分かるかも知れませんが、大凡のデータはAPIを叩いて取得したデータをそのまま利用できますが、contentUrlとhasPartがデータに手を加える形となっています。
特に、hasPartに設定するチャプターが癖ものでした。

チャプターを分析する

チャプター情報については、明確にAPIから提供はされず、コメント欄のテキストから判別しなくてはいけない仕様となっていました。
もっと具体的に言うとコメント欄に「H:i:s」で始まる行がある場合、「H:i:s チャプタータイトル」と自動で設定される仕様です。
そして、このコメント欄から抽出したいのは「開始時間/終了時間/チャプタータイトル」の3つです。

【コメント欄に含まれるチャプター情報】

これの何が厄介かと言うと、以下のように「時間の形式が複数存在する上に、すべてが「時分秒」の形式になっているが、それらを「秒」に修正しなければいけない」という箇所でした。
【ややこしかった処理】

  • チャプターの開始時間の形式が「00:00:00 / 0:00:00 / 00:00 / 0:00」と複数の形式がある
  • チャプターの開始時間しか記載されないので、終了時間の判定が「次のチャプターの開始時間」となる
  • 最後のチャプターの場合、「動画の終了時間がチャプターの終了時間」になるが、動画の終了時間はISO8601(例えばPT14M11Sで14分11秒)と、更に違う形式になる
  • Schemaでは、各種時間を「秒」の単位で扱わなくてはいけないので、最終的にはH:i:sもISO8601も秒に変換が必要

これらの解決を以下のように行なっています。

チャプターの一覧を生成

https://github.com/port-iikawa/youtube-data-api/blob/add-generate_schema/youtube-data-api.php#L137-L152

まず手始めに145行目でコメント欄に含まれるチャプターを取得していて、この正規表現でチャプター開始時間の形式の違いを吸収しています。
すると、結果の$matchesの結果はこうなります。
ここから、$chapter_start_times$chapter_heading_textsに、チャプター開始時間の一覧とタイトルの一覧を格納します。

$matches = [
    0 => [
        //H:i:s チャプタータイトルでマッチしたもの一覧
    ],
    1 => [
        // 各チャプターの開始時間
    ],
    2 => [
        // empty
    ],
    3 => [
        // 各チャプターのタイトル
    ]
];

チャプター情報を1つずつSchema形式にパース

https://github.com/port-iikawa/youtube-data-api/blob/add-generate_schema/youtube-data-api.php#L154-L184

パースする中での細い処理は、上記コードのコメントの通りです。
ISO8061形式を取り扱う時も、一度H:i:sに変換する事で「H:i:sから秒にしたいんだ」という気持ちが込められています。

H:i:sから秒への変換

https://github.com/port-iikawa/youtube-data-api/blob/add-generate_schema/youtube-data-api.php#L191-L205

上記パース処理で出てきた、H:i:sから秒に変換する関数です。
「:」でexplode()してあげて、時・分・秒をそれぞれ取り出し、最終的に秒数にしてリターンしています。

ISO8601からH:i:sへの変換

https://github.com/port-iikawa/youtube-data-api/blob/add-generate_schema/youtube-data-api.php#L207-L218

上記パース処理で出てきた、ISO8601からH:i:sに変換する関数です。
ISO8601形式では、「PT◯◯H△△M□□S」の表記となり、◯が時間で△が分、□が秒数を表しますが、時間と分は省略されるケースもあります。

それらを踏まえて212行目でマッチさせた情報から時・分・秒を変数に取り出しています。

取り出した値を、最後に「00:00:00」のように「0埋めされた2桁の文字列をコロンで区切る」形、つまりH:i:sのフォーマットになるよう、sprintf()で整えてリターンしています。

4. 取得したデータをSchemaマークアップの形式にして出力する

さて、色々な処理を経て、無事「記事内Youtube動画の情報を基にSchemaマークアップに適合した情報」を取得する事が出来ました。
あとは、13行目の$schemaに格納する際にJSONに変換してあげて、HTMLの<head />内に出力させれば完成となります。

尚、実際の実装をした案件では、WordPressの機能を利用して、ちょうど<head />タグの中でクラスのコンストラクタが呼ばれるようにしていたので、出力もシンプルにechoでお終いとなります。

https://github.com/port-iikawa/youtube-data-api/blob/add-generate_schema/youtube-data-api.php#L1-L15

最後に

ここまで長かかったですがお読みいただきありがとうございました!😊
依頼の発端としては、動画のマークアップがあるとSEO上良い、という事だったので、この実装で少しでも訪問者が増えてると嬉しいなーなどと思っておりますがはてさてどうでしょう。

そして、余談ですが、これを作ってる最中にふとこう思いました「チャプター集めて、そこから面白動画を発掘するようなサービスとか作れないかなぁ、、、」と。
しかし、残念な事に僕には創造力がカケラも備わっていないので、5秒で断念です😭
誰かチャプター使ってヒットしそうな案があったらこっそり教えてください!🥺

Discussion