From 3a30e73f7cdaaf6407d4ef87dbd592e80c62ff81 Mon Sep 17 00:00:00 2001 From: vycdev2 Date: Sat, 29 Aug 2026 02:04:08 +0000 Subject: [PATCH] fix: bypass YouTube consent redirects --- Morpheus.Tests/YoutubeUtilsTests.cs | 10 +++++----- Utilities/YoutubeUtils.cs | 9 +++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Morpheus.Tests/YoutubeUtilsTests.cs b/Morpheus.Tests/YoutubeUtilsTests.cs index c16068e..c58c3b0 100644 --- a/Morpheus.Tests/YoutubeUtilsTests.cs +++ b/Morpheus.Tests/YoutubeUtilsTests.cs @@ -41,12 +41,12 @@ await Assert.ThrowsAnyAsync(() => } [Theory] - [InlineData("https://www.youtube.com/@channel", "/@channel", "")] - [InlineData("youtube.com/user/channel", "/user/channel", "")] - [InlineData("https://m.youtube.com/c/channel?feature=share", "/c/channel", "")] - [InlineData("https://music.youtube.com/@channel", "/@channel", "")] + [InlineData("https://www.youtube.com/@channel", "/@channel", "?cbrd=1&ucbcb=1")] + [InlineData("youtube.com/user/channel", "/user/channel", "?cbrd=1&ucbcb=1")] + [InlineData("https://m.youtube.com/c/channel?feature=share", "/c/channel", "?cbrd=1&ucbcb=1")] + [InlineData("https://music.youtube.com/@channel", "/@channel", "?cbrd=1&ucbcb=1")] [InlineData("https://youtu.be/dQw4w9WgXcQ?si=tracking", "/watch", "?v=dQw4w9WgXcQ")] - [InlineData("@channel", "/@channel", "")] + [InlineData("@channel", "/@channel", "?cbrd=1&ucbcb=1")] public async Task ResolveChannelIdAsync_RequestsCanonicalYoutubeUrls( string input, string expectedPath, diff --git a/Utilities/YoutubeUtils.cs b/Utilities/YoutubeUtils.cs index 28593d0..8118783 100644 --- a/Utilities/YoutubeUtils.cs +++ b/Utilities/YoutubeUtils.cs @@ -11,6 +11,7 @@ public static partial class YoutubeUtils { private const string BrowserUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0 Safari/537.36"; + private const string YouTubeConsentBypassQuery = "?cbrd=1&ucbcb=1"; [GeneratedRegex("^UC[0-9A-Za-z_-]{22}$")] private static partial Regex ChannelIdRegex(); @@ -130,15 +131,15 @@ public static partial class YoutubeUtils if (!IsYoutubeHost(uri.Host) || !IsSupportedYoutubePath(uri.AbsolutePath)) return null; - // Canonicalize the host and discard user-controlled query/fragment values. The - // resolver only needs the channel path to scrape the corresponding YouTube page. - return new Uri($"https://www.youtube.com{uri.AbsolutePath}"); + // cbrd/ucbcb keeps unauthenticated server-side requests on the channel page + // instead of redirecting them to YouTube's consent page. + return new Uri($"https://www.youtube.com{uri.AbsolutePath}{YouTubeConsentBypassQuery}"); } private static Uri? BuildHandleUri(string handle) => string.IsNullOrWhiteSpace(handle) || handle.Contains('/') ? null - : new Uri($"https://www.youtube.com/@{Uri.EscapeDataString(handle)}"); + : new Uri($"https://www.youtube.com/@{Uri.EscapeDataString(handle)}{YouTubeConsentBypassQuery}"); private static bool IsYoutubeHost(string host) => host.Equals("youtube.com", StringComparison.OrdinalIgnoreCase) ||