file uploads
This commit is contained in:
parent
88f4aa920f
commit
2196c17e04
|
@ -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) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
485
src/stores.js
485
src/stores.js
|
@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue