fix some random destructoring
This commit is contained in:
parent
c95f667229
commit
4f5935be9b
6 changed files with 175 additions and 196 deletions
|
|
@ -227,14 +227,12 @@ function buildTooltipContent(el: Element, computed: CSSStyleDeclaration, rect: D
|
|||
html += `<div class="${cl("color")}"><span class="${cl("swatch")}" style="--c:${computed.color}"></span>${colorVar}</div>`;
|
||||
html += `<div class="${cl("hex")}">${hex}</div>`;
|
||||
|
||||
const { store } = settings;
|
||||
|
||||
if (store.showId) {
|
||||
if (settings.store.showId) {
|
||||
const { id } = el;
|
||||
if (id) html += `<div class="${cl("info")}"><span class="${cl("label")}">id:</span> #${id}</div>`;
|
||||
}
|
||||
|
||||
if (store.showClasses) {
|
||||
if (settings.store.showClasses) {
|
||||
const classes = el.className;
|
||||
if (classes && typeof classes === "string") {
|
||||
const truncated = classes.length > 60 ? classes.slice(0, 60) + "…" : classes;
|
||||
|
|
@ -242,28 +240,28 @@ function buildTooltipContent(el: Element, computed: CSSStyleDeclaration, rect: D
|
|||
}
|
||||
}
|
||||
|
||||
if (store.showFont) {
|
||||
if (settings.store.showFont) {
|
||||
const font = computed.fontFamily.split(",")[0].replace(/["']/g, "");
|
||||
const size = computed.fontSize;
|
||||
html += `<div class="${cl("info")}"><span class="${cl("label")}">font:</span> ${font} ${size}</div>`;
|
||||
}
|
||||
|
||||
if (store.showPadding) {
|
||||
if (settings.store.showPadding) {
|
||||
const padding = formatBoxValue(computed.padding);
|
||||
if (padding) html += `<div class="${cl("info")}"><span class="${cl("label")}">padding:</span> ${padding}</div>`;
|
||||
}
|
||||
|
||||
if (store.showMargin) {
|
||||
if (settings.store.showMargin) {
|
||||
const margin = formatBoxValue(computed.margin);
|
||||
if (margin) html += `<div class="${cl("info")}"><span class="${cl("label")}">margin:</span> ${margin}</div>`;
|
||||
}
|
||||
|
||||
if (store.showBorderRadius) {
|
||||
if (settings.store.showBorderRadius) {
|
||||
const radius = formatBoxValue(computed.borderRadius);
|
||||
if (radius) html += `<div class="${cl("info")}"><span class="${cl("label")}">radius:</span> ${radius}</div>`;
|
||||
}
|
||||
|
||||
if (store.showPosition) {
|
||||
if (settings.store.showPosition) {
|
||||
const pos = computed.position;
|
||||
if (pos !== "static") {
|
||||
const zIndex = computed.zIndex !== "auto" ? ` z:${computed.zIndex}` : "";
|
||||
|
|
@ -271,7 +269,7 @@ function buildTooltipContent(el: Element, computed: CSSStyleDeclaration, rect: D
|
|||
}
|
||||
}
|
||||
|
||||
if (store.showDisplay) {
|
||||
if (settings.store.showDisplay) {
|
||||
const { display } = computed;
|
||||
let extra = "";
|
||||
if (display === "flex" || display === "inline-flex") {
|
||||
|
|
|
|||
|
|
@ -449,10 +449,9 @@ function ServicePicker(props: {
|
|||
|
||||
function FallbackOrderSettings() {
|
||||
const update = useForceUpdater();
|
||||
const { store } = settings;
|
||||
const [dragIndex, setDragIndex] = React.useState<number | null>(null);
|
||||
const [order, setOrder] = React.useState<ServiceType[]>(() => {
|
||||
const configured = (store.fallbackOrder || defaultFallbackOrder)
|
||||
const configured = (settings.store.fallbackOrder || defaultFallbackOrder)
|
||||
.split(/[\n,]/)
|
||||
.map(entry => entry.trim())
|
||||
.filter((entry): entry is ServiceType => Object.values(ServiceType).includes(entry as ServiceType));
|
||||
|
|
@ -464,7 +463,7 @@ function FallbackOrderSettings() {
|
|||
|
||||
const commitOrder = (nextOrder: ServiceType[]) => {
|
||||
setOrder(nextOrder);
|
||||
store.fallbackOrder = nextOrder.join(",");
|
||||
settings.store.fallbackOrder = nextOrder.join(",");
|
||||
update();
|
||||
};
|
||||
|
||||
|
|
@ -515,24 +514,12 @@ function FallbackOrderSettings() {
|
|||
|
||||
export function SettingsComponent() {
|
||||
const update = useForceUpdater();
|
||||
const { store } = settings;
|
||||
const sharexFileInputRef = React.useRef<HTMLInputElement>(null);
|
||||
const isNest = store.serviceType === ServiceType.NEST;
|
||||
const isEzHost = store.serviceType === ServiceType.EZHOST;
|
||||
const isEncryptingHost = store.serviceType === ServiceType.ENCRYPTINGHOST;
|
||||
const isS3 = store.serviceType === ServiceType.S3;
|
||||
const isZipline = store.serviceType === ServiceType.ZIPLINE;
|
||||
const isCatbox = store.serviceType === ServiceType.CATBOX;
|
||||
const isLitterbox = store.serviceType === ServiceType.LITTERBOX;
|
||||
const isGofile = store.serviceType === ServiceType.GOFILE;
|
||||
const isPixelVault = store.serviceType === ServiceType.PIXELVAULT;
|
||||
const isPixelDrain = store.serviceType === ServiceType.PIXELDRAIN;
|
||||
const isShareX = store.serviceType === ServiceType.SHAREX;
|
||||
const isWebdav = store.serviceType === ServiceType.WEBDAV;
|
||||
const { serviceType } = settings.store;
|
||||
|
||||
const validateShareXConfig = () => {
|
||||
try {
|
||||
parseShareXConfig(store.sharexConfig || "");
|
||||
parseShareXConfig(settings.store.sharexConfig || "");
|
||||
showToast("ShareX config is valid", Toasts.Type.SUCCESS);
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : "Invalid ShareX config";
|
||||
|
|
@ -553,7 +540,7 @@ export function SettingsComponent() {
|
|||
try {
|
||||
const content = String(e.target?.result || "");
|
||||
const parsed = parseShareXConfig(content);
|
||||
store.sharexConfig = JSON.stringify(parsed, null, 2);
|
||||
settings.store.sharexConfig = JSON.stringify(parsed, null, 2);
|
||||
update();
|
||||
showToast("Imported ShareX config", Toasts.Type.SUCCESS);
|
||||
} catch (error) {
|
||||
|
|
@ -569,79 +556,79 @@ export function SettingsComponent() {
|
|||
<>
|
||||
<SettingsSection id="upload-service" name="Upload Service" description="Choose where FileUpload sends new files.">
|
||||
<ServicePicker
|
||||
value={store.serviceType as ServiceType}
|
||||
value={settings.store.serviceType as ServiceType}
|
||||
onChange={service => {
|
||||
store.serviceType = service;
|
||||
settings.store.serviceType = service;
|
||||
update();
|
||||
}}
|
||||
/>
|
||||
</SettingsSection>
|
||||
|
||||
{isZipline && (
|
||||
{serviceType === ServiceType.ZIPLINE && (
|
||||
<SettingGroup name="Zipline" description="Connection details for your Zipline instance.">
|
||||
<SettingTextInput
|
||||
name="Service URL"
|
||||
description="The URL of your Zipline instance"
|
||||
value={store.serviceUrl}
|
||||
onChange={v => store.serviceUrl = v}
|
||||
value={settings.store.serviceUrl}
|
||||
onChange={v => settings.store.serviceUrl = v}
|
||||
placeholder="https://your-zipline-instance.com"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Zipline Token"
|
||||
description="Your Zipline API authorization token"
|
||||
value={store.ziplineToken}
|
||||
onChange={v => store.ziplineToken = v}
|
||||
value={settings.store.ziplineToken}
|
||||
onChange={v => settings.store.ziplineToken = v}
|
||||
placeholder="Your Zipline API token"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Folder ID"
|
||||
description="Folder ID for uploads (leave empty for no folder)"
|
||||
value={store.folderId}
|
||||
onChange={v => store.folderId = v}
|
||||
value={settings.store.folderId}
|
||||
onChange={v => settings.store.folderId = v}
|
||||
placeholder="Leave empty for no folder"
|
||||
/>
|
||||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isEzHost && (
|
||||
{serviceType === ServiceType.EZHOST && (
|
||||
<SettingGroup name="E-Z Host" description="Connection details for E-Z Host uploads.">
|
||||
<SettingTextInput
|
||||
name="E-Z Host API Key"
|
||||
description="Your E-Z Host API key"
|
||||
value={store.ezHostKey}
|
||||
onChange={v => store.ezHostKey = v}
|
||||
value={settings.store.ezHostKey}
|
||||
onChange={v => settings.store.ezHostKey = v}
|
||||
placeholder="Your E-Z Host API key"
|
||||
/>
|
||||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isNest && (
|
||||
{serviceType === ServiceType.NEST && (
|
||||
<SettingGroup name="Nest" description="Connection details for Nest uploads.">
|
||||
<SettingTextInput
|
||||
name="Nest Token"
|
||||
description="Your Nest API authorization token"
|
||||
value={store.nestToken}
|
||||
onChange={v => store.nestToken = v}
|
||||
value={settings.store.nestToken}
|
||||
onChange={v => settings.store.nestToken = v}
|
||||
placeholder="Your Nest API token"
|
||||
/>
|
||||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isEncryptingHost && (
|
||||
{serviceType === ServiceType.ENCRYPTINGHOST && (
|
||||
<SettingGroup name="Encrypting.host" description="Connection details for Encrypting.host uploads.">
|
||||
<SettingTextInput
|
||||
name="Encrypting.host API Key"
|
||||
description="Your Encrypting.host API key"
|
||||
value={(store as { encryptingHostKey?: string; }).encryptingHostKey || ""}
|
||||
onChange={v => (store as { encryptingHostKey?: string; }).encryptingHostKey = v}
|
||||
value={(settings.store as { encryptingHostKey?: string; }).encryptingHostKey || ""}
|
||||
onChange={v => (settings.store as { encryptingHostKey?: string; }).encryptingHostKey = v}
|
||||
placeholder="Your Encrypting.host API key"
|
||||
/>
|
||||
<SettingsSection id="url-style" name="URL Style" description="How Encrypting.host should format returned links.">
|
||||
<Select
|
||||
options={encryptingHostUrlStyleOptions}
|
||||
isSelected={v => v === (store as { encryptingHostUrlStyle?: string; }).encryptingHostUrlStyle}
|
||||
isSelected={v => v === (settings.store as { encryptingHostUrlStyle?: string; }).encryptingHostUrlStyle}
|
||||
select={v => {
|
||||
(store as { encryptingHostUrlStyle?: string; }).encryptingHostUrlStyle = v;
|
||||
(settings.store as { encryptingHostUrlStyle?: string; }).encryptingHostUrlStyle = v;
|
||||
update();
|
||||
}}
|
||||
serialize={v => v}
|
||||
|
|
@ -650,122 +637,122 @@ export function SettingsComponent() {
|
|||
</SettingsSection>
|
||||
<SettingsSection id="domains-json" name="Domains JSON" description={"JSON array of domains to use, for example [\"offensive\"]."}>
|
||||
<TextArea
|
||||
value={(store as { encryptingHostDomains?: string; }).encryptingHostDomains || ""}
|
||||
value={(settings.store as { encryptingHostDomains?: string; }).encryptingHostDomains || ""}
|
||||
rows={3}
|
||||
placeholder='["offensive"]'
|
||||
onChange={v => (store as { encryptingHostDomains?: string; }).encryptingHostDomains = v}
|
||||
onChange={v => (settings.store as { encryptingHostDomains?: string; }).encryptingHostDomains = v}
|
||||
/>
|
||||
</SettingsSection>
|
||||
<SettingTextInput
|
||||
name="Embed Title"
|
||||
description="Optional title for embed style responses."
|
||||
value={(store as { encryptingHostTitle?: string; }).encryptingHostTitle || ""}
|
||||
onChange={v => (store as { encryptingHostTitle?: string; }).encryptingHostTitle = v}
|
||||
value={(settings.store as { encryptingHostTitle?: string; }).encryptingHostTitle || ""}
|
||||
onChange={v => (settings.store as { encryptingHostTitle?: string; }).encryptingHostTitle = v}
|
||||
placeholder="Optional title"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Embed Color"
|
||||
description="Optional color for embed style responses."
|
||||
value={(store as { encryptingHostColor?: string; }).encryptingHostColor || ""}
|
||||
onChange={v => (store as { encryptingHostColor?: string; }).encryptingHostColor = v}
|
||||
value={(settings.store as { encryptingHostColor?: string; }).encryptingHostColor || ""}
|
||||
onChange={v => (settings.store as { encryptingHostColor?: string; }).encryptingHostColor = v}
|
||||
placeholder="Optional color"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Fake Link"
|
||||
description="Optional fake link value for fakelink style responses."
|
||||
value={(store as { encryptingHostFakelink?: string; }).encryptingHostFakelink || ""}
|
||||
onChange={v => (store as { encryptingHostFakelink?: string; }).encryptingHostFakelink = v}
|
||||
value={(settings.store as { encryptingHostFakelink?: string; }).encryptingHostFakelink || ""}
|
||||
onChange={v => (settings.store as { encryptingHostFakelink?: string; }).encryptingHostFakelink = v}
|
||||
placeholder="Optional fake link"
|
||||
/>
|
||||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isS3 && (
|
||||
{serviceType === ServiceType.S3 && (
|
||||
<SettingGroup name="S3-Compatible Storage" description="Connection details and object naming for your bucket.">
|
||||
<SettingTextInput
|
||||
name="S3 Endpoint URL"
|
||||
description="S3-compatible endpoint (e.g. https://<accountid>.r2.cloudflarestorage.com)"
|
||||
value={store.s3Endpoint}
|
||||
onChange={v => store.s3Endpoint = v}
|
||||
value={settings.store.s3Endpoint}
|
||||
onChange={v => settings.store.s3Endpoint = v}
|
||||
placeholder="https://your-endpoint.example.com"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Bucket Name"
|
||||
description="Bucket to upload into"
|
||||
value={store.s3Bucket}
|
||||
onChange={v => store.s3Bucket = v}
|
||||
value={settings.store.s3Bucket}
|
||||
onChange={v => settings.store.s3Bucket = v}
|
||||
placeholder="my-bucket"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Region"
|
||||
description="AWS region or auto for Cloudflare R2"
|
||||
value={store.s3Region}
|
||||
onChange={v => store.s3Region = v}
|
||||
value={settings.store.s3Region}
|
||||
onChange={v => settings.store.s3Region = v}
|
||||
placeholder="auto"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Access Key ID"
|
||||
description="S3-compatible access key"
|
||||
value={store.s3AccessKeyId}
|
||||
onChange={v => store.s3AccessKeyId = v}
|
||||
value={settings.store.s3AccessKeyId}
|
||||
onChange={v => settings.store.s3AccessKeyId = v}
|
||||
placeholder="Your access key ID"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Secret Access Key"
|
||||
description="S3-compatible secret key"
|
||||
value={store.s3SecretAccessKey}
|
||||
onChange={v => store.s3SecretAccessKey = v}
|
||||
value={settings.store.s3SecretAccessKey}
|
||||
onChange={v => settings.store.s3SecretAccessKey = v}
|
||||
placeholder="Your secret access key"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Session Token"
|
||||
description="Optional temporary credential token"
|
||||
value={store.s3SessionToken}
|
||||
onChange={v => store.s3SessionToken = v}
|
||||
value={settings.store.s3SessionToken}
|
||||
onChange={v => settings.store.s3SessionToken = v}
|
||||
placeholder="Optional session token"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Public Base URL"
|
||||
description="Optional public URL base to use for returned links"
|
||||
value={store.s3PublicUrl}
|
||||
onChange={v => store.s3PublicUrl = v}
|
||||
value={settings.store.s3PublicUrl}
|
||||
onChange={v => settings.store.s3PublicUrl = v}
|
||||
placeholder="https://cdn.example.com"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Object Key Prefix"
|
||||
description="Optional folder/prefix inside the bucket"
|
||||
value={store.s3Prefix}
|
||||
onChange={v => store.s3Prefix = v}
|
||||
value={settings.store.s3Prefix}
|
||||
onChange={v => settings.store.s3Prefix = v}
|
||||
placeholder="uploads/discord"
|
||||
/>
|
||||
<SettingSwitch
|
||||
name="Use Path-Style Endpoint"
|
||||
description="Use endpoint/bucket/key format, recommended for R2."
|
||||
checked={store.s3ForcePathStyle}
|
||||
onChange={v => store.s3ForcePathStyle = v}
|
||||
checked={settings.store.s3ForcePathStyle}
|
||||
onChange={v => settings.store.s3ForcePathStyle = v}
|
||||
/>
|
||||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isCatbox && (
|
||||
{serviceType === ServiceType.CATBOX && (
|
||||
<SettingGroup name="Catbox" description="Optional account binding for Catbox uploads.">
|
||||
<SettingTextInput
|
||||
name="Catbox Userhash"
|
||||
description="Your Catbox userhash for account binding, leave empty for anonymous uploads."
|
||||
value={store.catboxUserhash}
|
||||
onChange={v => store.catboxUserhash = v}
|
||||
value={settings.store.catboxUserhash}
|
||||
onChange={v => settings.store.catboxUserhash = v}
|
||||
placeholder="Your Catbox userhash"
|
||||
/>
|
||||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isLitterbox && (
|
||||
{serviceType === ServiceType.LITTERBOX && (
|
||||
<SettingsSection id="litterbox-expiry" name="Litterbox Expiry" description="How long uploads are retained">
|
||||
<Select
|
||||
options={litterboxOptions}
|
||||
isSelected={v => v === store.litterboxExpiry}
|
||||
isSelected={v => v === settings.store.litterboxExpiry}
|
||||
select={v => {
|
||||
store.litterboxExpiry = v;
|
||||
settings.store.litterboxExpiry = v;
|
||||
update();
|
||||
}}
|
||||
serialize={v => v}
|
||||
|
|
@ -774,43 +761,43 @@ export function SettingsComponent() {
|
|||
</SettingsSection>
|
||||
)}
|
||||
|
||||
{isGofile && (
|
||||
{serviceType === ServiceType.GOFILE && (
|
||||
<SettingGroup name="GoFile" description="Optional account binding for GoFile uploads.">
|
||||
<SettingTextInput
|
||||
name="GoFile Token"
|
||||
description="Optional GoFile token to upload into your account."
|
||||
value={store.gofileToken}
|
||||
onChange={v => store.gofileToken = v}
|
||||
value={settings.store.gofileToken}
|
||||
onChange={v => settings.store.gofileToken = v}
|
||||
placeholder="Optional GoFile token"
|
||||
/>
|
||||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isPixelVault && (
|
||||
{serviceType === ServiceType.PIXELVAULT && (
|
||||
<SettingGroup name="PixelVault" description="Connection details for PixelVault uploads.">
|
||||
<SettingTextInput
|
||||
name="PixelVault Upload Key"
|
||||
description="Your PixelVault authorization key."
|
||||
value={store.pixelVaultKey}
|
||||
onChange={v => store.pixelVaultKey = v}
|
||||
value={settings.store.pixelVaultKey}
|
||||
onChange={v => settings.store.pixelVaultKey = v}
|
||||
placeholder="Your PixelVault upload key"
|
||||
/>
|
||||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isPixelDrain && (
|
||||
{serviceType === ServiceType.PIXELDRAIN && (
|
||||
<SettingGroup name="PixelDrain" description="Optional account binding for PixelDrain uploads.">
|
||||
<SettingTextInput
|
||||
name="PixelDrain API Key"
|
||||
description="Optional PixelDrain API key for authenticated uploads. Leave empty for anonymous uploads."
|
||||
value={store.pixelDrainKey}
|
||||
onChange={v => store.pixelDrainKey = v}
|
||||
value={settings.store.pixelDrainKey}
|
||||
onChange={v => settings.store.pixelDrainKey = v}
|
||||
placeholder="Your PixelDrain API key"
|
||||
/>
|
||||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isShareX && (
|
||||
{serviceType === ServiceType.SHAREX && (
|
||||
<SettingGroup name="ShareX Custom Uploader" description="Paste, import, or validate a ShareX custom uploader config.">
|
||||
<SettingsSection
|
||||
id="sharex-custom-uploader-config"
|
||||
|
|
@ -818,10 +805,10 @@ export function SettingsComponent() {
|
|||
description="Paste your ShareX custom uploader JSON (.sxcu/.json). DestinationType must include FileUploader or ImageUploader."
|
||||
>
|
||||
<TextArea
|
||||
value={store.sharexConfig}
|
||||
value={settings.store.sharexConfig}
|
||||
rows={10}
|
||||
placeholder='{"RequestMethod":"POST","RequestURL":"https://example.com/api/upload","Body":"MultipartFormData"}'
|
||||
onChange={v => store.sharexConfig = v}
|
||||
onChange={v => settings.store.sharexConfig = v}
|
||||
/>
|
||||
</SettingsSection>
|
||||
<SettingsSection id="sharex-config-actions" name="ShareX Config Actions" description="Import from file or validate pasted config">
|
||||
|
|
@ -840,34 +827,34 @@ export function SettingsComponent() {
|
|||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isWebdav && (
|
||||
{serviceType === ServiceType.WEBDAV && (
|
||||
<SettingGroup name="WebDAV" description="Connection details for WebDAV servers (Nextcloud, Owncloud, etc.).">
|
||||
<SettingTextInput
|
||||
name="Server URL"
|
||||
description="Base WebDAV URL (e.g. https://nextcloud.example.com/remote.php/dav/files/username)"
|
||||
value={store.webdavUrl}
|
||||
onChange={v => store.webdavUrl = v}
|
||||
value={settings.store.webdavUrl}
|
||||
onChange={v => settings.store.webdavUrl = v}
|
||||
placeholder="https://nextcloud.example.com/remote.php/dav/files/username"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Username"
|
||||
description="WebDAV username"
|
||||
value={store.webdavUsername}
|
||||
onChange={v => store.webdavUsername = v}
|
||||
value={settings.store.webdavUsername}
|
||||
onChange={v => settings.store.webdavUsername = v}
|
||||
placeholder="username"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Password or App Token"
|
||||
description="WebDAV password or app token"
|
||||
value={store.webdavPassword}
|
||||
onChange={v => store.webdavPassword = v}
|
||||
value={settings.store.webdavPassword}
|
||||
onChange={v => settings.store.webdavPassword = v}
|
||||
placeholder="password or app token"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Upload Directory"
|
||||
description="Optional subdirectory on the server to upload into (e.g. uploads)"
|
||||
value={store.webdavDirectory}
|
||||
onChange={v => store.webdavDirectory = v}
|
||||
value={settings.store.webdavDirectory}
|
||||
onChange={v => settings.store.webdavDirectory = v}
|
||||
placeholder="Leave empty for root directory"
|
||||
/>
|
||||
<SettingsSection id="server-type" name="Server Type" description="Select your WebDAV server type. Nextcloud and ownCloud will create a public share link. Generic returns the raw file URL.">
|
||||
|
|
@ -877,16 +864,16 @@ export function SettingsComponent() {
|
|||
{ label: "ownCloud", value: "owncloud" },
|
||||
{ label: "Generic WebDAV", value: "generic" }
|
||||
]}
|
||||
isSelected={v => v === store.webdavServerType}
|
||||
isSelected={v => v === settings.store.webdavServerType}
|
||||
select={v => {
|
||||
store.webdavServerType = v;
|
||||
settings.store.webdavServerType = v;
|
||||
update();
|
||||
}}
|
||||
serialize={v => v}
|
||||
placeholder="Select server type"
|
||||
/>
|
||||
</SettingsSection>
|
||||
{store.webdavServerType !== "generic" && (
|
||||
{settings.store.webdavServerType !== "generic" && (
|
||||
<SettingsSection id="share-link-format" name="Share Link Format" description="How to return the public share link. Share Page links to a web page; Direct Download links straight to the file; Markdown Link wraps the share page in a clickable filename.">
|
||||
<Select
|
||||
options={[
|
||||
|
|
@ -894,9 +881,9 @@ export function SettingsComponent() {
|
|||
{ label: "Direct Download", value: "direct-download" },
|
||||
{ label: "Markdown Link", value: "markdown" }
|
||||
]}
|
||||
isSelected={v => v === store.webdavShareType}
|
||||
isSelected={v => v === settings.store.webdavShareType}
|
||||
select={v => {
|
||||
store.webdavShareType = v;
|
||||
settings.store.webdavShareType = v;
|
||||
update();
|
||||
}}
|
||||
serialize={v => v}
|
||||
|
|
@ -911,27 +898,27 @@ export function SettingsComponent() {
|
|||
<SettingSwitch
|
||||
name="Strip Query Parameters"
|
||||
description="Strip query parameters from the uploaded file URL."
|
||||
checked={store.stripQueryParams}
|
||||
onChange={v => store.stripQueryParams = v}
|
||||
checked={settings.store.stripQueryParams}
|
||||
onChange={v => settings.store.stripQueryParams = v}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
name="Use Embed Proxy"
|
||||
description="Wrap uploaded video links with an embed proxy service for better Discord previews."
|
||||
checked={store.embedProxyEnabled}
|
||||
checked={settings.store.embedProxyEnabled}
|
||||
onChange={v => {
|
||||
store.embedProxyEnabled = v;
|
||||
settings.store.embedProxyEnabled = v;
|
||||
update();
|
||||
}}
|
||||
/>
|
||||
|
||||
{store.embedProxyEnabled && (
|
||||
{settings.store.embedProxyEnabled && (
|
||||
<SettingsSection id="embed-proxy-service" name="Embed Proxy Service" description="Choose which embed proxy service to use for uploaded video links">
|
||||
<Select
|
||||
options={embedProxyOptions}
|
||||
isSelected={v => v === store.embedProxyService}
|
||||
isSelected={v => v === settings.store.embedProxyService}
|
||||
select={v => {
|
||||
store.embedProxyService = v;
|
||||
settings.store.embedProxyService = v;
|
||||
update();
|
||||
}}
|
||||
serialize={v => v}
|
||||
|
|
@ -943,43 +930,43 @@ export function SettingsComponent() {
|
|||
<SettingSwitch
|
||||
name="Convert APNG to GIF"
|
||||
description="Convert APNG files to GIF format."
|
||||
checked={store.apngToGif}
|
||||
onChange={v => store.apngToGif = v}
|
||||
checked={settings.store.apngToGif}
|
||||
onChange={v => settings.store.apngToGif = v}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
name="Preserve Original Filename"
|
||||
description="Use the original filename instead of naming uploads as upload.ext."
|
||||
checked={Boolean((store as { preserveOriginalFilename?: boolean; }).preserveOriginalFilename)}
|
||||
onChange={v => (store as { preserveOriginalFilename?: boolean; }).preserveOriginalFilename = v}
|
||||
checked={Boolean((settings.store as { preserveOriginalFilename?: boolean; }).preserveOriginalFilename)}
|
||||
onChange={v => (settings.store as { preserveOriginalFilename?: boolean; }).preserveOriginalFilename = v}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
name="Auto Copy URL"
|
||||
description="Automatically copy the uploaded file URL to clipboard."
|
||||
checked={store.autoCopy}
|
||||
onChange={v => store.autoCopy = v}
|
||||
checked={settings.store.autoCopy}
|
||||
onChange={v => settings.store.autoCopy = v}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
name="Disable Fallback Uploaders"
|
||||
description="Only use the selected uploader without trying fallback hosts."
|
||||
checked={store.disableFallbacks}
|
||||
onChange={v => store.disableFallbacks = v}
|
||||
checked={settings.store.disableFallbacks}
|
||||
onChange={v => settings.store.disableFallbacks = v}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
name="Insert URL into Chat Input"
|
||||
description="After upload, insert the resulting URL into the current chat input."
|
||||
checked={store.autoSend}
|
||||
onChange={v => store.autoSend = v}
|
||||
checked={settings.store.autoSend}
|
||||
onChange={v => settings.store.autoSend = v}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
name="Format Inserted URL"
|
||||
description="Wrap inserted URLs in angle brackets to avoid Discord preview embedding."
|
||||
checked={store.autoFormat}
|
||||
onChange={v => store.autoFormat = v}
|
||||
checked={settings.store.autoFormat}
|
||||
onChange={v => settings.store.autoFormat = v}
|
||||
/>
|
||||
</SettingGroup>
|
||||
|
||||
|
|
@ -987,29 +974,29 @@ export function SettingsComponent() {
|
|||
<SettingSwitch
|
||||
name="Bypass Discord Upload Button"
|
||||
description="Use FileUpload when uploading through Discord's file picker."
|
||||
checked={Boolean((store as { bypassDiscordUpload?: boolean; }).bypassDiscordUpload)}
|
||||
onChange={v => (store as { bypassDiscordUpload?: boolean; }).bypassDiscordUpload = v}
|
||||
checked={Boolean((settings.store as { bypassDiscordUpload?: boolean; }).bypassDiscordUpload)}
|
||||
onChange={v => (settings.store as { bypassDiscordUpload?: boolean; }).bypassDiscordUpload = v}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
name="Auto Upload Pasted Files"
|
||||
description="Automatically upload files from clipboard to image host when pasting in chat input."
|
||||
checked={store.autoUploadPastedFiles}
|
||||
onChange={v => store.autoUploadPastedFiles = v}
|
||||
checked={settings.store.autoUploadPastedFiles}
|
||||
onChange={v => settings.store.autoUploadPastedFiles = v}
|
||||
/>
|
||||
|
||||
<SettingSwitch
|
||||
name="Respect Discord File Size Limit"
|
||||
description="Use FileUpload only for files larger than your current Discord upload limit."
|
||||
checked={store.bypassDiscordUploadOnlyOverLimit}
|
||||
onChange={v => store.bypassDiscordUploadOnlyOverLimit = v}
|
||||
checked={settings.store.bypassDiscordUploadOnlyOverLimit}
|
||||
onChange={v => settings.store.bypassDiscordUploadOnlyOverLimit = v}
|
||||
/>
|
||||
|
||||
<SettingTextInput
|
||||
name="Allowed File Types"
|
||||
description="Comma-separated list of extensions (e.g. png,jpg,gif). Leave empty to allow all."
|
||||
value={store.uploadAllowedFileTypes}
|
||||
onChange={v => store.uploadAllowedFileTypes = v}
|
||||
value={settings.store.uploadAllowedFileTypes}
|
||||
onChange={v => settings.store.uploadAllowedFileTypes = v}
|
||||
placeholder="png,jpg,gif,mp4,webp"
|
||||
/>
|
||||
</SettingGroup>
|
||||
|
|
@ -1018,8 +1005,8 @@ export function SettingsComponent() {
|
|||
<SettingTextInput
|
||||
name="CORS Proxy URL"
|
||||
description="CORS proxy used for web uploads. Leave empty to use the default proxy."
|
||||
value={store.corsProxyUrl || ""}
|
||||
onChange={v => store.corsProxyUrl = v}
|
||||
value={settings.store.corsProxyUrl || ""}
|
||||
onChange={v => settings.store.corsProxyUrl = v}
|
||||
placeholder="https://your-cors-proxy.example.com"
|
||||
/>
|
||||
|
||||
|
|
@ -1036,9 +1023,9 @@ export function SettingsComponent() {
|
|||
{ label: "5 minutes", value: 300000, default: true },
|
||||
{ label: "10 minutes", value: 600000 }
|
||||
]}
|
||||
isSelected={v => v === (store.uploadTimeoutMs || 300000)}
|
||||
isSelected={v => v === (settings.store.uploadTimeoutMs || 300000)}
|
||||
select={v => {
|
||||
store.uploadTimeoutMs = v;
|
||||
settings.store.uploadTimeoutMs = v;
|
||||
update();
|
||||
}}
|
||||
serialize={v => v}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@ function InstallInstructions() {
|
|||
}
|
||||
|
||||
function LyricsProviderSettings() {
|
||||
const { store } = settings;
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsSection id="lyrics-provider" name="Lyrics Provider" description="Where lyrics are fetched from.">
|
||||
|
|
@ -49,14 +47,14 @@ function LyricsProviderSettings() {
|
|||
{ value: Provider.Lrclib, label: "LRCLIB", default: true },
|
||||
{ value: Provider.Spotify, label: "Spotify (Musixmatch)" },
|
||||
]}
|
||||
isSelected={v => v === store.lyricsProvider}
|
||||
select={v => { store.lyricsProvider = v as Provider; }}
|
||||
isSelected={v => v === settings.store.lyricsProvider}
|
||||
select={v => { settings.store.lyricsProvider = v as Provider; }}
|
||||
serialize={v => v}
|
||||
placeholder="Select a lyrics provider"
|
||||
/>
|
||||
</SettingsSection>
|
||||
|
||||
{store.lyricsProvider === Provider.Spotify && (
|
||||
{settings.store.lyricsProvider === Provider.Spotify && (
|
||||
<SettingsSection
|
||||
id="spotify-lyrics-provider"
|
||||
name="Spotify Lyrics API Base URL"
|
||||
|
|
@ -64,9 +62,9 @@ function LyricsProviderSettings() {
|
|||
>
|
||||
<TextInput
|
||||
type="text"
|
||||
value={store.spotifyLyricsApiUrl}
|
||||
value={settings.store.spotifyLyricsApiUrl}
|
||||
onChange={v => {
|
||||
store.spotifyLyricsApiUrl = v;
|
||||
settings.store.spotifyLyricsApiUrl = v;
|
||||
void clearLyricsCache();
|
||||
showToast("Lyrics cache purged", Toasts.Type.SUCCESS);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -605,24 +605,23 @@ async function joinRandomVoice() {
|
|||
return;
|
||||
}
|
||||
|
||||
const { store } = settings;
|
||||
ChannelActions.selectVoiceChannel(channelId);
|
||||
|
||||
if (store.autoNavigate) {
|
||||
if (settings.store.autoNavigate) {
|
||||
ChannelRouter.transitionToChannel(channelId);
|
||||
}
|
||||
|
||||
const postJoinActions: PostJoinAction[] = [];
|
||||
if (store.selfMute && !MediaEngineStore.isSelfMute()) {
|
||||
if (settings.store.selfMute && !MediaEngineStore.isSelfMute()) {
|
||||
postJoinActions.push(() => VoiceActions.toggleSelfMute());
|
||||
}
|
||||
if (store.selfDeafen && !MediaEngineStore.isSelfDeaf()) {
|
||||
if (settings.store.selfDeafen && !MediaEngineStore.isSelfDeaf()) {
|
||||
postJoinActions.push(() => VoiceActions.toggleSelfDeaf());
|
||||
}
|
||||
if (store.autoCamera) {
|
||||
if (settings.store.autoCamera) {
|
||||
postJoinActions.push(enableCamera);
|
||||
}
|
||||
if (store.autoStream) {
|
||||
if (settings.store.autoStream) {
|
||||
postJoinActions.push(() => startChannelStream(channel));
|
||||
}
|
||||
|
||||
|
|
@ -646,16 +645,15 @@ function RandomVoiceButton({ iconForeground, hideTooltips, nameplate }: UserArea
|
|||
|
||||
function RandomVoiceMenu({ onClose }: { onClose(): void; }) {
|
||||
const [, rerender] = React.useReducer(value => value + 1, 0);
|
||||
const { store } = settings;
|
||||
const guilds = getRenderableGuilds();
|
||||
const allServerIds = guilds.map(guild => guild.id);
|
||||
const selectedServerIds = getSelectedServerIds(store) ?? allServerIds;
|
||||
const selectedServerIds = getSelectedServerIds(settings.store) ?? allServerIds;
|
||||
|
||||
const update = <K extends keyof typeof store>(key: K, value: typeof store[K]) => {
|
||||
store[key] = value;
|
||||
const update = <K extends keyof typeof settings.store>(key: K, value: typeof settings.store[K]) => {
|
||||
settings.store[key] = value;
|
||||
rerender();
|
||||
};
|
||||
const toggle = <K extends SelfSettingKey | StateFilterKey | "includeStates" | "avoidStates">(key: K) => update(key, !store[key]);
|
||||
const toggle = <K extends SelfSettingKey | StateFilterKey | "includeStates" | "avoidStates">(key: K) => update(key, !settings.store[key]);
|
||||
const selectAllServers = () => {
|
||||
setServerIds(guilds.map(guild => guild.id));
|
||||
rerender();
|
||||
|
|
@ -675,7 +673,7 @@ function RandomVoiceMenu({ onClose }: { onClose(): void; }) {
|
|||
|
||||
const setSlider = <K extends "UserAmount" | "spacesLeft" | "vcLimit">(key: K) =>
|
||||
debounce((value: number) => {
|
||||
store[key] = Math.round(value);
|
||||
settings.store[key] = Math.round(value);
|
||||
rerender();
|
||||
}, 50);
|
||||
|
||||
|
|
@ -717,7 +715,7 @@ function RandomVoiceMenu({ onClose }: { onClose(): void; }) {
|
|||
key={key}
|
||||
id={`random-voice-filter-${key}`}
|
||||
label={label}
|
||||
checked={store[key]}
|
||||
checked={settings.store[key]}
|
||||
action={() => toggle(key)}
|
||||
/>
|
||||
))}
|
||||
|
|
@ -725,15 +723,15 @@ function RandomVoiceMenu({ onClose }: { onClose(): void; }) {
|
|||
<Menu.MenuCheckboxItem
|
||||
id="random-voice-include-states"
|
||||
label="Include Filters"
|
||||
checked={store.includeStates}
|
||||
disabled={store.avoidStates || !hasStateFilters(store)}
|
||||
checked={settings.store.includeStates}
|
||||
disabled={settings.store.avoidStates || !hasStateFilters(settings.store)}
|
||||
action={() => toggle("includeStates")}
|
||||
/>
|
||||
<Menu.MenuCheckboxItem
|
||||
id="random-voice-avoid-states"
|
||||
label="Avoid Filters"
|
||||
checked={store.avoidStates}
|
||||
disabled={store.includeStates || !hasStateFilters(store)}
|
||||
checked={settings.store.avoidStates}
|
||||
disabled={settings.store.includeStates || !hasStateFilters(settings.store)}
|
||||
action={() => toggle("avoidStates")}
|
||||
/>
|
||||
</>
|
||||
|
|
@ -746,8 +744,8 @@ function RandomVoiceMenu({ onClose }: { onClose(): void; }) {
|
|||
label: "User Amount",
|
||||
sliderKey: "UserAmount",
|
||||
operationKey: "UserAmountOperation",
|
||||
sliderValue: store.UserAmount,
|
||||
operationValue: store.UserAmountOperation,
|
||||
sliderValue: settings.store.UserAmount,
|
||||
operationValue: settings.store.UserAmountOperation,
|
||||
onOperationChange: value => update("UserAmountOperation", value),
|
||||
onSliderChange: setSlider("UserAmount"),
|
||||
})}
|
||||
|
|
@ -759,8 +757,8 @@ function RandomVoiceMenu({ onClose }: { onClose(): void; }) {
|
|||
label: "Spaces Left",
|
||||
sliderKey: "spacesLeft",
|
||||
operationKey: "spacesLeftOperation",
|
||||
sliderValue: store.spacesLeft,
|
||||
operationValue: store.spacesLeftOperation,
|
||||
sliderValue: settings.store.spacesLeft,
|
||||
operationValue: settings.store.spacesLeftOperation,
|
||||
onOperationChange: value => update("spacesLeftOperation", value),
|
||||
onSliderChange: setSlider("spacesLeft"),
|
||||
})}
|
||||
|
|
@ -772,8 +770,8 @@ function RandomVoiceMenu({ onClose }: { onClose(): void; }) {
|
|||
label: "Voice Limit",
|
||||
sliderKey: "vcLimit",
|
||||
operationKey: "vcLimitOperation",
|
||||
sliderValue: store.vcLimit,
|
||||
operationValue: store.vcLimitOperation,
|
||||
sliderValue: settings.store.vcLimit,
|
||||
operationValue: settings.store.vcLimitOperation,
|
||||
onOperationChange: value => update("vcLimitOperation", value),
|
||||
onSliderChange: setSlider("vcLimit"),
|
||||
})}
|
||||
|
|
@ -787,7 +785,7 @@ function RandomVoiceMenu({ onClose }: { onClose(): void; }) {
|
|||
key={key}
|
||||
id={`random-voice-setting-${key}`}
|
||||
label={label}
|
||||
checked={store[key]}
|
||||
checked={settings.store[key]}
|
||||
action={() => toggle(key)}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -89,13 +89,12 @@ async function fetchMediaData(): Promise<JfMediaData | null> {
|
|||
}
|
||||
|
||||
async function getActivity(): Promise<Activity | null> {
|
||||
const { store } = settings;
|
||||
const mediaData = await fetchMediaData();
|
||||
if (!mediaData) return null;
|
||||
|
||||
let richPresenceType: number;
|
||||
if (store.jf_overrideType !== "off") {
|
||||
richPresenceType = parseInt(store.jf_overrideType as string, 10);
|
||||
if (settings.store.jf_overrideType !== "off") {
|
||||
richPresenceType = parseInt(settings.store.jf_overrideType as string, 10);
|
||||
} else {
|
||||
richPresenceType = mediaData.type === "Audio" ? 2 : 3;
|
||||
}
|
||||
|
|
@ -111,25 +110,25 @@ async function getActivity(): Promise<Activity | null> {
|
|||
.replace(/\{year\}/g, mediaData.year?.toString() || "");
|
||||
|
||||
let appName: string;
|
||||
const nameSetting = store.jf_nameDisplay || "default";
|
||||
const nameSetting = settings.store.jf_nameDisplay || "default";
|
||||
|
||||
switch (nameSetting) {
|
||||
case "full":
|
||||
if (mediaData.type === "Episode" && mediaData.seriesName) {
|
||||
appName = store.jf_privacyMode
|
||||
appName = settings.store.jf_privacyMode
|
||||
? `${mediaData.seriesName} - [Episode Hidden]`
|
||||
: `${mediaData.seriesName} - ${mediaData.name}`;
|
||||
} else if (mediaData.type === "Audio") {
|
||||
appName = store.jf_privacyMode
|
||||
appName = settings.store.jf_privacyMode
|
||||
? "[Track Hidden]"
|
||||
: `${mediaData.artist || "Unknown Artist"} - ${mediaData.name}`;
|
||||
} else {
|
||||
appName = store.jf_privacyMode ? "[Movie Hidden]" : mediaData.name || "Jellyfin";
|
||||
appName = settings.store.jf_privacyMode ? "[Movie Hidden]" : mediaData.name || "Jellyfin";
|
||||
}
|
||||
break;
|
||||
case "custom":
|
||||
appName = templateReplace(store.jf_customName || "{name} on Jellyfin");
|
||||
if (store.jf_privacyMode) {
|
||||
appName = templateReplace(settings.store.jf_customName || "{name} on Jellyfin");
|
||||
if (settings.store.jf_privacyMode) {
|
||||
appName = appName
|
||||
.replace(mediaData.name || "", "[Title Hidden]")
|
||||
.replace(mediaData.seriesName || "", "[Series Hidden]")
|
||||
|
|
@ -141,13 +140,13 @@ async function getActivity(): Promise<Activity | null> {
|
|||
if (mediaData.type === "Episode" && mediaData.seriesName) {
|
||||
appName = mediaData.seriesName;
|
||||
} else {
|
||||
appName = store.jf_privacyMode ? "[Media Hidden]" : mediaData.name || "Jellyfin";
|
||||
appName = settings.store.jf_privacyMode ? "[Media Hidden]" : mediaData.name || "Jellyfin";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const assets = {
|
||||
large_image: !store.jf_privacyMode && mediaData.imageUrl
|
||||
large_image: !settings.store.jf_privacyMode && mediaData.imageUrl
|
||||
? await getAsset(mediaData.imageUrl) : undefined,
|
||||
large_text: mediaData.seriesName || mediaData.album || undefined,
|
||||
};
|
||||
|
|
@ -155,9 +154,9 @@ async function getActivity(): Promise<Activity | null> {
|
|||
const getDetails = () => {
|
||||
let details: string;
|
||||
if (mediaData.type === "Episode" && mediaData.seriesName)
|
||||
details = store.jf_privacyMode ? "Watching a TV Show" : mediaData.seriesName;
|
||||
details = settings.store.jf_privacyMode ? "Watching a TV Show" : mediaData.seriesName;
|
||||
else
|
||||
details = store.jf_privacyMode ? "Watching Something" : mediaData.name;
|
||||
details = settings.store.jf_privacyMode ? "Watching Something" : mediaData.name;
|
||||
if (mediaData.isPaused) details += " - Paused";
|
||||
return details;
|
||||
};
|
||||
|
|
@ -169,7 +168,7 @@ async function getActivity(): Promise<Activity | null> {
|
|||
let episodeFormat = "";
|
||||
const season = mediaData.seasonNumber;
|
||||
const episode = mediaData.episodeNumber;
|
||||
const format = store.jf_episodeFormat || "long";
|
||||
const format = settings.store.jf_episodeFormat || "long";
|
||||
|
||||
if (season != null && episode != null) {
|
||||
switch (format) {
|
||||
|
|
@ -189,10 +188,10 @@ async function getActivity(): Promise<Activity | null> {
|
|||
episodeFormat = format === "fulltext" ? `Episode ${episode}` : `E${episode.toString().padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
state = (store.jf_showEpisodeName && mediaData.name && !store.jf_privacyMode)
|
||||
state = (settings.store.jf_showEpisodeName && mediaData.name && !settings.store.jf_privacyMode)
|
||||
? `${episodeFormat} - ${mediaData.name}`
|
||||
: episodeFormat;
|
||||
} else if (store.jf_privacyMode) {
|
||||
} else if (settings.store.jf_privacyMode) {
|
||||
state = mediaData.type === "Audio" ? "Listening to music" : (mediaData.year ? "(????)" : undefined);
|
||||
} else {
|
||||
state = mediaData.artist || (mediaData.year ? `(${mediaData.year})` : undefined);
|
||||
|
|
|
|||
|
|
@ -55,13 +55,12 @@ export default definePlugin({
|
|||
],
|
||||
|
||||
filterCodecs(codecs: CodecInfo[]) {
|
||||
const { store } = settings;
|
||||
const disabled: Record<string, boolean> = {
|
||||
AV1: store.disableAv1Codec,
|
||||
H265: store.disableH265Codec,
|
||||
H264: store.disableH264Codec,
|
||||
VP8: store.disableVP8Codec,
|
||||
VP9: store.disableVP9Codec,
|
||||
AV1: settings.store.disableAv1Codec,
|
||||
H265: settings.store.disableH265Codec,
|
||||
H264: settings.store.disableH264Codec,
|
||||
VP8: settings.store.disableVP8Codec,
|
||||
VP9: settings.store.disableVP9Codec,
|
||||
};
|
||||
return codecs.map(c => disabled[c.name] ? { ...c, encode: false } : c);
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue