Updated Code:
javascriptconst multer = require('multer');
const path = require('path');
const fs = require('fs-extra');
const { bucket } = require('./firebaseConfig'); // Import the Firebase Storage bucket
// Multer storage for handling chunk uploads in memory (since we won't store them locally)
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
router.post('/upload-video', Authorization.authorized, upload.single('video'), Product.uploadVideo);
class Product {
async uploadVideo(req, res) {
try {
const { title, description, course, is_free, instructorId, chunkNumber, totalChunks } = req.body;
if (!title || !description || !course || !instructorId || !totalChunks) {
return res.status(400).json({ errors: [{ msg: 'Missing required fields' }] });
}
const instructor = await Instructor.findById(instructorId);
if (!instructor) {
return res.status(400).json({ errors: [{ msg: 'Instructor not found' }] });
}
const selectedCourse = await ProductModel.findById(course);
if (!selectedCourse || selectedCourse.instructor_id.toString() !== instructorId) {
return res.status(400).json({ errors: [{ msg: 'Invalid course selection' }] });
}
if (!req.file) {
return res.status(400).json({ errors: [{ msg: 'No video file uploaded' }] });
}
const chunkFileName = `${instructorId}-${course}-${chunkNumber}-${Date.now()}`;
// Upload chunk to Firebase Storage
const chunkFile = bucket.file(`chunks/${chunkFileName}`);
await chunkFile.save(req.file.buffer, {
contentType: req.file.mimetype,
});
// Check if all chunks have been uploaded
const [files] = await bucket.getFiles({ prefix: `chunks/${instructorId}-${course}-` });
const sortedChunks = files.sort((a, b) => parseInt(a.name.split('-').pop()) - parseInt(b.name.split('-').pop()));
if (sortedChunks.length === parseInt(totalChunks)) {
// Assemble the video from the chunks
const finalFileName = `${instructorId}-${course}-${Date.now()}.mp4`;
const finalFile = bucket.file(`videos/${finalFileName}`);
const writeStream = finalFile.createWriteStream({ contentType: 'video/mp4' });
for (const file of sortedChunks) {
const [chunkBuffer] = await file.download();
writeStream.write(chunkBuffer);
}
writeStream.end();
writeStream.on('finish', async () => {
// Clean up chunk files from Firebase Storage
for (const file of sortedChunks) {
await file.delete();
}
// Save video info to the database
const video = new Video({
title,
description,
course: selectedCourse._id,
instructor: instructor._id,
is_free: is_free === 'true' || is_free === true,
video_url: await finalFile.getSignedUrl({
action: 'read',
expires: '03-09-2491', // Set expiration as needed
}),
});
const savedVideo = await video.save();
// Update ProductModel with new video information
selectedCourse.video_list.push(savedVideo._id);
selectedCourse.video_count += 1;
if (is_free === 'true') {
selectedCourse.free_video_count += 1;
}
await selectedCourse.save();
return res.status(201).json({ msg: 'Video uploaded successfully', video: savedVideo });
});
writeStream.on('error', (err) => {
console.error('Error writing final video:', err);
res.status(500).json({ errors: [{ msg: 'Error assembling video' }] });
});
} else {
return res.status(200).json({ msg: 'Chunk uploaded successfully, waiting for more chunks to complete the video' });
}
} catch (error) {
console.error('Video upload error:', error);
return res.status(500).json({ errors: [{ msg: 'Internal server error' }] });
}
}
}
module.exports = new Product();