135 lines
4.2 KiB
JavaScript
135 lines
4.2 KiB
JavaScript
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>} - 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<string>} - 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<Object>} - 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
|
|
}; |