feat: vertd

This commit is contained in:
not-nullptr 2025-02-09 00:07:42 +00:00
parent 35e98a5971
commit 8353491f64
14 changed files with 201 additions and 76 deletions

View File

@ -259,4 +259,20 @@ body {
h6 {
@apply font-display font-semibold;
}
code {
@apply font-mono bg-gray-200 rounded-md px-1 dynadark:bg-panel-alt dynadark:text-white;
}
p a {
@apply text-accent underline;
}
input[type="text"] {
@apply w-full p-3 rounded-lg bg-panel border-2 border-button pl-3 pr-[4rem];
}
input[type="text"]::placeholder {
@apply text-muted font-normal;
}
}

View File

@ -24,11 +24,19 @@
if (oldLength !== files.files.length) goto("/convert");
};
const uploadFiles = () => {
const uploadFiles = async () => {
const input = document.createElement("input");
input.type = "file";
input.multiple = true;
input.accept = converters
// filter converters to ones where await converter.valid() is true
const filteredConverters = (
await Promise.all(
converters.map(async (c) => {
if (await c.valid()) return c;
}),
)
).filter((c) => typeof c !== "undefined");
input.accept = filteredConverters
.map((c) => c.supportedFormats.join(","))
.join(",");
input.onchange = (e) => {

View File

@ -1,4 +1,4 @@
export const GITHUB_URL = 'https://github.com/VERT-sh/VERT';
export const GITHUB_API_URL = 'https://api.github.com/repos/VERT-sh/VERT';
export const DISCORD_URL = 'https://discord.gg/kqevGxYPak';
export const GITHUB_URL_VERT = "https://github.com/VERT-sh/VERT";
export const GITHUB_URL_VERTD = "https://github.com/VERT-sh/vertd";
export const GITHUB_API_URL = "https://api.github.com/repos/VERT-sh/VERT";
export const DISCORD_URL = "https://discord.gg/kqevGxYPak";

View File

@ -28,4 +28,8 @@ export class Converter {
): Promise<VertFile> {
throw new Error("Not implemented");
}
public async valid(): Promise<boolean> {
return true;
}
}

View File

@ -1,9 +1,9 @@
import { FFmpegConverter } from "./ffmpeg.svelte";
//import { VertdConverter } from "./vertd.svelte";
import { VertdConverter } from "./vertd.svelte";
import { VipsConverter } from "./vips.svelte";
export const converters = [
new VipsConverter(),
new FFmpegConverter(),
//new VertdConverter(),
new VertdConverter(),
];

View File

@ -1,4 +1,5 @@
import { log } from "$lib/logger";
import { Settings } from "$lib/sections/settings/index.svelte";
import { VertFile } from "$lib/types";
import { Converter } from "./converter.svelte";
@ -25,13 +26,15 @@ interface UploadResponse {
interface RouteMap {
"/api/upload": UploadResponse;
"/api/version": string;
}
const vertdFetch = async <U extends keyof RouteMap>(
url: U,
options: RequestInit,
): Promise<RouteMap[U]> => {
const res = await fetch(`http://127.0.0.1:8080${url}`, options);
const domain = Settings.instance.settings.vertdURL;
const res = await fetch(`${domain}${url}`, options);
const text = await res.text();
let json: VertdResponse<RouteMap[U]> = null!;
try {
@ -99,7 +102,8 @@ export class VertdConverter extends Converter {
public ready = $state(false);
public reportsProgress = true;
public supportedFormats = [".mkv", ".mp4", ".webm", ".avi"];
private log: (msg: string) => void = () => {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private log: (...msg: any[]) => void = () => {};
constructor() {
super();
@ -126,7 +130,10 @@ export class VertdConverter extends Converter {
});
return new Promise((resolve, reject) => {
const ws = new WebSocket(`ws://localhost:8080/api/ws`);
const apiUrl = Settings.instance.settings.vertdURL;
const ws = new WebSocket(
`ws://${apiUrl.replace("http://", "").replace("https://", "")}/api/ws`,
);
ws.onopen = () => {
this.log("opened ws connection to vertd");
const msg: StartJobMessage = {
@ -156,7 +163,7 @@ export class VertdConverter extends Converter {
case "jobFinished": {
this.log("job finished");
ws.close();
const url = `http://localhost:8080/api/download/${msg.data.jobId}/${uploadRes.auth}`;
const url = `${apiUrl}/api/download/${msg.data.jobId}/${uploadRes.auth}`;
this.log(`downloading from ${url}`);
const res = await fetch(url).then((res) => res.blob());
resolve(
@ -178,4 +185,20 @@ export class VertdConverter extends Converter {
};
});
}
public async valid(): Promise<boolean> {
if (!Settings.instance.settings.vertdURL) {
return false;
}
try {
await vertdFetch("/api/version", {
method: "GET",
});
return true;
} catch (e) {
this.log(e as unknown as string);
return false;
}
}
}

View File

@ -1,7 +1,7 @@
<script lang="ts">
import Panel from "$lib/components/visual/Panel.svelte";
import { HeartHandshakeIcon } from "lucide-svelte";
import { GITHUB_URL } from "$lib/consts";
import { GITHUB_URL_VERT } from "$lib/consts";
let { mainContribs, ghContribs } = $props();
</script>
@ -64,7 +64,7 @@
Big thanks to all these people for helping out!
<a
class="text-blue-500 font-normal hover:underline"
href={GITHUB_URL}
href={GITHUB_URL_VERT}
target="_blank"
rel="noopener noreferrer"
>
@ -76,7 +76,7 @@
Seems like no one has contributed yet...
<a
class="text-blue-500 font-normal hover:underline"
href={GITHUB_URL}
href={GITHUB_URL_VERT}
target="_blank"
rel="noopener noreferrer"
>

View File

@ -1,6 +1,6 @@
<script lang="ts">
import Panel from "$lib/components/visual/Panel.svelte";
import { DISCORD_URL, GITHUB_URL } from "$lib/consts";
import { DISCORD_URL, GITHUB_URL_VERT } from "$lib/consts";
import { GithubIcon, LinkIcon, MessageCircleMoreIcon } from "lucide-svelte";
</script>
@ -24,7 +24,7 @@
Discord
</a>
<a
href={GITHUB_URL}
href={GITHUB_URL_VERT}
target="_blank"
rel="noopener noreferrer"
class="btn flex-1 gap-2 p-4 rounded-full bg-button text-black dynadark:text-white flex items-center justify-center"

View File

@ -2,38 +2,14 @@
import { browser } from "$app/environment";
import FancyTextInput from "$lib/components/functional/FancyInput.svelte";
import Panel from "$lib/components/visual/Panel.svelte";
import { GITHUB_URL_VERTD } from "$lib/consts";
import { log } from "$lib/logger";
import { addToast } from "$lib/store/ToastProvider";
import { RefreshCwIcon, SaveAllIcon } from "lucide-svelte";
import { onMount } from "svelte";
import type { Settings } from "./index.svelte";
// let filenameFormat = "VERT_%name%";
let settings = $state({
filenameFormat: "VERT_%name%",
});
function save() {
log(["settings"], "saving settings");
if (!browser) return;
try {
localStorage.setItem("settings", JSON.stringify(settings));
addToast("success", "settings saved!");
} catch (error) {
log(["settings", "error"], `failed to save settings: ${error}`);
addToast("error", "Failed to save settings!");
}
}
onMount(() => {
const savedSettings = localStorage.getItem("settings");
if (savedSettings) {
const parsedSettings = JSON.parse(savedSettings);
settings = {
...settings,
...parsedSettings,
};
}
});
const { settings }: { settings: Settings } = $props();
</script>
<Panel class="flex flex-col gap-8 p-6">
@ -67,32 +43,11 @@
</div>
<FancyTextInput
placeholder="VERT_%name%"
bind:value={settings.filenameFormat}
bind:value={settings.settings.filenameFormat}
extension=".ext"
type="text"
/>
</div>
<div class="flex flex-col gap-4">
<div class="flex flex-col gap-2">
<p class="text-base font-bold">Second option</p>
<p class="text-sm text-muted font-normal">
This is just a sample option. This should not show up on
the live website. JOVANN, DO NOT ADD THIS TO THE LIVE
WEBSITE. PLEASE. JOVANN!!!!
</p>
</div>
</div>
<div class="flex justify-end">
<button
onclick={save}
class="w-fit btn px-6 py-4 bg-accent text-black flex items-center justify-center"
>
<SaveAllIcon size="24" class="inline-block mr-2" />
Save
</button>
</div>
</div>
</div>
</Panel>

View File

@ -0,0 +1,45 @@
<script lang="ts">
import { browser } from "$app/environment";
import FancyTextInput from "$lib/components/functional/FancyInput.svelte";
import Panel from "$lib/components/visual/Panel.svelte";
import { GITHUB_URL_VERTD } from "$lib/consts";
import { RefreshCwIcon, SaveAllIcon, ServerIcon } from "lucide-svelte";
import type { Settings } from "./index.svelte";
const { settings }: { settings: Settings } = $props();
</script>
<Panel class="flex flex-col gap-8 p-6">
<div class="flex flex-col gap-3">
<h2 class="text-2xl font-bold">
<ServerIcon
size="40"
class="inline-block -mt-1 mr-2 bg-accent-red p-2 rounded-full overflow-visible"
color="black"
/>
<code>vertd</code>
</h2>
<div class="flex flex-col gap-8">
<div class="flex flex-col gap-4">
<p class="text-sm text-muted font-normal">
VERT allows fully functional video conversion via the
<code>vertd</code> project. It acts as an FFmpeg wrapper on
a given server, allowing for GPU accelerated local* video
conversion. We currently don't provide our own instance due
to upkeep costs of servers with the necessary hardware for
speedy conversion. You can host your own instance by
downloading the binaries
<a href={GITHUB_URL_VERTD} target="_blank">here</a>.
</p>
<div class="flex flex-col gap-2">
<p class="text-base font-bold">Instance URL</p>
<input
type="text"
placeholder="http://localhost:24153"
bind:value={settings.settings.vertdURL}
/>
</div>
</div>
</div>
</div>
</Panel>

View File

@ -0,0 +1,31 @@
export { default as Appearance } from "./Appearance.svelte";
export { default as Conversion } from "./Conversion.svelte";
export { default as Vertd } from "./Vertd.svelte";
export interface ISettings {
filenameFormat: string;
vertdURL: string;
}
export class Settings {
public static instance = new Settings();
public settings: ISettings = $state({
filenameFormat: "VERT_%name%",
vertdURL: "",
});
public save() {
localStorage.setItem("settings", JSON.stringify(this.settings));
}
public load() {
const ls = localStorage.getItem("settings");
if (!ls) return;
const settings: ISettings = JSON.parse(ls);
this.settings = {
...this.settings,
...settings,
};
}
}

View File

@ -1,2 +0,0 @@
export { default as Appearance } from "./Appearance.svelte";
export { default as Conversion } from "./Conversion.svelte";

View File

@ -28,9 +28,10 @@
import { quintOut } from "svelte/easing";
import "../app.scss";
import { writable } from "svelte/store";
import { DISCORD_URL, GITHUB_URL } from "$lib/consts";
import { DISCORD_URL, GITHUB_URL_VERT } from "$lib/consts";
import { type Toast as ToastType, toasts } from "$lib/store/ToastProvider";
import Toast from "$lib/components/visual/Toast.svelte";
import { Settings } from "$lib/sections/settings/index.svelte";
let { children } = $props();
let shouldGoBack = writable(false);
@ -123,6 +124,10 @@
goingLeft = false;
}
});
onMount(() => {
Settings.instance.load();
});
</script>
<svelte:head>
@ -239,7 +244,7 @@
class="w-full h-full"
items={{
//"Privacy policy": "#",
"Source code": GITHUB_URL,
"Source code": GITHUB_URL_VERT,
"Discord server": DISCORD_URL,
}}
/>

View File

@ -1,7 +1,47 @@
<script lang="ts">
import * as Settings from "$lib/sections/settings";
import { browser } from "$app/environment";
import { log } from "$lib/logger";
import * as Settings from "$lib/sections/settings/index.svelte";
import { addToast } from "$lib/store/ToastProvider";
import { SettingsIcon } from "lucide-svelte";
import { onMount } from "svelte";
let isInitial = $state(true);
$effect(() => {
if (!browser) return;
if (isInitial) {
isInitial = false;
return;
}
const savedSettings = localStorage.getItem("settings");
if (savedSettings) {
const parsedSettings = JSON.parse(savedSettings);
if (parsedSettings === Settings.Settings.instance.settings) return;
}
log(["settings"], "saving settings");
try {
localStorage.setItem(
"settings",
JSON.stringify(Settings.Settings.instance.settings),
);
} catch (error) {
log(["settings", "error"], `failed to save settings: ${error}`);
addToast("error", "Failed to save settings!");
}
});
onMount(() => {
const savedSettings = localStorage.getItem("settings");
if (savedSettings) {
const parsedSettings = JSON.parse(savedSettings);
Settings.Settings.instance.settings = {
...Settings.Settings.instance.settings,
...parsedSettings,
};
}
});
</script>
<div class="flex flex-col h-full items-center">
@ -13,13 +53,13 @@
<div
class="w-full max-w-[1280px] flex flex-col md:flex-row gap-4 p-4 md:px-4 md:py-0"
>
<!-- Why VERT? & Credits -->
<div class="flex flex-col gap-4 flex-1">
<Settings.Conversion />
<Settings.Conversion settings={Settings.Settings.instance} />
<Settings.Vertd settings={Settings.Settings.instance} />
</div>
<div class="flex flex-col gap-4 flex-1">
<Settings.Appearance />
<div class="flex flex-col gap-4 flex-1">
<Settings.Appearance />
</div>
</div>
</div>