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:
parent
7aa4d49a97
commit
adaddb579e
18
dashboard/src/api/resize-media.ts
Normal file
18
dashboard/src/api/resize-media.ts
Normal 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
|
@ -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}
|
||||||
|
@ -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}
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user