| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- /**
- * @fileoverview IPC Integration utilities for Electron desktop app functionality
- * @author GrabZilla Development Team
- * @version 2.1.0
- * @since 2024-01-01
- */
- /**
- * IPC INTEGRATION MODULE
- *
- * Provides secure communication layer between renderer and main process
- *
- * Features:
- * - File system operations (save directory, cookie file selection)
- * - Binary version checking and management
- * - Video download operations with progress tracking
- * - Secure IPC channel management
- *
- * Dependencies:
- * - Electron contextBridge API (exposed via preload script)
- * - Main process IPC handlers
- *
- * Security:
- * - All IPC calls are validated and sanitized
- * - No direct access to Node.js APIs from renderer
- * - Secure contextBridge exposure pattern
- */
- class IPCManager {
- constructor() {
- this.isElectronAvailable = typeof window !== 'undefined' && window.electronAPI;
- this.downloadProgressListeners = new Map();
-
- if (this.isElectronAvailable) {
- this.setupProgressListener();
- }
- }
- /**
- * Check if Electron IPC is available
- * @returns {boolean} True if running in Electron environment
- */
- isAvailable() {
- return this.isElectronAvailable;
- }
- /**
- * Set up download progress listener
- */
- setupProgressListener() {
- if (!this.isElectronAvailable) return;
- window.electronAPI.onDownloadProgress((event, progressData) => {
- const { url, progress } = progressData;
-
- // Notify all registered listeners
- this.downloadProgressListeners.forEach((callback, listenerId) => {
- try {
- callback({ url, progress });
- } catch (error) {
- console.error(`Error in download progress listener ${listenerId}:`, error);
- }
- });
- });
- }
- /**
- * Register download progress listener
- * @param {string} listenerId - Unique identifier for the listener
- * @param {Function} callback - Callback function to handle progress updates
- */
- onDownloadProgress(listenerId, callback) {
- if (typeof callback !== 'function') {
- throw new Error('Progress callback must be a function');
- }
-
- this.downloadProgressListeners.set(listenerId, callback);
- }
- /**
- * Remove download progress listener
- * @param {string} listenerId - Listener identifier to remove
- */
- removeDownloadProgressListener(listenerId) {
- this.downloadProgressListeners.delete(listenerId);
- }
- /**
- * Select save directory using native file dialog
- * @returns {Promise<string|null>} Selected directory path or null if cancelled
- */
- async selectSaveDirectory() {
- if (!this.isElectronAvailable) {
- throw new Error('File selection not available in browser mode');
- }
- try {
- const directoryPath = await window.electronAPI.selectSaveDirectory();
- return directoryPath;
- } catch (error) {
- console.error('Error selecting save directory:', error);
- throw new Error('Failed to select save directory');
- }
- }
- /**
- * Select cookie file using native file dialog
- * @returns {Promise<string|null>} Selected file path or null if cancelled
- */
- async selectCookieFile() {
- if (!this.isElectronAvailable) {
- throw new Error('File selection not available in browser mode');
- }
- try {
- const filePath = await window.electronAPI.selectCookieFile();
- return filePath;
- } catch (error) {
- console.error('Error selecting cookie file:', error);
- throw new Error('Failed to select cookie file');
- }
- }
- /**
- * Check binary versions (yt-dlp, ffmpeg)
- * @returns {Promise<Object>} Binary version information
- */
- async checkBinaryVersions() {
- if (!this.isElectronAvailable) {
- throw new Error('Binary checking not available in browser mode');
- }
- try {
- const versions = await window.electronAPI.checkBinaryVersions();
- return versions;
- } catch (error) {
- console.error('Error checking binary versions:', error);
- throw new Error('Failed to check binary versions');
- }
- }
- /**
- * Get video metadata from URL
- * @param {string} url - Video URL to fetch metadata for
- * @returns {Promise<Object>} Video metadata (title, duration, thumbnail, etc.)
- */
- async getVideoMetadata(url) {
- if (!this.isElectronAvailable) {
- throw new Error('Metadata fetching not available in browser mode');
- }
- if (!url || typeof url !== 'string') {
- throw new Error('Valid URL is required for metadata fetching');
- }
- try {
- const metadata = await window.electronAPI.getVideoMetadata(url);
- return metadata;
- } catch (error) {
- console.error('Error fetching video metadata:', error);
- throw new Error(`Failed to fetch metadata: ${error.message}`);
- }
- }
- /**
- * Get metadata for multiple URLs in a single batch request (5-10x faster)
- * @param {string[]} urls - Array of video URLs to fetch metadata for
- * @returns {Promise<Object[]>} Array of video metadata objects with url property
- */
- async getBatchVideoMetadata(urls) {
- if (!this.isElectronAvailable) {
- throw new Error('Batch metadata fetching not available in browser mode');
- }
- if (!Array.isArray(urls) || urls.length === 0) {
- throw new Error('Valid URL array is required for batch metadata fetching');
- }
- try {
- const results = await window.electronAPI.getBatchVideoMetadata(urls);
- return results;
- } catch (error) {
- console.error('Error fetching batch video metadata:', error);
- throw new Error(`Failed to fetch batch metadata: ${error.message}`);
- }
- }
- /**
- * Download video with specified options
- * @param {Object} options - Download options
- * @param {string} options.url - Video URL to download
- * @param {string} options.quality - Video quality (720p, 1080p, etc.)
- * @param {string} options.format - Output format (None, H264, ProRes, etc.)
- * @param {string} options.savePath - Directory to save the video
- * @param {string} [options.cookieFile] - Optional cookie file path
- * @returns {Promise<Object>} Download result
- */
- async downloadVideo(options) {
- if (!this.isElectronAvailable) {
- throw new Error('Video download not available in browser mode');
- }
- // Validate required options (now includes videoId for parallel processing)
- const requiredFields = ['videoId', 'url', 'quality', 'format', 'savePath'];
- for (const field of requiredFields) {
- if (!options[field]) {
- throw new Error(`Missing required field: ${field}`);
- }
- }
- // Sanitize options
- const sanitizedOptions = {
- videoId: options.videoId,
- url: options.url.trim(),
- quality: options.quality,
- format: options.format,
- savePath: options.savePath,
- cookieFile: options.cookieFile || null
- };
- try {
- const result = await window.electronAPI.downloadVideo(sanitizedOptions);
- return result;
- } catch (error) {
- console.error('Error downloading video:', error);
- throw new Error(`Download failed: ${error.message}`);
- }
- }
- /**
- * Get download manager statistics
- * @returns {Promise<Object>} Download stats
- */
- async getDownloadStats() {
- if (!this.isElectronAvailable) {
- return {
- active: 0,
- queued: 0,
- maxConcurrent: 1,
- completed: 0,
- canAcceptMore: true
- };
- }
- try {
- const result = await window.electronAPI.getDownloadStats();
- return result.stats;
- } catch (error) {
- console.error('Error getting download stats:', error);
- throw new Error(`Failed to get download stats: ${error.message}`);
- }
- }
- /**
- * Cancel a specific download
- * @param {string} videoId - Video ID to cancel
- * @returns {Promise<boolean>} Success status
- */
- async cancelDownload(videoId) {
- if (!this.isElectronAvailable) {
- throw new Error('Cancel download not available in browser mode');
- }
- try {
- const result = await window.electronAPI.cancelDownload(videoId);
- return result.success;
- } catch (error) {
- console.error('Error cancelling download:', error);
- throw new Error(`Failed to cancel download: ${error.message}`);
- }
- }
- /**
- * Cancel all queued downloads
- * @returns {Promise<Object>} Cancel result with counts
- */
- async cancelAllDownloads() {
- if (!this.isElectronAvailable) {
- throw new Error('Cancel all downloads not available in browser mode');
- }
- try {
- const result = await window.electronAPI.cancelAllDownloads();
- return result;
- } catch (error) {
- console.error('Error cancelling all downloads:', error);
- throw new Error(`Failed to cancel downloads: ${error.message}`);
- }
- }
- /**
- * Get app version information
- * @returns {string} App version
- */
- getAppVersion() {
- if (!this.isElectronAvailable) {
- return '2.1.0'; // Fallback version
- }
- try {
- return window.electronAPI.getAppVersion();
- } catch (error) {
- console.error('Error getting app version:', error);
- return '2.1.0';
- }
- }
- /**
- * Get platform information
- * @returns {string} Platform identifier (darwin, win32, linux)
- */
- getPlatform() {
- if (!this.isElectronAvailable) {
- return 'unknown';
- }
- try {
- return window.electronAPI.getPlatform();
- } catch (error) {
- console.error('Error getting platform:', error);
- return 'unknown';
- }
- }
- /**
- * Validate IPC connection and available methods
- * @returns {Object} Validation result with available methods
- */
- validateConnection() {
- if (!this.isElectronAvailable) {
- return {
- connected: false,
- error: 'Electron API not available',
- availableMethods: []
- };
- }
- const expectedMethods = [
- 'selectSaveDirectory',
- 'selectCookieFile',
- 'checkBinaryVersions',
- 'getVideoMetadata',
- 'getBatchVideoMetadata',
- 'downloadVideo',
- 'getAppVersion',
- 'getPlatform',
- 'onDownloadProgress'
- ];
- const availableMethods = expectedMethods.filter(method =>
- typeof window.electronAPI[method] === 'function'
- );
- const missingMethods = expectedMethods.filter(method =>
- typeof window.electronAPI[method] !== 'function'
- );
- return {
- connected: true,
- availableMethods,
- missingMethods,
- allMethodsAvailable: missingMethods.length === 0
- };
- }
- }
- // Export singleton instance
- const ipcManager = new IPCManager();
- // Export for use in other modules
- if (typeof module !== 'undefined' && module.exports) {
- module.exports = ipcManager;
- } else if (typeof window !== 'undefined') {
- window.IPCManager = ipcManager;
- }
|