mirror of https://github.com/VERT-sh/VERT.git
Merge branch 'main' into feat/conversion-qol
This commit is contained in:
commit
424ed6e5d6
|
@ -129,7 +129,7 @@
|
|||
"unavailable": "unavailable (is the url right?)",
|
||||
"description": "The <code>vertd</code> project is a server wrapper for FFmpeg. This allows you to convert videos through the convenience of VERT's web interface, while still being able to harness the power of your GPU to do it as quickly as possible.",
|
||||
"hosting_info": "We host a public instance for your convenience, but it is quite easy to host your own on your PC or server if you know what you are doing. You can download the server binaries [vertd_link]here[/vertd_link] - the process of setting this up will become easier in the future, so stay tuned!",
|
||||
"instance_url": "Instance URL",
|
||||
"instance": "Instance",
|
||||
"url_placeholder": "Example: http://localhost:24153",
|
||||
"conversion_speed": "Conversion speed",
|
||||
"speed_description": "This describes the tradeoff between speed and quality. Faster speeds will result in lower quality, but will get the job done quicker.",
|
||||
|
@ -140,7 +140,11 @@
|
|||
"medium": "Medium",
|
||||
"fast": "Fast",
|
||||
"ultra_fast": "Ultra Fast"
|
||||
}
|
||||
},
|
||||
"auto_instance": "Auto (recommended)",
|
||||
"eu_instance": "Falkenstein, Germany",
|
||||
"us_instance": "Washington, USA",
|
||||
"custom_instance": "Custom"
|
||||
},
|
||||
"privacy": {
|
||||
"title": "Privacy",
|
||||
|
|
|
@ -109,7 +109,7 @@
|
|||
"unavailable": "no disponible (¿has comprobado la url?)",
|
||||
"description": "<code>vertd</code> es un proyecto que actúa como un servidor intermediario (\"wrapper\") para FFmpeg. Permite convertir vídeos sin dejar de lado la conveniente interfaz web de VERT y, a la vez, aprovecha la potencia de tu GPU para hacerlo lo más rápido posible.",
|
||||
"hosting_info": "Alojamos una instancia pública para tu conveniencia, pero es bastante fácil alojar una propia en tu PC o servidor si sabes lo que estás haciendo. Puedes descargar los binarios del servidor [vertd_link]aquí[/vertd_link]. ¡El proceso de instalación será más fácil en el futuro, así que mantente atento!",
|
||||
"instance_url": "URL de la instancia",
|
||||
"instance": "Instancia",
|
||||
"url_placeholder": "Ejemplo: http://localhost:24153",
|
||||
"conversion_speed": "Velocidad de conversión",
|
||||
"speed_description": "Esto describe el equilibrio entre velocidad y calidad. Velocidades más rápidas resultarán en una calidad más baja, pero harán el trabajo más rápido.",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { error, log } from "$lib/logger";
|
||||
import { Settings } from "$lib/sections/settings/index.svelte";
|
||||
import { VertdInstance } from "$lib/sections/settings/vertdSettings.svelte";
|
||||
import { VertFile } from "$lib/types";
|
||||
import { Converter, FormatInfo } from "./converter.svelte";
|
||||
|
||||
|
@ -33,7 +34,7 @@ const vertdFetch = async <U extends keyof RouteMap>(
|
|||
url: U,
|
||||
options: RequestInit,
|
||||
): Promise<RouteMap[U]> => {
|
||||
const domain = Settings.instance.settings.vertdURL;
|
||||
const domain = await VertdInstance.instance.url();
|
||||
const res = await fetch(`${domain}${url}`, options);
|
||||
const text = await res.text();
|
||||
let json: VertdResponse<RouteMap[U]> = null!;
|
||||
|
@ -142,7 +143,7 @@ const progressEstimate = (
|
|||
};
|
||||
|
||||
const uploadFile = async (file: VertFile): Promise<UploadResponse> => {
|
||||
const apiUrl = Settings.instance.settings.vertdURL;
|
||||
const apiUrl = await VertdInstance.instance.url();
|
||||
const formData = new FormData();
|
||||
formData.append("file", file.file, file.name);
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
@ -274,10 +275,9 @@ export class VertdConverter extends Converter {
|
|||
if (to.startsWith(".")) to = to.slice(1);
|
||||
|
||||
const uploadRes = await uploadFile(input);
|
||||
console.log(uploadRes);
|
||||
const apiUrl = await VertdInstance.instance.url();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const apiUrl = Settings.instance.settings.vertdURL;
|
||||
const protocol = apiUrl.startsWith("https") ? "wss:" : "ws:";
|
||||
const ws = new WebSocket(
|
||||
`${protocol}//${apiUrl.replace("http://", "").replace("https://", "")}/api/ws`,
|
||||
|
@ -386,7 +386,7 @@ export class VertdConverter extends Converter {
|
|||
}
|
||||
|
||||
public async valid(): Promise<boolean> {
|
||||
if (!Settings.instance.settings.vertdURL) {
|
||||
if (!(await VertdInstance.instance.url())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
export interface IpInfo {
|
||||
ip: string;
|
||||
network: string;
|
||||
version: string;
|
||||
city: string;
|
||||
region: string;
|
||||
region_code: string;
|
||||
country: string;
|
||||
country_name: string;
|
||||
country_code: string;
|
||||
country_code_iso3: string;
|
||||
country_capital: string;
|
||||
country_tld: string;
|
||||
continent_code: string;
|
||||
in_eu: boolean;
|
||||
postal: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
timezone: string;
|
||||
utc_offset: string;
|
||||
country_calling_code: string;
|
||||
currency: string;
|
||||
currency_name: string;
|
||||
languages: string;
|
||||
country_area: number;
|
||||
country_population: number;
|
||||
asn: string;
|
||||
org: string;
|
||||
}
|
||||
|
||||
export const ip = async (): Promise<IpInfo> => {
|
||||
return await fetch("https://ipapi.co/json/").then((r) => r.json());
|
||||
};
|
|
@ -8,7 +8,7 @@
|
|||
import { vertdLoaded } from "$lib/store/index.svelte";
|
||||
import { m } from "$lib/paraglide/messages";
|
||||
import { link } from "$lib/store/index.svelte";
|
||||
//import { converters } from "$lib/converters";
|
||||
import { VertdInstance, type VertdInner } from "./vertdSettings.svelte";
|
||||
|
||||
let vertdCommit = $state<string | null>(null);
|
||||
let abortController: AbortController | null = null;
|
||||
|
@ -16,37 +16,29 @@
|
|||
const { settings = $bindable() }: { settings: ISettings } = $props();
|
||||
|
||||
$effect(() => {
|
||||
if (settings.vertdURL) {
|
||||
if (abortController) abortController.abort();
|
||||
abortController = new AbortController();
|
||||
const { signal } = abortController;
|
||||
if (abortController) abortController.abort();
|
||||
abortController = new AbortController();
|
||||
const { signal } = abortController;
|
||||
|
||||
vertdCommit = "loading";
|
||||
fetch(`${settings.vertdURL}/api/version`, { signal })
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error("bad response");
|
||||
vertdCommit = "loading";
|
||||
VertdInstance.instance
|
||||
.url()
|
||||
.then((u) => fetch(`${u}/api/version`, { signal }))
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error("bad response");
|
||||
vertdLoaded.set(false);
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
vertdCommit = data.data;
|
||||
vertdLoaded.set(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.name !== "AbortError") {
|
||||
vertdCommit = null;
|
||||
vertdLoaded.set(false);
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
vertdCommit = data.data;
|
||||
vertdLoaded.set(true);
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err.name !== "AbortError") {
|
||||
vertdCommit = null;
|
||||
vertdLoaded.set(false);
|
||||
// const converter = converters.find((c) => c.name === "vertd");
|
||||
// if (converter) converter.status = "not-ready";
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (abortController) abortController.abort();
|
||||
vertdCommit = null;
|
||||
vertdLoaded.set(false);
|
||||
// const converter = converters.find((c) => c.name === "vertd");
|
||||
// if (converter) converter.status = "not-ready";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (abortController) abortController.abort();
|
||||
|
@ -92,13 +84,60 @@
|
|||
</p>
|
||||
<div class="flex flex-col gap-2">
|
||||
<p class="text-base font-bold">
|
||||
{m["settings.vertd.instance_url"]()}
|
||||
{m["settings.vertd.instance"]()}
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
placeholder={m["settings.vertd.url_placeholder"]()}
|
||||
bind:value={settings.vertdURL}
|
||||
<Dropdown
|
||||
options={[
|
||||
m["settings.vertd.auto_instance"](),
|
||||
m["settings.vertd.eu_instance"](),
|
||||
m["settings.vertd.us_instance"](),
|
||||
m["settings.vertd.custom_instance"](),
|
||||
]}
|
||||
onselect={(selected) => {
|
||||
let inner: VertdInner;
|
||||
switch (selected) {
|
||||
case m["settings.vertd.auto_instance"]():
|
||||
inner = { type: "auto" };
|
||||
break;
|
||||
case m["settings.vertd.eu_instance"]():
|
||||
inner = { type: "eu" };
|
||||
break;
|
||||
case m["settings.vertd.us_instance"]():
|
||||
inner = { type: "us" };
|
||||
break;
|
||||
case m["settings.vertd.custom_instance"]():
|
||||
inner = {
|
||||
type: "custom",
|
||||
};
|
||||
break;
|
||||
default:
|
||||
inner = { type: "auto" };
|
||||
}
|
||||
VertdInstance.instance.set(inner);
|
||||
}}
|
||||
selected={(() => {
|
||||
switch (VertdInstance.instance.innerData().type) {
|
||||
case "auto":
|
||||
return m["settings.vertd.auto_instance"]();
|
||||
case "eu":
|
||||
return m["settings.vertd.eu_instance"]();
|
||||
case "us":
|
||||
return m["settings.vertd.us_instance"]();
|
||||
case "custom":
|
||||
return m[
|
||||
"settings.vertd.custom_instance"
|
||||
]();
|
||||
}
|
||||
})()}
|
||||
settingsStyle
|
||||
/>
|
||||
{#if VertdInstance.instance.innerData().type === "custom"}
|
||||
<input
|
||||
type="text"
|
||||
placeholder={m["settings.vertd.url_placeholder"]()}
|
||||
bind:value={settings.vertdURL}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PUB_VERTD_URL } from "$env/static/public";
|
||||
import type { ConversionBitrate } from "$lib/converters/ffmpeg.svelte";
|
||||
import type { ConversionSpeed } from "$lib/converters/vertd.svelte";
|
||||
import { VertdInstance } from "./vertdSettings.svelte";
|
||||
|
||||
export { default as Appearance } from "./Appearance.svelte";
|
||||
export { default as Conversion } from "./Conversion.svelte";
|
||||
|
@ -53,9 +54,11 @@ export class Settings {
|
|||
|
||||
public save() {
|
||||
localStorage.setItem("settings", JSON.stringify(this.settings));
|
||||
VertdInstance.instance.save();
|
||||
}
|
||||
|
||||
public load() {
|
||||
VertdInstance.instance.load();
|
||||
const ls = localStorage.getItem("settings");
|
||||
if (!ls) return;
|
||||
const settings: ISettings = JSON.parse(ls);
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
import { ip, type IpInfo } from "$lib/ip";
|
||||
import { Settings } from "./index.svelte";
|
||||
|
||||
const LOCATIONS = [
|
||||
{
|
||||
latitude: 49.0976,
|
||||
longitude: 12.4869,
|
||||
url: "https://eu.vertd.vert.sh",
|
||||
},
|
||||
{
|
||||
latitude: 47.6587,
|
||||
longitude: -117.426,
|
||||
url: "https://usa.vertd.vert.sh",
|
||||
},
|
||||
];
|
||||
|
||||
const toRad = (value: number) => (value * Math.PI) / 180;
|
||||
const haversine = (lat1: number, lon1: number, lat2: number, lon2: number) => {
|
||||
const R = 6371; // km
|
||||
const dLat = toRad(lat2 - lat1);
|
||||
const dLon = toRad(lon2 - lon1);
|
||||
const a =
|
||||
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(toRad(lat1)) *
|
||||
Math.cos(toRad(lat2)) *
|
||||
Math.sin(dLon / 2) *
|
||||
Math.sin(dLon / 2);
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
const d = R * c;
|
||||
return d;
|
||||
};
|
||||
|
||||
export type VertdInner =
|
||||
| { type: "auto" }
|
||||
| { type: "eu" }
|
||||
| { type: "us" }
|
||||
| { type: "custom" };
|
||||
|
||||
export class VertdInstance {
|
||||
public static instance = new VertdInstance();
|
||||
|
||||
private cachedIp = $state<IpInfo | null>(null);
|
||||
|
||||
private inner = $state<VertdInner>({
|
||||
type: "auto",
|
||||
});
|
||||
|
||||
public save() {
|
||||
localStorage.setItem("vertdInstance", JSON.stringify(this.inner));
|
||||
}
|
||||
|
||||
public load() {
|
||||
const ls = localStorage.getItem("vertdInstance");
|
||||
if (!ls) return;
|
||||
const inner: VertdInner = JSON.parse(ls);
|
||||
this.inner = {
|
||||
...this.inner,
|
||||
...inner,
|
||||
};
|
||||
}
|
||||
|
||||
public innerData() {
|
||||
return this.inner;
|
||||
}
|
||||
|
||||
public set(inner: VertdInner) {
|
||||
this.inner = inner;
|
||||
this.save();
|
||||
}
|
||||
|
||||
public async url() {
|
||||
switch (this.inner.type) {
|
||||
case "auto": {
|
||||
if (!this.cachedIp) {
|
||||
this.cachedIp = await ip();
|
||||
}
|
||||
|
||||
return this.geographicallyOptimalInstance(this.cachedIp);
|
||||
}
|
||||
|
||||
case "eu": {
|
||||
return "https://eu.vertd.vert.sh";
|
||||
}
|
||||
|
||||
case "us": {
|
||||
return "https://usa.vertd.vert.sh";
|
||||
}
|
||||
|
||||
case "custom": {
|
||||
return Settings.instance.settings.vertdURL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private geographicallyOptimalInstance(ip: IpInfo) {
|
||||
let bestLocation = LOCATIONS[0];
|
||||
let bestDistance = haversine(
|
||||
ip.latitude,
|
||||
ip.longitude,
|
||||
bestLocation.latitude,
|
||||
bestLocation.longitude,
|
||||
);
|
||||
|
||||
for (let i = 1; i < LOCATIONS.length; i++) {
|
||||
const location = LOCATIONS[i];
|
||||
const distance = haversine(
|
||||
ip.latitude,
|
||||
ip.longitude,
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
bestLocation = location;
|
||||
}
|
||||
}
|
||||
|
||||
return bestLocation.url;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
import { page } from "$app/state";
|
||||
import { initStores as initAnimStores } from "$lib/animation/index.js";
|
||||
import { locales, localizeHref } from "$lib/paraglide/runtime";
|
||||
import { VertdInstance } from "$lib/sections/settings/vertdSettings.svelte.js";
|
||||
|
||||
let { children, data } = $props();
|
||||
let enablePlausible = $state(false);
|
||||
|
@ -92,11 +93,12 @@
|
|||
|
||||
Settings.instance.load();
|
||||
|
||||
fetch(`${Settings.instance.settings.vertdURL}/api/version`).then(
|
||||
(res) => {
|
||||
VertdInstance.instance
|
||||
.url()
|
||||
.then((u) => fetch(`${u}/api/version`))
|
||||
.then((res) => {
|
||||
if (res.ok) $vertdLoaded = true;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("paste", handlePaste);
|
||||
|
|
Loading…
Reference in New Issue