2018年4月29日日曜日

いくつかのバーチャルYouTuberのタグを覗いてみて感想 とついでに愚痴


いくつかのバーチャルYoutuberのタグを覗いてみました。
APIを使わずともソースの表示を見ることができるのですが、APIの方が便利で早いのです。
なんというか、配信者側が自分で「かわいい」とか「ロリ」とか書いてあるのって
なんか見てはいけもないモノを見た恥ずかしさを感じてしまいますね。

傾向として
企業勢は割としっかりやっているけど一部個人勢はタグ無しの人もいて、
せめてコラボした人の名前とかモノマネした人の名前入れてあげてって思う。
それができている人は、動画やその他の対応もしっかりしている感じもする。
効果のほどがどれだけあるか知りませんが関連動画に影響があるのでしょう?
生放送はついてない場合が多いのが勿体なぁ。

ある程度濁して書くと。
・今年に入ってからタグにバーチャルYouTuberって入れたんだな。あと数か国語で「かわいい」が入っているし戦略的。最初から「crazy」って入っているのは好感。
・清楚・イルカってタグにはないのか・・・
・ずっとイラストレーターの名前を入れているとは義理堅いなぁ
・動画内容と無関係なのに上位のバーチャルYoutuber名前入れるのはいかんのでは?
2名見つけたけどどちらも登録者数1万超えているのだからそんなのいらないんじゃねーの。
「新人」とか「初投稿」も入っているしコピペかな?
・さすがにパクリもとのタグはつけんか
・割と初期に自分でクソメガネってタグいれとる・・・あっ、もう一人クソメガネって入れている人がいた。
・○○勢と一括りにされるけど人によってタグをつけない人とつけている人と分けられているのが興味深い。
ほんとおまかせなんだなぁ

と、特に尖ったものも、面白いものもない。
(配信者じゃないので詳しく知らないですが、タグって後から編集できるようですね。
だからしばらくしてから編集されたものもあるかも。)






2018年4月22日日曜日

YouTube 生放送自動録画方法を考える

※この記事だけでは録画はできません。また、うまくいかないことがあると思います。

以前の記事でYouTuve Liveの検出もできたので録画もできるんじゃないかなぁと思い、
その手順を考えてみました。

生放送を検出する

ブラウザを立ち上げてその動画を表示

何かツールを使い録画する

終了を検出して録画を終了する、ブラウザの閉じる

最初に戻る。

という手順が考えられると思います。
ここでは、ブラウザを立ち上げて動画を表示するだけですが
コマンドプロンプトからキャプチャーソフトで録画・終了できるものは世の中には存在するので、そのキャプチャーソフトの使い方を参考にしてコードに反映させれば録画できるかもしれません。
今回はfirefoxを起動させていますが、他のブラウザだとウィンドウの最大化など制御項目が多いかも?


以下がコードとなります。以前の生放送の検出コードの改造です。
①は自分のAPIキー。②は生放送を監視したいチャンネルID
③にはキャプチャーソフトの録画開始処理。④はキャプチャーソフトの録画終了です。

livechkRec.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
$key = "①";
$channelId = "②";
 
$search_api_base = "https://www.googleapis.com/youtube/v3/search?part=snippet";
$search_api = $search_api_base . "&channelId=" . $channelId . "&type=video&eventType=live" . "&key=" . $key ;
 
//タイムゾーンを東京に設定
date_default_timezone_set('Asia/Tokyo');
echo  "起動開始時間". date('H:i:s') . PHP_EOL ;
 
loop:
//前半
$flag = 1;
while($flag)
{
 $search_contents = file_get_contents($search_api);
 $search_json = json_decode($search_contents,true);
 
 if(empty($search_json["items"]) == FALSE)
 {
  echo  date('H:i:s')  . "に放送開始!" . PHP_EOL ;;
  $videoId = $search_json["items"][0]["id"]["videoId"];
  $exestr = "Start firefox -new-window"." https://www.youtube.com/watch?v=" . $videoId;
  $ftolk =  popen($exestr,'r');
  pclose($ftolk);//ファイルポインタを閉じてるだけ
 
  //③ここに録画する処理を足す
  $flag = 0;//whileから抜ける
 }
 //60秒遅延
 usleep(60*1000000);
}
 
//後半
$flag = 1;
while($flag)
{
 $search_contents = file_get_contents($search_api);
 $search_json = json_decode($search_contents,true);
 
 if(empty($search_json["items"]) == TRUE)
 {
  echo  date('H:i:s')  . "に放送終了!" . PHP_EOL ;
  //④ここに録画を終了する処理を足す・ブラウザも閉じる!
  $flag = 0;//whileから抜ける
 }
 //60秒遅延
 usleep(60*1000000);
}
 
 
goto loop;
 
?>


注釈として
12行目と53行目がペアとなっています。処理が終了したら最初に戻るgoto処理。
26行目はファイルポインタを閉じているだけでブラウザを閉じるわけではありません。
PHPではアプリケーションを起動する関数はいろいろあるようですが、これが最適かはわかりません。とりあえず起動はできます。
24行目 引数に開きたいURLを付けるとfirefoxはそのURLを開く
42行目は生放送の終了条件のチェックです。60秒周期で監視しているので生放送1→生放送2も続けて同じ放送と判定されるから同じvideoIdであるか判定したほうがいいかも。

改善点・注意点として
firefoxの新しいウィンドウとして表示されますが、前回のブラウザの大きさに左右されてたりブックマークのサイドバーの状態が維持されています。
生放送でも広告が表示されることがありますが手動スキップはできません。
広告が1時間とか長かったから手も足も出ないです。
広告後に再生が手動の場合もある?
生放送後砂嵐画面で止まるなら音がでていないのでブラウザを終了する必要ないかも。
そもそもYouTubeが最近止まるってリロード必要の場合があるし。。。
60秒周期で処理していますがもっと短くしないと動作確認も大変かも
自分が家にいないときに動画が流れてうるさい場合もあるかも。
前回同様、限定公開も無理。

など考える点がたくさん。アプリの組み合わせで実現するだけだから仕方ないよね・・・
録画しっぱなし、生放送は一回だけの想定ならこれで使えないことはないと思う。
もう出先で通知を受け取ってリモートデスクトップの操作のが楽か?
あまり自分には生放送を録画するという必要に迫られていないのでこのぐらいでクローズ

MMOのチャットログを翻訳→読み上げる

MMO全盛期にできてればよかったのになぁ

8年以上前に海外のMMOを(英語がまったくもってできない)のにやっていたころ、
チャットログを翻訳できれば便利なのになぁと思ってなんとかできないかちょっと検討してみたのですが、
ゲーム側が記録続けているテキストファイルに対して最新の行を取得する方法がいまいちわからなかったのですよね。

時が立ちいろいろやってくうちに少し解決しました(C++ですけど)
対象のテキストファイルが更新されても一度EOFに到達している後は新しい行が取得できない
原因はEOFを一度見つけるとEOFを見つけたフラグが立つから。
それでfgetsで続きがよめなくなるので、このフラグをクリアする(ifs.clear()。)
このころは英語のMMOを既にやってないので翻訳は気にしなかったし、C++でAPI投げる方法も全然わからなかった。

今はPHPでYouTube Data APIを投げるのは慣れてきたので、できるじゃんということでやってみました。
PHPではifs.clear()みたいなのが見当たらないので
fseek($fp, ftell($fp))で確認した行まで再度シークすることによって
EOFのチェックを再び行うことによって実現しています。
この更新され続けるテキストを監視する方法がネットで検索しても出てこなかったから苦労した。
需要はありそうだし、他にエレガントのやり方があるのだろうか?

翻訳の部分はYouTube Data APIと同じノリで 「Google Cloud Translation API」を使います。
ただし、これは有料の機能です。
Google Play Consoleで300ドル分のクレジット期限付きであったのでこれを活用。(お試し用?)
20ドル/100万文字(全部マルチバイト文字だとたら200万バイト→1.9MB相当?)だそうですが10万文字で10ドルなど使った分だけ請求されるみたい。
Google Play Consoleの方でYouTube Data APIと同じように「Google Cloud Translation API」を有効にします。
既存のAPIキーがあればそれで「Google Cloud Translation API」が使用できます。
課金されるのでAPIキーが他人に漏れないように!


ついでにYouTubeの方もチャットも読み上げソフトで読めたらなぁと思い練習用に
翻訳後の結果を有名なソフト「棒読みちゃん」に読み上げさせます。
これは「棒読みちゃん」のフォルダの下にある「RemoteTalk.exe」にメッセージを投げればよいです。


以下、今回のコードの仕様
・PHPファイルと同じフォルダにあるtest.txtを対象とします。
・PHP実行後にtest.txtに追加された行を対象とします
・英語→日本語の翻訳を行います。(もともと日本語の場合もAPIへ投げているので課金されると思います。)
・翻訳結果を棒読みちゃんで読み上げを行います。
・ゲームチャットのログとして実行例として自分が昔やっていたMMO(サービス終了済み)を使用します
使用したゲームのログにあわせてメッセージ部分を取り出してください。
・自分の発言はロボット、それ以外は女性1の声質を使います

テスト用txtファイル例 これを上から5行目に1行ずつ足して保存すれば翻訳されたものが読み上げられます。
TEST.txt
1
2
3
4
2024/12/01 21:34:10 :>パーティー< :Let's attack from the back of the enemy
2024/12/01 21:39:21 :<パーティー> Okayu :用意ができたら
2024/12/01 21:39:27 :<パーティー> Okayu :When ready
2024/12/01 21:39:09 :>パーティー< :Then please tell me if you can



以下がコードとなります。①は自分のAPIキー。②は自分のフォルダ位置に合わせてください。
使用する前に棒読みちゃんを起動した状態で行ってください
ReadChat.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?php
define("APIKEY","①");
 
//翻訳関数
function trns_us($text)
{
 $url = "https://www.googleapis.com/language/translate/v2?key=" . APIKEY . "&source=en&target=ja&q=".urlencode($text);
 $resjson = file_get_contents($url);
 $resjson = mb_convert_encoding($resjson, 'UTF-8', 'auto');
 $resarray = json_decode($resjson);
 $result = $resarray->data->translations[0]->translatedText;
// echo $result;
 return $result;
}
 
//RemoteTalkへのパス
$toolpath="C:\\Users\\HogeHoge\\Documents\\tool\\BouyomiChan\\RemoteTalk\\RemoteTalk.exe /T ";//←②
 
define("TESTFILE","./TEST.txt");
 
$fp = fopen(TESTFILE, "r");
fseek( $fp, 0, SEEK_END);//最後の行へ移動
 
while(1)
{
 while ( $line = fgets($fp) )
 {
//  echo $line;
 
  //フォーマットに合わせて分解する
   $data=explode(":",$line);
  $str = $data[1];
 
  $data1=explode(" ",$data[0]);
  if(count($data1) == 5)
  {
   $name = $data1[3];
   $voice = " -1 -1 -1 1";//女性1
  }
  else
  {
   $name = "自分";
   $voice = " -1 -1 -1 6";//ロボット
  }
 
  //echo $name . PHP_EOL;//キャラ
   //echo $str . PHP_EOL;//テキストデータ
   
  $str = trns_us($str);//翻訳
   
  //棒読みちゃんに飛ばす
  $tmp_cmd = $toolpath ."\"". $str."\"".$voice;
  $ftolk =  popen($tmp_cmd,'r');
  pclose($ftolk);
 
  //配列のクリア
  unset($data);
  unset($data1);
 }
 usleep(1*1000000);//1秒待ち
 fseek($fp, ftell($fp));//再チェック
}
 fclose($fp);
 
?>



注釈として
//echoはデバッグ用です。
17行目は自分のフォルダに合わせてください。\\は2つずつ書くようにしてください。
31行から44行目は自分のゲームのフォーマットに合わせ。声の割り当ても自分の趣味で。
7行目。URLに翻訳した文とAPIキー。翻訳元言語、翻訳先言語を指定して投げると
JSONでレスポンスが返ってくるので結果を取り出すだけ
日本語だろうとかまわずAPIに投げます。
49行目 ここコメントアウトで翻訳なしに。
52行目。スペースが入ったりするとアレなので””で囲む。
53行目と54行目。PHPからのアプリ実行はいくつも方法があるのでよくわからないけど、
これでよさそう。
63行目。ここには到達しません。


改善点として
対象ファイルはドラッグとかで指定できるようにしたほうがいい
<ささやき>や<シャウト>で音量を変えたり、特定の人に特定の声質を当てるなど自分で好きなようにコーディングして調整してみたください。
エラーチャックはしてません。いつも通り動けばいいで雑です。
ファイルの行が減ったりすると暴走しそう。
YouTubeのチャットも同じように投げたり翻訳できたり自分用にカスタマイズできますね。
正規表現でチャットログのメッセージを分解した方がエレガントかも。


昔できなかったことができるってのは成長です!
次の機会まで 「Google Cloud Translation API」は無効にして封印!

2018年4月16日月曜日

目次:YouTube Data APIで遊ぶ

このブログ内のYouTube Data API関係の記事についての目次です。

1:PHPの開発環境
APIを使うために開発環境についての説明

2:YouTube Data APIの使い方 
APIキーについてとAPIの簡単な動作確認
API使用量制限について説明

3:YouTube生放送の検出 
コマンドプロンプトで生放送の開始を監視してビープ音を鳴らすというプログラムについての説明

4:YouTube コメントの全取得
動画のコメント欄を一括で取得するプログラムについての説明

5:OAuth2.0での認証と登録チャンネルリストの出力 
認証の方法
アクセストークンについての説明
自分自身の登録チャンネルをリスト出力

6:YouTube チャットの取得
生放送でのチャットメッセージ情報を取得

7:YouTube 生放送中の評価芸
API使って評価数や視聴者数を取得

8: YouTube生放送で評価数をリアルタイムでグラフ表示する 
PHPで難しそうなのでJavaScriptを使ってリアルタイムで変化するグラフ表示

9: YouTube 生放送自動録画方法を考える 
とりあえず考えてみる。対象の動画の表示だけはできたが・・・

10:YouTubeチャットで視聴者参加のためのツールを考える 
上位のバーチャルユーチューバーのチャット欄は早くて読みにくいので、
たぶんこんな感じで対象者を募って決めて。対象者だけ表示すると予想。

11:YouTubeの生放送のチャットを使ってアンケートを行う 
チャットのコメントでアンケートを実装してみる。

12:YouTubeの生放送のチャットをPHPから書き込む
チャットのコメントにPHPから周期的に書き込むサンプル。

以上。

YouTube生放送で評価数をリアルタイムでグラフ表示する

いままでPHPでがんばってたんですが。。。
おかしいよな、PHPでやり方を発信するはずがJavaScriptを使用なんて。


グラフ表示には 調べた結果Highchartsというのがよさそう

でもJavaScriptで書く必要がある

PHPのデータで保存したファイルをJavaScriptで逐次読もうと考えたけど難しそう

PHPあきらめて JavaScriptでやり始める

Firefoxにできたhtmlファイルを突っ込んで確かめていたけど、途中でChromeを右クリックの「検証」でブレークとかエラー表示が見えることに気付く

グラフの見栄え・気になる所をいろいろいじったらすごい時間をかかるのである程度妥協する

そして、出来上がったのがこれ

 HighchartsとJavaScriptを無勉から、で作り上げました。
Highchartsのこちらをベースにしています。

仕様は
 ・20点のデータ
 ・2秒間隔で取得
 ・高評価と低評価。チャンネル登録者数を表示
 ・最新値が見えるようにする



右が最新のデータで、左が過去データで流れていきます。

これがコードです。このhtmlをChromeやFirefoxに放り込んでみてください。古いIEでは動きません。


LivedataChart.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
<!DOCTYPE html>
<html lang = "ja">
  <head>
    <meta charset = "utf-8">
    <title>Live data</title>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
  
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
  </head>
  <body>
   
<script>
var input_y0=0,input_y1=0,input_y2=0;
var key = "①";
var videoId = "②";//今回はvideoIdを入力
var channelId="";//こっちは空欄でOK
 
    function requestvideo() {
      return $.ajax({
                type: 'GET',
                url: 'https://www.googleapis.com/youtube/v3/videos?part=statistics,snippet,liveStreamingDetails&id=' + videoId + '&key=' + key,
                dataType: 'json',
                async: false,//同期処理にする
                error: function() {
                    //error func
                    $('#yt-list').append('<li>Youtubeのデータが取得できませんでした。<\/li>');
                }
      }).done(function (jsondataYt) {
   channelId = jsondataYt.items[0].snippet.channelId;
         input_y0 = parseFloat(jsondataYt.items[0].statistics.likeCount);
         input_y1 = parseFloat(jsondataYt.items[0].statistics.dislikeCount);
      });
    }
 
 
    function requestchannel() {
      return $.ajax({
                type: 'GET',
                url: 'https://www.googleapis.com/youtube/v3/channels?part=statistics&id=' + channelId + '&key=' + key,
                dataType: 'json',
                async: false,//同期処理にする
                error: function() {
                    //error func
                    $('#yt-list').append('<li>Youtubeのデータが取得できませんでした。<\/li>');
                }
      }).done(function (jsondataYt) {
         input_y2 = parseInt(jsondataYt.items[0].statistics.subscriberCount);
      });
    }
 
   
 
 
 
Highcharts.setOptions({
  global: {
    useUTC: false
  },
    lang: {
        numericSymbols: null//軸のkキロ表示をやめる
    }
});
 
Highcharts.chart('container', {
  chart: {
   type: 'spline',
 
    animation: Highcharts.svg, // don't animate in old IE
    marginRight: 100,//右の軸が隠れるので適切な値にする
    events: {
      load: function () {
 
        // set up the updating of the chart each second
        var series0 = this.series[0];
        var series1 = this.series[1];
        var series2 = this.series[2];
 
        setInterval(function () {
          var x = (new Date()).getTime(); // current time
          requestvideo();//YouTubeAPI ビデオ側
          requestchannel();///YouTubeAPI チャンネル側
 
          series0.addPoint([x, input_y0], true, true);
          series1.addPoint([x, input_y1], true, true);
          series2.addPoint([x, input_y2], true, true);
          series0.update({name: '高評価:'+input_y0});
          series1.update({name: '低評価:'+input_y1});
          series2.update({name: '登録者数:'+input_y2});
       }, 2000);//2秒のインターバルタイマ
      }
    }
  },
  title: {
    text: 'Live data'
  },
  xAxis: {
    type: 'datetime',
    tickPixelInterval: 150
  },
  yAxis: [
      { // 1つ目のy軸設定
          title: {
              text: '評価'
          },
          plotLines: [{
              value: 0,
              width: 1,
              color: '#FF8080'
       }],
       allowDecimals: false //軸を整数表示
      },
      { // 2つ目のy軸設定
          title: {
              text: '登録者数'
          },
       allowDecimals: false, //軸を整数表示
          opposite: true // trueの場合グラフの右側にy軸を配置する
      }
  ],
  tooltip: {
    formatter: function () {
      return '<b>' + this.series.name + '</b><br/>' +
        Highcharts.dateFormat('%Y-%m-%d %H:%M:%S', this.x) + '<br/>' + this.y;
    }
  },
  legend: {
    enabled: true //凡例表示
  },
  exporting: {
    enabled: false
  },
  series: [{
    name: '高評価',
    yAxis: 0,
    data: (function () {
      var data = [],
        time = (new Date()).getTime(),
        i;
 
      for (i = -19; i <= 0; i += 1) {
        data.push({
          x: time + i * 1000,
          y: null
        });
      }
      return data;
    }())
  },
  {
    name: '低評価',
    yAxis: 0,
    data: (function () {
      var data = [],
        time = (new Date()).getTime(),
        i;
 
      for (i = -19; i <= 0; i += 1) {
        data.push({
          x: time + i * 1000,
          y: null
        });
      }
      return data;
    }())
   },
  {
    name: '登録者数',
    yAxis: 1,
    data: (function () {
      var data = [],
        time = (new Date()).getTime(),
        i;
 
      for (i = -19; i <= 0; i += 1) {
        data.push({
          x: time + i * 1000,
          y: null
        });
      }
      return data;
    }())
   }
    
   ]
});</script>
 
 
  </body>
</html>

注釈として
なんか上のコードの色が変ですがあきらめました。
Highchartsは、ダウンロードしても使えるみたいですがしなくてもできるらしいです。今回はダウンロードなしで使用しています。
16と17行目はPHPと使い方は同様です。APIキーと動画IDいれてください。
APIの使いかたはPHPと同じようなものです。URL作って投げて、レスポンスを使用する形は変わりません。
25行目と43行目については、PHPと違いJavascriptだとレスポンスが返るまでまたず処理が進んでしまう、雑な対策としてこの行追加でレスポンスが返るまで待ち状態となります。これでPHPと同じ感じに動作します。
62行目 Y軸が1000を1kとかの表示するのを禁止します。
71行目 第2のY軸を右に置こうとしたら表示されないので苦労しました。原因は画面の外に表示されていました。この値を増加したら解決。これで1時間は無駄にしました。
80行目は2秒周期インターバル動作させています。
85行目あたりaddPointのtrueとかすることによってシフト動作してくれます
88行目 汎用に最新値表示しています。最新の値をプロット上に出したかったのですがdataの要素の最後だけlabel表示をenableにしてもシフト動作すると前の要素に移動していってしまうのでaddPointを使わずfor文なりで更新するのがよさそう?メンドクサイのであきらめました。
112行目と118行目でY軸が整数表示
119行目で二つ目のY軸を右側に置きます
136行目でどちらのY軸に表示するか決まります
142行目等のi=-19でデータの点数を20点してます。-199にすれば200点になります。


なんとかやりきった。疲れた。

2018年4月15日日曜日

評価を実際見てみる

せっかく生放送の評価を取得できるようになったので実際とってグラフにしてみました。

一つ目。しばらく前の某バーチャルユーチューバーの麻雀配信の最後の方だけです。
たしか2戦目で配信者がビリになり、罰ゲームのルイズコピペ改変で場が温まっている後です。


Aの低評価増加は、配信者が1位をとった所です。
Bの低評価減少、高評価増加は罰ゲームをやりきった謎の感動
Cのピークは、 配信者実質3連続1位とか言いだしたイキリ芸のところです。少し前の高評価増はゲストのために評価戻しといてねの発言があったからです。
高評価と低評価が逆の形をしているので、コミュニケーションの一環みたいですね。

YouTube 生放送中の評価芸

生放送中をみるようになって思ったのですけど、
例えばチャンネル登録数がもう少しで5000人になるって時にわざと解除したり、
一発芸すると低評価が入ってすぐ戻ったりなど
評価の上下で視聴者とコミュニケーションするのだなと思いました。

ということで、生放送中の評価を取得するプログラムを書いてみました。
仕様は
・最初にタイトル等を表示する
・時間,視聴者数,再生数,高評価,低評価,チャンネル登録数を記録する
・10秒間隔で取得する
・終了条件はなし
です。
特に今回は追加のテクニックはありません。  ①にはAPIキー、②には今回は生放送中の動画のidを入れます。
https://www.youtube.com/watch?v=xxxxxxxxxxx
のxxxxxxxxxxxの文字列のことです。



livedata.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?php
$key = "①";
$videoId = "②";//今回はvideoIdを入力
 
// 処理開始日時
//タイムゾーンを東京に設定
date_default_timezone_set('Asia/Tokyo');
echo  "処理開始日時". date('Y-m-d H:i:s') . PHP_EOL;
 
//生放送の情報の取得して表示
$video_url_base = "https://www.googleapis.com/youtube/v3/videos?part=snippet,liveStreamingDetails";
$video_url = $video_url_base . "&id=" . $videoId . "&key=" . $key;
$video_contents = file_get_contents($video_url);
$video_json = json_decode($video_contents,true);
 
//配信タイトル(トピック)取得
$title = $video_json["items"][0]["snippet"]["title"];
//配信開始時刻取得
$StartTime = $video_json["items"][0]["liveStreamingDetails"]["actualStartTime"];
//チャンネル名取得
$channel_name = $video_json["items"][0]["snippet"]["channelTitle"];
//説明文取得
$description = $video_json["items"][0]["snippet"]["description"];
 
echo "タイトル:" . $title . PHP_EOL ;
echo "放送開始時間:" . date("Y-m-d H:i:s",strtotime($StartTime)) . PHP_EOL ;//タイムゾーンを東京に変更している
echo "チャンネル名:" . $channel_name . PHP_EOL ;
echo "概要欄:" . $description . PHP_EOL . PHP_EOL;
 
//チャンネルIDを取得
$channelId = $video_json["items"][0]["snippet"]["channelId"];
 
 
 
echo "10秒ごとにサンプリングする" . PHP_EOL;
echo "時間,視聴者数,再生数,高評価,低評価,チャンネル登録数" . PHP_EOL;
 
$video_url_base = "https://www.googleapis.com/youtube/v3/videos?part=statistics,liveStreamingDetails";
$video_url = $video_url_base . "&id=" . $videoId . "&key=" . $key;
 
$channel_url_base = "https://www.googleapis.com/youtube/v3/channels?part=statistics";
$channel_url = $channel_url_base . "&id=" . $channelId . "&key=" . $key;
 
while(true)
{
 //視聴者数 再生数 高評価 低評価
 $video_contents = file_get_contents($video_url);//動画側へのAPIを叩く
 $video_json = json_decode($video_contents,true);
 
 $viewCount = $video_json["items"][0]["statistics"]["viewCount"];
 $likeCount = $video_json["items"][0]["statistics"]["likeCount"];
 $dislikeCount = $video_json["items"][0]["statistics"]["dislikeCount"];
 $viewers = $video_json["items"][0]["liveStreamingDetails"]["concurrentViewers"];
 
 //チャンネル登録数
 $channel_contents = file_get_contents($channel_url);//チャンネル側へのAPIを叩く
 $channel_json = json_decode($channel_contents,true);
 $subscriberCount = $channel_json["items"][0]["statistics"]["subscriberCount"];
 
 //表示
 echo date('Y-m-d H:i:s')  . ",";
 echo $viewers . ",";
 echo $viewCount . ",";
 echo $likeCount ",";
 echo $dislikeCount . ",";
 echo $subscriberCount . PHP_EOL;
 
 //10 秒待つ
 usleep(10*1000000);
}
?>


注釈としては
YouTubeからのレスポンスのタイムゾーンが違うので 7行目と26行目で日本時間に変換しています。
61行目から66行目は取得した情報をカンマ区切りで出力しています。



備考として
・生放送中にリアルタイムでグラフがだせたらもっといいかも。PHPだけでは無理っぽいけど。あとチャットの勢いとかだせたらいいかなぁ。
・生放送中のアーカイブをそのままアップするとどうも生放送中の再生数が引き継がれる感じでした。
・低評価と高評価や再生数が約12分毎で更新することもあるし、もっと短いこともあってよくわからない。YouTubeのアルゴリズムの問題?
・取得の間隔は評価の変化が激しい時に3秒とか短めにできたら評価芸がわかっていいかもしれない。今回は10秒としましたがもっと短い周期でも制限的に大丈夫かも。
・長時間取得しっぱなしだと1日の使用量の制限を超える可能性があるので注意する。
・配信者は生放送中の上下が後からみることができるのだろうか?
・コードでは現在時刻で表示しているがliveStreamingDetailsのactualStartTimeでアーカイブの再生時刻と合わせられるかもしれない

2018年4月13日金曜日

YouTube チャットの取得

今回はYouTuve Liveのチャットの取得に挑戦します。

ということで、チャットが取得できるようになれば
特定の人を見えなくしたいとか、
特定の人だけピックアップするとか、
特定のキーワードで何かが加速したり、
スーパーチャットで何かしたり、
他のアプリケーションとの連携したり
チャットを保存したい
読み上げソフトで読み上げたり
翻訳したり
など、いろいろな目的がかなうようになると思います。

今回の仕様はこんな感じです。
・名前とテキストのみを表示
・コマンドプロンプトで表示する
・”www”(半角)を含むテキストを”NGNGNGNG”に置き換える
です。

使用しているAPIについてはvideoLiveChatMessagesを参照。
①にはAPIキー、②には今回は生放送中の動画のidを入れます。
https://www.youtube.com/watch?v=xxxxxxxxxxx
のxxxxxxxxxxxの文字列のことです。


以下がコードになります。 ※少ししか動作確認していません。
 追記2018/4/29 下の方に処理タイミングを変更したコードを載せました。


chat.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
$key = "①";
$videoId = "②";
 
//ChatIdの取得
$search_api="https://www.googleapis.com/youtube/v3/videos?part=liveStreamingDetails&id=" . $videoId ."&key=" . $key;
$search_contents = file_get_contents($search_api);
$search_json = json_decode($search_contents,true);
$ChatId = $search_json["items"][0]["liveStreamingDetails"]["activeLiveChatId"];
 
//ChatIdからliveChatにアクセス用のURL
$search_api="https://www.googleapis.com/youtube/v3/liveChat/messages?part=snippet,authorDetails&liveChatId=" . $ChatId ."&key=" . $key;
 
$next = "";
$url = $search_api;
 
while(1)
{
 if($next)
 {
  $url = $search_api . "&pageToken=" . $next;
 }
  
 $search_contents = file_get_contents($url);
 $search_json = json_decode($search_contents,true);
 
 foreach($search_json["items"] as $k => $data)
 {
  echo $data["authorDetails"]["displayName"];
  echo PHP_EOL;
  echo "\t";
  $text = $data["snippet"]["textMessageDetails"]["messageText"];
  if(strpos($text,"www") === false)
  {
   echo $text;
  }
  else
  {
   echo "NGNGNGNG";
  }
  echo PHP_EOL;
 }
 
 $next = empty($search_json["nextPageToken"])? 0 : $search_json["nextPageToken"];
 //pollingIntervalMillis + 500ms加算時間毎に読み込む
 $delay = $search_json["pollingIntervalMillis"]*1000 + 500*1000;
 usleep($delay);
 
}
 
?>



注釈として
6行目はpart=liveStreamingDetailsとして、”ChatId”を取得しています。
12行目でわかるように今回も続きを読み出すために&pageToken=を使用しています。
ただし、46行目にあるように"pollingIntervalMillis"より早くAPIを投げるとエラーとなるのでそれより500ms多めに遅延させています。
29行目から411行目を変更すればいろいろな機能を持たせられると思います。
コマンドプロンプトで>chatlog.txtやファイル書き込みで保存もできます。

結果はこんな感じです。
コマンドプロンプトなのでいまいちな見た目ですが・・・残像でるし

改善点として
・絵文字も使えないのでやっぱりhtmlなどでやるべきかなぁと。でも更新処理どうしていいかわからない。。。
・チャットが激しすぎるとどうなるのだろうという疑問。1回で取得する数のデフォルトは500ですがそれ以上の激しさでも追いつくか。
・pollingIntervalMillisは勢いで変化します。激しいほど長い感じです。
自分が見たのは4秒から9秒とかでした。
取得したメッセージを一気に表示しているのでpollingIntervalMillis/pageInfo.resultsPerPage
との間隔で少しずつ出力した方が自然な感じがします。
・生放送の終了判定がありませんので自分で強制終了するか確認処理が必要かと思います。放送自体が終了してもチャットが生きてたりで動作確認が必要かも。upcoming中はとりあえず取得ができる。

 -------------------------------------------------------------------------------------
追記2018/4/28
 配列でバーチャルYoutuber300人程度の発言のみを抽出コードを作って生放送見てたんだけど少し抜けがある。抽出処理の時間分待ち時間削ってないし、500ms分固定でプラスしてるのもイケてないのかも。
某所のコメントログでは比べるとどちらも抜けがあるしチャットは結構きついのかなぁ
公式のサンプルをもっと見た方がよかったか。
追記2018/4/29
 pollingIntervalMillisに対してフィルタ処理はそんな重くないことがわかったんで、とりあえず500ms固定を削除して待ち方を変更。
コードは以下


chat2.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?php
$key = "①";
$videoId = "②";
 
//ChatIdの取得
$search_api="https://www.googleapis.com/youtube/v3/videos?part=liveStreamingDetails&id=" . $videoId ."&key=" . $key;
$search_contents = file_get_contents($search_api);
$search_json = json_decode($search_contents,true);
$ChatId = $search_json["items"][0]["liveStreamingDetails"]["activeLiveChatId"];
 
//ChatIdからliveChatにアクセス用のURL
$search_api="https://www.googleapis.com/youtube/v3/liveChat/messages?part=snippet,authorDetails&liveChatId=" . $ChatId ."&key=" . $key;
 
$next = "";
$url = $search_api;
 
while(1)
{
 if($next)
 {
  $url = $search_api . "&pageToken=" . $next;
 }
  
 $search_contents = file_get_contents($url);
 $search_json = json_decode($search_contents,true);
 //時間測定開始
 $time_start = microtime(true);
 
 foreach($search_json["items"] as $k => $data)
 {
  echo $data["authorDetails"]["displayName"];
  echo PHP_EOL;
  echo "\t";
  $text = $data["snippet"]["textMessageDetails"]["messageText"];
  if(strpos($text,"www") === false)
  {
   echo $text;
  }
  else
  {
   echo "NGNGNGNG";
  }
  echo PHP_EOL;
 }
 
 $next = empty($search_json["nextPageToken"])? 0 : $search_json["nextPageToken"];
 //pollingIntervalMillis msの整数型を実数にしている
 $delay = $search_json["pollingIntervalMillis"]/1000.0;
 do{
  $time = microtime(true) - $time_start;
  usleep(100);//0.1ms待ち
 }while($time < $delay);
}
 
?>


注釈として
 51行目の待ちがないとCPU負荷が高いので挿入しています。

これで抜けがいくらか減ると思います。

2018年4月12日木曜日

OAuth2.0での認証と登録チャンネルリストの出力

YouTubeのAPIにはOAuth2.0での認証が必要なものがあります。
ここでは認証を行った後、自分のチャンネル登録のリストの出力を目標とします。

認証の仕方についてはこちらに書かれています。ざっと目を通せばよいです。

①認証情報の作成
APIキーを作成した時のようにconsole.developers.google.comへ行き
認証情報>認証情報を作成>OAuthクライアントID の作成>その他
で作成します。(たぶんこれでいいと思う。。。)



クライアントIDとクライアントシークレットをコピーしてファイルに保存してください。
※ここではclient_data.txtにクライアントIDとクライアントシークレットの順に文字列だけを保存します。

② トークンの取得
2つのphpファイルが必要です。
一つ目のphpをブラウザ上で実行する

googleの認証画面に行き許可ボタンを押す(goolgeアカウントのパスワードが必要かも)

認証情報をもらった2つ目のphpが更新トークンをファイルに保存する
という流れとなります。

2つ目のphpでは アクセストークンと更新トークンの両方が得られますが更新トークンだけを保存します。
トークンにはアクセストークンと更新トークンがあります。
アクセストークンをAPIキーのように&access_token=に渡して使用しますが1時間ぐらいで期限切れになってしまいます。アクセストークンを得るために認証画面にたびたいび行くのはメンドクサイです。
しかし更新トークンはもっと期限はもっと長いです。
なので更新トークンを使いアクセストークンを認証画面に無しでゲットしてphpで使用しようって流れとなります。

 ブラウザのURLに
「http://localhost/get_token.php」と入力してみてください。(C:\xampp\htdocs直下にファイルがある場合)
これで認証画面に遷移した後に許可をすると、 ReToken.txtに更新トークンが保存されます。
「更新トークンを保存しました。」と表示されれば成功です。

コードは以下のようになります。


1つ目のphp
get_token.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
// アプリケーション設定
define('CONSUMER_KEY', '①');
define('CALLBACK_URL', 'http://localhost/oauth.php');
 
// URL
define('AUTH_URL', 'https://accounts.google.com/o/oauth2/auth');
 
$params = array(
 'client_id' => CONSUMER_KEY,
 'redirect_uri' => CALLBACK_URL,
 'scope' => 'https://www.googleapis.com/auth/youtube.readonly',
 'response_type' => 'code',
 'access_type' => 'offline',
);
 
// 認証ページにリダイレクト
header("Location: " . AUTH_URL . '?' . http_build_query($params));
?>


注釈としては
コードの①にクライアントIDを入れる。(折角ファイルに保存したのでそのように改造しても可)
4行目のコールバックした時実行するphpについては、2つ目のファイル名にあわせる。
12行目のスコープはとりあえず「https://www.googleapis.com/auth/youtube.readonly」にしています。たとえば動画のアップロードをする、グーグルマップを使いたいなどの場合はそれぞれ違うURLになります。

2つ目のphp
oauth.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
// アプリケーション設定
define('CONSUMER_KEY', '①');
define('CONSUMER_SECRET', '②');
define('CALLBACK_URL', 'http://localhost/oauth.php');
 
// URL
define('TOKEN_URL', 'https://accounts.google.com/o/oauth2/token');
 
$params = array(
 'code' => $_GET['code'],
 'grant_type' => 'authorization_code',
 'redirect_uri' => CALLBACK_URL,
 'client_id' => CONSUMER_KEY,
 'client_secret' => CONSUMER_SECRET,
);
$headers = array(
    'Content-Type: application/x-www-form-urlencoded',
);
// POST送信
$options = array('http' => array(
 'method' => 'POST',
 'content' => http_build_query($params),
 'header' => implode("\r\n", $headers),
));
 
// アクセストークンの取得
$res = file_get_contents(TOKEN_URL, false, stream_context_create($options));
 
// レスポンス取得
$token = json_decode($res, true);
if(isset($token['error'])){
 echo 'エラー発生';
 exit;
}
$access_token = $token['access_token'];
$refresh_token = $token['refresh_token'];
 
//ファイルへの保存
$fp = @fopen("ReToken.txt","w") or die("File Open Error!\n");
fputs($fp,$refresh_token);
fclose($fp);
 
echo "更新トークンを保存しました。";
 
 
?>


注釈としては
コードの①にクライアントIDを入れる。②にはクライアントシークレットを入れる。
(折角ファイルに保存したのでそのように改造しても可)
11行目で認証した結果をgetしています。



③自分の登録しているチャンネルのリストの出力をする。
コード前半は、ファイルに保存されたクライアントID、クライアントシークレットと更新トークンを読み出し使用してアクセストークンを得てます。
後半は、アクセストークンを使い自分の登録チャンネルを出力しています。
出力のフォーマットして、チャンネルIDを””でくくり、タイトルをコメントアウトで出力して 以前の生放送の検出のコードで使用しやすいフォーマットとしました。
コマンドプロンプトで実行し>や>>で保存するなどすれば登録したチャンネルの整理やさらにコードを変更して登録者数でグループ分けするなどに活用できると思います。
使用しているAPIについてはこちらからを参照。

chlist.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php
define('TOKEN_URL', 'https://accounts.google.com/o/oauth2/token');
 
//ファイルからclient_idとclient_secretを読み込む
$fp = @fopen("client_data.txt","r") or die("File Open Error!\n");
$client_id = rtrim(fgets($fp));
$client_secret = rtrim(fgets($fp));
fclose($fp);
 
//ファイルから更新トークンを呼び出す
$fp = @fopen("ReToken.txt","r") or die("File Open Error!\n");
$refresh_token =rtrim( fgets($fp));
fclose($fp);
 
//リフレッシュトークンからアクセストークンを取得する
$params2 = array(
 
 'client_id' => $client_id,
 'client_secret' => $client_secret,
 'refresh_token' => $refresh_token,
 'grant_type' => 'refresh_token',
);
$headers = array(
    'Content-Type: application/x-www-form-urlencoded',
);
// POST送信
$options2 = array('http' => array(
 'method' => 'POST',
 'content' => http_build_query($params2),
 'header' => implode("\r\n", $headers),
));
 
// アクセストークンの取得
$res = file_get_contents(TOKEN_URL, false, stream_context_create($options2));
 
// レスポンス取得
$token = json_decode($res, true);
if(isset($token['error'])){
 echo 'エラー発生';
 exit;
}
$access_token = $token['access_token'];
 
 
 
 
//登録チャンネルのリストを取得する
$search_api="https://www.googleapis.com/youtube/v3/subscriptions?part=snippet&mine=true&access_token=" .$access_token;
 
$next = 1;
$url = $search_api;
while($next)
{
 if($next != 1)
 {
  $url = $search_api . "&pageToken=" . $next;
 }
  
 $search_contents = file_get_contents($url);
 $search_json = json_decode($search_contents,true);
 foreach($search_json["items"] as $k => $data)
 {
  echo '"' . $data["snippet"]["resourceId"]["channelId"].'",';
  echo "\x20";
  echo "//" . $data["snippet"]["title"];
  echo PHP_EOL;
 }
 $next = empty($search_json["nextPageToken"])? 0 : $search_json["nextPageToken"];
}
 
 
?>?>


注釈として
48行目&mine=trueとして自分のアカウントの情報を対象としています。(そのため自分のアカウントで承認されたアクセストークンが必要)。
またAPIキーではなく&access_token=としてアクセストークンを使用しています。
アクセストークンの使用期限は$resの情報に含まれています。
長時間の使用する場合は使用期限が切れる少し前に更新トークンを使用してアクセストークンを再取得する必要があります。連続使用するケースは思いつきませんが。。。

YouTube コメントの全取得

YouTubeの動画の下の方にコメントがありますね。
長いコメントは詳細を押さないと表示できなかったり、件数が多いと下へのスクロールを繰り返しさないといけなくて煩わしいと思ったことはありませんか?ということでコメントの全取得に挑戦します。
コマンドプロンプトで確認した後はhtmlで取得という二段階にわけて説明します。
使用するAPIは2つ。
APIの情報は親コメントがこちらで子のコメントはこちらです。

今回のAPI使用方法のポイントとしては、コメントの件数が多く一回の読み出しで終わらない場合は、["nextPageToken"]を使い&pageToken=にその値を入れてURLに付与することにより続きを読み出すことができます。
また、コメントは返信による子コメントがあります。
["totalReplyCount"]が1以上であれば子コメントが存在するので、commentThreadsのAPIで読み出します。件数が多い場合もあるのでpageTokenを使い終わるまで読みだしています。 子コメントに関わる変数名は$○○2としています。
またAPIの使用回数を減らすため 親コメントはmaxResults=100、子コメントはmaxResults=50をURLに付与しています。

コマンドプロンプトで行う場合の仕様
・textデータとして表示する
・タブでインデントぽくする
・絵文字はあきらめる
・アイコン画像はあきらめる
・コメントの並びは新しい順
・日時情報は省略
です。コードが以下になります。
①のはAPIキー、②には今回は動画のidを入れます。
https://www.youtube.com/watch?v=xxxxxxxxxxx
のxxxxxxxxxxxの文字列のことです。

comment.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<?php
 
$key = "①";
$videoId = "②";
 
$search_api_base = "https://www.googleapis.com/youtube/v3/commentThreads?part=snippet,replies";
$search_api_base2 = "https://www.googleapis.com/youtube/v3/comments?part=snippet";
 
$search_api = $search_api_base . "&videoId=" .$videoId. "&key=" . $key ."&textFormat=plainText&maxResults=100";
$url = $search_api;
$next = "";
while(1)
{
 if($next)
 {
  $url = $search_api . "&pageToken=" . $next;
 }
  
 
 $search_contents = file_get_contents($url);
 $search_json = json_decode($search_contents,true);
 
 foreach($search_json["items"] as $k => $data)
 {
  echo $data["snippet"]["topLevelComment"]["snippet"]["authorDisplayName"];
  echo PHP_EOL;
  echo "\t";
  $str = str_replace("\n","\n\t",$data["snippet"]["topLevelComment"]["snippet"]["textDisplay"]);
  print_r($str);
 
  echo PHP_EOL;
  echo PHP_EOL;
 
  if($data["snippet"]["totalReplyCount"] >= 1)
  {
   $next2 = "";
   $data2array = array();//子コメント配列の初期化
    
   while(1)
   {
    $parentId = $data["snippet"]["topLevelComment"]["id"];
    $search_api2 = $search_api_base2 . "&parentId=" . $parentId . "&key=" . $key ."&textFormat=plainText&maxResults=50";
    $url2 = $search_api2;
 
    if($next2)
    {
     $url2 = $search_api2 . "&pageToken=" . $next2;
    }
 
    $search_contents2 = file_get_contents($url2);
    $search_json2 = json_decode($search_contents2,true);
 
    foreach($search_json2["items"] as $k2 => $data2)
    {
     array_push($data2array,$data2);
    }
 
    if(empty($search_json2["nextPageToken"]) == FALSE)
    {
     $next2 = $search_json2["nextPageToken"];
    }
    else
    {
     //子コメントを古い順にするためひっくり返す
     $data2array = array_reverse($data2array);
     foreach($data2array as $k2 => $data2)
     {
      echo "\t";
      echo $data2["snippet"]["authorDisplayName"];
      echo PHP_EOL;
      echo "\t\t";
      $str = str_replace("\n","\n\t\t",$data2["snippet"]["textDisplay"]);
      print_r($str);
      echo PHP_EOL;
      echo PHP_EOL;
     }
     unset($data2array);
     break;
    }
   }
  }
 }
 
 if(empty($search_json["nextPageToken"]) == FALSE)
 {
  $next = $search_json["nextPageToken"];
 }
 else
 {
  break;
 }
}
 
?>


注釈として
子コメントも親コメントと同様に新しい順で読み出されるため、一度全部配列に保存しから 65行目でひっくり返して古い順にして読みやすくしています。
28と72行目では改行文字を改行文字と水平タブに置換することによって無理やりインデントを作っています。


動作確認がしたらコマンドプロンプトで以下のように[>>test.txt]や[>test.txt]とすればファイルに保存されます。
>は、新規ファイルを作成して実行結果を出力する
>>は、既存ファイルを作成して実行結果を出力する。ファイルなければ>と同じ動作
です。
結果の例はこんな感じです。一応ぼかしてあります。

2018年4月10日火曜日

YouTube生放送の検出

前回の記事でのコードを修正してYouTuve Live(生放送)の検出を行います。

生放送検出コードのバージョン1の仕様は
・指定したチャンネルが放送中の場合は1秒毎にブザー音を鳴らす。
・確認間隔を60秒として検出を行う。
・起動開始した時間と生放送を検出した時間を表示する。
以下がそのコードになります。
①にはAPIキー②には対象のチャンネルのID(UCで始まる文字列)が入ります。
適当な生放送中のチャンネルや開始時間がわかっているチャンネルなどで動作を確認してみてください。
使用するAPIの情報はこちらです。

livechk1.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
 $key = "①";
 $channelId = "②";
 
 $search_api_base = "https://www.googleapis.com/youtube/v3/search?part=snippet";
 $search_api = $search_api_base . "&channelId=" . $channelId . "&type=video&eventType=live" . "&key=" . $key ;
 
 //タイムゾーンを東京に設定
 date_default_timezone_set('Asia/Tokyo');
 echo  "起動開始時間". date('H:i:s') . PHP_EOL ;
 
 while(true)
 {
  $search_contents = file_get_contents($search_api);//←API投げるところ
  $search_json = json_decode($search_contents,true);
 
  if(empty($search_json["items"])==FALSE)
  {
   echo  date('H:i:s')  . "に放送開始!";
   while(true)
   {
    //1秒ごとにブザー
    echo "\x07";
    usleep(1*1000000);
   }
  }
  //60秒遅延
  usleep(60*1000000);
   
 }
?>

注釈として
10行目と19行目はdate関数で時間を取得しています。タイムゾーンを変更しないと日本時間で表示されないので9行目で変更しています。
17行目は、APIで「&eventType=live」を条件として検索をしているので生放送をしていない場合["items"]の配列は存在しません。逆にFALSEの場合は生放送中です。
このコードを終了する場合はキーボードで「Ctrl+c」と入力して強制停止してください。
以降の記事でも無限ループがあるものの停止方法は同様です。(そういうところが雑なので。。。)




次にバージョン1の仕様を拡張し複数のチャンネルの生放送の検出を行えるようにします。
生放送検出コードのバージョン2の仕様は
・起動時に生放送を検出したら検出したチャンネル名を表示する。この時はブザー音を鳴らさない。
・起動時に生放送をしていなかった残りのチャンネルについて確認間隔を60秒として検出を行う。
・検出した場合は1秒毎にブザーを鳴らす。
・ その後も残りのチャンネルについての検出を継続する。
(ブザーは鳴り続けているため運用として強制停止後に再度起動を想定)

・検出した場合そのチャンネル名を表示する。
・起動開始した時間と生放送を検出した時間を表示する。
です。

以下がそのコードになります。行数が増えましたが前半と後半にわけて考えればそれほど難しくないと思います。


livechk2.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?php
 $key = "①";
 $channelId_List = array(
 "②",// ○○チャンネル
 "②",// ××チャンネル
 );
 
 $search_api_base = "https://www.googleapis.com/youtube/v3/search?part=snippet";
 
 //タイムゾーンを東京に設定
 date_default_timezone_set('Asia/Tokyo');
 echo  "起動開始時間". date('H:i:s') . PHP_EOL ;
 
//前半
 foreach($channelId_List as $k => $channelId)
 {
  $search_api = $search_api_base . "&channelId=" . $channelId . "&type=video&eventType=live" . "&key=" . $key ;
  $search_contents = file_get_contents($search_api);
  $search_json = json_decode($search_contents,true);
 
  if(empty($search_json["items"])==FALSE)
  {
   //チャンネル名取得
   $channel_name = $search_json["items"][0]["snippet"]["channelTitle"];
   echo "放送中:" . $channel_name . PHP_EOL ;
   //起動時に見つかった場合周期的に見る必要がないのでリストから削除
   unset($channelId_List[$k]);
  }
 }
 
//後半
 $xbuzzer = 0;//ブザーフラグ
 
 while(true)
 {
  foreach($channelId_List as $k => $channelId)
  {
   $search_api = $search_api_base . "&channelId=" . $channelId . "&type=video&eventType=live" . "&key=" . $key ;
   $search_contents = file_get_contents($search_api);
   $search_json = json_decode($search_contents,true);
 
   if(empty($search_json["items"])==FALSE)
   {
    //ブザー開始
    $xbuzzer = 1;
     
    //チャンネル名取得
    $channel_name = $search_json["items"][0]["snippet"]["channelTitle"];
    echo  date('H:i:s')  . "に放送開始!:".$channel_name . PHP_EOL ;
    //見つかった場合周期的に見る必要がないのでリストから削除
    unset($channelId_List[$k]);
   }
  }
 
  //1秒のループ60回で60秒を作る
  for($i=0;$i<60;$i++)
  {
   if($xbuzzer == 1)
   {
    //1秒ごとにブザー
    echo "\x07";
   }
    
   usleep(1*1000000);
  }
 }
 
 
?>


出力例としてはこんな感じになると思います。


注釈として
 ・$channelId_Listに検出したいチャンネルを登録してできるようにしています。今回は手作業でおこなってください。自分のアカウントから登録チャンネルの情報から作成したいところですがその場合「OAuth 2.0のトークン」が必要になってくるため別途説明します。
・放送中のチャンネルを何回もチェックしないようにunset関数を使いchannelId_List配列からの削除を行っています。
・ブザーが鳴った条件を満たしたあと再度検出処理中はブザーが鳴らないため
ポッ*60回・無音区間・ポッ*60回・無音区間の繰り返しになります。
登録しているチャンネル数をが多くなればなるほど無音区間が増えてしまいます。(例:40個で約20秒無音区間)

2018年4月8日日曜日

YouTube Data APIの使い方

プログラムからYouTubeから情報を取得・アップロードする等場合はYouTube Data APIを使用します。
 YouTube Data APIについてはこのあたりをみればおおよそわかりますので軽く目を通してもらえればいいと思います。
このYouTube Data APIを使用するために2つの方法があります。
 APIの呼び出しについてはこのリンクを参照
「APIキー」もしくは「OAuth 2.0のトークン」が必要です。

このサイトでは基本はAPIキーを使用します。
アップロードなどの更新や自身のアカウントの情報の取得などはAPIキーではできません。

PHP開発環境

最初にPHPの開発環境を整えます。


私の環境は
XAMPP: Version 7.2.2(PHP:7.2.2)
OS:Windows7 Professional 64bit
エディタ:秀丸
です。
ここでは初心者の自分には難しかったのでPHPクライアントライブラリは使用しません。
ほんとはライブラリ使った方が楽だと思いますが英語だし・・・

はじめに


 最近、YouTubeを見ることが多くなり、生放送を見逃さないように検出したり、登録チャンネルのリストを出力したり、チャットのフィルタができたりしたら便利かなと思いYouTube Data APIに手をだしました。
今までC言語しか使用していませんでしたがYouTube Data APIを使用にするにあたり、開発環境の設定が簡単そうなPHPを選択。コマンドプロンプトで実行することをメインにしました。

 使用方法を公式サイトやいろいろなサイトを参考しました。

 ざっくりとした使い方を順を追って紹介するつもりです。
YouTube Data API関係の目次はこちら
 コマンドプロンプトで行うためアプリケーションと違い見栄えは良くないかもしれませんが、目的は達成できていると思います。
 YouTube Data APについてが終わったらちょこちょこなんかいいたいことを発信しよう。