Equicord/src/plugins/fixImagesQuality/index.tsx

106 lines
4.1 KiB
TypeScript

/*
* Vencord, a Discord client mod
* Copyright (c) 2024 Vendicated and contributors
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { definePluginSettings } from "@api/Settings";
import { Card } from "@components/Card";
import { Flex } from "@components/Flex";
import { Margins } from "@components/margins";
import { Paragraph } from "@components/Paragraph";
import { Devs } from "@utils/constants";
import { Logger } from "@utils/Logger";
import definePlugin, { OptionType } from "@utils/types";
const settings = definePluginSettings({
originalImagesInChat: {
type: OptionType.BOOLEAN,
description: "Also load the original image in Chat. WARNING: Read the caveats above",
default: false,
}
});
export default definePlugin({
name: "FixImagesQuality",
description: "Improves quality of images by loading them at their original resolution",
authors: [Devs.Nuckyz, Devs.Ven],
settings,
patches: [
{
find: ".handleImageLoad)",
replacement: {
match: /getSrc\(\i\)\{/,
replace: "$&var _vcSrc=$self.getSrc(this.props,arguments[1]);if(_vcSrc)return _vcSrc;"
}
}
],
settingsAboutComponent() {
return (
<Card variant="normal">
<Flex flexDirection="column" gap="4px">
<Paragraph size="md" weight="semibold">The default behaviour is the following:</Paragraph>
<Paragraph>
<ul>
<li>&mdash; In chat, optimised but full resolution images will be loaded.</li>
<li>&mdash; In the image modal, the original image will be loaded.</li>
</ul>
</Paragraph>
<Paragraph size="md" weight="semibold" className={Margins.top8}>You can also enable original image in chat, but beware of the following caveats:</Paragraph>
<Paragraph>
<ul>
<li>&mdash; Animated images (GIF, WebP, etc.) in chat will always animate, regardless of if the App is focused.</li>
<li>&mdash; May cause lag.</li>
</ul>
</Paragraph>
</Flex>
</Card>
);
},
getSrc(props: { src: string; width: number; height: number; contentType: string; mosaicStyleAlt?: boolean; trigger?: string; }, freeze?: boolean) {
if (!props?.src) return;
try {
const { contentType, height, src, width, mosaicStyleAlt, trigger } = props;
// Embed images do not have a content type set.
// It's difficult to differentiate between images and videos. but mosaicStyleAlt seems exclusive to images
const isImage = contentType?.startsWith("image/") ?? (typeof mosaicStyleAlt === "boolean");
if (!isImage || src.startsWith("data:")) return;
const url = new URL(src);
if (!url.pathname.startsWith("/attachments/")) return;
url.searchParams.set("animated", String(!freeze));
if (freeze && url.pathname.endsWith(".gif")) {
// gifs don't support animated=false, so we have no choice but to use webp
url.searchParams.set("format", "webp");
}
const isModal = !!trigger;
if (!settings.store.originalImagesInChat && !isModal) {
// make sure the image is not too large
const pixels = width * height;
const limit = 2000 * 1200;
if (pixels <= limit)
return url.toString();
const scale = Math.sqrt(pixels / limit);
url.searchParams.set("width", Math.round(width / scale).toString());
url.searchParams.set("height", Math.round(height / scale).toString());
return url.toString();
}
url.hostname = "cdn.discordapp.com";
return url.toString();
} catch (e) {
new Logger("FixImagesQuality").error("Failed to make image src", e);
return;
}
}
});