C#
Unity
YouTube

YouTubeLiveの配信コメントをUnityで利用する

スクリーンショット 2018-02-12 13.21.12.png

YouTube Liveのチャットコメントを使って遊んでみます。

チャンネルIDから辿る方法をとらずに、Unity上でOAuth認証を行います。

こちらのほぼUnity実装版に相当
https://blog.sky-net.pw/article/86

APIキーの用意

Google APIsに行き

  • プロジェクトを作成しYouTube Data API v3を有効化

次にOAuthクライアントを作成します。

  • 認証情報>「OAuth 同意画面」 に適当なサービス名を入力
  • 認証情報>「認証情報を作成」>「OAuth クライアント ID」から作成(アプリケーションの種類はその他、名前は適当)

これでclient_idclient_secertが得られます。

認可コードの取得

ここからUnity。

コード全体はこちら

public class YouTubeLiveController : MonoBehaviour { 

  IEnumerator Start() {
    var clientId = "client_id";
    var clientSecret = "client_secret";

    var code = "";
    LocalServer (c => code = c);

    var authUrl = "https://accounts.google.com/o/oauth2/v2/auth?response_type=code"
      + "&client_id=" + clientId
      + "&redirect_uri=" + "http://localhost:8080"
      + "&scope=" + "https://www.googleapis.com/auth/youtube.readonly"
      + "&access_type=" + "offline";
    Application.OpenURL (authUrl);
    yield return new WaitUntil (() => code != "");

    Debug.Log (code);
  }

  void LocalServer(Action<string> onReceive) {
    ThreadStart start = () => {
      try {
        var listener = new HttpListener();
        listener.Prefixes.Add("http://*:8080/");
        listener.Start();

        var context = listener.GetContext();
        var req = context.Request;
        var res = context.Response;

        var re = new Regex (@"/\?code=(?<c>.*)");
        var code = re.Match (req.RawUrl).Groups ["c"].ToString();
        onReceive(code);

        res.StatusCode = 200;
        res.Close();
      } catch (Exception e) {
        Debug.LogError(e);
      }
    };
    new Thread (start).Start ();
  }

}

client_idからコード取得用のURLを構築してApplication.OpenURLでブラウザを開きます。

ブラウザで認証後にUnity側で認証結果を受け取る手段がないため、ローカルサーバを立ててこちらにリダイレクトさせて無理やりcodeを取得します。

アクセストークンの取得

取得したcodeを使用してaccess_tokenを取得します。

  IEnumerator Start() {
    // snip

    var tokenUrl = "https://www.googleapis.com/oauth2/v4/token";
    var content = new Dictionary<string,string> () {
      { "code", code },
      { "client_id", clientId },
      { "client_secret", clientSecret },
      { "redirect_uri",  "http://localhost:8080" },
      { "grant_type", "authorization_code" },
      { "access_type", "offline" },
    };
    var request = UnityWebRequest.Post (tokenUrl, content);
    yield return request.SendWebRequest();

    var json = JSON.Parse (request.downloadHandler.text);
    var token = json["access_token"].RawString();

    Debug.Log (token);
  }

実際のレスポンスは次のようになっています。リフレッシュトークンも取得できますがここではとりあえず使用しません。

{
 "access_token": "ya29.GltgB...",
 "token_type": "Bearer",
 "expires_in": 3600,
 "refresh_token": "1/hsjPU..."
}

JSONパーサーとしてSimpleJsonを利用します。。RawString()の中身は

public static class SimpleJsonUtility {
  public static string RawString(this JSONNode node) {
    var len = node.ToString ().Length - 2;
    return node.ToString ().Substring (1, len);
  }
}

パース結果をjson["access_token"].ToString()すると""abcdef""のようにダブルクオーテーションまで文字列に含まれてしまうのでその対応をしています。

チャットIDの取得

YouTube Live Streaming APIを使っていきます。

はじめにliveChatIdを取得します。

  IEnumerator Start() {
    // snip

    var url = "https://www.googleapis.com/youtube/v3/liveBroadcasts?part=snippet";
    url += "&id=" + "xxxxxxxxxxx";

    var req = UnityWebRequest.Get (url);
    req.SetRequestHeader ("Authorization", "Bearer " + token);
    yield return req.SendWebRequest();

    json = JSON.Parse (req.downloadHandler.text);
    var chatId = json["items"][0]["snippet"]["liveChatId"].RawString();

    Debug.Log (chatId);
  }

idhttps://www.youtube.com/watch?v=xxxxxxxxxxxxxxxxxxxxxxの部分を入れます。

対象にしている動画がライブ中でない(かそもそもライブでない)場合はliveChatIdがレスポンスの中に含まれません。

また、トークンを指定せずにアクセスすると

{
 "error": {
  "errors": [
   {
    "domain": "global",
    "reason": "required",
    "message": "Login Required",
    "locationType": "header",
    "location": "Authorization"
   }
  ],
  "code": 401,
  "message": "Login Required"
 }
}

このように怒られます。

コメントの取得

対象のチャットからコメントを取得します。

  IEnumerator Start() {
    // snip

    url = "https://www.googleapis.com/youtube/v3/liveChat/messages?part=snippet,authorDetails";
    url += "&liveChatId=" + chatId;

    req = UnityWebRequest.Get (url);
    req.SetRequestHeader ("Authorization", "Bearer " + token);
    yield return req.SendWebRequest();

    json = JSON.Parse (req.downloadHandler.text);
    var items = json["items"];

    foreach (var item in items) {
      var snip = item.Value ["snippet"];
      var author = item.Value["authorDetails"];
      Debug.Log (author ["displayName"].RawString () + ": "
        + snip ["displayMessage"].RawString());
    }
    Debug.Log (json["nextPageToken"]);
  }

コメントだけでなくコメント投稿者の名前やアイコンを取得することもできます。

何度もコメントを取得するような場合、page_tokenを指定することで前回取得分からの差分のみを取得できます。

コメントの利用

使いやすく書き直したもの
https://gist.github.com/n0mimono/00f8e68a8fa5e2f9b36a4fab30adcd22

コメントを取得してキャラを走らせてみます。

public class Rotater : MonoBehaviour {
  [SerializeField]
  Transform character;

  [SerializeField]
  YouTubeLive.YtlController ctrl;

  float tgt;
  float speed;

  void Start() {
    Application.runInBackground = true;

    ctrl.OnMessage += msg => {
      tgt += msg.text.Length * 5f;
    };
  }

  void Update() {
    speed = Mathf.Lerp (tgt, speed, Time.deltaTime);

    transform.Rotate (Vector3.up, speed * Time.deltaTime);

    var animator = character.GetComponent<Animator> ();
    animator.SetFloat ("Speed", speed);
  }
}

結果

wak2.gif

チャットに書き込むとキャラが加速します。