Implement media resizing using Discord media cache (#470)

* Implement media resizing using Discord media cache
Improve performance by offloading image scaling to Discord
Only apply the resizing to images stored in the Discord CDN
Set the format of the resized images to WebP for improved performance

* Implemented suggestion for improved regex
This commit is contained in:
pulchra mentis 2022-08-17 15:50:36 -04:00 committed by GitHub
parent 7aa4d49a97
commit adaddb579e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 29 additions and 6 deletions

View File

@ -0,0 +1,18 @@
const discordCDNAttachmentRegex =
/^https:\/\/cdn\.discordapp\.com\/attachments\/([^?]+)/i
const resizeMedia = (
mediaURL: string,
dimensions?: number[],
format?: string,
) =>
mediaURL.replace(
discordCDNAttachmentRegex,
`https://media.discordapp.net/attachments/$1?width=${
dimensions?.[0] ?? 256
}&height=${dimensions?.[1] ?? dimensions?.[0] ?? 256}&format=${
format ?? 'webp'
}`,
)
export default resizeMedia

View File

@ -2,6 +2,7 @@
import { tick } from 'svelte'; import { tick } from 'svelte';
import { Modal, CardHeader, CardTitle, Image, Spinner } from 'sveltestrap'; import { Modal, CardHeader, CardTitle, Image, Spinner } from 'sveltestrap';
import default_avatar from '../assets/default_avatar.png'; import default_avatar from '../assets/default_avatar.png';
import resizeMedia from '../api/resize-media';
import { toHTML } from 'discord-markdown'; import { toHTML } from 'discord-markdown';
import twemoji from 'twemoji'; import twemoji from 'twemoji';
@ -19,6 +20,7 @@
} }
$: icon_url = item.avatar_url ? item.avatar_url : item.icon ? item.icon : default_avatar; $: icon_url = item.avatar_url ? item.avatar_url : item.icon ? item.icon : default_avatar;
$: icon_url_resized = resizeMedia(icon_url)
let avatarOpen = false; let avatarOpen = false;
const toggleAvatarModal = () => (avatarOpen = !avatarOpen); const toggleAvatarModal = () => (avatarOpen = !avatarOpen);
@ -45,7 +47,7 @@
</div> </div>
<div> <div>
{#if item && (item.avatar_url || item.icon)} {#if item && (item.avatar_url || item.icon)}
<img tabindex={0} on:keydown|stopPropagation={(event) => {if (event.key === "Enter") {avatarOpen = true}}} on:click|stopPropagation={toggleAvatarModal} class="rounded-circle avatar" src={icon_url} alt={altText} /> <img tabindex={0} on:keydown|stopPropagation={(event) => {if (event.key === "Enter") {avatarOpen = true}}} on:click|stopPropagation={toggleAvatarModal} class="rounded-circle avatar" src={icon_url_resized} alt={altText} />
{:else} {:else}
<img class="rounded-circle avatar" src={default_avatar} alt="icon (default)" tabindex={0} /> <img class="rounded-circle avatar" src={default_avatar} alt="icon (default)" tabindex={0} />
{/if} {/if}

View File

@ -4,6 +4,7 @@
import moment from 'moment'; import moment from 'moment';
import { toHTML } from 'discord-markdown'; import { toHTML } from 'discord-markdown';
import parseTimestamps from '../../api/parse-timestamps'; import parseTimestamps from '../../api/parse-timestamps';
import resizeMedia from '../../api/resize-media';
import Edit from './Edit.svelte'; import Edit from './Edit.svelte';
import twemoji from 'twemoji'; import twemoji from 'twemoji';
import Privacy from './Privacy.svelte'; import Privacy from './Privacy.svelte';
@ -124,7 +125,7 @@
{@html htmlDescription && htmlDescription} {@html htmlDescription && htmlDescription}
</div> </div>
{#if (group.banner && ((settings && settings.appearance.banner_bottom) || !settings))} {#if (group.banner && ((settings && settings.appearance.banner_bottom) || !settings))}
<img src={group.banner} alt="group banner" class="w-100 mb-3 rounded" style="max-height: 12em; object-fit: cover"/> <img src={resizeMedia(group.banner, [1200, 480])} alt="group banner" class="w-100 mb-3 rounded" style="max-height: 13em; object-fit: cover"/>
{/if} {/if}
{#if !isPublic} {#if !isPublic}

View File

@ -4,6 +4,7 @@
import moment from 'moment'; import moment from 'moment';
import { toHTML } from 'discord-markdown'; import { toHTML } from 'discord-markdown';
import parseTimestamps from '../../api/parse-timestamps'; import parseTimestamps from '../../api/parse-timestamps';
import resizeMedia from '../../api/resize-media';
import twemoji from 'twemoji'; import twemoji from 'twemoji';
import GroupEdit from './GroupEdit.svelte'; import GroupEdit from './GroupEdit.svelte';
@ -174,7 +175,7 @@
{@html htmlDescription && htmlDescription} {@html htmlDescription && htmlDescription}
</div> </div>
{#if (member.banner && ((settings && settings.appearance.banner_bottom) || !settings))} {#if (member.banner && ((settings && settings.appearance.banner_bottom) || !settings))}
<img src={member.banner} alt="member banner" class="w-100 mb-3 rounded" style="max-height: 17em; object-fit: cover"/> <img src={resizeMedia(member.banner, [1200, 480])} alt="member banner" class="w-100 mb-3 rounded" style="max-height: 13em; object-fit: cover"/>
{/if} {/if}
{#if !isPublic} {#if !isPublic}
<Button style="flex: 0" color="primary" on:click={() => editMode = true} aria-label="edit member information">Edit</Button> <Button style="flex: 0" color="primary" on:click={() => editMode = true} aria-label="edit member information">Edit</Button>

View File

@ -3,6 +3,7 @@
import moment from 'moment'; import moment from 'moment';
import { toHTML } from 'discord-markdown'; import { toHTML } from 'discord-markdown';
import parseTimestamps from '../../api/parse-timestamps'; import parseTimestamps from '../../api/parse-timestamps';
import resizeMedia from '../../api/resize-media';
import twemoji from 'twemoji'; import twemoji from 'twemoji';
import { System } from '../../api/types'; import { System } from '../../api/types';
@ -88,7 +89,7 @@
{@html htmlDescription} {@html htmlDescription}
</div> </div>
{#if (user.banner && ((settings && settings.appearance.banner_bottom) || !settings))} {#if (user.banner && ((settings && settings.appearance.banner_bottom) || !settings))}
<img src={user.banner} alt="system banner" class="w-100 mb-3 rounded" style="max-height: 12em; object-fit: cover"/> <img src={resizeMedia(user.banner, [1200, 480])} alt="system banner" class="w-100 mb-3 rounded" style="max-height: 13em; object-fit: cover"/>
{/if} {/if}
{#if !isPublic} {#if !isPublic}
<Button style="flex: 0" color="primary" on:click={() => editMode = true} aria-label="edit system information">Edit</Button> <Button style="flex: 0" color="primary" on:click={() => editMode = true} aria-label="edit system information">Edit</Button>