mirror of
https://github.com/Tyrrrz/DiscordChatExporter.git
synced 2026-02-14 07:43:31 -07:00
feat: preserve original URLs in HTML and JSON exports
Add data-original-url attributes to HTML media elements and originalUrl fields to JSON output, allowing users to access unmodified Discord CDN URLs even when assets are downloaded locally. This enables better URL tracking and archival workflows. Fixes #1414
This commit is contained in:
parent
8fff0f4445
commit
f6a4417261
|
|
@ -64,13 +64,11 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
|||
await WriteRolesAsync(Context.GetUserRoles(user.Id), cancellationToken);
|
||||
}
|
||||
|
||||
_writer.WriteString(
|
||||
"avatarUrl",
|
||||
await Context.ResolveAssetUrlAsync(
|
||||
Context.TryGetMember(user.Id)?.AvatarUrl ?? user.AvatarUrl,
|
||||
cancellationToken
|
||||
)
|
||||
);
|
||||
var avatarUrl = Context.TryGetMember(user.Id)?.AvatarUrl ?? user.AvatarUrl;
|
||||
var resolvedAvatarUrl = await Context.ResolveAssetUrlAsync(avatarUrl, cancellationToken);
|
||||
_writer.WriteString("avatarUrl", resolvedAvatarUrl);
|
||||
if (resolvedAvatarUrl != avatarUrl)
|
||||
_writer.WriteString("originalAvatarUrl", avatarUrl);
|
||||
|
||||
_writer.WriteEndObject();
|
||||
await _writer.FlushAsync(cancellationToken);
|
||||
|
|
@ -87,10 +85,10 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
|||
_writer.WriteString("name", emoji.Name);
|
||||
_writer.WriteString("code", emoji.Code);
|
||||
_writer.WriteBoolean("isAnimated", emoji.IsAnimated);
|
||||
_writer.WriteString(
|
||||
"imageUrl",
|
||||
await Context.ResolveAssetUrlAsync(emoji.ImageUrl, cancellationToken)
|
||||
);
|
||||
var resolvedImageUrl = await Context.ResolveAssetUrlAsync(emoji.ImageUrl, cancellationToken);
|
||||
_writer.WriteString("imageUrl", resolvedImageUrl);
|
||||
if (resolvedImageUrl != emoji.ImageUrl)
|
||||
_writer.WriteString("originalImageUrl", emoji.ImageUrl);
|
||||
|
||||
_writer.WriteEndObject();
|
||||
await _writer.FlushAsync(cancellationToken);
|
||||
|
|
@ -131,13 +129,12 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
|||
|
||||
if (!string.IsNullOrWhiteSpace(embedAuthor.IconUrl))
|
||||
{
|
||||
_writer.WriteString(
|
||||
"iconUrl",
|
||||
await Context.ResolveAssetUrlAsync(
|
||||
embedAuthor.IconProxyUrl ?? embedAuthor.IconUrl,
|
||||
cancellationToken
|
||||
)
|
||||
);
|
||||
var iconUrl = embedAuthor.IconProxyUrl ?? embedAuthor.IconUrl;
|
||||
var originalIconUrl = embedAuthor.IconUrl ?? embedAuthor.IconProxyUrl;
|
||||
var resolvedIconUrl = await Context.ResolveAssetUrlAsync(iconUrl, cancellationToken);
|
||||
_writer.WriteString("iconUrl", resolvedIconUrl);
|
||||
if (resolvedIconUrl != originalIconUrl)
|
||||
_writer.WriteString("originalIconUrl", originalIconUrl);
|
||||
}
|
||||
|
||||
_writer.WriteEndObject();
|
||||
|
|
@ -153,13 +150,12 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
|||
|
||||
if (!string.IsNullOrWhiteSpace(embedImage.Url))
|
||||
{
|
||||
_writer.WriteString(
|
||||
"url",
|
||||
await Context.ResolveAssetUrlAsync(
|
||||
embedImage.ProxyUrl ?? embedImage.Url,
|
||||
cancellationToken
|
||||
)
|
||||
);
|
||||
var imageUrl = embedImage.ProxyUrl ?? embedImage.Url;
|
||||
var originalUrl = embedImage.Url ?? embedImage.ProxyUrl;
|
||||
var resolvedUrl = await Context.ResolveAssetUrlAsync(imageUrl, cancellationToken);
|
||||
_writer.WriteString("url", resolvedUrl);
|
||||
if (resolvedUrl != originalUrl)
|
||||
_writer.WriteString("originalUrl", originalUrl);
|
||||
}
|
||||
|
||||
_writer.WriteNumber("width", embedImage.Width);
|
||||
|
|
@ -178,13 +174,12 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
|||
|
||||
if (!string.IsNullOrWhiteSpace(embedVideo.Url))
|
||||
{
|
||||
_writer.WriteString(
|
||||
"url",
|
||||
await Context.ResolveAssetUrlAsync(
|
||||
embedVideo.ProxyUrl ?? embedVideo.Url,
|
||||
cancellationToken
|
||||
)
|
||||
);
|
||||
var videoUrl = embedVideo.ProxyUrl ?? embedVideo.Url;
|
||||
var originalUrl = embedVideo.Url ?? embedVideo.ProxyUrl;
|
||||
var resolvedUrl = await Context.ResolveAssetUrlAsync(videoUrl, cancellationToken);
|
||||
_writer.WriteString("url", resolvedUrl);
|
||||
if (resolvedUrl != originalUrl)
|
||||
_writer.WriteString("originalUrl", originalUrl);
|
||||
}
|
||||
|
||||
_writer.WriteNumber("width", embedVideo.Width);
|
||||
|
|
@ -205,13 +200,12 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
|||
|
||||
if (!string.IsNullOrWhiteSpace(embedFooter.IconUrl))
|
||||
{
|
||||
_writer.WriteString(
|
||||
"iconUrl",
|
||||
await Context.ResolveAssetUrlAsync(
|
||||
embedFooter.IconProxyUrl ?? embedFooter.IconUrl,
|
||||
cancellationToken
|
||||
)
|
||||
);
|
||||
var iconUrl = embedFooter.IconProxyUrl ?? embedFooter.IconUrl;
|
||||
var originalIconUrl = embedFooter.IconUrl ?? embedFooter.IconProxyUrl;
|
||||
var resolvedIconUrl = await Context.ResolveAssetUrlAsync(iconUrl, cancellationToken);
|
||||
_writer.WriteString("iconUrl", resolvedIconUrl);
|
||||
if (resolvedIconUrl != originalIconUrl)
|
||||
_writer.WriteString("originalIconUrl", originalIconUrl);
|
||||
}
|
||||
|
||||
_writer.WriteEndObject();
|
||||
|
|
@ -339,10 +333,11 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
|||
_writer.WriteString("id", Context.Request.Guild.Id.ToString());
|
||||
_writer.WriteString("name", Context.Request.Guild.Name);
|
||||
|
||||
_writer.WriteString(
|
||||
"iconUrl",
|
||||
await Context.ResolveAssetUrlAsync(Context.Request.Guild.IconUrl, cancellationToken)
|
||||
);
|
||||
var guildIconUrl = Context.Request.Guild.IconUrl;
|
||||
var resolvedGuildIconUrl = await Context.ResolveAssetUrlAsync(guildIconUrl, cancellationToken);
|
||||
_writer.WriteString("iconUrl", resolvedGuildIconUrl);
|
||||
if (resolvedGuildIconUrl != guildIconUrl)
|
||||
_writer.WriteString("originalIconUrl", guildIconUrl);
|
||||
|
||||
_writer.WriteEndObject();
|
||||
|
||||
|
|
@ -360,13 +355,11 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
|||
|
||||
if (!string.IsNullOrWhiteSpace(Context.Request.Channel.IconUrl))
|
||||
{
|
||||
_writer.WriteString(
|
||||
"iconUrl",
|
||||
await Context.ResolveAssetUrlAsync(
|
||||
Context.Request.Channel.IconUrl,
|
||||
cancellationToken
|
||||
)
|
||||
);
|
||||
var channelIconUrl = Context.Request.Channel.IconUrl;
|
||||
var resolvedChannelIconUrl = await Context.ResolveAssetUrlAsync(channelIconUrl, cancellationToken);
|
||||
_writer.WriteString("iconUrl", resolvedChannelIconUrl);
|
||||
if (resolvedChannelIconUrl != channelIconUrl)
|
||||
_writer.WriteString("originalIconUrl", channelIconUrl);
|
||||
}
|
||||
|
||||
_writer.WriteEndObject();
|
||||
|
|
@ -433,10 +426,10 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
|||
_writer.WriteStartObject();
|
||||
|
||||
_writer.WriteString("id", attachment.Id.ToString());
|
||||
_writer.WriteString(
|
||||
"url",
|
||||
await Context.ResolveAssetUrlAsync(attachment.Url, cancellationToken)
|
||||
);
|
||||
var resolvedUrl = await Context.ResolveAssetUrlAsync(attachment.Url, cancellationToken);
|
||||
_writer.WriteString("url", resolvedUrl);
|
||||
if (resolvedUrl != attachment.Url)
|
||||
_writer.WriteString("originalUrl", attachment.Url);
|
||||
_writer.WriteString("fileName", attachment.FileName);
|
||||
_writer.WriteNumber("fileSizeBytes", attachment.FileSize.TotalBytes);
|
||||
|
||||
|
|
@ -463,10 +456,10 @@ internal class JsonMessageWriter(Stream stream, ExportContext context)
|
|||
_writer.WriteString("id", sticker.Id.ToString());
|
||||
_writer.WriteString("name", sticker.Name);
|
||||
_writer.WriteString("format", sticker.Format.ToString());
|
||||
_writer.WriteString(
|
||||
"sourceUrl",
|
||||
await Context.ResolveAssetUrlAsync(sticker.SourceUrl, cancellationToken)
|
||||
);
|
||||
var resolvedSourceUrl = await Context.ResolveAssetUrlAsync(sticker.SourceUrl, cancellationToken);
|
||||
_writer.WriteString("sourceUrl", resolvedSourceUrl);
|
||||
if (resolvedSourceUrl != sticker.SourceUrl)
|
||||
_writer.WriteString("originalSourceUrl", sticker.SourceUrl);
|
||||
|
||||
_writer.WriteEndObject();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,11 @@
|
|||
}
|
||||
|
||||
// Avatar
|
||||
<img class="chatlog__avatar" src="@await ResolveAssetUrlAsync(authorMember?.AvatarUrl ?? message.Author.AvatarUrl)" alt="Avatar" loading="lazy">
|
||||
{
|
||||
var avatarUrl = authorMember?.AvatarUrl ?? message.Author.AvatarUrl;
|
||||
var resolvedAvatarUrl = await ResolveAssetUrlAsync(avatarUrl);
|
||||
<img class="chatlog__avatar" src="@resolvedAvatarUrl" @(resolvedAvatarUrl != avatarUrl ? $"data-original-url=\"{avatarUrl}\"" : null) alt="Avatar" loading="lazy">
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -172,7 +176,9 @@
|
|||
? message.ReferencedMessage.Author.DisplayName
|
||||
: referencedUserMember?.DisplayName ?? message.ReferencedMessage.Author.DisplayName;
|
||||
|
||||
<img class="chatlog__reply-avatar" src="@await ResolveAssetUrlAsync(referencedUserMember?.AvatarUrl ?? message.ReferencedMessage.Author.AvatarUrl)" alt="Avatar" loading="lazy">
|
||||
var replyAvatarUrl = referencedUserMember?.AvatarUrl ?? message.ReferencedMessage.Author.AvatarUrl;
|
||||
var resolvedReplyAvatarUrl = await ResolveAssetUrlAsync(replyAvatarUrl);
|
||||
<img class="chatlog__reply-avatar" src="@resolvedReplyAvatarUrl" @(resolvedReplyAvatarUrl != replyAvatarUrl ? $"data-original-url=\"{replyAvatarUrl}\"" : null) alt="Avatar" loading="lazy">
|
||||
<div class="chatlog__reply-author" style="@(referencedUserColor is not null ? $"color: rgb({referencedUserColor.Value.R}, {referencedUserColor.Value.G}, {referencedUserColor.Value.B})" : null)" title="@message.ReferencedMessage.Author.FullName">@referencedUserDisplayName</div>
|
||||
<div class="chatlog__reply-content">
|
||||
<span class="chatlog__reply-link" onclick="scrollToMessage(event, '@message.ReferencedMessage.Id')">
|
||||
|
|
@ -205,7 +211,9 @@
|
|||
? message.Interaction.User.DisplayName
|
||||
: interactionUserMember?.DisplayName ?? message.Interaction.User.DisplayName;
|
||||
|
||||
<img class="chatlog__reply-avatar" src="@await ResolveAssetUrlAsync(interactionUserMember?.AvatarUrl ?? message.Interaction.User.AvatarUrl)" alt="Avatar" loading="lazy">
|
||||
var interactionAvatarUrl = interactionUserMember?.AvatarUrl ?? message.Interaction.User.AvatarUrl;
|
||||
var resolvedInteractionAvatarUrl = await ResolveAssetUrlAsync(interactionAvatarUrl);
|
||||
<img class="chatlog__reply-avatar" src="@resolvedInteractionAvatarUrl" @(resolvedInteractionAvatarUrl != interactionAvatarUrl ? $"data-original-url=\"{interactionAvatarUrl}\"" : null) alt="Avatar" loading="lazy">
|
||||
<div class="chatlog__reply-author" style="@(interactionUserColor is not null ? $"color: rgb({interactionUserColor.Value.R}, {interactionUserColor.Value.G}, {interactionUserColor.Value.B})" : null)" title="@message.Interaction.User.FullName">@interactionUserDisplayName</div>
|
||||
<div class="chatlog__reply-content">
|
||||
used /@message.Interaction.Name
|
||||
|
|
@ -275,20 +283,23 @@
|
|||
@* Attachment preview *@
|
||||
@if (attachment.IsImage)
|
||||
{
|
||||
<a href="@await ResolveAssetUrlAsync(attachment.Url)">
|
||||
<img class="chatlog__attachment-media" src="@await ResolveAssetUrlAsync(attachment.Url)" alt="@(attachment.Description ?? "Image attachment")" title="Image: @attachment.FileName (@attachment.FileSize)" loading="lazy">
|
||||
var resolvedUrl = await ResolveAssetUrlAsync(attachment.Url);
|
||||
<a href="@resolvedUrl">
|
||||
<img class="chatlog__attachment-media" src="@resolvedUrl" @(resolvedUrl != attachment.Url ? $"data-original-url=\"{attachment.Url}\"" : null) alt="@(attachment.Description ?? "Image attachment")" title="Image: @attachment.FileName (@attachment.FileSize)" loading="lazy">
|
||||
</a>
|
||||
}
|
||||
else if (attachment.IsVideo)
|
||||
{
|
||||
<video class="chatlog__attachment-media" controls>
|
||||
<source src="@await ResolveAssetUrlAsync(attachment.Url)" alt="@(attachment.Description ?? "Video attachment")" title="Video: @attachment.FileName (@attachment.FileSize)">
|
||||
var resolvedUrl = await ResolveAssetUrlAsync(attachment.Url);
|
||||
<video class="chatlog__attachment-media" controls @(resolvedUrl != attachment.Url ? $"data-original-url=\"{attachment.Url}\"" : null)>
|
||||
<source src="@resolvedUrl" alt="@(attachment.Description ?? "Video attachment")" title="Video: @attachment.FileName (@attachment.FileSize)">
|
||||
</video>
|
||||
}
|
||||
else if (attachment.IsAudio)
|
||||
{
|
||||
<audio class="chatlog__attachment-media" controls>
|
||||
<source src="@await ResolveAssetUrlAsync(attachment.Url)" alt="@(attachment.Description ?? "Audio attachment")" title="Audio: @attachment.FileName (@attachment.FileSize)">
|
||||
var resolvedUrl = await ResolveAssetUrlAsync(attachment.Url);
|
||||
<audio class="chatlog__attachment-media" controls @(resolvedUrl != attachment.Url ? $"data-original-url=\"{attachment.Url}\"" : null)>
|
||||
<source src="@resolvedUrl" alt="@(attachment.Description ?? "Audio attachment")" title="Audio: @attachment.FileName (@attachment.FileSize)">
|
||||
</audio>
|
||||
}
|
||||
else
|
||||
|
|
@ -330,9 +341,13 @@
|
|||
<div class="chatlog__embed">
|
||||
<div class="chatlog__embed-invite-container">
|
||||
<div class="chatlog__embed-invite-title">@(invite.Channel?.IsDirect == true ? "Invite to join a group DM" : "Invite to join a server")</div>
|
||||
@{
|
||||
var inviteIconUrl = invite.Channel?.IconUrl ?? invite.Guild.IconUrl;
|
||||
var resolvedInviteIconUrl = await ResolveAssetUrlAsync(inviteIconUrl);
|
||||
}
|
||||
<div class="chatlog__embed-invite">
|
||||
<div class="chatlog__embed-invite-guild-icon-container">
|
||||
<img class="chatlog__embed-invite-guild-icon" src="@await ResolveAssetUrlAsync(invite.Channel?.IconUrl ?? invite.Guild.IconUrl)" alt="Guild icon" loading="lazy">
|
||||
<img class="chatlog__embed-invite-guild-icon" src="@resolvedInviteIconUrl" @(resolvedInviteIconUrl != inviteIconUrl ? $"data-original-url=\"{inviteIconUrl}\"" : null) alt="Guild icon" loading="lazy">
|
||||
</div>
|
||||
<div class="chatlog__embed-invite-info">
|
||||
<div class="chatlog__embed-invite-guild-name">
|
||||
|
|
@ -388,7 +403,10 @@
|
|||
<div class="chatlog__embed-author-container">
|
||||
@if (!string.IsNullOrWhiteSpace(embed.Author.IconUrl))
|
||||
{
|
||||
<img class="chatlog__embed-author-icon" src="@await ResolveAssetUrlAsync(embed.Author.IconProxyUrl ?? embed.Author.IconUrl)" alt="Author icon" loading="lazy" onerror="this.style.visibility='hidden'">
|
||||
var authorIconUrl = embed.Author.IconProxyUrl ?? embed.Author.IconUrl;
|
||||
var originalAuthorIconUrl = embed.Author.IconUrl ?? embed.Author.IconProxyUrl;
|
||||
var resolvedAuthorIconUrl = await ResolveAssetUrlAsync(authorIconUrl);
|
||||
<img class="chatlog__embed-author-icon" src="@resolvedAuthorIconUrl" @(resolvedAuthorIconUrl != originalAuthorIconUrl ? $"data-original-url=\"{originalAuthorIconUrl}\"" : null) alt="Author icon" loading="lazy" onerror="this.style.visibility='hidden'">
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(embed.Author.Name))
|
||||
|
|
@ -441,9 +459,16 @@
|
|||
embed.Thumbnail?.ProxyUrl ?? embed.Thumbnail?.Url ??
|
||||
embed.Url;
|
||||
|
||||
var embedImageOriginalUrl =
|
||||
embed.Image?.Url ?? embed.Image?.ProxyUrl ??
|
||||
embed.Thumbnail?.Url ?? embed.Thumbnail?.ProxyUrl ??
|
||||
embed.Url;
|
||||
|
||||
var resolvedEmbedImageUrl = await ResolveAssetUrlAsync(embedImageUrl);
|
||||
|
||||
<div class="chatlog__embed">
|
||||
<a href="@await ResolveAssetUrlAsync(embedImageUrl)">
|
||||
<img class="chatlog__embed-generic-image" src="@await ResolveAssetUrlAsync(embedImageUrl)" alt="Embedded image" loading="lazy">
|
||||
<a href="@resolvedEmbedImageUrl">
|
||||
<img class="chatlog__embed-generic-image" src="@resolvedEmbedImageUrl" @(resolvedEmbedImageUrl != embedImageOriginalUrl ? $"data-original-url=\"{embedImageOriginalUrl}\"" : null) alt="Embedded image" loading="lazy">
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -457,9 +482,15 @@
|
|||
embed.Video?.ProxyUrl ?? embed.Video?.Url ??
|
||||
embed.Url;
|
||||
|
||||
var embedVideoOriginalUrl =
|
||||
embed.Video?.Url ?? embed.Video?.ProxyUrl ??
|
||||
embed.Url;
|
||||
|
||||
var resolvedEmbedVideoUrl = await ResolveAssetUrlAsync(embedVideoUrl);
|
||||
|
||||
<div class="chatlog__embed">
|
||||
<video class="chatlog__embed-generic-video" width="@embed.Video?.Width" height="@embed.Video?.Height" controls>
|
||||
<source src="@await ResolveAssetUrlAsync(embedVideoUrl)" alt="Embedded video">
|
||||
<video class="chatlog__embed-generic-video" width="@embed.Video?.Width" height="@embed.Video?.Height" controls @(resolvedEmbedVideoUrl != embedVideoOriginalUrl ? $"data-original-url=\"{embedVideoOriginalUrl}\"" : null)>
|
||||
<source src="@resolvedEmbedVideoUrl" alt="Embedded video">
|
||||
</video>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -470,9 +501,15 @@
|
|||
embed.Video?.ProxyUrl ?? embed.Video?.Url ??
|
||||
embed.Url;
|
||||
|
||||
var embedVideoOriginalUrl =
|
||||
embed.Video?.Url ?? embed.Video?.ProxyUrl ??
|
||||
embed.Url;
|
||||
|
||||
var resolvedEmbedVideoUrl = await ResolveAssetUrlAsync(embedVideoUrl);
|
||||
|
||||
<div class="chatlog__embed">
|
||||
<video class="chatlog__embed-generic-gifv" width="@embed.Video?.Width" height="@embed.Video?.Height" loop onmouseover="this.play()" onmouseout="this.pause()">
|
||||
<source src="@await ResolveAssetUrlAsync(embedVideoUrl)" alt="Embedded gifv">
|
||||
<video class="chatlog__embed-generic-gifv" width="@embed.Video?.Width" height="@embed.Video?.Height" loop onmouseover="this.play()" onmouseout="this.pause()" @(resolvedEmbedVideoUrl != embedVideoOriginalUrl ? $"data-original-url=\"{embedVideoOriginalUrl}\"" : null)>
|
||||
<source src="@resolvedEmbedVideoUrl" alt="Embedded gifv">
|
||||
</video>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -499,7 +536,10 @@
|
|||
<div class="chatlog__embed-author-container">
|
||||
@if (!string.IsNullOrWhiteSpace(embed.Author.IconUrl))
|
||||
{
|
||||
<img class="chatlog__embed-author-icon" src="@await ResolveAssetUrlAsync(embed.Author.IconProxyUrl ?? embed.Author.IconUrl)" alt="Author icon" loading="lazy" onerror="this.style.visibility='hidden'">
|
||||
var authorIconUrl = embed.Author.IconProxyUrl ?? embed.Author.IconUrl;
|
||||
var originalAuthorIconUrl = embed.Author.IconUrl ?? embed.Author.IconProxyUrl;
|
||||
var resolvedAuthorIconUrl = await ResolveAssetUrlAsync(authorIconUrl);
|
||||
<img class="chatlog__embed-author-icon" src="@resolvedAuthorIconUrl" @(resolvedAuthorIconUrl != originalAuthorIconUrl ? $"data-original-url=\"{originalAuthorIconUrl}\"" : null) alt="Author icon" loading="lazy" onerror="this.style.visibility='hidden'">
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(embed.Author.Name))
|
||||
|
|
@ -572,9 +612,12 @@
|
|||
@* Embed content *@
|
||||
@if (embed.Thumbnail is not null && !string.IsNullOrWhiteSpace(embed.Thumbnail.Url))
|
||||
{
|
||||
var thumbnailUrl = embed.Thumbnail.ProxyUrl ?? embed.Thumbnail.Url;
|
||||
var originalThumbnailUrl = embed.Thumbnail.Url ?? embed.Thumbnail.ProxyUrl;
|
||||
var resolvedThumbnailUrl = await ResolveAssetUrlAsync(thumbnailUrl);
|
||||
<div class="chatlog__embed-thumbnail-container">
|
||||
<a class="chatlog__embed-thumbnail-link" href="@await ResolveAssetUrlAsync(embed.Thumbnail.ProxyUrl ?? embed.Thumbnail.Url)">
|
||||
<img class="chatlog__embed-thumbnail" src="@await ResolveAssetUrlAsync(embed.Thumbnail.ProxyUrl ?? embed.Thumbnail.Url)" alt="Thumbnail" loading="lazy">
|
||||
<a class="chatlog__embed-thumbnail-link" href="@resolvedThumbnailUrl">
|
||||
<img class="chatlog__embed-thumbnail" src="@resolvedThumbnailUrl" @(resolvedThumbnailUrl != originalThumbnailUrl ? $"data-original-url=\"{originalThumbnailUrl}\"" : null) alt="Thumbnail" loading="lazy">
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -588,9 +631,12 @@
|
|||
{
|
||||
if (!string.IsNullOrWhiteSpace(image.Url))
|
||||
{
|
||||
var imageUrl = image.ProxyUrl ?? image.Url;
|
||||
var originalImageUrl = image.Url ?? image.ProxyUrl;
|
||||
var resolvedImageUrl = await ResolveAssetUrlAsync(imageUrl);
|
||||
<div class="chatlog__embed-image-container">
|
||||
<a class="chatlog__embed-image-link" href="@await ResolveAssetUrlAsync(image.ProxyUrl ?? image.Url)">
|
||||
<img class="chatlog__embed-image" src="@await ResolveAssetUrlAsync(image.ProxyUrl ?? image.Url)" alt="Image" loading="lazy">
|
||||
<a class="chatlog__embed-image-link" href="@resolvedImageUrl">
|
||||
<img class="chatlog__embed-image" src="@resolvedImageUrl" @(resolvedImageUrl != originalImageUrl ? $"data-original-url=\"{originalImageUrl}\"" : null) alt="Image" loading="lazy">
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -605,7 +651,10 @@
|
|||
@* Footer icon *@
|
||||
@if (!string.IsNullOrWhiteSpace(embed.Footer?.IconUrl))
|
||||
{
|
||||
<img class="chatlog__embed-footer-icon" src="@await ResolveAssetUrlAsync(embed.Footer.IconProxyUrl ?? embed.Footer.IconUrl)" alt="Footer icon" loading="lazy">
|
||||
var footerIconUrl = embed.Footer.IconProxyUrl ?? embed.Footer.IconUrl;
|
||||
var originalFooterIconUrl = embed.Footer.IconUrl ?? embed.Footer.IconProxyUrl;
|
||||
var resolvedFooterIconUrl = await ResolveAssetUrlAsync(footerIconUrl);
|
||||
<img class="chatlog__embed-footer-icon" src="@resolvedFooterIconUrl" @(resolvedFooterIconUrl != originalFooterIconUrl ? $"data-original-url=\"{originalFooterIconUrl}\"" : null) alt="Footer icon" loading="lazy">
|
||||
}
|
||||
|
||||
<span class="chatlog__embed-footer-text">
|
||||
|
|
@ -636,14 +685,15 @@
|
|||
@* Stickers *@
|
||||
@foreach (var sticker in message.Stickers)
|
||||
{
|
||||
var resolvedStickerUrl = await ResolveAssetUrlAsync(sticker.SourceUrl);
|
||||
<div class="chatlog__sticker" title="@sticker.Name">
|
||||
@if (sticker.IsImage)
|
||||
{
|
||||
<img class="chatlog__sticker--media" src="@await ResolveAssetUrlAsync(sticker.SourceUrl)" alt="Sticker">
|
||||
<img class="chatlog__sticker--media" src="@resolvedStickerUrl" @(resolvedStickerUrl != sticker.SourceUrl ? $"data-original-url=\"{sticker.SourceUrl}\"" : null) alt="Sticker">
|
||||
}
|
||||
else if (sticker.Format == StickerFormat.Lottie)
|
||||
{
|
||||
<div class="chatlog__sticker--media" data-source="@await ResolveAssetUrlAsync(sticker.SourceUrl)"></div>
|
||||
<div class="chatlog__sticker--media" data-source="@resolvedStickerUrl" @(resolvedStickerUrl != sticker.SourceUrl ? $"data-original-url=\"{sticker.SourceUrl}\"" : null)></div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
@ -654,8 +704,9 @@
|
|||
<div class="chatlog__reactions">
|
||||
@foreach (var reaction in message.Reactions)
|
||||
{
|
||||
var resolvedEmojiUrl = await ResolveAssetUrlAsync(reaction.Emoji.ImageUrl);
|
||||
<div class="chatlog__reaction" title="@reaction.Emoji.Code">
|
||||
<img class="chatlog__emoji chatlog__emoji--small" alt="@reaction.Emoji.Name" src="@await ResolveAssetUrlAsync(reaction.Emoji.ImageUrl)" loading="lazy">
|
||||
<img class="chatlog__emoji chatlog__emoji--small" alt="@reaction.Emoji.Name" src="@resolvedEmojiUrl" @(resolvedEmojiUrl != reaction.Emoji.ImageUrl ? $"data-original-url=\"{reaction.Emoji.ImageUrl}\"" : null) loading="lazy">
|
||||
<span class="chatlog__reaction-count">@reaction.Count</span>
|
||||
</div>
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue