@vertesia/workflow 0.56.0 → 0.58.0

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.
Files changed (64) hide show
  1. package/lib/cjs/activities/generateEmbeddings.js +158 -61
  2. package/lib/cjs/activities/generateEmbeddings.js.map +1 -1
  3. package/lib/cjs/activities/generateOrAssignContentType.js +19 -8
  4. package/lib/cjs/activities/generateOrAssignContentType.js.map +1 -1
  5. package/lib/cjs/activities/index-dsl.js +4 -2
  6. package/lib/cjs/activities/index-dsl.js.map +1 -1
  7. package/lib/cjs/activities/renditions/generateImageRendition.js +57 -0
  8. package/lib/cjs/activities/renditions/generateImageRendition.js.map +1 -0
  9. package/lib/cjs/activities/renditions/generateVideoRendition.js +196 -0
  10. package/lib/cjs/activities/renditions/generateVideoRendition.js.map +1 -0
  11. package/lib/cjs/dsl/dsl-workflow.js +8 -0
  12. package/lib/cjs/dsl/dsl-workflow.js.map +1 -1
  13. package/lib/cjs/index.js +3 -1
  14. package/lib/cjs/index.js.map +1 -1
  15. package/lib/cjs/utils/renditions.js +88 -0
  16. package/lib/cjs/utils/renditions.js.map +1 -0
  17. package/lib/esm/activities/generateEmbeddings.js +160 -63
  18. package/lib/esm/activities/generateEmbeddings.js.map +1 -1
  19. package/lib/esm/activities/generateOrAssignContentType.js +21 -10
  20. package/lib/esm/activities/generateOrAssignContentType.js.map +1 -1
  21. package/lib/esm/activities/index-dsl.js +2 -1
  22. package/lib/esm/activities/index-dsl.js.map +1 -1
  23. package/lib/esm/activities/renditions/generateImageRendition.js +54 -0
  24. package/lib/esm/activities/renditions/generateImageRendition.js.map +1 -0
  25. package/lib/esm/activities/renditions/generateVideoRendition.js +190 -0
  26. package/lib/esm/activities/renditions/generateVideoRendition.js.map +1 -0
  27. package/lib/esm/dsl/dsl-workflow.js +8 -0
  28. package/lib/esm/dsl/dsl-workflow.js.map +1 -1
  29. package/lib/esm/index.js +3 -1
  30. package/lib/esm/index.js.map +1 -1
  31. package/lib/esm/utils/renditions.js +80 -0
  32. package/lib/esm/utils/renditions.js.map +1 -0
  33. package/lib/types/activities/generateEmbeddings.d.ts +1 -1
  34. package/lib/types/activities/generateEmbeddings.d.ts.map +1 -1
  35. package/lib/types/activities/generateOrAssignContentType.d.ts.map +1 -1
  36. package/lib/types/activities/index-dsl.d.ts +2 -1
  37. package/lib/types/activities/index-dsl.d.ts.map +1 -1
  38. package/lib/types/activities/{generateImageRendition.d.ts → renditions/generateImageRendition.d.ts} +4 -5
  39. package/lib/types/activities/renditions/generateImageRendition.d.ts.map +1 -0
  40. package/lib/types/activities/renditions/generateVideoRendition.d.ts +15 -0
  41. package/lib/types/activities/renditions/generateVideoRendition.d.ts.map +1 -0
  42. package/lib/types/dsl/dsl-workflow.d.ts.map +1 -1
  43. package/lib/types/index.d.ts +3 -1
  44. package/lib/types/index.d.ts.map +1 -1
  45. package/lib/types/utils/renditions.d.ts +23 -0
  46. package/lib/types/utils/renditions.d.ts.map +1 -0
  47. package/lib/workflows-bundle.js +99 -34
  48. package/package.json +3 -3
  49. package/src/activities/generateEmbeddings.ts +440 -296
  50. package/src/activities/generateOrAssignContentType.ts +185 -144
  51. package/src/activities/index-dsl.ts +2 -1
  52. package/src/activities/renditions/generateImageRendition.ts +99 -0
  53. package/src/activities/renditions/generateVideoRendition.ts +288 -0
  54. package/src/dsl/dsl-workflow.ts +8 -0
  55. package/src/dsl/workflow-exec-child.test.ts +1 -0
  56. package/src/dsl/workflow.test.ts +1 -0
  57. package/src/index.ts +3 -1
  58. package/src/utils/renditions.ts +124 -0
  59. package/lib/cjs/activities/generateImageRendition.js +0 -167
  60. package/lib/cjs/activities/generateImageRendition.js.map +0 -1
  61. package/lib/esm/activities/generateImageRendition.js +0 -161
  62. package/lib/esm/activities/generateImageRendition.js.map +0 -1
  63. package/lib/types/activities/generateImageRendition.d.ts.map +0 -1
  64. package/src/activities/generateImageRendition.ts +0 -202
@@ -1,161 +0,0 @@
1
- import { log } from "@temporalio/activity";
2
- import { NodeStreamSource } from "@vertesia/client/node";
3
- import ffmpeg from 'fluent-ffmpeg';
4
- import fs from 'fs';
5
- import os from 'os';
6
- import path from 'path';
7
- import { imageResizer } from "../conversion/image.js";
8
- import { setupActivity } from "../dsl/setup/ActivityContext.js";
9
- import { NoDocumentFound, WorkflowParamNotFound } from "../errors.js";
10
- import { saveBlobToTempFile } from "../utils/blobs.js";
11
- export async function generateImageRendition(payload) {
12
- const { client, objectId, params: originParams } = await setupActivity(payload);
13
- // Fix: Use maxHeightWidth if max_hw is not provided
14
- const params = {
15
- ...originParams,
16
- max_hw: originParams.max_hw || originParams.maxHeightWidth || 1024, // Default to 1024 if both are missing
17
- format: originParams.format || originParams.format_output || 'png' // Default to png if format is missing
18
- };
19
- log.info(`Generating image rendition for ${objectId}`, { originParams, params });
20
- const inputObject = await client.objects.retrieve(objectId).catch((err) => {
21
- log.error(`Failed to retrieve document ${objectId}`, { err });
22
- if (err.message.includes("not found")) {
23
- throw new NoDocumentFound(`Document ${objectId} not found`, [objectId]);
24
- }
25
- throw err;
26
- });
27
- const renditionType = await client.types.getTypeByName("Rendition");
28
- if (!params.format) {
29
- log.error(`Format not found`);
30
- throw new WorkflowParamNotFound(`format`);
31
- }
32
- if (!renditionType) {
33
- log.error(`Rendition type not found`);
34
- throw new NoDocumentFound(`Rendition type not found`, [objectId]);
35
- }
36
- if (!inputObject.content?.source) {
37
- log.error(`Document ${objectId} has no source`);
38
- throw new NoDocumentFound(`Document ${objectId} has no source`, [objectId]);
39
- }
40
- if (!inputObject.content.type || (!inputObject.content.type?.startsWith("image/") && !inputObject.content.type?.startsWith("video/"))) {
41
- log.error(`Document ${objectId} is not an image or a video: ${inputObject.content.type}`);
42
- throw new NoDocumentFound(`Document ${objectId} is not an image or a video: ${inputObject.content.type}`, [objectId]);
43
- }
44
- //array of rendition files to upload
45
- let renditionPages = [];
46
- if (inputObject.content.type.startsWith('image/')) {
47
- const imageFile = await saveBlobToTempFile(client, inputObject.content.source);
48
- log.info(`Image ${objectId} copied to ${imageFile}`);
49
- renditionPages.push(imageFile);
50
- }
51
- else if (inputObject.content.type.startsWith('video/')) {
52
- const videoFile = await saveBlobToTempFile(client, inputObject.content.source);
53
- const tempOutputDir = fs.mkdtempSync(path.join(os.tmpdir(), 'video-rendition-'));
54
- const thumbnailPath = path.join(tempOutputDir, 'thumbnail.png');
55
- try {
56
- // Extract a frame at 10% of the video duration
57
- await new Promise((resolve, reject) => {
58
- ffmpeg.ffprobe(videoFile, (err, metadata) => {
59
- if (err) {
60
- log.error(`Failed to probe video metadata: ${err.message}`);
61
- return reject(err);
62
- }
63
- const duration = metadata.format.duration || 0;
64
- const timestamp = Math.max(0.1 * duration, 1);
65
- ffmpeg(videoFile)
66
- .screenshots({
67
- timestamps: [timestamp],
68
- filename: 'thumbnail.png',
69
- folder: tempOutputDir,
70
- size: `${params.max_hw}x?`
71
- })
72
- .on('end', () => {
73
- log.info(`Video frame extraction complete for ${objectId}`);
74
- resolve();
75
- })
76
- .on('error', (err) => {
77
- log.error(`Error extracting frame from video: ${err.message}`);
78
- reject(err);
79
- });
80
- });
81
- });
82
- if (fs.existsSync(thumbnailPath)) {
83
- renditionPages.push(thumbnailPath);
84
- }
85
- else {
86
- throw new Error(`Failed to generate thumbnail for video ${objectId}`);
87
- }
88
- }
89
- catch (error) {
90
- log.error(`Error generating image rendition for video: ${error instanceof Error ? error.message : 'Unknown error'}`);
91
- throw new Error(`Failed to generate image rendition for video: ${objectId}`);
92
- }
93
- }
94
- //generate rendition name, pass an index for multi parts
95
- const getRenditionName = (index = 0) => {
96
- const name = `renditions/${objectId}/${params.max_hw}/${index}.${params.format}`;
97
- return name;
98
- };
99
- if (!renditionPages || !renditionPages.length) {
100
- log.error(`Failed to generate rendition for ${objectId}`);
101
- throw new Error(`Failed to generate rendition for ${objectId}`);
102
- }
103
- log.info(`Uploading rendition for ${objectId} with ${renditionPages.length} pages (max_hw: ${params.max_hw}, format: ${params.format})`, { renditionPages });
104
- const uploads = renditionPages.map(async (page, i) => {
105
- const pageId = getRenditionName(i);
106
- let resizedImagePath = null;
107
- try {
108
- log.info(`Resizing image for ${objectId} page ${i}`, { page, params });
109
- // Resize the image using ImageMagick
110
- resizedImagePath = await imageResizer(page, params.max_hw, params.format);
111
- // Create a read stream from the resized image file
112
- const fileStream = fs.createReadStream(resizedImagePath);
113
- const format = "image/" + params.format;
114
- const fileId = pageId.split("/").pop() ?? "0." + params.format;
115
- const source = new NodeStreamSource(fileStream, fileId, format, pageId);
116
- log.info(`Uploading rendition for ${objectId} page ${i} with max_hw: ${params.max_hw} and format: ${params.format}`, {
117
- resizedImagePath,
118
- fileId,
119
- format,
120
- pageId,
121
- });
122
- const result = await client.objects.upload(source).catch((err) => {
123
- log.error(`Failed to upload rendition for ${objectId} page ${i}`, {
124
- error: err,
125
- errorMessage: err.message,
126
- stack: err.stack
127
- });
128
- return Promise.reject(`Upload failed: ${err.message}`);
129
- });
130
- log.info(`Rendition uploaded for ${objectId} page ${i}`, { result });
131
- return result;
132
- }
133
- catch (error) {
134
- log.error(`Failed to process rendition for ${objectId} page ${i}`, { error });
135
- return Promise.reject(error instanceof Error ? error.message : null);
136
- }
137
- });
138
- const uploaded = await Promise.all(uploads);
139
- if (!uploaded || !uploaded.length || !uploaded[0]) {
140
- log.error(`Failed to upload rendition for ${objectId}`, { uploaded });
141
- throw new Error(`Failed to upload rendition for ${objectId} - upload object is empty`);
142
- }
143
- log.info(`Creating rendition for ${objectId} with max_hw: ${params.max_hw} and format: ${params.format}`, {
144
- uploaded,
145
- });
146
- const rendition = await client.objects.create({
147
- name: inputObject.name + ` [Rendition ${params.max_hw}]`,
148
- type: renditionType.id,
149
- parent: inputObject.id,
150
- content: uploaded[0],
151
- properties: {
152
- mime_type: "image/" + params.format,
153
- source_etag: inputObject.content.source,
154
- height: params.max_hw,
155
- width: params.max_hw,
156
- },
157
- });
158
- log.info(`Rendition ${rendition.id} created for ${objectId}`, { rendition });
159
- return { id: rendition.id, format: params.format, status: "success" };
160
- }
161
- //# sourceMappingURL=generateImageRendition.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generateImageRendition.js","sourceRoot":"","sources":["../../../src/activities/generateImageRendition.ts"],"names":[],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"generateImageRendition.d.ts","sourceRoot":"","sources":["../../../src/activities/generateImageRendition.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,2BAA2B,EAAE,eAAe,EAAuB,MAAM,kBAAkB,CAAC;AAUrG,UAAU,4BAA4B;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAuB,SAAQ,eAAe,CAAC,4BAA4B,CAAC;IACzF,IAAI,EAAE,wBAAwB,CAAC;CAClC;AAED,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,2BAA2B,CAAC,4BAA4B,CAAC;;;;GAoL9G"}
@@ -1,202 +0,0 @@
1
- import { log } from "@temporalio/activity";
2
- import { NodeStreamSource } from "@vertesia/client/node";
3
- import { DSLActivityExecutionPayload, DSLActivitySpec, RenditionProperties } from "@vertesia/common";
4
- import ffmpeg from 'fluent-ffmpeg';
5
- import fs from 'fs';
6
- import os from 'os';
7
- import path from 'path';
8
- import { imageResizer } from "../conversion/image.js";
9
- import { setupActivity } from "../dsl/setup/ActivityContext.js";
10
- import { NoDocumentFound, WorkflowParamNotFound } from "../errors.js";
11
- import { saveBlobToTempFile } from "../utils/blobs.js";
12
-
13
- interface GenerateImageRenditionParams {
14
- max_hw: number; //maximum size of the longest side of the image
15
- format: string; //format of the output image
16
- }
17
-
18
- export interface GenerateImageRendition extends DSLActivitySpec<GenerateImageRenditionParams> {
19
- name: "generateImageRendition";
20
- }
21
-
22
- export async function generateImageRendition(payload: DSLActivityExecutionPayload<GenerateImageRenditionParams>) {
23
- const { client, objectId, params: originParams } = await setupActivity<GenerateImageRenditionParams>(payload);
24
-
25
- // Fix: Use maxHeightWidth if max_hw is not provided
26
- const params = {
27
- ...originParams,
28
- max_hw: originParams.max_hw || (originParams as any).maxHeightWidth || 1024, // Default to 1024 if both are missing
29
- format: originParams.format || (originParams as any).format_output || 'png' // Default to png if format is missing
30
- };
31
-
32
- log.info(`Generating image rendition for ${objectId}`, { originParams, params });
33
-
34
- const inputObject = await client.objects.retrieve(objectId).catch((err) => {
35
- log.error(`Failed to retrieve document ${objectId}`, { err });
36
- if (err.message.includes("not found")) {
37
- throw new NoDocumentFound(`Document ${objectId} not found`, [objectId]);
38
- }
39
- throw err;
40
- });
41
- const renditionType = await client.types.getTypeByName("Rendition");
42
-
43
- if (!params.format) {
44
- log.error(`Format not found`);
45
- throw new WorkflowParamNotFound(`format`);
46
- }
47
-
48
- if (!renditionType) {
49
- log.error(`Rendition type not found`);
50
- throw new NoDocumentFound(`Rendition type not found`, [objectId]);
51
- }
52
-
53
- if (!inputObject.content?.source) {
54
- log.error(`Document ${objectId} has no source`);
55
- throw new NoDocumentFound(`Document ${objectId} has no source`, [objectId]);
56
- }
57
-
58
- if (!inputObject.content.type || (!inputObject.content.type?.startsWith("image/") && !inputObject.content.type?.startsWith("video/"))) {
59
- log.error(`Document ${objectId} is not an image or a video: ${inputObject.content.type}`);
60
- throw new NoDocumentFound(`Document ${objectId} is not an image or a video: ${inputObject.content.type}`, [objectId]);
61
- }
62
-
63
- //array of rendition files to upload
64
- let renditionPages: string[] = [];
65
-
66
- if (inputObject.content.type.startsWith('image/')) {
67
- const imageFile = await saveBlobToTempFile(client, inputObject.content.source);
68
- log.info(`Image ${objectId} copied to ${imageFile}`);
69
- renditionPages.push(imageFile);
70
- } else if (inputObject.content.type.startsWith('video/')) {
71
- const videoFile = await saveBlobToTempFile(client, inputObject.content.source);
72
- const tempOutputDir = fs.mkdtempSync(path.join(os.tmpdir(), 'video-rendition-'));
73
- const thumbnailPath = path.join(tempOutputDir, 'thumbnail.png');
74
-
75
- try {
76
- // Extract a frame at 10% of the video duration
77
- await new Promise<void>((resolve, reject) => {
78
- ffmpeg.ffprobe(videoFile, (err, metadata) => {
79
- if (err) {
80
- log.error(`Failed to probe video metadata: ${err.message}`);
81
- return reject(err);
82
- }
83
-
84
- const duration = metadata.format.duration || 0;
85
- const timestamp = Math.max(0.1 * duration, 1);
86
-
87
- ffmpeg(videoFile)
88
- .screenshots({
89
- timestamps: [timestamp],
90
- filename: 'thumbnail.png',
91
- folder: tempOutputDir,
92
- size: `${params.max_hw}x?`
93
- })
94
- .on('end', () => {
95
- log.info(`Video frame extraction complete for ${objectId}`);
96
- resolve();
97
- })
98
- .on('error', (err) => {
99
- log.error(`Error extracting frame from video: ${err.message}`);
100
- reject(err);
101
- });
102
- });
103
- });
104
-
105
- if (fs.existsSync(thumbnailPath)) {
106
- renditionPages.push(thumbnailPath);
107
- } else {
108
- throw new Error(`Failed to generate thumbnail for video ${objectId}`);
109
- }
110
- } catch (error) {
111
- log.error(`Error generating image rendition for video: ${error instanceof Error ? error.message : 'Unknown error'}`);
112
- throw new Error(`Failed to generate image rendition for video: ${objectId}`);
113
- }
114
- }
115
-
116
- //generate rendition name, pass an index for multi parts
117
- const getRenditionName = (index: number = 0) => {
118
- const name = `renditions/${objectId}/${params.max_hw}/${index}.${params.format}`;
119
- return name;
120
- };
121
-
122
- if (!renditionPages || !renditionPages.length) {
123
- log.error(`Failed to generate rendition for ${objectId}`);
124
- throw new Error(`Failed to generate rendition for ${objectId}`);
125
- }
126
-
127
- log.info(
128
- `Uploading rendition for ${objectId} with ${renditionPages.length} pages (max_hw: ${params.max_hw}, format: ${params.format})`,
129
- { renditionPages },
130
- );
131
- const uploads = renditionPages.map(async (page, i) => {
132
- const pageId = getRenditionName(i);
133
- let resizedImagePath = null;
134
-
135
- try {
136
- log.info(`Resizing image for ${objectId} page ${i}`, { page, params });
137
- // Resize the image using ImageMagick
138
- resizedImagePath = await imageResizer(page, params.max_hw, params.format);
139
-
140
- // Create a read stream from the resized image file
141
- const fileStream = fs.createReadStream(resizedImagePath);
142
- const format = "image/" + params.format;
143
- const fileId = pageId.split("/").pop() ?? "0." + params.format;
144
- const source = new NodeStreamSource(
145
- fileStream,
146
- fileId,
147
- format,
148
- pageId,
149
- );
150
-
151
- log.info(
152
- `Uploading rendition for ${objectId} page ${i} with max_hw: ${params.max_hw} and format: ${params.format}`, {
153
- resizedImagePath,
154
- fileId,
155
- format,
156
- pageId,
157
- }
158
- );
159
-
160
- const result = await client.objects.upload(source).catch((err) => {
161
- log.error(`Failed to upload rendition for ${objectId} page ${i}`, {
162
- error: err,
163
- errorMessage: err.message,
164
- stack: err.stack
165
- });
166
- return Promise.reject(`Upload failed: ${err.message}`);
167
- });
168
- log.info(`Rendition uploaded for ${objectId} page ${i}`, { result });
169
-
170
- return result;
171
- } catch (error) {
172
- log.error(`Failed to process rendition for ${objectId} page ${i}`, { error });
173
- return Promise.reject(error instanceof Error ? error.message : null);
174
- }
175
- });
176
-
177
- const uploaded = await Promise.all(uploads);
178
- if (!uploaded || !uploaded.length || !uploaded[0]) {
179
- log.error(`Failed to upload rendition for ${objectId}`, { uploaded });
180
- throw new Error(`Failed to upload rendition for ${objectId} - upload object is empty`);
181
- }
182
-
183
- log.info(`Creating rendition for ${objectId} with max_hw: ${params.max_hw} and format: ${params.format}`, {
184
- uploaded,
185
- });
186
- const rendition = await client.objects.create({
187
- name: inputObject.name + ` [Rendition ${params.max_hw}]`,
188
- type: renditionType.id,
189
- parent: inputObject.id,
190
- content: uploaded[0],
191
- properties: {
192
- mime_type: "image/" + params.format,
193
- source_etag: inputObject.content.source,
194
- height: params.max_hw,
195
- width: params.max_hw,
196
- } satisfies RenditionProperties,
197
- });
198
-
199
- log.info(`Rendition ${rendition.id} created for ${objectId}`, { rendition });
200
-
201
- return { id: rendition.id, format: params.format, status: "success" };
202
- }