2023-08-22 12:05:49 -04:00
|
|
|
import { v4 as uuidv4 } from "uuid";
|
2023-08-22 21:57:53 -04:00
|
|
|
import { writable, derived, get } from "svelte/store";
|
2023-08-22 12:05:49 -04:00
|
|
|
|
2023-08-22 21:57:53 -04:00
|
|
|
// export const inputs = writable([]);
|
|
|
|
// export const output = writable("out.mp4");
|
|
|
|
// export const filters = writable([]);
|
|
|
|
export const nodes = writable([]);
|
|
|
|
export const edges = writable([]);
|
2023-08-24 12:09:55 -04:00
|
|
|
export const auto = writable(true);
|
2023-08-24 23:17:49 -04:00
|
|
|
export const selectedFilter = writable();
|
2023-08-22 12:05:49 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
const PREFIX = "";
|
|
|
|
|
2023-08-22 21:57:53 -04:00
|
|
|
addNode({ name: "punch.mp4" }, "input");
|
|
|
|
addNode({ name: "out.mp4" }, "output");
|
2023-08-22 12:05:49 -04:00
|
|
|
|
2023-08-22 21:57:53 -04:00
|
|
|
function makeFilterArgs(f) {
|
|
|
|
let fCommand = f.name;
|
|
|
|
if (f.params && f.params.length > 0) {
|
|
|
|
let params = f.params
|
|
|
|
.map((p) => {
|
|
|
|
if (p.value === "" || p.value === null || p.value === p.default) return null;
|
|
|
|
return `${p.name}=${p.value}`;
|
|
|
|
})
|
|
|
|
.filter((p) => p !== null)
|
|
|
|
.join(":");
|
|
|
|
if (params) fCommand += "=" + params;
|
|
|
|
}
|
|
|
|
return fCommand;
|
2023-08-22 12:05:49 -04:00
|
|
|
}
|
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
export const previewCommandSvelvet = derived([edges, nodes], ([$edges, $nodes]) => {
|
2023-08-23 16:27:48 -04:00
|
|
|
// [0:v]f1=val,f2=val[out] -map out
|
|
|
|
// [0:v]f1=val,f2=val[1];[1][1:v]overlay[out] -map out`
|
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
let finalCommand = [];
|
2023-08-23 16:27:48 -04:00
|
|
|
|
2023-08-23 19:30:04 -04:00
|
|
|
let filtergraph = [];
|
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
const inputs = $nodes.filter((n) => n.nodeType == "input");
|
|
|
|
const outputs = $nodes.filter((n) => n.nodeType == "output");
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
const inputIds = {};
|
|
|
|
for (let i = 0; i < inputs.length; i++) {
|
|
|
|
const inp = inputs[i];
|
|
|
|
inputIds[inp.id] = i;
|
|
|
|
}
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
const edgeIds = {};
|
|
|
|
for (let i = 0; i < $edges.length; i++) {
|
|
|
|
const e = $edges[i];
|
|
|
|
edgeIds[e.id] = i + 1;
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
const source = $nodes.find((n) => PREFIX + n.id === e.source);
|
|
|
|
const target = $nodes.find((n) => PREFIX + n.id === e.target);
|
2023-08-24 12:09:55 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
if (!source || !target) continue;
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
if (source.nodeType === "input") {
|
|
|
|
if (e.sourceAnchor.startsWith("A-v")) {
|
|
|
|
edgeIds[e.id] = inputIds[source.id] + ":v";
|
|
|
|
}
|
|
|
|
if (e.sourceAnchor.startsWith("A-a")) {
|
|
|
|
edgeIds[e.id] = inputIds[source.id] + ":a";
|
|
|
|
}
|
|
|
|
}
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
if (target.nodeType === "output") {
|
|
|
|
edgeIds[e.id] = "out";
|
|
|
|
}
|
|
|
|
}
|
2023-08-23 19:30:04 -04:00
|
|
|
|
|
|
|
for (let n of $nodes.filter((n) => n.nodeType == "filter")) {
|
|
|
|
let cmd = "";
|
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
const outs = $edges.filter((e) => e.source == PREFIX + n.id);
|
|
|
|
const ins = $edges.filter((e) => e.target == PREFIX + n.id);
|
2023-08-23 19:30:04 -04:00
|
|
|
|
|
|
|
if (outs.length == 0 && ins.length == 0) continue;
|
|
|
|
|
|
|
|
for (let i of ins) {
|
2023-08-24 14:14:32 -04:00
|
|
|
const eid = edgeIds[i.id];
|
|
|
|
cmd += `[${eid}]`;
|
2023-08-23 19:30:04 -04:00
|
|
|
}
|
|
|
|
cmd += makeFilterArgs(n.data);
|
|
|
|
for (let o of outs) {
|
2023-08-24 14:14:32 -04:00
|
|
|
const eid = edgeIds[o.id];
|
|
|
|
cmd += `[${eid}]`;
|
2023-08-23 19:30:04 -04:00
|
|
|
}
|
2023-08-24 14:14:32 -04:00
|
|
|
filtergraph.push(cmd);
|
2023-08-23 19:30:04 -04:00
|
|
|
}
|
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
finalCommand.push("ffmpeg");
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
for (let inp of inputs) {
|
|
|
|
finalCommand.push("-i");
|
|
|
|
finalCommand.push(inp.data.name);
|
|
|
|
}
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
finalCommand.push("-filter_complex");
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
finalCommand.push('"' + filtergraph.join(";") + '"');
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
for (let out of outputs) {
|
|
|
|
finalCommand.push("-map");
|
|
|
|
finalCommand.push('"[out]"');
|
|
|
|
}
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
for (let inp of inputs) {
|
|
|
|
finalCommand.push("-map");
|
|
|
|
finalCommand.push(inputIds[inp.id] + ":a");
|
|
|
|
}
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
for (let out of outputs) {
|
|
|
|
finalCommand.push(out.data.name);
|
|
|
|
}
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
const entireCommand = finalCommand.join(" ");
|
|
|
|
return entireCommand;
|
2023-08-23 19:30:04 -04:00
|
|
|
});
|
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
export const previewCommand = derived([edges, nodes], ([$edges, $nodes]) => {
|
2023-08-23 19:30:04 -04:00
|
|
|
// [0:v]f1=val,f2=val[out] -map out
|
|
|
|
// [0:v]f1=val,f2=val[1];[1][1:v]overlay[out] -map out`
|
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
let hasVid = false;
|
|
|
|
let hasAud = false;
|
|
|
|
|
|
|
|
let finalCommand = [];
|
2023-08-23 19:30:04 -04:00
|
|
|
|
2023-08-23 16:27:48 -04:00
|
|
|
let filtergraph = [];
|
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
const inputs = $nodes.filter((n) => n.nodeType == "input");
|
|
|
|
const outputs = $nodes.filter((n) => n.nodeType == "output");
|
2023-08-23 16:27:48 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
const inputIds = {};
|
|
|
|
for (let i = 0; i < inputs.length; i++) {
|
|
|
|
const inp = inputs[i];
|
|
|
|
inputIds[inp.id] = i;
|
|
|
|
}
|
2023-08-23 16:27:48 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
const edgeIds = {};
|
|
|
|
for (let i = 0; i < $edges.length; i++) {
|
|
|
|
const e = $edges[i];
|
|
|
|
edgeIds[e.id] = i + 1;
|
|
|
|
|
|
|
|
const source = $nodes.find((n) => n.id === e.source);
|
|
|
|
const target = $nodes.find((n) => n.id === e.target);
|
|
|
|
|
2023-08-24 23:17:49 -04:00
|
|
|
if (source && target) {
|
|
|
|
|
|
|
|
if (source.nodeType === "input") {
|
|
|
|
if (e.sourceHandle.includes("v")) {
|
|
|
|
edgeIds[e.id] = inputIds[source.id] + ":v";
|
|
|
|
}
|
|
|
|
if (e.sourceHandle.includes("a")) {
|
|
|
|
edgeIds[e.id] = inputIds[source.id] + ":a";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target.nodeType === "output") {
|
|
|
|
const outType = e.targetHandle.includes("a") ? "aud_out" : "vid_out";
|
|
|
|
edgeIds[e.id] = outType;
|
|
|
|
}
|
|
|
|
}
|
2023-08-24 14:14:32 -04:00
|
|
|
}
|
2023-08-23 16:27:48 -04:00
|
|
|
|
|
|
|
for (let n of $nodes.filter((n) => n.nodeType == "filter")) {
|
|
|
|
let cmd = "";
|
|
|
|
|
|
|
|
const outs = $edges.filter((e) => e.source == n.id);
|
|
|
|
const ins = $edges.filter((e) => e.target == n.id);
|
|
|
|
|
|
|
|
if (outs.length == 0 && ins.length == 0) continue;
|
|
|
|
|
|
|
|
for (let i of ins) {
|
2023-08-24 14:14:32 -04:00
|
|
|
const eid = edgeIds[i.id];
|
|
|
|
cmd += `[${eid}]`;
|
2023-08-23 16:27:48 -04:00
|
|
|
}
|
|
|
|
cmd += makeFilterArgs(n.data);
|
|
|
|
for (let o of outs) {
|
2023-08-24 14:14:32 -04:00
|
|
|
const eid = edgeIds[o.id];
|
|
|
|
cmd += `[${eid}]`;
|
2023-08-23 16:27:48 -04:00
|
|
|
}
|
2023-08-24 14:14:32 -04:00
|
|
|
filtergraph.push(cmd);
|
2023-08-23 16:27:48 -04:00
|
|
|
}
|
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
finalCommand.push("ffmpeg");
|
2023-08-23 16:27:48 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
for (let inp of inputs) {
|
|
|
|
finalCommand.push("-i");
|
|
|
|
finalCommand.push(inp.data.name);
|
|
|
|
}
|
2023-08-23 16:27:48 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
if (filtergraph.length > 0) {
|
2023-08-24 14:47:22 -04:00
|
|
|
const fg = '"' + filtergraph.join(";") + '"';
|
|
|
|
hasVid = fg.includes(":v]");
|
|
|
|
hasAud = fg.includes(":a]");
|
2023-08-23 16:27:48 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
finalCommand.push("-filter_complex");
|
|
|
|
finalCommand.push(fg);
|
2023-08-23 16:27:48 -04:00
|
|
|
|
2023-08-24 14:47:22 -04:00
|
|
|
finalCommand.push("-map");
|
2023-08-24 14:14:32 -04:00
|
|
|
if (hasAud) {
|
|
|
|
finalCommand.push('"[aud_out]"');
|
|
|
|
} else {
|
2023-08-24 14:47:22 -04:00
|
|
|
finalCommand.push("0:a");
|
|
|
|
}
|
2023-08-23 16:27:48 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
if (hasVid) {
|
|
|
|
finalCommand.push("-map");
|
|
|
|
finalCommand.push('"[vid_out]"');
|
|
|
|
}
|
|
|
|
}
|
2023-08-23 16:27:48 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
// for (let inp of inputs) {
|
|
|
|
// finalCommand.push("-map");
|
|
|
|
// finalCommand.push(inputIds[inp.id] + ":a");
|
|
|
|
// }
|
|
|
|
|
|
|
|
for (let out of outputs) {
|
|
|
|
finalCommand.push(out.data.name);
|
|
|
|
}
|
2023-08-23 16:27:48 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
const entireCommand = finalCommand.join(" ");
|
|
|
|
return entireCommand;
|
2023-08-22 21:57:53 -04:00
|
|
|
});
|
2023-08-22 12:05:49 -04:00
|
|
|
|
2023-08-23 16:27:48 -04:00
|
|
|
export const previewCommandOld = derived(nodes, ($nodes) => {
|
2023-08-22 21:57:53 -04:00
|
|
|
const cInputs = $nodes
|
|
|
|
.filter((n) => n.nodeType === "input")
|
|
|
|
.map((i) => `-i ${i.data.name}`)
|
|
|
|
.join(" ");
|
2023-08-22 12:05:49 -04:00
|
|
|
|
2023-08-22 21:57:53 -04:00
|
|
|
const cOutput = $nodes
|
|
|
|
.filter((n) => n.nodeType === "output")
|
|
|
|
.map((i) => `${i.data.name}`)
|
|
|
|
.join(" ");
|
2023-08-22 12:05:49 -04:00
|
|
|
|
2023-08-22 21:57:53 -04:00
|
|
|
const cFilters = $nodes
|
|
|
|
.filter((n) => n.nodeType === "filter")
|
|
|
|
.map((n) => n.data)
|
|
|
|
.map(makeFilterArgs)
|
|
|
|
.join(",");
|
|
|
|
|
|
|
|
let out = `ffmpeg ${cInputs}`;
|
|
|
|
|
|
|
|
if (cFilters) out += ` -filter_complex "${cFilters}"`;
|
|
|
|
|
|
|
|
out += ` ${cOutput}`;
|
|
|
|
return out;
|
|
|
|
});
|
|
|
|
|
|
|
|
export const inputs = derived(nodes, ($nodes) => {
|
|
|
|
return $nodes.filter((n) => n.nodeType === "input").map((n) => n.data);
|
|
|
|
});
|
|
|
|
|
|
|
|
export const filters = derived(nodes, ($nodes) => {
|
|
|
|
return $nodes.filter((n) => n.nodeType === "filter").map((n) => n.data);
|
|
|
|
});
|
|
|
|
|
|
|
|
export const output = derived(nodes, ($nodes) => {
|
|
|
|
return $nodes.filter((n) => n.nodeType === "output").map((n) => n.data);
|
|
|
|
});
|
2023-08-22 12:05:49 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
nodes.subscribe(($nodes) => {
|
|
|
|
const isAuto = get(auto);
|
|
|
|
if (!isAuto) return;
|
|
|
|
|
|
|
|
const outputNodes = $nodes.filter((n) => n.nodeType === "output");
|
|
|
|
const inputNodes = $nodes.filter((n) => n.nodeType === "input");
|
|
|
|
const filterNodes = $nodes.filter((n) => n.nodeType === "filter");
|
2023-08-24 14:47:22 -04:00
|
|
|
const orderedNodes = [].concat(filterNodes, outputNodes).filter((n) => n != undefined);
|
2023-08-24 14:14:32 -04:00
|
|
|
|
2023-08-24 14:47:22 -04:00
|
|
|
const filled = [];
|
2023-08-24 14:14:32 -04:00
|
|
|
let newEdges = [];
|
2023-08-24 14:47:22 -04:00
|
|
|
|
|
|
|
function connectNode(n1, rest) {
|
|
|
|
for (let i = 0; i < n1.data.outputs.length; i++) {
|
|
|
|
const edgeType = n1.data.outputs[i];
|
|
|
|
for (let j = 0; j < rest.length; j++) {
|
2023-08-24 16:07:27 -04:00
|
|
|
let found = false;
|
2023-08-24 14:47:22 -04:00
|
|
|
const n2 = rest[j];
|
|
|
|
for (let k = 0; k < n2.data.inputs.length; k++) {
|
2023-08-24 16:07:27 -04:00
|
|
|
const targetEdgeType = n2.data.inputs[k];
|
|
|
|
if (edgeType === targetEdgeType && !filled.includes(n2.id + k)) {
|
2023-08-24 14:47:22 -04:00
|
|
|
newEdges.push({
|
|
|
|
id: uuidv4(),
|
|
|
|
type: "default",
|
|
|
|
source: n1.id,
|
|
|
|
target: n2.id,
|
|
|
|
sourceHandle: edgeType + "_" + i,
|
|
|
|
targetHandle: edgeType + "_" + k,
|
|
|
|
});
|
2023-08-24 16:07:27 -04:00
|
|
|
filled.push(n2.id + k);
|
|
|
|
found = true;
|
2023-08-24 14:47:22 -04:00
|
|
|
break;
|
|
|
|
}
|
2023-08-24 14:14:32 -04:00
|
|
|
}
|
2023-08-24 16:07:27 -04:00
|
|
|
if (found) break;
|
2023-08-24 14:14:32 -04:00
|
|
|
}
|
|
|
|
}
|
2023-08-24 14:47:22 -04:00
|
|
|
const nextNode = rest.shift();
|
|
|
|
if (nextNode) {
|
|
|
|
connectNode(nextNode, rest);
|
|
|
|
}
|
2023-08-24 14:14:32 -04:00
|
|
|
}
|
2023-08-24 14:47:22 -04:00
|
|
|
|
|
|
|
for (let inpNode of inputNodes) {
|
|
|
|
connectNode(inpNode, [...orderedNodes]);
|
|
|
|
}
|
|
|
|
// console.log("new", newEdges);
|
2023-08-24 14:14:32 -04:00
|
|
|
edges.set(newEdges);
|
|
|
|
});
|
|
|
|
|
2023-08-22 21:57:53 -04:00
|
|
|
export function addNode(data, type) {
|
|
|
|
let ins = [];
|
|
|
|
let outs = [];
|
|
|
|
|
|
|
|
if (type === "input") {
|
|
|
|
outs = ["v", "a"];
|
|
|
|
} else if (type === "output") {
|
|
|
|
ins = ["v", "a"];
|
|
|
|
} else if (type === "filter") {
|
|
|
|
const [_ins, _outs] = data.type.split("->");
|
|
|
|
ins = _ins.toLowerCase().split("");
|
|
|
|
outs = _outs.toLowerCase().split("");
|
|
|
|
if (data.params) {
|
|
|
|
data.params = data.params.map((p) => {
|
|
|
|
p.value = null;
|
|
|
|
if (p.default != null) p.value = p.default;
|
|
|
|
return p;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-24 23:17:49 -04:00
|
|
|
data.nodeType = type;
|
2023-08-22 21:57:53 -04:00
|
|
|
data.inputs = ins;
|
|
|
|
data.outputs = outs;
|
|
|
|
|
|
|
|
let node = {
|
|
|
|
id: uuidv4(),
|
|
|
|
type: "ffmpeg",
|
|
|
|
data: data,
|
|
|
|
nodeType: type,
|
2023-08-24 16:07:27 -04:00
|
|
|
position: { x: 0, y: 0 },
|
2023-08-22 21:57:53 -04:00
|
|
|
};
|
|
|
|
|
2023-08-24 16:07:27 -04:00
|
|
|
nodes.update((_nodes) => {
|
|
|
|
_nodes.push(node);
|
|
|
|
|
|
|
|
const isAuto = get(auto);
|
|
|
|
|
|
|
|
if (isAuto) {
|
2023-08-24 23:17:49 -04:00
|
|
|
const w = 120;
|
2023-08-24 16:07:27 -04:00
|
|
|
const h = 50;
|
2023-08-24 23:17:49 -04:00
|
|
|
const margin = 50;
|
2023-08-24 16:07:27 -04:00
|
|
|
let prev = null;
|
2023-08-22 21:57:53 -04:00
|
|
|
|
2023-08-24 16:07:27 -04:00
|
|
|
for (let n of _nodes) {
|
|
|
|
if (n.nodeType === "input") {
|
|
|
|
n.position = { x: 0, y: prev ? prev.position.y + h + margin : 0 };
|
|
|
|
prev = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let n of _nodes) {
|
|
|
|
if (n.nodeType === "filter") {
|
|
|
|
let _w = prev && prev.width ? prev.width : w;
|
2023-08-24 23:17:49 -04:00
|
|
|
n.position = { x: prev ? prev.position.x + _w + margin : 0, y: -50 };
|
2023-08-24 16:07:27 -04:00
|
|
|
prev = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let n of _nodes) {
|
|
|
|
if (n.nodeType === "output") {
|
|
|
|
let _w = prev && prev.width ? prev.width : w;
|
|
|
|
n.position = { x: prev ? prev.position.x + _w + margin : 0, y: 0 };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-08-24 23:17:49 -04:00
|
|
|
if (node.nodeType === "filter") {
|
|
|
|
selectedFilter.set(_nodes.length - 1);
|
|
|
|
}
|
2023-08-24 16:07:27 -04:00
|
|
|
return _nodes;
|
|
|
|
});
|
2023-08-24 23:17:49 -04:00
|
|
|
|
|
|
|
|
2023-08-22 21:57:53 -04:00
|
|
|
}
|
2023-08-22 12:05:49 -04:00
|
|
|
|
2023-08-22 21:57:53 -04:00
|
|
|
export function removeNode(id) {
|
|
|
|
nodes.update((_nodes) => {
|
|
|
|
const index = _nodes.findIndex((n) => n.id === id);
|
|
|
|
_nodes.splice(index, 1);
|
|
|
|
return _nodes;
|
|
|
|
});
|
2023-08-24 12:09:55 -04:00
|
|
|
|
2023-08-24 14:14:32 -04:00
|
|
|
edges.update((_edges) => {
|
|
|
|
for (let i = _edges.length - 1; i--; i >= 0) {
|
|
|
|
const e = _edges[i];
|
|
|
|
if ("N-" + e.source === id || "N-" + e.target === id) {
|
|
|
|
_edges.splice(i, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return _edges;
|
|
|
|
});
|
2023-08-22 12:05:49 -04:00
|
|
|
}
|