@stinkycomputing/sesame-snigel-plugin 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config-serializer.d.ts +8 -0
- package/dist/config-serializer.js +80 -0
- package/dist/graph-builder.d.ts +10 -0
- package/dist/graph-builder.js +127 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +36 -0
- package/dist/node-defs.d.ts +8 -0
- package/dist/node-defs.js +89 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.js +3 -0
- package/package.json +6 -3
- package/src/config-serializer.ts +0 -103
- package/src/graph-builder.ts +0 -171
- package/src/index.ts +0 -27
- package/src/node-defs.ts +0 -94
- package/src/types.ts +0 -32
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snigel plugin – serializeNodes() implementation.
|
|
3
|
+
*
|
|
4
|
+
* Serializes snigel/control nodes (and their connected snigel/output and
|
|
5
|
+
* snigel/super-source children) into the extensions.snigel array.
|
|
6
|
+
*/
|
|
7
|
+
import type { MinimalNode, MinimalEdge } from '@stinkycomputing/sesame-editor-api';
|
|
8
|
+
export declare function serializeSnigelNodes<TNode extends MinimalNode, TEdge extends MinimalEdge>(allNodes: TNode[], allEdges: TEdge[]): Record<string, unknown>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Snigel plugin – serializeNodes() implementation.
|
|
4
|
+
*
|
|
5
|
+
* Serializes snigel/control nodes (and their connected snigel/output and
|
|
6
|
+
* snigel/super-source children) into the extensions.snigel array.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.serializeSnigelNodes = serializeSnigelNodes;
|
|
10
|
+
const sesame_editor_api_1 = require("@stinkycomputing/sesame-editor-api");
|
|
11
|
+
function serializeSnigelNodes(allNodes, allEdges) {
|
|
12
|
+
const snigels = [];
|
|
13
|
+
(0, sesame_editor_api_1.getNodesByCategory)(allNodes, 'snigel').forEach(snigelNode => {
|
|
14
|
+
const serialized = serializeSnigel(snigelNode, allEdges, allNodes);
|
|
15
|
+
if (serialized)
|
|
16
|
+
snigels.push({ ...serialized, _meta: (0, sesame_editor_api_1.nodeMeta)(snigelNode) });
|
|
17
|
+
});
|
|
18
|
+
if (snigels.length === 0)
|
|
19
|
+
return {};
|
|
20
|
+
return { snigel: snigels };
|
|
21
|
+
}
|
|
22
|
+
function serializeSnigel(node, edges, nodes) {
|
|
23
|
+
const p = (0, sesame_editor_api_1.nodeProps)(node);
|
|
24
|
+
const d = (0, sesame_editor_api_1.getData)(node);
|
|
25
|
+
const inputs = d.dynamicInputs ?? d.def.inputs;
|
|
26
|
+
const nodeOutputs = d.dynamicOutputs ?? d.def.outputs;
|
|
27
|
+
// Collect sources
|
|
28
|
+
const snSources = [];
|
|
29
|
+
inputs.forEach((port, i) => {
|
|
30
|
+
if (port.type !== 'source/device')
|
|
31
|
+
return;
|
|
32
|
+
const conn = (0, sesame_editor_api_1.findSourceConnection)(edges, nodes, node.id, `in-${i}`);
|
|
33
|
+
if (!conn)
|
|
34
|
+
return;
|
|
35
|
+
const connData = (0, sesame_editor_api_1.getData)(conn.node);
|
|
36
|
+
if (connData.def.category === 'snigel-super') {
|
|
37
|
+
snSources.push(serializeSnigelSuperSource(conn.node, edges, nodes));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
snSources.push(conn.connectionId);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
// Collect outputs (snigel/output nodes)
|
|
44
|
+
const snOutputs = [];
|
|
45
|
+
nodeOutputs.forEach((_port, i) => {
|
|
46
|
+
const outEdge = edges.find(e => e.source === node.id && e.sourceHandle === `out-${i}`);
|
|
47
|
+
if (!outEdge)
|
|
48
|
+
return;
|
|
49
|
+
const outNode = nodes.find(n => n.id === outEdge.target);
|
|
50
|
+
if (!outNode)
|
|
51
|
+
return;
|
|
52
|
+
const op = (0, sesame_editor_api_1.nodeProps)(outNode);
|
|
53
|
+
snOutputs.push({ id: op.id, type: op.type, video: {}, _meta: (0, sesame_editor_api_1.nodeMeta)(outNode) });
|
|
54
|
+
});
|
|
55
|
+
return {
|
|
56
|
+
id: p.id,
|
|
57
|
+
controllerPort: p.controllerPort,
|
|
58
|
+
sizeGb: p.sizeGb,
|
|
59
|
+
videoFilePath: p.videoFilePath,
|
|
60
|
+
ingestFolder: p.ingestFolder,
|
|
61
|
+
egressFolder: p.egressFolder,
|
|
62
|
+
video: { encoder: { bitrateKbs: p.bitrate, codec: p.codec || 'hevc' } },
|
|
63
|
+
sources: snSources,
|
|
64
|
+
outputs: snOutputs,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function serializeSnigelSuperSource(node, edges, nodes) {
|
|
68
|
+
const p = (0, sesame_editor_api_1.nodeProps)(node);
|
|
69
|
+
const d = (0, sesame_editor_api_1.getData)(node);
|
|
70
|
+
const inputs = d.dynamicInputs ?? d.def.inputs;
|
|
71
|
+
const srcs = [];
|
|
72
|
+
inputs.forEach((port, i) => {
|
|
73
|
+
if (port.type !== 'source/device')
|
|
74
|
+
return;
|
|
75
|
+
const conn = (0, sesame_editor_api_1.findSourceConnection)(edges, nodes, node.id, `in-${i}`);
|
|
76
|
+
if (conn)
|
|
77
|
+
srcs.push(conn.connectionId);
|
|
78
|
+
});
|
|
79
|
+
return { id: p.id, type: 'super', sources: srcs, _meta: (0, sesame_editor_api_1.nodeMeta)(node) };
|
|
80
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snigel plugin – buildNodes() implementation.
|
|
3
|
+
*
|
|
4
|
+
* Creates snigel/control, snigel/output, and snigel/super-source nodes from
|
|
5
|
+
* config, wires internal edges, and returns snigel-output nodes as
|
|
6
|
+
* supplementalNodes so the core graph-builder can wire them to outputs/RTT.
|
|
7
|
+
*/
|
|
8
|
+
import type { Node, Edge } from '@xyflow/react';
|
|
9
|
+
import type { NodeFactory, CoreNodeMaps, PluginBuildResult, ISesameConfig } from '@stinkycomputing/sesame-editor-api';
|
|
10
|
+
export declare function buildSnigelNodes(extensions: Record<string, unknown>, config: ISesameConfig, factory: NodeFactory<Node, Edge>, coreNodes: CoreNodeMaps<Node>): PluginBuildResult<Node, Edge>;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Snigel plugin – buildNodes() implementation.
|
|
4
|
+
*
|
|
5
|
+
* Creates snigel/control, snigel/output, and snigel/super-source nodes from
|
|
6
|
+
* config, wires internal edges, and returns snigel-output nodes as
|
|
7
|
+
* supplementalNodes so the core graph-builder can wire them to outputs/RTT.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.buildSnigelNodes = buildSnigelNodes;
|
|
11
|
+
const node_defs_1 = require("./node-defs");
|
|
12
|
+
function codecToEditorValue(codec) {
|
|
13
|
+
if (!codec)
|
|
14
|
+
return 'hevc';
|
|
15
|
+
return codec;
|
|
16
|
+
}
|
|
17
|
+
function buildSnigelNodes(extensions, config, factory, coreNodes) {
|
|
18
|
+
const nodes = [];
|
|
19
|
+
const edges = [];
|
|
20
|
+
const supplementalNodes = new Map();
|
|
21
|
+
// Support both new format (extensions.snigel) and legacy (config.snigels)
|
|
22
|
+
const snigels = (extensions['snigel'] ??
|
|
23
|
+
config['snigels'] ??
|
|
24
|
+
[]);
|
|
25
|
+
const { sourceNodes } = coreNodes;
|
|
26
|
+
const snigelNodes = new Map();
|
|
27
|
+
const snigelSuperNodes = new Map();
|
|
28
|
+
let x = factory.X_OFFSET * 6; // position after core columns
|
|
29
|
+
snigels.forEach((snigel, idx) => {
|
|
30
|
+
const snigelOutputTemplate = node_defs_1.snigelControlDef.outputs[0];
|
|
31
|
+
const dynamicInputs = snigel.sources.map((_, i) => ({
|
|
32
|
+
label: `Source ${i + 1}`, type: 'source/device',
|
|
33
|
+
}));
|
|
34
|
+
dynamicInputs.push({ label: `Source ${dynamicInputs.length + 1}`, type: 'source/device' });
|
|
35
|
+
const snigelOutputsList = snigel.outputs?.length
|
|
36
|
+
? snigel.outputs
|
|
37
|
+
: [{ id: `${snigel.id}-out1`, video: {}, type: 'pvw' }];
|
|
38
|
+
const dynamicOutputs = snigelOutputsList.map((_, i) => ({
|
|
39
|
+
label: `Output ${i + 1}`,
|
|
40
|
+
type: 'source/snigel',
|
|
41
|
+
maxConnections: snigelOutputTemplate?.maxConnections,
|
|
42
|
+
}));
|
|
43
|
+
dynamicOutputs.push({
|
|
44
|
+
label: `Output ${dynamicOutputs.length + 1}`,
|
|
45
|
+
type: 'source/snigel',
|
|
46
|
+
maxConnections: snigelOutputTemplate?.maxConnections,
|
|
47
|
+
});
|
|
48
|
+
const snigelNode = factory.makeNode(snigel.id, 'snigel/control', factory.pos(snigel, x, idx), {
|
|
49
|
+
id: snigel.id,
|
|
50
|
+
controllerPort: snigel.controllerPort,
|
|
51
|
+
sizeGb: snigel.sizeGb,
|
|
52
|
+
bitrate: snigel.video?.encoder?.bitrateKbs ?? 50000,
|
|
53
|
+
codec: codecToEditorValue(snigel.video?.encoder?.codec),
|
|
54
|
+
videoFilePath: snigel.videoFilePath,
|
|
55
|
+
ingestFolder: snigel.ingestFolder ?? '',
|
|
56
|
+
egressFolder: snigel.egressFolder ?? '',
|
|
57
|
+
}, factory.collapsed(snigel), dynamicInputs, dynamicOutputs);
|
|
58
|
+
nodes.push(snigelNode);
|
|
59
|
+
snigelNodes.set(snigel.id, snigelNode);
|
|
60
|
+
// Snigel outputs
|
|
61
|
+
snigelOutputsList.forEach((sOut, oIdx) => {
|
|
62
|
+
const outNode = factory.makeNode(sOut.id, 'snigel/output', factory.pos(sOut, x + factory.X_OFFSET, idx + oIdx), { id: sOut.id, type: sOut.type }, factory.collapsed(sOut));
|
|
63
|
+
nodes.push(outNode);
|
|
64
|
+
supplementalNodes.set(sOut.id, outNode);
|
|
65
|
+
// snigel/control → snigel/output
|
|
66
|
+
edges.push(factory.edge(snigelNode.id, `out-${oIdx}`, outNode.id, 'in-0'));
|
|
67
|
+
});
|
|
68
|
+
// Snigel super-sources
|
|
69
|
+
snigel.sources
|
|
70
|
+
.filter((s) => typeof s === 'object' && s.type === 'super')
|
|
71
|
+
.forEach(ss => {
|
|
72
|
+
const dynamicSSInputs = ss.sources.map((_, i) => ({
|
|
73
|
+
label: `Source ${i + 1}`, type: 'source/device',
|
|
74
|
+
}));
|
|
75
|
+
dynamicSSInputs.push({ label: `Source ${dynamicSSInputs.length + 1}`, type: 'source/device' });
|
|
76
|
+
const ssNode = factory.makeNode(ss.id, 'snigel/super-source', factory.pos(ss, x - factory.X_OFFSET, idx), { id: ss.id }, factory.collapsed(ss), dynamicSSInputs);
|
|
77
|
+
nodes.push(ssNode);
|
|
78
|
+
snigelSuperNodes.set(ss.id, ssNode);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
// ── Snigel ← sources edges ────────────────────────────────────────────
|
|
82
|
+
// Build these after all nodes exist so findOutputSlot can search them.
|
|
83
|
+
const allNodes = [...nodes]; // plugin nodes only; coreNodes passed via sourceNodes map
|
|
84
|
+
snigels.forEach(snigel => {
|
|
85
|
+
const snigelNode = snigelNodes.get(snigel.id);
|
|
86
|
+
if (!snigelNode)
|
|
87
|
+
return;
|
|
88
|
+
snigel.sources.forEach((src, srcIdx) => {
|
|
89
|
+
if (typeof src === 'string') {
|
|
90
|
+
const slashIdx = src.indexOf('/');
|
|
91
|
+
const configId = slashIdx >= 0 ? src.slice(0, slashIdx) : src;
|
|
92
|
+
const outputName = slashIdx >= 0 ? src.slice(slashIdx) : undefined;
|
|
93
|
+
const srcNode = sourceNodes.get(configId);
|
|
94
|
+
if (srcNode) {
|
|
95
|
+
// We need to search core + plugin nodes for the slot
|
|
96
|
+
const combinedNodes = [...(Array.from(sourceNodes.values())), ...allNodes];
|
|
97
|
+
const srcSlot = factory.findOutputSlot(combinedNodes, srcNode.id, 'source/device', outputName);
|
|
98
|
+
if (srcSlot)
|
|
99
|
+
edges.push(factory.edge(srcNode.id, srcSlot, snigelNode.id, `in-${srcIdx}`));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
else if (src.type === 'super') {
|
|
103
|
+
const ss = src;
|
|
104
|
+
const ssNode = snigelSuperNodes.get(ss.id);
|
|
105
|
+
if (ssNode) {
|
|
106
|
+
const combinedNodes = [...(Array.from(sourceNodes.values())), ...allNodes];
|
|
107
|
+
const srcSlot = factory.findOutputSlot(combinedNodes, ssNode.id, 'source/device');
|
|
108
|
+
if (srcSlot)
|
|
109
|
+
edges.push(factory.edge(ssNode.id, srcSlot, snigelNode.id, `in-${srcIdx}`));
|
|
110
|
+
// Super-source ← core sources
|
|
111
|
+
ss.sources.forEach((innerSrc, innerIdx) => {
|
|
112
|
+
const slashIdx = innerSrc.indexOf('/');
|
|
113
|
+
const configId = slashIdx >= 0 ? innerSrc.slice(0, slashIdx) : innerSrc;
|
|
114
|
+
const outputName = slashIdx >= 0 ? innerSrc.slice(slashIdx) : undefined;
|
|
115
|
+
const innerNode = sourceNodes.get(configId);
|
|
116
|
+
if (innerNode) {
|
|
117
|
+
const slot = factory.findOutputSlot(combinedNodes, innerNode.id, 'source/device', outputName);
|
|
118
|
+
if (slot)
|
|
119
|
+
edges.push(factory.edge(innerNode.id, slot, ssNode.id, `in-${innerIdx}`));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
return { nodes, edges, supplementalNodes };
|
|
127
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snigel config editor plugin.
|
|
3
|
+
*
|
|
4
|
+
* Register this with the Sesame config editor at startup:
|
|
5
|
+
*
|
|
6
|
+
* import { SnigelPlugin } from '@stinkycomputing/sesame-snigel-plugin';
|
|
7
|
+
* import { registerPlugin } from './plugins';
|
|
8
|
+
* registerPlugin(SnigelPlugin);
|
|
9
|
+
*/
|
|
10
|
+
import type { Node, Edge } from '@xyflow/react';
|
|
11
|
+
import type { ISesameEditorPlugin } from '@stinkycomputing/sesame-editor-api';
|
|
12
|
+
export declare const SnigelPlugin: ISesameEditorPlugin<Node, Edge>;
|
|
13
|
+
export * from './types';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Snigel config editor plugin.
|
|
4
|
+
*
|
|
5
|
+
* Register this with the Sesame config editor at startup:
|
|
6
|
+
*
|
|
7
|
+
* import { SnigelPlugin } from '@stinkycomputing/sesame-snigel-plugin';
|
|
8
|
+
* import { registerPlugin } from './plugins';
|
|
9
|
+
* registerPlugin(SnigelPlugin);
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
23
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.SnigelPlugin = void 0;
|
|
27
|
+
const node_defs_1 = require("./node-defs");
|
|
28
|
+
const graph_builder_1 = require("./graph-builder");
|
|
29
|
+
const config_serializer_1 = require("./config-serializer");
|
|
30
|
+
exports.SnigelPlugin = {
|
|
31
|
+
id: 'com.stinkycomputing.snigel',
|
|
32
|
+
nodeTypes: [node_defs_1.snigelControlDef, node_defs_1.snigelOutputDef, node_defs_1.snigelSuperSourceDef],
|
|
33
|
+
buildNodes: graph_builder_1.buildSnigelNodes,
|
|
34
|
+
serializeNodes: config_serializer_1.serializeSnigelNodes,
|
|
35
|
+
};
|
|
36
|
+
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Node type definitions for all three Snigel node types. */
|
|
2
|
+
import type { NodeTypeDef, MetadataDef } from '@stinkycomputing/sesame-editor-api';
|
|
3
|
+
export declare const snigelStateMetadata: MetadataDef;
|
|
4
|
+
export declare const snigelCommandsMetadata: MetadataDef;
|
|
5
|
+
export declare const snigelColor = "#784212";
|
|
6
|
+
export declare const snigelControlDef: NodeTypeDef;
|
|
7
|
+
export declare const snigelOutputDef: NodeTypeDef;
|
|
8
|
+
export declare const snigelSuperSourceDef: NodeTypeDef;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Node type definitions for all three Snigel node types. */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.snigelSuperSourceDef = exports.snigelOutputDef = exports.snigelControlDef = exports.snigelColor = exports.snigelCommandsMetadata = exports.snigelStateMetadata = void 0;
|
|
5
|
+
exports.snigelStateMetadata = {
|
|
6
|
+
id: 'snigel.jlc.state.v1',
|
|
7
|
+
label: 'Remote Control State',
|
|
8
|
+
direction: 'emit',
|
|
9
|
+
portType: 'metadata/snigel-state',
|
|
10
|
+
format: 'binary',
|
|
11
|
+
};
|
|
12
|
+
exports.snigelCommandsMetadata = {
|
|
13
|
+
id: 'snigel.jlc.commands.v1',
|
|
14
|
+
label: 'Remote Control Commands',
|
|
15
|
+
direction: 'receive',
|
|
16
|
+
portType: 'metadata/snigel-commands',
|
|
17
|
+
format: 'binary',
|
|
18
|
+
};
|
|
19
|
+
exports.snigelColor = '#784212';
|
|
20
|
+
exports.snigelControlDef = {
|
|
21
|
+
type: 'snigel/control',
|
|
22
|
+
title: 'Snigel Replay',
|
|
23
|
+
category: 'snigel',
|
|
24
|
+
menuGroup: 'Snigel',
|
|
25
|
+
menuOrder: 7,
|
|
26
|
+
menuItemOrder: -1,
|
|
27
|
+
color: exports.snigelColor,
|
|
28
|
+
inputs: [
|
|
29
|
+
{ label: 'Source 1', type: 'source/device' },
|
|
30
|
+
],
|
|
31
|
+
outputs: [
|
|
32
|
+
{ label: 'Output 1', type: 'source/snigel', maxConnections: 1 },
|
|
33
|
+
],
|
|
34
|
+
widgets: [
|
|
35
|
+
{ type: 'number', label: 'Size (GB)', property: 'sizeGb', options: { min: 0, max: 1000, step: 1, precision: 0 } },
|
|
36
|
+
{ type: 'text', label: 'Port', property: 'controllerPort' },
|
|
37
|
+
{ type: 'text', label: 'Video folder', property: 'videoFilePath' },
|
|
38
|
+
{ type: 'text', label: 'Ingest folder', property: 'ingestFolder' },
|
|
39
|
+
{ type: 'text', label: 'Egress folder', property: 'egressFolder' },
|
|
40
|
+
{ type: 'combo', label: 'Codec', property: 'codec', options: { values: ['avc', 'hevc', 'av1'] } },
|
|
41
|
+
{ type: 'number', label: 'Bitrate', property: 'bitrate', options: { min: 0, max: 10000000, step: 1000, precision: 0 } },
|
|
42
|
+
],
|
|
43
|
+
defaultProperties: { id: '', controllerPort: 'COM1', videoFilePath: '', ingestFolder: '', egressFolder: '', sizeGb: 1, bitrate: 50000, codec: 'hevc' },
|
|
44
|
+
dynamicPorts: true,
|
|
45
|
+
metadata: [exports.snigelStateMetadata, exports.snigelCommandsMetadata],
|
|
46
|
+
};
|
|
47
|
+
exports.snigelOutputDef = {
|
|
48
|
+
type: 'snigel/output',
|
|
49
|
+
title: 'Snigel Output',
|
|
50
|
+
category: 'snigel-output',
|
|
51
|
+
menuGroup: 'Snigel',
|
|
52
|
+
menuOrder: 7,
|
|
53
|
+
color: exports.snigelColor,
|
|
54
|
+
inputs: [
|
|
55
|
+
{ label: 'Source', type: 'source/snigel' },
|
|
56
|
+
],
|
|
57
|
+
outputs: [
|
|
58
|
+
{ label: 'Video', type: 'video/composition', name: '/compositor' },
|
|
59
|
+
{ label: 'Audio 1+2', type: 'audio/stereo', name: '/audio_mixer_0' },
|
|
60
|
+
{ label: 'Audio 3+4', type: 'audio/stereo', name: '/audio_mixer_1' },
|
|
61
|
+
{ label: 'Audio 5+6', type: 'audio/stereo', name: '/audio_mixer_2' },
|
|
62
|
+
{ label: 'Audio 7+8', type: 'audio/stereo', name: '/audio_mixer_3' },
|
|
63
|
+
{ label: 'Audio Bus 1+2', type: 'audio/raw', name: '/audio_mixer_0' },
|
|
64
|
+
{ label: 'Audio Bus 3+4', type: 'audio/raw', name: '/audio_mixer_1' },
|
|
65
|
+
{ label: 'Audio Bus 5+6', type: 'audio/raw', name: '/audio_mixer_2' },
|
|
66
|
+
{ label: 'Audio Bus 7+8', type: 'audio/raw', name: '/audio_mixer_3' },
|
|
67
|
+
],
|
|
68
|
+
widgets: [
|
|
69
|
+
{ type: 'combo', label: 'Type', property: 'type', options: { values: ['pvw', 'pgm'] } },
|
|
70
|
+
],
|
|
71
|
+
defaultProperties: { id: '', type: 'pgm' },
|
|
72
|
+
};
|
|
73
|
+
exports.snigelSuperSourceDef = {
|
|
74
|
+
type: 'snigel/super-source',
|
|
75
|
+
title: 'Snigel Super Slow',
|
|
76
|
+
category: 'snigel-super',
|
|
77
|
+
menuGroup: 'Snigel',
|
|
78
|
+
menuOrder: 7,
|
|
79
|
+
color: exports.snigelColor,
|
|
80
|
+
inputs: [
|
|
81
|
+
{ label: 'Source 1', type: 'source/device' },
|
|
82
|
+
],
|
|
83
|
+
outputs: [
|
|
84
|
+
{ label: 'Source', type: 'source/device' },
|
|
85
|
+
],
|
|
86
|
+
widgets: [],
|
|
87
|
+
defaultProperties: { id: '' },
|
|
88
|
+
dynamicPorts: true,
|
|
89
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Snigel-specific config types (formerly in sesame-interop). */
|
|
2
|
+
export type SnigelCodec = 'avc' | 'hevc' | 'av1';
|
|
3
|
+
export interface ISuperSlowSnigelSource {
|
|
4
|
+
id: string;
|
|
5
|
+
type: 'super';
|
|
6
|
+
sources: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface ISnigel {
|
|
9
|
+
id: string;
|
|
10
|
+
sources: (string | ISuperSlowSnigelSource)[];
|
|
11
|
+
sizeGb: number;
|
|
12
|
+
videoFilePath: string;
|
|
13
|
+
ingestFolder: string;
|
|
14
|
+
egressFolder: string;
|
|
15
|
+
video: {
|
|
16
|
+
encoder?: {
|
|
17
|
+
codec?: SnigelCodec;
|
|
18
|
+
bitrateKbs?: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
controllerPort: string;
|
|
22
|
+
outputs?: ISnigelOutput[];
|
|
23
|
+
}
|
|
24
|
+
export type SnigelOutputType = 'pgm' | 'pvw';
|
|
25
|
+
export interface ISnigelOutput {
|
|
26
|
+
id: string;
|
|
27
|
+
type: SnigelOutputType;
|
|
28
|
+
video: Record<string, unknown>;
|
|
29
|
+
}
|
package/dist/types.js
ADDED
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stinkycomputing/sesame-snigel-plugin",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Snigel replay plugin for the Sesame config editor",
|
|
5
|
-
"main": "
|
|
6
|
-
"types": "
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
7
10
|
"scripts": {
|
|
8
11
|
"build": "tsc"
|
|
9
12
|
},
|
package/src/config-serializer.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Snigel plugin – serializeNodes() implementation.
|
|
3
|
-
*
|
|
4
|
-
* Serializes snigel/control nodes (and their connected snigel/output and
|
|
5
|
-
* snigel/super-source children) into the extensions.snigel array.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { MinimalNode, MinimalEdge } from '@stinkycomputing/sesame-editor-api';
|
|
9
|
-
import {
|
|
10
|
-
getData,
|
|
11
|
-
nodeProps,
|
|
12
|
-
nodeMeta,
|
|
13
|
-
findSourceConnection,
|
|
14
|
-
getNodesByCategory,
|
|
15
|
-
} from '@stinkycomputing/sesame-editor-api';
|
|
16
|
-
|
|
17
|
-
export function serializeSnigelNodes<TNode extends MinimalNode, TEdge extends MinimalEdge>(
|
|
18
|
-
allNodes: TNode[],
|
|
19
|
-
allEdges: TEdge[],
|
|
20
|
-
): Record<string, unknown> {
|
|
21
|
-
const snigels: unknown[] = [];
|
|
22
|
-
|
|
23
|
-
getNodesByCategory(allNodes, 'snigel').forEach(snigelNode => {
|
|
24
|
-
const serialized = serializeSnigel(snigelNode, allEdges, allNodes);
|
|
25
|
-
if (serialized) snigels.push({ ...serialized, _meta: nodeMeta(snigelNode) });
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
if (snigels.length === 0) return {};
|
|
29
|
-
return { snigel: snigels };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function serializeSnigel<TNode extends MinimalNode, TEdge extends MinimalEdge>(
|
|
33
|
-
node: TNode,
|
|
34
|
-
edges: TEdge[],
|
|
35
|
-
nodes: TNode[],
|
|
36
|
-
): Record<string, unknown> | undefined {
|
|
37
|
-
const p = nodeProps(node);
|
|
38
|
-
const d = getData(node);
|
|
39
|
-
const inputs = d.dynamicInputs ?? d.def.inputs;
|
|
40
|
-
const nodeOutputs = d.dynamicOutputs ?? d.def.outputs;
|
|
41
|
-
|
|
42
|
-
// Collect sources
|
|
43
|
-
const snSources: unknown[] = [];
|
|
44
|
-
inputs.forEach((port, i) => {
|
|
45
|
-
if (port.type !== 'source/device') return;
|
|
46
|
-
const conn = findSourceConnection(edges, nodes, node.id, `in-${i}`);
|
|
47
|
-
if (!conn) return;
|
|
48
|
-
const connData = getData(conn.node);
|
|
49
|
-
if (connData.def.category === 'snigel-super') {
|
|
50
|
-
snSources.push(serializeSnigelSuperSource(conn.node, edges, nodes));
|
|
51
|
-
} else {
|
|
52
|
-
snSources.push(conn.connectionId);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// Collect outputs (snigel/output nodes)
|
|
57
|
-
const snOutputs: unknown[] = [];
|
|
58
|
-
nodeOutputs.forEach((_port, i) => {
|
|
59
|
-
const outEdge = edges.find(e => e.source === node.id && e.sourceHandle === `out-${i}`);
|
|
60
|
-
if (!outEdge) return;
|
|
61
|
-
const outNode = nodes.find(n => n.id === outEdge.target);
|
|
62
|
-
if (!outNode) return;
|
|
63
|
-
const op = nodeProps(outNode);
|
|
64
|
-
snOutputs.push({ id: op.id, type: op.type, video: {}, _meta: nodeMeta(outNode) });
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const codecMap: Record<string, string> = {
|
|
68
|
-
h264: 'low_latency',
|
|
69
|
-
hevc: 'low_latency_hevc',
|
|
70
|
-
av1: 'low_latency_av1',
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
id: p.id,
|
|
75
|
-
controllerPort: p.controllerPort,
|
|
76
|
-
sizeGb: p.sizeGb,
|
|
77
|
-
videoFilePath: p.videoFilePath,
|
|
78
|
-
ingestFolder: p.ingestFolder,
|
|
79
|
-
egressFolder: p.egressFolder,
|
|
80
|
-
video: { encoder: { bitrateKbs: p.bitrate, preset: codecMap[String(p.codec)] || 'low_latency_hevc' } },
|
|
81
|
-
sources: snSources,
|
|
82
|
-
outputs: snOutputs,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function serializeSnigelSuperSource<TNode extends MinimalNode, TEdge extends MinimalEdge>(
|
|
87
|
-
node: TNode,
|
|
88
|
-
edges: TEdge[],
|
|
89
|
-
nodes: TNode[],
|
|
90
|
-
): Record<string, unknown> {
|
|
91
|
-
const p = nodeProps(node);
|
|
92
|
-
const d = getData(node);
|
|
93
|
-
const inputs = d.dynamicInputs ?? d.def.inputs;
|
|
94
|
-
const srcs: string[] = [];
|
|
95
|
-
|
|
96
|
-
inputs.forEach((port, i) => {
|
|
97
|
-
if (port.type !== 'source/device') return;
|
|
98
|
-
const conn = findSourceConnection(edges, nodes, node.id, `in-${i}`);
|
|
99
|
-
if (conn) srcs.push(String(getData(conn.node).properties.id));
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
return { id: p.id, type: 'super', sources: srcs, _meta: nodeMeta(node) };
|
|
103
|
-
}
|
package/src/graph-builder.ts
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Snigel plugin – buildNodes() implementation.
|
|
3
|
-
*
|
|
4
|
-
* Creates snigel/control, snigel/output, and snigel/super-source nodes from
|
|
5
|
-
* config, wires internal edges, and returns snigel-output nodes as
|
|
6
|
-
* supplementalNodes so the core graph-builder can wire them to outputs/RTT.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { Node, Edge } from '@xyflow/react';
|
|
10
|
-
import type { NodeFactory, CoreNodeMaps, PluginBuildResult, ISesameConfig } from '@stinkycomputing/sesame-editor-api';
|
|
11
|
-
import type { ISnigel, ISuperSlowSnigelSource } from './types';
|
|
12
|
-
import { snigelControlDef } from './node-defs';
|
|
13
|
-
|
|
14
|
-
function presetToCodec(preset: string | undefined): string {
|
|
15
|
-
if (!preset) return 'hevc';
|
|
16
|
-
if (preset.includes('hevc')) return 'hevc';
|
|
17
|
-
if (preset.includes('av1')) return 'av1';
|
|
18
|
-
return 'h264';
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function buildSnigelNodes(
|
|
22
|
-
extensions: Record<string, unknown>,
|
|
23
|
-
config: ISesameConfig,
|
|
24
|
-
factory: NodeFactory<Node, Edge>,
|
|
25
|
-
coreNodes: CoreNodeMaps<Node>,
|
|
26
|
-
): PluginBuildResult<Node, Edge> {
|
|
27
|
-
const nodes: Node[] = [];
|
|
28
|
-
const edges: Edge[] = [];
|
|
29
|
-
const supplementalNodes = new Map<string, Node>();
|
|
30
|
-
|
|
31
|
-
// Support both new format (extensions.snigel) and legacy (config.snigels)
|
|
32
|
-
const snigels = (
|
|
33
|
-
(extensions['snigel'] as ISnigel[] | undefined) ??
|
|
34
|
-
((config as unknown as Record<string, unknown>)['snigels'] as ISnigel[] | undefined) ??
|
|
35
|
-
[]
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
const { sourceNodes } = coreNodes;
|
|
39
|
-
const snigelNodes = new Map<string, Node>();
|
|
40
|
-
const snigelSuperNodes = new Map<string, Node>();
|
|
41
|
-
|
|
42
|
-
let x = factory.X_OFFSET * 6; // position after core columns
|
|
43
|
-
|
|
44
|
-
snigels.forEach((snigel, idx) => {
|
|
45
|
-
const snigelOutputTemplate = snigelControlDef.outputs[0];
|
|
46
|
-
|
|
47
|
-
const dynamicInputs = snigel.sources.map((_, i) => ({
|
|
48
|
-
label: `Source ${i + 1}`, type: 'source/device' as const,
|
|
49
|
-
}));
|
|
50
|
-
dynamicInputs.push({ label: `Source ${dynamicInputs.length + 1}`, type: 'source/device' });
|
|
51
|
-
|
|
52
|
-
const snigelOutputsList = snigel.outputs?.length
|
|
53
|
-
? snigel.outputs
|
|
54
|
-
: [{ id: `${snigel.id}-out1`, video: {}, type: 'pvw' as const }];
|
|
55
|
-
|
|
56
|
-
const dynamicOutputs = snigelOutputsList.map((_, i) => ({
|
|
57
|
-
label: `Output ${i + 1}`,
|
|
58
|
-
type: 'source/snigel',
|
|
59
|
-
maxConnections: snigelOutputTemplate?.maxConnections,
|
|
60
|
-
}));
|
|
61
|
-
dynamicOutputs.push({
|
|
62
|
-
label: `Output ${dynamicOutputs.length + 1}`,
|
|
63
|
-
type: 'source/snigel',
|
|
64
|
-
maxConnections: snigelOutputTemplate?.maxConnections,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
const snigelNode = factory.makeNode(
|
|
68
|
-
snigel.id,
|
|
69
|
-
'snigel/control',
|
|
70
|
-
factory.pos(snigel, x, idx),
|
|
71
|
-
{
|
|
72
|
-
id: snigel.id,
|
|
73
|
-
controllerPort: snigel.controllerPort,
|
|
74
|
-
sizeGb: snigel.sizeGb,
|
|
75
|
-
bitrate: snigel.video?.encoder?.bitrateKbs ?? 50000,
|
|
76
|
-
codec: presetToCodec(snigel.video?.encoder?.preset),
|
|
77
|
-
videoFilePath: snigel.videoFilePath,
|
|
78
|
-
ingestFolder: snigel.ingestFolder ?? '',
|
|
79
|
-
egressFolder: snigel.egressFolder ?? '',
|
|
80
|
-
},
|
|
81
|
-
factory.collapsed(snigel),
|
|
82
|
-
dynamicInputs,
|
|
83
|
-
dynamicOutputs,
|
|
84
|
-
);
|
|
85
|
-
nodes.push(snigelNode);
|
|
86
|
-
snigelNodes.set(snigel.id, snigelNode);
|
|
87
|
-
|
|
88
|
-
// Snigel outputs
|
|
89
|
-
snigelOutputsList.forEach((sOut, oIdx) => {
|
|
90
|
-
const outNode = factory.makeNode(
|
|
91
|
-
sOut.id,
|
|
92
|
-
'snigel/output',
|
|
93
|
-
factory.pos(sOut, x + factory.X_OFFSET, idx + oIdx),
|
|
94
|
-
{ id: sOut.id, type: sOut.type },
|
|
95
|
-
factory.collapsed(sOut),
|
|
96
|
-
);
|
|
97
|
-
nodes.push(outNode);
|
|
98
|
-
supplementalNodes.set(sOut.id, outNode);
|
|
99
|
-
|
|
100
|
-
// snigel/control → snigel/output
|
|
101
|
-
edges.push(factory.edge(snigelNode.id, `out-${oIdx}`, outNode.id, 'in-0'));
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// Snigel super-sources
|
|
105
|
-
snigel.sources
|
|
106
|
-
.filter((s): s is ISuperSlowSnigelSource => typeof s === 'object' && (s as ISuperSlowSnigelSource).type === 'super')
|
|
107
|
-
.forEach(ss => {
|
|
108
|
-
const dynamicSSInputs = ss.sources.map((_, i) => ({
|
|
109
|
-
label: `Source ${i + 1}`, type: 'source/device' as const,
|
|
110
|
-
}));
|
|
111
|
-
dynamicSSInputs.push({ label: `Source ${dynamicSSInputs.length + 1}`, type: 'source/device' });
|
|
112
|
-
|
|
113
|
-
const ssNode = factory.makeNode(
|
|
114
|
-
ss.id,
|
|
115
|
-
'snigel/super-source',
|
|
116
|
-
factory.pos(ss, x - factory.X_OFFSET, idx),
|
|
117
|
-
{ id: ss.id },
|
|
118
|
-
factory.collapsed(ss),
|
|
119
|
-
dynamicSSInputs,
|
|
120
|
-
);
|
|
121
|
-
nodes.push(ssNode);
|
|
122
|
-
snigelSuperNodes.set(ss.id, ssNode);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// ── Snigel ← sources edges ────────────────────────────────────────────
|
|
127
|
-
// Build these after all nodes exist so findOutputSlot can search them.
|
|
128
|
-
const allNodes = [...nodes]; // plugin nodes only; coreNodes passed via sourceNodes map
|
|
129
|
-
|
|
130
|
-
snigels.forEach(snigel => {
|
|
131
|
-
const snigelNode = snigelNodes.get(snigel.id);
|
|
132
|
-
if (!snigelNode) return;
|
|
133
|
-
|
|
134
|
-
snigel.sources.forEach((src, srcIdx) => {
|
|
135
|
-
if (typeof src === 'string') {
|
|
136
|
-
const slashIdx = src.indexOf('/');
|
|
137
|
-
const configId = slashIdx >= 0 ? src.slice(0, slashIdx) : src;
|
|
138
|
-
const outputName = slashIdx >= 0 ? src.slice(slashIdx) : undefined;
|
|
139
|
-
const srcNode = sourceNodes.get(configId);
|
|
140
|
-
if (srcNode) {
|
|
141
|
-
// We need to search core + plugin nodes for the slot
|
|
142
|
-
const combinedNodes = [...(Array.from(sourceNodes.values())), ...allNodes];
|
|
143
|
-
const srcSlot = factory.findOutputSlot(combinedNodes, srcNode.id, 'source/device', outputName);
|
|
144
|
-
if (srcSlot) edges.push(factory.edge(srcNode.id, srcSlot, snigelNode.id, `in-${srcIdx}`));
|
|
145
|
-
}
|
|
146
|
-
} else if (src.type === 'super') {
|
|
147
|
-
const ss = src as ISuperSlowSnigelSource;
|
|
148
|
-
const ssNode = snigelSuperNodes.get(ss.id);
|
|
149
|
-
if (ssNode) {
|
|
150
|
-
const combinedNodes = [...(Array.from(sourceNodes.values())), ...allNodes];
|
|
151
|
-
const srcSlot = factory.findOutputSlot(combinedNodes, ssNode.id, 'source/device');
|
|
152
|
-
if (srcSlot) edges.push(factory.edge(ssNode.id, srcSlot, snigelNode.id, `in-${srcIdx}`));
|
|
153
|
-
|
|
154
|
-
// Super-source ← core sources
|
|
155
|
-
ss.sources.forEach((innerSrc, innerIdx) => {
|
|
156
|
-
const slashIdx = innerSrc.indexOf('/');
|
|
157
|
-
const configId = slashIdx >= 0 ? innerSrc.slice(0, slashIdx) : innerSrc;
|
|
158
|
-
const outputName = slashIdx >= 0 ? innerSrc.slice(slashIdx) : undefined;
|
|
159
|
-
const innerNode = sourceNodes.get(configId);
|
|
160
|
-
if (innerNode) {
|
|
161
|
-
const slot = factory.findOutputSlot(combinedNodes, innerNode.id, 'source/device', outputName);
|
|
162
|
-
if (slot) edges.push(factory.edge(innerNode.id, slot, ssNode.id, `in-${innerIdx}`));
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
return { nodes, edges, supplementalNodes };
|
|
171
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Snigel config editor plugin.
|
|
3
|
-
*
|
|
4
|
-
* Register this with the Sesame config editor at startup:
|
|
5
|
-
*
|
|
6
|
-
* import { SnigelPlugin } from '@stinkycomputing/sesame-snigel-plugin';
|
|
7
|
-
* import { registerPlugin } from './plugins';
|
|
8
|
-
* registerPlugin(SnigelPlugin);
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { Node, Edge } from '@xyflow/react';
|
|
12
|
-
import type { ISesameEditorPlugin } from '@stinkycomputing/sesame-editor-api';
|
|
13
|
-
import { snigelControlDef, snigelOutputDef, snigelSuperSourceDef } from './node-defs';
|
|
14
|
-
import { buildSnigelNodes } from './graph-builder';
|
|
15
|
-
import { serializeSnigelNodes } from './config-serializer';
|
|
16
|
-
|
|
17
|
-
export const SnigelPlugin: ISesameEditorPlugin<Node, Edge> = {
|
|
18
|
-
id: 'com.stinkycomputing.snigel',
|
|
19
|
-
|
|
20
|
-
nodeTypes: [snigelControlDef, snigelOutputDef, snigelSuperSourceDef],
|
|
21
|
-
|
|
22
|
-
buildNodes: buildSnigelNodes,
|
|
23
|
-
|
|
24
|
-
serializeNodes: serializeSnigelNodes,
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export * from './types';
|
package/src/node-defs.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/** Node type definitions for all three Snigel node types. */
|
|
2
|
-
|
|
3
|
-
import type { NodeTypeDef, MetadataDef } from '@stinkycomputing/sesame-editor-api';
|
|
4
|
-
|
|
5
|
-
export const snigelStateMetadata: MetadataDef = {
|
|
6
|
-
id: 'snigel.jlc.state.v1',
|
|
7
|
-
label: 'Remote Control State',
|
|
8
|
-
direction: 'emit',
|
|
9
|
-
portType: 'metadata/snigel-state',
|
|
10
|
-
format: 'binary',
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const snigelCommandsMetadata: MetadataDef = {
|
|
14
|
-
id: 'snigel.jlc.commands.v1',
|
|
15
|
-
label: 'Remote Control Commands',
|
|
16
|
-
direction: 'receive',
|
|
17
|
-
portType: 'metadata/snigel-commands',
|
|
18
|
-
format: 'binary',
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const snigelColor = '#784212';
|
|
22
|
-
|
|
23
|
-
export const snigelControlDef: NodeTypeDef = {
|
|
24
|
-
type: 'snigel/control',
|
|
25
|
-
title: 'Snigel Replay',
|
|
26
|
-
category: 'snigel',
|
|
27
|
-
menuGroup: 'Snigel',
|
|
28
|
-
menuOrder: 7,
|
|
29
|
-
menuItemOrder: -1,
|
|
30
|
-
color: snigelColor,
|
|
31
|
-
inputs: [
|
|
32
|
-
{ label: 'Source 1', type: 'source/device' },
|
|
33
|
-
],
|
|
34
|
-
outputs: [
|
|
35
|
-
{ label: 'Output 1', type: 'source/snigel', maxConnections: 1 },
|
|
36
|
-
],
|
|
37
|
-
widgets: [
|
|
38
|
-
{ type: 'number', label: 'Size (GB)', property: 'sizeGb', options: { min: 0, max: 1000, step: 1, precision: 0 } },
|
|
39
|
-
{ type: 'text', label: 'Port', property: 'controllerPort' },
|
|
40
|
-
{ type: 'text', label: 'Video folder', property: 'videoFilePath' },
|
|
41
|
-
{ type: 'text', label: 'Ingest folder', property: 'ingestFolder' },
|
|
42
|
-
{ type: 'text', label: 'Egress folder', property: 'egressFolder' },
|
|
43
|
-
{ type: 'combo', label: 'Codec', property: 'codec', options: { values: ['h264', 'hevc', 'av1'] } },
|
|
44
|
-
{ type: 'number', label: 'Bitrate', property: 'bitrate', options: { min: 0, max: 10000000, step: 1000, precision: 0 } },
|
|
45
|
-
],
|
|
46
|
-
defaultProperties: { id: '', controllerPort: 'COM1', videoFilePath: '', ingestFolder: '', egressFolder: '', sizeGb: 1, bitrate: 50000, codec: 'hevc' },
|
|
47
|
-
dynamicPorts: true,
|
|
48
|
-
metadata: [snigelStateMetadata, snigelCommandsMetadata],
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export const snigelOutputDef: NodeTypeDef = {
|
|
52
|
-
type: 'snigel/output',
|
|
53
|
-
title: 'Snigel Output',
|
|
54
|
-
category: 'snigel-output',
|
|
55
|
-
menuGroup: 'Snigel',
|
|
56
|
-
menuOrder: 7,
|
|
57
|
-
color: snigelColor,
|
|
58
|
-
inputs: [
|
|
59
|
-
{ label: 'Source', type: 'source/snigel' },
|
|
60
|
-
],
|
|
61
|
-
outputs: [
|
|
62
|
-
{ label: 'Video', type: 'video/composition', name: '/compositor' },
|
|
63
|
-
{ label: 'Audio 1+2', type: 'audio/stereo', name: '/audio_mixer_0' },
|
|
64
|
-
{ label: 'Audio 3+4', type: 'audio/stereo', name: '/audio_mixer_1' },
|
|
65
|
-
{ label: 'Audio 5+6', type: 'audio/stereo', name: '/audio_mixer_2' },
|
|
66
|
-
{ label: 'Audio 7+8', type: 'audio/stereo', name: '/audio_mixer_3' },
|
|
67
|
-
{ label: 'Audio Bus 1+2', type: 'audio/raw', name: '/audio_mixer_0' },
|
|
68
|
-
{ label: 'Audio Bus 3+4', type: 'audio/raw', name: '/audio_mixer_1' },
|
|
69
|
-
{ label: 'Audio Bus 5+6', type: 'audio/raw', name: '/audio_mixer_2' },
|
|
70
|
-
{ label: 'Audio Bus 7+8', type: 'audio/raw', name: '/audio_mixer_3' },
|
|
71
|
-
],
|
|
72
|
-
widgets: [
|
|
73
|
-
{ type: 'combo', label: 'Type', property: 'type', options: { values: ['pvw', 'pgm'] } },
|
|
74
|
-
],
|
|
75
|
-
defaultProperties: { id: '', type: 'pgm' },
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
export const snigelSuperSourceDef: NodeTypeDef = {
|
|
79
|
-
type: 'snigel/super-source',
|
|
80
|
-
title: 'Snigel Super Slow',
|
|
81
|
-
category: 'snigel-super',
|
|
82
|
-
menuGroup: 'Snigel',
|
|
83
|
-
menuOrder: 7,
|
|
84
|
-
color: snigelColor,
|
|
85
|
-
inputs: [
|
|
86
|
-
{ label: 'Source 1', type: 'source/device' },
|
|
87
|
-
],
|
|
88
|
-
outputs: [
|
|
89
|
-
{ label: 'Source', type: 'source/device' },
|
|
90
|
-
],
|
|
91
|
-
widgets: [],
|
|
92
|
-
defaultProperties: { id: '' },
|
|
93
|
-
dynamicPorts: true,
|
|
94
|
-
};
|
package/src/types.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/** Snigel-specific config types (formerly in sesame-interop). */
|
|
2
|
-
|
|
3
|
-
export interface ISuperSlowSnigelSource {
|
|
4
|
-
id: string;
|
|
5
|
-
type: 'super';
|
|
6
|
-
sources: string[];
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface ISnigel {
|
|
10
|
-
id: string;
|
|
11
|
-
sources: (string | ISuperSlowSnigelSource)[];
|
|
12
|
-
sizeGb: number;
|
|
13
|
-
videoFilePath: string;
|
|
14
|
-
ingestFolder: string;
|
|
15
|
-
egressFolder: string;
|
|
16
|
-
video: {
|
|
17
|
-
encoder?: {
|
|
18
|
-
preset?: string;
|
|
19
|
-
bitrateKbs?: number;
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
controllerPort: string;
|
|
23
|
-
outputs: ISnigelOutput[];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export type SnigelOutputType = 'pgm' | 'pvw';
|
|
27
|
-
|
|
28
|
-
export interface ISnigelOutput {
|
|
29
|
-
id: string;
|
|
30
|
-
type: SnigelOutputType;
|
|
31
|
-
video: Record<string, unknown>;
|
|
32
|
-
}
|