This commit is contained in:
Sam Lavigne 2023-08-18 18:59:16 -04:00
parent fbecacf0b6
commit 6885b808f5
8 changed files with 684 additions and 550 deletions

View File

@ -1 +1,8 @@
# Interactive FFMPEG Command Generator
### Todo:
- Type ahead for filters
- Sep audio/video filters
- Preview videos
- FFMPEG Wasm

8
package-lock.json generated
View File

@ -7,6 +7,9 @@
"": {
"name": "ffmpeg-builder",
"version": "0.0.0",
"dependencies": {
"@leeoniya/ufuzzy": "^1.0.8"
},
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.4.2",
"svelte": "^4.0.5",
@ -426,6 +429,11 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@leeoniya/ufuzzy": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@leeoniya/ufuzzy/-/ufuzzy-1.0.8.tgz",
"integrity": "sha512-HQ6aJlYpWLq1f9AiApJl0aOIXlJUtuhBOYfSfv5rt3XNYkCBveojtnL6FvOVpJ2gEJ2wqgMW8xOHkLVYAbXghg=="
},
"node_modules/@sveltejs/vite-plugin-svelte": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.5.tgz",

View File

@ -12,5 +12,8 @@
"@sveltejs/vite-plugin-svelte": "^2.4.2",
"svelte": "^4.0.5",
"vite": "^4.4.5"
},
"dependencies": {
"@leeoniya/ufuzzy": "^1.0.8"
}
}

View File

@ -4,13 +4,17 @@
import Input from "./Input.svelte";
import Output from "./Output.svelte";
import Filter from "./Filter.svelte";
import FilterPicker from "./FilterPicker.svelte";
import Modal from "./Modal.svelte";
let showFilterModal = false;
function newInput() {
$inputs = [...$inputs, ""];
}
function newFilter() {
$filters = [...$filters, {}];
showFilterModal = true;
}
let command = "";
@ -70,6 +74,9 @@
<!-- {JSON.stringify($filters)} -->
<h3>Filters</h3>
<button on:click={newFilter}>Add Filter</button>
<Modal bind:showModal={showFilterModal}>
<FilterPicker bind:showFilterModal />
</Modal>
<div class="filters-holder">
{#each $filters as f, index}
<div class="filter">

View File

@ -8,78 +8,50 @@
description: "",
};
let filterIndex;
export let index;
console.log(index);
export let index;
function remove() {
console.log(index);
$filters.splice(index, 1);
$filters = $filters;
}
function changeFilter() {
filter = {
name: current.name,
description: current.description,
params: current.params.map((p) => {
p.value = null;
if (p.default != null) p.value = p.default;
return p;
}),
};
console.log(filter);
function remove() {
$filters.splice(index, 1);
$filters = $filters;
}
$: current = FILTERS[filterIndex];
</script>
<div class="filter-holder">
<div class="head">
<select bind:value={filterIndex} on:change={changeFilter}>
<option />
{#each FILTERS as f, i}
<option value={i}>{f.name}</option>
{/each}
</select>
<div class="name">{filter.name}</div>
<button on:click={remove}>X</button>
</div>
{#if filter.name}
<div class="description">{current.description}</div>
<div class="description">{filter.description}</div>
{#if filter.params.length > 0}
<div class="options">
{#each filter.params as p}
<!-- <p>{p.min} {p.max} {p.default}</p> -->
<div class="param-holder">
<div class="param">
<span class="p-name">{p.name}</span>
<span class="p-value">
{#if p.options && p.options.length > 0}
<select bind:value={p.value}>
{#each p.options as o}
<option value={o.value}>{o.value}
{#if o.desc}({o.desc}){/if}
</option>
{/each}
</select>
{:else}
{#if p.type == "float" || p.type == "double" || p.type == "long" || p.type=="int"}
<input type="range" min={p.min} max={p.max} bind:value={p.value} />
<input bind:value={p.value} />
{:else}
<input bind:value={p.value} />
{/if}
{/if}
</span>
</div>
<div class="p-description">{p.desc}</div>
{#if filter.params.length > 0}
<div class="options">
{#each filter.params as p}
<div class="param-holder">
<div class="param">
<span class="p-name">{p.name}</span>
<span class="p-value">
{#if p.options && p.options.length > 0}
<select bind:value={p.value}>
{#each p.options as o}
<option value={o.value}
>{o.value}
{#if o.desc}({o.desc}){/if}
</option>
{/each}
</select>
{:else if p.type == "float" || p.type == "double" || p.type == "long" || p.type == "int"}
<input type="range" min={p.min} max={p.max} bind:value={p.value} />
<input bind:value={p.value} />
{:else}
<input bind:value={p.value} />
{/if}
</span>
</div>
{/each}
</div>
{/if}
<div class="p-description">{p.desc}</div>
</div>
{/each}
</div>
{/if}
</div>
@ -114,11 +86,11 @@
border-top: 1px solid #999;
padding: 10px 0px;
}
.param-holder:last-child {
padding-bottom: 0;
}
.param-holder:last-child {
padding-bottom: 0;
}
.p-description {
opacity: 0.8;
opacity: 0.8;
font-size: 0.9em;
}

74
src/FilterPicker.svelte Normal file
View File

@ -0,0 +1,74 @@
<script>
import uFuzzy from "@leeoniya/ufuzzy";
import FILTERS from "./filters.json";
import { filters } from "./stores.js";
let allfilters = [...FILTERS];
let q = "";
export let showFilterModal;
const uf = new uFuzzy();
function add(f) {
const newFilter = { ...f };
newFilter.params = f.params.map((p) => {
p.value = null;
if (p.default != null) p.value = p.default;
return p;
});
$filters = [...$filters, newFilter];
showFilterModal = false;
console.log(newFilter);
}
function update() {
let newFilters = [];
const [idxs, info, order] = uf.search(
FILTERS.map((m) => m.name + " " + m.description),
q
);
if (idxs) {
for (let i of idxs) {
newFilters.push(FILTERS[i]);
}
allfilters = newFilters;
} else {
allfilters = FILTERS;
}
}
</script>
<div class="holder">
<div class="search">
<input placeholder="Search Filters" on:keyup={update} bind:value={q} />
</div>
<div class="all-filters">
{#each allfilters as f}
<div class="filter" on:click={() => add(f)}>
<div class="name">{f.name} <span class="type">{f.type}</span></div>
<div class="desc">{f.description}</div>
</div>
{/each}
</div>
</div>
<style>
.holder {
display: flex;
flex-direction: column;
height: 400px;
}
.filter {
border-bottom: 1px solid #000;
padding: 10px 0px;
cursor: pointer;
}
.all-filters {
flex: 1;
overflow: scroll;
}
.desc {
font-size: 0.9em;
}
</style>

63
src/Modal.svelte Normal file
View File

@ -0,0 +1,63 @@
<script>
export let showModal; // boolean
let dialog; // HTMLDialogElement
$: if (dialog && showModal) dialog.showModal();
</script>
<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-noninteractive-element-interactions -->
<dialog
bind:this={dialog}
on:close={() => (showModal = false)}
on:click|self={() => dialog.close()}
>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div on:click|stopPropagation>
<slot name="header" />
<slot />
<!-- svelte-ignore a11y-autofocus -->
<button autofocus on:click={() => dialog.close()}>Close</button>
</div>
</dialog>
<style>
dialog {
max-width: 600px;
min-width: 600px;
border-radius: 0.2em;
border: none;
padding: 0;
}
dialog::backdrop {
background: rgba(0, 0, 0, 0.3);
}
dialog > div {
padding: 1em;
}
dialog[open] {
animation: zoom 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes zoom {
from {
transform: scale(0.95);
}
to {
transform: scale(1);
}
}
dialog[open]::backdrop {
animation: fade 0.2s ease-out;
}
@keyframes fade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
button {
display: block;
}
</style>

File diff suppressed because it is too large Load Diff