mirror of
https://github.com/wukko/cobalt.git
synced 2025-05-20 15:17:07 +02:00
web: very early implementation of a fetch worker
This commit is contained in:
parent
affe49474d
commit
5d7724762d
@ -6,5 +6,6 @@
|
|||||||
|
|
||||||
"state.waiting": "queued",
|
"state.waiting": "queued",
|
||||||
"state.starting": "starting...",
|
"state.starting": "starting...",
|
||||||
"state.running.remux": "remuxing"
|
"state.running.remux": "remuxing",
|
||||||
|
"state.running.fetch": "downloading"
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { t } from "$lib/i18n/translations";
|
|||||||
import { downloadFile } from "$lib/download";
|
import { downloadFile } from "$lib/download";
|
||||||
import { createDialog } from "$lib/state/dialogs";
|
import { createDialog } from "$lib/state/dialogs";
|
||||||
import { downloadButtonState } from "$lib/state/omnibox";
|
import { downloadButtonState } from "$lib/state/omnibox";
|
||||||
|
import { createSavePipeline } from "$lib/queen-bee/queue";
|
||||||
|
|
||||||
import type { DialogInfo } from "$lib/types/dialog";
|
import type { DialogInfo } from "$lib/types/dialog";
|
||||||
|
|
||||||
@ -79,8 +80,11 @@ export const savingHandler = async (link: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === "local-processing") {
|
if (response.status === "local-processing") {
|
||||||
// TODO: actual implementation
|
// TODO: remove debug logging
|
||||||
console.log(response);
|
console.log(response);
|
||||||
|
|
||||||
|
downloadButtonState.set("done");
|
||||||
|
return createSavePipeline(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status === "picker") {
|
if (response.status === "picker") {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { addItem } from "$lib/state/queen-bee/queue";
|
import { addItem } from "$lib/state/queen-bee/queue";
|
||||||
import type { CobaltPipelineItem } from "$lib/types/workers";
|
import type { CobaltPipelineItem } from "$lib/types/workers";
|
||||||
|
import type { CobaltLocalProcessingResponse } from "$lib/types/api";
|
||||||
|
|
||||||
export const getMediaType = (type: string) => {
|
export const getMediaType = (type: string) => {
|
||||||
const kind = type.split('/')[0];
|
const kind = type.split('/')[0];
|
||||||
@ -34,3 +35,27 @@ export const createRemuxPipeline = (file: File) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createSavePipeline = (info: CobaltLocalProcessingResponse) => {
|
||||||
|
const parentId = crypto.randomUUID();
|
||||||
|
const pipeline: CobaltPipelineItem[] = [];
|
||||||
|
|
||||||
|
for (const tunnel of info.tunnel) {
|
||||||
|
pipeline.push({
|
||||||
|
worker: "fetch",
|
||||||
|
workerId: crypto.randomUUID(),
|
||||||
|
parentId,
|
||||||
|
workerArgs: {
|
||||||
|
url: tunnel,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
addItem({
|
||||||
|
id: parentId,
|
||||||
|
state: "waiting",
|
||||||
|
pipeline,
|
||||||
|
filename: info.filename,
|
||||||
|
mediaType: "video",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import RemuxWorker from "$lib/workers/remux?worker";
|
import RemuxWorker from "$lib/workers/remux?worker";
|
||||||
|
import FetchWorker from "$lib/workers/fetch?worker";
|
||||||
|
|
||||||
import { itemDone, itemError, queue } from "$lib/state/queen-bee/queue";
|
import { itemDone, itemError, queue } from "$lib/state/queen-bee/queue";
|
||||||
import { updateWorkerProgress } from "$lib/state/queen-bee/current-tasks";
|
import { updateWorkerProgress } from "$lib/state/queen-bee/current-tasks";
|
||||||
@ -6,10 +7,10 @@ import { updateWorkerProgress } from "$lib/state/queen-bee/current-tasks";
|
|||||||
import type { CobaltQueue } from "$lib/types/queue";
|
import type { CobaltQueue } from "$lib/types/queue";
|
||||||
import type { CobaltPipelineItem } from "$lib/types/workers";
|
import type { CobaltPipelineItem } from "$lib/types/workers";
|
||||||
|
|
||||||
const killWorker = (worker: Worker, unsubscribe: () => void, interval: NodeJS.Timeout) => {
|
const killWorker = (worker: Worker, unsubscribe: () => void, interval?: NodeJS.Timeout) => {
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
worker.terminate();
|
worker.terminate();
|
||||||
clearInterval(interval);
|
if (interval) clearInterval(interval);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const runRemuxWorker = async (workerId: string, parentId: string, file: File) => {
|
export const runRemuxWorker = async (workerId: string, parentId: string, file: File) => {
|
||||||
@ -89,10 +90,61 @@ export const runRemuxWorker = async (workerId: string, parentId: string, file: F
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const runFetchWorker = async (workerId: string, parentId: string, url: string) => {
|
||||||
|
const worker = new FetchWorker();
|
||||||
|
|
||||||
|
const unsubscribe = queue.subscribe((queue: CobaltQueue) => {
|
||||||
|
if (!queue[parentId]) {
|
||||||
|
// TODO: remove logging
|
||||||
|
console.log("worker's parent is gone, so it killed itself");
|
||||||
|
killWorker(worker, unsubscribe);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.postMessage({
|
||||||
|
cobaltFetchWorker: {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
worker.onmessage = (event) => {
|
||||||
|
const eventData = event.data.cobaltFetchWorker;
|
||||||
|
if (!eventData) return;
|
||||||
|
|
||||||
|
if (eventData.progress) {
|
||||||
|
updateWorkerProgress(workerId, {
|
||||||
|
percentage: eventData.progress,
|
||||||
|
size: eventData.size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventData.file) {
|
||||||
|
killWorker(worker, unsubscribe);
|
||||||
|
return itemDone(
|
||||||
|
parentId,
|
||||||
|
workerId,
|
||||||
|
eventData.file,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventData.error) {
|
||||||
|
killWorker(worker, unsubscribe);
|
||||||
|
return itemError(parentId, workerId, eventData.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const startWorker = async ({ worker, workerId, parentId, workerArgs }: CobaltPipelineItem) => {
|
export const startWorker = async ({ worker, workerId, parentId, workerArgs }: CobaltPipelineItem) => {
|
||||||
switch (worker) {
|
switch (worker) {
|
||||||
case "remux":
|
case "remux":
|
||||||
await runRemuxWorker(workerId, parentId, workerArgs.files[0]);
|
if (workerArgs?.files) {
|
||||||
|
await runRemuxWorker(workerId, parentId, workerArgs.files[0]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "fetch":
|
||||||
|
if (workerArgs?.url) {
|
||||||
|
await runFetchWorker(workerId, parentId, workerArgs.url)
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,14 +41,14 @@ type CobaltTunnelResponse = {
|
|||||||
status: CobaltResponseType.Tunnel,
|
status: CobaltResponseType.Tunnel,
|
||||||
} & CobaltPartialURLResponse;
|
} & CobaltPartialURLResponse;
|
||||||
|
|
||||||
type CobaltLocalProcessingResponse = {
|
export type CobaltLocalProcessingResponse = {
|
||||||
status: CobaltResponseType.LocalProcessing,
|
status: CobaltResponseType.LocalProcessing,
|
||||||
tunnel: string[],
|
tunnel: string[],
|
||||||
|
|
||||||
// TODO: proper type for processing types
|
// TODO: proper type for processing types
|
||||||
type: string,
|
type: string,
|
||||||
service: string,
|
service: string,
|
||||||
filename?: string,
|
filename: string,
|
||||||
|
|
||||||
metadata?: {
|
metadata?: {
|
||||||
album?: string,
|
album?: string,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
export const resultFileTypes = ["video", "audio", "image"] as const;
|
export const resultFileTypes = ["video", "audio", "image"] as const;
|
||||||
|
|
||||||
export type CobaltWorkerType = "remux" | "removebg";
|
export type CobaltWorkerType = "remux" | "fetch";
|
||||||
export type CobaltPipelineResultFileType = typeof resultFileTypes[number];
|
export type CobaltPipelineResultFileType = typeof resultFileTypes[number];
|
||||||
|
|
||||||
export type CobaltWorkerProgress = {
|
export type CobaltWorkerProgress = {
|
||||||
@ -10,7 +10,8 @@ export type CobaltWorkerProgress = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type CobaltWorkerArgs = {
|
export type CobaltWorkerArgs = {
|
||||||
files: File[],
|
files?: File[],
|
||||||
|
url?: string,
|
||||||
//TODO: args for libav & etc with unique types
|
//TODO: args for libav & etc with unique types
|
||||||
}
|
}
|
||||||
|
|
||||||
|
74
web/src/lib/workers/fetch.ts
Normal file
74
web/src/lib/workers/fetch.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
const error = (code: string) => {
|
||||||
|
// TODO: return proper errors and code here
|
||||||
|
self.postMessage({
|
||||||
|
cobaltFetchWorker: {
|
||||||
|
error: code,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchFile = async (url: string) => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
error("file response wasn't ok");
|
||||||
|
return self.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
|
||||||
|
const contentLength = response.headers.get('Content-Length');
|
||||||
|
|
||||||
|
const totalBytes = contentLength ? parseInt(contentLength, 10) : null;
|
||||||
|
const reader = response.body?.getReader();
|
||||||
|
|
||||||
|
if (!reader) {
|
||||||
|
error("no reader");
|
||||||
|
return self.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
let receivedBytes = 0;
|
||||||
|
const chunks = [];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
|
||||||
|
receivedBytes += value.length;
|
||||||
|
chunks.push(value);
|
||||||
|
|
||||||
|
if (totalBytes) {
|
||||||
|
self.postMessage({
|
||||||
|
cobaltFetchWorker: {
|
||||||
|
progress: Math.round((receivedBytes / totalBytes) * 100),
|
||||||
|
size: receivedBytes,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receivedBytes === 0) {
|
||||||
|
error("tunnel is broken");
|
||||||
|
return self.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = new File(chunks, "file", { type: contentType });
|
||||||
|
|
||||||
|
self.postMessage({
|
||||||
|
cobaltFetchWorker: {
|
||||||
|
file
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
error("error when downloading the file");
|
||||||
|
return self.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onmessage = async (event: MessageEvent) => {
|
||||||
|
if (event.data.cobaltFetchWorker) {
|
||||||
|
await fetchFile(event.data.cobaltFetchWorker.url);
|
||||||
|
self.close();
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user