Replace YouTube iframe embeds with clickable thumbnails (#1474)

* Initial plan

* Replace YouTube iframe with clickable thumbnail

- Changed YouTube embed URL from embed format to watch format
- Added ThumbnailUrl property to YouTubeVideoEmbedProjection using YouTube's standard thumbnail URL
- Updated MessageGroupTemplate to render thumbnail image with link instead of iframe
- Updated CSS to style thumbnail appropriately
- Updated test to check for anchor link and thumbnail image instead of iframe

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Address code review feedback

- Use hqdefault.jpg instead of maxresdefault.jpg (guaranteed to exist for all videos)
- Extract thumbnail URL logic to GetThumbnailUrl method for better testability

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Simplify YouTube embed test to focus on data not structure

- Remove checks for HTML structure (class names, nested elements)
- Focus on verifying YouTube URL with correct video ID exists
- Follow same pattern as other embed tests (Spotify, Twitch)

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Fix data-canonical-url and improve test specificity

- Use canonical (non-proxy) URL for data-canonical-url attribute
- Extract thumbnailUrl to local variable to avoid duplicate calls
- Update test to check for img with video ID in src, avoiding false positives
- Test now verifies the actual thumbnail data rather than any link

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

* Inline thumbnail URL logic directly into attributes

- Remove GetThumbnailUrl method from YouTubeVideoEmbedProjection
- Remove local variables from template
- Put coalescing logic directly in src and data-canonical-url attributes

Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Tyrrrz <1935960+Tyrrrz@users.noreply.github.com>
This commit is contained in:
Copilot
2026-02-12 19:04:17 +02:00
committed by GitHub
parent 81e286751f
commit 72f9e981de
4 changed files with 20 additions and 7 deletions

View File

@@ -181,8 +181,14 @@ public class HtmlEmbedSpecs
);
// Assert
var iframeUrl = message.QuerySelector("iframe")?.GetAttribute("src");
iframeUrl.Should().StartWith("https://www.youtube.com/embed/qOWW4OlgbvE");
// Check that the YouTube video thumbnail image exists with the correct video ID
var youtubeThumbnailSrc = message
.QuerySelectorAll("img")
.Select(e => e.GetAttribute("src"))
.WhereNotNull()
.FirstOrDefault(s => s.Contains("qOWW4OlgbvE", StringComparison.Ordinal));
youtubeThumbnailSrc.Should().NotBeNull();
}
[Fact]

View File

@@ -2,7 +2,10 @@
public partial record YouTubeVideoEmbedProjection(string VideoId)
{
public string Url { get; } = $"https://www.youtube.com/embed/{VideoId}";
public string Url { get; } = $"https://www.youtube.com/watch?v={VideoId}";
// Using hqdefault.jpg which is guaranteed to exist for all YouTube videos
public string ThumbnailUrl { get; } = $"https://i.ytimg.com/vi/{VideoId}/hqdefault.jpg";
}
public partial record YouTubeVideoEmbedProjection

View File

@@ -424,9 +424,11 @@
</div>
}
@* Video player *@
@* Video thumbnail *@
<div class="chatlog__embed-youtube-container">
<iframe class="chatlog__embed-youtube" src="@youTubeVideoEmbed.Url" width="400" height="225"></iframe>
<a href="@youTubeVideoEmbed.Url">
<img class="chatlog__embed-youtube-thumbnail" src="@await ResolveAssetUrlAsync(embed.Thumbnail?.ProxyUrl ?? embed.Thumbnail?.Url ?? youTubeVideoEmbed.ThumbnailUrl)" alt="YouTube video thumbnail" loading="lazy" onerror="this.style.visibility='hidden'" data-canonical-url="@(embed.Thumbnail?.Url ?? youTubeVideoEmbed.ThumbnailUrl)">
</a>
</div>
</div>
</div>

View File

@@ -711,9 +711,11 @@
margin-top: 0.6rem;
}
.chatlog__embed-youtube {
border: 0;
.chatlog__embed-youtube-thumbnail {
max-width: 400px;
max-height: 225px;
border-radius: 3px;
cursor: pointer;
}
.chatlog__sticker {