file uploads

This commit is contained in:
Sam Lavigne 2023-08-30 18:01:10 -04:00
parent 88f4aa920f
commit 2196c17e04
4 changed files with 341 additions and 315 deletions

View File

@ -1,6 +1,15 @@
<script> <script>
import { onMount } from "svelte"; import { onMount } from "svelte";
import { selectedFilter, nodes, inputs, outputs, previewCommand } from "./stores.js"; import {
selectedFilter,
nodes,
inputs,
outputs,
auto,
addNode,
inputNames,
previewCommand,
} from "./stores.js";
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 Graph from "./Graph.svelte";
@ -9,9 +18,7 @@
const isChrome = navigator.userAgent.match(/chrome|chromium|crios/i); const isChrome = navigator.userAgent.match(/chrome|chromium|crios/i);
const baseURL = `https://unpkg.com/@ffmpeg/core${!isChrome ? "-mt" : ""}@0.12.2/dist/esm`; const baseURL = `https://unpkg.com/@ffmpeg/core${!isChrome ? "-mt" : ""}@0.12.2/dist/esm`;
// const baseURL = "";
const TIMEOUT = 40000; const TIMEOUT = 40000;
const ffmpeg = new FFmpeg(); const ffmpeg = new FFmpeg();
let videoValue = "/" + $inputs[0].name; let videoValue = "/" + $inputs[0].name;
@ -22,6 +29,18 @@
let commandRef; let commandRef;
let vidPlayerRef; let vidPlayerRef;
let renderProgress = 0; let renderProgress = 0;
let fileinput;
function addInput() {
addNode({ ...$inputNames[$inputNames.length - 1] }, "input");
}
async function onFileSelected(e) {
let vid = e.target.files[0];
await ffmpeg.writeFile(vid.name, await fetchFile(vid));
$inputNames.push({ name: vid.name, url: vid.name });
addInput();
}
function copyCommand() { function copyCommand() {
commandRef.select(); commandRef.select();
@ -38,20 +57,18 @@
try { try {
if (log.trim() != "") log += "\n\n"; if (log.trim() != "") log += "\n\n";
for (let vid of $inputs) { const fontNames = [
await ffmpeg.writeFile(vid.name, await fetchFile("/" + vid.name)); ...new Set([...$previewCommand.join(" ").matchAll(/\W([a-z]+\.ttf)/g)].map((f) => f[1])),
];
for (let f of fontNames) {
await ffmpeg.writeFile(f, await fetchFile("/" + f));
} }
const fontNames = [...new Set([...$previewCommand.join(" ").matchAll(/\W([a-z]+\.ttf)/g)].map(f => f[1]))]; let clist = [...$previewCommand];
clist.shift(); // remove "ffmpeg" from start of command
for (let f of fontNames) { clist.unshift("-hide_banner", "-loglevel", "error");
await ffmpeg.writeFile(f, await fetchFile("/" + f)); clist = clist.map((c) => c.replaceAll('"', ""));
}
let clist = [...$previewCommand];
clist.shift() // remove "ffmpeg" from start of command
clist.unshift("-hide_banner", "-loglevel", "error")
clist = clist.map(c => c.replaceAll('"', ""));
if (outname.endsWith("mp4")) { if (outname.endsWith("mp4")) {
clist.splice(clist.length - 1, 0, "-pix_fmt"); clist.splice(clist.length - 1, 0, "-pix_fmt");
clist.splice(clist.length - 1, 0, "yuv420p"); clist.splice(clist.length - 1, 0, "yuv420p");
@ -95,6 +112,9 @@
workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, "text/javascript"), workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, "text/javascript"),
}); });
} }
for (let vid of $inputNames) {
await ffmpeg.writeFile(vid.name, await fetchFile(vid.url));
}
ffmpegLoaded = true; ffmpegLoaded = true;
} }
@ -196,7 +216,23 @@
</section> </section>
<section class="graph"> <section class="graph">
<Graph /> <div class="graph-holder">
<div class="nav">
<button on:click={addInput}>Add Sample Input</button>
<button on:click={() => fileinput.click()}>Upload File</button>
<input
type="file"
accept="video/*"
on:change={(e) => onFileSelected(e)}
bind:this={fileinput}
style="display: none;"
/>
<label for="auto"
><input id="auto" type="checkbox" bind:checked={$auto} />Automatic Layout</label
>
</div>
<Graph {fetchFile} ffmepg={ffmpeg} />
</div>
</section> </section>
<section class="filter-editor"> <section class="filter-editor">
@ -383,6 +419,11 @@
ol li { ol li {
margin-bottom: 5px; margin-bottom: 5px;
} }
.graph-holder {
flex-direction: column;
display: flex;
flex: 1;
}
@media only screen and (max-width: 1400px) { @media only screen and (max-width: 1400px) {
} }

View File

@ -1,5 +1,5 @@
<script> <script>
import { addNode, nodes, edges, auto, selectedFilter } from "./stores.js"; import { nodes, edges, auto, selectedFilter } from "./stores.js";
import { import {
SvelteFlowProvider, SvelteFlowProvider,
SvelteFlow, SvelteFlow,
@ -7,7 +7,7 @@
Background, Background,
BackgroundVariant, BackgroundVariant,
} from "@xyflow/svelte"; } from "@xyflow/svelte";
import '@xyflow/svelte/dist/style.css'; import "@xyflow/svelte/dist/style.css";
import Node from "./Node.svelte"; import Node from "./Node.svelte";
import FitComp from "./FitComp.svelte"; import FitComp from "./FitComp.svelte";
import ButtonEdge from "./ButtonEdge.svelte"; import ButtonEdge from "./ButtonEdge.svelte";
@ -28,59 +28,41 @@
} }
} }
} }
function addInput() {
addNode({ name: "shoe.mp4" }, "input");
}
</script> </script>
<SvelteFlowProvider> <SvelteFlowProvider>
<div class="holder"> <FitComp />
<FitComp /> <div class="flow">
<div class="nav"> <div style="height: 100%; width: 100%">
<button on:click={addInput}>Add Input</button> <SvelteFlow
<label for="auto" nodesDraggable={!$auto}
><input id="auto" type="checkbox" bind:checked={$auto} />Automatic Layout</label nodesConnectable={!$auto}
panOnDrag={!$auto}
edgesUpdatable={!$auto}
connectOnClick={true}
nodesFocusable={false}
edgesFocusable={false}
elementsSelectable={false}
nodesSelectable={!$auto}
zoomOnScroll={!$auto}
deleteKey={0}
multiSelectionKeyCode={null}
on:nodeclick={onClick}
{nodeTypes}
{edgeTypes}
{nodes}
{edges}
snapGrid={[10, 10]}
fitView
> >
</div> <Controls />
<div class="flow"> <Background variant={BackgroundVariant.Dots} />
<div style="height: 100%; width: 100%"> </SvelteFlow>
<SvelteFlow
nodesDraggable={!$auto}
nodesConnectable={!$auto}
panOnDrag={!$auto}
edgesUpdatable={!$auto}
connectOnClick={true}
nodesFocusable={false}
edgesFocusable={false}
elementsSelectable={false}
nodesSelectable={!$auto}
zoomOnScroll={!$auto}
deleteKey={0}
multiSelectionKeyCode={null}
on:nodeclick={onClick}
{nodeTypes}
{edgeTypes}
{nodes}
{edges}
snapGrid={[10, 10]}
fitView
>
<Controls />
<Background variant={BackgroundVariant.Dots} />
</SvelteFlow>
</div>
</div> </div>
</div> </div>
</SvelteFlowProvider> </SvelteFlowProvider>
<style> <style>
.holder {
flex-direction: column;
display: flex;
flex: 1;
}
.flow { .flow {
flex: 1; flex: 1;
margin-top: 10px; margin-top: 10px;

View File

@ -5,17 +5,17 @@
export let data = { ext: "", nodeType: "", name: "", inputs: [], outputs: [] }; export let data = { ext: "", nodeType: "", name: "", inputs: [], outputs: [] };
export let id; export let id;
$: isSelected = $selectedFilter && $nodes[$selectedFilter] && $nodes[$selectedFilter].id === id; $: isSelected = $selectedFilter && $nodes[$selectedFilter] && $nodes[$selectedFilter].id === id;
function remove() { function remove() {
removeNode(id); removeNode(id);
} }
function resetNode() { function resetNode() {
// hack to deselect node and apply changes on chrome // hack to deselect node and apply changes on chrome
$nodes.find(n => n.id === id).selected = false; $nodes.find((n) => n.id === id).selected = false;
$nodes = $nodes $nodes = $nodes;
} }
function changeFile() { function changeFile() {
const newFile = OUTPUTNAMES.find((n) => n.name === data.name); const newFile = OUTPUTNAMES.find((n) => n.name === data.name);
@ -24,7 +24,7 @@
data.ext = newFile.ext; data.ext = newFile.ext;
data = data; data = data;
resetNode(); resetNode();
} }
</script> </script>
@ -81,7 +81,7 @@
} }
:global(.svelte-flow__node) { :global(.svelte-flow__node) {
box-shadow: 2px 2px 0px var(--b2); box-shadow: 2px 2px 0px var(--b2);
background-color: #fff; background-color: #fff;
} }
:global(.svelte-flow__node.selected) { :global(.svelte-flow__node.selected) {
outline: 1px solid var(--b1) !important; outline: 1px solid var(--b1) !important;
@ -94,7 +94,7 @@
background-color: white !important; background-color: white !important;
font-size: 10px; font-size: 10px;
line-height: 6px; line-height: 6px;
text-align: center; text-align: center;
} }
:global(.handle.a) { :global(.handle.a) {
/* border: 1px solid var(--b2) !important; */ /* border: 1px solid var(--b2) !important; */
@ -102,10 +102,10 @@
.node { .node {
padding: 5px; padding: 5px;
} }
.selected { .selected {
outline: 2px solid var(--b1); outline: 2px solid var(--b1);
background-color: var(--b2); background-color: var(--b2);
} }
.head { .head {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@ -7,322 +7,325 @@ export const auto = writable(true);
export const selectedFilter = writable(); export const selectedFilter = writable();
export const INPUTNAMES = [ export const INPUTNAMES = [
{name: "punch.mp4", ext: "mp4", outputs: ["v", "a"], inputs: []}, { name: "punch.mp4", url: "/punch.mp4", ext: "mp4", outputs: ["v", "a"], inputs: [] },
{name: "shoe.mp4", ext: "mp4", outputs: ["v", "a"], inputs: []} { name: "shoe.mp4", url: "/shoe.mp4", ext: "mp4", outputs: ["v", "a"], inputs: [] },
]; ];
export const OUTPUTNAMES = [ export const OUTPUTNAMES = [
{name: "out.mp4", ext: "mp4", inputs: ["v", "a"], outputs: []}, { name: "out.mp4", ext: "mp4", inputs: ["v", "a"], outputs: [] },
{name: "out.gif", ext: "gif", inputs: ["v"], outputs: []} { name: "out.gif", ext: "gif", inputs: ["v"], outputs: [] },
]; ];
addNode({...INPUTNAMES[0]}, "input"); export const inputNames = writable(INPUTNAMES);
addNode({...OUTPUTNAMES[0]}, "output"); export const outputNames = writable(OUTPUTNAMES);
addNode({ ...INPUTNAMES[0] }, "input");
addNode({ ...OUTPUTNAMES[0] }, "output");
export function makeFilterArgs(f) { export function makeFilterArgs(f) {
let fCommand = f.name; let fCommand = f.name;
if (f.params && f.params.length > 0) { if (f.params && f.params.length > 0) {
let params = f.params let params = f.params
.map((p) => { .map((p) => {
if (p.value === "" || p.value === null || p.value === p.default) return null; if (p.value === "" || p.value === null || p.value === p.default) return null;
return `${p.name}=${p.value}`; return `${p.name}=${p.value}`;
}) })
.filter((p) => p !== null) .filter((p) => p !== null)
.join(":"); .join(":");
if (params) fCommand += "=" + params; if (params) fCommand += "=" + params;
} }
return fCommand; return fCommand;
} }
export const previewCommand = derived([edges, nodes], ([$edges, $nodes]) => { export const previewCommand = derived([edges, nodes], ([$edges, $nodes]) => {
let finalCommand = []; let finalCommand = [];
let filtergraph = []; let filtergraph = [];
let labelIndex = 1; let labelIndex = 1;
const edgeIds = {}; const edgeIds = {};
const inputIdMap = {}; const inputIdMap = {};
const inputs = $nodes.filter((n) => n.nodeType === "input"); const inputs = $nodes.filter((n) => n.nodeType === "input");
const inputIds = inputs.map((n) => n.id); const inputIds = inputs.map((n) => n.id);
const inputEdges = $edges.filter((e) => inputIds.includes(e.source)); const inputEdges = $edges.filter((e) => inputIds.includes(e.source));
const outputs = $nodes.filter((n) => n.nodeType === "output"); const outputs = $nodes.filter((n) => n.nodeType === "output");
// create edge labels for each input // create edge labels for each input
inputs.forEach((inp, i) => (inputIdMap[inp.id] = i)); inputs.forEach((inp, i) => (inputIdMap[inp.id] = i));
// create edge labels for each filter // create edge labels for each filter
function traverseEdges(edg, type) { function traverseEdges(edg, type) {
const outEdges = $edges.filter((e) => e.source === edg.target && e.sourceHandle.includes(type)); const outEdges = $edges.filter((e) => e.source === edg.target && e.sourceHandle.includes(type));
let label; let label;
const inNode = $nodes.find((n) => n.id === edg.source); const inNode = $nodes.find((n) => n.id === edg.source);
const outNode = $nodes.find((n) => n.id === edg.target); const outNode = $nodes.find((n) => n.id === edg.target);
if (inNode && outNode) { if (inNode && outNode) {
if (inNode.nodeType === "input" && outNode.nodeType === "filter") { if (inNode.nodeType === "input" && outNode.nodeType === "filter") {
label = inputIdMap[inNode.id] + ":" + edg.sourceHandle[0]; label = inputIdMap[inNode.id] + ":" + edg.sourceHandle[0];
} else if (inNode.nodeType === "filter" && outNode.nodeType === "filter") { } else if (inNode.nodeType === "filter" && outNode.nodeType === "filter") {
label = labelIndex; label = labelIndex;
labelIndex++; labelIndex++;
} else if (inNode.nodeType === "filter" && outNode.nodeType === "output") { } else if (inNode.nodeType === "filter" && outNode.nodeType === "output") {
label = "out_" + type; label = "out_" + type;
} else if (inNode.nodeType === "input" && outNode.nodeType === "output") { } else if (inNode.nodeType === "input" && outNode.nodeType === "output") {
label = "FILTERLESS_" + inputIdMap[inNode.id] + ":" + type; label = "FILTERLESS_" + inputIdMap[inNode.id] + ":" + type;
} else { } else {
label = "UNKNOWN"; label = "UNKNOWN";
} }
edgeIds[edg.id] = label; edgeIds[edg.id] = label;
} }
for (let e2 of outEdges) { for (let e2 of outEdges) {
traverseEdges(e2, type); traverseEdges(e2, type);
} }
} }
for (let inp of inputEdges) { for (let inp of inputEdges) {
for (let t of ["v", "a"]) { for (let t of ["v", "a"]) {
if (inp.sourceHandle.includes(t)) { if (inp.sourceHandle.includes(t)) {
traverseEdges(inp, t); traverseEdges(inp, t);
} }
} }
} }
for (let n of $nodes.filter((n) => n.nodeType == "filter")) { for (let n of $nodes.filter((n) => n.nodeType == "filter")) {
let cmd = { weight: 0, in: [], out: [], cmd: "" }; let cmd = { weight: 0, in: [], out: [], cmd: "" };
const outs = $edges.filter((e) => e.source == n.id); const outs = $edges.filter((e) => e.source == n.id);
const ins = $edges.filter((e) => e.target == n.id); const ins = $edges.filter((e) => e.target == n.id);
if (outs.length == 0 && ins.length == 0) continue; if (outs.length == 0 && ins.length == 0) continue;
for (let i of ins) { for (let i of ins) {
const eid = edgeIds[i.id]; const eid = edgeIds[i.id];
if (typeof eid == "string" && eid.includes(":")) cmd.weight = -1000; if (typeof eid == "string" && eid.includes(":")) cmd.weight = -1000;
cmd.in.push(eid); cmd.in.push(eid);
} }
cmd.cmd = makeFilterArgs(n.data); cmd.cmd = makeFilterArgs(n.data);
for (let o of outs) { for (let o of outs) {
const eid = edgeIds[o.id]; const eid = edgeIds[o.id];
if (typeof eid == "string" && eid.includes("out")) cmd.weight = 1000; if (typeof eid == "string" && eid.includes("out")) cmd.weight = 1000;
else cmd.weight = eid; else cmd.weight = eid;
cmd.out.push(eid); cmd.out.push(eid);
} }
filtergraph.push(cmd); filtergraph.push(cmd);
} }
filtergraph.sort((a, b) => { filtergraph.sort((a, b) => {
return a.weight - b.weight; return a.weight - b.weight;
}); });
filtergraph = filtergraph.map((c) => { filtergraph = filtergraph.map((c) => {
return c.in.map((i) => `[${i}]`).join("") + c.cmd + c.out.map((i) => `[${i}]`).join(""); return c.in.map((i) => `[${i}]`).join("") + c.cmd + c.out.map((i) => `[${i}]`).join("");
}); });
finalCommand.push("ffmpeg"); finalCommand.push("ffmpeg");
for (let inp of inputs) { for (let inp of inputs) {
finalCommand.push("-i"); finalCommand.push("-i");
finalCommand.push(inp.data.name); finalCommand.push(inp.data.name);
} }
let hasVid = false; let hasVid = false;
let hasAud = false; let hasAud = false;
let mediaMaps = Object.values(edgeIds) let mediaMaps = Object.values(edgeIds)
.map((eid) => { .map((eid) => {
if (String(eid).includes("FILTERLESS")) { if (String(eid).includes("FILTERLESS")) {
return eid.split("_")[1]; return eid.split("_")[1];
} }
return null; return null;
}) })
.filter((m) => m !== null); .filter((m) => m !== null);
if (filtergraph.length > 0) { if (filtergraph.length > 0) {
let fg = `"${filtergraph.join(";")}"`; let fg = `"${filtergraph.join(";")}"`;
// this crazy thing replaces stuff like [1];[1] with a comma! // this crazy thing replaces stuff like [1];[1] with a comma!
fg = fg.replaceAll(/(\[\d+\]);\1(?!\[)/g, ","); fg = fg.replaceAll(/(\[\d+\]);\1(?!\[)/g, ",");
hasVid = fg.includes(":v]"); hasVid = fg.includes(":v]");
hasAud = fg.includes(":a]"); hasAud = fg.includes(":a]");
finalCommand.push("-filter_complex", fg); finalCommand.push("-filter_complex", fg);
if (hasAud) { if (hasAud) {
finalCommand.push("-map", '"[out_a]"'); finalCommand.push("-map", '"[out_a]"');
} }
if (hasVid) { if (hasVid) {
finalCommand.push("-map", '"[out_v]"'); finalCommand.push("-map", '"[out_v]"');
} }
for (let m of mediaMaps) { for (let m of mediaMaps) {
finalCommand.push("-map", m); finalCommand.push("-map", m);
} }
} }
for (let out of outputs) { for (let out of outputs) {
finalCommand.push(out.data.name); finalCommand.push(out.data.name);
} }
return finalCommand; return finalCommand;
}); });
export const inputs = derived(nodes, ($nodes) => { export const inputs = derived(nodes, ($nodes) => {
return $nodes.filter((n) => n.nodeType === "input").map((n) => n.data); return $nodes.filter((n) => n.nodeType === "input").map((n) => n.data);
}); });
export const outputs = derived(nodes, ($nodes) => { export const outputs = derived(nodes, ($nodes) => {
return $nodes.filter((n) => n.nodeType === "output").map((n) => n.data); return $nodes.filter((n) => n.nodeType === "output").map((n) => n.data);
}); });
nodes.subscribe(($nodes) => { nodes.subscribe(($nodes) => {
const isAuto = get(auto); const isAuto = get(auto);
if (!isAuto) return; if (!isAuto) return;
const outputNodes = $nodes.filter((n) => n.nodeType === "output"); const outputNodes = $nodes.filter((n) => n.nodeType === "output");
const inputNodes = $nodes.filter((n) => n.nodeType === "input"); const inputNodes = $nodes.filter((n) => n.nodeType === "input");
const filterNodes = $nodes.filter((n) => n.nodeType === "filter"); const filterNodes = $nodes.filter((n) => n.nodeType === "filter");
const orderedNodes = [].concat(filterNodes, outputNodes).filter((n) => n != undefined); const orderedNodes = [].concat(filterNodes, outputNodes).filter((n) => n != undefined);
const filled = []; const filled = [];
let newEdges = []; let newEdges = [];
function connectNode(n1, rest) { function connectNode(n1, rest) {
for (let i = 0; i < n1.data.outputs.length; i++) { for (let i = 0; i < n1.data.outputs.length; i++) {
const edgeType = n1.data.outputs[i]; const edgeType = n1.data.outputs[i];
for (let j = 0; j < rest.length; j++) { for (let j = 0; j < rest.length; j++) {
let found = false; let found = false;
const n2 = rest[j]; const n2 = rest[j];
for (let k = 0; k < n2.data.inputs.length; k++) { for (let k = 0; k < n2.data.inputs.length; k++) {
const targetEdgeType = n2.data.inputs[k]; const targetEdgeType = n2.data.inputs[k];
if (edgeType === targetEdgeType && !filled.includes(n2.id + k)) { if (edgeType === targetEdgeType && !filled.includes(n2.id + k)) {
newEdges.push({ newEdges.push({
id: uuidv4(), id: uuidv4(),
type: "default", type: "default",
source: n1.id, source: n1.id,
target: n2.id, target: n2.id,
sourceHandle: edgeType + "_" + i, sourceHandle: edgeType + "_" + i,
targetHandle: edgeType + "_" + k, targetHandle: edgeType + "_" + k,
}); });
filled.push(n2.id + k); filled.push(n2.id + k);
found = true; found = true;
break; break;
} }
} }
if (found) break; if (found) break;
} }
} }
const nextNode = rest.shift(); const nextNode = rest.shift();
if (nextNode) { if (nextNode) {
connectNode(nextNode, rest); connectNode(nextNode, rest);
} }
} }
for (let inpNode of inputNodes) { for (let inpNode of inputNodes) {
connectNode(inpNode, [...orderedNodes]); connectNode(inpNode, [...orderedNodes]);
} }
edges.set(newEdges); edges.set(newEdges);
}); });
export function addNode(_data, type) { export function addNode(_data, type) {
const data = JSON.parse(JSON.stringify(_data)); const data = JSON.parse(JSON.stringify(_data));
let ins = []; let ins = [];
let outs = []; let outs = [];
if (type === "input") { if (type === "input") {
outs = ["v", "a"]; outs = ["v", "a"];
} else if (type === "output") { } else if (type === "output") {
ins = ["v", "a"]; ins = ["v", "a"];
} else if (type === "filter") { } else if (type === "filter") {
const [_ins, _outs] = data.type.split("->"); const [_ins, _outs] = data.type.split("->");
ins = _ins.toLowerCase().split(""); ins = _ins.toLowerCase().split("");
outs = _outs.toLowerCase().split(""); outs = _outs.toLowerCase().split("");
if (data.params) { if (data.params) {
data.params = data.params.map((p) => { data.params = data.params.map((p) => {
p.value = null; p.value = null;
if (p.default != null) p.value = p.default; if (p.default != null) p.value = p.default;
return p; return p;
}); });
} }
} }
data.nodeType = type; data.nodeType = type;
data.inputs = ins; data.inputs = ins;
data.outputs = outs; data.outputs = outs;
let node = { let node = {
id: uuidv4(), id: uuidv4(),
type: "ffmpeg", type: "ffmpeg",
data: data, data: data,
nodeType: type, nodeType: type,
position: { x: 0, y: 0 }, position: { x: 0, y: 0 },
}; };
nodes.update((_nodes) => { nodes.update((_nodes) => {
_nodes.push(node); _nodes.push(node);
const isAuto = get(auto); const isAuto = get(auto);
if (isAuto) { if (isAuto) {
const w = 120; const w = 120;
const h = 50; const h = 50;
const margin = 50; const margin = 50;
let prev = null; let prev = null;
for (let n of _nodes) { for (let n of _nodes) {
if (n.nodeType === "input") { if (n.nodeType === "input") {
n.position = { x: 0, y: prev ? prev.position.y + h + margin : 0 }; n.position = { x: 0, y: prev ? prev.position.y + h + margin : 0 };
prev = n; prev = n;
} }
} }
for (let n of _nodes) { for (let n of _nodes) {
if (n.nodeType === "filter") { if (n.nodeType === "filter") {
let _w = prev && prev.width ? prev.width : w; let _w = prev && prev.width ? prev.width : w;
n.position = { x: prev ? prev.position.x + _w + margin : 0, y: -50 }; n.position = { x: prev ? prev.position.x + _w + margin : 0, y: -50 };
prev = n; prev = n;
} }
} }
for (let n of _nodes) { for (let n of _nodes) {
if (n.nodeType === "output") { if (n.nodeType === "output") {
let _w = prev && prev.width ? prev.width : w; let _w = prev && prev.width ? prev.width : w;
n.position = { x: prev ? prev.position.x + _w + margin : 0, y: 0 }; n.position = { x: prev ? prev.position.x + _w + margin : 0, y: 0 };
} }
} }
} }
if (node.nodeType === "filter") { if (node.nodeType === "filter") {
selectedFilter.set(_nodes.length - 1); selectedFilter.set(_nodes.length - 1);
} }
return _nodes; return _nodes;
}); });
} }
export function resetNodes() { export function resetNodes() {
nodes.set([]); nodes.set([]);
addNode({ name: "punch.mp4" }, "input"); addNode({ name: "punch.mp4" }, "input");
addNode({ name: "out.mp4" }, "output"); addNode({ name: "out.mp4" }, "output");
} }
export function removeNode(id) { export function removeNode(id) {
nodes.update((_nodes) => { nodes.update((_nodes) => {
const index = _nodes.findIndex((n) => n.id === id); const index = _nodes.findIndex((n) => n.id === id);
_nodes.splice(index, 1); _nodes.splice(index, 1);
return _nodes; return _nodes;
}); });
} }
export function removeEdge(id) { export function removeEdge(id) {
edges.update((_edges) => { edges.update((_edges) => {
const index = _edges.findIndex((e) => e.id === id); const index = _edges.findIndex((e) => e.id === id);
_edges.splice(index, 1); _edges.splice(index, 1);
return _edges; return _edges;
}); });
} }