playing with graphs

This commit is contained in:
Sam Lavigne 2023-08-22 12:05:49 -04:00
parent 9289f78f4d
commit 326aedb3fd
8 changed files with 247 additions and 27 deletions

10
package-lock.json generated
View File

@ -16,6 +16,7 @@
"@sveltejs/vite-plugin-svelte": "^2.4.2", "@sveltejs/vite-plugin-svelte": "^2.4.2",
"svelte": "^4.0.5", "svelte": "^4.0.5",
"svelte-dnd-action": "^0.9.25", "svelte-dnd-action": "^0.9.25",
"svelvet": "^8.1.0",
"uuid": "^9.0.0", "uuid": "^9.0.0",
"vite": "^4.4.5" "vite": "^4.4.5"
} }
@ -842,6 +843,15 @@
"svelte": "^3.19.0 || ^4.0.0" "svelte": "^3.19.0 || ^4.0.0"
} }
}, },
"node_modules/svelvet": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/svelvet/-/svelvet-8.1.0.tgz",
"integrity": "sha512-rH67tgb7e2aTBQZBCW+V5hSvulLwvzBiOml9Dzdz2ATkgQr58mHi2WtlQFOOb+eZt6zH/J10a2MataC29Qdpuw==",
"dev": true,
"peerDependencies": {
"svelte": ">=3.59.2 || ^4.0.0"
}
},
"node_modules/uuid": { "node_modules/uuid": {
"version": "9.0.0", "version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",

View File

@ -12,6 +12,7 @@
"@sveltejs/vite-plugin-svelte": "^2.4.2", "@sveltejs/vite-plugin-svelte": "^2.4.2",
"svelte": "^4.0.5", "svelte": "^4.0.5",
"svelte-dnd-action": "^0.9.25", "svelte-dnd-action": "^0.9.25",
"svelvet": "^8.1.0",
"uuid": "^9.0.0", "uuid": "^9.0.0",
"vite": "^4.4.5" "vite": "^4.4.5"
}, },

View File

@ -1,10 +1,11 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { inputs, output, filters } from "./stores.js"; import { inputs, addInput, output, filters } from "./stores.js";
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 FilterPicker from "./FilterPicker.svelte";
import Graph from "./Graph.svelte";
import { FFmpeg } from "@ffmpeg/ffmpeg"; import { FFmpeg } from "@ffmpeg/ffmpeg";
import { fetchFile, toBlobURL } from "@ffmpeg/util"; import { fetchFile, toBlobURL } from "@ffmpeg/util";
import { dndzone } from "svelte-dnd-action"; import { dndzone } from "svelte-dnd-action";
@ -17,7 +18,7 @@
const ffmpeg = new FFmpeg(); const ffmpeg = new FFmpeg();
let command = ""; let command = "";
let videoValue = "/" + $inputs[0]; let videoValue = "/" + $inputs[0].name;
let ffmpegLoaded = false; let ffmpegLoaded = false;
let rendering = false; let rendering = false;
let log = ""; let log = "";
@ -25,7 +26,7 @@
let commandRef; let commandRef;
function newInput() { function newInput() {
$inputs = [...$inputs, "punch.mp4"]; addInput("punch.mp4");
} }
function render() { function render() {
@ -42,7 +43,7 @@
rendering = true; rendering = true;
try { try {
for (let vid of $inputs) { for (let vid of $inputs) {
await ffmpeg.writeFile(vid, await fetchFile("/" + vid)); await ffmpeg.writeFile(vid.name, await fetchFile("/" + vid.name));
} }
const clist = commandList(); const clist = commandList();
console.log(clist); console.log(clist);
@ -81,7 +82,8 @@
} }
function updateCommand() { function updateCommand() {
const cInputs = $inputs.map((i) => `-i ${i}`).join(" "); console.log($inputs);
const cInputs = $inputs.map((i) => `-i ${i.name}`).join(" ");
const cOutput = $output; const cOutput = $output;
@ -116,7 +118,7 @@
let command = []; let command = [];
for (let vid of $inputs) { for (let vid of $inputs) {
command.push("-i"); command.push("-i");
command.push(vid); command.push(vid.name);
} }
// const audioFilters = $filters.filter(f => f.type[0] === "A").map(makeFilterArgs); // const audioFilters = $filters.filter(f => f.type[0] === "A").map(makeFilterArgs);
// const videoFilters = $filters.filter(f => f.type[0] === "V").map(makeFilterArgs); // const videoFilters = $filters.filter(f => f.type[0] === "V").map(makeFilterArgs);
@ -179,7 +181,7 @@
<button on:click={newInput}>Add Input</button> <button on:click={newInput}>Add Input</button>
</div> </div>
{#each $inputs as inp, index} {#each $inputs as inp, index}
<Input bind:filename={inp} {index} /> <Input bind:filename={inp.name} id={inp.id} {index} />
{/each} {/each}
</section> </section>
@ -233,6 +235,10 @@
</div> </div>
</div> </div>
</section> </section>
<section class="graph">
<Graph />
</section>
</main> </main>
<style> <style>

View File

@ -1,5 +1,5 @@
<script> <script>
import { filters } from "./stores.js"; import { filters, removeFilter } from "./stores.js";
export let filter = { export let filter = {
name: "", name: "",
@ -10,9 +10,7 @@
let show = false; let show = false;
function remove() { function remove() {
const index = $filters.findIndex((f) => f.id === filter.id); removeFilter(filter.id);
$filters.splice(index, 1);
$filters = $filters;
} }
function reset() { function reset() {

View File

@ -1,8 +1,7 @@
<script> <script>
import uFuzzy from "@leeoniya/ufuzzy"; import uFuzzy from "@leeoniya/ufuzzy";
import { v4 as uuidv4 } from "uuid";
import FILTERS from "./filters.json"; import FILTERS from "./filters.json";
import { filters } from "./stores.js"; import { addFilter } from "./stores.js";
export let select = "video"; export let select = "video";
$: selectedFilters = selectFilters(select); $: selectedFilters = selectFilters(select);
@ -26,16 +25,7 @@
} }
function add(f) { function add(f) {
const newFilter = { ...f, filterId: f.id, id: uuidv4() }; addFilter(f);
if (f.params) {
newFilter.params = f.params.map((p) => {
p.value = null;
if (p.default != null) p.value = p.default;
return p;
});
}
$filters = [...$filters, newFilter];
console.log(newFilter);
} }
function update() { function update() {

164
src/Graph.svelte Normal file
View File

@ -0,0 +1,164 @@
<script>
import { inputs, output, filters } from "./stores.js";
import { Anchor, Node, Svelvet, Minimap, Controls } from "svelvet";
import { generateInput, generateOutput } from "svelvet";
function countInputs(f) {
const [ins, outs] = f.type.split("->");
if (ins == "N") return 1;
return ins.length;
}
function countCons(f) {
const [ins, outs] = f.type.split("->");
return { in: ins.split(""), out: outs.split("") };
}
function countOutputs(f) {
const [ins, outs] = f.type.split("->");
if (outs == "N") return 1;
return outs.length;
}
function onConnect(e) {
const sourceAnchor = e.detail.sourceAnchor;
const targetAnchor = e.detail.targetAnchor;
const sourceNode = e.detail.sourceNode;
const targetNode = e.detail.targetNode;
console.log(sourceNode.id, "->", targetNode.id)
console.log(sourceAnchor.id, "->", targetAnchor.id)
}
function onDisconnect(e) {
const sourceAnchor = e.detail.sourceAnchor;
const targetAnchor = e.detail.targetAnchor;
const sourceNode = e.detail.sourceNode;
const targetNode = e.detail.targetNode;
console.log(sourceNode.id, "-/>", targetNode.id)
console.log(sourceAnchor.id, "-/>", targetAnchor.id)
}
</script>
<Svelvet
id="my-canvas"
width={800}
height={500}
snapTo={25}
on:disconnection={onDisconnect}
on:connection={onConnect}
>
{#each $inputs as inp, index}
<Node inputs={0} outputs={2}>
<div class="node">
<div class="header">
{inp}
</div>
<slot />
</div>
<div class="output-anchors">
<Anchor id={"v" + index} let:linked let:connecting let:hovering output>
<div class:linked class:hovering class:connecting class="anchor video">v</div>
</Anchor>
<Anchor id={"a" + index} let:linked let:connecting let:hovering output>
<div class:linked class:hovering class:connecting class="anchor audio">a</div>
</Anchor>
</div>
</Node>
{/each}
{#each $filters as f, index}
<Node inputs={countInputs(f)} outputs={countOutputs(f)}>
<div class="node">
<div class="header">
{f.name}
</div>
<slot />
</div>
<div class="input-anchors">
{#each countCons(f).in as inp}
<Anchor let:linked let:connecting let:hovering input>
<div class:linked class:hovering class:connecting class="anchor in {inp}">{inp}</div>
</Anchor>
{/each}
</div>
<div class="output-anchors">
{#each countCons(f).out as out}
<Anchor let:linked let:connecting let:hovering output>
<div class:linked class:hovering class:connecting class="anchor in {out}">{out}</div>
</Anchor>
{/each}
</div>
</Node>
{/each}
<Node inputs={2} outputs="0" position={{ x: 600, y: 250 }}>
<div class="node">
<div class="header">
{$output}
</div>
<slot />
</div>
<div class="input-anchors">
<Anchor id={"output_video"} let:linked let:connecting let:hovering input>
<div class:linked class:hovering class:connecting class="anchor video">v</div>
</Anchor>
<Anchor id={"output_audio"} let:linked let:connecting let:hovering input>
<div class:linked class:hovering class:connecting class="anchor audio">a</div>
</Anchor>
</div>
</Node>
<Controls />
</Svelvet>
<style>
.node {
box-sizing: border-box;
width: fit-content;
height: fit-content;
position: relative;
pointer-events: auto;
display: flex;
flex-direction: column;
padding: 10px;
gap: 10px;
background-color: #fff;
border: 1px solid var(--b1);
font: 12px Times, serif;
box-shadow: none !important;
}
.output-anchors {
position: absolute;
right: -16px;
top: 0px;
display: flex;
flex-direction: column;
gap: 2px;
}
.input-anchors {
position: absolute;
left: -16px;
top: 0px;
display: flex;
flex-direction: column;
gap: 2px;
}
.anchor {
background-color: #fff;
font-size: 12px;
text-align: center;
line-height: 12px;
width: 14px;
height: 14px;
font-family: Times, serif;
border: solid 1px white;
border-color: var(--b1);
}
.hovering {
scale: 1.2;
}
.linked {
background-color: rgb(17, 214, 17) !important;
}
.connecting {
background-color: goldenrod;
}
</style>

View File

@ -1,11 +1,12 @@
<script> <script>
import { inputs } from "./stores.js"; import { removeInput } from "./stores.js";
export let filename = "punch.mp4"; export let filename = "punch.mp4";
export let id;
export let index; export let index;
function remove() { function remove() {
$inputs.splice(index, 1); console.log(id);
$inputs = $inputs; removeInput(id);
} }
</script> </script>

View File

@ -1,5 +1,55 @@
import { v4 as uuidv4 } from "uuid";
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
export const inputs = writable(["punch.mp4"]); export const inputs = writable([{name: "punch.mp4", id: uuidv4()}]);
export const output = writable("out.mp4"); export const output = writable("out.mp4");
export const filters = writable([]); export const filters = writable([]);
export function addFilter(f) {
const newFilter = { ...f, filterId: f.id, id: uuidv4() };
if (f.params) {
newFilter.params = f.params.map((p) => {
p.value = null;
if (p.default != null) p.value = p.default;
return p;
});
}
filters.update((filts) => {
filts.push(newFilter)
return filts;
})
}
export function removeFilter(id) {
filters.update((filts) => {
const index = filts.findIndex((f) => f.id === id);
filts.splice(index, 1);
return filts;
});
}
export function addOutput(f) {
}
export function removeOutput(f) {
}
export function addInput(f) {
const newInput = {name: f, id: uuidv4()}
inputs.update((inps) => {
inps.push(newInput);
return inps;
});
}
export function removeInput(id) {
inputs.update((inps) => {
const index = inps.findIndex((f) => f.id === id);
inps.splice(index, 1);
return inps;
});
}