2018年4月29日日曜日

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


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

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

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

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






2018年4月22日日曜日

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

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

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

生放送を検出する

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

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

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

最初に戻る。

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


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



注釈として
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行ずつ足して保存すれば翻訳されたものが読み上げられます。



以下がコードとなります。①は自分のAPIキー。②は自分のフォルダ位置に合わせてください。
使用する前に棒読みちゃんを起動した状態で行ってください



注釈として
//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では動きません。


<!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の文字列のことです。





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



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