mirror of https://github.com/VERT-sh/VERT.git
feat: block repeat failures (i really hope this functions because i wrote it a month ago)
This commit is contained in:
parent
7832b6a43d
commit
9dfbfe6412
|
|
@ -98,6 +98,7 @@
|
||||||
"vertd_details_to": "<b>To format:</b> {to}",
|
"vertd_details_to": "<b>To format:</b> {to}",
|
||||||
"vertd_details_error_message": "<b>Error message:</b> [view_link]View error logs[/view_link]",
|
"vertd_details_error_message": "<b>Error message:</b> [view_link]View error logs[/view_link]",
|
||||||
"vertd_details_close": "Close",
|
"vertd_details_close": "Close",
|
||||||
|
"vertd_ratelimit": "Your video, '{filename}', has failed to convert a few times. To prevent server overload, further conversion attempts for this file have been temporarily blocked. Please try again later.",
|
||||||
"unsupported_format": "Only image, video, audio, and document files are supported",
|
"unsupported_format": "Only image, video, audio, and document files are supported",
|
||||||
"format_output_only": "This format can currently only be used as output (converted to), not as input.",
|
"format_output_only": "This format can currently only be used as output (converted to), not as input.",
|
||||||
"vertd_not_found": "Could not find the vertd instance to start video conversion. Are you sure the instance URL is set correctly?",
|
"vertd_not_found": "Could not find the vertd instance to start video conversion. Are you sure the instance URL is set correctly?",
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,6 @@
|
||||||
"audio": "ses",
|
"audio": "ses",
|
||||||
"doc": "belge",
|
"doc": "belge",
|
||||||
"image": "görsel"
|
"image": "görsel"
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|
@ -228,8 +227,8 @@
|
||||||
},
|
},
|
||||||
"workers": {
|
"workers": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"general": "{dosya} dönüştürülürken hata oluştu: {message}",
|
"general": "{file} dönüştürülürken hata oluştu: {message}",
|
||||||
"cancel": "{dosya} için dönüştürme işlemi iptal edilirken hata oluştu: {message}",
|
"cancel": "{file} için dönüştürme işlemi iptal edilirken hata oluştu: {message}",
|
||||||
"magick": "Magick işlemi sırasında hata oluştu, görsel dönüştürme işlemi beklendiği gibi çalışmayabilir.",
|
"magick": "Magick işlemi sırasında hata oluştu, görsel dönüştürme işlemi beklendiği gibi çalışmayabilir.",
|
||||||
"ffmpeg": "ffmpeg yüklenirken hata oluştu, bazı özellikler çalışmayabilir.",
|
"ffmpeg": "ffmpeg yüklenirken hata oluştu, bazı özellikler çalışmayabilir.",
|
||||||
"no_audio": "Ses akışı bulunamadı.",
|
"no_audio": "Ses akışı bulunamadı.",
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,11 @@
|
||||||
import VertdErrorComponent from "$lib/components/functional/VertdError.svelte";
|
import VertdErrorComponent from "$lib/components/functional/VertdError.svelte";
|
||||||
import { error, log } from "$lib/logger";
|
import { error, log } from "$lib/logger";
|
||||||
|
import { m } from "$lib/paraglide/messages";
|
||||||
import { Settings } from "$lib/sections/settings/index.svelte";
|
import { Settings } from "$lib/sections/settings/index.svelte";
|
||||||
import { VertdInstance } from "$lib/sections/settings/vertdSettings.svelte";
|
import { VertdInstance } from "$lib/sections/settings/vertdSettings.svelte";
|
||||||
import { VertFile } from "$lib/types";
|
import { VertFile } from "$lib/types";
|
||||||
import { Converter, FormatInfo } from "./converter.svelte";
|
import { Converter, FormatInfo } from "./converter.svelte";
|
||||||
|
|
||||||
interface VertdError {
|
|
||||||
type: "error";
|
|
||||||
data: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface VertdSuccess<T> {
|
|
||||||
type: "success";
|
|
||||||
data: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
type VertdResponse<T> = VertdError | VertdSuccess<T>;
|
|
||||||
|
|
||||||
interface UploadResponse {
|
interface UploadResponse {
|
||||||
id: string;
|
id: string;
|
||||||
auth: string;
|
auth: string;
|
||||||
|
|
@ -49,6 +38,7 @@ export const vertdFetch: {
|
||||||
url: U,
|
url: U,
|
||||||
options: RequestInit,
|
options: RequestInit,
|
||||||
): Promise<RouteResponseMap[U]>;
|
): Promise<RouteResponseMap[U]>;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} = async (url: any, options: RequestInit, body?: any) => {
|
} = async (url: any, options: RequestInit, body?: any) => {
|
||||||
const domain = await VertdInstance.instance.url();
|
const domain = await VertdInstance.instance.url();
|
||||||
|
|
||||||
|
|
@ -298,9 +288,50 @@ export class VertdConverter extends Converter {
|
||||||
this.status = "ready";
|
this.status = "ready";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private blocked(hash: string): boolean {
|
||||||
|
const blockedHashes = Settings.instance.settings.vertdBlockedHashes;
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const dates = blockedHashes.get(hash) || [];
|
||||||
|
const filteredDates = dates.filter(
|
||||||
|
(date) => now.getTime() - date.getTime() < 60 * 60 * 1000,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (filteredDates.length === 0) {
|
||||||
|
blockedHashes.delete(hash);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockedHashes.set(hash, filteredDates);
|
||||||
|
|
||||||
|
Settings.instance.save();
|
||||||
|
|
||||||
|
return filteredDates.length >= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
private failure(hash: string): void {
|
||||||
|
const blockedHashes = Settings.instance.settings.vertdBlockedHashes;
|
||||||
|
const now = new Date();
|
||||||
|
const dates = blockedHashes.get(hash) || [];
|
||||||
|
dates.push(now);
|
||||||
|
blockedHashes.set(hash, dates);
|
||||||
|
Settings.instance.save();
|
||||||
|
}
|
||||||
|
|
||||||
public async convert(input: VertFile, to: string): Promise<VertFile> {
|
public async convert(input: VertFile, to: string): Promise<VertFile> {
|
||||||
if (to.startsWith(".")) to = to.slice(1);
|
if (to.startsWith(".")) to = to.slice(1);
|
||||||
|
|
||||||
|
const hash = await input.hash();
|
||||||
|
|
||||||
|
if (this.blocked(hash)) {
|
||||||
|
this.log(`conversion blocked for file ${input.name}`);
|
||||||
|
throw new Error(
|
||||||
|
m["convert.errors.vertd_ratelimit"]({
|
||||||
|
filename: input.name,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const uploadRes = await uploadFile(input);
|
const uploadRes = await uploadFile(input);
|
||||||
const apiUrl = await VertdInstance.instance.url();
|
const apiUrl = await VertdInstance.instance.url();
|
||||||
|
|
||||||
|
|
@ -372,6 +403,8 @@ export class VertdConverter extends Converter {
|
||||||
case "error": {
|
case "error": {
|
||||||
this.log(`error: ${msg.data.message}`);
|
this.log(`error: ${msg.data.message}`);
|
||||||
this.activeConversions.delete(input.id);
|
this.activeConversions.delete(input.id);
|
||||||
|
this.failure(hash);
|
||||||
|
|
||||||
reject({
|
reject({
|
||||||
component: VertdErrorComponent,
|
component: VertdErrorComponent,
|
||||||
additional: {
|
additional: {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ export interface ISettings {
|
||||||
ffmpegQuality: ConversionBitrate; // audio (or audio <-> video)
|
ffmpegQuality: ConversionBitrate; // audio (or audio <-> video)
|
||||||
ffmpegSampleRate: string; // audio (or audio <-> video)
|
ffmpegSampleRate: string; // audio (or audio <-> video)
|
||||||
ffmpegCustomSampleRate: number; // audio (or audio <-> video) - only used when ffmpegSampleRate is "custom"
|
ffmpegCustomSampleRate: number; // audio (or audio <-> video) - only used when ffmpegSampleRate is "custom"
|
||||||
|
vertdBlockedHashes: Map<string, Date[]>; // hashes of files blocked from vertd conversion
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Settings {
|
export class Settings {
|
||||||
|
|
@ -50,6 +51,7 @@ export class Settings {
|
||||||
ffmpegQuality: "auto",
|
ffmpegQuality: "auto",
|
||||||
ffmpegSampleRate: "auto",
|
ffmpegSampleRate: "auto",
|
||||||
ffmpegCustomSampleRate: 44100,
|
ffmpegCustomSampleRate: 44100,
|
||||||
|
vertdBlockedHashes: new Map<string, Date[]>(),
|
||||||
});
|
});
|
||||||
|
|
||||||
public save() {
|
public save() {
|
||||||
|
|
@ -62,6 +64,14 @@ export class Settings {
|
||||||
const ls = localStorage.getItem("settings");
|
const ls = localStorage.getItem("settings");
|
||||||
if (!ls) return;
|
if (!ls) return;
|
||||||
const settings: ISettings = JSON.parse(ls);
|
const settings: ISettings = JSON.parse(ls);
|
||||||
|
const vertdBlockedHashes = new Map<string, Date[]>(
|
||||||
|
Object.entries(
|
||||||
|
settings.vertdBlockedHashes || this.settings.vertdBlockedHashes,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
settings.vertdBlockedHashes = vertdBlockedHashes;
|
||||||
|
|
||||||
this.settings = {
|
this.settings = {
|
||||||
...this.settings,
|
...this.settings,
|
||||||
...settings,
|
...settings,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import { byNative, converters } from "$lib/converters";
|
import { byNative, converters } from "$lib/converters";
|
||||||
import type { Converter } from "$lib/converters/converter.svelte";
|
import type { Converter } from "$lib/converters/converter.svelte";
|
||||||
import { error } from "$lib/logger";
|
|
||||||
import { m } from "$lib/paraglide/messages";
|
import { m } from "$lib/paraglide/messages";
|
||||||
import { ToastManager } from "$lib/toast/index.svelte";
|
import { ToastManager } from "$lib/toast/index.svelte";
|
||||||
import type { Component } from "svelte";
|
import type { Component } from "svelte";
|
||||||
|
|
@ -196,6 +195,40 @@ export class VertFile {
|
||||||
URL.revokeObjectURL(blob);
|
URL.revokeObjectURL(blob);
|
||||||
a.remove();
|
a.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hash(): Promise<string> {
|
||||||
|
const stream = this.file.stream();
|
||||||
|
const hashes = new Set<string>();
|
||||||
|
const reader = stream.getReader();
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
function processChunk() {
|
||||||
|
reader.read().then(({ done, value }) => {
|
||||||
|
if (done) {
|
||||||
|
const combinedHash = Array.from(hashes).sort().join("");
|
||||||
|
resolve(combinedHash);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
crypto.subtle
|
||||||
|
.digest("SHA-256", value)
|
||||||
|
.then((hashBuffer) => {
|
||||||
|
const hashArray = Array.from(
|
||||||
|
new Uint8Array(hashBuffer),
|
||||||
|
);
|
||||||
|
const hashHex = hashArray
|
||||||
|
.map((b) => b.toString(16).padStart(2, "0"))
|
||||||
|
.join("");
|
||||||
|
hashes.add(hashHex);
|
||||||
|
processChunk();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
processChunk();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Categories {
|
export interface Categories {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue