using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using Flurl; using MoreLinq; using Sony.Filtr.Contracts.Abstractions; using Sony.Filtr.Contracts.Entities; using Sony.Filtr.Utility; namespace Sony.Filtr.YouTube.Api { public class YouTubeApi { private readonly HttpClient _httpClient; private readonly IServiceAccountManager _serviceAccountManager; private readonly GoogleOAuth2Api _googleOAuth2Api; private readonly IRateLimiter _rateLimiter; public YouTubeApi(IServiceAccountManager serviceAccountManager, GoogleOAuth2Api googleOAuth2Api, IRateLimiter rateLimiter, string key) { _serviceAccountManager = serviceAccountManager; _googleOAuth2Api = googleOAuth2Api; _rateLimiter = rateLimiter; _httpClient = BuildClient(rateLimiter, key); } private static HttpClient BuildClient(IRateLimiter rateLimiter, string key) { var rateLimitedHandler = new RateLimitedHttpMessageHandler(new HttpClientHandler() {AutomaticDecompression = DecompressionMethods.GZip}, rateLimiter); var apiKeyHandler = new ApiKeyHttpClientHandler(key) { InnerHandler = rateLimitedHandler }; HttpClient client = new HttpClient(apiKeyHandler); client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); client.DefaultRequestHeaders.UserAgent.ParseAdd("Filtr/1.0 (gzip)"); return client; } protected YouTubeApi(HttpClient httpClient) { _httpClient = httpClient; } protected HttpClient GetDefaultClient() { return _httpClient; } public async Task GetChannelsAsync(List channelIds) { var url = new Url("https://www.googleapis.com/youtube/v3/channels"); url.SetQueryParam("part", "snippet,contentDetails,statistics"); url.SetQueryParam("id", string.Join(",", channelIds)); var response = await GetDefaultClient().GetAsync(url); var playlistResponse = await response.Content.ReadAsAsync(); return playlistResponse; } public async Task GetChannelSectionsAsync(string channelId) { var url = new Url("https://www.googleapis.com/youtube/v3/channelSections"); url.SetQueryParam("part", $"snippet,contentDetails"); url.SetQueryParam("channelId", channelId); var response = await GetDefaultClient().GetAsync(url); var channelSectionResponse = await response.Content.ReadAsAsync(); return channelSectionResponse; } public async Task GetPlaylistAsync(string playlistId) { var url = new Url("https://www.googleapis.com/youtube/v3/playlists"); url.SetQueryParam("part", "snippet"); url.SetQueryParam("id", playlistId); var response = await GetDefaultClient().GetAsync(url); var playlistResponse = await response.Content.ReadAsAsync(); return playlistResponse.items.FirstOrDefault(); } public async Task GetPlaylistsForChannelAsync(string channelId, int maxPlaylistBatchSize = _MaxPlaylistBatchSize, string nextPageToken = null) { var url = new Url("https://www.googleapis.com/youtube/v3/playlists"); url.SetQueryParam("part", "snippet"); url.SetQueryParam("channelId", channelId); url.SetQueryParam("maxResults", maxPlaylistBatchSize); if (!string.IsNullOrEmpty(nextPageToken)) { url.SetQueryParam("pageToken", nextPageToken); } var response = await GetDefaultClient().GetAsync(url); var playlistResponse = await response.Content.ReadAsAsync(); return playlistResponse; } public async Task GetVideosAsync(IEnumerable videoIds) { var url = new Url("https://www.googleapis.com/youtube/v3/videos"); url.SetQueryParam("part", "id,snippet,contentDetails,liveStreamingDetails,localizations,player,recordingDetails,statistics,status,topicDetails"); url.SetQueryParam("id", string.Join(",", videoIds)); var response = await GetDefaultClient().GetAsync(url); var videoListResponse = await response.Content.ReadAsAsync(); return videoListResponse; } public async Task> GetAllVideosAsync(List videoIds) { List fullVideoItems = new List(); foreach (var videoBatch in videoIds.Batch(50)) { var videos = await GetVideosAsync(videoBatch); fullVideoItems.AddRange(videos.items); } return fullVideoItems; } public async Task> GetAllPlaylistItemsAsync(string playlistId) { var pageItemsResponse = await GetPlaylistItemsAsync(playlistId, _MaxPlaylistBatchSize); if (pageItemsResponse == null) return null; var allItems = new List(); allItems.AddRange(pageItemsResponse.items); while (!string.IsNullOrWhiteSpace(pageItemsResponse.nextPageToken)) { pageItemsResponse = await GetPlaylistItemsAsync(playlistId, _MaxPlaylistBatchSize, pageItemsResponse.nextPageToken); allItems.AddRange(pageItemsResponse.items); } return allItems; } private const int _MaxPlaylistBatchSize = 50; public async Task> GetAllPlaylistsFromChannelAsync(string channelId) { var pageItemsResponse = await GetPlaylistsForChannelAsync(channelId, _MaxPlaylistBatchSize); if (pageItemsResponse == null) return null; var allItems = new List(); allItems.AddRange(pageItemsResponse.items); while (!string.IsNullOrWhiteSpace(pageItemsResponse.nextPageToken)) { pageItemsResponse = await GetPlaylistsForChannelAsync(channelId, _MaxPlaylistBatchSize, pageItemsResponse.nextPageToken); allItems.AddRange(pageItemsResponse.items); } return allItems; } public async Task GetPlaylistItemsAsync(string playlistId, int amount = 50, string pageToken = null) { var url = new Url("https://www.googleapis.com/youtube/v3/playlistItems"); url.SetQueryParam("part", "id,snippet,status"); url.SetQueryParam("playlistId", playlistId); url.SetQueryParam("maxResults", amount); //url.SetQueryParam("key", accessToken); if (!string.IsNullOrWhiteSpace(pageToken)) { url.SetQueryParam("pageToken", pageToken); } var response = await GetDefaultClient().GetAsync(url); if (response.StatusCode == HttpStatusCode.NotFound) { return null; } if (!response.IsSuccessStatusCode) { throw YoutubeApiException.FromHttpResponseMessage(response, "GetPlaylistItemsAsync failed"); } var playlistItemsResponse = await response.Content.ReadAsAsync(); return playlistItemsResponse; } public async Task SearchVideosAsync(string searchQuery, int maxResults, CancellationToken cancellationToken) { var url = new Url("https://www.googleapis.com/youtube/v3/search"); url.SetQueryParam("part", "snippet"); url.SetQueryParam("type", "video"); url.SetQueryParam("q", searchQuery); url.SetQueryParam("maxResults", maxResults); var client = GetDefaultClient(); var response = await client.GetAsync(url, cancellationToken); return await response.Content.ReadAsAsync(cancellationToken); } public async Task SearchMusicVideosByChannelAsync(string channelId, int maxResults, CancellationToken cancellationToken) { var url = new Url("https://www.googleapis.com/youtube/v3/search"); url.SetQueryParam("part", "snippet"); url.SetQueryParam("type", "video"); url.SetQueryParam("videoCategoryId", 10); url.SetQueryParam("channelId", channelId); url.SetQueryParam("maxResults", maxResults); var client = GetDefaultClient(); var response = await client.GetAsync(url, cancellationToken); return await response.Content.ReadAsAsync(cancellationToken); } public AuthenticatedYoutubeApi GetAuthenticatedYoutubeApi(ServiceAccount serviceAccount) { return new AuthenticatedYoutubeApi(serviceAccount, _serviceAccountManager, _googleOAuth2Api, _rateLimiter); } } }