概要
Youtube公式に提供されているAPIを利用して、Youtubeライブのコメント、スパチャを取得します。
下記は作りかけの機能ですが、APIを利用するとこんなの作れます。
UnityでYoutubeLiveのコメビュをつくってみるテスト。 pic.twitter.com/TZbgfsZltV
— unagi (@UnagiHuman) December 22, 2020
Youtube APIを利用出来るようにする
下記URLを参考に以下の3つを作成します。
- クライアントID
- クライアントシークレット
- APIキー
https://dev.classmethod.jp/articles/oauth2-youtube-data-api/
YouTube Data API Client Library for .NETのインストール
YouTube Data API Client Library for .NETはNugetにありますが、UnityでNugetを利用するにはNuget for Unityをインストールします。
そして、Nuget for Unityで「Google.Apis.Youtube.v3」を検索してインストールします。
Oauthの実装
下記コードをGameObjectにアタッチして実行するとOAuthできます。_cliendIdと_clientSecretは↑で手に入れたものを入力します(下記コードの実行にはUniTaskのインストールが別途必要です)
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using UnityEngine;
using System.Threading;
using Cysharp.Threading.Tasks;
using Google.Apis.Auth.OAuth2;
using Google.Apis.YouTube.v3;
public class YoutubeAuthorizeSample : MonoBehaviour
{
[SerializeField] private string _cliendId;
[SerializeField] private string _clientSecret;
[SerializeField] private string _user = "SampleGame";
private void Start()
{
Authorize().Forget();
}
async UniTask Authorize()
{
YoutubeAuthorize.AuthorizeAsync(_cliendId, _clientSecret, _user).Forget();
}
private NameValueCollection ParseQueryString(string s)
{
NameValueCollection nvc = new NameValueCollection();
// remove anything other than query string from url
if (s.Contains("?"))
{
s = s.Substring(s.IndexOf('?') + 1);
}
foreach (string vp in Regex.Split(s, "&"))
{
string[] singlePair = Regex.Split(vp, "=");
if (singlePair.Length == 2)
{
nvc.Add(singlePair[0], singlePair[1]);
}
else
{
// only one key with no value specified in query string
nvc.Add(singlePair[0], string.Empty);
}
}
return nvc;
}
async UniTask<UserCredential> AuthorizeAsync(string cliendId, string clientSecret, string user)
{
var secrets = new ClientSecrets
{
ClientId = cliendId,
ClientSecret = clientSecret
};
var scopes = new string[]
{
YouTubeService.Scope.YoutubeReadonly
};
return await GoogleWebAuthorizationBroker.AuthorizeAsync(secrets, scopes, user, CancellationToken.None);
}
}
コメント、スーパーチャットの取得
下記コードをGameObjectにアタッチし、_apiKeyに↑のAPIキー、_liveChatURLにはコメントを取得したいYoutubeライブのURLを入力して実行すると、OnReceiveLiveChatMessageにコメントとスパチャの情報が渡ります。コメント取得はポーリング式で、APIから取得できるLiveChatMessageのPollingIntervalMillis間隔で取得できます。PollingIntervalMillisはサーバーの状況によって変動するらしいですが、大体が5秒程度です。
using System;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using UnityEngine;
using System.Threading;
using Cysharp.Threading.Tasks;
using Google.Apis.Services;
using Google.Apis.YouTube.v3;
using Google.Apis.YouTube.v3.Data;
using UniRx;
using UnityEngine.UI;
public class YoutubeChatSample : MonoBehaviour
{
public System.Action<LiveChatMessage> OnReceiveLiveChatMessage;
[SerializeField] private string _apiKey;
[SerializeField] private string _liveChatURL;
void Start()
{
var cancel = this.GetCancellationTokenOnDestroy();
ConnectAsync(cancel).Forget();
}
async UniTask ConnectAsync(CancellationToken cancel)
{
var youtubeService = new YouTubeService(new BaseClientService.Initializer
{
ApiKey = _apiKey
});
var response = await GetVideoListResponse(_liveChatURL, youtubeService);
if (response.Items.Count > 0)
{
var item = response.Items[0];
var chatId = item.LiveStreamingDetails.ActiveLiveChatId;
var livechatRequest = youtubeService.LiveChatMessages.List(chatId, new string[] { "snippet", "authorDetails" });
livechatRequest.PageToken = null;
var livechatResponse = await livechatRequest.ExecuteAsync();
foreach (var liveChatItem in livechatResponse.Items)
{
OnReceiveLiveChatMessage?.Invoke(liveChatItem);
}
await UniTask.Delay((int)livechatResponse.PollingIntervalMillis);
var nextPageToken = livechatResponse.NextPageToken;
while (true)
{
if (cancel.IsCancellationRequested)
{
youtubeService.Dispose();
break;
}
try
{
livechatRequest.PageToken = nextPageToken;
livechatResponse = await livechatRequest.ExecuteAsync();
foreach (LiveChatMessage liveChatItem in livechatResponse.Items)
{
OnReceiveLiveChatMessage?.Invoke(liveChatItem);
}
}
catch (Exception e)
{
Debug.Log(e.Message);
}
nextPageToken = livechatResponse.NextPageToken;
Debug.Log((int)livechatResponse.PollingIntervalMillis);
await UniTask.Delay((int)livechatResponse.PollingIntervalMillis, false, PlayerLoopTiming.Update, cancel);
}
}
}
async UniTask<VideoListResponse> GetVideoListResponse(string liveChatURL, YouTubeService youtubeService)
{
var uri = new Uri(_liveChatURL);
var qscoll = ParseQueryString(uri.Query);
var videoId = qscoll["v"];
var videosList = youtubeService.Videos.List("LiveStreamingDetails");
videosList.Id = videoId;
try
{
return await videosList.ExecuteAsync();
}
catch (Exception e)
{
throw e;
}
}
private NameValueCollection ParseQueryString(string s)
{
NameValueCollection nvc = new NameValueCollection();
// remove anything other than query string from url
if (s.Contains("?"))
{
s = s.Substring(s.IndexOf('?') + 1);
}
foreach (string vp in Regex.Split(s, "&"))
{
string[] singlePair = Regex.Split(vp, "=");
if (singlePair.Length == 2)
{
nvc.Add(singlePair[0], singlePair[1]);
}
else
{
// only one key with no value specified in query string
nvc.Add(singlePair[0], string.Empty);
}
}
return nvc;
}
}