From 2ec03dda68acaf330d7a74beb0a6eaec41716f61 Mon Sep 17 00:00:00 2001 From: laurencee Date: Sun, 19 Nov 2017 16:39:18 +1000 Subject: [PATCH] Allow adding youtube channels by channelid Fix bug with only showing the first offline youtube channel in the livestream datagrid --- ExternalAPIs.Tests/YoutubeClientShould.cs | 8 +++---- .../Dto/QueryRoot/GetChannelIdByNameRoot.cs | 6 +----- .../Youtube/IYoutubeReadonlyClient.cs | 7 +++---- ExternalAPIs/Youtube/RequestConstants.cs | 2 +- ExternalAPIs/Youtube/YoutubeReadonlyClient.cs | 10 ++++----- GlobalAssemblyInfo.cs | 4 ++-- .../Model/ApiClients/LivestreamQueryResult.cs | 5 +++++ .../Model/ApiClients/YoutubeApiClient.cs | 21 ++++++++++++------- 8 files changed, 34 insertions(+), 29 deletions(-) diff --git a/ExternalAPIs.Tests/YoutubeClientShould.cs b/ExternalAPIs.Tests/YoutubeClientShould.cs index d9ec1ce..c7daa0c 100644 --- a/ExternalAPIs.Tests/YoutubeClientShould.cs +++ b/ExternalAPIs.Tests/YoutubeClientShould.cs @@ -35,18 +35,18 @@ public async Task ThrowNotFoudExceptionForInvalidVideoId() } [Fact, Trait("Category", "LocalOnly")] - public async Task GetChannelIdFromChannelName() + public async Task GetChannelIdFromUsername() { const string channelName = "LoLChampSeries"; - var channelId = await sut.GetChannelIdFromChannelName(channelName); - + var channelId = await sut.GetChannelIdFromUsername(channelName); + Assert.NotNull(channelId); } [Fact, Trait("Category", "LocalOnly")] public async Task GetLiveVideos() { - // channelId of the value returned by the test "GetChannelIdFromChannelName" + // channelId of the value returned by the test "GetChannelIdFromUsername" const string channelId = "UCvqRdlKsE5Q8mf8YXbdIJLw"; var onlineVideos = await sut.GetLivestreamVideos(channelId); diff --git a/ExternalAPIs/Youtube/Dto/QueryRoot/GetChannelIdByNameRoot.cs b/ExternalAPIs/Youtube/Dto/QueryRoot/GetChannelIdByNameRoot.cs index 786eccd..72d7d61 100644 --- a/ExternalAPIs/Youtube/Dto/QueryRoot/GetChannelIdByNameRoot.cs +++ b/ExternalAPIs/Youtube/Dto/QueryRoot/GetChannelIdByNameRoot.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; using Newtonsoft.Json; namespace ExternalAPIs.Youtube.Dto.QueryRoot diff --git a/ExternalAPIs/Youtube/IYoutubeReadonlyClient.cs b/ExternalAPIs/Youtube/IYoutubeReadonlyClient.cs index 88f5739..c73b357 100644 --- a/ExternalAPIs/Youtube/IYoutubeReadonlyClient.cs +++ b/ExternalAPIs/Youtube/IYoutubeReadonlyClient.cs @@ -7,13 +7,12 @@ namespace ExternalAPIs.Youtube public interface IYoutubeReadonlyClient { /// A channel in youtube can have multiple livestreams running at one time - /// A channel id discovered from querying - /// - /// Task GetLivestreamVideos(string channelId, CancellationToken cancellationToken = default(CancellationToken)); - Task GetChannelIdFromChannelName(string channelName, CancellationToken cancellationToken = default(CancellationToken)); + /// Discover the channel id given a username + Task GetChannelIdFromUsername(string userName, CancellationToken cancellationToken = default(CancellationToken)); + /// Get the details for a youtube videoid Task GetLivestreamDetails(string videoId, CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/ExternalAPIs/Youtube/RequestConstants.cs b/ExternalAPIs/Youtube/RequestConstants.cs index d1f1607..d50742d 100644 --- a/ExternalAPIs/Youtube/RequestConstants.cs +++ b/ExternalAPIs/Youtube/RequestConstants.cs @@ -7,6 +7,6 @@ public static class RequestConstants public const string VideoLivestreamDetails = GoogleApiRoot + @"videos?part=LiveStreamingDetails&key=" + API_KEY; public const string VideoSnippet = GoogleApiRoot + @"videos?part=Snippet&key=" + API_KEY; public const string SearchChannelLiveVideos = GoogleApiRoot + @"search?type=video&eventType=live&part=snippet,id&channelId={0}&key=" + API_KEY; - public const string GetChannelIdByName = GoogleApiRoot + @"channels?forUsername={0}&part=id&key=" + API_KEY; + public const string GetChannelIdByUsername = GoogleApiRoot + @"channels?forUsername={0}&part=id&key=" + API_KEY; } } \ No newline at end of file diff --git a/ExternalAPIs/Youtube/YoutubeReadonlyClient.cs b/ExternalAPIs/Youtube/YoutubeReadonlyClient.cs index eacbb7b..fd562b3 100644 --- a/ExternalAPIs/Youtube/YoutubeReadonlyClient.cs +++ b/ExternalAPIs/Youtube/YoutubeReadonlyClient.cs @@ -20,15 +20,15 @@ public class YoutubeReadonlyClient : IYoutubeReadonlyClient return searchChannelLiveVideos; } - public async Task GetChannelIdFromChannelName(string channelName, CancellationToken cancellationToken = default(CancellationToken)) + public async Task GetChannelIdFromUsername(string userName, CancellationToken cancellationToken = default(CancellationToken)) { - if (IsNullOrWhiteSpace(channelName)) - throw new ArgumentException("Argument is null or whitespace", nameof(channelName)); + if (IsNullOrWhiteSpace(userName)) + throw new ArgumentException("Argument is null or whitespace", nameof(userName)); - var request = RequestConstants.GetChannelIdByName.Replace("{0}", channelName); + var request = RequestConstants.GetChannelIdByUsername.Replace("{0}", userName); var channelDetails = await HttpClientExtensions.ExecuteRequest(request, cancellationToken); if (channelDetails.Items == null || channelDetails.Items.Count == 0) - throw new HttpRequestWithStatusException(HttpStatusCode.BadRequest, "Channel name not found " + channelName); + throw new HttpRequestWithStatusException(HttpStatusCode.BadRequest, "No channel found for username" + userName); return channelDetails.Items?.FirstOrDefault()?.Id; } diff --git a/GlobalAssemblyInfo.cs b/GlobalAssemblyInfo.cs index 8efa9f1..a71dab6 100644 --- a/GlobalAssemblyInfo.cs +++ b/GlobalAssemblyInfo.cs @@ -22,5 +22,5 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.11.0.0")] -[assembly: AssemblyFileVersion("2.11.0.0")] \ No newline at end of file +[assembly: AssemblyVersion("2.11.1.0")] +[assembly: AssemblyFileVersion("2.11.1.0")] \ No newline at end of file diff --git a/Livestream.Monitor/Model/ApiClients/LivestreamQueryResult.cs b/Livestream.Monitor/Model/ApiClients/LivestreamQueryResult.cs index ea4ad1d..96b09c2 100644 --- a/Livestream.Monitor/Model/ApiClients/LivestreamQueryResult.cs +++ b/Livestream.Monitor/Model/ApiClients/LivestreamQueryResult.cs @@ -20,5 +20,10 @@ public LivestreamQueryResult(ChannelIdentifier channelIdentifier) public bool IsSuccess => FailedQueryException == null; public FailedQueryException FailedQueryException { get; set; } + + public override string ToString() + { + return $"{ChannelIdentifier} - IsSuccess: {IsSuccess}"; + } } } \ No newline at end of file diff --git a/Livestream.Monitor/Model/ApiClients/YoutubeApiClient.cs b/Livestream.Monitor/Model/ApiClients/YoutubeApiClient.cs index 22b1d22..e1ce8c9 100644 --- a/Livestream.Monitor/Model/ApiClients/YoutubeApiClient.cs +++ b/Livestream.Monitor/Model/ApiClients/YoutubeApiClient.cs @@ -113,15 +113,15 @@ public Task> GetUserFollows(string userName) throw new NotImplementedException(); } - private async Task GetChannelIdByChannelName(string channelName, CancellationToken cancellationToken) + private async Task GetChannelIdByUsername(string userName, CancellationToken cancellationToken) { - string channelId = (string)cache.Get(channelName); + string channelId = (string)cache.Get(userName); if (channelId != null) return channelId; - channelId = await youtubeClient.GetChannelIdFromChannelName(channelName, cancellationToken); + channelId = await youtubeClient.GetChannelIdFromUsername(userName, cancellationToken); // the id will never change so cache it forever once it's found - cache.Add(channelName, channelId, DateTimeOffset.MaxValue); + cache.Add(userName, channelId, DateTimeOffset.MaxValue); return channelId; } @@ -135,7 +135,12 @@ private async Task> QueryChannels( try { - var channelId = await GetChannelIdByChannelName(channelIdentifier.ChannelId, cancellationToken); + string channelId; + if (channelIdentifier.ChannelId.StartsWith("UC") || channelIdentifier.ChannelId.StartsWith("HC")) + channelId = channelIdentifier.ChannelId; + else + channelId = await GetChannelIdByUsername(channelIdentifier.ChannelId, cancellationToken); + var videoIds = await GetVideoIdsByChannelId(channelId, cancellationToken); var livestreamModels = await GetLivestreamModels(channelIdentifier, videoIds, cancellationToken); queryResults.AddRange(livestreamModels.Select(x => new LivestreamQueryResult(channelIdentifier) @@ -150,7 +155,7 @@ private async Task> QueryChannels( { queryResults.Add(new LivestreamQueryResult(channelIdentifier) { - LivestreamModel = new LivestreamModel("offline", channelIdentifier) + LivestreamModel = new LivestreamModel("offline-" + channelIdentifier.ChannelId, channelIdentifier) { DisplayName = channelIdentifier.ChannelId, Description = "[Offline youtube stream]", @@ -208,12 +213,12 @@ private async Task> GetLivestreamModels(ChannelIdentifier } retryCount++; } - + if (livestreamDetails == null) continue; var snippet = videoRoot.Items?.FirstOrDefault()?.Snippet; if (snippet == null) continue; - + var livestreamModel = new LivestreamModel(videoId, channelIdentifier) { Live = snippet.LiveBroadcastContent != "none" }; if (!livestreamModel.Live) continue;