This commit is contained in:
parent
fbecacf0b6
commit
6885b808f5
|
@ -1 +1,8 @@
|
||||||
# Interactive FFMPEG Command Generator
|
# Interactive FFMPEG Command Generator
|
||||||
|
|
||||||
|
### Todo:
|
||||||
|
|
||||||
|
- Type ahead for filters
|
||||||
|
- Sep audio/video filters
|
||||||
|
- Preview videos
|
||||||
|
- FFMPEG Wasm
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "ffmpeg-builder",
|
"name": "ffmpeg-builder",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@leeoniya/ufuzzy": "^1.0.8"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/vite-plugin-svelte": "^2.4.2",
|
"@sveltejs/vite-plugin-svelte": "^2.4.2",
|
||||||
"svelte": "^4.0.5",
|
"svelte": "^4.0.5",
|
||||||
|
@ -426,6 +429,11 @@
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@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": {
|
"node_modules/@sveltejs/vite-plugin-svelte": {
|
||||||
"version": "2.4.5",
|
"version": "2.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.5.tgz",
|
||||||
|
|
|
@ -12,5 +12,8 @@
|
||||||
"@sveltejs/vite-plugin-svelte": "^2.4.2",
|
"@sveltejs/vite-plugin-svelte": "^2.4.2",
|
||||||
"svelte": "^4.0.5",
|
"svelte": "^4.0.5",
|
||||||
"vite": "^4.4.5"
|
"vite": "^4.4.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@leeoniya/ufuzzy": "^1.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,17 @@
|
||||||
import Input from "./Input.svelte";
|
import Input from "./Input.svelte";
|
||||||
import Output from "./Output.svelte";
|
import Output from "./Output.svelte";
|
||||||
import Filter from "./Filter.svelte";
|
import Filter from "./Filter.svelte";
|
||||||
|
import FilterPicker from "./FilterPicker.svelte";
|
||||||
|
import Modal from "./Modal.svelte";
|
||||||
|
|
||||||
|
let showFilterModal = false;
|
||||||
|
|
||||||
function newInput() {
|
function newInput() {
|
||||||
$inputs = [...$inputs, ""];
|
$inputs = [...$inputs, ""];
|
||||||
}
|
}
|
||||||
|
|
||||||
function newFilter() {
|
function newFilter() {
|
||||||
$filters = [...$filters, {}];
|
showFilterModal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let command = "";
|
let command = "";
|
||||||
|
@ -70,6 +74,9 @@
|
||||||
<!-- {JSON.stringify($filters)} -->
|
<!-- {JSON.stringify($filters)} -->
|
||||||
<h3>Filters</h3>
|
<h3>Filters</h3>
|
||||||
<button on:click={newFilter}>Add Filter</button>
|
<button on:click={newFilter}>Add Filter</button>
|
||||||
|
<Modal bind:showModal={showFilterModal}>
|
||||||
|
<FilterPicker bind:showFilterModal />
|
||||||
|
</Modal>
|
||||||
<div class="filters-holder">
|
<div class="filters-holder">
|
||||||
{#each $filters as f, index}
|
{#each $filters as f, index}
|
||||||
<div class="filter">
|
<div class="filter">
|
||||||
|
|
|
@ -8,78 +8,50 @@
|
||||||
description: "",
|
description: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
let filterIndex;
|
export let index;
|
||||||
export let index;
|
|
||||||
console.log(index);
|
|
||||||
|
|
||||||
function remove() {
|
function remove() {
|
||||||
console.log(index);
|
$filters.splice(index, 1);
|
||||||
$filters.splice(index, 1);
|
$filters = $filters;
|
||||||
$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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$: current = FILTERS[filterIndex];
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="filter-holder">
|
<div class="filter-holder">
|
||||||
<div class="head">
|
<div class="head">
|
||||||
<select bind:value={filterIndex} on:change={changeFilter}>
|
<div class="name">{filter.name}</div>
|
||||||
<option />
|
|
||||||
{#each FILTERS as f, i}
|
|
||||||
<option value={i}>{f.name}</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
<button on:click={remove}>X</button>
|
<button on:click={remove}>X</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if filter.name}
|
<div class="description">{filter.description}</div>
|
||||||
<div class="description">{current.description}</div>
|
|
||||||
|
|
||||||
{#if filter.params.length > 0}
|
{#if filter.params.length > 0}
|
||||||
<div class="options">
|
<div class="options">
|
||||||
{#each filter.params as p}
|
{#each filter.params as p}
|
||||||
<!-- <p>{p.min} {p.max} {p.default}</p> -->
|
<div class="param-holder">
|
||||||
<div class="param-holder">
|
<div class="param">
|
||||||
<div class="param">
|
<span class="p-name">{p.name}</span>
|
||||||
<span class="p-name">{p.name}</span>
|
<span class="p-value">
|
||||||
<span class="p-value">
|
{#if p.options && p.options.length > 0}
|
||||||
{#if p.options && p.options.length > 0}
|
<select bind:value={p.value}>
|
||||||
<select bind:value={p.value}>
|
{#each p.options as o}
|
||||||
{#each p.options as o}
|
<option value={o.value}
|
||||||
<option value={o.value}>{o.value}
|
>{o.value}
|
||||||
{#if o.desc}({o.desc}){/if}
|
{#if o.desc}({o.desc}){/if}
|
||||||
</option>
|
</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
|
{:else if p.type == "float" || p.type == "double" || p.type == "long" || p.type == "int"}
|
||||||
{:else}
|
<input type="range" min={p.min} max={p.max} bind:value={p.value} />
|
||||||
{#if p.type == "float" || p.type == "double" || p.type == "long" || p.type=="int"}
|
<input bind:value={p.value} />
|
||||||
<input type="range" min={p.min} max={p.max} bind:value={p.value} />
|
{:else}
|
||||||
<input bind:value={p.value} />
|
<input bind:value={p.value} />
|
||||||
{:else}
|
{/if}
|
||||||
<input bind:value={p.value} />
|
</span>
|
||||||
{/if}
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="p-description">{p.desc}</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
<div class="p-description">{p.desc}</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/each}
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -114,11 +86,11 @@
|
||||||
border-top: 1px solid #999;
|
border-top: 1px solid #999;
|
||||||
padding: 10px 0px;
|
padding: 10px 0px;
|
||||||
}
|
}
|
||||||
.param-holder:last-child {
|
.param-holder:last-child {
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
.p-description {
|
.p-description {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
968
src/filters.json
968
src/filters.json
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue