mirror of https://github.com/VERT-sh/VERT.git
feat: generic server upload warning
added a generic server upload warning, turning it into a toast w/ options instead of a dialog. fixed some hardcoded translations & tooltips
This commit is contained in:
parent
d18fe38832
commit
ba6507b7e6
|
|
@ -44,7 +44,7 @@
|
||||||
"partial_support": "This format can only be converted as {direction}.",
|
"partial_support": "This format can only be converted as {direction}.",
|
||||||
"direction_input": "input (from)",
|
"direction_input": "input (from)",
|
||||||
"direction_output": "output (to)",
|
"direction_output": "output (to)",
|
||||||
"video_server_processing": "Video uploads to a server for processing by default, learn how to set it up locally here."
|
"video_server_processing": "When possible, VERT converts videos locally in your browser. Some conversions may require server-side processing, learn how to set it up locally here."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"convert": {
|
"convert": {
|
||||||
|
|
@ -62,9 +62,10 @@
|
||||||
"large_file_warning": "Due to browser / device limitations, video to audio conversion is disabled for this file as it is larger than {limit}GB. We recommend using Firefox or Safari for files of this size since they have less limitations.",
|
"large_file_warning": "Due to browser / device limitations, video to audio conversion is disabled for this file as it is larger than {limit}GB. We recommend using Firefox or Safari for files of this size since they have less limitations.",
|
||||||
"external_warning": {
|
"external_warning": {
|
||||||
"title": "External server warning",
|
"title": "External server warning",
|
||||||
"text": "If you choose to convert into a video format, those files will be uploaded to an external server to be converted. Do you want to continue?",
|
"text": "Converting {filename} with its current settings is not supported locally and requires server-side processing. Do you want to continue?",
|
||||||
"yes": "Yes",
|
"yes": "Convert on server",
|
||||||
"no": "No"
|
"no": "Cancel conversion",
|
||||||
|
"dont_show_again": "Don't show again"
|
||||||
},
|
},
|
||||||
"panel": {
|
"panel": {
|
||||||
"convert_all": "Convert all",
|
"convert_all": "Convert all",
|
||||||
|
|
@ -149,6 +150,7 @@
|
||||||
"errors": {
|
"errors": {
|
||||||
"cant_convert": "We can't convert this file.",
|
"cant_convert": "We can't convert this file.",
|
||||||
"converter_fallback": {
|
"converter_fallback": {
|
||||||
|
"title": "Change converter?",
|
||||||
"body": "Conversion failed for {filename}. Would you like to try the next compatible converter {converter}?",
|
"body": "Conversion failed for {filename}. Would you like to try the next compatible converter {converter}?",
|
||||||
"yes": "Next converter",
|
"yes": "Next converter",
|
||||||
"no": "Cancel conversion",
|
"no": "Cancel conversion",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import { m } from "$lib/paraglide/messages";
|
import { m } from "$lib/paraglide/messages";
|
||||||
|
|
||||||
type Additional = {
|
type Additional = {
|
||||||
fileName: string;
|
filename: string;
|
||||||
nextConverter: string;
|
nextConverter: string;
|
||||||
onNext: () => void | Promise<void>;
|
onNext: () => void | Promise<void>;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
|
|
@ -10,13 +10,13 @@
|
||||||
|
|
||||||
let { additional }: { additional: Additional } = $props();
|
let { additional }: { additional: Additional } = $props();
|
||||||
|
|
||||||
export const title = "An error occurred";
|
export const title = m["convert.errors.converter_fallback.title"]();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-4">
|
||||||
<p class="text-black">
|
<p class="text-black">
|
||||||
{m["convert.errors.converter_fallback.body"]({
|
{m["convert.errors.converter_fallback.body"]({
|
||||||
filename: additional.fileName,
|
filename: additional.filename,
|
||||||
converter: additional.nextConverter,
|
converter: additional.nextConverter,
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
<script lang="ts" module>
|
||||||
|
export interface ServerUploadWarningProps {
|
||||||
|
filename: string;
|
||||||
|
onProceed: () => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
onDontShowAgain: () => void;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { m } from "$lib/paraglide/messages";
|
||||||
|
import type { ToastProps } from "$lib/util/toast.svelte";
|
||||||
|
|
||||||
|
const toast: ToastProps<ServerUploadWarningProps> = $props();
|
||||||
|
|
||||||
|
export const title = m["convert.external_warning.title"]();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-4">
|
||||||
|
<p class="text-black">
|
||||||
|
{m["convert.external_warning.text"]({
|
||||||
|
filename: toast.additional.filename,
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<button
|
||||||
|
onclick={toast.additional.onDontShowAgain}
|
||||||
|
class="btn rounded-lg h-fit py-2 w-full bg-accent-blue text-black"
|
||||||
|
>
|
||||||
|
{m["convert.external_warning.dont_show_again"]()}
|
||||||
|
</button>
|
||||||
|
<div class="flex gap-4">
|
||||||
|
<button
|
||||||
|
onclick={toast.additional.onProceed}
|
||||||
|
class="btn rounded-lg h-fit py-2 w-full bg-accent-red-alt text-white"
|
||||||
|
>
|
||||||
|
{m["convert.external_warning.yes"]()}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onclick={toast.additional.onCancel}
|
||||||
|
class="btn rounded-lg h-fit py-2 w-full"
|
||||||
|
>
|
||||||
|
{m["convert.external_warning.no"]()}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
from?: string;
|
from?: string;
|
||||||
to?: string;
|
to?: string;
|
||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
fileName?: string;
|
filename?: string;
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
let submitting = $state(false);
|
let submitting = $state(false);
|
||||||
|
|
||||||
export const title = "An error occurred";
|
export const title = m["convert.errors.vertd_generic_title"]();
|
||||||
|
|
||||||
const remove = () => {
|
const remove = () => {
|
||||||
ToastManager.remove(toast.id);
|
ToastManager.remove(toast.id);
|
||||||
|
|
|
||||||
|
|
@ -112,8 +112,8 @@
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.tooltip {
|
.tooltip {
|
||||||
--border-size: 1px;
|
--border-size: 1px;
|
||||||
@apply fixed bg-panel-alt text-foreground border border-stone-400 dynadark:border-white drop-shadow-lg text-xs rounded-full pointer-events-none z-[999] max-w-xs break-words whitespace-normal;
|
@apply fixed bg-panel-alt text-foreground border border-stone-400 dynadark:border-white drop-shadow-lg text-xs pointer-events-none z-[999] max-w-[200px] break-words whitespace-normal;
|
||||||
@apply px-5 py-2.5;
|
@apply px-3.5 py-2 rounded-xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tooltip-top {
|
.tooltip-top {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import { error, log } from "$lib/util/logger";
|
||||||
import { VertFile } from "$lib/types";
|
import { VertFile } from "$lib/types";
|
||||||
import { parseBlob, selectCover } from "music-metadata";
|
import { parseBlob, selectCover } from "music-metadata";
|
||||||
import { writable } from "svelte/store";
|
import { writable } from "svelte/store";
|
||||||
import { addDialog } from "./DialogProvider";
|
|
||||||
import PQueue from "p-queue";
|
import PQueue from "p-queue";
|
||||||
import { getLocale, setLocale } from "$lib/paraglide/runtime";
|
import { getLocale, setLocale } from "$lib/paraglide/runtime";
|
||||||
import { m } from "$lib/paraglide/messages";
|
import { m } from "$lib/paraglide/messages";
|
||||||
|
|
@ -236,7 +235,6 @@ class Files {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _warningShown = false;
|
|
||||||
private async _add(file: VertFile | File) {
|
private async _add(file: VertFile | File) {
|
||||||
if (file instanceof VertFile) {
|
if (file instanceof VertFile) {
|
||||||
this.files.push(file);
|
this.files.push(file);
|
||||||
|
|
@ -302,43 +300,6 @@ class Files {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: only show if vertd is needed/requested
|
|
||||||
const isServerVideo = convName === "vertd";
|
|
||||||
const acceptedExternalWarning =
|
|
||||||
localStorage.getItem("acceptedExternalWarning") === "true";
|
|
||||||
if (isServerVideo && !acceptedExternalWarning && !this._warningShown) {
|
|
||||||
this._warningShown = true;
|
|
||||||
const title = m["convert.external_warning.title"]();
|
|
||||||
const message = m["convert.external_warning.text"]();
|
|
||||||
const buttons = [
|
|
||||||
{
|
|
||||||
text: m["convert.external_warning.no"](),
|
|
||||||
action: () => {
|
|
||||||
this.files = [
|
|
||||||
...this.files.filter(
|
|
||||||
(f) =>
|
|
||||||
!f.converters
|
|
||||||
.map((c) => c.name)
|
|
||||||
.includes("vertd"),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
this._warningShown = false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: m["convert.external_warning.yes"](),
|
|
||||||
action: () => {
|
|
||||||
localStorage.setItem(
|
|
||||||
"acceptedExternalWarning",
|
|
||||||
"true",
|
|
||||||
);
|
|
||||||
this._warningShown = false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
addDialog(title, message, buttons, "warning");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -378,7 +339,7 @@ class Files {
|
||||||
if (this.files.length === 0) return;
|
if (this.files.length === 0) return;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const dlFiles: any[] = [];
|
const dlFiles: any[] = [];
|
||||||
const fileNames: string[] = [];
|
const filenames: string[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < this.files.length; i++) {
|
for (let i = 0; i < this.files.length; i++) {
|
||||||
const file = this.files[i];
|
const file = this.files[i];
|
||||||
|
|
@ -392,7 +353,7 @@ class Files {
|
||||||
let to = result.to;
|
let to = result.to;
|
||||||
if (!to.startsWith(".")) to = `.${to}`;
|
if (!to.startsWith(".")) to = `.${to}`;
|
||||||
|
|
||||||
fileNames.push(file.file.name.replace(/\.[^/.]+$/, "") + to);
|
filenames.push(file.file.name.replace(/\.[^/.]+$/, "") + to);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this.files.length; i++) {
|
for (let i = 0; i < this.files.length; i++) {
|
||||||
|
|
@ -401,19 +362,19 @@ class Files {
|
||||||
|
|
||||||
if (!result) continue;
|
if (!result) continue;
|
||||||
|
|
||||||
let fileName = fileNames[i];
|
let filename = filenames[i];
|
||||||
|
|
||||||
// check if this filename appears more than once
|
// check if this filename appears more than once
|
||||||
const isDuplicate = fileNames.filter((name) => name === fileName).length > 1;
|
const isDuplicate = filenames.filter((name) => name === filename).length > 1;
|
||||||
if (isDuplicate) {
|
if (isDuplicate) {
|
||||||
const nameParts = fileName.lastIndexOf(".");
|
const nameParts = filename.lastIndexOf(".");
|
||||||
const nameWithoutExt = fileName.substring(0, nameParts);
|
const nameWithoutExt = filename.substring(0, nameParts);
|
||||||
const ext = fileName.substring(nameParts);
|
const ext = filename.substring(nameParts);
|
||||||
fileName = `${nameWithoutExt} (${i + 1})${ext}`;
|
filename = `${nameWithoutExt} (${i + 1})${ext}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
dlFiles.push({
|
dlFiles.push({
|
||||||
name: fileName,
|
name: filename,
|
||||||
lastModified: Date.now(),
|
lastModified: Date.now(),
|
||||||
input: await result.file.arrayBuffer(),
|
input: await result.file.arrayBuffer(),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { ToastManager } from "$lib/util/toast.svelte";
|
||||||
import type { Component } from "svelte";
|
import type { Component } from "svelte";
|
||||||
import { MAX_ARRAY_BUFFER_SIZE } from "$lib/store/index.svelte";
|
import { MAX_ARRAY_BUFFER_SIZE } from "$lib/store/index.svelte";
|
||||||
import FallbackToast from "$lib/components/functional/popups/FallbackToast.svelte";
|
import FallbackToast from "$lib/components/functional/popups/FallbackToast.svelte";
|
||||||
|
import ServerUploadWarning from "$lib/components/functional/popups/ServerUploadWarning.svelte";
|
||||||
import type {
|
import type {
|
||||||
ConversionSettings,
|
ConversionSettings,
|
||||||
SettingDefinition,
|
SettingDefinition,
|
||||||
|
|
@ -41,6 +42,7 @@ export class VertFile {
|
||||||
private fallbackToastId: number | null = null;
|
private fallbackToastId: number | null = null;
|
||||||
private attemptedConverters = new Set<string>();
|
private attemptedConverters = new Set<string>();
|
||||||
private retryingFallback = false;
|
private retryingFallback = false;
|
||||||
|
private vertdWarningToastId: number | null = null;
|
||||||
|
|
||||||
public isZip = $state(() => this.from === ".zip");
|
public isZip = $state(() => this.from === ".zip");
|
||||||
|
|
||||||
|
|
@ -168,6 +170,13 @@ export class VertFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!converter) throw new Error("No converter found");
|
if (!converter) throw new Error("No converter found");
|
||||||
|
|
||||||
|
const canProceed = await this.confirmServerWarning(converter);
|
||||||
|
if (!canProceed) {
|
||||||
|
this.cancelled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.attemptedConverters.add(converter.name);
|
this.attemptedConverters.add(converter.name);
|
||||||
log(["file", "convert"], `using converter: ${converter.name}`);
|
log(["file", "convert"], `using converter: ${converter.name}`);
|
||||||
|
|
||||||
|
|
@ -215,7 +224,7 @@ export class VertFile {
|
||||||
disappearing: false,
|
disappearing: false,
|
||||||
message: FallbackToast,
|
message: FallbackToast,
|
||||||
additional: {
|
additional: {
|
||||||
fileName: this.file.name,
|
filename: this.file.name,
|
||||||
nextConverter: nextConverter.name,
|
nextConverter: nextConverter.name,
|
||||||
onNext: async () => {
|
onNext: async () => {
|
||||||
if (this.fallbackToastId !== null)
|
if (this.fallbackToastId !== null)
|
||||||
|
|
@ -262,6 +271,47 @@ export class VertFile {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async confirmServerWarning(converter: Converter): Promise<boolean> {
|
||||||
|
if (converter.name !== "vertd") return true;
|
||||||
|
if (localStorage.getItem("acceptedExternalWarning") === "true")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let resolved = false;
|
||||||
|
|
||||||
|
const finish = (shouldProceed: boolean) => {
|
||||||
|
if (resolved) return;
|
||||||
|
resolved = true;
|
||||||
|
if (this.vertdWarningToastId !== null)
|
||||||
|
ToastManager.remove(this.vertdWarningToastId);
|
||||||
|
this.vertdWarningToastId = null;
|
||||||
|
resolve(shouldProceed);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.vertdWarningToastId !== null)
|
||||||
|
ToastManager.remove(this.vertdWarningToastId);
|
||||||
|
|
||||||
|
this.vertdWarningToastId = ToastManager.add({
|
||||||
|
type: "warning",
|
||||||
|
disappearing: false,
|
||||||
|
message: ServerUploadWarning,
|
||||||
|
additional: {
|
||||||
|
filename: this.file.name,
|
||||||
|
onProceed: () => {
|
||||||
|
finish(true);
|
||||||
|
},
|
||||||
|
onCancel: () => {
|
||||||
|
finish(false);
|
||||||
|
},
|
||||||
|
onDontShowAgain: () => {
|
||||||
|
localStorage.setItem("acceptedExternalWarning", "true");
|
||||||
|
finish(true);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async convertZip(converter: Converter): Promise<VertFile> {
|
private async convertZip(converter: Converter): Promise<VertFile> {
|
||||||
const { extractZip, createZip } = await import("$lib/util/zip");
|
const { extractZip, createZip } = await import("$lib/util/zip");
|
||||||
const { default: PQueue } = await import("p-queue");
|
const { default: PQueue } = await import("p-queue");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue