streamby-core 0.29.5 → 0.29.7
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.
|
@@ -78,6 +78,8 @@ const ensureTablesExist = async (pool) => {
|
|
|
78
78
|
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS credentials JSONB DEFAULT '[]';
|
|
79
79
|
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "apiConnections" JSONB DEFAULT '[]';
|
|
80
80
|
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS "dbConnections" JSONB DEFAULT '[]';
|
|
81
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS workflow JSONB;
|
|
82
|
+
ALTER TABLE streamby.projects ADD COLUMN IF NOT EXISTS pipelines JSONB DEFAULT '[]';
|
|
81
83
|
`);
|
|
82
84
|
// Ensure dbType exists with a non-null default
|
|
83
85
|
await pool.query(`
|
|
@@ -5,7 +5,7 @@ const express_1 = require("express");
|
|
|
5
5
|
const manager_1 = require("../../models/manager");
|
|
6
6
|
const auth_1 = require("../../utils/auth");
|
|
7
7
|
const export_1 = require("../../services/export");
|
|
8
|
-
const
|
|
8
|
+
const executor_1 = require("../../services/executor");
|
|
9
9
|
const connectionManager_1 = require("../../adapters/database/connectionManager");
|
|
10
10
|
const mongodb_1 = require("mongodb");
|
|
11
11
|
const encryption_1 = require("../../utils/encryption");
|
|
@@ -355,7 +355,7 @@ function exportRouter(config) {
|
|
|
355
355
|
}
|
|
356
356
|
let data;
|
|
357
357
|
if (exportMetadata.nodeSchema) {
|
|
358
|
-
data = await (0,
|
|
358
|
+
data = await (0, executor_1.executeExport)(exportMetadata.nodeSchema, project, config);
|
|
359
359
|
return res.json(data ?? null);
|
|
360
360
|
}
|
|
361
361
|
// Legacy path: raw JSON or external API export
|
|
@@ -9,7 +9,9 @@ const mongodb_1 = require("mongodb");
|
|
|
9
9
|
function workflowRouter(config) {
|
|
10
10
|
const router = (0, express_1.Router)();
|
|
11
11
|
const Project = (0, manager_1.getModel)('projects');
|
|
12
|
-
//
|
|
12
|
+
// A project has exactly one Workflow (the central canvas), stored as the single
|
|
13
|
+
// `project.workflow` object. PATCH upserts it.
|
|
14
|
+
const resolveWorkflow = (project) => project.workflow ?? null;
|
|
13
15
|
router.get('/projects/:id/workflow', async (req, res) => {
|
|
14
16
|
try {
|
|
15
17
|
const auth = await config.authProvider(req);
|
|
@@ -20,7 +22,7 @@ function workflowRouter(config) {
|
|
|
20
22
|
return res.status(404).json({ message: 'Project not found' });
|
|
21
23
|
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
22
24
|
return res.status(403).json({ message: 'Unauthorized access' });
|
|
23
|
-
const workflow = (project
|
|
25
|
+
const workflow = resolveWorkflow(project);
|
|
24
26
|
if (!workflow)
|
|
25
27
|
return res.status(404).json({ message: 'No workflow found for this project' });
|
|
26
28
|
res.json({ data: workflow, message: 'Workflow fetched successfully' });
|
|
@@ -39,150 +41,34 @@ function workflowRouter(config) {
|
|
|
39
41
|
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
40
42
|
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
41
43
|
}
|
|
42
|
-
const existing = (project.workflows ?? [])[0];
|
|
43
|
-
if (!existing)
|
|
44
|
-
return res.status(404).json({ message: 'No workflow found for this project' });
|
|
45
|
-
const { name, description, status, nodeSchema } = req.body;
|
|
46
|
-
const updated = {
|
|
47
|
-
...existing,
|
|
48
|
-
name: name ?? existing.name,
|
|
49
|
-
description: description ?? existing.description,
|
|
50
|
-
status: status ?? existing.status,
|
|
51
|
-
nodeSchema: nodeSchema !== undefined ? nodeSchema : existing.nodeSchema,
|
|
52
|
-
updatedAt: new Date(),
|
|
53
|
-
};
|
|
54
|
-
if (project.dbType === 'sql') {
|
|
55
|
-
const SqlProject = (0, manager_1.getModel)('projects', 'sql');
|
|
56
|
-
const updatedWorkflows = (project.workflows ?? []).map((w, i) => i === 0 ? updated : w);
|
|
57
|
-
await SqlProject.update({ _id: projectId }, { workflows: updatedWorkflows });
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
await Project.update({ _id: new mongodb_1.ObjectId(projectId), 'workflows.id': existing.id }, {
|
|
61
|
-
$set: {
|
|
62
|
-
'workflows.$.name': updated.name,
|
|
63
|
-
'workflows.$.description': updated.description,
|
|
64
|
-
'workflows.$.status': updated.status,
|
|
65
|
-
'workflows.$.nodeSchema': updated.nodeSchema,
|
|
66
|
-
'workflows.$.updatedAt': updated.updatedAt,
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
res.json({ data: updated, message: 'Workflow updated successfully' });
|
|
71
|
-
}
|
|
72
|
-
catch (err) {
|
|
73
|
-
res.status(500).json({ message: 'Failed to update workflow', details: err.message });
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
router.get('/projects/:id/workflows', async (req, res) => {
|
|
77
|
-
try {
|
|
78
|
-
const auth = await config.authProvider(req);
|
|
79
|
-
if (!auth?.userId)
|
|
80
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
81
|
-
const project = await Project.findOne({ _id: req.params.id });
|
|
82
|
-
if (!project)
|
|
83
|
-
return res.status(404).json({ message: 'Project not found' });
|
|
84
|
-
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
85
|
-
return res.status(403).json({ message: 'Unauthorized access' });
|
|
86
|
-
res.json({ data: project.workflows ?? [], message: 'Workflows fetched successfully' });
|
|
87
|
-
}
|
|
88
|
-
catch (err) {
|
|
89
|
-
res.status(500).json({ message: 'Failed to fetch workflows', details: err.message });
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
router.get('/projects/:id/workflows/:workflow_id', async (req, res) => {
|
|
93
|
-
try {
|
|
94
|
-
const auth = await config.authProvider(req);
|
|
95
|
-
if (!auth?.userId)
|
|
96
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
97
|
-
const project = await Project.findOne({ _id: req.params.id });
|
|
98
|
-
if (!project)
|
|
99
|
-
return res.status(404).json({ message: 'Project not found' });
|
|
100
|
-
if (!(0, auth_1.isProjectMember)(project, auth.userId))
|
|
101
|
-
return res.status(403).json({ message: 'Unauthorized access' });
|
|
102
|
-
const workflow = (project.workflows ?? []).find((w) => w.id === req.params.workflow_id);
|
|
103
|
-
if (!workflow)
|
|
104
|
-
return res.status(404).json({ message: 'Workflow not found' });
|
|
105
|
-
res.json({ data: workflow, message: 'Workflow fetched successfully' });
|
|
106
|
-
}
|
|
107
|
-
catch (err) {
|
|
108
|
-
res.status(500).json({ message: 'Failed to fetch workflow', details: err.message });
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
router.post('/projects/:id/workflows', async (req, res) => {
|
|
112
|
-
try {
|
|
113
|
-
const auth = await config.authProvider(req);
|
|
114
|
-
if (!auth?.userId)
|
|
115
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
116
|
-
const projectId = req.params.id;
|
|
117
|
-
const project = await Project.findOne({ _id: projectId });
|
|
118
|
-
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
119
|
-
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
120
|
-
}
|
|
121
|
-
const { name, description } = req.body;
|
|
122
|
-
if (!name)
|
|
123
|
-
return res.status(400).json({ message: 'Missing workflow name' });
|
|
124
44
|
const now = new Date();
|
|
125
|
-
const
|
|
45
|
+
const existing = resolveWorkflow(project); // upsert: create the single workflow if absent
|
|
46
|
+
const base = existing ?? {
|
|
126
47
|
id: (0, crypto_1.randomUUID)(),
|
|
127
|
-
name,
|
|
128
|
-
description:
|
|
48
|
+
name: 'Workflow',
|
|
49
|
+
description: '',
|
|
129
50
|
status: 'draft',
|
|
130
51
|
projectId,
|
|
131
52
|
nodeSchema: null,
|
|
132
53
|
createdAt: now,
|
|
133
|
-
updatedAt: now,
|
|
134
54
|
};
|
|
135
|
-
if (project.dbType === 'sql') {
|
|
136
|
-
const SqlProject = (0, manager_1.getModel)('projects', 'sql');
|
|
137
|
-
const currentWorkflows = project.workflows ?? [];
|
|
138
|
-
await SqlProject.update({ _id: projectId }, { workflows: [...currentWorkflows, workflow] });
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
await Project.update({ _id: new mongodb_1.ObjectId(projectId) }, { $push: { workflows: workflow } });
|
|
142
|
-
}
|
|
143
|
-
res.status(201).json({ data: workflow, message: 'Workflow created successfully' });
|
|
144
|
-
}
|
|
145
|
-
catch (err) {
|
|
146
|
-
res.status(500).json({ message: 'Failed to create workflow', details: err.message });
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
router.patch('/projects/:id/workflows/:workflow_id', async (req, res) => {
|
|
150
|
-
try {
|
|
151
|
-
const auth = await config.authProvider(req);
|
|
152
|
-
if (!auth?.userId)
|
|
153
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
154
|
-
const { id: projectId, workflow_id: workflowId } = req.params;
|
|
155
|
-
const project = await Project.findOne({ _id: projectId });
|
|
156
|
-
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
157
|
-
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
158
|
-
}
|
|
159
|
-
const existing = (project.workflows ?? []).find((w) => w.id === workflowId);
|
|
160
|
-
if (!existing)
|
|
161
|
-
return res.status(404).json({ message: 'Workflow not found' });
|
|
162
55
|
const { name, description, status, nodeSchema } = req.body;
|
|
163
56
|
const updated = {
|
|
164
|
-
...
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
57
|
+
...base,
|
|
58
|
+
id: base.id,
|
|
59
|
+
projectId,
|
|
60
|
+
name: name ?? base.name,
|
|
61
|
+
description: description ?? base.description,
|
|
62
|
+
status: status ?? base.status,
|
|
63
|
+
nodeSchema: nodeSchema !== undefined ? nodeSchema : base.nodeSchema,
|
|
64
|
+
updatedAt: now,
|
|
170
65
|
};
|
|
171
66
|
if (project.dbType === 'sql') {
|
|
172
67
|
const SqlProject = (0, manager_1.getModel)('projects', 'sql');
|
|
173
|
-
|
|
174
|
-
await SqlProject.update({ _id: projectId }, { workflows: updatedWorkflows });
|
|
68
|
+
await SqlProject.update({ _id: projectId }, { workflow: updated });
|
|
175
69
|
}
|
|
176
70
|
else {
|
|
177
|
-
await Project.update({ _id: new mongodb_1.ObjectId(projectId),
|
|
178
|
-
$set: {
|
|
179
|
-
'workflows.$.name': updated.name,
|
|
180
|
-
'workflows.$.description': updated.description,
|
|
181
|
-
'workflows.$.status': updated.status,
|
|
182
|
-
'workflows.$.nodeSchema': updated.nodeSchema,
|
|
183
|
-
'workflows.$.updatedAt': updated.updatedAt,
|
|
184
|
-
},
|
|
185
|
-
});
|
|
71
|
+
await Project.update({ _id: new mongodb_1.ObjectId(projectId) }, { $set: { workflow: updated } });
|
|
186
72
|
}
|
|
187
73
|
res.json({ data: updated, message: 'Workflow updated successfully' });
|
|
188
74
|
}
|
|
@@ -190,32 +76,5 @@ function workflowRouter(config) {
|
|
|
190
76
|
res.status(500).json({ message: 'Failed to update workflow', details: err.message });
|
|
191
77
|
}
|
|
192
78
|
});
|
|
193
|
-
router.delete('/projects/:id/workflows/:workflow_id', async (req, res) => {
|
|
194
|
-
try {
|
|
195
|
-
const auth = await config.authProvider(req);
|
|
196
|
-
if (!auth?.userId)
|
|
197
|
-
return res.status(401).json({ message: 'Unauthorized' });
|
|
198
|
-
const { id: projectId, workflow_id: workflowId } = req.params;
|
|
199
|
-
const project = await Project.findOne({ _id: projectId });
|
|
200
|
-
if (!project || !(0, auth_1.isProjectMember)(project, auth.userId)) {
|
|
201
|
-
return res.status(403).json({ message: 'Unauthorized project access' });
|
|
202
|
-
}
|
|
203
|
-
const existing = (project.workflows ?? []).find((w) => w.id === workflowId);
|
|
204
|
-
if (!existing)
|
|
205
|
-
return res.status(404).json({ message: 'Workflow not found' });
|
|
206
|
-
if (project.dbType === 'sql') {
|
|
207
|
-
const SqlProject = (0, manager_1.getModel)('projects', 'sql');
|
|
208
|
-
const updatedWorkflows = (project.workflows ?? []).filter((w) => w.id !== workflowId);
|
|
209
|
-
await SqlProject.update({ _id: projectId }, { workflows: updatedWorkflows });
|
|
210
|
-
}
|
|
211
|
-
else {
|
|
212
|
-
await Project.update({ _id: new mongodb_1.ObjectId(projectId) }, { $pull: { workflows: { id: workflowId } } });
|
|
213
|
-
}
|
|
214
|
-
res.json({ message: 'Workflow deleted successfully' });
|
|
215
|
-
}
|
|
216
|
-
catch (err) {
|
|
217
|
-
res.status(500).json({ message: 'Failed to delete workflow', details: err.message });
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
79
|
return router;
|
|
221
80
|
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.executeExport = executeExport;
|
|
4
|
+
const connectionManager_1 = require("../adapters/database/connectionManager");
|
|
5
|
+
const encryption_1 = require("../utils/encryption");
|
|
6
|
+
const dbConnection_1 = require("./dbConnection");
|
|
7
|
+
const jobQueue_1 = require("./jobQueue");
|
|
8
|
+
const mediaProcessor_1 = require("./mediaProcessor");
|
|
9
|
+
const vfxProcessor_1 = require("./vfxProcessor");
|
|
10
|
+
const aiProcessor_1 = require("./aiProcessor");
|
|
11
|
+
const storage_1 = require("../providers/storage");
|
|
12
|
+
function applyFilterConfig(payload, config) {
|
|
13
|
+
const isArr = Array.isArray(payload);
|
|
14
|
+
let r = isArr ? [...payload] : payload;
|
|
15
|
+
if (config.conditions?.length) {
|
|
16
|
+
const matches = (item) => config.conditions.every(c => {
|
|
17
|
+
const v = item?.[c.field];
|
|
18
|
+
switch (c.op) {
|
|
19
|
+
case 'eq': return String(v) === c.value;
|
|
20
|
+
case 'neq': return String(v) !== c.value;
|
|
21
|
+
case 'gt': return Number(v) > Number(c.value);
|
|
22
|
+
case 'lt': return Number(v) < Number(c.value);
|
|
23
|
+
case 'gte': return Number(v) >= Number(c.value);
|
|
24
|
+
case 'lte': return Number(v) <= Number(c.value);
|
|
25
|
+
case 'contains': return String(v).includes(c.value);
|
|
26
|
+
case 'startsWith': return String(v).startsWith(c.value);
|
|
27
|
+
case 'endsWith': return String(v).endsWith(c.value);
|
|
28
|
+
default: return true;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
r = isArr ? r.filter(matches) : (matches(r) ? r : null);
|
|
32
|
+
if (r === null)
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (config.includeFields?.length) {
|
|
36
|
+
const pick = (item) => {
|
|
37
|
+
if (!item || typeof item !== 'object')
|
|
38
|
+
return item;
|
|
39
|
+
const o = {};
|
|
40
|
+
for (const f of config.includeFields)
|
|
41
|
+
if (f in item)
|
|
42
|
+
o[f] = item[f];
|
|
43
|
+
return o;
|
|
44
|
+
};
|
|
45
|
+
r = isArr ? r.map(pick) : pick(r);
|
|
46
|
+
}
|
|
47
|
+
if (config.renameFields?.length) {
|
|
48
|
+
const ren = (item) => {
|
|
49
|
+
if (!item || typeof item !== 'object')
|
|
50
|
+
return item;
|
|
51
|
+
const o = { ...item };
|
|
52
|
+
for (const { from, to } of config.renameFields) {
|
|
53
|
+
if (from in o) {
|
|
54
|
+
o[to] = o[from];
|
|
55
|
+
delete o[from];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return o;
|
|
59
|
+
};
|
|
60
|
+
r = isArr ? r.map(ren) : ren(r);
|
|
61
|
+
}
|
|
62
|
+
if (config.limit && isArr)
|
|
63
|
+
r = r.slice(0, config.limit);
|
|
64
|
+
if (config.wrapKey)
|
|
65
|
+
r = { [config.wrapKey]: r };
|
|
66
|
+
return r;
|
|
67
|
+
}
|
|
68
|
+
function getSources(nodes, edges, targetId, targetHandle) {
|
|
69
|
+
return edges
|
|
70
|
+
.filter(e => e.target === targetId && e.targetHandle === targetHandle)
|
|
71
|
+
.map(e => nodes.find(n => n.id === e.source))
|
|
72
|
+
.filter((n) => n !== undefined);
|
|
73
|
+
}
|
|
74
|
+
function getTarget(nodes, edges, sourceId, sourceHandle) {
|
|
75
|
+
const edge = edges.find(e => e.source === sourceId && e.sourceHandle === sourceHandle);
|
|
76
|
+
return edge ? (nodes.find(n => n.id === edge.target) ?? null) : null;
|
|
77
|
+
}
|
|
78
|
+
async function executeExport(nodeSchema, project, config) {
|
|
79
|
+
const nodes = nodeSchema.nodes;
|
|
80
|
+
const edges = nodeSchema.edges;
|
|
81
|
+
// 1. Data layer — nodes connected to streamby in-bottom
|
|
82
|
+
const dataSources = getSources(nodes, edges, 'streamby', 'in-bottom');
|
|
83
|
+
const dataResults = [];
|
|
84
|
+
for (const node of dataSources) {
|
|
85
|
+
if (node.type === 'jsonInputNode') {
|
|
86
|
+
try {
|
|
87
|
+
dataResults.push(JSON.parse(node.data?.jsonString || 'null'));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
dataResults.push(null);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else if (node.type === 'dataSourceNode') {
|
|
94
|
+
const tableName = (node.data?.tableName || node.data?.subtitle || node.data?.label);
|
|
95
|
+
if (!tableName)
|
|
96
|
+
continue;
|
|
97
|
+
const connectionId = node.data?.connectionId;
|
|
98
|
+
const recordId = node.data?.recordId;
|
|
99
|
+
const projectIdentifier = project._id?.toString() ?? project.id;
|
|
100
|
+
const fetchData = async (client, type) => recordId
|
|
101
|
+
? (0, dbConnection_1.queryRecordByIdInternal)(client, type, tableName, recordId, projectIdentifier)
|
|
102
|
+
: (0, dbConnection_1.queryRecordsInternal)(client, type, tableName, 500, 0, projectIdentifier);
|
|
103
|
+
if (connectionId) {
|
|
104
|
+
const { client, type } = (0, connectionManager_1.getConnection)(connectionId);
|
|
105
|
+
dataResults.push(await fetchData(client, type));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// legacy fallback for nodes saved before connectionId was introduced
|
|
109
|
+
const targetDb = config.databases?.find((db) => db.type === project.dbType && db.main)
|
|
110
|
+
?? config.databases?.find((db) => db.type === project.dbType);
|
|
111
|
+
if (!targetDb)
|
|
112
|
+
throw new Error(`No database connection for type ${project.dbType}`);
|
|
113
|
+
const { client, type } = (0, connectionManager_1.getConnection)(targetDb.id);
|
|
114
|
+
dataResults.push(await fetchData(client, type));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (node.type === 'apiConnectionNode') {
|
|
118
|
+
const apiConnection = project.apiConnections?.find((c) => c.id === node.data?.connectionId);
|
|
119
|
+
if (!apiConnection)
|
|
120
|
+
continue;
|
|
121
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
122
|
+
if (apiConnection.credentialId && (0, encryption_1.isEncryptionKeySet)()) {
|
|
123
|
+
const credential = project.credentials?.find((c) => c.id === apiConnection.credentialId);
|
|
124
|
+
if (credential) {
|
|
125
|
+
const decrypted = (0, encryption_1.decrypt)(credential.encryptedValue);
|
|
126
|
+
const prefix = apiConnection.prefix ? `${apiConnection.prefix} ` : '';
|
|
127
|
+
headers['Authorization'] = `${prefix}${decrypted}`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const response = await fetch(apiConnection.apiUrl, { method: apiConnection.method || 'GET', headers });
|
|
131
|
+
if (!response.ok)
|
|
132
|
+
throw new Error(`API connection fetch failed: ${response.statusText}`);
|
|
133
|
+
dataResults.push(await response.json());
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
let payload = dataResults.length === 1 ? dataResults[0] : dataResults;
|
|
137
|
+
// 2. Process layer — chain from streamby out-top → processNode in-process → out-process → ...
|
|
138
|
+
const adapter = (0, storage_1.createStorageProvider)(config.storageProviders);
|
|
139
|
+
const projectId = project._id?.toString() ?? project.id;
|
|
140
|
+
// userId is not available in pipeline context; use a sentinel for job attribution
|
|
141
|
+
const systemUserId = 'pipeline';
|
|
142
|
+
let processNode = getTarget(nodes, edges, 'streamby', 'out-top');
|
|
143
|
+
while (processNode) {
|
|
144
|
+
const nodeData = processNode.data ?? {};
|
|
145
|
+
const fileId = nodeData.fileId;
|
|
146
|
+
if (processNode.type === 'transcodeNode' && fileId) {
|
|
147
|
+
const job = (0, jobQueue_1.createJob)('transcode', systemUserId, projectId, nodeData);
|
|
148
|
+
setImmediate(() => (0, mediaProcessor_1.runTranscodeJob)(job.jobId, fileId, projectId, {
|
|
149
|
+
codec: nodeData.codec,
|
|
150
|
+
resolution: nodeData.resolution,
|
|
151
|
+
outputFormat: nodeData.outputFormat,
|
|
152
|
+
bitrate: nodeData.bitrate,
|
|
153
|
+
audioCodec: nodeData.audioCodec,
|
|
154
|
+
}, adapter));
|
|
155
|
+
payload = { ...payload, jobId: job.jobId, jobType: 'transcode' };
|
|
156
|
+
}
|
|
157
|
+
else if (processNode.type === 'captionNode' && fileId) {
|
|
158
|
+
const job = (0, jobQueue_1.createJob)('caption', systemUserId, projectId, nodeData);
|
|
159
|
+
setImmediate(() => (0, mediaProcessor_1.runCaptionJob)(job.jobId, fileId, projectId, {
|
|
160
|
+
sourceLanguage: nodeData.sourceLanguage,
|
|
161
|
+
outputFormat: nodeData.outputFormat,
|
|
162
|
+
provider: nodeData.provider,
|
|
163
|
+
}));
|
|
164
|
+
payload = { ...payload, jobId: job.jobId, jobType: 'caption' };
|
|
165
|
+
}
|
|
166
|
+
else if (processNode.type === 'thumbnailNode' && fileId) {
|
|
167
|
+
const job = (0, jobQueue_1.createJob)('thumbnail', systemUserId, projectId, nodeData);
|
|
168
|
+
setImmediate(() => (0, mediaProcessor_1.runThumbnailJob)(job.jobId, fileId, projectId, {
|
|
169
|
+
timecode: nodeData.timecode,
|
|
170
|
+
resolution: nodeData.resolution,
|
|
171
|
+
strategy: nodeData.strategy,
|
|
172
|
+
}));
|
|
173
|
+
payload = { ...payload, jobId: job.jobId, jobType: 'thumbnail' };
|
|
174
|
+
}
|
|
175
|
+
else if (processNode.type === 'ingestNode' && fileId) {
|
|
176
|
+
const job = (0, jobQueue_1.createJob)('ingest', systemUserId, projectId, nodeData);
|
|
177
|
+
setImmediate(() => (0, mediaProcessor_1.runIngestJob)(job.jobId, fileId, projectId));
|
|
178
|
+
payload = { ...payload, jobId: job.jobId, jobType: 'ingest' };
|
|
179
|
+
}
|
|
180
|
+
else if (processNode.type === 'renderJobNode' && fileId) {
|
|
181
|
+
const job = (0, jobQueue_1.createJob)('render', systemUserId, projectId, nodeData);
|
|
182
|
+
setImmediate(() => (0, vfxProcessor_1.runRenderJob)(job.jobId, fileId, projectId, {
|
|
183
|
+
renderer: nodeData.renderer,
|
|
184
|
+
renderFarmConnectionId: nodeData.renderFarmConnectionId,
|
|
185
|
+
frameRange: nodeData.frameRange,
|
|
186
|
+
resolution: nodeData.resolution,
|
|
187
|
+
samples: nodeData.samples,
|
|
188
|
+
outputFormat: nodeData.outputFormat,
|
|
189
|
+
}));
|
|
190
|
+
payload = { ...payload, jobId: job.jobId, jobType: 'render' };
|
|
191
|
+
}
|
|
192
|
+
else if (processNode.type === 'formatConvertNode' && fileId) {
|
|
193
|
+
const job = (0, jobQueue_1.createJob)('format-convert', systemUserId, projectId, nodeData);
|
|
194
|
+
setImmediate(() => (0, vfxProcessor_1.runFormatConvertJob)(job.jobId, fileId, projectId, {
|
|
195
|
+
inputFormat: nodeData.inputFormat,
|
|
196
|
+
outputFormat: nodeData.outputFormat,
|
|
197
|
+
applyTransforms: nodeData.applyTransforms,
|
|
198
|
+
embedTextures: nodeData.embedTextures,
|
|
199
|
+
}));
|
|
200
|
+
payload = { ...payload, jobId: job.jobId, jobType: 'format-convert' };
|
|
201
|
+
}
|
|
202
|
+
else if (processNode.type === 'lodNode' && fileId) {
|
|
203
|
+
const job = (0, jobQueue_1.createJob)('lod', systemUserId, projectId, nodeData);
|
|
204
|
+
setImmediate(() => (0, vfxProcessor_1.runLodJob)(job.jobId, fileId, projectId, {
|
|
205
|
+
levels: nodeData.levels,
|
|
206
|
+
reductionRatios: nodeData.reductionRatios,
|
|
207
|
+
algorithm: nodeData.algorithm,
|
|
208
|
+
outputFormat: nodeData.outputFormat,
|
|
209
|
+
}));
|
|
210
|
+
payload = { ...payload, jobId: job.jobId, jobType: 'lod' };
|
|
211
|
+
}
|
|
212
|
+
else if (processNode.type === 'qcCheckNode' && fileId) {
|
|
213
|
+
const checks = nodeData.checks ?? [];
|
|
214
|
+
const checkResults = checks.map((name) => ({
|
|
215
|
+
name, passed: true, value: 'ok', threshold: 'ok',
|
|
216
|
+
}));
|
|
217
|
+
payload = { ...payload, qcReport: { checks: checkResults, overallPassed: true } };
|
|
218
|
+
}
|
|
219
|
+
else if (processNode.type === 'transcriptionNode' && fileId) {
|
|
220
|
+
const job = (0, jobQueue_1.createJob)('transcription', systemUserId, projectId, nodeData);
|
|
221
|
+
setImmediate(() => (0, aiProcessor_1.runTranscriptionJob)(job.jobId, fileId, projectId, {
|
|
222
|
+
model: nodeData.model,
|
|
223
|
+
sourceLanguage: nodeData.sourceLanguage,
|
|
224
|
+
outputFormats: nodeData.outputFormats,
|
|
225
|
+
provider: nodeData.provider,
|
|
226
|
+
credentialId: nodeData.credentialId,
|
|
227
|
+
}));
|
|
228
|
+
payload = { ...payload, jobId: job.jobId, jobType: 'transcription' };
|
|
229
|
+
}
|
|
230
|
+
else if (processNode.type === 'upscaleNode' && fileId) {
|
|
231
|
+
const job = (0, jobQueue_1.createJob)('upscale', systemUserId, projectId, nodeData);
|
|
232
|
+
setImmediate(() => (0, aiProcessor_1.runUpscaleJob)(job.jobId, fileId, projectId, {
|
|
233
|
+
scale: nodeData.scale,
|
|
234
|
+
model: nodeData.model,
|
|
235
|
+
mode: nodeData.mode,
|
|
236
|
+
credentialId: nodeData.credentialId,
|
|
237
|
+
}));
|
|
238
|
+
payload = { ...payload, jobId: job.jobId, jobType: 'upscale' };
|
|
239
|
+
}
|
|
240
|
+
else if (processNode.type === 'proceduralAssetNode') {
|
|
241
|
+
const job = (0, jobQueue_1.createJob)('generate-asset', systemUserId, projectId, nodeData);
|
|
242
|
+
setImmediate(() => (0, aiProcessor_1.runGenerateAssetJob)(job.jobId, projectId, {
|
|
243
|
+
assetType: nodeData.assetType,
|
|
244
|
+
provider: nodeData.provider,
|
|
245
|
+
prompt: nodeData.prompt ?? '',
|
|
246
|
+
seed: nodeData.seed,
|
|
247
|
+
credentialId: nodeData.credentialId,
|
|
248
|
+
}));
|
|
249
|
+
payload = { ...payload, jobId: job.jobId, jobType: 'generate-asset' };
|
|
250
|
+
}
|
|
251
|
+
else if (processNode.type === 'pipelineSuggestNode') {
|
|
252
|
+
const suggestion = await (0, aiProcessor_1.buildPipelineSuggestion)({ nodes, edges }, Object.keys(nodeSchema));
|
|
253
|
+
payload = { ...payload, pipelineSuggestion: suggestion };
|
|
254
|
+
}
|
|
255
|
+
processNode = getTarget(nodes, edges, processNode.id, 'out-process');
|
|
256
|
+
}
|
|
257
|
+
// 3. Filter + output lane — streamby out-right → filterNode|annotationNode|deliverableNode|distributionNode
|
|
258
|
+
const outputLaneTypes = new Set(['filterNode', 'annotationNode', 'deliverableNode', 'distributionNode']);
|
|
259
|
+
let outputNode = getTarget(nodes, edges, 'streamby', 'out-right');
|
|
260
|
+
while (outputNode) {
|
|
261
|
+
if (outputNode.type === 'filterNode') {
|
|
262
|
+
const cfg = outputNode.data?.filterConfig;
|
|
263
|
+
if (cfg)
|
|
264
|
+
payload = applyFilterConfig(payload, cfg);
|
|
265
|
+
}
|
|
266
|
+
else if (outputNode.type === 'deliverableNode') {
|
|
267
|
+
payload = {
|
|
268
|
+
...payload,
|
|
269
|
+
deliverable: {
|
|
270
|
+
type: outputNode.data?.deliverableType ?? 'asset-bundle',
|
|
271
|
+
version: outputNode.data?.deliverableVersion ?? '1.0.0',
|
|
272
|
+
},
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
else if (outputNode.type === 'distributionNode') {
|
|
276
|
+
// Distribution is async — the actual publish is handled by the host worker
|
|
277
|
+
payload = {
|
|
278
|
+
...payload,
|
|
279
|
+
distribution: {
|
|
280
|
+
target: outputNode.data?.distributionTarget,
|
|
281
|
+
connectionId: outputNode.data?.distributionConnectionId,
|
|
282
|
+
channel: outputNode.data?.channel,
|
|
283
|
+
status: 'queued',
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
// annotationNode — pass-through; annotations are stored separately via the review API
|
|
288
|
+
const next = getTarget(nodes, edges, outputNode.id, 'out-filter');
|
|
289
|
+
outputNode = next && outputLaneTypes.has(next.type) ? next : null;
|
|
290
|
+
}
|
|
291
|
+
return payload;
|
|
292
|
+
}
|