Resolve merge conflicts for video uploads and processing

This commit is contained in:
2025-11-28 16:33:32 +01:00
parent 97e54355fb
commit 8c87f1537b
2 changed files with 40 additions and 28 deletions
+11 -4
View File
@@ -76,12 +76,15 @@ const upload = multer({
}, },
fileFilter: (req, file, cb) => { fileFilter: (req, file, cb) => {
// Accept images and common video formats with proper validation // Accept images and common video formats with proper validation
const allowedMimeTypes = ['image/jpeg', 'image/png', 'image/webp', 'video/mp4', 'video/quicktime', 'video/webm']; const allowedMimeTypes = [
'image/jpeg', 'image/png', 'image/webp',
'video/mp4', 'video/webm', 'video/quicktime', 'video/x-msvideo'
];
if (validateFileType(file.originalname, file.mimetype, allowedMimeTypes)) { if (validateFileType(file.originalname, file.mimetype, allowedMimeTypes)) {
return cb(null, true); return cb(null, true);
} else { } else {
cb(new Error('Only JPEG, PNG, WebP images or MP4/MOV/WEBM videos are allowed')); cb(new Error('Only JPEG, PNG, WebP images and MP4, WebM, MOV, AVI videos are allowed'));
} }
}, },
// Add abort on limit to stop processing when limits are exceeded // Add abort on limit to stop processing when limits are exceeded
@@ -90,8 +93,12 @@ const upload = multer({
// Create content validator middleware // Create content validator middleware
const validateUploadContent = createFileUploadValidator({ const validateUploadContent = createFileUploadValidator({
allowedTypes: ['image/jpeg', 'image/png', 'image/webp', 'video/mp4', 'video/quicktime', 'video/webm'], allowedTypes: [
maxFileSize: 50 * 1024 * 1024, 'image/jpeg', 'image/png', 'image/webp',
'video/mp4', 'video/webm', 'video/quicktime', 'video/x-msvideo'
],
// 10GB per file to accommodate large videos; overall limits enforced elsewhere
maxFileSize: 10 * 1024 * 1024 * 1024,
validateContent: true validateContent: true
}); });
+29 -24
View File
@@ -2,6 +2,7 @@ const path = require('path');
const fs = require('fs').promises; const fs = require('fs').promises;
const { db } = require('../database/db'); const { db } = require('../database/db');
const { generateThumbnail, generateVideoPlaceholder } = require('./imageProcessor'); const { generateThumbnail, generateVideoPlaceholder } = require('./imageProcessor');
const { processUploadedVideo } = require('./videoProcessor');
const { generatePhotoFilename } = require('../utils/filenameSanitizer'); const { generatePhotoFilename } = require('../utils/filenameSanitizer');
const { isVideoMimeType } = require('../utils/fileSecurityUtils'); const { isVideoMimeType } = require('../utils/fileSecurityUtils');
const mime = require('mime-types'); const mime = require('mime-types');
@@ -155,10 +156,21 @@ async function processUploadedPhotos(files, eventId, uploadedBy = 'admin', categ
} }
} }
// Generate thumbnail or placeholder // Generate thumbnail and metadata
let thumbnailPath = null; let thumbnailPath = null;
let videoMetadata = null;
if (isVideo) { if (isVideo) {
thumbnailPath = await generateVideoPlaceholder(newFilename); const thumbnailDir = path.join(getStoragePath(), 'thumbnails');
await fs.mkdir(thumbnailDir, { recursive: true });
const videoThumbnailPath = path.join(thumbnailDir, `thumb_${newFilename.replace(/\.[^.]+$/, '.jpg')}`);
try {
const result = await processUploadedVideo(newPath, videoThumbnailPath);
videoMetadata = result?.metadata || null;
thumbnailPath = path.relative(getStoragePath(), videoThumbnailPath);
} catch (videoErr) {
console.error('Failed to process uploaded video, falling back to placeholder:', videoErr.message);
thumbnailPath = await generateVideoPlaceholder(newFilename);
}
} else { } else {
thumbnailPath = await generateThumbnail(newPath); thumbnailPath = await generateThumbnail(newPath);
} }
@@ -173,32 +185,25 @@ async function processUploadedPhotos(files, eventId, uploadedBy = 'admin', categ
const clientName = trx?.client?.config?.client; const clientName = trx?.client?.config?.client;
const supportsReturning = ['pg', 'postgres', 'postgresql'].includes(clientName); const supportsReturning = ['pg', 'postgres', 'postgresql'].includes(clientName);
const photoData = {
event_id: eventId,
filename: newFilename,
path: relativePath,
thumbnail_path: relativeThumbPath,
type: photoType,
size_bytes: file.size,
uploaded_by: uploadedBy,
source_origin: 'managed',
mime_type: resolvedMime,
media_type: isVideo ? 'video' : 'photo'
};
if (supportsReturning) { if (supportsReturning) {
insertResult = await trx('photos') insertResult = await trx('photos')
.insert({ .insert(photoData)
event_id: eventId,
filename: newFilename,
path: relativePath,
thumbnail_path: relativeThumbPath,
type: photoType,
size_bytes: file.size,
uploaded_by: uploadedBy,
source_origin: 'managed',
mime_type: resolvedMime
})
.returning('id'); .returning('id');
} else { } else {
insertResult = await trx('photos').insert({ insertResult = await trx('photos').insert(photoData);
event_id: eventId,
filename: newFilename,
path: relativePath,
thumbnail_path: relativeThumbPath,
type: photoType,
size_bytes: file.size,
uploaded_by: uploadedBy,
source_origin: 'managed',
mime_type: resolvedMime
});
} }
const insertedId = Array.isArray(insertResult) const insertedId = Array.isArray(insertResult)