LoginSignup
25
22

More than 5 years have passed since last update.

UnityでYouTubeLiveのAPIを叩いてUIに変数を表示する

Last updated at Posted at 2018-01-30

最初に

ニコ生とYoutubeLiveで同時配信をしていて、ニコ生しかコメントを取得できていなかったため、YoutubeLiveからチャットを取得するプログラムを組みました。
一応下のコードで、YouTubeLiveからチャットが取得できます。
あと、その下の方で、何をしたのかちょっとずつ解説を書きます。
間違ってたらご指摘下さい。

あと、こんなクソ汚いコード使われたくないので、ちゃんとしたの、だれかunitypackageで、だしてくりー。。。
一応プロジェクト置いておくね。
https://github.com/platoronical/youtubelivecommentReciever
※色々あって、プロジェクトが変わりました。

コード!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using System.IO;
using MiniJSON;
using System;
using Newtonsoft.Json;
using UnityEngine.UI;



public class GetHttpSample : MonoBehaviour {


    //ここから使う

    private string apikey = "xxxx";//APIキー  

    private string searchBaseURI = "https://www.googleapis.com/youtube/v3/search?key=[APIkey]&part=snippet&channelId=";

    private string searchBaseChannnel = "UCBGdwqBtOebIhrtOPzOXj0w";//ここを書き換えるYoutubeチャンネル

    private string searchBaseStr = "&eventType=live&type=video";

    private string videoId;

    private string youtubeAPIbase = "https://www.googleapis.com/youtube/v3/";

    private string channnelSearch = "videos?part=liveStreamingDetails&id=";

    private string chatId;

    private string pagetoken = "&pageToken=";

    private string chatURIUp = "liveChat/messages?liveChatId=";

    private bool connectionflag = false;

    private string nextPageTokenstr = null;

    private string jsontext;

    //コメントと投稿時間だけ出るやつ
    private string chatURIbottom = "&part=snippet&hl=ja&maxResults=2000&fields=items/snippet/displayMessage,items/snippet/publishedAt,items/authorDetails/displayName&key=";//&part=snippet&hl=ja&maxResults=2000&fields=items/snippet/displayMessage,items/snippet/publishedAt&key=
    //全部出るやつ
    private string chatURIbottom2 = "&part=snippet,authorDetails&key=";

    [SerializeField]
    private GameObject canvas;

    // Use this for initialization
    void Start () {
        StartCoroutine(GetYoutubeAPI());
    }

    // Update is called once per frame
    void Update () {
    }

    private IEnumerator GetYoutubeAPI()
    {
        var urisample = searchBaseURI + searchBaseChannnel + searchBaseStr;

        UnityWebRequest liverequest = UnityWebRequest.Get(urisample);//testURI + apikey);
        yield return liverequest.SendWebRequest();

        if (liverequest.isHttpError || liverequest.isNetworkError)
        {
            Debug.Log(liverequest.error);
        }
        else
        {
            jsontext = liverequest.downloadHandler.text;
            //MiniJSON つかうー!!!!
            var mjson = (IDictionary)MiniJSON.Json.Deserialize(jsontext);

            var mitems = (IList)mjson["items"];
            var mid = (IDictionary)mitems[0];
            var sid = (IDictionary)mid["id"];
            string mvideoId = (string)sid["videoId"];

            //videoIdを取得
            videoId = (string)sid["videoId"];

            /*vs2017ならできる??
            var chatJsonObj = JsonConvert.DeserializeObject<dynamic>(jsontext);
            string videoId2 = chatJsonObj.items[0].id.videoId;
            Debug.Log("videoID2017 : " + videoId2);
            */           
            StartCoroutine(GetChatId());
        }
    }

    private IEnumerator GetChatId()
    {
        StopCoroutine(GetYoutubeAPI());

        //ChatIdを取得しにいくよ!!
        var searchChannel = youtubeAPIbase + channnelSearch + videoId + "&key=" + apikey;

        UnityWebRequest channelrequest = UnityWebRequest.Get(searchChannel);
        yield return channelrequest.SendWebRequest();

        var mchanjson = (IDictionary)Json.Deserialize(channelrequest.downloadHandler.text);

        var citems = (IList)mchanjson["items"];
        var cslsd = (IDictionary)citems[0];
        var clad = (IDictionary)cslsd["liveStreamingDetails"];
        string mvchatId = (string)clad["activeLiveChatId"];
        //chatIdを取得
        chatId = (string)clad["activeLiveChatId"];

        StartCoroutine(GetComment());
    }

    private IEnumerator GetComment()
    {
        StopCoroutine(GetChatId());
        yield return new WaitForSeconds(5.0f);

        //チャットを取りに行く!!!
        var chatURI = youtubeAPIbase + chatURIUp + chatId + pagetoken + nextPageTokenstr + chatURIbottom2 + apikey;

        UnityWebRequest connectChatrequest = UnityWebRequest.Get(chatURI);
        yield return connectChatrequest.SendWebRequest();

        var commentlogjson = (IDictionary)Json.Deserialize(connectChatrequest.downloadHandler.text);

        //このif文は全くの無意味
        if(nextPageTokenstr == (string)commentlogjson["nextPageToken"])
        {
            Debug.Log("sameToken");
        }
        else
        {
            nextPageTokenstr = (string)commentlogjson["nextPageToken"];

            var pageinfo = (IDictionary)commentlogjson["pageInfo"];
            int commentcount = int.Parse(pageinfo["totalResults"].ToString());

            //コメント分だけ描画
            for(var i = 0; i < (int)commentcount; i++)
            {
                GameObject cvn = Instantiate(canvas);

                var citems = (IList)commentlogjson["items"];
                var cslsd = (IDictionary)citems[i];
                var clad = (IDictionary)cslsd["snippet"];
                string message = (string)clad["displayMessage"];

                cvn.transform.Find("Description").gameObject.GetComponent<Text>().text = message;

                var author = (IDictionary)cslsd["authorDetails"];
                var dispName = (string)author["displayName"];

                cvn.transform.Find("Name").gameObject.GetComponent<Text>().text = dispName;

                float _x = UnityEngine.Random.Range(-400f, 400f);
                float _y = UnityEngine.Random.Range(-250f, 250f);
                cvn.transform.position = new Vector3(_x, _y, cvn.transform.position.z);
            }
        }
        StartCoroutine(stopWait());
    }

    IEnumerator stopWait()
    {
        yield return new WaitForSeconds(1f);
        StartCoroutine(GetComment());
    }
}

山盛りですが、ひとつづつ。
何をやりたいかということはこの前のエントリーとかぶりますが、まぁ。
https://qiita.com/platoronical/items/ba99be66d0cfb85e6038

構造!

今回はコルーチンの塊です。

private IEnumerator GetYoutubeAPI()
{
 //Youtubeへ接続しに行ってます
}
private IEnumerator GetChatId()
{
 //videoIdを基に、chatIdを探しに行っています。
}
private IEnumerator GetComment()
{
 //chatIdを基に、コメントを取得します。
}
IEnumerator stopWait()
{
 // StartCoroutine(GetComment()); で、コメントを再取得しにいっています。
}

JSON!

jsonのパースは、いろいろあってきれいな書き方が、VS2017から.NET4.6から出来るようです。

var chatJsonObj = JsonConvert.DeserializeObject<dynamic>(jsontext);
string videoId2 = chatJsonObj.items[0].id.videoId;
Debug.Log("videoID2017 : " + videoId2);

上記のようなコードで取れるようです。きれいでいいなー。
今回はそれを知らなかったので、MiniJSONというプラグイン使ってパースしています。

var mjson = (IDictionary)MiniJSON.Json.Deserialize(jsontext);

var mitems = (IList)mjson["items"];
var mid = (IDictionary)mitems[0];
var sid = (IDictionary)mid["id"];
string mvideoId = (string)sid["videoId"];            

さっきのコードと同じことをしてます。
今回はこのような形で、展開していくことにしました。
色々つまづきポイントはあったんですが、最後の最後に整数値へのキャストで、躓いたので紹介します。

int commentcount = int.Parse(pageinfo["totalResults"].ToString());

もうね、キャストの仕方がありすぎて、どうしたものかと思った。

YoutubeAPI!

おまけ程度だけど、紹介します。
多分一番意味分かんないのは、uriを作っているところで、もっとスマートに出来るでしょーって感じなんだけど、それは置いておいて。
pageTokenだと思います。
コメント差分が欲しかったため、どうしたものかと思って部屋の中で土下座していると、API側で教えてくれることがわかり。
nextPageTokenと言うものを取得して、
&pageToken=GNvdg86Q_9gCIJi49NOQ_9gC
みたいなパラメーターを、URIにつけてやると、差分だけ取得できます。
で、このパラメーターはNULLでもオッケーなので、初回起動時は、
&pageToken=&key=xxxxx
みたいな感じで何もないと全件取得してくれます。

UnityWebRequest connectChatrequest = UnityWebRequest.Get(chatURI);
yield return connectChatrequest.SendWebRequest();
var commentlogjson = (IDictionary)Json.Deserialize(connectChatrequest.downloadHandler.text);
//中略
nextPageTokenstr = (string)commentlogjson["nextPageToken"];

上記のようなことをして、トークン取得してます。
URI作るときは、以下のように作っています。
各、変数は、まぁ、書いてるので…。

//チャットを取りに行く!!!
var chatURI = youtubeAPIbase + chatURIUp + chatId + pagetoken + nextPageTokenstr + chatURIbottom2 + apikey;

何度も取得する!

コルーチンでやっています。

private IEnumerator GetComment()
{
   yield return new WaitForSeconds(5.0f);
   StartCoroutine(stopWait());
}
IEnumerator stopWait()
{
   yield return new WaitForSeconds(1f);
   StartCoroutine(GetComment());
}

上記のようにコルーチンをループさせてます。
正しいのかは知りません。

UIに表示

prefabで、CanvasにTextを2つ貼り付けたものを用意します。
読み込めるフィールドを、用意して使います。

[SerializeField]
private GameObject canvas;
//中略
//使うときに生成
GameObject cvn = Instantiate(canvas);
//パーッ寿司ながら中身取り出しにいく。
var citems = (IList)commentlogjson["items"];
var cslsd = (IDictionary)citems[i];
var clad = (IDictionary)cslsd["snippet"];
string message = (string)clad["displayMessage"];
//本文を取得
cvn.transform.Find("Description").gameObject.GetComponent<Text>().text = message;

var author = (IDictionary)cslsd["authorDetails"];
var dispName = (string)author["displayName"];
//名前をつける
cvn.transform.Find("Name").gameObject.GetComponent<Text>().text = dispName;

最後に

ここ二日ほど付き合ってくれた様々な方、ありがとうございました…。
さて、これを本体側に組み込んでいくか…。

よかったら、Youtubeチャンネル登録お願いします。
次回の放送はわかりませんが、チャットが取得できてるはず!
https://www.youtube.com/channel/UC8bcQkG7UiUlxb3wcqrs86Q

きっと!

25
22
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
22