From cf77cf6fd647c29bbeb9b9a2d6fc5620a3b0997c Mon Sep 17 00:00:00 2001 From: Maya Date: Sun, 3 May 2026 23:13:21 +0300 Subject: [PATCH] feat: vertd custom headers (#180) allows for adding custom headers to vertd requests. fixes #180 --- messages/en.json | 5 + .../components/functional/FancyInput.svelte | 50 +++- .../functional/popups/SettingsModal.svelte | 6 +- src/lib/converters/vertd.svelte.ts | 25 +- src/lib/sections/settings/Vertd.svelte | 238 +++++++++++------- src/lib/sections/settings/index.svelte.ts | 6 +- .../sections/settings/vertdSettings.svelte.ts | 26 +- 7 files changed, 243 insertions(+), 113 deletions(-) diff --git a/messages/en.json b/messages/en.json index 380b2e5..6fb0e81 100644 --- a/messages/en.json +++ b/messages/en.json @@ -289,6 +289,11 @@ "us": "Washington, USA", "custom": "Custom" }, + "custom_headers": { + "label": "Custom headers", + "description": "Add custom headers to be sent with each request to the vertd instance, which could be used for authentication or other purposes (one per line).", + "placeholder": "Header-Name: Header Value" + }, "conversion_speed": { "label": "Conversion speed", "description": "This describes the tradeoff between speed and quality. Faster speeds will result in lower quality, but will get the job done quicker.", diff --git a/src/lib/components/functional/FancyInput.svelte b/src/lib/components/functional/FancyInput.svelte index b5b0313..8f1b7ce 100644 --- a/src/lib/components/functional/FancyInput.svelte +++ b/src/lib/components/functional/FancyInput.svelte @@ -1,9 +1,14 @@ @@ -22,32 +28,52 @@ {#if type === "checkbox"}
{#if checked} -
- - +
+ +
{/if}
+ {:else if multiline} + {:else} {/if} diff --git a/src/lib/components/functional/popups/SettingsModal.svelte b/src/lib/components/functional/popups/SettingsModal.svelte index 35524df..4d2eb34 100644 --- a/src/lib/components/functional/popups/SettingsModal.svelte +++ b/src/lib/components/functional/popups/SettingsModal.svelte @@ -218,7 +218,7 @@ placeholder={setting.placeholder} disabled={disabled || setting.disabled} - oninput={(e) => + oninput={(e: any) => handleSettingChange( setting.customInputKey!, e.currentTarget @@ -238,7 +238,7 @@ ] ?? setting.default} placeholder={setting.placeholder} - onchange={(e) => + onchange={(e: any) => handleSettingChange( setting.key, e.currentTarget.checked, @@ -295,7 +295,7 @@ ] ?? setting.default} placeholder={setting.placeholder} - oninput={(e) => + oninput={(e: any) => handleSettingChange( setting.key, e.currentTarget.value, diff --git a/src/lib/converters/vertd.svelte.ts b/src/lib/converters/vertd.svelte.ts index 15bc8af..f693cd8 100644 --- a/src/lib/converters/vertd.svelte.ts +++ b/src/lib/converters/vertd.svelte.ts @@ -2,7 +2,10 @@ import VertdErrorComponent from "$lib/components/functional/popups/VertdError.sv import { error, log } from "$lib/util/logger"; import { m } from "$lib/paraglide/messages"; import { Settings } from "$lib/sections/settings/index.svelte"; -import { VertdInstance } from "$lib/sections/settings/vertdSettings.svelte"; +import { + VertdInstance, + getVertdCustomHeaders, +} from "$lib/sections/settings/vertdSettings.svelte"; import { VertFile } from "$lib/types"; import { Converter, FormatInfo } from "./converter.svelte"; import { PUB_DISABLE_FAILURE_BLOCKS } from "$env/static/public"; @@ -60,16 +63,18 @@ export const vertdFetch: { // eslint-disable-next-line @typescript-eslint/no-explicit-any } = async (url: any, options: RequestInit, body?: any) => { const domain = await VertdInstance.instance.url(); + const headers = new Headers(options.headers); + for (const [key, value] of Object.entries(getVertdCustomHeaders())) + headers.set(key, value); // if there is a body, insert a Content-Type: application/json header if (body) { - options.headers = { - "Content-Type": "application/json", - ...(options.headers || {}), - }; + headers.set("Content-Type", "application/json"); options.body = JSON.stringify(body); } + options.headers = headers; + const res = await fetch(domain + url, options); const text = await res.text(); @@ -250,6 +255,7 @@ const createUploadTask = async (file: VertFile): Promise => { formData.append("file", file.file, file.name); const xhr = new XMLHttpRequest(); xhr.open("POST", `${apiUrl}/api/upload`, true); + const customHeaders = getVertdCustomHeaders(); const promise = new Promise((resolve, reject) => { xhr.upload.addEventListener("progress", (e) => { @@ -285,6 +291,9 @@ const createUploadTask = async (file: VertFile): Promise => { reject(new Error("Conversion cancelled")); }; + for (const [key, value] of Object.entries(customHeaders)) + xhr.setRequestHeader(key, value); + xhr.send(formData); console.log("sent!"); }); @@ -299,6 +308,7 @@ const downloadFile = async (url: string, file: VertFile): Promise => { const xhr = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.responseType = "blob"; + const customHeaders = getVertdCustomHeaders(); return new Promise((resolve, reject) => { xhr.addEventListener("progress", (e) => { @@ -322,6 +332,9 @@ const downloadFile = async (url: string, file: VertFile): Promise => { reject(xhr.statusText); }; + for (const [key, value] of Object.entries(customHeaders)) + xhr.setRequestHeader(key, value); + xhr.send(); }); }; @@ -712,7 +725,7 @@ export class VertdConverter extends Converter { // trim/crop/rotate - also have another ui for this prob const animatedImages = [".gif", ".webp", ".apng"]; - if (animatedImages.includes(input.from)) { + if (animatedImages.includes(input.to)) { return [fps, resolution, metadata]; } else { return [ diff --git a/src/lib/sections/settings/Vertd.svelte b/src/lib/sections/settings/Vertd.svelte index 508a6d6..0cd892a 100644 --- a/src/lib/sections/settings/Vertd.svelte +++ b/src/lib/sections/settings/Vertd.svelte @@ -8,7 +8,11 @@ import { vertdLoaded } from "$lib/store/index.svelte"; import { m } from "$lib/paraglide/messages"; import { link, sanitize } from "$lib/store/index.svelte"; - import { VertdInstance, type VertdInner } from "./vertdSettings.svelte"; + import { + VertdInstance, + getVertdCustomHeaders, + type VertdInner, + } from "./vertdSettings.svelte"; import FancyInput from "$lib/components/functional/FancyInput.svelte"; let vertdCommit = $state(null); @@ -24,7 +28,12 @@ vertdCommit = "loading"; VertdInstance.instance .url() - .then((u) => fetch(`${u}/api/version`, { signal })) + .then((u) => + fetch(`${u}/api/version`, { + signal, + headers: getVertdCustomHeaders(), + }), + ) .then((res) => { if (!res.ok) throw new Error("bad response"); vertdLoaded.set(false); @@ -57,32 +66,41 @@ /> {m["settings.vertd.title"]()} -

- {m["settings.vertd.status"]()} - {vertdCommit - ? vertdCommit === "loading" - ? m["settings.vertd.loading"]() - : m["settings.vertd.available"]({ commitId: vertdCommit }) - : m["settings.vertd.unavailable"]()} -

-
-
-

- {@html sanitize(m["settings.vertd.description.main"]())} -

-

- {@html sanitize(link( +

+

+ {m["settings.vertd.status"]()} + {vertdCommit + ? vertdCommit === "loading" + ? m["settings.vertd.loading"]() + : m["settings.vertd.available"]({ + commitId: vertdCommit, + }) + : m["settings.vertd.unavailable"]()} +

+ +

+ {@html sanitize(m["settings.vertd.description.main"]())} +

+

+ {@html sanitize( + link( "vertd_link", m["settings.vertd.description.info"](), GITHUB_URL_VERTD, - ))} -

+ ), + )} +

+
+ +
+

{m["settings.vertd.instance.label"]()} @@ -135,75 +153,117 @@ {#if VertdInstance.instance.innerData().type === "custom"} {/if}

-
-
-

- {m["settings.vertd.conversion_speed.label"]()} -

-

- {m["settings.vertd.conversion_speed.description"]()} -

-
- { - switch (settings.vertdSpeed) { - case "verySlow": - return m[ - "settings.vertd.conversion_speed.speeds.very_slow" - ](); - case "slower": - return m["settings.vertd.conversion_speed.speeds.slower"](); - case "slow": - return m["settings.vertd.conversion_speed.speeds.slow"](); - case "medium": - return m["settings.vertd.conversion_speed.speeds.medium"](); - case "fast": - return m["settings.vertd.conversion_speed.speeds.fast"](); - case "ultraFast": - return m[ - "settings.vertd.conversion_speed.speeds.ultra_fast" - ](); - } - })()} - onselect={(selected) => { - switch (selected) { - case m["settings.vertd.conversion_speed.speeds.very_slow"](): - settings.vertdSpeed = "verySlow"; - break; - case m["settings.vertd.conversion_speed.speeds.slower"](): - settings.vertdSpeed = "slower"; - break; - case m["settings.vertd.conversion_speed.speeds.slow"](): - settings.vertdSpeed = "slow"; - break; - case m["settings.vertd.conversion_speed.speeds.medium"](): - settings.vertdSpeed = "medium"; - break; - case m["settings.vertd.conversion_speed.speeds.fast"](): - settings.vertdSpeed = "fast"; - break; - case m["settings.vertd.conversion_speed.speeds.ultra_fast"](): - settings.vertdSpeed = "ultraFast"; - break; - } - }} +
+
+
+

+ {m["settings.vertd.conversion_speed.label"]()} +

+

+ {m["settings.vertd.conversion_speed.description"]()} +

+
+ { + switch (settings.vertdSpeed) { + case "verySlow": + return m[ + "settings.vertd.conversion_speed.speeds.very_slow" + ](); + case "slower": + return m[ + "settings.vertd.conversion_speed.speeds.slower" + ](); + case "slow": + return m[ + "settings.vertd.conversion_speed.speeds.slow" + ](); + case "medium": + return m[ + "settings.vertd.conversion_speed.speeds.medium" + ](); + case "fast": + return m[ + "settings.vertd.conversion_speed.speeds.fast" + ](); + case "ultraFast": + return m[ + "settings.vertd.conversion_speed.speeds.ultra_fast" + ](); + } + })()} + onselect={(selected) => { + switch (selected) { + case m[ + "settings.vertd.conversion_speed.speeds.very_slow" + ](): + settings.vertdSpeed = "verySlow"; + break; + case m[ + "settings.vertd.conversion_speed.speeds.slower" + ](): + settings.vertdSpeed = "slower"; + break; + case m[ + "settings.vertd.conversion_speed.speeds.slow" + ](): + settings.vertdSpeed = "slow"; + break; + case m[ + "settings.vertd.conversion_speed.speeds.medium" + ](): + settings.vertdSpeed = "medium"; + break; + case m[ + "settings.vertd.conversion_speed.speeds.fast" + ](): + settings.vertdSpeed = "fast"; + break; + case m[ + "settings.vertd.conversion_speed.speeds.ultra_fast" + ](): + settings.vertdSpeed = "ultraFast"; + break; + } + }} + /> +
+
+
+

+ {m["settings.vertd.custom_headers.label"]()} +

+

+ {m["settings.vertd.custom_headers.description"]()} +

+
-
- +
diff --git a/src/lib/sections/settings/index.svelte.ts b/src/lib/sections/settings/index.svelte.ts index 4b3409b..ccfa0fd 100644 --- a/src/lib/sections/settings/index.svelte.ts +++ b/src/lib/sections/settings/index.svelte.ts @@ -25,11 +25,12 @@ export interface ISettings { plausible: boolean; vertdURL: string; vertdSpeed: ConversionSpeed; // videos + vertdBlockedHashes: Map; // hashes of files blocked from vertd conversion + vertdCustomHeaders: string; // custom headers to send to the vertd server magickQuality: number; // images ffmpegQuality: ConversionBitrate; // audio (or audio <-> video) ffmpegSampleRate: string; // audio (or audio <-> video) ffmpegCustomSampleRate: number; // audio (or audio <-> video) - only used when ffmpegSampleRate is "custom" - vertdBlockedHashes: Map; // hashes of files blocked from vertd conversion } export class Settings { @@ -48,11 +49,12 @@ export class Settings { plausible: true, vertdURL: PUB_VERTD_URL, vertdSpeed: "slow", + vertdBlockedHashes: new Map(), + vertdCustomHeaders: "", magickQuality: 100, ffmpegQuality: "auto", ffmpegSampleRate: "auto", ffmpegCustomSampleRate: 44100, //TODO: make string to match for vertd - vertdBlockedHashes: new Map(), }); public save() { diff --git a/src/lib/sections/settings/vertdSettings.svelte.ts b/src/lib/sections/settings/vertdSettings.svelte.ts index 8209678..1f660d7 100644 --- a/src/lib/sections/settings/vertdSettings.svelte.ts +++ b/src/lib/sections/settings/vertdSettings.svelte.ts @@ -13,6 +13,29 @@ export type VertdInner = | { type: "us" } | { type: "custom" }; +export const getVertdCustomHeaders = (): Record => { + const raw = Settings.instance.settings.vertdCustomHeaders.trim(); + if (!raw) return {}; + + const headers: Record = {}; + + for (const line of raw.split(/\r?\n/)) { + const trimmed = line.trim(); + if (!trimmed) continue; + + const separatorIndex = trimmed.indexOf(":"); + if (separatorIndex <= 0) continue; + + const key = trimmed.slice(0, separatorIndex).trim(); + const value = trimmed.slice(separatorIndex + 1).trim(); + if (!key || !value) continue; + + headers[key] = value; + } + + return headers; +}; + export class VertdInstance { public static instance = new VertdInstance(); @@ -61,7 +84,8 @@ export class VertdInstance { await fetch(url, { method: "GET", cache: "no-store", - mode: "no-cors", + mode: "cors", + headers: getVertdCustomHeaders(), }); return performance.now() - start; } catch {