const fs = require('fs'); const path = require('path'); const ffmpeg = require('fluent-ffmpeg'); // Supported media file extensions const MEDIA_EXTENSIONS = ['.mp4', '.mkv', '.avi', '.mov', '.flv', '.webm']; /** * Check if a file has a media extension * @param {string} filePath - Path to the file * @returns {boolean} - True if file is a media file */ function isMediaFile(filePath) { const ext = path.extname(filePath).toLowerCase(); return MEDIA_EXTENSIONS.includes(ext); } /** * Scan a directory for media files * @param {string} directoryPath - Path to the directory to scan * @returns {Promise} - Array of media file paths */ async function scanDirectory(directoryPath) { try { const files = fs.readdirSync(directoryPath); const mediaFiles = []; for (const file of files) { const filePath = path.join(directoryPath, file); try { const stat = fs.statSync(filePath); if (stat.isDirectory()) { // Recursively scan subdirectories const subDirFiles = await scanDirectory(filePath); mediaFiles.push(...subDirFiles); } else if (isMediaFile(filePath)) { // Extract duration for media files let fileData = { path: filePath, name: file, size: stat.size, modified: stat.mtime, duration: '00:00', isProblematic: false }; try { const durationResult = await extractFileDuration(filePath); fileData.duration = durationResult.duration; fileData.isProblematic = durationResult.isProblematic; } catch (error) { console.warn(`Failed to extract duration for ${filePath}:`, error.message); } mediaFiles.push(fileData); } } catch (fileError) { // Skip files/directories that cause permission errors console.warn(`Skipping file/directory due to permission error: ${filePath}`); continue; } } return mediaFiles; } catch (error) { throw new Error(`Failed to scan directory: ${error.message}`); } } /** * Extract file duration using ffmpeg * @param {string} filePath - Path to the media file * @returns {Promise} - Duration in mm:ss format */ async function extractFileDuration(filePath) { return new Promise((resolve, reject) => { ffmpeg.ffprobe(filePath, (err, metadata) => { if (err) { console.warn(`ffprobe error for ${filePath}:`, err.message); // Try alternative approach - attempt to get duration from file stats or use fallback try { const fs = require('fs'); const stats = fs.statSync(filePath); console.log(`File size for ${filePath}: ${stats.size} bytes`); // For now, return 00:00 as fallback for problematic files resolve({ duration: '00:00', isProblematic: true }); } catch (statsError) { console.warn(`Could not get file stats for ${filePath}:`, statsError.message); resolve({ duration: '00:00', isProblematic: true }); } return; } const duration = metadata.format.duration; if (!duration) { resolve({ duration: '00:00', isProblematic: false }); return; } // Convert seconds to mm:ss format const minutes = Math.floor(duration / 60); const seconds = Math.floor(duration % 60); const formattedDuration = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; resolve({ duration: formattedDuration, isProblematic: false }); }); }); } /** * Extract file metadata (duration and quality) * @param {string} filePath - Path to the media file * @returns {Promise} - File metadata */ async function extractFileMetadata(filePath) { // Extract duration using ffmpeg let duration = '00:00'; try { duration = await extractFileDuration(filePath); } catch (error) { console.warn(`Failed to extract duration for ${filePath}:`, error.message); } return { duration: duration, quality: 'unknown' }; } module.exports = { scanDirectory, extractFileMetadata, isMediaFile, extractFileDuration };