diff --git a/backend/controllers/file-controller.ts b/backend/controllers/file-controller.ts index 5a2f527..830e175 100644 --- a/backend/controllers/file-controller.ts +++ b/backend/controllers/file-controller.ts @@ -265,18 +265,10 @@ class FileController { readStream.on("error", (e: Error) => { console.log("read stream error", e); - if (!responseSent) { - responseSent = true; - res.status(500).send("Server error downloading publicfile"); - } }); decipher.on("error", (e: Error) => { console.log("decipher stream error", e); - if (!responseSent) { - responseSent = true; - res.status(500).send("Server error downloading public file"); - } }); res.set("Content-Type", "binary/octet-stream"); @@ -291,10 +283,6 @@ class FileController { if (e instanceof Error) { console.log("\nGet Public Download Error File Route:", e.message); } - if (!responseSent) { - responseSent = true; - res.status(500).send("Server error downloading public file"); - } } }; @@ -561,10 +549,6 @@ class FileController { const handleError = (e: Error) => { console.log("stream video read stream error", e); - if (!responseSent) { - responseSent = true; - res.status(500).send("Server error streaming video"); - } cleanUp(); }; @@ -603,11 +587,6 @@ class FileController { if (e instanceof Error) { console.log("\nStream Video Error File Route:", e.message); } - - if (!responseSent) { - responseSent = true; - res.status(500).send("Server error streaming video"); - } } }; diff --git a/backend/services/chunk-service/utils/createVideoThumbnail.ts b/backend/services/chunk-service/utils/createVideoThumbnail.ts index 0100cbb..37e7574 100644 --- a/backend/services/chunk-service/utils/createVideoThumbnail.ts +++ b/backend/services/chunk-service/utils/createVideoThumbnail.ts @@ -18,6 +18,21 @@ import { createGenericParams } from "./storageHelper"; const storageActions = env.dbType === "s3" ? new S3Actions() : new FilesystemActions(); +const attemptToRemoveChunks = async ( + file: FileInterface, + thumbnailFilename: string +) => { + try { + const readStreamParams = createGenericParams({ + filePath: env.fsDirectory + thumbnailFilename, + Key: thumbnailFilename, + }); + await storageActions.removeChunks(readStreamParams); + } catch (e) { + console.log("error removing chunks", e); + } +}; + const createVideoThumbnail = ( file: FileInterface, filename: string, @@ -38,7 +53,7 @@ const createVideoThumbnail = ( const readStream = storageActions.createReadStream(readStreamParams); - const { writeStream } = storageActions.createWriteStream( + const { writeStream, emitter } = storageActions.createWriteStream( readStreamParams, readStream, thumbnailFilename @@ -73,8 +88,12 @@ const createVideoThumbnail = ( const decryptedReadStream = readStream.pipe(decipher); + let error = false; + const handleFinish = async () => { try { + if (error) return; + const thumbnailModel = new Thumbnail({ name: filename, owner: user._id, @@ -114,6 +133,12 @@ const createVideoThumbnail = ( } }; + if (emitter) { + emitter.on("finish", async () => { + await handleFinish(); + }); + } + ffmpeg(decryptedReadStream, { timeout: 60, }) @@ -125,11 +150,18 @@ const createVideoThumbnail = ( "-vf scale='if(gt(iw,ih),600,-1):if(gt(ih,iw),300,-1)'", ]) .on("start", (command) => {}) - .on("end", async () => { - await handleFinish(); + .on("end", async (err) => { + if (!emitter) { + await handleFinish(); + } }) .on("error", async (err, sdf, stderr) => { console.log("thumbnail error attempting temp directory fix"); + + error = true; + + await attemptToRemoveChunks(file, thumbnailFilename); + if (env.tempDirectory) { const updatedFile = await tempCreateVideoThumbnailFS( file, diff --git a/backend/services/chunk-service/utils/tempCreateVideoThumbnailFS.ts b/backend/services/chunk-service/utils/tempCreateVideoThumbnailFS.ts index 5c47da6..3acb5fd 100644 --- a/backend/services/chunk-service/utils/tempCreateVideoThumbnailFS.ts +++ b/backend/services/chunk-service/utils/tempCreateVideoThumbnailFS.ts @@ -36,7 +36,7 @@ const tempCreateVideoThumbnailFS = ( const readStream = storageActions.createReadStream(readStreamParams); - const { writeStream } = storageActions.createWriteStream( + const { writeStream, emitter } = storageActions.createWriteStream( readStreamParams, readStream, thumbnailFilename @@ -62,6 +62,50 @@ const tempCreateVideoThumbnailFS = ( decryptedReadStream.pipe(tempWriteStream, { end: true }); + if (emitter) { + emitter.on("finish", async () => { + await handleFinish(); + }); + } + + const handleFinish = async () => { + const thumbnailModel = new Thumbnail({ + name: filename, + owner: user._id, + IV: thumbnailIV, + path: env.fsDirectory + thumbnailFilename, + s3ID: thumbnailFilename, + }); + + await thumbnailModel.save(); + if (!file._id) { + return reject(); + } + const updateFileResponse = await File.updateOne( + { _id: new ObjectId(file._id), "metadata.owner": user._id }, + { + $set: { + "metadata.hasThumbnail": true, + "metadata.thumbnailID": thumbnailModel._id, + }, + } + ); + if (updateFileResponse.modifiedCount === 0) { + return reject(); + } + + const updatedFile = await File.findById({ + _id: new ObjectId(file._id), + "metadata.owner": user._id, + }); + + if (!updatedFile) return reject(); + + fs.unlink(tempDirectory, (err) => { + resolve(updatedFile?.toObject()); + }); + }; + tempWriteStream.on("finish", () => { ffmpeg(tempDirectory, { timeout: 60, @@ -73,95 +117,18 @@ const tempCreateVideoThumbnailFS = ( "-vframes 1", "-vf scale='if(gt(iw,ih),600,-1):if(gt(ih,iw),300,-1)'", ]) - .on("start", (command) => { - /** - * log - */ - }) + .on("start", () => {}) .on("end", async () => { - const thumbnailModel = new Thumbnail({ - name: filename, - owner: user._id, - IV: thumbnailIV, - path: env.fsDirectory + thumbnailFilename, - s3ID: thumbnailFilename, - }); - - await thumbnailModel.save(); - if (!file._id) { - return reject(); + if (!emitter) { + await handleFinish(); } - const updateFileResponse = await File.updateOne( - { _id: new ObjectId(file._id), "metadata.owner": user._id }, - { - $set: { - "metadata.hasThumbnail": true, - "metadata.thumbnailID": thumbnailModel._id, - }, - } - ); - if (updateFileResponse.modifiedCount === 0) { - return reject(); - } - - const updatedFile = await File.findById({ - _id: new ObjectId(file._id), - "metadata.owner": user._id, - }); - - if (!updatedFile) return reject(); - - fs.unlink(tempDirectory, (err) => { - resolve(updatedFile?.toObject()); - }); }) .on("error", (err, _, stderr) => { console.log("error", err, stderr); resolve(file); - /** - * log - */ }) .pipe(thumbnailCipher) .pipe(writeStream, { end: true }); - - // ffmpeg() - // .input(readStream.pipe(decipher)) - // .seekInput("00:00:01.00") - // .outputFormat("image2") - // .pipe(writeStream, { end: true }) - // .on("end", async () => { - // const thumbnailModel = new Thumbnail({ - // name: filename, - // owner: user._id, - // IV: thumbnailIV, - // path: env.fsDirectory + thumbnailFilename, - // }); - - // await thumbnailModel.save(); - // resolve(file); - // }) - // .on("error", (e) => { - // console.log("error", e); - // reject(e); - // }); - - // ffmpeg() - // .input(readStream.pipe(decipher)) - // .size("300x?") - // .outputOptions("-frames:v 1") - // .on("end", async () => { - // const thumbnailModel = new Thumbnail({ - // name: filename, - // owner: user._id, - // IV: thumbnailIV, - // path: env.fsDirectory + thumbnailFilename, - // }); - - // await thumbnailModel.save(); - // resolve(file); - // }) - // .output(writeStream); }); }); }; diff --git a/serverUtils/createVideoThumbnails.js b/serverUtils/createVideoThumbnails.js new file mode 100644 index 0000000..7b0b993 --- /dev/null +++ b/serverUtils/createVideoThumbnails.js @@ -0,0 +1,62 @@ +const getEnvVariables = require("../dist/enviroment/get-env-variables"); +getEnvVariables(); +const mongoose = require("./mongoServerUtil"); +const conn = mongoose.connection; +const File = require("../dist/models/file-model"); +const User = require("../dist/models/user-model"); +const crypto = require("crypto"); +const createVideoThumbnail = + require("../dist/services/chunk-service/utils/createVideoThumbnail").default; +const getKey = require("../dist/key/get-key").default; + +const waitForDatabase = () => { + return new Promise((resolve, reject) => { + if (conn.readyState !== 1) { + conn.once("open", () => { + resolve(); + }); + } else { + resolve(); + } + }); +}; + +const updateDocs = async () => { + console.log("\nWaiting for database..."); + await waitForDatabase(); + console.log("Connected to database\n"); + + console.log("Getting Key..."); + await getKey(); + console.log("Key Got\n"); + + // console.log("env", process.env.KEY); + + console.log("Getting file list..."); + const files = await File.find({ + filename: { + $regex: + /\.(mp4|mov|avi|mkv|webm|wmv|flv|mpg|mpeg|3gp|3g2|mxf|ogv|ogg|m4v)$/i, + }, + "metadata.thumbnailID": "", + "metadata.owner": "670c8c321ebf0d4211617eb9", + }); + + console.log("Found", files.length, "files"); + + for (let i = 0; i < files.length; i++) { + const currentFile = files[i]; + + console.log("current file", currentFile._id); + + const user = await User.findById(currentFile.metadata.owner); + + await createVideoThumbnail(currentFile, currentFile.filename, user); + } + + console.log("Done"); + + process.exit(); +}; + +updateDocs();