From f2c9f4516481729f613de94796f2ba40237a2f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20D=C3=BCrrenberger?= Date: Tue, 9 Dec 2025 11:42:51 +0100 Subject: [PATCH 1/2] Fix YouTube video embedding The V2 API has been shutdown and V3 requires authentication via API key. Luckily the oembed API is still publicly available. --- Source/HtmlRenderer/Core/Dom/CssBoxFrame.cs | 110 ++++++++++---------- 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/Source/HtmlRenderer/Core/Dom/CssBoxFrame.cs b/Source/HtmlRenderer/Core/Dom/CssBoxFrame.cs index 0b810e5fc..86f1fff55 100644 --- a/Source/HtmlRenderer/Core/Dom/CssBoxFrame.cs +++ b/Source/HtmlRenderer/Core/Dom/CssBoxFrame.cs @@ -146,7 +146,7 @@ private void LoadYoutubeDataAsync(Uri uri) { try { - var apiUri = new Uri(string.Format("https://gdata.youtube.com/feeds/api/videos/{0}?v=2&alt=json", uri.Segments[2])); + var apiUri = new Uri(string.Format("https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={0}&format=json", uri.Segments[2])); var client = new WebClient(); client.Encoding = Encoding.UTF8; @@ -172,89 +172,93 @@ private void OnDownloadYoutubeApiCompleted(object sender, DownloadStringComplete { if (e.Error == null) { - var idx = e.Result.IndexOf("\"media$title\"", StringComparison.Ordinal); + var idx = e.Result.IndexOf("\"title\"", StringComparison.Ordinal); if (idx > -1) { - idx = e.Result.IndexOf("\"$t\"", idx); + idx = e.Result.IndexOf('"', idx + 7); if (idx > -1) { - idx = e.Result.IndexOf('"', idx + 4); - if (idx > -1) + var endIdx = e.Result.IndexOf('"', idx + 1); + while (endIdx > 0 && e.Result[endIdx - 1] == '\\') + endIdx = e.Result.IndexOf('"', endIdx + 1); + if (endIdx > -1) { - var endIdx = e.Result.IndexOf('"', idx + 1); - while (e.Result[endIdx - 1] == '\\') - endIdx = e.Result.IndexOf('"', endIdx + 1); - if (endIdx > -1) - { - _videoTitle = e.Result.Substring(idx + 1, endIdx - idx - 1).Replace("\\\"", "\""); - } + _videoTitle = e.Result.Substring(idx + 1, endIdx - idx - 1).Replace("\\\"", "\""); } } } - idx = e.Result.IndexOf("\"media$thumbnail\"", StringComparison.Ordinal); + idx = e.Result.IndexOf("\"thumbnail_url\"", StringComparison.Ordinal); if (idx > -1) { - var iidx = e.Result.IndexOf("sddefault", idx); - if (iidx > -1) - { - if (string.IsNullOrEmpty(Width)) - Width = "640px"; - if (string.IsNullOrEmpty(Height)) - Height = "480px"; - } - else + idx = e.Result.IndexOf('"', idx + 15); + if (idx > -1) { - iidx = e.Result.IndexOf("hqdefault", idx); - if (iidx > -1) + var endIdx = e.Result.IndexOf('"', idx + 1); + while (endIdx > 0 && e.Result[endIdx - 1] == '\\') + endIdx = e.Result.IndexOf('"', endIdx + 1); + if (endIdx > -1) { - if (string.IsNullOrEmpty(Width)) - Width = "480px"; - if (string.IsNullOrEmpty(Height)) - Height = "360px"; + _videoImageUrl = e.Result.Substring(idx + 1, endIdx - idx - 1).Replace("\\\"", "\""); } - else + } + + idx = e.Result.IndexOf("\"thumbnail_width\"", StringComparison.Ordinal); + if (idx > -1) + { + idx = e.Result.IndexOf(':', idx); + if (idx > -1) { - iidx = e.Result.IndexOf("mqdefault", idx); - if (iidx > -1) - { - if (string.IsNullOrEmpty(Width)) - Width = "320px"; - if (string.IsNullOrEmpty(Height)) - Height = "180px"; - } - else + var endIdx = e.Result.IndexOf(',', idx); + if (endIdx > -1) { - iidx = e.Result.IndexOf("default", idx); - if (string.IsNullOrEmpty(Width)) - Width = "120px"; - if (string.IsNullOrEmpty(Height)) - Height = "90px"; + var widthStr = e.Result.Substring(idx + 1, endIdx - idx - 1).Trim(); + if (int.TryParse(widthStr, out int width)) + { + if (string.IsNullOrEmpty(Width)) + Width = width + "px"; + } } } } - iidx = e.Result.LastIndexOf("http:", iidx, StringComparison.Ordinal); - if (iidx > -1) + idx = e.Result.IndexOf("\"thumbnail_height\"", StringComparison.Ordinal); + if (idx > -1) { - var endIdx = e.Result.IndexOf('"', iidx); - if (endIdx > -1) + idx = e.Result.IndexOf(':', idx); + if (idx > -1) { - _videoImageUrl = e.Result.Substring(iidx, endIdx - iidx).Replace("\\\"", "\"").Replace("\\", ""); + var endIdx = e.Result.IndexOf(',', idx); + if (endIdx == -1) + endIdx = e.Result.IndexOf('}', idx); + if (endIdx > -1) + { + var heightStr = e.Result.Substring(idx + 1, endIdx - idx - 1).Trim(); + if (int.TryParse(heightStr, out int height)) + { + if (string.IsNullOrEmpty(Height)) + Height = height + "px"; + } + } } } } - idx = e.Result.IndexOf("\"link\"", StringComparison.Ordinal); + idx = e.Result.IndexOf("\"html\"", StringComparison.Ordinal); if (idx > -1) { - idx = e.Result.IndexOf("http:", idx); + idx = e.Result.IndexOf("src=", idx); if (idx > -1) { - var endIdx = e.Result.IndexOf('"', idx); - if (endIdx > -1) + idx = e.Result.IndexOf("embed/", idx); + if (idx > -1) { - _videoLinkUrl = e.Result.Substring(idx, endIdx - idx).Replace("\\\"", "\"").Replace("\\", ""); + var endIdx = e.Result.IndexOf('?', idx); + if (endIdx > -1) + { + var videoId = e.Result.Substring(idx + 6, endIdx - idx - 6); + _videoLinkUrl = "https://www.youtube.com/watch?v=" + videoId; + } } } } From 8d164894ca032eefa0981bf0ad1a6479c6723350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20D=C3=BCrrenberger?= Date: Tue, 9 Dec 2025 12:22:26 +0100 Subject: [PATCH 2/2] Fix Vimeo video embedding The API response has slightly changed --- .../Demo/Common/Samples/06.Embeded video.htm | 3 +- Source/HtmlRenderer/Core/Dom/CssBoxFrame.cs | 42 ++++++++++++------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/Source/Demo/Common/Samples/06.Embeded video.htm b/Source/Demo/Common/Samples/06.Embeded video.htm index ea67b2f7f..af499d19b 100644 --- a/Source/Demo/Common/Samples/06.Embeded video.htm +++ b/Source/Demo/Common/Samples/06.Embeded video.htm @@ -33,7 +33,8 @@

Example

-