mirror of https://github.com/VERT-sh/VERT.git
feat: vertd implementation
This commit is contained in:
parent
50100b7570
commit
b87d54ae08
|
@ -1,4 +1,9 @@
|
|||
import { FFmpegConverter } from "./ffmpeg.svelte";
|
||||
import { VertdConverter } from "./vertd.svelte";
|
||||
import { VipsConverter } from "./vips.svelte";
|
||||
|
||||
export const converters = [new VipsConverter(), new FFmpegConverter()];
|
||||
export const converters = [
|
||||
new VipsConverter(),
|
||||
new FFmpegConverter(),
|
||||
new VertdConverter(),
|
||||
];
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
import { log } from "$lib/logger";
|
||||
import { VertFile } from "$lib/types";
|
||||
import { Converter } from "./converter.svelte";
|
||||
|
||||
interface VertdError {
|
||||
type: "error";
|
||||
data: string;
|
||||
}
|
||||
|
||||
interface VertdSuccess<T> {
|
||||
type: "success";
|
||||
data: T;
|
||||
}
|
||||
|
||||
type VertdResponse<T> = VertdError | VertdSuccess<T>;
|
||||
|
||||
interface UploadResponse {
|
||||
id: string;
|
||||
auth: string;
|
||||
from: string;
|
||||
to: null;
|
||||
completed: false;
|
||||
totalFrames: number;
|
||||
}
|
||||
|
||||
interface RouteMap {
|
||||
"/api/upload": UploadResponse;
|
||||
}
|
||||
|
||||
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 text = await res.text();
|
||||
let json: VertdResponse<RouteMap[U]> = null!;
|
||||
try {
|
||||
json = JSON.parse(text);
|
||||
} catch {
|
||||
throw new Error(text);
|
||||
}
|
||||
|
||||
if (json.type === "error") {
|
||||
throw new Error(json.data);
|
||||
}
|
||||
|
||||
return json.data as RouteMap[U];
|
||||
};
|
||||
|
||||
// ws types
|
||||
interface StartJobMessage {
|
||||
type: "startJob";
|
||||
data: {
|
||||
token: string;
|
||||
jobId: string;
|
||||
to: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ErrorMessage {
|
||||
type: "error";
|
||||
data: {
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ProgressMessage {
|
||||
type: "progressUpdate";
|
||||
data: ProgressData;
|
||||
}
|
||||
|
||||
interface CompletedMessage {
|
||||
type: "jobFinished";
|
||||
data: {
|
||||
jobId: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface FpsProgress {
|
||||
type: "fps";
|
||||
data: number;
|
||||
}
|
||||
|
||||
interface FrameProgress {
|
||||
type: "frame";
|
||||
data: number;
|
||||
}
|
||||
|
||||
type ProgressData = FpsProgress | FrameProgress;
|
||||
|
||||
type VertdMessage =
|
||||
| StartJobMessage
|
||||
| ErrorMessage
|
||||
| ProgressMessage
|
||||
| CompletedMessage;
|
||||
|
||||
export class VertdConverter extends Converter {
|
||||
public name = "vertd";
|
||||
public ready = $state(false);
|
||||
public reportsProgress = true;
|
||||
public supportedFormats = [".mkv", ".mp4", ".webm", ".avi"];
|
||||
private log: (msg: string) => void = () => {};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.log = (msg) => log(["converters", this.name], msg);
|
||||
this.log("created converter");
|
||||
this.log("not rly sure how to implement this :P");
|
||||
this.ready = true;
|
||||
}
|
||||
|
||||
public async convert(input: VertFile, to: string): Promise<VertFile> {
|
||||
if (to.startsWith(".")) to = to.slice(1);
|
||||
// POST http://localhost:8080/api/upload
|
||||
// multipart body, key is "file", value is the file
|
||||
const formData = new FormData();
|
||||
formData.append("file", input.file, input.name);
|
||||
// const uploadRes = await fetch("http://localhost:8080/api/upload", {
|
||||
// method: "POST",
|
||||
// body: formData,
|
||||
// });
|
||||
|
||||
const uploadRes = await vertdFetch("/api/upload", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const ws = new WebSocket(`ws://localhost:8080/api/ws`);
|
||||
ws.onopen = () => {
|
||||
this.log("opened ws connection to vertd");
|
||||
const msg: StartJobMessage = {
|
||||
type: "startJob",
|
||||
data: {
|
||||
jobId: uploadRes.id,
|
||||
token: uploadRes.auth,
|
||||
to,
|
||||
},
|
||||
};
|
||||
ws.send(JSON.stringify(msg));
|
||||
this.log("sent startJob message");
|
||||
};
|
||||
|
||||
ws.onmessage = async (e) => {
|
||||
const msg: VertdMessage = JSON.parse(e.data);
|
||||
this.log(`received message ${msg.type}`);
|
||||
switch (msg.type) {
|
||||
case "progressUpdate": {
|
||||
const data = msg.data;
|
||||
if (data.type !== "frame") break;
|
||||
const frame = data.data;
|
||||
input.progress = (frame / uploadRes.totalFrames) * 100;
|
||||
break;
|
||||
}
|
||||
|
||||
case "jobFinished": {
|
||||
this.log("job finished");
|
||||
ws.close();
|
||||
const url = `http://localhost:8080/api/download/${msg.data.jobId}/${uploadRes.auth}`;
|
||||
this.log(`downloading from ${url}`);
|
||||
const res = await fetch(url).then((res) => res.blob());
|
||||
resolve(
|
||||
new VertFile(
|
||||
new File([res], input.name),
|
||||
to,
|
||||
this,
|
||||
undefined,
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case "error": {
|
||||
this.log(`error: ${msg.data.message}`);
|
||||
reject(msg.data.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue