From 9b8d00d1845a29cb935764a79a949d8247090a33 Mon Sep 17 00:00:00 2001
From: Toastie <toastie@toastiet0ast.com>
Date: Thu, 20 Mar 2025 22:44:39 +1300
Subject: [PATCH] fixed youtube stream notifications incase invalid channel was
 provided

---
 .../StreamNotifications/NotifChecker.cs       |   7 +-
 .../Providers/YouTubeProvider.cs              | 108 +++++++++---------
 2 files changed, 59 insertions(+), 56 deletions(-)

diff --git a/src/EllieBot/Modules/Searches/_common/StreamNotifications/NotifChecker.cs b/src/EllieBot/Modules/Searches/_common/StreamNotifications/NotifChecker.cs
index 3ddebec..2c68d5a 100644
--- a/src/EllieBot/Modules/Searches/_common/StreamNotifications/NotifChecker.cs
+++ b/src/EllieBot/Modules/Searches/_common/StreamNotifications/NotifChecker.cs
@@ -63,20 +63,19 @@ public class NotifChecker
                                 .ToDictionary(x => x.Key.Name, x => x.Value));
 
                     var newStreamData = await oldStreamDataDict
-                        .Select(x =>
+                        .Select(async x =>
                         {
                             // get all stream data for the streams of this type
                             if (_streamProviders.TryGetValue(x.Key,
                                     out var provider))
                             {
-                                return provider.GetStreamDataAsync(x.Value
+                                return await provider.GetStreamDataAsync(x.Value
                                     .Select(entry => entry.Key)
                                     .ToList());
                             }
 
                             // this means there's no provider for this stream data, (and there was before?)
-                            return Task.FromResult<IReadOnlyCollection<StreamData>>(
-                                new List<StreamData>());
+                            return [];
                         })
                         .WhenAll();
 
diff --git a/src/EllieBot/Modules/Searches/_common/StreamNotifications/Providers/YouTubeProvider.cs b/src/EllieBot/Modules/Searches/_common/StreamNotifications/Providers/YouTubeProvider.cs
index 0983e64..aa761cd 100644
--- a/src/EllieBot/Modules/Searches/_common/StreamNotifications/Providers/YouTubeProvider.cs
+++ b/src/EllieBot/Modules/Searches/_common/StreamNotifications/Providers/YouTubeProvider.cs
@@ -1,12 +1,7 @@
-using System.Net;
-using EllieBot.Db.Models;
-using EllieBot.Services;
+using EllieBot.Db.Models;
 using System.Text.RegularExpressions;
 using System.Net.Http.Json;
-using System.Text.Json;
 using System.Text.Json.Serialization;
-using System.Xml.Linq;
-using AngleSharp.Browser;
 
 namespace EllieBot.Modules.Searches.Common.StreamNotifications.Providers;
 
@@ -113,56 +108,65 @@ public sealed partial class YouTubeProvider : Provider
     /// <returns><see cref="StreamData"/> of the channel. Null if none found</returns>
     public override async Task<StreamData?> GetStreamDataAsync(string channelId)
     {
-        var instances = _scs.Data.InvidiousInstances;
-
-        if (instances is not { Count: > 0 })
-            return null;
-
-        var invInstance = instances[_rng.Next(0, instances.Count)];
-        var client = _httpFactory.CreateClient();
-        client.BaseAddress = new Uri(invInstance);
-
-        var channel = await client.GetFromJsonAsync<InvidiousChannelResponse>($"/api/v1/channels/{channelId}");
-        if (channel is null)
-            return null;
-
-        var response =
-            await client.GetFromJsonAsync<InvChannelStreamsResponse>($"/api/v1/channels/{channelId}/streams");
-        if (response is null)
-            return null;
-
-        var vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming && x.LengthSeconds == 0);
-        var isLive = false;
-        if (vid is null)
+        try
         {
-            vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming);
+            var instances = _scs.Data.InvidiousInstances;
+
+            if (instances is not { Count: > 0 })
+                return null;
+
+            var invInstance = instances[_rng.Next(0, instances.Count)];
+            var client = _httpFactory.CreateClient();
+            client.BaseAddress = new Uri(invInstance);
+
+            var channel = await client.GetFromJsonAsync<InvidiousChannelResponse>($"/api/v1/channels/{channelId}");
+            if (channel is null)
+                return null;
+
+            var response =
+                await client.GetFromJsonAsync<InvChannelStreamsResponse>($"/api/v1/channels/{channelId}/streams");
+            if (response is null)
+                return null;
+
+            var vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming && x.LengthSeconds == 0);
+            var isLive = false;
+            if (vid is null)
+            {
+                vid = response.Videos.FirstOrDefault(x => !x.IsUpcoming);
+            }
+            else
+            {
+                isLive = true;
+            }
+
+            if (vid is null)
+                return null;
+
+            var avatarUrl = channel?.AuthorThumbnails?.Select(x => x.Url).LastOrDefault();
+
+            return new StreamData()
+            {
+                Game = "Livestream",
+                Name = vid.Author,
+                Preview = vid.Thumbnails
+                    .Skip(1)
+                    .Select(x => "https://i.ytimg.com/" + x.Url)
+                    .FirstOrDefault(),
+                Title = vid.Title,
+                Viewers = vid.ViewCount,
+                AvatarUrl = avatarUrl,
+                IsLive = isLive,
+                StreamType = FollowedStream.FType.Youtube,
+                StreamUrl = "https://youtube.com/watch?v=" + vid.VideoId,
+                UniqueName = vid.AuthorId,
+            };
         }
-        else
+        catch (Exception ex)
         {
-            isLive = true;
-        }
-
-        if (vid is null)
+            Log.Warning(ex, "Unable to get stream data for a youtube channel {ChannelId}", channelId);
+            _failingStreams.TryAdd(channelId, DateTime.UtcNow);
             return null;
-
-        var avatarUrl = channel?.AuthorThumbnails?.Select(x => x.Url).LastOrDefault();
-
-        return new StreamData()
-        {
-            Game = "Livestream",
-            Name = vid.Author,
-            Preview = vid.Thumbnails
-                .Skip(1)
-                .Select(x => "https://i.ytimg.com/" + x.Url)
-                .FirstOrDefault(),
-            Title = vid.Title,
-            Viewers = vid.ViewCount,
-            AvatarUrl = avatarUrl,
-            IsLive = isLive,
-            StreamType = FollowedStream.FType.Youtube,
-            StreamUrl = "https://youtube.com/watch?v=" + vid.VideoId,
-            UniqueName = vid.AuthorId,
-        };
+        }
     }
 
     /// <summary>