Added quality column feature. Files now display quality information like 1080p30fps or 720p60fps. Enhanced error handling for problematic files with visual labeling and quality extraction.
This commit is contained in:
parent
ac2248d7ed
commit
65988a942e
@ -131,6 +131,7 @@
|
||||
.file-duration, .file-quality {
|
||||
margin: 0 10px;
|
||||
color: #666;
|
||||
min-width: 80px; /* Ensure consistent column width */
|
||||
}
|
||||
|
||||
.problematic-label {
|
||||
|
||||
@ -70,7 +70,7 @@ function displayFiles(files) {
|
||||
|
||||
// Use actual duration from file metadata
|
||||
const duration = file.duration || '00:00';
|
||||
const quality = 'unknown'; // Placeholder for quality
|
||||
const quality = file.quality || 'unknown';
|
||||
|
||||
// Add label for problematic files
|
||||
const problemLabel = file.isProblematic ? '<span class="problematic-label">⚠️</span> ' : '';
|
||||
|
||||
@ -35,14 +35,15 @@ async function scanDirectory(directoryPath) {
|
||||
const subDirFiles = await scanDirectory(filePath);
|
||||
mediaFiles.push(...subDirFiles);
|
||||
} else if (isMediaFile(filePath)) {
|
||||
// Extract duration for media files
|
||||
// Extract duration and quality for media files
|
||||
let fileData = {
|
||||
path: filePath,
|
||||
name: file,
|
||||
size: stat.size,
|
||||
modified: stat.mtime,
|
||||
duration: '00:00',
|
||||
isProblematic: false
|
||||
isProblematic: false,
|
||||
quality: 'unknown'
|
||||
};
|
||||
|
||||
try {
|
||||
@ -53,6 +54,13 @@ async function scanDirectory(directoryPath) {
|
||||
console.warn(`Failed to extract duration for ${filePath}:`, error.message);
|
||||
}
|
||||
|
||||
try {
|
||||
const quality = await extractVideoQuality(filePath);
|
||||
fileData.quality = quality;
|
||||
} catch (error) {
|
||||
console.warn(`Failed to extract quality for ${filePath}:`, error.message);
|
||||
}
|
||||
|
||||
mediaFiles.push(fileData);
|
||||
}
|
||||
} catch (fileError) {
|
||||
@ -107,23 +115,106 @@ async function extractFileDuration(filePath) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract video quality information using ffmpeg
|
||||
* @param {string} filePath - Path to the media file
|
||||
* @returns {Promise<string>} - Quality information (e.g., "1080p30fps")
|
||||
*/
|
||||
async function extractVideoQuality(filePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ffmpeg.ffprobe(filePath, (err, metadata) => {
|
||||
if (err) {
|
||||
console.warn(`ffprobe error for quality extraction ${filePath}:`, err.message);
|
||||
resolve('unknown');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Find the video stream
|
||||
const videoStream = metadata.streams.find(stream => stream.codec_type === 'video');
|
||||
|
||||
if (!videoStream) {
|
||||
resolve('unknown');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get resolution
|
||||
const width = videoStream.width || 0;
|
||||
const height = videoStream.height || 0;
|
||||
|
||||
// Determine quality based on height
|
||||
let quality = 'unknown';
|
||||
if (height >= 2160) {
|
||||
quality = '4K';
|
||||
} else if (height >= 1440) {
|
||||
quality = '1440p';
|
||||
} else if (height >= 1080) {
|
||||
quality = '1080p';
|
||||
} else if (height >= 720) {
|
||||
quality = '720p';
|
||||
} else if (height >= 480) {
|
||||
quality = '480p';
|
||||
} else {
|
||||
quality = 'unknown';
|
||||
}
|
||||
|
||||
// Get frame rate
|
||||
let fps = '';
|
||||
if (videoStream.r_frame_rate) {
|
||||
// Parse frame rate like "30/1" or "25/1"
|
||||
const fpsParts = videoStream.r_frame_rate.split('/');
|
||||
if (fpsParts.length === 2) {
|
||||
const fpsValue = Math.round(parseInt(fpsParts[0]) / parseInt(fpsParts[1]));
|
||||
fps = `${fpsValue}fps`;
|
||||
} else {
|
||||
const fpsValue = Math.round(parseFloat(videoStream.r_frame_rate));
|
||||
fps = `${fpsValue}fps`;
|
||||
}
|
||||
}
|
||||
|
||||
// Format the quality string
|
||||
if (quality !== 'unknown' && fps) {
|
||||
resolve(`${quality}${fps}`);
|
||||
} else if (quality !== 'unknown') {
|
||||
resolve(quality);
|
||||
} else if (fps) {
|
||||
resolve(fps);
|
||||
} else {
|
||||
resolve('unknown');
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(`Error extracting quality for ${filePath}:`, error.message);
|
||||
resolve('unknown');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
// Extract duration and quality using ffmpeg
|
||||
let duration = '00:00';
|
||||
let quality = 'unknown';
|
||||
|
||||
try {
|
||||
duration = await extractFileDuration(filePath);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to extract duration for ${filePath}:`, error.message);
|
||||
}
|
||||
|
||||
try {
|
||||
quality = await extractVideoQuality(filePath);
|
||||
} catch (error) {
|
||||
console.warn(`Failed to extract quality for ${filePath}:`, error.message);
|
||||
}
|
||||
|
||||
return {
|
||||
duration: duration,
|
||||
quality: 'unknown'
|
||||
quality: quality
|
||||
};
|
||||
}
|
||||
|
||||
@ -131,5 +222,6 @@ module.exports = {
|
||||
scanDirectory,
|
||||
extractFileMetadata,
|
||||
isMediaFile,
|
||||
extractFileDuration
|
||||
};
|
||||
extractFileDuration,
|
||||
extractVideoQuality
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user