UNPKG

ems-editor

Version:

EMS Video Editor SDK - Universal JavaScript SDK for external video editor integration

794 lines (625 loc) โ€ข 19.9 kB
# EMS Editor SDK A powerful JavaScript SDK for integrating with the EMS Video Editor through WebSocket communication. Provides real-time workspace loading, template importing, render management, and MAM integration. ## Features - ๐Ÿš€ **Real-time WebSocket Communication** - Bidirectional communication with the video editor - ๐Ÿ“ฅ **Workspace Loading** - Load workspaces from server with progress tracking - ๐Ÿ“‹ **Template Import** - Import video templates and assets dynamically - ๐ŸŽฌ **Render Management** - Start renders and track progress in real-time - โšก **Quick Render** - One-click rendering of current workspace with minimal setup - ๐Ÿ“Š **Render Job Tracking** - Monitor render job status and progress by job ID - ๐Ÿ“ค **MAM Integration** - Upload rendered videos to Media Asset Management systems - ๐ŸŽจ **Theme Customization** - Apply custom themes and branding - ๐Ÿ”„ **Template Change Tracking** - Monitor template modifications with dirty flag system and real-time notifications ## Installation ```bash npm install ems-editor ``` ## Quick Start ### Basic Setup ```javascript import EMSEditor from 'ems-editor'; const editor = new EMSEditor({ serverUrl: 'http://localhost:5200', sessionId: 'your-session-id' // Optional }); // Set iframe reference const iframe = document.getElementById('editor-iframe'); editor.setIframe(iframe); // Wait for editor to be ready editor.once('ready', () => { console.log('Editor is ready!'); }); ``` ### Load Workspace from Server ```javascript // Load workspace with progress tracking const result = await editor.loadWorkspaceFromServer({ workspaceId: 'workspace-uuid-123', mergeMode: 'replace', switchToWorkspace: true }, { onProgress: (progress) => { console.log(`${progress.stage}: ${progress.progress}% - ${progress.message}`); }, onComplete: (result) => { console.log('โœ… Workspace loaded:', result.workspaceName); }, onError: (error) => { console.error('โŒ Loading failed:', error); } }); ``` ### Template Change Tracking The SDK provides built-in change tracking to monitor when templates have been modified after being imported from external sources. This is useful for showing "unsaved changes" indicators and preventing data loss. ```javascript // Check current dirty status if (editor.isDirty()) { console.log('โš ๏ธ Template has been modified since import'); } else { console.log('โœ… Template is clean (no modifications)'); } // Listen for real-time status changes (throttled to once per second) editor.on('templateStatusChanged', (status) => { const { isDirty, templateId, templateName, timestamp } = status; if (isDirty) { console.log(`๐Ÿ”„ Template "${templateName}" has unsaved changes`); // Show save prompt, enable save button, add * to title, etc. updateUI({ showUnsavedIndicator: true }); } else { console.log(`โœ… Template "${templateName}" is saved`); // Remove save indicators, disable save button, etc. updateUI({ showUnsavedIndicator: false }); } }); // Example UI integration function createSaveButton() { const button = document.createElement('button'); const updateButton = () => { if (editor.isDirty()) { button.textContent = 'Save Changes *'; button.disabled = false; button.classList.add('has-changes'); } else { button.textContent = 'Saved'; button.disabled = true; button.classList.remove('has-changes'); } }; // Initial state updateButton(); // Update on changes editor.on('templateStatusChanged', updateButton); return button; } ``` **Key Features:** - ๐Ÿ”„ **Real-time tracking** - Automatically detects when users modify templates in the editor - โšก **Throttled notifications** - WebSocket messages are throttled to once per second to prevent spam - ๐ŸŽฏ **Accurate state** - Distinguishes between templates imported from external sources vs. user modifications - ๐Ÿงน **Auto-reset** - Dirty flag automatically resets when new templates are imported ### Quick Render Current Workspace ```javascript // Quick render with default settings (MP4, H.264) const renderJob = await editor.quickRender(); if (renderJob.success) { // Track the render progress const finalResult = await editor.waitForRenderComplete(renderJob.jobId, { onProgress: (result) => { console.log(`๐ŸŽฌ ${result.status}: ${result.progress || 0}%`); } }); if (finalResult.status === 'completed') { console.log('โœ… Video ready:', finalResult.videoUrl); } } else { console.error('โŒ Quick render failed:', renderJob.error); } ``` ### Track Render Job by ID ```javascript // Get current status of a render job const jobStatus = await editor.getRenderResult('job-abc-123'); console.log('Job Status:', jobStatus.status, jobStatus.progress + '%'); // Or wait for completion with polling const completed = await editor.waitForRenderComplete('job-abc-123'); console.log('Completed:', completed.videoUrl); ``` ## API Reference ### Methods #### Template and Asset Management ##### `importTemplate(templateData: any): Promise<ImportFromAssetsResult>` Import a video template into the editor. ```javascript // Import a template const result = await editor.importTemplate({ name: 'My Video Template', tracks: [...], assets: {...}, settings: { width: 1920, height: 1080, fps: 30, duration: 600 } }); console.log('Template imported:', result.templateId); ``` ##### `importFromAssets(assets: AssetInfo[], options?): Promise<ImportFromAssetsResult>` Generate and import a template from an array of media assets. ```javascript // Import from assets const assets = [ { id: 'video1', name: 'Intro Video', type: 'video', fileUrl: 'https://example.com/video.mp4', duration: 5000, width: 1920, height: 1080 }, { id: 'audio1', name: 'Background Music', type: 'audio', fileUrl: 'https://example.com/music.mp3', duration: 30000 } ]; const result = await editor.importFromAssets(assets, { name: 'Auto-Generated Video', width: 1920, height: 1080, fps: 30 }); console.log('Assets imported:', result.templateName); ``` #### Workspace Management ##### `createWorkspace(options?): Promise<{workspaceId: string, success: boolean}>` Create a new workspace/tab in the editor. ```javascript // Create a new workspace const result = await editor.createWorkspace({ name: 'New Project', description: 'My new video project', width: 1920, height: 1080, fps: 30, duration: 450, quality: 'high' }); console.log('Workspace created:', result.workspaceId); ``` ##### `getWorkspacesList(): Promise<any[]>` Get list of all open workspaces. ```javascript // Get all workspaces const workspaces = await editor.getWorkspacesList(); workspaces.forEach(workspace => { console.log(`Workspace: ${workspace.name} (${workspace.id})`); }); ``` ##### `renameWorkspace(workspaceId: string, newName: string): Promise<{success: boolean}>` Rename an existing workspace. ```javascript // Rename a workspace await editor.renameWorkspace('workspace-123', 'Updated Project Name'); ``` ##### `clearWorkspaces(): Promise<void>` Clear all workspaces/tabs in the editor. ```javascript // Clear all workspaces await editor.clearWorkspaces(); console.log('All workspaces cleared'); ``` #### Video Rendering ##### `startRender(renderOptions: RenderOptions): Promise<void>` Start the video rendering process. ```javascript // Start render with basic options await editor.startRender({ title: 'My Video', color: '#FF5733', duration: 300, fps: 30, width: 1920, height: 1080, format: 'mp4', quality: 'high' }); // Listen for render progress editor.on('renderProgress', (progress) => { console.log(`Render progress: ${progress.progress}%`); }); editor.on('renderComplete', (result) => { console.log('Video rendered:', result.videoUrl); }); ``` ##### `startRenderWithMAMUpload(options: RenderOptions, callbacks?: MAMUploadCallbacks): Promise<StartRenderWithMAMUploadResult>` Start rendering with MAM (Media Asset Management) upload tracking. ```javascript // Start render with MAM upload const result = await editor.startRenderWithMAMUpload({ title: 'Corporate Video', format: 'mp4', quality: 'ultra', uploadToMAM: true, mamToken: 'your-mam-token', mamBaseUrl: 'https://your-mam-system.com/api', mamProjectId: 12345, mamBinId: 67890 }, { onProgress: (progress) => { console.log(`MAM Upload: ${progress.stage} - ${progress.progress}%`); }, onStageChange: (stage, data) => { console.log(`Stage changed to: ${stage}`); }, onComplete: (result) => { console.log('Uploaded to MAM:', result.assetId); }, onError: (error) => { console.error('MAM upload failed:', error); } }); console.log('MAM upload tracking ID:', result.tracker.trackingId); ``` ##### `trackMAMUpload(jobId: string, callbacks?: MAMUploadCallbacks): MAMUploadTracker` Track MAM upload progress for a specific job. ```javascript // Track existing MAM upload const tracker = editor.trackMAMUpload('job-123', { onProgress: (progress) => { console.log(`Upload progress: ${progress.progress}%`); }, onComplete: (result) => { console.log('MAM upload completed:', result.assetUrl); } }); // Stop tracking when done setTimeout(() => { tracker.stop(); }, 60000); ``` ##### `quickRender(options?: QuickRenderOptions): Promise<QuickRenderResult>` Start a quick render of the current workspace with minimal configuration. Uses the current workspace template with optional parameter overrides. ```javascript // Quick render with defaults (MP4, H.264) const result = await editor.quickRender(); // Quick render with custom options const result = await editor.quickRender({ title: 'My Quick Video', format: 'mp4', codec: 'h264', fps: 30 }); console.log('Quick render started:', result.jobId); // Check if successful if (result.success) { console.log('Job ID:', result.jobId); } else { console.error('Error:', result.error); } ``` ##### `getRenderResult(jobId: string): Promise<RenderResult>` Get the current status and result of a render job by its job ID. ```javascript // Get current job status const result = await editor.getRenderResult('job-abc-123'); console.log('Job Status:', { jobId: result.jobId, status: result.status, // 'queued', 'in-progress', 'completed', 'failed' progress: result.progress, // Progress percentage (0-100) videoUrl: result.videoUrl, // Available when completed mamAssetId: result.mamAssetId // MAM asset ID if uploaded }); ``` ##### `waitForRenderComplete(jobId: string, options?): Promise<RenderResult>` Wait for a render job to complete with automatic polling and progress tracking. ```javascript // Wait for render completion with progress callbacks const finalResult = await editor.waitForRenderComplete('job-abc-123', { onProgress: (result) => { console.log(`Progress: ${result.status} ${result.progress || 0}%`); }, pollInterval: 2000, // Poll every 2 seconds timeout: 300000 // 5 minute timeout }); // Result when completed if (finalResult.status === 'completed') { console.log('โœ… Video ready:', finalResult.videoUrl); } else { console.error('โŒ Render failed:', finalResult.status); } // Advanced usage with detailed progress tracking try { const result = await editor.waitForRenderComplete(jobId, { onProgress: (result) => { switch (result.status) { case 'queued': console.log('โณ Job queued...'); break; case 'in-progress': console.log(`๐ŸŽฌ Rendering: ${result.progress || 0}%`); break; } }, pollInterval: 1500, timeout: 600000 // 10 minutes }); console.log('Final result:', result); } catch (error) { console.error('Render failed or timed out:', error.message); } ``` #### Theme and UI Customization ##### `setTheme(theme: 'light' | 'dark'): Promise<void>` Set the editor theme. ```javascript // Set dark theme await editor.setTheme('dark'); // Set light theme await editor.setTheme('light'); ``` ##### `updateTheme(themeId?: string, customTheme?: any): Promise<void>` Update theme with preset or custom theme. ```javascript // Use preset theme await editor.updateTheme('professional-dark'); // Use custom theme object await editor.updateTheme(null, { name: 'Custom Theme', colors: { primary: '#007cba', secondary: '#6c757d', background: '#f8f9fa' } }); ``` ##### `customizeTheme(themeConfig): Promise<void>` Customize theme with specific colors. ```javascript // Customize theme colors await editor.customizeTheme({ primary: '#007cba', secondary: '#6c757d', background: '#ffffff', accent: '#17a2b8' }); ``` #### MAM Integration ##### `loginToMAM(mamConfig: any): Promise<void>` Login to MAM system. ```javascript // Login to MAM await editor.loginToMAM({ apiUrl: 'https://your-mam-system.com/api', tokens: { accessToken: 'your-access-token', refreshToken: 'your-refresh-token' }, selectedProject: { id_project: 12345, name: 'My Project' } }); console.log('Logged into MAM system'); ``` ##### `logoutFromMAM(): Promise<void>` Logout from MAM system. ```javascript // Logout from MAM await editor.logoutFromMAM(); console.log('Logged out from MAM'); ``` ### Events The SDK extends EventEmitter and emits the following events: #### Connection Events ```javascript // WebSocket connected editor.on('connected', () => { console.log('Connected to server'); }); // Editor ready for operations editor.on('ready', () => { console.log('Editor is ready'); }); // WebSocket disconnected editor.on('disconnected', () => { console.log('Disconnected from server'); }); // Connection error editor.on('error', (error) => { console.error('Connection error:', error); }); ``` #### Workspace Events ```javascript // Workspace loading progress editor.on('workspaceLoadingProgress', (progress) => { console.log(`Loading: ${progress.stage} - ${progress.progress}%`); }); // Workspace loaded successfully editor.on('workspaceLoaded', (result) => { console.log('Workspace loaded:', result.workspaceName); }); // Workspace loading failed editor.on('workspaceLoadingError', (error) => { console.error('Loading failed:', error.error); }); // Workspaces cleared editor.on('workspacesCleared', () => { console.log('All workspaces cleared'); }); // Workspace created editor.on('workspaceCreated', (result) => { console.log('Workspace created:', result.workspaceId); }); // Workspace renamed editor.on('workspaceRenamed', (result) => { console.log('Workspace renamed:', result.newName); }); ``` #### Template Events ```javascript // Template imported editor.on('templateImported', (result) => { console.log('Template imported:', result.templateName); }); // Template added to workspace editor.on('templateAddedToWorkspace', (result) => { console.log('Template added to workspace:', result.workspaceId); }); // Template modification status changed editor.on('templateStatusChanged', (status) => { console.log('Template dirty status:', status.isDirty); console.log('Template ID:', status.templateId); console.log('Template name:', status.templateName); console.log('Changed at:', status.timestamp); // Update UI based on dirty status if (status.isDirty) { console.log('โš ๏ธ Template has unsaved changes'); // Show save indicators, enable save buttons, etc. } else { console.log('โœ… Template is clean'); // Hide save indicators, disable save buttons, etc. } }); ``` #### Render Events ```javascript // Render progress updates editor.on('renderProgress', (progress) => { console.log(`Rendering: ${progress.progress}%`); }); // Render completed editor.on('renderComplete', (result) => { console.log('Render completed:', result.videoUrl); }); // Quick render events editor.on('quickRenderStarted', (result) => { console.log('Quick render started:', result.jobId); }); editor.on('quickRenderCompleted', (result) => { console.log('Quick render completed:', result.jobId); }); // Pipeline events (detailed render stages) editor.on('pipelineEvent', (event) => { console.log(`Pipeline: ${event.stage} - ${event.message}`); }); ``` #### MAM Events ```javascript // MAM login status editor.on('mamStatus', (status) => { console.log('MAM status:', status.connected ? 'Connected' : 'Disconnected'); }); // MAM upload events editor.on('mamUploadEvent', (event) => { console.log(`MAM Upload: ${event.stage} - ${event.progress}%`); }); // MAM upload completed editor.on('mamUploadComplete', (result) => { console.log('MAM upload completed:', result.assetId); }); // Specific MAM upload stages editor.on('mamUploadPreparing', (data) => console.log('Preparing upload...')); editor.on('mamUploadAnalyzing', (data) => console.log('Analyzing media...')); editor.on('mamUploadConverting', (data) => console.log('Converting format...')); editor.on('mamUploadUploading', (data) => console.log('Uploading to MAM...')); editor.on('mamUploadCompleting', (data) => console.log('Finalizing...')); editor.on('mamUploadCompleted', (data) => console.log('Upload complete!')); editor.on('mamUploadFailed', (data) => console.log('Upload failed:', data.error)); ``` #### Theme Events ```javascript // Theme changed editor.on('themeChanged', (theme) => { console.log('Theme changed to:', theme.name); }); // Theme updated editor.on('themeUpdate', (themeData) => { console.log('Theme updated:', themeData); }); // Custom theme applied editor.on('customTheme', (theme) => { console.log('Custom theme applied:', theme.name); }); ``` #### Editor Events ```javascript // Editor ready (from iframe) editor.on('editorReady', () => { console.log('Editor iframe is ready'); }); // Editor disconnected editor.on('editorDisconnected', () => { console.log('Editor disconnected'); }); // Workspaces list received editor.on('workspacesListReceived', (workspaces) => { console.log('Received workspaces list:', workspaces.length); }); ``` #### Utility Methods ##### `isConnected(): boolean` Check if WebSocket is connected. ```javascript if (editor.isConnected()) { console.log('WebSocket is connected'); } ``` ##### `isReady(): boolean` Check if editor is ready for operations. ```javascript if (editor.isReady()) { console.log('Editor is ready'); } ``` ##### `getSessionId(): string` Get the current session ID. ```javascript const sessionId = editor.getSessionId(); console.log('Session ID:', sessionId); ``` ##### `isDirty(): boolean` Check if the current template/workspace has unsaved changes. Returns `true` if the template has been modified since it was last imported from an external source. ```javascript // Check if template has been modified if (editor.isDirty()) { console.log('Template has unsaved changes'); // Show save prompt or disable certain actions } else { console.log('Template is clean (no changes since import)'); } // Example usage in UI const saveButton = document.getElementById('save-btn'); const updateSaveButton = () => { if (editor.isDirty()) { saveButton.textContent = 'Save Changes*'; saveButton.disabled = false; } else { saveButton.textContent = 'Saved'; saveButton.disabled = true; } }; // Update save button when template status changes editor.on('templateStatusChanged', updateSaveButton); ``` ##### `destroy(): void` Clean up resources and disconnect. ```javascript // Clean up when done editor.destroy(); ``` ## Examples See the `../../examples/` directory for complete examples: - `simple-workspace-loading.html` - Basic usage - `workspace-loading-example.html` - Advanced usage with UI - `nodejs-client-sdk-example.js` - Node.js integration ## Documentation For detailed documentation, see: - [Workspace Loading Guide](../../docs/WORKSPACE_LOADING_GUIDE.md) - [WebSocket API Reference](../../docs/WEBSOCKET_API_REFERENCE.md) ## License MIT License