From 91327220a0a5cc9c483dbf2e9138fc736b26b319 Mon Sep 17 00:00:00 2001 From: wukko Date: Mon, 16 Dec 2024 17:23:43 +0600 Subject: [PATCH 001/439] web/PopoverContainer: create a reusable popover component --- .../components/misc/PopoverContainer.svelte | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 web/src/components/misc/PopoverContainer.svelte diff --git a/web/src/components/misc/PopoverContainer.svelte b/web/src/components/misc/PopoverContainer.svelte new file mode 100644 index 00000000..d43e8426 --- /dev/null +++ b/web/src/components/misc/PopoverContainer.svelte @@ -0,0 +1,70 @@ + + +
+ {#if renderPopover} + + {/if} +
+ + From 5d75ee493de6d68aedc1f09001863047d3230c29 Mon Sep 17 00:00:00 2001 From: wukko Date: Mon, 16 Dec 2024 17:24:05 +0600 Subject: [PATCH 002/439] web/SupportedServices: use the general popover component --- .../components/save/SupportedServices.svelte | 110 ++++++------------ 1 file changed, 38 insertions(+), 72 deletions(-) diff --git a/web/src/components/save/SupportedServices.svelte b/web/src/components/save/SupportedServices.svelte index 6dcb9244..adf63ef5 100644 --- a/web/src/components/save/SupportedServices.svelte +++ b/web/src/components/save/SupportedServices.svelte @@ -1,18 +1,21 @@ @@ -49,7 +40,7 @@
- {#if renderPopover} -
-
- {#if loaded} - {#each services as service} -
{service}
- {/each} - {:else} - {#each { length: 17 } as _} - - {/each} - {/if} -
-
- {$t("save.services.disclaimer")} -
+ +
+ {#if loaded} + {#each services as service} +
{service}
+ {/each} + {:else} + {#each { length: 17 } as _} + + {/each} + {/if}
- {/if} +
+ {$t("save.services.disclaimer")} +
+
diff --git a/web/src/components/downloads/DownloadStatus.svelte b/web/src/components/downloads/DownloadStatus.svelte new file mode 100644 index 00000000..743805c7 --- /dev/null +++ b/web/src/components/downloads/DownloadStatus.svelte @@ -0,0 +1,132 @@ + + + + + diff --git a/web/src/components/misc/Meowbalt.svelte b/web/src/components/misc/Meowbalt.svelte index 26ad14b3..929c63aa 100644 --- a/web/src/components/misc/Meowbalt.svelte +++ b/web/src/components/misc/Meowbalt.svelte @@ -1,6 +1,5 @@ + +
+
+
+
+ +
+ + {filename} + +
+
+
+
+
{status}
+
+
+ + +
+
+ + diff --git a/web/src/components/downloads/DownloadManager.svelte b/web/src/components/downloads/DownloadManager.svelte index 6008adb5..abf28e03 100644 --- a/web/src/components/downloads/DownloadManager.svelte +++ b/web/src/components/downloads/DownloadManager.svelte @@ -1,17 +1,73 @@ -
- + -
-
downloads
- {#if downloadQueue.length > 0} - {/if}
-
- {#each downloadQueue as item} - + {#each processingQueue as item} + {/each} - {#if downloadQueue.length === 0} + {#if processingQueue.length === 0}
- your downloads will appear here! + downloads will appear here!
{/if}
@@ -123,7 +124,7 @@
diff --git a/web/src/components/queue/ProcessingQueueStub.svelte b/web/src/components/queue/ProcessingQueueStub.svelte new file mode 100644 index 00000000..5a4afef7 --- /dev/null +++ b/web/src/components/queue/ProcessingQueueStub.svelte @@ -0,0 +1,44 @@ + + +
+ + + {$t("queue.stub", { + value: $t(`queue.stub.${randomAction()}`), + })} + +
+ + From 3f46395bd20b0c5da54440aeb1192c117e63192e Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Dec 2024 17:10:08 +0600 Subject: [PATCH 010/439] web/state/queue: add `nukeEntireQueue()` --- web/src/lib/state/queue.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/web/src/lib/state/queue.ts b/web/src/lib/state/queue.ts index c586623d..ee431743 100644 --- a/web/src/lib/state/queue.ts +++ b/web/src/lib/state/queue.ts @@ -58,4 +58,13 @@ export function removeFromOngoingQueue(id: string) { }); } +export function nukeEntireQueue() { + update(() => { + return {}; + }); + updateOngoing(() => { + return {}; + }); +} + export { queue, ongoingQueue }; From eba8dc3767d277b77f6b5bb9d2eee9bfc77ce466 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Dec 2024 17:10:30 +0600 Subject: [PATCH 011/439] web/ProcessingQueue: make the clear button actually clear the queue --- web/src/components/queue/ProcessingQueue.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/components/queue/ProcessingQueue.svelte b/web/src/components/queue/ProcessingQueue.svelte index 9609ad5f..f7967942 100644 --- a/web/src/components/queue/ProcessingQueue.svelte +++ b/web/src/components/queue/ProcessingQueue.svelte @@ -18,7 +18,7 @@ import IconVolume3 from "@tabler/icons-svelte/IconVolume3.svelte"; import settings from "$lib/state/settings"; - import { addToQueue, queue } from "$lib/state/queue"; + import { addToQueue, nukeEntireQueue, queue } from "$lib/state/queue"; import type { QueueItem } from "$lib/types/queue"; let popover: SvelteComponent; @@ -77,7 +77,7 @@
{$t("queue.title")}
{#if queueLength > 0} - From f3ff3656ef412125fa28a92218d0dbccc6a5b307 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Dec 2024 17:47:48 +0600 Subject: [PATCH 012/439] web/ProcessingQueue: fix ui on narrow screens --- .../components/queue/ProcessingQueue.svelte | 22 ++++++++++++------- .../queue/ProcessingQueueItem.svelte | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/web/src/components/queue/ProcessingQueue.svelte b/web/src/components/queue/ProcessingQueue.svelte index f7967942..5eae4b24 100644 --- a/web/src/components/queue/ProcessingQueue.svelte +++ b/web/src/components/queue/ProcessingQueue.svelte @@ -63,7 +63,7 @@ }); -
+
diff --git a/web/src/components/queue/ProcessingQueueItem.svelte b/web/src/components/queue/ProcessingQueueItem.svelte index a9114f7f..6c43a6cf 100644 --- a/web/src/components/queue/ProcessingQueueItem.svelte +++ b/web/src/components/queue/ProcessingQueueItem.svelte @@ -52,7 +52,7 @@ } .processing-item { - width: 425px; + width: 100%; padding: 8px 0; gap: 8px; border-bottom: 1.5px var(--button-elevated) solid; From 5860efa6207116cbabca5cb83bcc7f4c195b5236 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Dec 2024 17:48:40 +0600 Subject: [PATCH 013/439] web/PopoverContainer: hide for screen readers when not expanded --- web/src/components/misc/PopoverContainer.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/misc/PopoverContainer.svelte b/web/src/components/misc/PopoverContainer.svelte index d43e8426..832f5749 100644 --- a/web/src/components/misc/PopoverContainer.svelte +++ b/web/src/components/misc/PopoverContainer.svelte @@ -19,7 +19,7 @@ }; -
+
{#if renderPopover} {/if} From 73d0b24aafc5572b8347697b58a725f2ad3f757a Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Dec 2024 17:57:07 +0600 Subject: [PATCH 014/439] web/layout: move processing queue into content for better a11y --- web/src/routes/+layout.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index f942606f..b7e7aade 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -85,13 +85,13 @@ {#if device.is.iPhone && app.is.installed} {/if} - {#if $updated} {/if}
+ {#if ($turnstileEnabled && $page.url.pathname === "/") || $turnstileCreated} {/if} From 8c9f7ff36d99b774c32d3d3592911f698af4a5de Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 18 Dec 2024 18:42:34 +0600 Subject: [PATCH 015/439] web/ProcessingQueue: align buttons to center vertically --- web/src/components/queue/ProcessingQueue.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/components/queue/ProcessingQueue.svelte b/web/src/components/queue/ProcessingQueue.svelte index 5eae4b24..728d31af 100644 --- a/web/src/components/queue/ProcessingQueue.svelte +++ b/web/src/components/queue/ProcessingQueue.svelte @@ -126,7 +126,6 @@ gap: 12px; padding: 16px; padding-bottom: 0; - width: calc(100% - 16px * 2); max-width: 425px; } @@ -135,6 +134,7 @@ display: flex; flex-direction: row; justify-content: space-between; + align-items: center; } .header-title { @@ -155,6 +155,7 @@ background: none; box-shadow: none; text-align: left; + border-radius: 3px; } .header-buttons button :global(svg) { From 06000cbc776c7e3055d7abde56674a7850d0380a Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 19 Dec 2024 21:09:51 +0600 Subject: [PATCH 016/439] web/SectionHeading: added a new prop to disable the link --- web/src/components/misc/SectionHeading.svelte | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/web/src/components/misc/SectionHeading.svelte b/web/src/components/misc/SectionHeading.svelte index e3b1e635..8741030f 100644 --- a/web/src/components/misc/SectionHeading.svelte +++ b/web/src/components/misc/SectionHeading.svelte @@ -8,6 +8,7 @@ export let title: string; export let sectionId: string; export let beta = false; + export let nolink = false; export let copyData = ""; const sectionURL = `${$page.url.origin}${$page.url.pathname}#${sectionId}`; @@ -32,18 +33,20 @@
{/if} - + {#if !nolink} + + {/if}
From 302ff4ff2922abc6258fa3491dfb444ec4c22a25 Mon Sep 17 00:00:00 2001 From: wukko Date: Tue, 14 Jan 2025 18:21:16 +0600 Subject: [PATCH 043/439] web/sidebar/CobaltLogo: fix padding --- web/src/components/sidebar/CobaltLogo.svelte | 4 ++-- web/src/components/sidebar/SidebarTab.svelte | 2 +- web/src/routes/+layout.svelte | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/web/src/components/sidebar/CobaltLogo.svelte b/web/src/components/sidebar/CobaltLogo.svelte index 25ef81ba..e4158a69 100644 --- a/web/src/components/sidebar/CobaltLogo.svelte +++ b/web/src/components/sidebar/CobaltLogo.svelte @@ -11,10 +11,10 @@ display: flex; justify-content: center; align-items: center; - padding: calc(var(--padding) * 2); + padding: calc(var(--sidebar-tab-padding) * 2); /* accommodate space for scaling animation */ - padding-bottom: calc(var(--padding) * 2 - var(--sidebar-inner-padding)); + padding-bottom: calc(var(--sidebar-tab-padding) * 2 - var(--sidebar-inner-padding)); } @media screen and (max-width: 535px) { diff --git a/web/src/components/sidebar/SidebarTab.svelte b/web/src/components/sidebar/SidebarTab.svelte index cbc9399b..9307d7a4 100644 --- a/web/src/components/sidebar/SidebarTab.svelte +++ b/web/src/components/sidebar/SidebarTab.svelte @@ -58,7 +58,7 @@ align-items: center; text-align: center; gap: 3px; - padding: 10px 3px; + padding: var(--sidebar-tab-padding) 3px; color: var(--sidebar-highlight); font-size: var(--sidebar-font-size); opacity: 0.75; diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 37f0078d..b69182fe 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -149,6 +149,7 @@ --sidebar-width: 80px; --sidebar-font-size: 11px; --sidebar-inner-padding: 4px; + --sidebar-tab-padding: 10px; /* reduce default inset by 5px if it's not 0 */ --sidebar-height-mobile: calc( From d649a00718818ed1d6387642e3b3df773bc1f092 Mon Sep 17 00:00:00 2001 From: wukko Date: Tue, 14 Jan 2025 18:25:43 +0600 Subject: [PATCH 044/439] web/Sidebar: fix bottom padding on desktop --- web/src/components/sidebar/Sidebar.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/sidebar/Sidebar.svelte b/web/src/components/sidebar/Sidebar.svelte index f1bf5644..5935d7a4 100644 --- a/web/src/components/sidebar/Sidebar.svelte +++ b/web/src/components/sidebar/Sidebar.svelte @@ -68,7 +68,7 @@ height: 100%; justify-content: space-between; padding: var(--sidebar-inner-padding); - padding-bottom: calc(var(--sidebar-inner-padding) * 2); + padding-bottom: var(--sidebar-tab-padding); overflow-y: scroll; } From 2f2d39dc4c1a178123b3efff7d31f6eb63e566b8 Mon Sep 17 00:00:00 2001 From: wukko Date: Tue, 14 Jan 2025 18:30:33 +0600 Subject: [PATCH 045/439] web/removebg: fix types (remove garbage) --- web/src/lib/workers/removebg.ts | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/web/src/lib/workers/removebg.ts b/web/src/lib/workers/removebg.ts index 10cbe0ca..e6a59bcc 100644 --- a/web/src/lib/workers/removebg.ts +++ b/web/src/lib/workers/removebg.ts @@ -4,20 +4,10 @@ const models = { light: { id: "briaai/RMBG-1.4", input: "input", - modelConfig: { - device: "wasm", - dtype: "fp32", - }, - processorConfig: {}, }, heavy: { id: "onnx-community/BiRefNet_lite", input: "input_image", - modelConfig: { - device: "webgpu", - dtype: "fp16", - }, - processorConfig: {}, } } @@ -25,9 +15,12 @@ export const removeImageBackground = async (file: File) => { const image = await RawImage.fromBlob(new Blob([file])); const model_type = "light"; - const model = await AutoModel.from_pretrained(models[model_type].id, models[model_type].modelConfig); + const model = await AutoModel.from_pretrained(models[model_type].id, { + device: "wasm", + dtype: "fp32", + }); - const processor = await AutoProcessor.from_pretrained(models[model_type].id, models[model_type].processorConfig); + const processor = await AutoProcessor.from_pretrained(models[model_type].id, {}); if (model && processor) { const { pixel_values } = await processor(image); From 28d8927c085171449e4942151f0589e83f1f0b63 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 15 Jan 2025 17:22:34 +0600 Subject: [PATCH 046/439] web/removebg: convert to a proper web worker no more hanging ui :3 --- web/src/lib/workers/removebg.ts | 48 ++++++++++++++---------- web/src/routes/cutout/+page.svelte | 59 ++++++++++++++++++------------ web/src/routes/cutout/+page.ts | 1 + 3 files changed, 66 insertions(+), 42 deletions(-) create mode 100644 web/src/routes/cutout/+page.ts diff --git a/web/src/lib/workers/removebg.ts b/web/src/lib/workers/removebg.ts index e6a59bcc..4f8e7bd8 100644 --- a/web/src/lib/workers/removebg.ts +++ b/web/src/lib/workers/removebg.ts @@ -11,8 +11,30 @@ const models = { } } +export const maskImage = async (source: Blob, mask: RawImage) => { + const image = await RawImage.fromBlob(source); + + const canvas = document.createElement('canvas'); + canvas.width = image.width; + canvas.height = image.height; + const ctx = canvas.getContext('2d'); + + if (!ctx) return; + + ctx.drawImage(image.toCanvas(), 0, 0); + + const pixelData = ctx.getImageData(0, 0, image.width, image.height); + for (let i = 0; i < mask.data.length; ++i) { + pixelData.data[4 * i + 3] = mask.data[i]; + } + ctx.putImageData(pixelData, 0, 0); + + return canvas; +} + export const removeImageBackground = async (file: File) => { - const image = await RawImage.fromBlob(new Blob([file])); + const originalImageBlob = new Blob([file]); + const image = await RawImage.fromBlob(originalImageBlob); const model_type = "light"; const model = await AutoModel.from_pretrained(models[model_type].id, { @@ -24,26 +46,14 @@ export const removeImageBackground = async (file: File) => { if (model && processor) { const { pixel_values } = await processor(image); - const { output } = await model({ [models[model_type].input]: pixel_values }); - const mask = await RawImage.fromTensor(output[0].mul(255).to('uint8')).resize(image.width, image.height); - const canvas = document.createElement('canvas'); - canvas.width = image.width; - canvas.height = image.height; - const ctx = canvas.getContext('2d'); - - if (!ctx) return; - - ctx.drawImage(image.toCanvas(), 0, 0); - - const pixelData = ctx.getImageData(0, 0, image.width, image.height); - for (let i = 0; i < mask.data.length; ++i) { - pixelData.data[4 * i + 3] = mask.data[i]; - } - ctx.putImageData(pixelData, 0, 0); - - return canvas; + self.postMessage({ source: originalImageBlob, mask }); } } + +self.onmessage = async (event: MessageEvent) => { + await removeImageBackground(event.data.file); + self.close(); +} diff --git a/web/src/routes/cutout/+page.svelte b/web/src/routes/cutout/+page.svelte index dded67bb..eed2ef19 100644 --- a/web/src/routes/cutout/+page.svelte +++ b/web/src/routes/cutout/+page.svelte @@ -1,36 +1,49 @@ - {#if !thinking && !done} + {#if state === "empty"} thinking very hard rn...
{/if} - {#if done} + {#if state === "done"}
thought a lot, here's what i got:
{/if} - {#if thinking || done} + {#if ["busy", "done"].includes(state)}
- {#if !done} + {#if state === "busy"} {/if}
{/if} - {#if done} + {#if state === "done"}
+ +
+ {/if}
@@ -272,11 +224,22 @@
processing ({progress}%, {speed}x)...
+ {:else if currentProgress && speed} +
+ processing ({currentProgress}s, {speed}x)... +
{:else} - processing... +
processing...
{/if} - {:else} - done! + {/if}
@@ -311,7 +274,6 @@ transition: transform 0.2s, opacity 0.2s; - pointer-events: none; } #remux-processing.processing { @@ -331,6 +293,7 @@ padding: var(--padding); gap: var(--padding); justify-content: center; + align-items: center; } .progress-bar { @@ -348,6 +311,9 @@ #remux-receiver { max-width: 450px; + display: flex; + flex-direction: column; + gap: var(--padding); } #remux-bullets { @@ -357,6 +323,18 @@ max-width: 450px; } + .button-row { + display: flex; + flex-direction: row; + gap: 6px; + } + + button { + padding: 12px 24px; + border-radius: 200px; + width: fit-content; + } + @media screen and (max-width: 920px) { #remux-open { flex-direction: column; diff --git a/web/src/routes/remux/+page.ts b/web/src/routes/remux/+page.ts new file mode 100644 index 00000000..a3d15781 --- /dev/null +++ b/web/src/routes/remux/+page.ts @@ -0,0 +1 @@ +export const ssr = false; From 0e26424355bf685a1bfc8dfb76b9fc5b7be59114 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 15 Jan 2025 22:25:59 +0600 Subject: [PATCH 048/439] web/libav: remove environment import to fix the worker --- web/src/lib/libav.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/src/lib/libav.ts b/web/src/lib/libav.ts index 048323e1..af5c2e8a 100644 --- a/web/src/lib/libav.ts +++ b/web/src/lib/libav.ts @@ -2,7 +2,6 @@ import mime from "mime"; import LibAV, { type LibAV as LibAVInstance } from "@imput/libav.js-remux-cli"; import type { FFmpegProgressCallback, FFmpegProgressEvent, FFmpegProgressStatus, FileInfo, RenderParams } from "./types/libav"; import type { FfprobeData } from "fluent-ffmpeg"; -import { browser } from "$app/environment"; export default class LibAVWrapper { libav: Promise | null; @@ -11,7 +10,7 @@ export default class LibAVWrapper { constructor(onProgress?: FFmpegProgressCallback) { this.libav = null; - this.concurrency = Math.min(4, browser ? navigator.hardwareConcurrency : 0); + this.concurrency = Math.min(4, navigator.hardwareConcurrency || 0); this.onProgress = onProgress; } From f544768784238b3517fe0f5d80bee324575442a2 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 15 Jan 2025 23:14:29 +0600 Subject: [PATCH 049/439] web/cutout: add a button to cancel the job --- web/src/lib/workers/removebg.ts | 3 +++ web/src/routes/cutout/+page.svelte | 39 +++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/web/src/lib/workers/removebg.ts b/web/src/lib/workers/removebg.ts index 0ab8d6ed..811e5f13 100644 --- a/web/src/lib/workers/removebg.ts +++ b/web/src/lib/workers/removebg.ts @@ -38,6 +38,9 @@ const removeImageBackground = async (file: File) => { const model_type = "light"; const model = await AutoModel.from_pretrained(models[model_type].id, { + progress_callback: (progress) => { + console.log(progress); + }, device: "wasm", dtype: "fp32", }); diff --git a/web/src/routes/cutout/+page.svelte b/web/src/routes/cutout/+page.svelte index eed2ef19..23ff14c3 100644 --- a/web/src/routes/cutout/+page.svelte +++ b/web/src/routes/cutout/+page.svelte @@ -19,13 +19,15 @@ let state: "empty" | "busy" | "done" = "empty"; - const worker = new RemoveBgWorker(); + let worker: Worker; const processImage = async () => { if (!file) return; state = "busy"; + worker = new RemoveBgWorker(); + worker.postMessage({ file }); worker.onmessage = async (event) => { const maskedCanvas = await maskImage(event.data.source, event.data.mask); @@ -39,6 +41,8 @@ result = maskedCanvas; imageContainer.append(maskedCanvas); + + worker.terminate(); }; worker.onerror = (e) => { @@ -73,12 +77,21 @@ acceptTypes={["image/*"]} acceptExtensions={["jpg", "png", "webp"]} /> + {#if file} +
+ + +
+ {/if}
this is a very early & basic proof-of-concept, nothing about this feature is final or complete. please don't share or talk about it.
- {#if file} - - {/if} {/if} {#if state === "busy"} @@ -97,8 +110,22 @@
{/if} + {#if state === "busy"} +
+ +
+ {/if} + {#if state === "done"} -
+
{/if} From 28eb9ebe5d949db9c0189bcc9952c3d0d41b4fc2 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 17 Jan 2025 01:16:51 +0600 Subject: [PATCH 052/439] web/remux: improve page <-> worker messaging --- web/src/lib/workers/remux.ts | 35 +++++++++++++++++++------------ web/src/routes/remux/+page.svelte | 19 ++++++++++------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/web/src/lib/workers/remux.ts b/web/src/lib/workers/remux.ts index cd4f73fd..502d03c0 100644 --- a/web/src/lib/workers/remux.ts +++ b/web/src/lib/workers/remux.ts @@ -2,16 +2,20 @@ import mime from "mime"; import LibAVWrapper from "$lib/libav"; const error = (code: string) => { - return { - error: `error.${code}`, - } + self.postMessage({ + cobaltRemuxWorker: { + error: `error.${code}`, + } + }) } const ff = new LibAVWrapper((progress) => { self.postMessage({ - progress: { - durationProcessed: progress.out_time_sec, - speed: progress.speed, + cobaltRemuxWorker: { + progress: { + durationProcessed: progress.out_time_sec, + speed: progress.speed, + } } }) }); @@ -29,18 +33,21 @@ const remux = async (file: File) => { console.error("uh oh! out of memory"); console.error(e); - self.postMessage(error("remux.out_of_resources")); + error("remux.out_of_resources"); + self.close(); } }); if (!file_info?.format) { - self.postMessage(error("remux.corrupted")); + error("remux.corrupted"); return; } self.postMessage({ - progressInfo: { - duration: Number(file_info.format.duration), + cobaltRemuxWorker: { + progressInfo: { + duration: Number(file_info.format.duration), + } } }); @@ -59,7 +66,7 @@ const remux = async (file: File) => { console.error("uh-oh! render error"); console.error(e); - self.postMessage(error("remux.out_of_resources")); + error("remux.out_of_resources"); }); if (!render) { @@ -72,8 +79,10 @@ const remux = async (file: File) => { const filename = `${filenameParts.join(".")} (remux).${filenameExt}`; self.postMessage({ - render, - filename + cobaltRemuxWorker: { + render, + filename + } }); } catch (e) { console.log(e); diff --git a/web/src/routes/remux/+page.svelte b/web/src/routes/remux/+page.svelte index 0c9e00f7..ff644693 100644 --- a/web/src/routes/remux/+page.svelte +++ b/web/src/routes/remux/+page.svelte @@ -45,10 +45,13 @@ }; worker.onmessage = (event) => { - console.log(event.data); + const eventData = event.data.cobaltRemuxWorker; + if (!eventData) return; - if (event.data.progress) { - let eprogress = event.data.progress; + console.log(eventData); + + if (eventData.progress) { + let eprogress = eventData.progress; if (eprogress?.speed) { speed = eprogress.speed; @@ -72,17 +75,17 @@ console.log(eprogress, progress, speed, currentProgress); } - if (event.data.render) { + if (eventData.render) { processing = false; worker.terminate(); return downloadFile({ - file: new File([event.data.render], event.data.filename, { - type: event.data.render.type, + file: new File([eventData.render], eventData.filename, { + type: eventData.render.type, }), }); } - if (event.data.error) { + if (eventData.error) { processing = false; worker.terminate(); @@ -90,7 +93,7 @@ id: "remux-error", type: "small", meowbalt: "error", - bodyText: $t(event.data.error), + bodyText: $t(eventData.error), buttons: [ { text: $t("button.gotit"), From cc3e3be118e476543d2737448a602794deb2984f Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 17 Jan 2025 01:25:52 +0600 Subject: [PATCH 053/439] web/cutout: fix canvas visibility --- web/src/routes/cutout/+page.svelte | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/web/src/routes/cutout/+page.svelte b/web/src/routes/cutout/+page.svelte index 3d923819..b38a29e0 100644 --- a/web/src/routes/cutout/+page.svelte +++ b/web/src/routes/cutout/+page.svelte @@ -12,7 +12,6 @@ let draggedOver = false; let file: File | undefined; - let result: ImageBitmap; let imageContainer: HTMLElement; let canvas: HTMLCanvasElement; @@ -20,7 +19,7 @@ let worker: Worker; - const renderImageToCanvas = () => { + const renderImageToCanvas = (result: ImageBitmap) => { if (canvas && result) { canvas.width = result.width; canvas.height = result.height; @@ -40,8 +39,7 @@ const eventData = event.data.cobaltRemoveBgWorker; if (eventData.result) { state = "done"; - result = eventData.result; - renderImageToCanvas(); + renderImageToCanvas(eventData.result); } }; @@ -53,7 +51,7 @@ }; const exportImage = async () => { - if (!result || !file) return; + if (!file) return; const resultBlob = await new Promise((resolve, reject) => { canvas.toBlob(blob => { @@ -110,7 +108,7 @@ {/if} {#if ["busy", "done"].includes(state)} -
+
{#if state === "busy"} {/if} @@ -175,6 +173,14 @@ box-shadow: var(--button-box-shadow); } + #image-preview canvas { + display: none; + } + + #image-preview.done canvas { + display: block; + } + .button-row { display: flex; flex-direction: row; From b85771dc1dd017947cb1b248a9112d8648c524b7 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 17 Jan 2025 01:45:11 +0600 Subject: [PATCH 054/439] web/removebg: differentiate messaging even more, add temporary logging --- web/src/lib/workers/removebg.ts | 15 +++++++++++---- web/src/routes/cutout/+page.svelte | 25 +++++++++++++++---------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/web/src/lib/workers/removebg.ts b/web/src/lib/workers/removebg.ts index 29f62ff6..66b6fc3c 100644 --- a/web/src/lib/workers/removebg.ts +++ b/web/src/lib/workers/removebg.ts @@ -40,24 +40,31 @@ const removeImageBackground = async (file: File) => { dtype: "fp32", }); + console.log("we're past model loading!"); + const processor = await AutoProcessor.from_pretrained(models[model_type].id, {}); + console.log("now also past processor!"); + if (model && processor) { const { pixel_values } = await processor(image); + console.log("got pixel values"); const { output } = await model({ [models[model_type].input]: pixel_values }); + console.log("got output"); const mask = await RawImage.fromTensor(output[0].mul(255).to('uint8')).resize(image.width, image.height); + console.log("got the mask"); self.postMessage({ cobaltRemoveBgWorker: { result: maskImage(image, mask), } - }); + }); } } self.onmessage = async (event: MessageEvent) => { - if (event.data.file) { - await removeImageBackground(event.data.file); + if (event.data.cobaltRemoveBgWorker.file) { + await removeImageBackground(event.data.cobaltRemoveBgWorker.file); + self.close(); } - self.close(); } diff --git a/web/src/routes/cutout/+page.svelte b/web/src/routes/cutout/+page.svelte index b38a29e0..fdc49e66 100644 --- a/web/src/routes/cutout/+page.svelte +++ b/web/src/routes/cutout/+page.svelte @@ -1,6 +1,6 @@
- +
{#if queueLength > 0} - {/if} - - {#if $settings.advanced.debug} - - {/if}
{#each queueItems as [id, item]} {/each} {#if queueLength === 0} diff --git a/web/src/components/queue/ProcessingQueueItem.svelte b/web/src/components/queue/ProcessingQueueItem.svelte index 6c43a6cf..75d6743b 100644 --- a/web/src/components/queue/ProcessingQueueItem.svelte +++ b/web/src/components/queue/ProcessingQueueItem.svelte @@ -1,41 +1,67 @@
- +
{filename}
-
-
-
-
{id}: {status}
+ {#if state === "running"} +
+
+
+ {/if} +
{id}: {state}
- - + {/if} +
diff --git a/web/src/lib/queen-bee/queue.ts b/web/src/lib/queen-bee/queue.ts new file mode 100644 index 00000000..8e2b3b90 --- /dev/null +++ b/web/src/lib/queen-bee/queue.ts @@ -0,0 +1,36 @@ +import { addItem } from "$lib/state/queen-bee/queue"; +import type { CobaltPipelineItem } from "$lib/types/workers"; + +export const getMediaType = (type: string) => { + const kind = type.split('/')[0]; + + // can't use .includes() here for some reason + if (kind === "video" || kind === "audio" || kind === "image") { + return kind; + } +} + +export const createRemuxPipeline = (file: File) => { + // chopped khia + const parentId = crypto.randomUUID(); + const mediaType = getMediaType(file.type); + + const pipeline: CobaltPipelineItem[] = [{ + worker: "remux", + workerId: crypto.randomUUID(), + parentId, + workerArgs: { + files: [file], + }, + }]; + + if (mediaType) { + addItem({ + id: parentId, + state: "waiting", + pipeline, + filename: file.name, + mediaType, + }) + } +} diff --git a/web/src/lib/queen-bee/run-worker.ts b/web/src/lib/queen-bee/run-worker.ts new file mode 100644 index 00000000..8163865a --- /dev/null +++ b/web/src/lib/queen-bee/run-worker.ts @@ -0,0 +1,60 @@ +import RemuxWorker from "$lib/workers/remux?worker"; +//import RemoveBgWorker from "$lib/workers/removebg?worker"; + +import type { CobaltPipelineItem } from "$lib/types/workers"; +import { itemDone, itemError } from "$lib/state/queen-bee/queue"; + +const workerError = (parentId: string, workerId: string, worker: Worker, error: string) => { + itemError(parentId, workerId, error); + worker.terminate(); +} + +const workerSuccess = (parentId: string, workerId: string, worker: Worker, file: File) => { + itemDone(parentId, workerId, file); + worker.terminate(); +} + +export const runRemuxWorker = async (workerId: string, parentId: string, file: File) => { + const worker = new RemuxWorker(); + + worker.postMessage({ file }); + + worker.onerror = (e) => { + console.error("remux worker exploded:", e); + + // TODO: proper error code + workerError(parentId, workerId, worker, "internal error"); + }; + + worker.onmessage = (event) => { + const eventData = event.data.cobaltRemuxWorker; + if (!eventData) return; + + console.log(eventData); + + // TODO: calculate & use progress again + + if (eventData.render) { + return workerSuccess( + parentId, + workerId, + worker, + new File([eventData.render], eventData.filename, { + type: eventData.render.type, + }) + ); + } + + if (eventData.error) { + return workerError(parentId, workerId, worker, eventData.error); + } + }; +} + +export const startWorker = async ({ worker, workerId, parentId, workerArgs }: CobaltPipelineItem) => { + switch (worker) { + case "remux": + await runRemuxWorker(workerId, parentId, workerArgs.files[0]); + break; + } +} diff --git a/web/src/lib/queen-bee/scheduler.ts b/web/src/lib/queen-bee/scheduler.ts new file mode 100644 index 00000000..468415ae --- /dev/null +++ b/web/src/lib/queen-bee/scheduler.ts @@ -0,0 +1,41 @@ +import { get } from "svelte/store"; +import { itemRunning, queue } from "$lib/state/queen-bee/queue"; +import { startWorker } from "$lib/queen-bee/run-worker"; +import { addWorkerToQueue, currentTasks } from "$lib/state/queen-bee/current-tasks"; + +export const checkTasks = () => { + const queueItems = get(queue); + const ongoingTasks = get(currentTasks) + + if (Object.keys(ongoingTasks).length > 0) return; + + for (const item of Object.keys(queueItems)) { + const task = queueItems[item]; + + if (task.state === "running") { + break; + } + + if (task.state === "waiting") { + for (let i = 0; i < task.pipeline.length; i++) { + // TODO: loop here and pass the file between pipelines + // or schedule several tasks one after another but within + // one parent & pipeline + const pipelineItem = task.pipeline[i]; + + startWorker(pipelineItem); + + addWorkerToQueue({ + id: pipelineItem.workerId, + parentId: task.id, + step: i + 1, + totalSteps: task.pipeline.length, + }); + + itemRunning(task.id, i); + break; + } + break; + } + } +} diff --git a/web/src/lib/state/queen-bee/current-tasks.ts b/web/src/lib/state/queen-bee/current-tasks.ts new file mode 100644 index 00000000..e1c26d85 --- /dev/null +++ b/web/src/lib/state/queen-bee/current-tasks.ts @@ -0,0 +1,40 @@ +import { readable, type Updater } from "svelte/store"; + +import type { CobaltWorkerProgress } from "$lib/types/workers"; +import type { CobaltCurrentTasks, CobaltCurrentTaskItem } from "$lib/types/queen-bee"; + +let update: (_: Updater) => void; + +const currentTasks = readable( + {}, + (_, _update) => { update = _update } +); + +export function addWorkerToQueue(item: CobaltCurrentTaskItem) { + update(tasks => { + tasks[item.id] = item; + return tasks; + }); +} + +export function removeWorkerFromQueue(id: string) { + update(tasks => { + delete tasks[id]; + return tasks; + }); +} + +export function updateWorkerProgress(id: string, progress: CobaltWorkerProgress) { + update(tasks => { + tasks[id].progress = progress; + return tasks; + }); +} + +export function clearQueue() { + update(() => { + return {}; + }); +} + +export { currentTasks }; diff --git a/web/src/lib/state/queen-bee/queue.ts b/web/src/lib/state/queen-bee/queue.ts new file mode 100644 index 00000000..4d784844 --- /dev/null +++ b/web/src/lib/state/queen-bee/queue.ts @@ -0,0 +1,84 @@ +import { readable, type Updater } from "svelte/store"; +import type { CobaltQueue, CobaltQueueItem } from "$lib/types/queue"; +import { checkTasks } from "$lib/queen-bee/scheduler"; +import { removeWorkerFromQueue } from "./current-tasks"; + +let update: (_: Updater) => void; + +const queue = readable( + {}, + (_, _update) => { update = _update } +); + +export function addItem(item: CobaltQueueItem) { + update(queueData => { + queueData[item.id] = item; + return queueData; + }); + + checkTasks(); +} + +export function itemError(id: string, workerId: string, error: string) { + update(queueData => { + if (queueData[id]) { + queueData[id] = { + ...queueData[id], + state: "error", + errorCode: error, + } + } + return queueData; + }); + + removeWorkerFromQueue(workerId); + checkTasks(); +} + +export function itemDone(id: string, workerId: string, file: File) { + update(queueData => { + if (queueData[id]) { + queueData[id] = { + ...queueData[id], + state: "done", + resultFile: file, + } + } + return queueData; + }); + + removeWorkerFromQueue(workerId); + checkTasks(); +} + +export function itemRunning(id: string, step: number) { + update(queueData => { + if (queueData[id]) { + queueData[id] = { + ...queueData[id], + state: "running", + currentStep: step, + } + } + return queueData; + }); + + checkTasks(); +} + +export function removeItem(id: string) { + update(queueData => { + delete queueData[id]; + return queueData; + }); + + checkTasks(); +} + +export function clearQueue() { + update(() => { + return {}; + }); +} + +export { queue }; diff --git a/web/src/lib/state/queue.ts b/web/src/lib/state/queue.ts deleted file mode 100644 index ee431743..00000000 --- a/web/src/lib/state/queue.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { merge } from "ts-deepmerge"; -import { get, readable, type Updater } from "svelte/store"; -import type { OngoingQueueItem, QueueItem } from "$lib/types/queue"; - -type Queue = { - [id: string]: QueueItem; -} - -type OngoingQueue = { - [id: string]: OngoingQueueItem; -} - -let update: (_: Updater) => void; - -const queue = readable( - {}, - (_, _update) => { update = _update } -); - -export function addToQueue(item: QueueItem) { - update(queueData => { - queueData[item.id] = item; - return queueData; - }); -} - -export function removeFromQueue(id: string) { - update(queueData => { - delete queueData[id]; - return queueData; - }); -} - -let updateOngoing: (_: Updater) => void; - -const ongoingQueue = readable( - {}, - (_, _update) => { updateOngoing = _update } -); - -export function updateOngoingQueue(id: string, itemInfo: Partial = {}) { - updateOngoing(queueData => { - if (get(queue)?.id) { - queueData[id] = merge(queueData[id], { - id, - ...itemInfo, - }); - } - - return queueData; - }); -} - -export function removeFromOngoingQueue(id: string) { - updateOngoing(queue => { - delete queue[id]; - return queue; - }); -} - -export function nukeEntireQueue() { - update(() => { - return {}; - }); - updateOngoing(() => { - return {}; - }); -} - -export { queue, ongoingQueue }; diff --git a/web/src/lib/types/queen-bee.ts b/web/src/lib/types/queen-bee.ts new file mode 100644 index 00000000..3684ac5a --- /dev/null +++ b/web/src/lib/types/queen-bee.ts @@ -0,0 +1,13 @@ +import type { CobaltWorkerProgress } from "$lib/types/workers"; + +export type CobaltCurrentTaskItem = { + id: string, + parentId: string, // parent id is queue id to which this pipeline worker belongs to + step: number, + totalSteps: number, + progress?: CobaltWorkerProgress, +} + +export type CobaltCurrentTasks = { + [id: string]: CobaltCurrentTaskItem, +} diff --git a/web/src/lib/types/queue.ts b/web/src/lib/types/queue.ts index 6785b514..91caac90 100644 --- a/web/src/lib/types/queue.ts +++ b/web/src/lib/types/queue.ts @@ -1,34 +1,37 @@ -type ProcessingStep = "mux" | "mux_hls" | "encode"; -type ProcessingPreset = "mp4" | "webm" | "copy"; -type ProcessingState = "completed" | "failed" | "canceled" | "waiting" | "downloading" | "muxing" | "converting"; -type ProcessingType = "video" | "video_mute" | "audio" | "audio_convert" | "image" | "gif"; -type QueueFileType = "video" | "audio" | "image" | "gif"; +import type { CobaltPipelineItem, CobaltPipelineResultFileType } from "$lib/types/workers"; -export type ProcessingStepItem = { - type: ProcessingStep, - preset?: ProcessingPreset, -} +export type CobaltQueueItemState = "waiting" | "running" | "done" | "error"; -export type QueueFile = { - type: QueueFileType, - url: string, -} - -export type QueueItem = { +export type CobaltQueueBaseItem = { id: string, - status: ProcessingState, - type: ProcessingType, + state: CobaltQueueItemState, + pipeline: CobaltPipelineItem[], + // TODO: metadata filename: string, - files: QueueFile[], - processingSteps: ProcessingStepItem[], -} + mediaType: CobaltPipelineResultFileType, +}; -export type OngoingQueueItem = { - id: string, - currentStep?: ProcessingStep, - size?: { - expected: number, - current: number, - }, - speed?: number, -} +export type CobaltQueueItemWaiting = CobaltQueueBaseItem & { + state: "waiting", +}; + +export type CobaltQueueItemRunning = CobaltQueueBaseItem & { + state: "running", + currentStep: number, +}; + +export type CobaltQueueItemDone = CobaltQueueBaseItem & { + state: "done", + resultFile: File, +}; + +export type CobaltQueueItemError = CobaltQueueBaseItem & { + state: "error", + errorCode: string, +}; + +export type CobaltQueueItem = CobaltQueueItemWaiting | CobaltQueueItemRunning | CobaltQueueItemDone | CobaltQueueItemError; + +export type CobaltQueue = { + [id: string]: CobaltQueueItem, +}; diff --git a/web/src/lib/types/workers.ts b/web/src/lib/types/workers.ts new file mode 100644 index 00000000..3bbc79c5 --- /dev/null +++ b/web/src/lib/types/workers.ts @@ -0,0 +1,22 @@ +export const resultFileTypes = ["video", "audio", "image"] as const; + +export type CobaltWorkerType = "remux" | "removebg"; +export type CobaltPipelineResultFileType = typeof resultFileTypes[number]; + +export type CobaltWorkerProgress = { + indeterminate: boolean, + speed?: number, + percentage: number, +} + +export type CobaltWorkerArgs = { + files: File[], + //TODO: args for libav & etc with unique types +} + +export type CobaltPipelineItem = { + worker: CobaltWorkerType, + workerId: string, + parentId: string, + workerArgs: CobaltWorkerArgs, +} diff --git a/web/src/routes/remux/+page.svelte b/web/src/routes/remux/+page.svelte index ff644693..787063cd 100644 --- a/web/src/routes/remux/+page.svelte +++ b/web/src/routes/remux/+page.svelte @@ -1,11 +1,5 @@ @@ -150,7 +38,7 @@ - {#if file} + {#if files}
- {/if} -
-
From 2ae0fd01dde0fe23f25c1c7a7e9e9fafb8092d45 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 31 Jan 2025 21:59:44 +0600 Subject: [PATCH 086/439] web/ProcessingQueue: use full progress per item, not just running task --- web/src/components/queue/ProcessingQueue.svelte | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/web/src/components/queue/ProcessingQueue.svelte b/web/src/components/queue/ProcessingQueue.svelte index 2d9e0915..8535f58e 100644 --- a/web/src/components/queue/ProcessingQueue.svelte +++ b/web/src/components/queue/ProcessingQueue.svelte @@ -19,11 +19,20 @@ $: queue = Object.entries($readableQueue); + const totalItemProgress = (completed: number, current: number, total: number) => { + return (completed * 100 + current) / total + } + $: totalProgress = queue.length ? queue.map(([, item]) => { - if (item.state === "done" || item.state === "error") + if (item.state === "done" || item.state === "error") { return 100; - else if (item.state === "running") - return $currentTasks[item.runningWorker]?.progress?.percentage || 0; + } else if (item.state === "running") { + return totalItemProgress( + item.completedWorkers?.length || 0, + $currentTasks[item.runningWorker]?.progress?.percentage || 0, + item.pipeline.length || 0 + ); + } return 0; }).reduce((a, b) => a + b) / (100 * queue.length) : 0; From a7c1317af764424b7d67bb3ddf71ba666b52cfb2 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 31 Jan 2025 22:02:18 +0600 Subject: [PATCH 087/439] web/state/queue: clear pipeline results on error --- web/src/lib/state/queen-bee/queue.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/src/lib/state/queen-bee/queue.ts b/web/src/lib/state/queen-bee/queue.ts index d2ca093c..43716d36 100644 --- a/web/src/lib/state/queen-bee/queue.ts +++ b/web/src/lib/state/queen-bee/queue.ts @@ -22,6 +22,10 @@ export function addItem(item: CobaltQueueItem) { export function itemError(id: string, workerId: string, error: string) { update(queueData => { if (queueData[id]) { + if (queueData[id].state === "running" && queueData[id].pipelineResults) { + delete queueData[id].pipelineResults; + } + queueData[id] = { ...queueData[id], state: "error", From 6513ab38d04396c7afec94ece8ca1b7e6d98cbc8 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 31 Jan 2025 22:02:35 +0600 Subject: [PATCH 088/439] web/state/queue: clear all current tasks on queue clear --- web/src/lib/state/queen-bee/current-tasks.ts | 2 +- web/src/lib/state/queen-bee/queue.ts | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/web/src/lib/state/queen-bee/current-tasks.ts b/web/src/lib/state/queen-bee/current-tasks.ts index aa413702..528197d6 100644 --- a/web/src/lib/state/queen-bee/current-tasks.ts +++ b/web/src/lib/state/queen-bee/current-tasks.ts @@ -31,7 +31,7 @@ export function updateWorkerProgress(workerId: string, progress: CobaltWorkerPro }); } -export function clearQueue() { +export function clearCurrentTasks() { update(() => { return {}; }); diff --git a/web/src/lib/state/queen-bee/queue.ts b/web/src/lib/state/queen-bee/queue.ts index 43716d36..1ff45162 100644 --- a/web/src/lib/state/queen-bee/queue.ts +++ b/web/src/lib/state/queen-bee/queue.ts @@ -1,7 +1,7 @@ import { readable, type Updater } from "svelte/store"; -import type { CobaltQueue, CobaltQueueItem } from "$lib/types/queue"; import { checkTasks } from "$lib/queen-bee/scheduler"; -import { removeWorkerFromQueue } from "./current-tasks"; +import type { CobaltQueue, CobaltQueueItem } from "$lib/types/queue"; +import { clearCurrentTasks, removeWorkerFromQueue } from "$lib/state/queen-bee/current-tasks"; let update: (_: Updater) => void; @@ -103,6 +103,8 @@ export function clearQueue() { update(() => { return {}; }); + + clearCurrentTasks(); } export { queue }; From 00d376d4ac66f513b736e70cf918acb7ba423872 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 31 Jan 2025 22:08:57 +0600 Subject: [PATCH 089/439] web/scheduler: break the global loop if current task is not done i forgot to put break here, just blinded out that break on line 55 is breaking only its own inner loop --- web/src/lib/queen-bee/scheduler.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/web/src/lib/queen-bee/scheduler.ts b/web/src/lib/queen-bee/scheduler.ts index c018dc5b..85120ba4 100644 --- a/web/src/lib/queen-bee/scheduler.ts +++ b/web/src/lib/queen-bee/scheduler.ts @@ -55,6 +55,8 @@ export const checkTasks = () => { break; } } + + break; } // start the nearest waiting task and wait to be called again From ef08633bdb8afcd0e651df2f42c46b01c3c55f3f Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 31 Jan 2025 23:06:17 +0600 Subject: [PATCH 090/439] web/ProcessingQueueItem: mobile css fixes --- web/src/components/queue/ProcessingQueue.svelte | 4 +++- web/src/components/queue/ProcessingQueueItem.svelte | 10 +++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/web/src/components/queue/ProcessingQueue.svelte b/web/src/components/queue/ProcessingQueue.svelte index 8535f58e..0d00a5d0 100644 --- a/web/src/components/queue/ProcessingQueue.svelte +++ b/web/src/components/queue/ProcessingQueue.svelte @@ -125,6 +125,8 @@ flex-direction: row; justify-content: space-between; align-items: center; + flex-wrap: wrap; + gap: 6px; } .header-buttons { @@ -157,7 +159,7 @@ flex-direction: column; max-height: 65vh; - overflow: scroll; + overflow-y: scroll; } @media screen and (max-width: 535px) { diff --git a/web/src/components/queue/ProcessingQueueItem.svelte b/web/src/components/queue/ProcessingQueueItem.svelte index d57a004b..e5b8ee4d 100644 --- a/web/src/components/queue/ProcessingQueueItem.svelte +++ b/web/src/components/queue/ProcessingQueueItem.svelte @@ -111,9 +111,10 @@ .file-actions { display: flex; flex-direction: row; - justify-content: center; + justify-content: flex-start; align-items: center; position: relative; + overflow: hidden; } .processing-item { @@ -209,6 +210,13 @@ } } + /* TODO: fix this shitty workaround */ + @media(hover: none) { + .processing-info { + width: calc(100% - 80px); + } + } + .action-button { padding: 8px; height: auto; From 590b42a574552e887fb41852df3158cbc8ae2e34 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 31 Jan 2025 23:20:44 +0600 Subject: [PATCH 091/439] web/ProcessingQueueItem: fix processing-info overflow on mobile --- web/src/components/queue/ProcessingQueueItem.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/components/queue/ProcessingQueueItem.svelte b/web/src/components/queue/ProcessingQueueItem.svelte index e5b8ee4d..f3da210d 100644 --- a/web/src/components/queue/ProcessingQueueItem.svelte +++ b/web/src/components/queue/ProcessingQueueItem.svelte @@ -210,10 +210,10 @@ } } - /* TODO: fix this shitty workaround */ @media(hover: none) { .processing-info { - width: calc(100% - 80px); + overflow: hidden; + flex: 1; } } From 90dcc48cad0d30d54867a1dd83d46c1e411ba61d Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 31 Jan 2025 23:54:41 +0600 Subject: [PATCH 092/439] web/i18n/queue: update stub text --- web/i18n/en/queue.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/i18n/en/queue.json b/web/i18n/en/queue.json index e02cf052..4ce13ab7 100644 --- a/web/i18n/en/queue.json +++ b/web/i18n/en/queue.json @@ -1,6 +1,6 @@ { "title": "processing queue", - "stub": "nothing in the queue yet, just the two of us.\ntry {{ value }} something!", + "stub": "nothing here yet, just the two of us.\ntry {{ value }} something!", "stub.download": "downloading", "stub.remux": "remuxing", From ee459e86949256329466345a9a7448dadceb6e39 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 31 Jan 2025 23:59:01 +0600 Subject: [PATCH 093/439] web/layout: always display processing queue because the remux page relies on it --- web/src/routes/+layout.svelte | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte index 00cdbf23..c10788f1 100644 --- a/web/src/routes/+layout.svelte +++ b/web/src/routes/+layout.svelte @@ -91,9 +91,7 @@ {/if}
- {#if $settings.advanced.localProcessing} - - {/if} + {#if ($turnstileEnabled && $page.url.pathname === "/") || $turnstileCreated} {/if} From 0a8323be54c81600d2a45a2561658d8b63a2a5d2 Mon Sep 17 00:00:00 2001 From: wukko Date: Sat, 1 Feb 2025 22:49:21 +0600 Subject: [PATCH 094/439] web/tsconfig: add webworker lib --- web/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/tsconfig.json b/web/tsconfig.json index 52b33f5e..3ee0d63e 100644 --- a/web/tsconfig.json +++ b/web/tsconfig.json @@ -10,7 +10,8 @@ "sourceMap": true, "strict": true, "moduleResolution": "bundler", - "types": ["turnstile-types"] + "types": ["turnstile-types"], + "lib": ["WebWorker"] } // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files From 5464574a3e37bb7d06152daa38b221c868cdb209 Mon Sep 17 00:00:00 2001 From: wukko Date: Sat, 1 Feb 2025 23:26:57 +0600 Subject: [PATCH 095/439] web/workers: use opfs instead of blobs for better memory management spent almost an entire day figuring this out but it's so worth it --- .../queue/ProcessingQueueItem.svelte | 4 +- web/src/lib/libav.ts | 67 ++++--------------- web/src/lib/queen-bee/queue.ts | 5 +- web/src/lib/queen-bee/run-worker.ts | 20 ++++-- web/src/lib/state/queen-bee/queue.ts | 8 ++- web/src/lib/storage.ts | 53 +++++++++++++++ web/src/lib/types/libav.ts | 4 +- web/src/lib/types/queue.ts | 5 +- web/src/lib/types/storage.ts | 4 ++ web/src/lib/types/workers.ts | 3 +- web/src/lib/workers/fetch.ts | 20 ++++-- web/src/lib/workers/remux.ts | 10 ++- 12 files changed, 124 insertions(+), 79 deletions(-) create mode 100644 web/src/lib/storage.ts create mode 100644 web/src/lib/types/storage.ts diff --git a/web/src/components/queue/ProcessingQueueItem.svelte b/web/src/components/queue/ProcessingQueueItem.svelte index f3da210d..ac6aac65 100644 --- a/web/src/components/queue/ProcessingQueueItem.svelte +++ b/web/src/components/queue/ProcessingQueueItem.svelte @@ -65,7 +65,7 @@
{#if info.state === "done"} - {formatFileSize(info.resultFile?.size)} + {formatFileSize(info.resultFile?.file?.size)} {/if} {#if info.state === "running"} @@ -95,7 +95,7 @@ {#if info.state === "done" && info.resultFile} diff --git a/web/src/lib/libav.ts b/web/src/lib/libav.ts index b6275f26..6beac0c5 100644 --- a/web/src/lib/libav.ts +++ b/web/src/lib/libav.ts @@ -1,3 +1,4 @@ +import { OPFSStorage } from "$lib/storage"; import LibAV, { type LibAV as LibAVInstance } from "@imput/libav.js-remux-cli"; import type { FfprobeData } from "fluent-ffmpeg"; @@ -69,27 +70,18 @@ export default class LibAVWrapper { try { for (let i = 0; i < files.length; i++) { - await libav.mkreadaheadfile(`input${i}`, files[i]); + const file = files[i].file; + + await libav.mkreadaheadfile(`input${i}`, file); ffInputs.push('-i', `input${i}`); } await libav.mkwriterdev(outputName); await libav.mkwriterdev('progress.txt'); - const totalInputSize = files.reduce((a, b) => a + b.size, 0); + const storage = await OPFSStorage.init(); - const MB = 1024 * 1024; - const chunks: Uint8Array[] = []; - const chunkSize = Math.min(512 * MB, totalInputSize); - - // since we expect the output file to be roughly the same size - // as inputs, preallocate its size for the output - for (let toAllocate = totalInputSize; toAllocate > 0; toAllocate -= chunkSize) { - chunks.push(new Uint8Array(chunkSize)); - } - - let actualSize = 0; - libav.onwrite = (name, pos, data) => { + libav.onwrite = async (name, pos, data) => { if (name === 'progress.txt') { try { return this.#emitProgress(data); @@ -98,26 +90,7 @@ export default class LibAVWrapper { } } else if (name !== outputName) return; - const writeEnd = pos + data.length; - if (writeEnd > chunkSize * chunks.length) { - chunks.push(new Uint8Array(chunkSize)); - } - - const chunkIndex = pos / chunkSize | 0; - const offset = pos - (chunkSize * chunkIndex); - - if (offset + data.length > chunkSize) { - chunks[chunkIndex].set( - data.subarray(0, chunkSize - offset), offset - ); - chunks[chunkIndex + 1].set( - data.subarray(chunkSize - offset), 0 - ); - } else { - chunks[chunkIndex].set(data, offset); - } - - actualSize = Math.max(writeEnd, actualSize); + await storage.write(data, pos); }; await libav.ffmpeg([ @@ -130,30 +103,14 @@ export default class LibAVWrapper { outputName ]); - // if we didn't need as much space as we allocated for some reason, - // shrink the buffers so that we don't inflate the file with zeroes - const outputView: Uint8Array[] = []; + const file = await storage.res(); - for (let i = 0; i < chunks.length; ++i) { - outputView.push( - chunks[i].subarray( - 0, Math.min(chunkSize, actualSize) - ) - ); + if (file.size === 0) return; - actualSize -= chunkSize; - if (actualSize <= 0) { - break; - } + return { + file, + type: output.type, } - - const renderBlob = new Blob( - outputView, - { type: output.type } - ); - - if (renderBlob.size === 0) return; - return renderBlob; } finally { try { await libav.unlink(outputName); diff --git a/web/src/lib/queen-bee/queue.ts b/web/src/lib/queen-bee/queue.ts index 10a9fef8..141d800c 100644 --- a/web/src/lib/queen-bee/queue.ts +++ b/web/src/lib/queen-bee/queue.ts @@ -23,7 +23,10 @@ export const createRemuxPipeline = (file: File) => { workerId: crypto.randomUUID(), parentId, workerArgs: { - files: [file], + files: [{ + file, + type: file.type, + }], ffargs: [ "-c", "copy", "-map", "0" diff --git a/web/src/lib/queen-bee/run-worker.ts b/web/src/lib/queen-bee/run-worker.ts index 4c05072f..47746806 100644 --- a/web/src/lib/queen-bee/run-worker.ts +++ b/web/src/lib/queen-bee/run-worker.ts @@ -8,6 +8,7 @@ import { pipelineTaskDone, itemError, queue } from "$lib/state/queen-bee/queue"; import type { FileInfo } from "$lib/types/libav"; import type { CobaltQueue } from "$lib/types/queue"; import type { CobaltPipelineItem } from "$lib/types/workers"; +import type { CobaltFileReference } from "$lib/types/storage"; const killWorker = (worker: Worker, unsubscribe: () => void, interval?: NodeJS.Timeout) => { unsubscribe(); @@ -15,7 +16,14 @@ const killWorker = (worker: Worker, unsubscribe: () => void, interval?: NodeJS.T if (interval) clearInterval(interval); } -export const runRemuxWorker = async (workerId: string, parentId: string, files: File[], args: string[], output: FileInfo, filename: string) => { +export const runRemuxWorker = async ( + workerId: string, + parentId: string, + files: CobaltFileReference[], + args: string[], + output: FileInfo, + filename: string +) => { const worker = new RemuxWorker(); // sometimes chrome refuses to start libav wasm, @@ -86,9 +94,7 @@ export const runRemuxWorker = async (workerId: string, parentId: string, files: return pipelineTaskDone( parentId, workerId, - new File([eventData.render], eventData.filename, { - type: eventData.render.type, - }) + eventData.render, ); } @@ -127,12 +133,12 @@ export const runFetchWorker = async (workerId: string, parentId: string, url: st }) } - if (eventData.file) { + if (eventData.result) { killWorker(worker, unsubscribe); return pipelineTaskDone( parentId, workerId, - eventData.file, + eventData.result, ); } @@ -144,7 +150,7 @@ export const runFetchWorker = async (workerId: string, parentId: string, url: st } export const startWorker = async ({ worker, workerId, parentId, workerArgs }: CobaltPipelineItem) => { - let files: File[] = []; + let files: CobaltFileReference[] = []; switch (worker) { case "remux": diff --git a/web/src/lib/state/queen-bee/queue.ts b/web/src/lib/state/queen-bee/queue.ts index 1ff45162..5151551b 100644 --- a/web/src/lib/state/queen-bee/queue.ts +++ b/web/src/lib/state/queen-bee/queue.ts @@ -1,8 +1,10 @@ import { readable, type Updater } from "svelte/store"; import { checkTasks } from "$lib/queen-bee/scheduler"; -import type { CobaltQueue, CobaltQueueItem } from "$lib/types/queue"; import { clearCurrentTasks, removeWorkerFromQueue } from "$lib/state/queen-bee/current-tasks"; +import type { CobaltFileReference } from "$lib/types/storage"; +import type { CobaltQueue, CobaltQueueItem } from "$lib/types/queue"; + let update: (_: Updater) => void; const queue = readable( @@ -39,7 +41,7 @@ export function itemError(id: string, workerId: string, error: string) { checkTasks(); } -export function itemDone(id: string, file: File) { +export function itemDone(id: string, file: CobaltFileReference) { update(queueData => { if (queueData[id]) { if (queueData[id].state === "running" && queueData[id].pipelineResults) { @@ -58,7 +60,7 @@ export function itemDone(id: string, file: File) { checkTasks(); } -export function pipelineTaskDone(id: string, workerId: string, file: File) { +export function pipelineTaskDone(id: string, workerId: string, file: CobaltFileReference) { update(queueData => { if (queueData[id] && queueData[id].state === "running") { queueData[id].pipelineResults = [...queueData[id].pipelineResults || [], file]; diff --git a/web/src/lib/storage.ts b/web/src/lib/storage.ts new file mode 100644 index 00000000..68a5982c --- /dev/null +++ b/web/src/lib/storage.ts @@ -0,0 +1,53 @@ +export class OPFSStorage { + #root; + #handle; + #io; + + constructor(root: FileSystemDirectoryHandle, handle: FileSystemFileHandle, reader: FileSystemSyncAccessHandle) { + this.#root = root; + this.#handle = handle; + this.#io = reader; + } + + static async init() { + const root = await navigator.storage.getDirectory(); + const cobaltDir = await root.getDirectoryHandle('cobalt-processing-data', { create: true }); + const handle = await cobaltDir.getFileHandle(crypto.randomUUID(), { create: true }); + const reader = await handle.createSyncAccessHandle(); + + return new this(cobaltDir, handle, reader); + } + + async res() { + // await for compat with ios 15 + await this.#io.flush(); + await this.#io.close(); + return await this.#handle.getFile(); + } + + read(size: number, offset: number) { + const out = new Uint8Array(size); + const bytesRead = this.#io.read(out, { at: offset }); + + return out.subarray(0, bytesRead); + } + + async write(data: Uint8Array | Int8Array, offset: number) { + const writ = this.#io.write(data, { at: offset }); + + if (data.length !== writ) { + console.log(data.length, writ); + } + + return writ; + } + + async destroy() { + await this.#root.removeEntry(this.#handle.name); + } + + static isAvailable() { + return !!navigator.storage?.getDirectory; + } +} + diff --git a/web/src/lib/types/libav.ts b/web/src/lib/types/libav.ts index 92a07a7d..9871e4cb 100644 --- a/web/src/lib/types/libav.ts +++ b/web/src/lib/types/libav.ts @@ -1,10 +1,12 @@ +import type { CobaltFileReference } from "$lib/types/storage"; + export type FileInfo = { type?: string, extension?: string, } export type RenderParams = { - files: File[], + files: CobaltFileReference[], output: FileInfo, args: string[], } diff --git a/web/src/lib/types/queue.ts b/web/src/lib/types/queue.ts index f50093a4..c09e0d6b 100644 --- a/web/src/lib/types/queue.ts +++ b/web/src/lib/types/queue.ts @@ -1,3 +1,4 @@ +import type { CobaltFileReference } from "$lib/types/storage"; import type { CobaltPipelineItem, CobaltPipelineResultFileType } from "$lib/types/workers"; export type CobaltQueueItemState = "waiting" | "running" | "done" | "error"; @@ -19,12 +20,12 @@ export type CobaltQueueItemRunning = CobaltQueueBaseItem & { state: "running", runningWorker: string, completedWorkers?: string[], - pipelineResults?: File[], + pipelineResults?: CobaltFileReference[], }; export type CobaltQueueItemDone = CobaltQueueBaseItem & { state: "done", - resultFile: File, + resultFile: CobaltFileReference, }; export type CobaltQueueItemError = CobaltQueueBaseItem & { diff --git a/web/src/lib/types/storage.ts b/web/src/lib/types/storage.ts new file mode 100644 index 00000000..a9bb13a7 --- /dev/null +++ b/web/src/lib/types/storage.ts @@ -0,0 +1,4 @@ +export type CobaltFileReference = { + file: File, + type: string, +} diff --git a/web/src/lib/types/workers.ts b/web/src/lib/types/workers.ts index 6a6e2c98..e42d8ba7 100644 --- a/web/src/lib/types/workers.ts +++ b/web/src/lib/types/workers.ts @@ -1,4 +1,5 @@ import type { FileInfo } from "$lib/types/libav"; +import type { CobaltFileReference } from "$lib/types/storage"; export const resultFileTypes = ["video", "audio", "image"] as const; @@ -12,7 +13,7 @@ export type CobaltWorkerProgress = { } export type CobaltWorkerArgs = { - files?: File[], + files?: CobaltFileReference[], url?: string, ffargs?: string[], output?: FileInfo, diff --git a/web/src/lib/workers/fetch.ts b/web/src/lib/workers/fetch.ts index 0965c51d..aa30a4c4 100644 --- a/web/src/lib/workers/fetch.ts +++ b/web/src/lib/workers/fetch.ts @@ -1,3 +1,5 @@ +import { OPFSStorage } from "$lib/storage"; + const error = (code: string) => { // TODO: return proper errors and code here self.postMessage({ @@ -22,20 +24,21 @@ const fetchFile = async (url: string) => { const totalBytes = contentLength ? parseInt(contentLength, 10) : null; const reader = response.body?.getReader(); + const storage = await OPFSStorage.init(); + if (!reader) { error("no reader"); return self.close(); } let receivedBytes = 0; - const chunks = []; while (true) { const { done, value } = await reader.read(); if (done) break; + await storage.write(value, receivedBytes); receivedBytes += value.length; - chunks.push(value); if (totalBytes) { self.postMessage({ @@ -52,15 +55,22 @@ const fetchFile = async (url: string) => { return self.close(); } - const file = new File(chunks, "file", { type: contentType }); + const file = await storage.res(); + + if (Number(contentLength) !== file.size) { + error("file is not downloaded fully"); + } self.postMessage({ cobaltFetchWorker: { - file + result: { + file, + type: contentType, + } } }); } catch (e) { - console.log(e) + console.log(e); error("error when downloading the file"); return self.close(); } diff --git a/web/src/lib/workers/remux.ts b/web/src/lib/workers/remux.ts index 61f06bfc..1f3f6aff 100644 --- a/web/src/lib/workers/remux.ts +++ b/web/src/lib/workers/remux.ts @@ -1,5 +1,7 @@ import LibAVWrapper from "$lib/libav"; + import type { FileInfo } from "$lib/types/libav"; +import type { CobaltFileReference } from "$lib/types/storage"; const error = (code: string) => { self.postMessage({ @@ -25,14 +27,17 @@ const ff = new LibAVWrapper((progress) => { ff.init(); -const remux = async (files: File[], args: string[], output: FileInfo, filename: string) => { +const remux = async (files: CobaltFileReference[], args: string[], output: FileInfo, filename: string) => { if (!(files && output && args)) return; await ff.init(); try { // probing just the first file in files array (usually audio) for duration progress - const file_info = await ff.probe(files[0]).catch((e) => { + const probeFile = files[0]?.file; + if (!probeFile) return error("couldn't probe one of files"); + + const file_info = await ff.probe(probeFile).catch((e) => { if (e?.message?.toLowerCase().includes("out of memory")) { console.error("uh oh! out of memory"); console.error(e); @@ -87,6 +92,7 @@ const remux = async (files: File[], args: string[], output: FileInfo, filename: }); } catch (e) { console.log(e); + return error("remux.crashed"); } } From 50df95b2127ba534102e28ddf80e49864d03f003 Mon Sep 17 00:00:00 2001 From: wukko Date: Sun, 2 Feb 2025 00:15:44 +0600 Subject: [PATCH 096/439] web/queue: clear files from storage when needed --- web/src/lib/state/queen-bee/queue.ts | 23 +++++++++++++++++------ web/src/lib/storage.ts | 23 +++++++++++++++-------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/web/src/lib/state/queen-bee/queue.ts b/web/src/lib/state/queen-bee/queue.ts index 5151551b..e6016297 100644 --- a/web/src/lib/state/queen-bee/queue.ts +++ b/web/src/lib/state/queen-bee/queue.ts @@ -1,10 +1,23 @@ import { readable, type Updater } from "svelte/store"; + import { checkTasks } from "$lib/queen-bee/scheduler"; +import { clearFileStorage, removeFromFileStorage } from "$lib/storage"; import { clearCurrentTasks, removeWorkerFromQueue } from "$lib/state/queen-bee/current-tasks"; import type { CobaltFileReference } from "$lib/types/storage"; import type { CobaltQueue, CobaltQueueItem } from "$lib/types/queue"; +const clearPipelineCache = (queueItem: CobaltQueueItem) => { + if (queueItem.state === "running" && queueItem.pipelineResults) { + for (const item of queueItem.pipelineResults) { + removeFromFileStorage(item.file.name); + } + delete queueItem.pipelineResults; + } + + return queueItem; +} + let update: (_: Updater) => void; const queue = readable( @@ -24,9 +37,7 @@ export function addItem(item: CobaltQueueItem) { export function itemError(id: string, workerId: string, error: string) { update(queueData => { if (queueData[id]) { - if (queueData[id].state === "running" && queueData[id].pipelineResults) { - delete queueData[id].pipelineResults; - } + queueData[id] = clearPipelineCache(queueData[id]); queueData[id] = { ...queueData[id], @@ -44,9 +55,7 @@ export function itemError(id: string, workerId: string, error: string) { export function itemDone(id: string, file: CobaltFileReference) { update(queueData => { if (queueData[id]) { - if (queueData[id].state === "running" && queueData[id].pipelineResults) { - delete queueData[id].pipelineResults; - } + queueData[id] = clearPipelineCache(queueData[id]); queueData[id] = { ...queueData[id], @@ -93,6 +102,7 @@ export function removeItem(id: string) { for (const worker in queueData[id].pipeline) { removeWorkerFromQueue(queueData[id].pipeline[worker].workerId); } + clearPipelineCache(queueData[id]); delete queueData[id]; return queueData; @@ -107,6 +117,7 @@ export function clearQueue() { }); clearCurrentTasks(); + clearFileStorage(); } export { queue }; diff --git a/web/src/lib/storage.ts b/web/src/lib/storage.ts index 68a5982c..cae26e79 100644 --- a/web/src/lib/storage.ts +++ b/web/src/lib/storage.ts @@ -1,3 +1,5 @@ +const cobaltProcessingDir = "cobalt-processing-data"; + export class OPFSStorage { #root; #handle; @@ -11,7 +13,7 @@ export class OPFSStorage { static async init() { const root = await navigator.storage.getDirectory(); - const cobaltDir = await root.getDirectoryHandle('cobalt-processing-data', { create: true }); + const cobaltDir = await root.getDirectoryHandle(cobaltProcessingDir, { create: true }); const handle = await cobaltDir.getFileHandle(crypto.randomUUID(), { create: true }); const reader = await handle.createSyncAccessHandle(); @@ -33,13 +35,7 @@ export class OPFSStorage { } async write(data: Uint8Array | Int8Array, offset: number) { - const writ = this.#io.write(data, { at: offset }); - - if (data.length !== writ) { - console.log(data.length, writ); - } - - return writ; + return this.#io.write(data, { at: offset }) } async destroy() { @@ -51,3 +47,14 @@ export class OPFSStorage { } } +export const removeFromFileStorage = async (filename: string) => { + const root = await navigator.storage.getDirectory(); + const cobaltDir = await root.getDirectoryHandle(cobaltProcessingDir); + return await cobaltDir.removeEntry(filename); +} + +export const clearFileStorage = async () => { + const root = await navigator.storage.getDirectory(); + return await root.removeEntry(cobaltProcessingDir, { recursive: true }); +} + From 61efa619a27a9d3365207ad3d84dab3e1e4dc038 Mon Sep 17 00:00:00 2001 From: wukko Date: Sun, 2 Feb 2025 00:31:54 +0600 Subject: [PATCH 097/439] web/queue: fix filename on downloads, add mimetype, remove duplicates filename is no longer passed to workers for no reason --- web/src/components/queue/ProcessingQueueItem.svelte | 13 ++++++++----- web/src/lib/queen-bee/queue.ts | 4 ++-- web/src/lib/queen-bee/run-worker.ts | 7 ++----- web/src/lib/types/queue.ts | 1 + web/src/lib/types/workers.ts | 1 - web/src/lib/workers/remux.ts | 9 ++++----- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/web/src/components/queue/ProcessingQueueItem.svelte b/web/src/components/queue/ProcessingQueueItem.svelte index ac6aac65..30881930 100644 --- a/web/src/components/queue/ProcessingQueueItem.svelte +++ b/web/src/components/queue/ProcessingQueueItem.svelte @@ -34,13 +34,14 @@ const download = (file: File) => downloadFile({ - file, + file: new File([file], info.filename, { + type: info.mimeType, + }), });
-
@@ -56,7 +57,7 @@ {/each} @@ -69,7 +70,9 @@ {/if} {#if info.state === "running"} - {(info.completedWorkers?.length || 0) + 1}/{info.pipeline.length} + {#if info.pipeline.length > 1} + {(info.completedWorkers?.length || 0) + 1}/{info.pipeline.length} + {/if} {#if runningWorker && progress && progress.percentage} {$t(`queue.state.running.${runningWorker.type}`)}: {Math.ceil( progress.percentage @@ -210,7 +213,7 @@ } } - @media(hover: none) { + @media (hover: none) { .processing-info { overflow: hidden; flex: 1; diff --git a/web/src/lib/queen-bee/queue.ts b/web/src/lib/queen-bee/queue.ts index 141d800c..c1d0f8a4 100644 --- a/web/src/lib/queen-bee/queue.ts +++ b/web/src/lib/queen-bee/queue.ts @@ -35,7 +35,6 @@ export const createRemuxPipeline = (file: File) => { type: file.type, extension: file.name.split(".").pop(), }, - filename: file.name, }, }]; @@ -45,6 +44,7 @@ export const createRemuxPipeline = (file: File) => { state: "waiting", pipeline, filename: file.name, + mimeType: file.type, mediaType, }) } @@ -82,7 +82,6 @@ export const createSavePipeline = (info: CobaltLocalProcessingResponse) => { type: mime.getType(info.filename) || undefined, extension: info.filename.split(".").pop(), }, - filename: info.filename, }, }) @@ -91,6 +90,7 @@ export const createSavePipeline = (info: CobaltLocalProcessingResponse) => { state: "waiting", pipeline, filename: info.filename, + mimeType: mime.getType(info.filename) || undefined, mediaType: "video", }) } diff --git a/web/src/lib/queen-bee/run-worker.ts b/web/src/lib/queen-bee/run-worker.ts index 47746806..4ee5f0b4 100644 --- a/web/src/lib/queen-bee/run-worker.ts +++ b/web/src/lib/queen-bee/run-worker.ts @@ -21,8 +21,7 @@ export const runRemuxWorker = async ( parentId: string, files: CobaltFileReference[], args: string[], - output: FileInfo, - filename: string + output: FileInfo ) => { const worker = new RemuxWorker(); @@ -55,7 +54,6 @@ export const runRemuxWorker = async ( files, args, output, - filename, } }); @@ -165,14 +163,13 @@ export const startWorker = async ({ worker, workerId, parentId, workerArgs }: Co } } - if (files.length > 0 && workerArgs.ffargs && workerArgs.output && workerArgs.filename) { + if (files.length > 0 && workerArgs.ffargs && workerArgs.output) { await runRemuxWorker( workerId, parentId, files, workerArgs.ffargs, workerArgs.output, - workerArgs.filename ); } break; diff --git a/web/src/lib/types/queue.ts b/web/src/lib/types/queue.ts index c09e0d6b..07edeaaa 100644 --- a/web/src/lib/types/queue.ts +++ b/web/src/lib/types/queue.ts @@ -9,6 +9,7 @@ export type CobaltQueueBaseItem = { pipeline: CobaltPipelineItem[], // TODO: metadata filename: string, + mimeType?: string, mediaType: CobaltPipelineResultFileType, }; diff --git a/web/src/lib/types/workers.ts b/web/src/lib/types/workers.ts index e42d8ba7..fdb8d341 100644 --- a/web/src/lib/types/workers.ts +++ b/web/src/lib/types/workers.ts @@ -17,7 +17,6 @@ export type CobaltWorkerArgs = { url?: string, ffargs?: string[], output?: FileInfo, - filename?: string, } export type CobaltPipelineItem = { diff --git a/web/src/lib/workers/remux.ts b/web/src/lib/workers/remux.ts index 1f3f6aff..9f2dc60e 100644 --- a/web/src/lib/workers/remux.ts +++ b/web/src/lib/workers/remux.ts @@ -27,7 +27,7 @@ const ff = new LibAVWrapper((progress) => { ff.init(); -const remux = async (files: CobaltFileReference[], args: string[], output: FileInfo, filename: string) => { +const remux = async (files: CobaltFileReference[], args: string[], output: FileInfo) => { if (!(files && output && args)) return; await ff.init(); @@ -86,8 +86,7 @@ const remux = async (files: CobaltFileReference[], args: string[], output: FileI self.postMessage({ cobaltRemuxWorker: { - render, - filename + render } }); } catch (e) { @@ -99,8 +98,8 @@ const remux = async (files: CobaltFileReference[], args: string[], output: FileI self.onmessage = async (event: MessageEvent) => { const ed = event.data.cobaltRemuxWorker; if (ed) { - if (ed.files && ed.args && ed.output && ed.filename) { - await remux(ed.files, ed.args, ed.output, ed.filename); + if (ed.files && ed.args && ed.output) { + await remux(ed.files, ed.args, ed.output); } } } From 19a342457b64a013b2b154034565bb7aaafd3082 Mon Sep 17 00:00:00 2001 From: wukko Date: Sun, 2 Feb 2025 01:08:07 +0600 Subject: [PATCH 098/439] web/storage: catch the missing dir error --- web/src/lib/storage.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/web/src/lib/storage.ts b/web/src/lib/storage.ts index cae26e79..6cbb510c 100644 --- a/web/src/lib/storage.ts +++ b/web/src/lib/storage.ts @@ -54,7 +54,13 @@ export const removeFromFileStorage = async (filename: string) => { } export const clearFileStorage = async () => { - const root = await navigator.storage.getDirectory(); - return await root.removeEntry(cobaltProcessingDir, { recursive: true }); + if (navigator.storage.getDirectory) { + const root = await navigator.storage.getDirectory(); + try { + await root.removeEntry(cobaltProcessingDir, { recursive: true }); + } catch { + // ignore the error because the dir might be missing and that's okay! + } + } } From 945f87d93ba04a0e544595ec08f0b624b4dbb0d2 Mon Sep 17 00:00:00 2001 From: wukko Date: Sun, 2 Feb 2025 01:53:59 +0600 Subject: [PATCH 099/439] web/libav: allow passing options to init --- web/src/lib/libav.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/web/src/lib/libav.ts b/web/src/lib/libav.ts index 6beac0c5..fc158934 100644 --- a/web/src/lib/libav.ts +++ b/web/src/lib/libav.ts @@ -15,10 +15,14 @@ export default class LibAVWrapper { this.onProgress = onProgress; } - init() { + init(options?: LibAV.LibAVOpts) { + if (!options) options = { + yesthreads: true, + } + if (this.concurrency && !this.libav) { this.libav = LibAV.LibAV({ - yesthreads: true, + ...options, base: '/_libav' }); } From c8ecf41b1075b27b7802475ba53cb422549b7673 Mon Sep 17 00:00:00 2001 From: wukko Date: Sun, 2 Feb 2025 01:54:15 +0600 Subject: [PATCH 100/439] web/ProcessingQueueItem: fix stray space on error --- web/src/components/queue/ProcessingQueueItem.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/src/components/queue/ProcessingQueueItem.svelte b/web/src/components/queue/ProcessingQueueItem.svelte index 30881930..1b1381b1 100644 --- a/web/src/components/queue/ProcessingQueueItem.svelte +++ b/web/src/components/queue/ProcessingQueueItem.svelte @@ -69,6 +69,10 @@ {formatFileSize(info.resultFile?.file?.size)} {/if} + {#if info.state === "error"} + {info.errorCode} + {/if} + {#if info.state === "running"} {#if info.pipeline.length > 1} {(info.completedWorkers?.length || 0) + 1}/{info.pipeline.length} @@ -84,10 +88,6 @@ {/if} {/if} - {#if info.state === "error"} - {info.errorCode} - {/if} - {#if info.state === "waiting"} {$t("queue.state.waiting")} {/if} From 12ea601e6d0d6076713b334c54b5b3ff7d7c8d2e Mon Sep 17 00:00:00 2001 From: wukko Date: Sun, 2 Feb 2025 02:01:37 +0600 Subject: [PATCH 101/439] web/state/queue: clean up result file when removing the task --- web/src/lib/state/queen-bee/queue.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/web/src/lib/state/queen-bee/queue.ts b/web/src/lib/state/queen-bee/queue.ts index e6016297..3deabe04 100644 --- a/web/src/lib/state/queen-bee/queue.ts +++ b/web/src/lib/state/queen-bee/queue.ts @@ -14,6 +14,9 @@ const clearPipelineCache = (queueItem: CobaltQueueItem) => { } delete queueItem.pipelineResults; } + if (queueItem.state === "done") { + removeFromFileStorage(queueItem.resultFile.file.name); + } return queueItem; } From de5a2d10caca56a36e5891176fb2969768115802 Mon Sep 17 00:00:00 2001 From: wukko Date: Sun, 2 Feb 2025 02:08:50 +0600 Subject: [PATCH 102/439] web/SectionHeading: reduce line height for beta tag --- web/src/components/misc/SectionHeading.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/misc/SectionHeading.svelte b/web/src/components/misc/SectionHeading.svelte index 8741030f..bb90550e 100644 --- a/web/src/components/misc/SectionHeading.svelte +++ b/web/src/components/misc/SectionHeading.svelte @@ -93,7 +93,7 @@ color: var(--primary); font-size: 11px; font-weight: 500; - line-height: 1.9; + line-height: 1.86; text-transform: uppercase; } From 161b3a7e3c51d4655a11783c23c91366045e3cb4 Mon Sep 17 00:00:00 2001 From: wukko Date: Sun, 2 Feb 2025 02:28:31 +0600 Subject: [PATCH 103/439] web/i18n/queue: update title --- web/i18n/en/queue.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/i18n/en/queue.json b/web/i18n/en/queue.json index 4ce13ab7..b5db8c52 100644 --- a/web/i18n/en/queue.json +++ b/web/i18n/en/queue.json @@ -1,5 +1,5 @@ { - "title": "processing queue", + "title": "local processing", "stub": "nothing here yet, just the two of us.\ntry {{ value }} something!", "stub.download": "downloading", "stub.remux": "remuxing", From 6c18f1d46085fb143067df8ed8ae78f3f4dc6d5a Mon Sep 17 00:00:00 2001 From: wukko Date: Sun, 2 Feb 2025 14:45:31 +0600 Subject: [PATCH 104/439] web/ProcessingQueueItem: fix queue scroll --- web/src/components/queue/ProcessingQueueItem.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/components/queue/ProcessingQueueItem.svelte b/web/src/components/queue/ProcessingQueueItem.svelte index 1b1381b1..ac385613 100644 --- a/web/src/components/queue/ProcessingQueueItem.svelte +++ b/web/src/components/queue/ProcessingQueueItem.svelte @@ -117,7 +117,6 @@ justify-content: flex-start; align-items: center; position: relative; - overflow: hidden; } .processing-item { From 1716c1d2af2bda9ef0bccc9aeabe5f846265f999 Mon Sep 17 00:00:00 2001 From: wukko Date: Mon, 3 Feb 2025 18:08:47 +0600 Subject: [PATCH 105/439] web/state/queue: check if pipeline exists before removing workers --- web/src/lib/state/queen-bee/queue.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/web/src/lib/state/queen-bee/queue.ts b/web/src/lib/state/queen-bee/queue.ts index 3deabe04..ff5a2b64 100644 --- a/web/src/lib/state/queen-bee/queue.ts +++ b/web/src/lib/state/queen-bee/queue.ts @@ -102,10 +102,12 @@ export function itemRunning(id: string, workerId: string) { export function removeItem(id: string) { update(queueData => { - for (const worker in queueData[id].pipeline) { - removeWorkerFromQueue(queueData[id].pipeline[worker].workerId); + if (queueData[id].pipeline) { + for (const worker in queueData[id].pipeline) { + removeWorkerFromQueue(queueData[id].pipeline[worker].workerId); + } + clearPipelineCache(queueData[id]); } - clearPipelineCache(queueData[id]); delete queueData[id]; return queueData; From 88d4b4dc7ce93a58556ec8e2eaf144224754a486 Mon Sep 17 00:00:00 2001 From: wukko Date: Mon, 3 Feb 2025 18:09:03 +0600 Subject: [PATCH 106/439] web/ProgressBar: check if completedWorkers exists --- web/src/components/queue/ProgressBar.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/queue/ProgressBar.svelte b/web/src/components/queue/ProgressBar.svelte index f2f5d448..cbb1b877 100644 --- a/web/src/components/queue/ProgressBar.svelte +++ b/web/src/components/queue/ProgressBar.svelte @@ -13,7 +13,7 @@ class="progress" style="width: {Math.min(100, percentage || 0)}%" >
- {:else if completedWorkers.includes(workerId)} + {:else if completedWorkers?.includes(workerId)}
Date: Wed, 5 Feb 2025 17:07:29 +0600 Subject: [PATCH 107/439] web/SettingsInput: hide sensitive info (such as api key) --- web/src/components/settings/SettingsInput.svelte | 6 ++++++ web/src/routes/settings/instances/+page.svelte | 1 + 2 files changed, 7 insertions(+) diff --git a/web/src/components/settings/SettingsInput.svelte b/web/src/components/settings/SettingsInput.svelte index 4a3fc373..35197979 100644 --- a/web/src/components/settings/SettingsInput.svelte +++ b/web/src/components/settings/SettingsInput.svelte @@ -20,8 +20,12 @@ export let placeholder: string; export let altText: string; export let type: "url" | "uuid" = "url"; + + export let isPassword = false; export let showInstanceWarning = false; + let inputType = isPassword ? "password" : "text"; + const regex = { url: "https?:\\/\\/[a-z0-9.\\-]+(:\\d+)?/?", uuid: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", @@ -74,6 +78,8 @@ pattern={regex[type]} aria-label={altText} aria-hidden="false" + + { ...{ type: inputType } } /> {#if inputValue.length === 0} diff --git a/web/src/routes/settings/instances/+page.svelte b/web/src/routes/settings/instances/+page.svelte index d795bebd..fc252b96 100644 --- a/web/src/routes/settings/instances/+page.svelte +++ b/web/src/routes/settings/instances/+page.svelte @@ -50,6 +50,7 @@ placeholder="00000000-0000-0000-0000-000000000000" altText={$t("settings.processing.access_key.input.alt_text")} type="uuid" + isPassword /> {/if}
From c5d8d3387024503847dcffd46d96f24668cb4ba6 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 5 Feb 2025 18:30:00 +0600 Subject: [PATCH 108/439] web/SettingsInput: hide sensitive input & allow to show it with a button also fixed padding & svg rendering in safari --- web/i18n/en/button.json | 4 +- .../components/settings/SettingsInput.svelte | 70 ++++++++++++++++--- .../routes/settings/instances/+page.svelte | 2 +- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/web/i18n/en/button.json b/web/i18n/en/button.json index f066049e..e75ac3d1 100644 --- a/web/i18n/en/button.json +++ b/web/i18n/en/button.json @@ -17,5 +17,7 @@ "export": "export", "yes": "yes", "no": "no", - "clear": "clear" + "clear": "clear", + "show_input": "show input", + "hide_input": "hide input" } diff --git a/web/src/components/settings/SettingsInput.svelte b/web/src/components/settings/SettingsInput.svelte index 35197979..9bf90a47 100644 --- a/web/src/components/settings/SettingsInput.svelte +++ b/web/src/components/settings/SettingsInput.svelte @@ -15,17 +15,18 @@ import IconX from "@tabler/icons-svelte/IconX.svelte"; import IconCheck from "@tabler/icons-svelte/IconCheck.svelte"; + import IconEye from "@tabler/icons-svelte/IconEye.svelte"; + import IconEyeClosed from "@tabler/icons-svelte/IconEyeClosed.svelte"; + export let settingId: Id; export let settingContext: Context; export let placeholder: string; export let altText: string; export let type: "url" | "uuid" = "url"; - export let isPassword = false; + export let sensitive = false; export let showInstanceWarning = false; - let inputType = isPassword ? "password" : "text"; - const regex = { url: "https?:\\/\\/[a-z0-9.\\-]+(:\\d+)?/?", uuid: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", @@ -36,6 +37,10 @@ let inputFocused = false; let validInput = false; + let inputHidden = true; + + $: inputType = sensitive && inputHidden ? "password" : "text"; + const writeToSettings = (value: string, type: "url" | "uuid" | "text") => { updateSetting({ [settingContext]: { @@ -62,13 +67,20 @@
-
+
0} + aria-hidden="false" + > (validInput = input.checkValidity())} - on:input={() => (inputFocused = true)} + on:input={() => { + validInput = input.checkValidity(); + inputFocused = true; + }} on:focus={() => (inputFocused = true)} on:blur={() => (inputFocused = false)} spellcheck="false" @@ -78,8 +90,7 @@ pattern={regex[type]} aria-label={altText} aria-hidden="false" - - { ...{ type: inputType } } + {...{ type: inputType }} /> {#if inputValue.length === 0} @@ -87,6 +98,22 @@ {placeholder} {/if} + + {#if sensitive && inputValue.length > 0} + + {/if}
@@ -117,7 +144,7 @@ } #input-container { - padding: 0 18px; + padding: 0 16px; border-radius: var(--border-radius); color: var(--secondary); background-color: var(--button); @@ -129,6 +156,10 @@ overflow: hidden; } + #input-container.extra-button { + padding-right: 4px; + } + #input-container, #input-box { font-size: 13.5px; @@ -182,11 +213,30 @@ .settings-input-button :global(svg) { height: 21px; width: 21px; - stroke-width: 1.5px; + stroke-width: 1.8px; } .settings-input-button[disabled] { opacity: 0.5; pointer-events: none; } + + .input-inner-button { + height: 34px; + width: 34px; + padding: 0; + box-shadow: none; + /* 4px is padding outside of the button */ + border-radius: calc(var(--border-radius) - 4px); + } + + .input-inner-button :global(svg) { + height: 18px; + width: 18px; + stroke-width: 1.8px; + } + + :global(svg) { + will-change: transform; + } diff --git a/web/src/routes/settings/instances/+page.svelte b/web/src/routes/settings/instances/+page.svelte index fc252b96..cd1a2f1b 100644 --- a/web/src/routes/settings/instances/+page.svelte +++ b/web/src/routes/settings/instances/+page.svelte @@ -50,7 +50,7 @@ placeholder="00000000-0000-0000-0000-000000000000" altText={$t("settings.processing.access_key.input.alt_text")} type="uuid" - isPassword + sensitive /> {/if}
From 4f50b44e68d1ba190083fb253d731fd72a4cdf0f Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 5 Feb 2025 19:01:30 +0600 Subject: [PATCH 109/439] web/SettingsInput: make the clear button non-destructive clear button now clears data only in the input box, not actual data if you accidentally clear old data and don't save it, you can restore it with one click :3 --- web/i18n/en/button.json | 4 +- .../components/settings/SettingsInput.svelte | 89 ++++++++++++------- 2 files changed, 58 insertions(+), 35 deletions(-) diff --git a/web/i18n/en/button.json b/web/i18n/en/button.json index e75ac3d1..f387e7d4 100644 --- a/web/i18n/en/button.json +++ b/web/i18n/en/button.json @@ -19,5 +19,7 @@ "no": "no", "clear": "clear", "show_input": "show input", - "hide_input": "hide input" + "hide_input": "hide input", + "restore_input": "restore input", + "clear_input": "clear input" } diff --git a/web/src/components/settings/SettingsInput.svelte b/web/src/components/settings/SettingsInput.svelte index 9bf90a47..267d4cf1 100644 --- a/web/src/components/settings/SettingsInput.svelte +++ b/web/src/components/settings/SettingsInput.svelte @@ -14,10 +14,13 @@ import IconX from "@tabler/icons-svelte/IconX.svelte"; import IconCheck from "@tabler/icons-svelte/IconCheck.svelte"; + import IconArrowBack from "@tabler/icons-svelte/IconArrowBack.svelte"; import IconEye from "@tabler/icons-svelte/IconEye.svelte"; import IconEyeClosed from "@tabler/icons-svelte/IconEyeClosed.svelte"; + type SettingsInputType = "url" | "uuid"; + export let settingId: Id; export let settingContext: Context; export let placeholder: string; @@ -41,7 +44,11 @@ $: inputType = sensitive && inputHidden ? "password" : "text"; - const writeToSettings = (value: string, type: "url" | "uuid" | "text") => { + const checkInput = () => { + validInput = input.checkValidity() || inputValue === ""; + } + + const writeToSettings = (value: string, type: SettingsInputType) => { updateSetting({ [settingContext]: { [settingId]: @@ -55,8 +62,9 @@ if (showInstanceWarning) { await customInstanceWarning(); - if ($settings.processing.seenCustomWarning && inputValue) { - return writeToSettings(inputValue, type); + if ($settings.processing.seenCustomWarning) { + // allow writing empty strings + return writeToSettings(inputValue, inputValue ? type : "uuid"); } return; @@ -70,7 +78,6 @@
0} aria-hidden="false" > { - validInput = input.checkValidity(); inputFocused = true; + checkInput(); }} on:focus={() => (inputFocused = true)} on:blur={() => (inputFocused = false)} @@ -93,26 +100,52 @@ {...{ type: inputType }} /> + {#if inputValue.length > 0} + + + {#if sensitive} + + {/if} + {/if} + {#if inputValue.length === 0} - {/if} - {#if sensitive && inputValue.length > 0} - + {#if String($settings[settingContext][settingId]).length > 0} + + {/if} {/if}
@@ -120,20 +153,11 @@ - -
@@ -145,6 +169,7 @@ #input-container { padding: 0 16px; + padding-right: 4px; border-radius: var(--border-radius); color: var(--secondary); background-color: var(--button); @@ -156,10 +181,6 @@ overflow: hidden; } - #input-container.extra-button { - padding-right: 4px; - } - #input-container, #input-box { font-size: 13.5px; From c8ea19a69c055ec2b2734b082da8b74c582b0b75 Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 5 Feb 2025 19:09:37 +0600 Subject: [PATCH 110/439] web/SettingsInput: fix z-index of input inner buttons --- web/src/components/settings/SettingsInput.svelte | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/web/src/components/settings/SettingsInput.svelte b/web/src/components/settings/SettingsInput.svelte index 267d4cf1..f1287272 100644 --- a/web/src/components/settings/SettingsInput.svelte +++ b/web/src/components/settings/SettingsInput.svelte @@ -198,12 +198,6 @@ padding: 12px 0; } - #input-box::placeholder { - color: var(--gray); - /* fix for firefox */ - opacity: 1; - } - .input-placeholder { position: absolute; color: var(--gray); @@ -249,6 +243,7 @@ box-shadow: none; /* 4px is padding outside of the button */ border-radius: calc(var(--border-radius) - 4px); + z-index: 1; } .input-inner-button :global(svg) { From 23f28acff0364e0bf13e7212a6ebef6776ceb91c Mon Sep 17 00:00:00 2001 From: wukko Date: Wed, 5 Feb 2025 19:23:29 +0600 Subject: [PATCH 111/439] web/i18n/error: update age-restriction & login errors --- web/i18n/en/error.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/i18n/en/error.json b/web/i18n/en/error.json index 6b83ceb9..75481632 100644 --- a/web/i18n/en/error.json +++ b/web/i18n/en/error.json @@ -50,7 +50,7 @@ "api.content.video.unavailable": "i can't access this video. it may be restricted on {{ service }}'s side. try a different link!", "api.content.video.live": "this video is currently live, so i can't download it yet. wait for the live stream to finish and try again!", "api.content.video.private": "this video is private, so i can't access it. change its visibility or try another one!", - "api.content.video.age": "this video is age-restricted, so i can't access it anonymously. try a different link!", + "api.content.video.age": "this video is age-restricted, so i can't access it anonymously at the moment. try again or try a different link!", "api.content.video.region": "this video is region locked, and the processing instance is in a different location. try a different link!", "api.content.region": "this content is region locked, and the processing instance is in a different location. try a different link!", @@ -58,11 +58,11 @@ "api.content.post.unavailable": "couldn't find anything about this post. its visibility may be limited or it may not exist. make sure your link works and try again in a few seconds!", "api.content.post.private": "couldn't get anything about this post because it's from a private account. try a different link!", - "api.content.post.age": "this post is age-restricted and isn't available without logging in. try a different link!", + "api.content.post.age": "this post is age-restricted, so i can't access it anonymously at the moment. try again or try a different link!", "api.youtube.no_matching_format": "youtube didn't return a valid video + audio format combo, either video or audio is missing. formats for this video may be re-encoding on youtube's side or something went wrong when parsing them. try enabling the hls option in video settings!", "api.youtube.decipher": "youtube updated its decipher algorithm and i couldn't extract the info about the video. try again in a few seconds, but if this issue sticks, please report it!", - "api.youtube.login": "couldn't get this video because youtube asked the instance to log in. this is potentially caused by the processing instance not having any active account tokens or youtube updating something about their api. try again in a few seconds, but if it still doesn't work, please report this issue!", + "api.youtube.login": "couldn't get this video because youtube told the processing instance that it's a bot and restricted its access. try again in a few seconds, but if it still doesn't work, please report this issue!", "api.youtube.token_expired": "couldn't get this video because the youtube token expired and i couldn't refresh it. try again in a few seconds, but if it still doesn't work, tell the instance owner about this error!", "api.youtube.no_hls_streams": "couldn't find any matching HLS streams for this video. try downloading it without HLS!", "api.youtube.api_error": "youtube updated something about its api and i couldn't get any info about this video. try again in a few seconds, but if this issue sticks, please report it!", From 0ce777cbfc200387972755a3501f69e243c757cd Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 6 Feb 2025 14:29:42 +0600 Subject: [PATCH 112/439] api/internal-hls: transform segment uri when probing the HLS tunnel --- api/src/stream/internal-hls.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/api/src/stream/internal-hls.js b/api/src/stream/internal-hls.js index e4416288..b56c93ff 100644 --- a/api/src/stream/internal-hls.js +++ b/api/src/stream/internal-hls.js @@ -121,7 +121,15 @@ export async function probeInternalHLSTunnel(streamInfo) { if (!randomSegment.uri) throw "segment is missing URI"; - const segmentSize = await getSegmentSize(randomSegment.uri, config) / randomSegment.duration; + let segmentUrl; + + if (getURL(randomSegment.uri)) { + segmentUrl = new URL(randomSegment.uri); + } else { + segmentUrl = new URL(randomSegment.uri, streamInfo.url); + } + + const segmentSize = await getSegmentSize(segmentUrl, config) / randomSegment.duration; return segmentSize; }) ); From 7a042e3bfa19a6b7d80d3e22f748a184551773d9 Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 6 Feb 2025 22:28:08 +0600 Subject: [PATCH 113/439] web/ProcessingQueue: clear old files from storage on page load --- web/src/components/queue/ProcessingQueue.svelte | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/src/components/queue/ProcessingQueue.svelte b/web/src/components/queue/ProcessingQueue.svelte index 0d00a5d0..f8ce8b7b 100644 --- a/web/src/components/queue/ProcessingQueue.svelte +++ b/web/src/components/queue/ProcessingQueue.svelte @@ -1,8 +1,9 @@
From cff47da74240f350801e1b4215c81dc8b01fa1a6 Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 6 Feb 2025 22:56:05 +0600 Subject: [PATCH 114/439] web/ProcessingQueue: add estimated storage usage --- web/i18n/en/queue.json | 3 +- .../components/queue/ProcessingQueue.svelte | 60 ++++++++++++++----- web/src/lib/storage.ts | 7 +++ 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/web/i18n/en/queue.json b/web/i18n/en/queue.json index b5db8c52..b18477ba 100644 --- a/web/i18n/en/queue.json +++ b/web/i18n/en/queue.json @@ -7,5 +7,6 @@ "state.waiting": "queued", "state.starting": "starting...", "state.running.remux": "remuxing", - "state.running.fetch": "downloading" + "state.running.fetch": "downloading", + "estimated_storage_usage": "estimated storage usage:" } diff --git a/web/src/components/queue/ProcessingQueue.svelte b/web/src/components/queue/ProcessingQueue.svelte index f8ce8b7b..ca76b7ac 100644 --- a/web/src/components/queue/ProcessingQueue.svelte +++ b/web/src/components/queue/ProcessingQueue.svelte @@ -3,7 +3,8 @@ import { onNavigate } from "$app/navigation"; import { onMount, type SvelteComponent } from "svelte"; - import { clearFileStorage } from "$lib/storage"; + import { formatFileSize } from "$lib/util"; + import { clearFileStorage, getStorageQuota } from "$lib/storage"; import { currentTasks } from "$lib/state/queen-bee/current-tasks"; import { clearQueue, queue as readableQueue } from "$lib/state/queen-bee/queue"; @@ -20,6 +21,13 @@ $: queue = Object.entries($readableQueue); + let quotaUsage = 0; + + const updateQuota = async () => { + const storageInfo = await getStorageQuota(); + quotaUsage = storageInfo?.usage || 0; + } + const totalItemProgress = (completed: number, current: number, total: number) => { return (completed * 100 + current) / total } @@ -41,6 +49,7 @@ const popoverAction = async () => { expanded = !expanded; + if (expanded) updateQuota(); }; onNavigate(() => { @@ -68,20 +77,31 @@ expandStart="right" >
- -
- {#if queue.length} - - {/if} +
+ +
+ {#if queue.length} + + {/if} +
+ + {#if quotaUsage} +
+ {$t("queue.estimated_storage_usage")} {formatFileSize(quotaUsage)} +
+ {/if}
{#each queue as [id, item]} @@ -127,6 +147,13 @@ } #processing-header { + display: flex; + flex-direction: column; + flex-wrap: wrap; + gap: 4px; + } + + .header-top { display: flex; flex-direction: row; justify-content: space-between; @@ -135,6 +162,11 @@ gap: 6px; } + .storage-info { + font-size: 12px; + color: var(--gray); + } + .header-buttons { display: flex; flex-direction: row; diff --git a/web/src/lib/storage.ts b/web/src/lib/storage.ts index 6cbb510c..3d9a04a3 100644 --- a/web/src/lib/storage.ts +++ b/web/src/lib/storage.ts @@ -64,3 +64,10 @@ export const clearFileStorage = async () => { } } +export const getStorageQuota = async () => { + let estimate; + if (navigator.storage.estimate) { + estimate = await navigator.storage.estimate(); + } + return estimate; +} From 1f79bf6e5205f817e9930a1250b5c989e5b17eef Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 6 Feb 2025 23:44:05 +0600 Subject: [PATCH 115/439] web/settings/advanced: add cache clearing, refactor data management --- web/i18n/en/button.json | 3 +- web/i18n/en/dialog.json | 9 ++-- web/i18n/en/settings.json | 7 ++-- .../settings/ClearStorageButton.svelte | 42 +++++++++++++++++++ .../settings/DataSettingsButton.svelte | 32 ++++++++++++++ .../components/settings/ManageSettings.svelte | 10 ++--- .../settings/ResetSettingsButton.svelte | 32 ++++---------- web/src/routes/settings/advanced/+page.svelte | 8 +++- 8 files changed, 105 insertions(+), 38 deletions(-) create mode 100644 web/src/components/settings/ClearStorageButton.svelte create mode 100644 web/src/components/settings/DataSettingsButton.svelte diff --git a/web/i18n/en/button.json b/web/i18n/en/button.json index f387e7d4..4c29c342 100644 --- a/web/i18n/en/button.json +++ b/web/i18n/en/button.json @@ -21,5 +21,6 @@ "show_input": "show input", "hide_input": "hide input", "restore_input": "restore input", - "clear_input": "clear input" + "clear_input": "clear input", + "clear_cache": "clear cache" } diff --git a/web/i18n/en/dialog.json b/web/i18n/en/dialog.json index 3e6f5dec..35a15a8c 100644 --- a/web/i18n/en/dialog.json +++ b/web/i18n/en/dialog.json @@ -1,6 +1,6 @@ { - "reset.title": "reset all data?", - "reset.body": "are you sure you want to reset all data? this action is immediate and irreversible.", + "reset_settings.title": "reset all settings?", + "reset_settings.body": "are you sure you want to reset all settings? this action is immediate and irreversible.", "picker.title": "select what to save", "picker.description.desktop": "click an item to save it. images can also be saved via the right click menu.", @@ -21,5 +21,8 @@ "safety.custom_instance.body": "custom instances can potentially pose privacy & safety risks.\n\nbad instances can:\n1. redirect you away from cobalt and try to scam you.\n2. log all information about your requests, store it forever, and use it to track you.\n3. serve you malicious files (such as malware).\n4. force you to watch ads, or make you pay for downloading.\n\nafter this point, we can't protect you. please be mindful of what instances to use and always trust your gut. if anything feels off, come back to this page, reset the custom instance, and report it to us on github.", "processing.ongoing": "cobalt is currently processing media in this tab. going away will abort it. are you sure you want to do this?", - "processing.title.ongoing": "processing will be cancelled" + "processing.title.ongoing": "processing will be cancelled", + + "clear_cache.title": "clear all cache?", + "clear_cache.body": "all files from the processing queue will be removed and on-device features will take longer to load. this action is immediate and irreversible." } diff --git a/web/i18n/en/settings.json b/web/i18n/en/settings.json index f4b28b34..ca1374d1 100644 --- a/web/i18n/en/settings.json +++ b/web/i18n/en/settings.json @@ -111,8 +111,6 @@ "advanced.debug.title": "enable features for nerds", "advanced.debug.description": "gives you easy access to app info that can be useful for debugging. enabling this does not affect functionality of cobalt in any way.", - "advanced.data": "data management", - "advanced.local-processing": "local processing", "advanced.local-processing.title": "mux and convert media on device", "advanced.local-processing.description": "when downloading media, cobalt will use on-device processing to mux or convert it. exclusive local features such as remux or convert are not affected, they always run locally.", @@ -126,5 +124,8 @@ "processing.access_key.description": "cobalt will use this key to make requests to the processing instance instead of other authentication methods. make sure the instance supports api keys!", "processing.custom_instance.input.alt_text": "custom instance domain", - "processing.access_key.input.alt_text": "u-u-i-d access key" + "processing.access_key.input.alt_text": "u-u-i-d access key", + + "advanced.settings_data": "settings data", + "advanced.local_storage": "local storage" } diff --git a/web/src/components/settings/ClearStorageButton.svelte b/web/src/components/settings/ClearStorageButton.svelte new file mode 100644 index 00000000..04e6d852 --- /dev/null +++ b/web/src/components/settings/ClearStorageButton.svelte @@ -0,0 +1,42 @@ + + + + + {$t("button.clear_cache")} + diff --git a/web/src/components/settings/DataSettingsButton.svelte b/web/src/components/settings/DataSettingsButton.svelte new file mode 100644 index 00000000..653a72a7 --- /dev/null +++ b/web/src/components/settings/DataSettingsButton.svelte @@ -0,0 +1,32 @@ + + + + + diff --git a/web/src/components/settings/ManageSettings.svelte b/web/src/components/settings/ManageSettings.svelte index abc053bf..273f0f32 100644 --- a/web/src/components/settings/ManageSettings.svelte +++ b/web/src/components/settings/ManageSettings.svelte @@ -5,7 +5,7 @@ import { validateSettings } from "$lib/settings/validate"; import { storedSettings, updateSetting, loadFromString } from "$lib/state/settings"; - import ActionButton from "$components/buttons/ActionButton.svelte"; + import DataSettingsButton from "$components/settings/DataSettingsButton.svelte"; import ResetSettingsButton from "$components/settings/ResetSettingsButton.svelte"; import IconFileExport from "@tabler/icons-svelte/IconFileExport.svelte"; @@ -95,16 +95,16 @@
- + {$t("button.import")} - + {#if $storedSettings.schemaVersion} - + {$t("button.export")} - + {/if} {#if $storedSettings.schemaVersion} diff --git a/web/src/components/settings/ResetSettingsButton.svelte b/web/src/components/settings/ResetSettingsButton.svelte index acc1465d..f05b0684 100644 --- a/web/src/components/settings/ResetSettingsButton.svelte +++ b/web/src/components/settings/ResetSettingsButton.svelte @@ -3,15 +3,16 @@ import { createDialog } from "$lib/state/dialogs"; import { resetSettings } from "$lib/state/settings"; - import IconTrash from "@tabler/icons-svelte/IconTrash.svelte"; + import IconRestore from "@tabler/icons-svelte/IconRestore.svelte"; + import DataSettingsButton from "$components/settings/DataSettingsButton.svelte"; const resetDialog = () => { createDialog({ id: "wipe-confirm", type: "small", icon: "warn-red", - title: $t("dialog.reset.title"), - bodyText: $t("dialog.reset.body"), + title: $t("dialog.reset_settings.title"), + bodyText: $t("dialog.reset_settings.body"), buttons: [ { text: $t("button.cancel"), @@ -30,26 +31,7 @@ }; - - - + diff --git a/web/src/routes/settings/advanced/+page.svelte b/web/src/routes/settings/advanced/+page.svelte index f66ada39..7c533ecc 100644 --- a/web/src/routes/settings/advanced/+page.svelte +++ b/web/src/routes/settings/advanced/+page.svelte @@ -1,9 +1,11 @@ @@ -15,6 +17,10 @@ /> - + + + + + From dc33c07b398c318841e62d2f4f7a2bf6eb3b4ecd Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 6 Feb 2025 23:45:03 +0600 Subject: [PATCH 116/439] web/storage: add clearCacheStorage function --- web/src/lib/storage.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/web/src/lib/storage.ts b/web/src/lib/storage.ts index 3d9a04a3..30a6ffea 100644 --- a/web/src/lib/storage.ts +++ b/web/src/lib/storage.ts @@ -64,6 +64,14 @@ export const clearFileStorage = async () => { } } +export const clearCacheStorage = async () => { + const keys = await caches.keys(); + + for (const key of keys) { + caches.delete(key); + } +} + export const getStorageQuota = async () => { let estimate; if (navigator.storage.estimate) { From 95d9913e3ea906223bdeff56c3080066cad43c4c Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 7 Feb 2025 16:47:36 +0600 Subject: [PATCH 117/439] web/Sidebar: always show cutout tab --- web/src/components/sidebar/Sidebar.svelte | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/web/src/components/sidebar/Sidebar.svelte b/web/src/components/sidebar/Sidebar.svelte index 05738c9f..9ac9832f 100644 --- a/web/src/components/sidebar/Sidebar.svelte +++ b/web/src/components/sidebar/Sidebar.svelte @@ -1,7 +1,4 @@ From adaf502d665d8825964af94f9fc5035a6f7fd3b8 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 7 Feb 2025 16:55:28 +0600 Subject: [PATCH 119/439] web: remove the early prototype of cutout functionality at the time of this commit, there are no models that are good enough and can run in a web browser. this feature might come back when web onnx gets support for beefier models. --- pnpm-lock.yaml | 452 ------------------- web/i18n/en/tabs.json | 3 +- web/package.json | 1 - web/src/components/sidebar/Sidebar.svelte | 3 - web/src/components/sidebar/SidebarTab.svelte | 2 +- web/src/lib/workers/removebg.ts | 70 --- web/src/routes/cutout/+page.svelte | 194 -------- web/src/routes/cutout/+page.ts | 1 - web/vite.config.ts | 3 - 9 files changed, 2 insertions(+), 727 deletions(-) delete mode 100644 web/src/lib/workers/removebg.ts delete mode 100644 web/src/routes/cutout/+page.svelte delete mode 100644 web/src/routes/cutout/+page.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa25fcaa..15707176 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -100,9 +100,6 @@ importers: '@fontsource/redaction-10': specifier: ^5.0.2 version: 5.0.2 - '@huggingface/transformers': - specifier: ^3.2.4 - version: 3.2.4 '@imput/libav.js-remux-cli': specifier: ^6.5.7 version: 6.5.7 @@ -204,9 +201,6 @@ packages: resolution: {integrity: sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==} engines: {node: '>=6.0.0'} - '@emnapi/runtime@1.3.1': - resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} - '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} @@ -540,13 +534,6 @@ packages: '@fontsource/redaction-10@5.0.2': resolution: {integrity: sha512-PODxYvb06YrNxdUBGcygiMibpgcZihzmvkmlX/TQAA2F7BUU/anfSKQi/VnLdJ/8LIK81/bUY+i7L/GP27FkVw==} - '@huggingface/jinja@0.3.2': - resolution: {integrity: sha512-F2FvuIc+w1blGsaqJI/OErRbWH6bVJDCBI8Rm5D86yZ2wlwrGERsfIaru7XUv9eYC3DMP3ixDRRtF0h6d8AZcQ==} - engines: {node: '>=18'} - - '@huggingface/transformers@3.2.4': - resolution: {integrity: sha512-XLXoC2lj72SXCftDh1ptmYwCrjDtky+WT7W51jZERU4jbKzEXz0qHC+vCZwGX+Q7nTFogdwIGU2SuaCG9XKBLA==} - '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -567,111 +554,6 @@ packages: resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} engines: {node: '>=18.18'} - '@img/sharp-darwin-arm64@0.33.5': - resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.33.5': - resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.0.4': - resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.0.4': - resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.0.4': - resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linux-arm@1.0.5': - resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} - cpu: [arm] - os: [linux] - - '@img/sharp-libvips-linux-s390x@1.0.4': - resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} - cpu: [s390x] - os: [linux] - - '@img/sharp-libvips-linux-x64@1.0.4': - resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} - cpu: [x64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': - resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-x64@1.0.4': - resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} - cpu: [x64] - os: [linux] - - '@img/sharp-linux-arm64@0.33.5': - resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linux-arm@0.33.5': - resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - - '@img/sharp-linux-s390x@0.33.5': - resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - - '@img/sharp-linux-x64@0.33.5': - resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-linuxmusl-arm64@0.33.5': - resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linuxmusl-x64@0.33.5': - resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-wasm32@0.33.5': - resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - - '@img/sharp-win32-ia32@0.33.5': - resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.33.5': - resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] - '@imput/libav.js-remux-cli@6.5.7': resolution: {integrity: sha512-41ld+R5aEwllKdbiszOoCf+K614/8bnuheTZnqjgZESwDtX07xu9O8GO0m/Cpsm7O2CyqPZa1qzDx6BZVO15DQ==} @@ -682,10 +564,6 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@isaacs/fs-minipass@4.0.1': - resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} - engines: {node: '>=18.0.0'} - '@jridgewell/gen-mapping@0.3.5': resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} @@ -739,36 +617,6 @@ packages: '@polka/url@1.0.0-next.25': resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} - '@protobufjs/aspromise@1.1.2': - resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} - - '@protobufjs/base64@1.1.2': - resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} - - '@protobufjs/codegen@2.0.4': - resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} - - '@protobufjs/eventemitter@1.1.0': - resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} - - '@protobufjs/fetch@1.1.0': - resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} - - '@protobufjs/float@1.0.2': - resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} - - '@protobufjs/inquire@1.1.0': - resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} - - '@protobufjs/path@1.1.2': - resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} - - '@protobufjs/pool@1.1.0': - resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} - - '@protobufjs/utf8@1.1.0': - resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - '@redis/bloom@1.2.0': resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} peerDependencies: @@ -1135,10 +983,6 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chownr@3.0.0: - resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} - engines: {node: '>=18'} - cluster-key-slot@1.1.2: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} engines: {node: '>=0.10.0'} @@ -1153,13 +997,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - - color@4.2.3: - resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} - engines: {node: '>=12.5.0'} - commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -1260,10 +1097,6 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} - detect-libc@2.0.3: - resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} - engines: {node: '>=8'} - devalue@5.1.1: resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} @@ -1442,9 +1275,6 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatbuffers@1.12.0: - resolution: {integrity: sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==} - flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} @@ -1526,9 +1356,6 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - guid-typescript@1.0.9: - resolution: {integrity: sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1604,9 +1431,6 @@ packages: resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} engines: {node: '>= 10'} - is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1703,9 +1527,6 @@ packages: lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - long@5.2.4: - resolution: {integrity: sha512-qtzLbJE8hq7VabR3mISmVGtoXP8KGc2Z/AT8OuqlYD7JTR3oqrgwdjnk07wpj1twXxYmgDXgoKVWUG/fReSzHg==} - lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -1790,19 +1611,10 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - minizlib@3.0.1: - resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==} - engines: {node: '>= 18'} - mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true - mkdirp@3.0.1: - resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} - engines: {node: '>=10'} - hasBin: true - mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -1867,19 +1679,6 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} - onnxruntime-common@1.20.1: - resolution: {integrity: sha512-YiU0s0IzYYC+gWvqD1HzLc46Du1sXpSiwzKb63PACIJr6LfL27VsXSXQvt68EzD3V0D5Bc0vyJTjmMxp0ylQiw==} - - onnxruntime-common@1.21.0-dev.20241205-6ed77cc374: - resolution: {integrity: sha512-U4DGq/dZiboIEK0Zv1KUuWJesJ/txUALpWSXwI8kqOCSxe8GrI65xfRFeMbqYFhPVGAWZPsBpT1zo1s4ksrlrg==} - - onnxruntime-node@1.20.1: - resolution: {integrity: sha512-di/I4HDXRw+FLgq+TyHmQEDd3cEp9iFFZm0r4uJ1Wd7b/WE1VXtKWo8yemex347c6GNF/3Pv86ZfPhIWxORr0w==} - os: [win32, darwin, linux] - - onnxruntime-web@1.21.0-dev.20241205-d27fecd3d3: - resolution: {integrity: sha512-neeC9mv1sFWjUFrTaDl7enufNxbtSSTwR5V2i35ga4yXWS6r1MbpUwWwD1X+VKANujbSG8M5pk/ohRAOm2QhMQ==} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -1950,9 +1749,6 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} - platform@1.3.6: - resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} - postcss-load-config@6.0.1: resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} engines: {node: '>= 18'} @@ -1995,10 +1791,6 @@ packages: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} - protobufjs@7.4.0: - resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} - engines: {node: '>=12.0.0'} - proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -2056,10 +1848,6 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rimraf@5.0.10: - resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} - hasBin: true - rollup@4.24.0: resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -2104,10 +1892,6 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sharp@0.33.5: - resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2127,9 +1911,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - sirv@3.0.0: resolution: {integrity: sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==} engines: {node: '>=18'} @@ -2300,10 +2081,6 @@ packages: syscall-napi@0.0.6: resolution: {integrity: sha512-qHbwjyFXAAekKUXxl70lhDiBYJ3e7XM7kQwu7LV3F0pHMenKox+VcZPZkRkhdmL/wNJD3NmrMGnL7161kdecUQ==} - tar@7.4.3: - resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} - engines: {node: '>=18'} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -2505,10 +2282,6 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yallist@5.0.0: - resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} - engines: {node: '>=18'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2541,11 +2314,6 @@ snapshots: http-response-object: 3.0.2 parse-cache-control: 1.0.1 - '@emnapi/runtime@1.3.1': - dependencies: - tslib: 2.6.3 - optional: true - '@esbuild/aix-ppc64@0.21.5': optional: true @@ -2738,15 +2506,6 @@ snapshots: '@fontsource/redaction-10@5.0.2': {} - '@huggingface/jinja@0.3.2': {} - - '@huggingface/transformers@3.2.4': - dependencies: - '@huggingface/jinja': 0.3.2 - onnxruntime-node: 1.20.1 - onnxruntime-web: 1.21.0-dev.20241205-d27fecd3d3 - sharp: 0.33.5 - '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -2760,81 +2519,6 @@ snapshots: '@humanwhocodes/retry@0.4.1': {} - '@img/sharp-darwin-arm64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.0.4 - optional: true - - '@img/sharp-darwin-x64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.0.4 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.0.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.0.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.0.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.0.5': - optional: true - - '@img/sharp-libvips-linux-s390x@1.0.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.0.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.0.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.0.4': - optional: true - - '@img/sharp-linux-arm64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.0.4 - optional: true - - '@img/sharp-linux-arm@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.0.5 - optional: true - - '@img/sharp-linux-s390x@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.0.4 - optional: true - - '@img/sharp-linux-x64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.0.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.33.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 - optional: true - - '@img/sharp-wasm32@0.33.5': - dependencies: - '@emnapi/runtime': 1.3.1 - optional: true - - '@img/sharp-win32-ia32@0.33.5': - optional: true - - '@img/sharp-win32-x64@0.33.5': - optional: true - '@imput/libav.js-remux-cli@6.5.7': {} '@imput/psl@2.0.4': @@ -2850,10 +2534,6 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@isaacs/fs-minipass@4.0.1': - dependencies: - minipass: 7.1.2 - '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 @@ -2905,29 +2585,6 @@ snapshots: '@polka/url@1.0.0-next.25': {} - '@protobufjs/aspromise@1.1.2': {} - - '@protobufjs/base64@1.1.2': {} - - '@protobufjs/codegen@2.0.4': {} - - '@protobufjs/eventemitter@1.1.0': {} - - '@protobufjs/fetch@1.1.0': - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/inquire': 1.1.0 - - '@protobufjs/float@1.0.2': {} - - '@protobufjs/inquire@1.1.0': {} - - '@protobufjs/path@1.1.2': {} - - '@protobufjs/pool@1.1.0': {} - - '@protobufjs/utf8@1.1.0': {} - '@redis/bloom@1.2.0(@redis/client@1.6.0)': dependencies: '@redis/client': 1.6.0 @@ -3311,8 +2968,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chownr@3.0.0: {} - cluster-key-slot@1.1.2: optional: true @@ -3330,16 +2985,6 @@ snapshots: color-name@1.1.4: {} - color-string@1.9.1: - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - - color@4.2.3: - dependencies: - color-convert: 2.0.1 - color-string: 1.9.1 - commander@4.1.1: {} compare-versions@6.1.1: {} @@ -3417,8 +3062,6 @@ snapshots: detect-indent@6.1.0: {} - detect-libc@2.0.3: {} - devalue@5.1.1: {} dotenv@16.4.5: {} @@ -3691,8 +3334,6 @@ snapshots: flatted: 3.3.1 keyv: 4.5.4 - flatbuffers@1.12.0: {} - flatted@3.3.1: {} foreground-child@3.3.0: @@ -3780,8 +3421,6 @@ snapshots: graphemer@1.4.0: {} - guid-typescript@1.0.9: {} - has-flag@4.0.0: {} has-property-descriptors@1.0.2: @@ -3848,8 +3487,6 @@ snapshots: ipaddr.js@2.2.0: {} - is-arrayish@0.3.2: {} - is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -3930,8 +3567,6 @@ snapshots: lodash.sortby@4.7.0: {} - long@5.2.4: {} - lru-cache@10.4.3: {} lru-cache@11.0.2: {} @@ -3995,17 +3630,10 @@ snapshots: minipass@7.1.2: {} - minizlib@3.0.1: - dependencies: - minipass: 7.1.2 - rimraf: 5.0.10 - mkdirp@0.5.6: dependencies: minimist: 1.2.8 - mkdirp@3.0.1: {} - mri@1.2.0: {} mrmime@2.0.0: {} @@ -4052,24 +3680,6 @@ snapshots: dependencies: mimic-fn: 2.1.0 - onnxruntime-common@1.20.1: {} - - onnxruntime-common@1.21.0-dev.20241205-6ed77cc374: {} - - onnxruntime-node@1.20.1: - dependencies: - onnxruntime-common: 1.20.1 - tar: 7.4.3 - - onnxruntime-web@1.21.0-dev.20241205-d27fecd3d3: - dependencies: - flatbuffers: 1.12.0 - guid-typescript: 1.0.9 - long: 5.2.4 - onnxruntime-common: 1.21.0-dev.20241205-6ed77cc374 - platform: 1.3.6 - protobufjs: 7.4.0 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -4131,8 +3741,6 @@ snapshots: pirates@4.0.6: {} - platform@1.3.6: {} - postcss-load-config@6.0.1(postcss@8.4.47): dependencies: lilconfig: 3.1.2 @@ -4155,21 +3763,6 @@ snapshots: progress@2.0.3: {} - protobufjs@7.4.0: - dependencies: - '@protobufjs/aspromise': 1.1.2 - '@protobufjs/base64': 1.1.2 - '@protobufjs/codegen': 2.0.4 - '@protobufjs/eventemitter': 1.1.0 - '@protobufjs/fetch': 1.1.0 - '@protobufjs/float': 1.0.2 - '@protobufjs/inquire': 1.1.0 - '@protobufjs/path': 1.1.2 - '@protobufjs/pool': 1.1.0 - '@protobufjs/utf8': 1.1.0 - '@types/node': 20.14.14 - long: 5.2.4 - proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -4227,10 +3820,6 @@ snapshots: dependencies: glob: 7.2.3 - rimraf@5.0.10: - dependencies: - glob: 10.4.5 - rollup@4.24.0: dependencies: '@types/estree': 1.0.6 @@ -4314,32 +3903,6 @@ snapshots: setprototypeof@1.2.0: {} - sharp@0.33.5: - dependencies: - color: 4.2.3 - detect-libc: 2.0.3 - semver: 7.6.3 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.33.5 - '@img/sharp-darwin-x64': 0.33.5 - '@img/sharp-libvips-darwin-arm64': 1.0.4 - '@img/sharp-libvips-darwin-x64': 1.0.4 - '@img/sharp-libvips-linux-arm': 1.0.5 - '@img/sharp-libvips-linux-arm64': 1.0.4 - '@img/sharp-libvips-linux-s390x': 1.0.4 - '@img/sharp-libvips-linux-x64': 1.0.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 - '@img/sharp-libvips-linuxmusl-x64': 1.0.4 - '@img/sharp-linux-arm': 0.33.5 - '@img/sharp-linux-arm64': 0.33.5 - '@img/sharp-linux-s390x': 0.33.5 - '@img/sharp-linux-x64': 0.33.5 - '@img/sharp-linuxmusl-arm64': 0.33.5 - '@img/sharp-linuxmusl-x64': 0.33.5 - '@img/sharp-wasm32': 0.33.5 - '@img/sharp-win32-ia32': 0.33.5 - '@img/sharp-win32-x64': 0.33.5 - shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -4357,10 +3920,6 @@ snapshots: signal-exit@4.1.0: {} - simple-swizzle@0.2.2: - dependencies: - is-arrayish: 0.3.2 - sirv@3.0.0: dependencies: '@polka/url': 1.0.0-next.25 @@ -4507,15 +4066,6 @@ snapshots: syscall-napi@0.0.6: optional: true - tar@7.4.3: - dependencies: - '@isaacs/fs-minipass': 4.0.1 - chownr: 3.0.0 - minipass: 7.1.2 - minizlib: 3.0.1 - mkdirp: 3.0.1 - yallist: 5.0.0 - thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -4690,8 +4240,6 @@ snapshots: yallist@4.0.0: optional: true - yallist@5.0.0: {} - yocto-queue@0.1.0: {} youtubei.js@13.0.0: diff --git a/web/i18n/en/tabs.json b/web/i18n/en/tabs.json index 21408038..3cab9cc0 100644 --- a/web/i18n/en/tabs.json +++ b/web/i18n/en/tabs.json @@ -4,6 +4,5 @@ "updates": "updates", "donate": "donate", "about": "about", - "remux": "remux", - "cutout": "cut out" + "remux": "remux" } diff --git a/web/package.json b/web/package.json index 6018ff45..9f51e006 100644 --- a/web/package.json +++ b/web/package.json @@ -28,7 +28,6 @@ "@fontsource-variable/noto-sans-mono": "^5.0.20", "@fontsource/ibm-plex-mono": "^5.0.13", "@fontsource/redaction-10": "^5.0.2", - "@huggingface/transformers": "^3.2.4", "@imput/libav.js-remux-cli": "^6.5.7", "@imput/version-info": "workspace:^", "@sveltejs/adapter-static": "^3.0.6", diff --git a/web/src/components/sidebar/Sidebar.svelte b/web/src/components/sidebar/Sidebar.svelte index 9ac9832f..e799b015 100644 --- a/web/src/components/sidebar/Sidebar.svelte +++ b/web/src/components/sidebar/Sidebar.svelte @@ -14,8 +14,6 @@ import IconHeart from "@tabler/icons-svelte/IconHeart.svelte"; import IconInfoCircle from "@tabler/icons-svelte/IconInfoCircle.svelte"; - import IconCut from "@tabler/icons-svelte/IconCut.svelte"; - let screenWidth: number; let settingsLink = defaultNavPage("settings"); let aboutLink = defaultNavPage("about"); @@ -33,7 +31,6 @@
@@ -170,10 +246,9 @@ line-break: anywhere; display: flex; align-items: center; - gap: 6px; } - .file-status.error { + .file-status.error:not(.retrying) { color: var(--medium-red); } @@ -183,6 +258,29 @@ stroke-width: 2px; } + .status-icon, + .status-spinner, + .status-text { + display: flex; + } + + /* + margin is used instead of gap cuz queued state doesn't have an icon. + margin is applied only to the visible icon, so there's no awkward gap. + */ + .status-icon :global(svg) { + margin-right: 6px; + } + + :global([dir="rtl"]) .status-icon :global(svg) { + margin-left: 6px; + margin-right: 0; + } + + .status-spinner :global(svg) { + animation: spinner 0.7s infinite linear; + } + .file-actions { gap: 4px; } @@ -206,6 +304,18 @@ ); } + :global([dir="rtl"]) .file-actions { + left: 0; + right: unset; + padding-left: 0; + padding-right: 18px; + mask-image: linear-gradient( + -90deg, + rgba(255, 255, 255, 0) 0%, + rgba(0, 0, 0, 1) 20% + ); + } + .processing-item:hover .file-actions { visibility: visible; opacity: 1; From a84b21a5010423472721e050111dcdd5405cab01 Mon Sep 17 00:00:00 2001 From: wukko Date: Thu, 6 Mar 2025 22:50:42 +0600 Subject: [PATCH 203/439] web/runners/remux: retry to run the worker 10 times awkwardly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this is absolutely foul and really needs fixing but i guess it works for now 😭 --- web/src/lib/queen-bee/run-worker.ts | 1 + web/src/lib/queen-bee/runners/remux.ts | 29 +++++++++++++++++++------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/web/src/lib/queen-bee/run-worker.ts b/web/src/lib/queen-bee/run-worker.ts index 6e876cb3..72f56006 100644 --- a/web/src/lib/queen-bee/run-worker.ts +++ b/web/src/lib/queen-bee/run-worker.ts @@ -36,6 +36,7 @@ export const startWorker = async ({ worker, workerId, parentId, workerArgs }: Co files, workerArgs.ffargs, workerArgs.output, + true, // resetStartCounter ); } break; diff --git a/web/src/lib/queen-bee/runners/remux.ts b/web/src/lib/queen-bee/runners/remux.ts index 55a6681d..4ffaf11e 100644 --- a/web/src/lib/queen-bee/runners/remux.ts +++ b/web/src/lib/queen-bee/runners/remux.ts @@ -8,28 +8,41 @@ import type { FileInfo } from "$lib/types/libav"; import type { CobaltQueue } from "$lib/types/queue"; import type { CobaltFileReference } from "$lib/types/storage"; +let startAttempts = 0; + export const runRemuxWorker = async ( workerId: string, parentId: string, files: CobaltFileReference[], args: string[], - output: FileInfo + output: FileInfo, + resetStartCounter?: boolean ) => { const worker = new RemuxWorker(); // sometimes chrome refuses to start libav wasm, - // so we check the health and kill self if it doesn't spawn + // so we check if it started, try 10 more times if not, and kill self if it still doesn't work + // TODO: fix the underlying issue because this is ridiculous + + if (resetStartCounter) startAttempts = 0; let bumpAttempts = 0; - const startCheck = setInterval(() => { + const startCheck = setInterval(async () => { bumpAttempts++; - if (bumpAttempts === 8) { - killWorker(worker, unsubscribe, startCheck); - console.error("worker didn't start after 4 seconds, so it was killed"); + if (bumpAttempts === 10) { + startAttempts++; + if (startAttempts <= 10) { + killWorker(worker, unsubscribe, startCheck); + console.error("worker didn't start after 5 seconds, so it was killed and started again"); + return await runRemuxWorker(workerId, parentId, files, args, output); + } else { + killWorker(worker, unsubscribe, startCheck); + console.error("worker didn't start after 10 attempts, so we're giving up"); - // TODO: proper error code - return itemError(parentId, workerId, "worker didn't start"); + // TODO: proper error code + return itemError(parentId, workerId, "worker didn't start"); + } } }, 500); From dab88f7ed8c8d6a31056f5a9afece50983c3f034 Mon Sep 17 00:00:00 2001 From: wukko Date: Fri, 7 Mar 2025 15:20:34 +0600 Subject: [PATCH 204/439] web/ProcessingStatus: update the icon --- web/src/components/queue/ProcessingStatus.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/components/queue/ProcessingStatus.svelte b/web/src/components/queue/ProcessingStatus.svelte index e8a74156..04bcf819 100644 --- a/web/src/components/queue/ProcessingStatus.svelte +++ b/web/src/components/queue/ProcessingStatus.svelte @@ -1,5 +1,5 @@
diff --git a/web/src/components/queue/ProcessingQueue.svelte b/web/src/components/queue/ProcessingQueue.svelte index ca76b7ac..5da1a39b 100644 --- a/web/src/components/queue/ProcessingQueue.svelte +++ b/web/src/components/queue/ProcessingQueue.svelte @@ -47,11 +47,14 @@ $: indeterminate = queue.length > 0 && totalProgress === 0; - const popoverAction = async () => { + const popoverAction = () => { expanded = !expanded; - if (expanded) updateQuota(); }; + $: if (expanded) { + updateQuota(); + } + onNavigate(() => { expanded = false; }); @@ -59,21 +62,20 @@ onMount(() => { // clear old files from storage on first page load clearFileStorage(); - }) + });
diff --git a/web/src/components/save/SupportedServices.svelte b/web/src/components/save/SupportedServices.svelte index c141499e..4f8c625b 100644 --- a/web/src/components/save/SupportedServices.svelte +++ b/web/src/components/save/SupportedServices.svelte @@ -41,7 +41,7 @@