mirror of https://github.com/VERT-sh/VERT.git
feat: start vertd settings
removed the settings auto detection (since it made UX a bit worse), added conversion speed slider - need to implement in vertd
This commit is contained in:
parent
f129682474
commit
892db30480
|
|
@ -84,7 +84,7 @@
|
||||||
"settings": {
|
"settings": {
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"title": "File conversion settings",
|
"title": "File conversion settings",
|
||||||
"description": "Change the conversion settings for <b>{filename}</b>, which is using <b>{converter}</b>. These settings may not be available for all formats.",
|
"description": "Change the conversion settings for <b>{filename}</b>, which is using <b>{converter}</b>. These settings may not be available for all formats. This is an early beta and may have some issues.",
|
||||||
"none": "No settings available for this format.",
|
"none": "No settings available for this format.",
|
||||||
"image": {
|
"image": {
|
||||||
"quality": "Quality",
|
"quality": "Quality",
|
||||||
|
|
@ -94,30 +94,42 @@
|
||||||
},
|
},
|
||||||
"audio": {
|
"audio": {
|
||||||
"bitrate": "Bitrate (kbps)",
|
"bitrate": "Bitrate (kbps)",
|
||||||
|
"bitrate_placeholder": "Custom bitrate",
|
||||||
"sample_rate": "Sample rate (Hz)",
|
"sample_rate": "Sample rate (Hz)",
|
||||||
|
"sample_rate_placeholder": "Custom sample rate",
|
||||||
"channels": "Audio channels",
|
"channels": "Audio channels",
|
||||||
"tracks": "Audio tracks"
|
"channels_placeholder": "Custom audio channels",
|
||||||
|
"tracks": "Audio tracks",
|
||||||
|
"tracks_placeholder": "Custom audio tracks"
|
||||||
},
|
},
|
||||||
"video": {
|
"video": {
|
||||||
"quality": "Quality",
|
"quality": "Quality",
|
||||||
"metadata": "Metadata",
|
"metadata": "Metadata",
|
||||||
"speed": "Conversion speed",
|
"speed": "Conversion speed",
|
||||||
"speed_very_slow": "Very Slow",
|
"speed_description": "This will be overridden if you manually set the bitrate or resolution below - selecting options other than \"auto\".",
|
||||||
"speed_slower": "Slower",
|
"speed_very_slow": "Highest quality (slowest)",
|
||||||
"speed_slow": "Slow",
|
"speed_slower": "Higher quality (slower)",
|
||||||
"speed_medium": "Medium",
|
"speed_slow": "High quality (slow)",
|
||||||
"speed_fast": "Fast",
|
"speed_medium": "Medium quality (average)",
|
||||||
"speed_ultra_fast": "Ultra Fast",
|
"speed_fast": "Lower quality (faster)",
|
||||||
|
"speed_ultra_fast": "Lowest quality (fastest)",
|
||||||
"fps": "Frame rate (FPS)",
|
"fps": "Frame rate (FPS)",
|
||||||
"fps_placeholder": "Auto",
|
"fps_placeholder": "Custom frame rate",
|
||||||
"resolution": "Resolution",
|
"resolution": "Resolution",
|
||||||
"resolution_placeholder": "Auto (e.g., 1920x1080)"
|
"resolution_placeholder": "Custom resolution",
|
||||||
|
"video_bitrate": "Video bitrate (kbps)",
|
||||||
|
"audio_bitrate": "Audio bitrate (kbps)",
|
||||||
|
"bitrate_placeholder": "Custom bitrate",
|
||||||
|
"sample_rate": "Audio sample rate (Hz)",
|
||||||
|
"sample_rate_placeholder": "Custom sample rate"
|
||||||
},
|
},
|
||||||
"document": {
|
"document": {
|
||||||
"something": "Something"
|
"something": "Something"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"metadata": "Metadata"
|
"metadata": "Metadata",
|
||||||
|
"auto": "auto",
|
||||||
|
"custom": "custom"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tooltips": {
|
"tooltips": {
|
||||||
|
|
|
||||||
|
|
@ -86,13 +86,17 @@
|
||||||
{:else}
|
{:else}
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
{#each availableSettings as setting (setting.key)}
|
{#each availableSettings as setting (setting.key)}
|
||||||
<div class="flex flex-col gap-2">
|
<div
|
||||||
|
class={setting.forceFullWidth
|
||||||
|
? "col-span-2"
|
||||||
|
: "flex flex-col gap-2"}
|
||||||
|
>
|
||||||
<p class="text-sm font-bold">
|
<p class="text-sm font-bold">
|
||||||
{setting.label}
|
{setting.label}
|
||||||
</p>
|
</p>
|
||||||
<!-- prob unneeded -->
|
<!-- prob unneeded -->
|
||||||
{#if setting.description}
|
{#if setting.description}
|
||||||
<p class="text-xs text-muted">
|
<p class="text-xs text-muted mt-1">
|
||||||
{setting.description}
|
{setting.description}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
@ -102,9 +106,11 @@
|
||||||
options={setting.options?.map(
|
options={setting.options?.map(
|
||||||
(opt) => opt.value,
|
(opt) => opt.value,
|
||||||
) || []}
|
) || []}
|
||||||
selected={file.conversionSettings[
|
selected={settings[setting.key] ??
|
||||||
setting.key
|
file.conversionSettings[
|
||||||
] ?? setting.default}
|
setting.key
|
||||||
|
] ??
|
||||||
|
setting.default}
|
||||||
settingsStyle
|
settingsStyle
|
||||||
onselect={(value) =>
|
onselect={(value) =>
|
||||||
handleSettingChange(
|
handleSettingChange(
|
||||||
|
|
@ -139,9 +145,11 @@
|
||||||
{:else if setting.type === "boolean"}
|
{:else if setting.type === "boolean"}
|
||||||
<FancyInput
|
<FancyInput
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={file.conversionSettings[
|
checked={settings[setting.key] ??
|
||||||
setting.key
|
file.conversionSettings[
|
||||||
] ?? setting.default}
|
setting.key
|
||||||
|
] ??
|
||||||
|
setting.default}
|
||||||
placeholder={setting.placeholder}
|
placeholder={setting.placeholder}
|
||||||
onchange={(e) =>
|
onchange={(e) =>
|
||||||
handleSettingChange(
|
handleSettingChange(
|
||||||
|
|
@ -149,12 +157,51 @@
|
||||||
e.currentTarget.checked,
|
e.currentTarget.checked,
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
{:else if setting.type === "range"}
|
||||||
|
{@const rangeValue = (settings[
|
||||||
|
setting.key
|
||||||
|
] ??
|
||||||
|
file.conversionSettings[
|
||||||
|
setting.key
|
||||||
|
] ??
|
||||||
|
setting.default ??
|
||||||
|
setting.min ??
|
||||||
|
0) as number}
|
||||||
|
{@const rangeLabel =
|
||||||
|
setting.options?.[rangeValue]
|
||||||
|
?.label ?? rangeValue}
|
||||||
|
<div class="flex items-center mt-2 gap-2">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min={setting.min}
|
||||||
|
max={setting.max}
|
||||||
|
step={setting.step}
|
||||||
|
value={rangeValue}
|
||||||
|
class="range-slider w-full"
|
||||||
|
oninput={(e) => {
|
||||||
|
const nextValue =
|
||||||
|
e.currentTarget
|
||||||
|
.valueAsNumber;
|
||||||
|
handleSettingChange(
|
||||||
|
setting.key,
|
||||||
|
nextValue,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="text-sm max-w-28 w-full text-right"
|
||||||
|
>
|
||||||
|
{rangeLabel}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<FancyInput
|
<FancyInput
|
||||||
type={setting.type}
|
type={setting.type}
|
||||||
value={file.conversionSettings[
|
value={settings[setting.key] ??
|
||||||
setting.key
|
file.conversionSettings[
|
||||||
] ?? setting.default}
|
setting.key
|
||||||
|
] ??
|
||||||
|
setting.default}
|
||||||
placeholder={setting.placeholder}
|
placeholder={setting.placeholder}
|
||||||
oninput={(e) =>
|
oninput={(e) =>
|
||||||
handleSettingChange(
|
handleSettingChange(
|
||||||
|
|
|
||||||
|
|
@ -109,21 +109,11 @@ export class FFmpegConverter extends Converter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getAvailableSettings(
|
public async getAvailableSettings(): Promise<SettingDefinition[]> {
|
||||||
input: VertFile,
|
|
||||||
): Promise<SettingDefinition[]> {
|
|
||||||
// audio - bitrate, sample rate, channels, normalize, trim silence
|
// audio - bitrate, sample rate, channels, normalize, trim silence
|
||||||
|
|
||||||
const global = Settings.instance.settings;
|
const global = Settings.instance.settings;
|
||||||
|
|
||||||
const ffmpeg = await this.setupFFmpeg(input, true);
|
|
||||||
const buf = new Uint8Array(await input.file.arrayBuffer());
|
|
||||||
await ffmpeg.writeFile("input", buf);
|
|
||||||
|
|
||||||
// TODO: should we really be doing all this detection here? it adds a lot of time before the settings even show up.
|
|
||||||
// which isn't very nice for the UX guh
|
|
||||||
|
|
||||||
const detectedBitrate = await this.detectAudioBitrate(ffmpeg);
|
|
||||||
const bitrate: SettingDefinition = {
|
const bitrate: SettingDefinition = {
|
||||||
key: "bitrate",
|
key: "bitrate",
|
||||||
label: m["convert.settings.audio.bitrate"](),
|
label: m["convert.settings.audio.bitrate"](),
|
||||||
|
|
@ -135,10 +125,9 @@ export class FFmpegConverter extends Converter {
|
||||||
})),
|
})),
|
||||||
hasCustomInput: true,
|
hasCustomInput: true,
|
||||||
customInputKey: "customBitrate",
|
customInputKey: "customBitrate",
|
||||||
placeholder: detectedBitrate ?? "128"
|
placeholder: m["convert.settings.audio.bitrate_placeholder"](),
|
||||||
};
|
};
|
||||||
|
|
||||||
const detectedSampleRate = await this.detectAudioSampleRate(ffmpeg);
|
|
||||||
const sampleRate: SettingDefinition = {
|
const sampleRate: SettingDefinition = {
|
||||||
key: "sampleRate",
|
key: "sampleRate",
|
||||||
label: m["convert.settings.audio.sample_rate"](),
|
label: m["convert.settings.audio.sample_rate"](),
|
||||||
|
|
@ -153,31 +142,31 @@ export class FFmpegConverter extends Converter {
|
||||||
})),
|
})),
|
||||||
hasCustomInput: true,
|
hasCustomInput: true,
|
||||||
customInputKey: "customSampleRate",
|
customInputKey: "customSampleRate",
|
||||||
placeholder: detectedSampleRate ?? "44100"
|
placeholder: m["convert.settings.audio.sample_rate_placeholder"](),
|
||||||
};
|
};
|
||||||
|
|
||||||
const audioTracks = await this.detectAudioTracks(ffmpeg);
|
|
||||||
const tracks: SettingDefinition = {
|
const tracks: SettingDefinition = {
|
||||||
key: "tracks",
|
key: "tracks",
|
||||||
label: m["convert.settings.audio.tracks"](),
|
label: m["convert.settings.audio.tracks"](),
|
||||||
type: "number",
|
type: "number",
|
||||||
default: audioTracks ?? 1,
|
default: 1,
|
||||||
min: 1,
|
min: 1,
|
||||||
max: audioTracks ? audioTracks : 1,
|
placeholder: m["convert.settings.audio.tracks_placeholder"](),
|
||||||
placeholder: audioTracks ?? 1
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const audioChannels = await this.detectAudioChannels(ffmpeg);
|
|
||||||
const channels: SettingDefinition = {
|
const channels: SettingDefinition = {
|
||||||
key: "channels",
|
key: "channels",
|
||||||
label: m["convert.settings.audio.channels"](),
|
label: m["convert.settings.audio.channels"](),
|
||||||
type: "number",
|
type: "number",
|
||||||
default: audioChannels ?? 2,
|
default: 2,
|
||||||
min: 1,
|
min: 1,
|
||||||
max: audioChannels ? audioChannels * 2 : 5,
|
max: 8,
|
||||||
placeholder: audioChannels ?? 2
|
placeholder: m["convert.settings.audio.channels_placeholder"](),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* common
|
||||||
|
*/
|
||||||
const metadata: SettingDefinition = {
|
const metadata: SettingDefinition = {
|
||||||
key: "metadata",
|
key: "metadata",
|
||||||
label: m["convert.settings.common.metadata"](),
|
label: m["convert.settings.common.metadata"](),
|
||||||
|
|
@ -426,100 +415,6 @@ export class FFmpegConverter extends Converter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async detectAudioTracks(ffmpeg: FFmpeg): Promise<number | null> {
|
|
||||||
const args = [
|
|
||||||
"-v",
|
|
||||||
"error",
|
|
||||||
"-select_streams",
|
|
||||||
"a",
|
|
||||||
"-show_entries",
|
|
||||||
"stream=index",
|
|
||||||
"-of",
|
|
||||||
"json",
|
|
||||||
"input",
|
|
||||||
];
|
|
||||||
|
|
||||||
try {
|
|
||||||
let output = "";
|
|
||||||
|
|
||||||
const tracksListener = (event: { message: string }) => {
|
|
||||||
output += `${event.message}\n`;
|
|
||||||
};
|
|
||||||
|
|
||||||
ffmpeg.on("log", tracksListener);
|
|
||||||
|
|
||||||
try {
|
|
||||||
log(
|
|
||||||
["converters", this.name],
|
|
||||||
`Running ffprobe to detect audio tracks with args: ${args.join(" ")}`,
|
|
||||||
);
|
|
||||||
await ffmpeg.ffprobe.call(ffmpeg, args);
|
|
||||||
} finally {
|
|
||||||
ffmpeg.off("log", tracksListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!output.trim()) return null;
|
|
||||||
|
|
||||||
const parsed = JSON.parse(output);
|
|
||||||
const tracks = Array.isArray(parsed?.streams)
|
|
||||||
? parsed.streams.length
|
|
||||||
: null;
|
|
||||||
|
|
||||||
log(
|
|
||||||
["converters", this.name],
|
|
||||||
`Detected stream audio tracks: ${tracks}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
return tracks;
|
|
||||||
} catch (err) {
|
|
||||||
error(
|
|
||||||
["converters", this.name],
|
|
||||||
`Error detecting audio tracks: ${err}`,
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async detectAudioChannels(ffmpeg: FFmpeg): Promise<number | null> {
|
|
||||||
const args = [
|
|
||||||
"-v",
|
|
||||||
"0",
|
|
||||||
"-select_streams",
|
|
||||||
"a",
|
|
||||||
"-show_entries",
|
|
||||||
"stream=channels",
|
|
||||||
"-of",
|
|
||||||
"compact=p=0:nk=1",
|
|
||||||
"input",
|
|
||||||
];
|
|
||||||
|
|
||||||
try {
|
|
||||||
let channels: number | null = null;
|
|
||||||
|
|
||||||
const channelsListener = (event: { message: string }) => {
|
|
||||||
if (channels !== null) return;
|
|
||||||
const n = parseInt(event.message.trim(), 10);
|
|
||||||
if (!n) return;
|
|
||||||
channels = n;
|
|
||||||
log(
|
|
||||||
["converters", this.name],
|
|
||||||
`Detected stream audio channels: ${channels}`,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
ffmpeg.on("log", channelsListener);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await ffmpeg.ffprobe.call(ffmpeg, args);
|
|
||||||
return channels;
|
|
||||||
} finally {
|
|
||||||
ffmpeg.off("log", channelsListener);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async buildConversionCommand(
|
private async buildConversionCommand(
|
||||||
ffmpeg: FFmpeg,
|
ffmpeg: FFmpeg,
|
||||||
input: VertFile,
|
input: VertFile,
|
||||||
|
|
@ -962,12 +857,12 @@ export type ConversionBitrate = (typeof CONVERSION_BITRATES)[number];
|
||||||
export const SAMPLE_RATES = [
|
export const SAMPLE_RATES = [
|
||||||
"auto",
|
"auto",
|
||||||
"custom",
|
"custom",
|
||||||
"48000",
|
48000,
|
||||||
"44100",
|
44100,
|
||||||
"32000",
|
32000,
|
||||||
"22050",
|
22050,
|
||||||
"16000",
|
16000,
|
||||||
"11025",
|
11025,
|
||||||
"8000",
|
8000,
|
||||||
] as const;
|
] as const;
|
||||||
export type SampleRate = (typeof SAMPLE_RATES)[number];
|
export type SampleRate = (typeof SAMPLE_RATES)[number];
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import type {
|
||||||
SettingDefinition,
|
SettingDefinition,
|
||||||
ConversionSettings,
|
ConversionSettings,
|
||||||
} from "$lib/types/conversion-settings";
|
} from "$lib/types/conversion-settings";
|
||||||
|
import { CONVERSION_BITRATES, SAMPLE_RATES } from "./ffmpeg.svelte";
|
||||||
|
|
||||||
interface UploadResponse {
|
interface UploadResponse {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -85,6 +86,15 @@ export type ConversionSpeed =
|
||||||
| "fast"
|
| "fast"
|
||||||
| "ultraFast";
|
| "ultraFast";
|
||||||
|
|
||||||
|
const vertdSpeedValues: ConversionSpeed[] = [
|
||||||
|
"verySlow",
|
||||||
|
"slower",
|
||||||
|
"slow",
|
||||||
|
"medium",
|
||||||
|
"fast",
|
||||||
|
"ultraFast",
|
||||||
|
];
|
||||||
|
|
||||||
interface StartJobMessage {
|
interface StartJobMessage {
|
||||||
type: "startJob";
|
type: "startJob";
|
||||||
data: {
|
data: {
|
||||||
|
|
@ -375,30 +385,128 @@ export class VertdConverter extends Converter {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const quality: SettingDefinition = {
|
const qualitySpeedRange: SettingDefinition = {
|
||||||
key: "vertdSpeed",
|
key: "vertdSpeedSlider",
|
||||||
label: m["convert.settings.video.speed"](),
|
label: m["convert.settings.video.speed"](),
|
||||||
type: "select",
|
description: m["convert.settings.video.speed_description"](),
|
||||||
default: "medium",
|
type: "range",
|
||||||
options: qualityOptions,
|
min: 0,
|
||||||
|
max: qualityOptions.length - 1,
|
||||||
|
step: 1,
|
||||||
|
default: 3,
|
||||||
|
options: qualityOptions.map((option, index) => ({
|
||||||
|
value: index,
|
||||||
|
label: option.label,
|
||||||
|
speedValue: option.value,
|
||||||
|
})),
|
||||||
|
forceFullWidth: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: for fps and resolution, set placeholder to detected values
|
|
||||||
const fps: SettingDefinition = {
|
const fps: SettingDefinition = {
|
||||||
key: "fps",
|
key: "fps",
|
||||||
label: m["convert.settings.video.fps"](),
|
label: m["convert.settings.video.fps"](),
|
||||||
|
type: "select",
|
||||||
|
default: "auto",
|
||||||
|
options: [
|
||||||
|
{ value: "auto", label: m["convert.settings.common.auto"]() },
|
||||||
|
{
|
||||||
|
value: "custom",
|
||||||
|
label: m["convert.settings.common.custom"](),
|
||||||
|
},
|
||||||
|
{ value: 24, label: "24" },
|
||||||
|
{ value: 30, label: "30" },
|
||||||
|
{ value: 60, label: "60" },
|
||||||
|
{ value: 120, label: "120" },
|
||||||
|
{ value: 144, label: "144" },
|
||||||
|
{ value: 240, label: "240" },
|
||||||
|
],
|
||||||
|
hasCustomInput: true,
|
||||||
|
customInputKey: "customFps",
|
||||||
placeholder: m["convert.settings.video.fps_placeholder"](),
|
placeholder: m["convert.settings.video.fps_placeholder"](),
|
||||||
type: "number",
|
|
||||||
min: 1,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const resolution: SettingDefinition = {
|
const resolution: SettingDefinition = {
|
||||||
key: "resolution",
|
key: "resolution",
|
||||||
label: m["convert.settings.video.resolution"](),
|
label: m["convert.settings.video.resolution"](),
|
||||||
|
type: "select",
|
||||||
|
default: "auto",
|
||||||
|
options: [
|
||||||
|
{ value: "auto", label: m["convert.settings.common.auto"]() },
|
||||||
|
{
|
||||||
|
value: "custom",
|
||||||
|
label: m["convert.settings.common.custom"](),
|
||||||
|
},
|
||||||
|
{ value: "426x240", label: "426x240" },
|
||||||
|
{ value: "640x360", label: "640x360" },
|
||||||
|
{ value: "854x480", label: "854x480" },
|
||||||
|
{ value: "1280x720", label: "1280x720" },
|
||||||
|
{ value: "1920x1080", label: "1920x1080" },
|
||||||
|
{ value: "2560x1440", label: "2560x1440" },
|
||||||
|
{ value: "3840x2160", label: "3840x2160" },
|
||||||
|
],
|
||||||
|
hasCustomInput: true,
|
||||||
|
customInputKey: "customResolution",
|
||||||
placeholder: m["convert.settings.video.resolution_placeholder"](),
|
placeholder: m["convert.settings.video.resolution_placeholder"](),
|
||||||
type: "string",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: allow CRF for consistent quality?
|
||||||
|
const videoBitrate: SettingDefinition = {
|
||||||
|
key: "videoBitrate",
|
||||||
|
label: m["convert.settings.video.video_bitrate"](),
|
||||||
|
type: "select",
|
||||||
|
default: "auto",
|
||||||
|
options: [
|
||||||
|
{ value: "auto", label: m["convert.settings.common.auto"]() },
|
||||||
|
{
|
||||||
|
value: "custom",
|
||||||
|
label: m["convert.settings.common.custom"](),
|
||||||
|
},
|
||||||
|
{ value: 1000, label: "1000 kbps" },
|
||||||
|
{ value: 2500, label: "2500 kbps" },
|
||||||
|
{ value: 5000, label: "5000 kbps" },
|
||||||
|
{ value: 8000, label: "8000 kbps" },
|
||||||
|
{ value: 12000, label: "12000 kbps" },
|
||||||
|
{ value: 18000, label: "18000 kbps" },
|
||||||
|
],
|
||||||
|
hasCustomInput: true,
|
||||||
|
customInputKey: "customBitrate",
|
||||||
|
placeholder: m["convert.settings.video.bitrate_placeholder"](),
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* audio settings
|
||||||
|
*/
|
||||||
|
const audioBitrate: SettingDefinition = {
|
||||||
|
key: "audioBitrate",
|
||||||
|
label: m["convert.settings.video.audio_bitrate"](),
|
||||||
|
type: "select",
|
||||||
|
default: "auto",
|
||||||
|
options: CONVERSION_BITRATES.map((b) => ({
|
||||||
|
value: b,
|
||||||
|
label: b,
|
||||||
|
})),
|
||||||
|
hasCustomInput: true,
|
||||||
|
customInputKey: "customBitrate",
|
||||||
|
placeholder: m["convert.settings.audio.bitrate_placeholder"](),
|
||||||
|
};
|
||||||
|
|
||||||
|
const sampleRate: SettingDefinition = {
|
||||||
|
key: "sampleRate",
|
||||||
|
label: m["convert.settings.audio.sample_rate"](),
|
||||||
|
type: "select",
|
||||||
|
default: "auto",
|
||||||
|
options: SAMPLE_RATES.map((r) => ({
|
||||||
|
value: r,
|
||||||
|
label: r,
|
||||||
|
})),
|
||||||
|
hasCustomInput: true,
|
||||||
|
customInputKey: "customSampleRate",
|
||||||
|
placeholder: m["convert.settings.audio.sample_rate_placeholder"](),
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* common
|
||||||
|
*/
|
||||||
const metadata: SettingDefinition = {
|
const metadata: SettingDefinition = {
|
||||||
key: "metadata",
|
key: "metadata",
|
||||||
label: m["convert.settings.common.metadata"](),
|
label: m["convert.settings.common.metadata"](),
|
||||||
|
|
@ -408,9 +516,15 @@ export class VertdConverter extends Converter {
|
||||||
|
|
||||||
// trim/crop/rotate - also have another ui for this prob
|
// trim/crop/rotate - also have another ui for this prob
|
||||||
|
|
||||||
// import all audio settings?
|
return [
|
||||||
|
qualitySpeedRange,
|
||||||
return [quality, fps, resolution, metadata];
|
videoBitrate,
|
||||||
|
resolution,
|
||||||
|
fps,
|
||||||
|
metadata,
|
||||||
|
audioBitrate,
|
||||||
|
sampleRate,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getDefaultSettings(): Promise<ConversionSettings> {
|
public async getDefaultSettings(): Promise<ConversionSettings> {
|
||||||
|
|
@ -419,10 +533,19 @@ export class VertdConverter extends Converter {
|
||||||
settings.forEach((setting) => {
|
settings.forEach((setting) => {
|
||||||
defaults[setting.key] = setting.default;
|
defaults[setting.key] = setting.default;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (defaults.vertdSpeedSlider !== undefined) {
|
||||||
|
const sliderIndex = defaults.vertdSpeedSlider as number;
|
||||||
|
defaults.vertdSpeed = vertdSpeedValues[sliderIndex];
|
||||||
|
}
|
||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async convert(input: VertFile, to: string, settings: ConversionSettings): Promise<VertFile> {
|
public async convert(
|
||||||
|
input: VertFile,
|
||||||
|
to: string,
|
||||||
|
settings: ConversionSettings,
|
||||||
|
): Promise<VertFile> {
|
||||||
if (to.startsWith(".")) to = to.slice(1);
|
if (to.startsWith(".")) to = to.slice(1);
|
||||||
|
|
||||||
let fileUpload = input;
|
let fileUpload = input;
|
||||||
|
|
@ -481,7 +604,14 @@ export class VertdConverter extends Converter {
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
const speed = Settings.instance.settings.vertdSpeed;
|
let speed = settings.vertdSpeed as ConversionSpeed | undefined;
|
||||||
|
const sliderIndex = settings.vertdSpeedSlider as
|
||||||
|
| number
|
||||||
|
| undefined;
|
||||||
|
if (sliderIndex !== undefined) {
|
||||||
|
speed = vertdSpeedValues[sliderIndex] || speed;
|
||||||
|
}
|
||||||
|
if (!speed) speed = Settings.instance.settings.vertdSpeed;
|
||||||
const keepMetadata = Settings.instance.settings.metadata;
|
const keepMetadata = Settings.instance.settings.metadata;
|
||||||
this.log(
|
this.log(
|
||||||
`opened ws connection to vertd for file ${input.name}`,
|
`opened ws connection to vertd for file ${input.name}`,
|
||||||
|
|
|
||||||
|
|
@ -413,6 +413,36 @@ body {
|
||||||
@apply outline outline-accent outline-2;
|
@apply outline outline-accent outline-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="range"].range-slider {
|
||||||
|
@apply w-full h-[10px] appearance-none bg-transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"].range-slider:focus {
|
||||||
|
@apply outline-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for some reason, thumb and tracks behave differently in webkit (chromium) and firefox
|
||||||
|
// so i had to do some manual adjustments to get them similar :sob: -maya
|
||||||
|
input[type="range"].range-slider::-webkit-slider-runnable-track {
|
||||||
|
@apply h-[10px] rounded-full bg-button;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"].range-slider::-webkit-slider-thumb {
|
||||||
|
@apply bg-panel w-[18px] h-[18px] -mt-1 rounded-full cursor-pointer shadow-md;
|
||||||
|
appearance: none;
|
||||||
|
border: 2px solid var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"].range-slider::-moz-range-track {
|
||||||
|
@apply h-[10px] rounded-full bg-button;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="range"].range-slider::-moz-range-thumb {
|
||||||
|
@apply bg-panel border-2 border-accent w-4 h-4 rounded-full cursor-pointer shadow-md;
|
||||||
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
@apply border-separator;
|
@apply border-separator;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,21 @@
|
||||||
export type SettingType = "number" | "select" | "boolean" | "string" | "range";
|
export type SettingType = "number" | "select" | "boolean" | "string" | "range";
|
||||||
|
|
||||||
export interface SettingDefinition {
|
export interface SettingDefinition {
|
||||||
key: string;
|
key: string;
|
||||||
label: string;
|
label: string;
|
||||||
type: SettingType;
|
type: SettingType;
|
||||||
default?: any;
|
default?: any;
|
||||||
placeholder?: any;
|
placeholder?: any;
|
||||||
min?: number;
|
min?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
step?: number;
|
step?: number;
|
||||||
options?: Array<{ value: any; label: any }>; // for select types
|
options?: Array<{ value: any; label: any; speedValue?: any }>; // for select/range types
|
||||||
description?: string;
|
description?: string;
|
||||||
hasCustomInput?: boolean; // for select types with a "custom" option
|
hasCustomInput?: boolean; // for select types with a "custom" option
|
||||||
customInputKey?: string; // key to use for custom input value in settings object
|
customInputKey?: string; // key to use for custom input value in settings object
|
||||||
|
forceFullWidth?: boolean; // force setting to take up full width (usually grid 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ConversionSettings {
|
export interface ConversionSettings {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue