BlazorのStream Renderingを使ってYoutubeの動画から取得した大量のコメントを表示してみようと思います。
実行環境
- Visual Studio 2022 17.9.6
- .NET 8
View
後述のCommentServiceを使ってコメントを取得します。取得後はtableとバインドしているフィールドにコメントを追加しStateHasChange()を呼びます。StateHasChange()を呼ぶことで更新した分のHTMLがサーバからクライアントに送信されます。
@page "/comment-getter" @using YoutubeCommentGetter.Core @attribute [StreamRendering] @inject CommentService CommentService <table class="table"> @foreach (var comment in _comments) { <tr class="table-light"><td>@comment</td></tr> } </table> @code { private readonly List<string> _comments = new(); // 動画IDはクエリパラメータで指定する [SupplyParameterFromQuery] public string? VideoId { get; set; } protected override async Task OnInitializedAsync() { await foreach (var commentsPerPage in CommentService.GetAsync(VideoId)) { // コメントを100件ずつ取得して更新 _comments.InsertRange(0, commentsPerPage); StateHasChanged(); // 画面の変化が分かりやすいように少し間を置く await Task.Delay(500); } } }
Logic
APIの仕様上、コメントは最大で100件ずつしか取得できないようになっています。なので100件ずつコメントをyield returnして消費側でコメントを100件ずつ描画できるようにしました。
public class CommentService(IOptions<GoogleApiSettings> options) { private readonly GoogleApiSettings _settings = options.Value; public async IAsyncEnumerable<IEnumerable<string>> GetAsync(string videoId) { var youtubeService = new YouTubeService(new BaseClientService.Initializer() { ApiKey = _settings.ApiKey, ApplicationName = _settings.ApplicationName }); var commentThreadsRequest = youtubeService.CommentThreads.List("snippet"); commentThreadsRequest.VideoId = videoId; commentThreadsRequest.TextFormat = CommentThreadsResource.ListRequest.TextFormatEnum.PlainText; commentThreadsRequest.Order = CommentThreadsResource.ListRequest.OrderEnum.Time; commentThreadsRequest.MaxResults = 100; var nextPageToken = string.Empty; while (nextPageToken is not null) { var result = new List<string>(); commentThreadsRequest.PageToken = nextPageToken; var response = await commentThreadsRequest.ExecuteAsync(); foreach (var commentThread in response.Items) { // コメントの取得 var commentSnippet = commentThread.Snippet.TopLevelComment.Snippet; result.Add(commentSnippet.TextDisplay); // リプライの取得 if (commentThread.Replies is null) continue; result.AddRange(commentThread.Replies.Comments.Select(reply => reply.Snippet).Select(replySnippet => replySnippet.TextDisplay)); } nextPageToken = response.NextPageToken; yield return result; } } }
動かしてみる
こんな感じになりました。
HTTP/2のストリームを使って通信するので1リクエスト内でデータをやり取りしています。devtoolsを見るとやたら長い通信をしているのがあるのが分かります。