どうやってYoutube配信中の枠情報、メン限・限定公開枠を自動取得しているの?

雑記

こんばんわに。

 

うちは「ホロライブYoutube登録・再生数通知BOT(@holo_analytics」)というTwitterのBOTを運用しています。※おまけでDiscord版も開発

 

ホロライブYoutube登録・再生数通知BOTの概要

  1. チャンネル登録者数はキリ番になったら通知する
  2. 動画再生数がキリ番になったら通知する
  3. 配信中の枠の動画再生数もキリ番になったら通知する
  4. チャンネルホーム画面に表示されないメン限・限定公開の枠も検出する

チャンネル登録者数や動画再生数を通知するだけならさほど難しいことではなくQiitaとかを見ればわかるのですが、③④に関して情報が皆無です。

ですが、やり方は存在するため配信中の再生数もBOTで通知できています。

Youtube Data APIでは配信中の枠を取得できないのに…なぜ?

という方向けに解説します。

Youtube Data APIで動画一覧を取得する

Youtube Data APIでは各チャンネルの動画をまとめて取得することができます。

動画一覧の取得の流れ

  1. チャンネルIDを使ってチャンネル情報を取得(Channels:list)
  2. contentDetailsプロパティ内にあるアップロード済み動画一覧のキー(uploads)を取得
  3. (PlaylistItems: list)を使って動画一覧を取得(最大50件)
  4. 50件以上取得したい場合は”nextPageToken”をパラメーターに加えて再度APIを呼び出すことでページ送りが出来るので、”nextPageToken”を取得できなくなるまでループすると全動画取得できます
  5. 後は(Video:list)を使って各動画の詳細を調べていくだけ

コード化するとこうなります。

<?php
$apikey = '<apikey>';
$channel_id = '<channel_id>';
$call_url = 'https://www.googleapis.com/youtube/v3/channels?part=contentDetails&id='.$channel_id.'&key='.$apikey;
$json = file_get_contents($call_url);
$json = mb_convert_encoding($json, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN');
$json = json_decode($json);
$uploadid = $json->items[0]->contentDetails->relatedPlaylists->uploads;
//ページ送り用トークン
$pageToken='';
do{
    //動画一覧を取得する
    $call_url = 'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId='.$uploadid.'&maxResults=50&key='.$apikey.'&'.$pageToken;
    
    $json_pl = file_get_contents($call_url);
    $json_pl = mb_convert_encoding($json_pl, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN');
    $json_pl = json_decode($json_pl);
    
    //動画一つ一つを処理
    $ids = '';
    foreach($json_pl->items as $video){
        //50個ずつまとめてAPIコール
        $ids .= $video->id.',';    
    }
    $ids = substr($ids, 0, -1);
    $call_url = 'https://www.googleapis.com/youtube/v3/videos?id='.$ids.'&key='.$apikey.'&part=snippet,statistics';
    $json_video = file_get_contents($call_url);
    $json_video = mb_convert_encoding($json_video, 'UTF8', 'ASCII,JIS,UTF-8,EUC-JP,SJIS-WIN');
    $json_video = json_decode($json_video);

    foreach($json_video->items as $item){
//    echo $item->id . PHP_EOL;
//    echo $item->snippet->title . PHP_EOL;
//    echo $item->statistics->viewCount . PHP_EOL;
    }
    
    
    //ページ送りチェック
    if(property_exists($json_pl,'nextPageToken')){
        $pageToken = 'pageToken='.$json_pl->nextPageToken;
    }else{
        $pageToken = '';
    }
}while($pageToken != '');
?>

これでアップロード済み動画の取得を自動化できます

ただし、この方法だとアップロードされた動画の情報しか取得できないため、配信中の枠の再生数などをプログラムで監視することができません。

配信中の枠を取得するには?

配信中の枠の再生数などを調べようと思ったら必ず動画IDが必要です。

このIDさえわかれば再生数や評価、配信開始時間など取得できるのですが、Youtube Data APIで取得しようと思うと消費クォータが馬鹿みたいに大きい(Search:list)を使わなければなりません。

正直言って非現実的です。

 

そこでRSSを使います。

Youtubeはチャンネルの新着動画情報をRSSで配信しています。

https://www.youtube.com/feeds/videos.xml?channel_id=<channel_id>

実はこのRSSには配信前の待機枠や配信中の枠も含まれています。そのため、こちらのRSSを各言語のパーサーなどで解析してあげることで配信前・配信中の枠を取得できます。

//RSS取得
$feed=file_get_contents('https://www.youtube.com/feeds/videos.xml?channel_id='.$channel_id);
$invalid_characters = '/[^\x9\xa\x20-\xD7FF\xE000-\xFFFD]/';
$feed = preg_replace($invalid_characters, '', $feed);
// 文字列をXMLとして解析して、SimpleXMLElementクラスのインスタンスに変換
$rss = simplexml_load_string($feed);

//ループ
foreach($rss->entry as $item){
    preg_match('/yt:video:([\s\S].*)/',$item->id[0],$match);
    $videoid=$match[1];
}

こちらの方法はYoutube Data APIを使いませんので、Youtube Data APIのクォータも消費しません。

さらにRSSはプログラムで機械的に取得するのが一般的であるため、毎秒取得するレベルでもない限り、APIを使わないアクセスでもブロックされることもありません(うちのBOTでは6分周期でホロライブプロダクション全チャンネルのRSSをチェックしています)。

取得できるのは新着15件のみですので、全動画チェックする場合はYoutube Data APIでアップロード済み動画を全取得してキャッシュしておく必要がありますが、それさえできればRSSで十分です。

RSS更新間隔も10~30分程度ですので、30分で15本以上動画を上げられない限りは全取得可能です。

タイムラグが結構厳しい

Youtube RSSの更新頻度は10~30分周期と結構感覚が長いです。動画再生数を通知するだけであれば全然問題ないのですが、Discord版BOTではゲリラ配信通知などタイムラグが致命傷になる機能を実装しています。

1分程度ならまだしも、10分・30分のラグは致命的です。使い物になりません。枠バグで非表示になったらラグどころか取得・通知すらできません

また、この問題を解決しようにもYoutube Data APIは使えません。ゴリ押しでSearch:listを使うとしても、こちらも謎のラグがあったりAIのフィルタがかかると検索に引っかかりにくくなります。

そこでTwitter APIを使用します。

ホロライブ所属タレントは配信が始まると動画URLとともにツイートしてくれます。

このツイートを解析することでRSSでは取得が間に合わない枠も即取得できます。

https://youtu.be/{video_id}
https://www.youtube.com/watch?v={video_id}

 

URLに動画IDがあるので、正規表現で抽出すればオーケーです。

限定公開・メン限も取得可能

メンバー限定は仕様が変わったおかげで配信ページURLがバレてもメンバーじゃないと視聴できないように変更されました。

その仕様変更のおかげでメンバー限定配信予定があるライバーはツイートしてくれることが多いです。

メンバー限定動画・配信も動画IDさえわかれば配信ステータスなどを監視できる(再生数は取得不可)ので、配信開始通知などに利用できるのです。

リストを使えば効果的

各ライバーアカウントのTLを一つずつチェックしようと思うと1周するのに40秒以上かかる(APIコールリミットの都合上そうせざるを得ない)ので、タイミング次第では10秒以上もラグが発生してしまいます。

そこでリスト機能を使いましょう。

うちのBOTではホロライバーのみのリストを作成して、そのTLを監視しています。

リストであっても15分間のコールリミットが900ですので、どれだけライバーが増えても1秒間隔でチェックすることが可能です。

ただし、アーカイブされた直後のものや過去の動画をツイート・他のチャンネルの動画をツイートするときもあるので、そちらは個別に対策が必要です。

現状お手上げパッパラパーなURL

Youtubeには現在配信中の枠に自動でリダイレクトされる特殊なURLが存在します。

https://www.youtube.com/channel/<channel_id>/live

このURLからは動画IDを調べることが難しいです。適当にリダイレクト先を調べるコードも書きましたが、ヘッダーやらに動画IDらしき情報が見当たらなかったので、うちのBOTでは唯一の非対応URLとしています。

解決しました。暇なときに解説を更新します。

最後に

動画再生数取得はYoutube Data APIを使いますが、最適化しないとあっという間にクォータを消費してしまいます。

 

次回は全動画(現在12500本程度)再生数通知ラグを1分以内に抑える方法やBilibiliの配信状況を監視する方法でも書けたらな~と思います。

タイトルとURLをコピーしました