@juspay/neurolink 9.22.2 → 9.23.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.
- package/CHANGELOG.md +12 -0
- package/dist/adapters/video/directorPipeline.d.ts +31 -0
- package/dist/adapters/video/directorPipeline.js +516 -0
- package/dist/adapters/video/ffmpegAdapter.d.ts +78 -0
- package/dist/adapters/video/ffmpegAdapter.js +206 -0
- package/dist/adapters/video/frameExtractor.d.ts +28 -0
- package/dist/adapters/video/frameExtractor.js +143 -0
- package/dist/adapters/video/vertexVideoHandler.d.ts +25 -25
- package/dist/adapters/video/vertexVideoHandler.js +173 -42
- package/dist/adapters/video/videoMerger.d.ts +22 -0
- package/dist/adapters/video/videoMerger.js +171 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.js +2 -0
- package/dist/constants/videoErrors.d.ts +45 -0
- package/dist/constants/videoErrors.js +46 -0
- package/dist/core/baseProvider.js +42 -1
- package/dist/lib/adapters/video/directorPipeline.d.ts +31 -0
- package/dist/lib/adapters/video/directorPipeline.js +517 -0
- package/dist/lib/adapters/video/ffmpegAdapter.d.ts +78 -0
- package/dist/lib/adapters/video/ffmpegAdapter.js +207 -0
- package/dist/lib/adapters/video/frameExtractor.d.ts +28 -0
- package/dist/lib/adapters/video/frameExtractor.js +144 -0
- package/dist/lib/adapters/video/vertexVideoHandler.d.ts +25 -25
- package/dist/lib/adapters/video/vertexVideoHandler.js +173 -42
- package/dist/lib/adapters/video/videoMerger.d.ts +22 -0
- package/dist/lib/adapters/video/videoMerger.js +172 -0
- package/dist/lib/constants/index.d.ts +1 -0
- package/dist/lib/constants/index.js +2 -0
- package/dist/lib/constants/videoErrors.d.ts +45 -0
- package/dist/lib/constants/videoErrors.js +47 -0
- package/dist/lib/core/baseProvider.js +42 -1
- package/dist/lib/types/content.d.ts +1 -1
- package/dist/lib/types/generateTypes.d.ts +18 -1
- package/dist/lib/types/multimodal.d.ts +64 -4
- package/dist/lib/types/multimodal.js +36 -1
- package/dist/lib/utils/parameterValidation.d.ts +8 -1
- package/dist/lib/utils/parameterValidation.js +80 -1
- package/dist/types/content.d.ts +1 -1
- package/dist/types/generateTypes.d.ts +18 -1
- package/dist/types/multimodal.d.ts +64 -4
- package/dist/types/multimodal.js +36 -1
- package/dist/utils/parameterValidation.d.ts +8 -1
- package/dist/utils/parameterValidation.js +80 -1
- package/package.json +1 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared FFmpeg Adapter for Video Operations
|
|
3
|
+
*
|
|
4
|
+
* Centralizes FFmpeg binary resolution, process execution, and temporary file
|
|
5
|
+
* management for all video adapter modules (frameExtractor, videoMerger).
|
|
6
|
+
*
|
|
7
|
+
* Follows the adapter pattern used in `src/lib/adapters/tts/` and
|
|
8
|
+
* `src/lib/adapters/providerImageAdapter.ts`.
|
|
9
|
+
*
|
|
10
|
+
* @module adapters/video/ffmpegAdapter
|
|
11
|
+
*/
|
|
12
|
+
import { randomUUID } from "node:crypto";
|
|
13
|
+
import { readdirSync, rmdirSync, unlinkSync } from "node:fs";
|
|
14
|
+
import { mkdir, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
15
|
+
import { tmpdir } from "node:os";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { logger } from "../../utils/logger.js";
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// CONSTANTS
|
|
20
|
+
// ============================================================================
|
|
21
|
+
/** Timeout for frame-extraction FFmpeg operations (30 seconds) */
|
|
22
|
+
export const FFMPEG_FRAME_TIMEOUT_MS = 30_000;
|
|
23
|
+
/** Timeout for merge/concat FFmpeg operations (2 minutes) */
|
|
24
|
+
export const FFMPEG_MERGE_TIMEOUT_MS = 120_000;
|
|
25
|
+
/** Max stdout/stderr buffer for frame extraction (10 MB) */
|
|
26
|
+
export const FFMPEG_FRAME_MAX_BUFFER = 10 * 1024 * 1024;
|
|
27
|
+
/** Max stdout/stderr buffer for merge operations (50 MB) */
|
|
28
|
+
export const FFMPEG_MERGE_MAX_BUFFER = 50 * 1024 * 1024;
|
|
29
|
+
/** FFmpeg JPEG quality scale (2 = high quality, range 2-31) */
|
|
30
|
+
export const JPEG_QUALITY = "2";
|
|
31
|
+
/** Seconds before end-of-video to seek when extracting last frame */
|
|
32
|
+
export const LAST_FRAME_SEEK_OFFSET = "0.5";
|
|
33
|
+
/** Minimum valid MP4 buffer size in bytes (ftyp header = 8 bytes minimum) */
|
|
34
|
+
export const MIN_VIDEO_BUFFER_SIZE = 12;
|
|
35
|
+
/** MP4 ftyp box magic bytes at offset 4: "ftyp" */
|
|
36
|
+
const FTYP_MAGIC = Buffer.from([0x66, 0x74, 0x79, 0x70]);
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// TEMP DIRECTORY MANAGEMENT
|
|
39
|
+
// ============================================================================
|
|
40
|
+
/** Track active temp directories for process cleanup */
|
|
41
|
+
const activeTempDirs = new Set();
|
|
42
|
+
let cleanupRegistered = false;
|
|
43
|
+
/**
|
|
44
|
+
* Register process-level cleanup handlers once.
|
|
45
|
+
* Removes all tracked temp directories on abnormal exit.
|
|
46
|
+
*/
|
|
47
|
+
function ensureCleanupRegistered() {
|
|
48
|
+
if (cleanupRegistered) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
cleanupRegistered = true;
|
|
52
|
+
const cleanup = () => {
|
|
53
|
+
for (const dir of activeTempDirs) {
|
|
54
|
+
try {
|
|
55
|
+
// Sync removal for exit handlers — best-effort only
|
|
56
|
+
const entries = readdirSync(dir);
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
try {
|
|
59
|
+
unlinkSync(join(dir, entry));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
/* best-effort */
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
rmdirSync(dir);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
/* best-effort */
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
activeTempDirs.clear();
|
|
72
|
+
};
|
|
73
|
+
process.on("exit", cleanup);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create a tracked temporary directory for FFmpeg operations.
|
|
77
|
+
*
|
|
78
|
+
* @param prefix - Directory name prefix (e.g. "frame", "merge")
|
|
79
|
+
* @returns Absolute path to the created directory
|
|
80
|
+
*/
|
|
81
|
+
export async function createTrackedTempDir(prefix) {
|
|
82
|
+
ensureCleanupRegistered();
|
|
83
|
+
const dir = join(tmpdir(), `neurolink-${prefix}-${randomUUID()}`);
|
|
84
|
+
await mkdir(dir, { recursive: true });
|
|
85
|
+
activeTempDirs.add(dir);
|
|
86
|
+
return dir;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Clean up temporary files and their parent directory.
|
|
90
|
+
* Logs failures at debug level instead of swallowing silently.
|
|
91
|
+
*
|
|
92
|
+
* @param tempDir - The temporary directory to remove
|
|
93
|
+
* @param files - File paths within tempDir to delete
|
|
94
|
+
*/
|
|
95
|
+
export async function cleanupTempFiles(tempDir, ...files) {
|
|
96
|
+
for (const filePath of files) {
|
|
97
|
+
try {
|
|
98
|
+
await unlink(filePath);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
logger.debug("Failed to clean up temp file", {
|
|
102
|
+
path: filePath,
|
|
103
|
+
error: err instanceof Error ? err.message : String(err),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
logger.debug("Failed to remove temp directory", {
|
|
112
|
+
path: tempDir,
|
|
113
|
+
error: err instanceof Error ? err.message : String(err),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
activeTempDirs.delete(tempDir);
|
|
117
|
+
}
|
|
118
|
+
// ============================================================================
|
|
119
|
+
// FFMPEG BINARY RESOLUTION
|
|
120
|
+
// ============================================================================
|
|
121
|
+
/** Cached FFmpeg binary path to avoid repeated resolution */
|
|
122
|
+
let cachedFfmpegPath = null;
|
|
123
|
+
/**
|
|
124
|
+
* Resolve the FFmpeg binary path.
|
|
125
|
+
*
|
|
126
|
+
* Resolution order:
|
|
127
|
+
* 1. `FFMPEG_PATH` environment variable
|
|
128
|
+
* 2. `ffmpeg-static` npm package (optional peer dependency)
|
|
129
|
+
* 3. System `ffmpeg` on PATH
|
|
130
|
+
*
|
|
131
|
+
* @returns Absolute or relative path to the FFmpeg binary
|
|
132
|
+
*/
|
|
133
|
+
export async function getFfmpegPath() {
|
|
134
|
+
if (cachedFfmpegPath) {
|
|
135
|
+
return cachedFfmpegPath;
|
|
136
|
+
}
|
|
137
|
+
if (process.env.FFMPEG_PATH) {
|
|
138
|
+
cachedFfmpegPath = process.env.FFMPEG_PATH;
|
|
139
|
+
return cachedFfmpegPath;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const ffmpegStatic = await import("ffmpeg-static");
|
|
143
|
+
const staticPath = ffmpegStatic.default ?? ffmpegStatic;
|
|
144
|
+
if (typeof staticPath === "string" && staticPath.length > 0) {
|
|
145
|
+
cachedFfmpegPath = staticPath;
|
|
146
|
+
return cachedFfmpegPath;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// ffmpeg-static not installed — fall through to system binary
|
|
151
|
+
logger.debug("ffmpeg-static not available, using system ffmpeg binary");
|
|
152
|
+
}
|
|
153
|
+
cachedFfmpegPath = "ffmpeg";
|
|
154
|
+
logger.warn("Using system ffmpeg binary. If video operations fail with ENOENT, install ffmpeg-static or set FFMPEG_PATH.");
|
|
155
|
+
return cachedFfmpegPath;
|
|
156
|
+
}
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// FFMPEG PROCESS EXECUTION
|
|
159
|
+
// ============================================================================
|
|
160
|
+
/**
|
|
161
|
+
* Run an FFmpeg command via `child_process.execFile`.
|
|
162
|
+
*
|
|
163
|
+
* @param args - FFmpeg CLI arguments (without the binary path)
|
|
164
|
+
* @param options - Timeout and buffer size overrides
|
|
165
|
+
* @returns stdout and stderr from the process
|
|
166
|
+
* @throws Error if the process exits with a non-zero code or times out
|
|
167
|
+
*/
|
|
168
|
+
export async function runFfmpeg(args, options = {}) {
|
|
169
|
+
const { execFile } = await import("node:child_process");
|
|
170
|
+
const ffmpegPath = await getFfmpegPath();
|
|
171
|
+
const timeoutMs = options.timeoutMs ?? FFMPEG_FRAME_TIMEOUT_MS;
|
|
172
|
+
const maxBuffer = options.maxBuffer ?? FFMPEG_FRAME_MAX_BUFFER;
|
|
173
|
+
return new Promise((resolve, reject) => {
|
|
174
|
+
const proc = execFile(ffmpegPath, args, { timeout: timeoutMs, maxBuffer }, (error, stdout, stderr) => {
|
|
175
|
+
if (error) {
|
|
176
|
+
reject(error);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
resolve({ stdout: stdout || "", stderr: stderr || "" });
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
proc.on("error", reject);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
// ============================================================================
|
|
186
|
+
// BUFFER VALIDATION
|
|
187
|
+
// ============================================================================
|
|
188
|
+
/**
|
|
189
|
+
* Validate that a buffer looks like a valid MP4 video.
|
|
190
|
+
*
|
|
191
|
+
* Checks minimum size and the presence of an `ftyp` box header.
|
|
192
|
+
*
|
|
193
|
+
* @param buffer - Buffer to validate
|
|
194
|
+
* @returns `true` if the buffer passes basic MP4 validation
|
|
195
|
+
*/
|
|
196
|
+
export function isValidMp4Buffer(buffer) {
|
|
197
|
+
if (buffer.length < MIN_VIDEO_BUFFER_SIZE) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
// Check for ftyp box: bytes 4-7 should be "ftyp"
|
|
201
|
+
return buffer.subarray(4, 8).equals(FTYP_MAGIC);
|
|
202
|
+
}
|
|
203
|
+
// ============================================================================
|
|
204
|
+
// FILE I/O HELPERS
|
|
205
|
+
// ============================================================================
|
|
206
|
+
export { writeFile, readFile, join };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame Extractor for Director Mode
|
|
3
|
+
*
|
|
4
|
+
* Extracts first and last frames from MP4 video buffers using FFmpeg.
|
|
5
|
+
* Used by Director Mode to obtain boundary frames for Veo 3.1
|
|
6
|
+
* first-and-last-frame interpolation transitions.
|
|
7
|
+
*
|
|
8
|
+
* Uses the shared FFmpeg adapter for binary resolution, temp file management,
|
|
9
|
+
* and process execution — following the adapter pattern in `adapters/tts/`.
|
|
10
|
+
*
|
|
11
|
+
* @module adapters/video/frameExtractor
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Extract the first frame from a video buffer as JPEG.
|
|
15
|
+
*
|
|
16
|
+
* @param videoBuffer - MP4 video buffer
|
|
17
|
+
* @returns JPEG image buffer of the first frame
|
|
18
|
+
* @throws {VideoError} If buffer validation or extraction fails
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractFirstFrame(videoBuffer: Buffer): Promise<Buffer>;
|
|
21
|
+
/**
|
|
22
|
+
* Extract the last frame from a video buffer as JPEG.
|
|
23
|
+
*
|
|
24
|
+
* @param videoBuffer - MP4 video buffer
|
|
25
|
+
* @returns JPEG image buffer of the last frame
|
|
26
|
+
* @throws {VideoError} If buffer validation or extraction fails
|
|
27
|
+
*/
|
|
28
|
+
export declare function extractLastFrame(videoBuffer: Buffer): Promise<Buffer>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frame Extractor for Director Mode
|
|
3
|
+
*
|
|
4
|
+
* Extracts first and last frames from MP4 video buffers using FFmpeg.
|
|
5
|
+
* Used by Director Mode to obtain boundary frames for Veo 3.1
|
|
6
|
+
* first-and-last-frame interpolation transitions.
|
|
7
|
+
*
|
|
8
|
+
* Uses the shared FFmpeg adapter for binary resolution, temp file management,
|
|
9
|
+
* and process execution — following the adapter pattern in `adapters/tts/`.
|
|
10
|
+
*
|
|
11
|
+
* @module adapters/video/frameExtractor
|
|
12
|
+
*/
|
|
13
|
+
import { ErrorCategory, ErrorSeverity } from "../../constants/enums.js";
|
|
14
|
+
import { logger } from "../../utils/logger.js";
|
|
15
|
+
import { cleanupTempFiles, createTrackedTempDir, FFMPEG_FRAME_MAX_BUFFER, FFMPEG_FRAME_TIMEOUT_MS, isValidMp4Buffer, JPEG_QUALITY, join, LAST_FRAME_SEEK_OFFSET, readFile, runFfmpeg, writeFile, } from "./ffmpegAdapter.js";
|
|
16
|
+
import { VIDEO_ERROR_CODES } from "../../constants/videoErrors.js";
|
|
17
|
+
import { VideoError } from "./vertexVideoHandler.js";
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// INTERNAL HELPERS
|
|
20
|
+
// ============================================================================
|
|
21
|
+
/**
|
|
22
|
+
* Validate a video buffer before FFmpeg processing.
|
|
23
|
+
*
|
|
24
|
+
* @throws {VideoError} If the buffer is empty, too small, or not a valid MP4
|
|
25
|
+
*/
|
|
26
|
+
function assertValidVideoBuffer(videoBuffer, operation) {
|
|
27
|
+
if (!Buffer.isBuffer(videoBuffer) || videoBuffer.length === 0) {
|
|
28
|
+
throw new VideoError({
|
|
29
|
+
code: VIDEO_ERROR_CODES.DIRECTOR_FRAME_EXTRACTION_FAILED,
|
|
30
|
+
message: `Cannot ${operation}: video buffer is empty or not a Buffer`,
|
|
31
|
+
category: ErrorCategory.VALIDATION,
|
|
32
|
+
severity: ErrorSeverity.HIGH,
|
|
33
|
+
retriable: false,
|
|
34
|
+
context: { operation, bufferSize: videoBuffer?.length ?? 0 },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (!isValidMp4Buffer(videoBuffer)) {
|
|
38
|
+
throw new VideoError({
|
|
39
|
+
code: VIDEO_ERROR_CODES.DIRECTOR_FRAME_EXTRACTION_FAILED,
|
|
40
|
+
message: `Cannot ${operation}: buffer does not appear to be a valid MP4 (missing ftyp header)`,
|
|
41
|
+
category: ErrorCategory.VALIDATION,
|
|
42
|
+
severity: ErrorSeverity.HIGH,
|
|
43
|
+
retriable: false,
|
|
44
|
+
context: {
|
|
45
|
+
operation,
|
|
46
|
+
bufferSize: videoBuffer.length,
|
|
47
|
+
headerHex: videoBuffer.subarray(0, 12).toString("hex"),
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Core frame extraction logic shared between first/last frame extractors.
|
|
54
|
+
*/
|
|
55
|
+
async function extractFrame(videoBuffer, position) {
|
|
56
|
+
const startTime = Date.now();
|
|
57
|
+
assertValidVideoBuffer(videoBuffer, `extract ${position} frame`);
|
|
58
|
+
const tempDir = await createTrackedTempDir("frame");
|
|
59
|
+
const inputPath = join(tempDir, "input.mp4");
|
|
60
|
+
const outputPath = join(tempDir, `${position}_frame.jpg`);
|
|
61
|
+
try {
|
|
62
|
+
await writeFile(inputPath, videoBuffer);
|
|
63
|
+
const args = position === "first"
|
|
64
|
+
? [
|
|
65
|
+
"-y",
|
|
66
|
+
"-i",
|
|
67
|
+
inputPath,
|
|
68
|
+
"-vframes",
|
|
69
|
+
"1",
|
|
70
|
+
"-q:v",
|
|
71
|
+
JPEG_QUALITY,
|
|
72
|
+
"-f",
|
|
73
|
+
"image2",
|
|
74
|
+
outputPath,
|
|
75
|
+
]
|
|
76
|
+
: [
|
|
77
|
+
"-y",
|
|
78
|
+
"-sseof",
|
|
79
|
+
`-${LAST_FRAME_SEEK_OFFSET}`,
|
|
80
|
+
"-i",
|
|
81
|
+
inputPath,
|
|
82
|
+
"-update",
|
|
83
|
+
"1",
|
|
84
|
+
"-q:v",
|
|
85
|
+
JPEG_QUALITY,
|
|
86
|
+
"-f",
|
|
87
|
+
"image2",
|
|
88
|
+
outputPath,
|
|
89
|
+
];
|
|
90
|
+
await runFfmpeg(args, {
|
|
91
|
+
timeoutMs: FFMPEG_FRAME_TIMEOUT_MS,
|
|
92
|
+
maxBuffer: FFMPEG_FRAME_MAX_BUFFER,
|
|
93
|
+
});
|
|
94
|
+
const frameBuffer = await readFile(outputPath);
|
|
95
|
+
logger.debug(`Extracted ${position} frame`, {
|
|
96
|
+
inputSize: videoBuffer.length,
|
|
97
|
+
frameSize: frameBuffer.length,
|
|
98
|
+
elapsedMs: Date.now() - startTime,
|
|
99
|
+
});
|
|
100
|
+
return frameBuffer;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
// Re-throw VideoErrors as-is
|
|
104
|
+
if (error instanceof VideoError) {
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
throw new VideoError({
|
|
108
|
+
code: VIDEO_ERROR_CODES.DIRECTOR_FRAME_EXTRACTION_FAILED,
|
|
109
|
+
message: `Failed to extract ${position} frame: ${error instanceof Error ? error.message : String(error)}`,
|
|
110
|
+
category: ErrorCategory.EXECUTION,
|
|
111
|
+
severity: ErrorSeverity.HIGH,
|
|
112
|
+
retriable: false,
|
|
113
|
+
context: { position, bufferSize: videoBuffer.length },
|
|
114
|
+
originalError: error instanceof Error ? error : undefined,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
await cleanupTempFiles(tempDir, inputPath, outputPath);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// ============================================================================
|
|
122
|
+
// PUBLIC API
|
|
123
|
+
// ============================================================================
|
|
124
|
+
/**
|
|
125
|
+
* Extract the first frame from a video buffer as JPEG.
|
|
126
|
+
*
|
|
127
|
+
* @param videoBuffer - MP4 video buffer
|
|
128
|
+
* @returns JPEG image buffer of the first frame
|
|
129
|
+
* @throws {VideoError} If buffer validation or extraction fails
|
|
130
|
+
*/
|
|
131
|
+
export async function extractFirstFrame(videoBuffer) {
|
|
132
|
+
return extractFrame(videoBuffer, "first");
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Extract the last frame from a video buffer as JPEG.
|
|
136
|
+
*
|
|
137
|
+
* @param videoBuffer - MP4 video buffer
|
|
138
|
+
* @returns JPEG image buffer of the last frame
|
|
139
|
+
* @throws {VideoError} If buffer validation or extraction fails
|
|
140
|
+
*/
|
|
141
|
+
export async function extractLastFrame(videoBuffer) {
|
|
142
|
+
return extractFrame(videoBuffer, "last");
|
|
143
|
+
}
|
|
@@ -10,35 +10,14 @@
|
|
|
10
10
|
* @see https://cloud.google.com/vertex-ai/generative-ai/docs/video/generate-videos
|
|
11
11
|
*/
|
|
12
12
|
import { ErrorCategory, ErrorSeverity } from "../../constants/enums.js";
|
|
13
|
+
import { VIDEO_ERROR_CODES } from "../../constants/videoErrors.js";
|
|
13
14
|
import type { VideoGenerationResult, VideoOutputOptions } from "../../types/multimodal.js";
|
|
14
15
|
import { NeuroLinkError } from "../../utils/errorHandling.js";
|
|
15
16
|
/**
|
|
16
|
-
* Video
|
|
17
|
-
*
|
|
18
|
-
* These are for runtime/execution errors during video generation.
|
|
19
|
-
* Pure option/shape validation (missing image option, invalid config values, etc.)
|
|
20
|
-
* is handled by parameterValidation.ts using ERROR_CODES from errorHandling.ts.
|
|
21
|
-
*
|
|
22
|
-
* Error categorization:
|
|
23
|
-
* - INVALID_INPUT → ErrorCategory.execution (runtime I/O failures)
|
|
24
|
-
* - parameterValidation errors → ErrorCategory.validation (schema/option issues)
|
|
25
|
-
*
|
|
26
|
-
* Following TTS pattern (TTS_ERROR_CODES + TTSError in ttsProcessor.ts)
|
|
17
|
+
* Video error codes - re-exported from constants module for backward compatibility.
|
|
18
|
+
* @see {@link VIDEO_ERROR_CODES} in constants/videoErrors.ts for definitions
|
|
27
19
|
*/
|
|
28
|
-
export
|
|
29
|
-
/** Video generation API call failed */
|
|
30
|
-
readonly GENERATION_FAILED: "VIDEO_GENERATION_FAILED";
|
|
31
|
-
/** Provider (Vertex AI) not properly configured */
|
|
32
|
-
readonly PROVIDER_NOT_CONFIGURED: "VIDEO_PROVIDER_NOT_CONFIGURED";
|
|
33
|
-
/** Polling for video completion timed out */
|
|
34
|
-
readonly POLL_TIMEOUT: "VIDEO_POLL_TIMEOUT";
|
|
35
|
-
/**
|
|
36
|
-
* Runtime I/O error during input processing.
|
|
37
|
-
* Used for: failed URL fetch, failed file read, corrupt/unreadable buffer.
|
|
38
|
-
* NOT for: missing options or invalid config shapes (use parameterValidation).
|
|
39
|
-
*/
|
|
40
|
-
readonly INVALID_INPUT: "VIDEO_INVALID_INPUT";
|
|
41
|
-
};
|
|
20
|
+
export { VIDEO_ERROR_CODES };
|
|
42
21
|
/**
|
|
43
22
|
* Video generation error class
|
|
44
23
|
* Extends NeuroLinkError for consistent error handling across the SDK
|
|
@@ -99,3 +78,24 @@ export declare function isVertexVideoConfigured(): boolean;
|
|
|
99
78
|
* ```
|
|
100
79
|
*/
|
|
101
80
|
export declare function generateVideoWithVertex(image: Buffer, prompt: string, options?: VideoOutputOptions, region?: string): Promise<VideoGenerationResult>;
|
|
81
|
+
/**
|
|
82
|
+
* Generate a transition clip using Veo 3.1 Fast's first-and-last-frame interpolation.
|
|
83
|
+
*
|
|
84
|
+
* This calls the Veo API with both `image` (first frame) and `lastFrame` (last frame),
|
|
85
|
+
* producing a video that smoothly interpolates between the two frames.
|
|
86
|
+
*
|
|
87
|
+
* @param firstFrame - JPEG buffer of the first frame (last frame of clip N)
|
|
88
|
+
* @param lastFrame - JPEG buffer of the last frame (first frame of clip N+1)
|
|
89
|
+
* @param prompt - Transition prompt describing desired visual flow
|
|
90
|
+
* @param options - Video output options (resolution, aspect ratio, audio)
|
|
91
|
+
* @param durationSeconds - Duration of the transition clip (4, 6, or 8)
|
|
92
|
+
* @param region - Vertex AI region override
|
|
93
|
+
* @returns Video buffer of the transition clip
|
|
94
|
+
*
|
|
95
|
+
* @throws {VideoError} When API returns an error or polling times out
|
|
96
|
+
*/
|
|
97
|
+
export declare function generateTransitionWithVertex(firstFrame: Buffer, lastFrame: Buffer, prompt: string, options?: {
|
|
98
|
+
aspectRatio?: "9:16" | "16:9";
|
|
99
|
+
resolution?: "720p" | "1080p";
|
|
100
|
+
audio?: boolean;
|
|
101
|
+
}, durationSeconds?: 4 | 6 | 8, region?: string): Promise<Buffer>;
|