UNPKG

@juspay/neurolink

Version:

Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio

222 lines 7.06 kB
/** * Video file utilities for CLI * * Provides functionality for saving generated video to files with proper * error handling and directory creation. Follows the pattern established * by audioFileUtils.ts for TTS output handling. * * @module cli/utils/videoFileUtils */ import fs from "fs"; import path from "path"; /** * Format file size in human-readable format * * @param bytes - Size in bytes * @returns Formatted string (e.g., "32 KB", "1.5 MB") */ export function formatVideoFileSize(bytes) { if (bytes < 1024) { return `${bytes} B`; } else if (bytes < 1024 * 1024) { return `${(bytes / 1024).toFixed(1)} KB`; } else if (bytes < 1024 * 1024 * 1024) { return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } else { return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`; } } /** * Resolve the output path, handling both absolute and relative paths * * @param outputPath - User-specified output path * @returns Resolved absolute path */ export function resolveVideoOutputPath(outputPath) { if (path.isAbsolute(outputPath)) { return outputPath; } return path.resolve(process.cwd(), outputPath); } /** * Ensure parent directories exist, creating them if necessary * * @param filePath - Full path to the file */ export async function ensureVideoDirectoryExists(filePath) { const directory = path.dirname(filePath); try { await fs.promises.access(directory, fs.constants.F_OK); } catch { // Directory doesn't exist, create it recursively await fs.promises.mkdir(directory, { recursive: true }); } } /** * Get appropriate file extension for video media type * * @param mediaType - Video media type * @returns File extension (including dot) */ export function getVideoExtension(mediaType) { switch (mediaType) { case "video/mp4": return ".mp4"; case "video/webm": return ".webm"; default: // Unreachable but kept for runtime safety return ".mp4"; } } /** * Validate and normalize output path, adding extension if needed * * If the specified path has no extension or an invalid extension, the appropriate * extension (.mp4 or .webm) will be added based on the mediaType. * If the extension doesn't match the mediaType (e.g., .webm specified but mediaType * is video/mp4), the extension will be replaced to match the actual format. * * @param outputPath - User-specified output path * @param mediaType - Video media type for extension * @returns Normalized output path with correct extension */ export function normalizeVideoOutputPath(outputPath, mediaType = "video/mp4") { const resolvedPath = resolveVideoOutputPath(outputPath); const ext = path.extname(resolvedPath).toLowerCase(); const expectedExt = getVideoExtension(mediaType); const validExtensions = [".mp4", ".webm"]; if (!ext || !validExtensions.includes(ext)) { return resolvedPath + expectedExt; } // If extension doesn't match mediaType, replace it if (ext !== expectedExt) { const basePath = resolvedPath.slice(0, -ext.length); return basePath + expectedExt; } return resolvedPath; } /** * Format video duration in human-readable format * * @param seconds - Duration in seconds * @returns Formatted string (e.g., "4s", "1m 30s") */ export function formatVideoDuration(seconds) { if (seconds < 60) { return `${seconds}s`; } const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; if (remainingSeconds === 0) { return `${minutes}m`; } return `${minutes}m ${remainingSeconds}s`; } /** * Format video dimensions in human-readable format * * @param width - Video width in pixels * @param height - Video height in pixels * @returns Formatted string (e.g., "1920x1080") */ export function formatVideoDimensions(width, height) { return `${width}x${height}`; } /** * Save generated video result to a file * * Creates parent directories if they don't exist and handles both * absolute and relative paths. * * @param video - Video generation result containing video buffer * @param outputPath - Path where the video should be saved * @returns Save result with success status, path, and size * * @example * ```typescript * const result = await saveVideoToFile(videoResult, "./output/video.mp4"); * if (result.success) { * console.log(`Saved to ${result.path} (${formatVideoFileSize(result.size)})`); * } * ``` */ export async function saveVideoToFile(video, outputPath) { let normalizedPath = outputPath; try { // Normalize the output path normalizedPath = normalizeVideoOutputPath(outputPath, video.mediaType); // Ensure parent directories exist await ensureVideoDirectoryExists(normalizedPath); // Write the video buffer to file await fs.promises.writeFile(normalizedPath, video.data); // Get the actual file size const stats = await fs.promises.stat(normalizedPath); return { success: true, path: normalizedPath, size: stats.size, }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error occurred"; return { success: false, path: normalizedPath, size: 0, error: errorMessage, }; } } /** * Type guard to check if an object is a valid VideoGenerationResult * * @param obj - Object to check * @returns True if object is a valid VideoGenerationResult */ export function isVideoGenerationResult(obj) { if (!obj || typeof obj !== "object") { return false; } const video = obj; // Check required fields if (!(video.data instanceof Buffer)) { return false; } // Check mediaType is valid if (video.mediaType !== "video/mp4" && video.mediaType !== "video/webm") { return false; } return true; } /** * Get video metadata summary for display * * @param video - Video generation result * @returns Formatted metadata summary string */ export function getVideoMetadataSummary(video) { const parts = []; if (video.metadata?.duration) { parts.push(`Duration: ${formatVideoDuration(video.metadata.duration)}`); } if (video.metadata?.dimensions) { parts.push(`Resolution: ${formatVideoDimensions(video.metadata.dimensions.width, video.metadata.dimensions.height)}`); } if (video.metadata?.aspectRatio) { parts.push(`Aspect: ${video.metadata.aspectRatio}`); } if (video.metadata?.audioEnabled !== undefined) { parts.push(`Audio: ${video.metadata.audioEnabled ? "Yes" : "No"}`); } if (video.metadata?.processingTime) { const seconds = (video.metadata.processingTime / 1000).toFixed(1); parts.push(`Processing: ${seconds}s`); } return parts.join(" | "); } //# sourceMappingURL=videoFileUtils.js.map