early video support with thumbnailing

merge-requests/208/head
fatchan 5 years ago
parent b868bb52ce
commit e90847b0a4
  1. 3
      controllers/api.js
  2. 11
      helpers/files/file-check-mime-types.js
  3. 11
      helpers/files/format-size.js
  4. 0
      helpers/files/image-identify.js
  5. 2
      helpers/files/image-thumbnail.js
  6. 16
      helpers/files/video-identify.js
  7. 21
      helpers/files/video-thumbnail.js
  8. 59
      models/api/make-post.js
  9. 23
      package-lock.json
  10. 3
      package.json
  11. 2
      server.js
  12. 2
      static/css/style.css
  13. 4
      views/mixins/post.pug

@ -11,7 +11,6 @@ const express = require('express')
, getThread = require(__dirname+'/../models/api/get-thread.js')
, getCatalog = require(__dirname+'/../models/api/get-catalog.js')
, getBoards = require(__dirname+'/../models/api/get-boards.js');
/*
(async () => {
await Boards.deleteIncrement('pol');
@ -30,6 +29,8 @@ const express = require('express')
name: 'Random',
description: 'post anything here',
})
await Posts.db.collection('b').createIndex({"thread": 1});
await Posts.db.collection('pol').createIndex({"thread": 1});
})();
*/

@ -1,5 +1,14 @@
'use strict';
const allowedMimeTypes = new Set(['image/jpeg', 'image/pjpeg', 'image/png', 'image/gif']);
const allowedMimeTypes = new Set([
'image/jpeg',
'image/pjpeg',
'image/png',
'image/gif',
'image/webp',
'image/bmp',
'video/mp4',
'video/webm',
]);
module.exports = (mimetype) => allowedMimeTypes.has(mimetype);

@ -0,0 +1,11 @@
'use strict';
const sizes = ['B', 'KB', 'MB'];
const k = 1024;
module.exports = (bytes) => {
if (bytes === 0) {
return '0B';
}
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))}${sizes[i]}`;
};

@ -8,7 +8,7 @@ module.exports = (filename) => {
gm(uploadDirectory + filename)
.resize(128, 128)
.noProfile()
.write(uploadDirectory + 'thumb-' + filename, function (err) {
.write(`${uploadDirectory}thumb-${filename.split('.')[0]}.png`, function (err) {
if (err) {
return reject(err);
}

@ -0,0 +1,16 @@
const ffmpeg = require('fluent-ffmpeg')
, configs = require(__dirname+'/../../configs/main.json')
, uploadDirectory = require(__dirname+'/../uploadDirectory.js');
module.exports = (filename) => {
return new Promise((resolve, reject) => {
ffmpeg.ffprobe(uploadDirectory + filename, (err, metadata) => {
if (err) {
return reject(err)
}
return resolve(metadata);
});
});
};

@ -0,0 +1,21 @@
const ffmpeg = require('fluent-ffmpeg')
, configs = require(__dirname+'/../../configs/main.json')
, uploadDirectory = require(__dirname+'/../uploadDirectory.js');
module.exports = (filename) => {
return new Promise((resolve, reject) => {
ffmpeg(uploadDirectory + filename)
.on('end', () => {
return resolve();
})
.screenshots({
timestamps: [0],
count: 1,
filename: `thumb-${filename.split('.')[0]}.png`,
folder: uploadDirectory,
size: '128x128'
});
});
};

@ -18,9 +18,12 @@ const uuidv4 = require('uuid/v4')
}
}
, fileUpload = require(__dirname+'/../../helpers/files/file-upload.js')
, fileThumbnail = require(__dirname+'/../../helpers/files/file-thumbnail.js')
, fileIdentify = require(__dirname+'/../../helpers/files/file-identify.js')
, fileCheckMimeType = require(__dirname+'/../../helpers/files/file-check-mime-types.js');
, fileCheckMimeType = require(__dirname+'/../../helpers/files/file-check-mime-types.js')
, imageThumbnail = require(__dirname+'/../../helpers/files/image-thumbnail.js')
, imageIdentify = require(__dirname+'/../../helpers/files/image-identify.js')
, videoThumbnail = require(__dirname+'/../../helpers/files/video-thumbnail.js')
, videoIdentify = require(__dirname+'/../../helpers/files/video-identify.js')
, formatSize = require(__dirname+'/../../helpers/files/format-size.js')
module.exports = async (req, res, numFiles) => {
@ -62,21 +65,47 @@ module.exports = async (req, res, numFiles) => {
// then upload, thumb, get metadata, etc.
for (let i = 0; i < numFiles; i++) {
const file = req.files.file[i];
const filename = uuidv4() + path.extname(file.name);
const uuid = uuidv4();
const filename = uuid + path.extname(file.name);
// try to save, thumbnail and get metadata
try {
//upload file
await fileUpload(req, res, file, filename);
const fileData = await fileIdentify(filename);
await fileThumbnail(filename);
const processedFile = {
filename: filename,
originalFilename: file.name,
mimetype: file.mimetype,
size: file.size, // size in bytes
geometry: fileData.size, // object with width and height pixels
sizeString: fileData.Filesize, // 123 Ki string
geometryString: fileData.Geometry, // 123 x 123 string
//get metadata
let processedFile = {
filename: filename,
originalFilename: file.name,
mimetype: file.mimetype,
size: file.size,
};
//handle video vs image ffmpeg vs graphicsmagick
const mainType = file.mimetype.split('/')[0];
switch (mainType) {
case 'image':
const imageData = await imageIdentify(filename);
processedFile.geometry = imageData.size // object with width and height pixels
processedFile.sizeString = formatSize(processedFile.size) // 123 Ki string
processedFile.geometryString = imageData.Geometry // 123 x 123 string
await imageThumbnail(filename);
break;
case 'video':
//video metadata
const videoData = await videoIdentify(filename);
processedFile.geometry = {width: videoData.streams[0].coded_width, height: videoData.streams[0].coded_height} // object with width and height pixels
processedFile.sizeString = formatSize(processedFile.size) // 123 Ki string
processedFile.geometryString = `${processedFile.geometry.width}x${processedFile.geometry.height}` // 123 x 123 string
await videoThumbnail(filename);
break;
default:
return res.status(500).render('error'); //how did we get here?
}
//make thumbnail
//handle gifs with multiple geometry and size
if (Array.isArray(processedFile.geometry)) {
processedFile.geometry = processedFile.geometry[0];
@ -87,7 +116,9 @@ module.exports = async (req, res, numFiles) => {
if (Array.isArray(processedFile.geometryString)) {
processedFile.geometryString = processedFile.geometryString[0];
}
files.push(processedFile);
} catch (err) {
console.error(err);
//TODO: DELETE FAILED FILES

23
package-lock.json generated

@ -115,6 +115,14 @@
"resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
"integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
},
"async": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
"integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
"requires": {
"lodash": "^4.17.11"
}
},
"babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
@ -688,9 +696,9 @@
}
},
"express-fileupload": {
"version": "1.1.3-alpha.2",
"resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.1.3-alpha.2.tgz",
"integrity": "sha512-askIbniNmGzLBsmzDzfy9aR3vOFCUgNBOKesplC8XAYT85rOOTlgK0gdJMwgDKQ8tw4sdfgYpAnAbuPbYoyQKg==",
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.1.4.tgz",
"integrity": "sha512-/Fu4+pVQ1lED3jb17kqAjxYS3b/m0WWUSWBOCxBjdXc41SpXV1lUVc3LGAFLaLf6EkcUMzeAYRNJ7dWW9n/Jmw==",
"requires": {
"busboy": "^0.2.14"
}
@ -742,6 +750,15 @@
}
}
},
"fluent-ffmpeg": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.2.tgz",
"integrity": "sha1-yVLeIkD4EuvaCqgAbXd27irPfXQ=",
"requires": {
"async": ">=0.2.9",
"which": "^1.1.1"
}
},
"forwarded": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",

@ -10,8 +10,9 @@
"cookie-parser": "^1.4.3",
"csurf": "^1.9.0",
"express": "^4.16.3",
"express-fileupload": "^1.1.3-alpha.2",
"express-fileupload": "^1.1.4",
"express-session": "^1.15.6",
"fluent-ffmpeg": "^2.1.2",
"fs": "0.0.1-security",
"helmet": "^3.13.0",
"mongodb": "^3.2.2",

@ -27,7 +27,7 @@ const express = require('express')
app.use(upload({
createParentPath: true,
safeFileNames: true,
preserveExtension: true,
preserveExtension: 4,
limits: {
fileSize: 10 * 1024 * 1024,
files: 3

@ -80,6 +80,8 @@ input, textarea {
.post-file-info {
text-align: center;
margin: 2px;
max-width: 128px;
word-break: break-all;
}
.post-file-src {

@ -16,12 +16,12 @@ mixin post(board, post, truncate)
each file in post.files
.post-file
.post-file-info
span: a(href='/img/thumb-'+file.filename download=file.originalFilename) Download
span: a(href='/img/thumb-'+file.filename download=file.originalFilename) #{file.originalFilename}
br
span (#{file.sizeString} #{file.geometryString})
.post-file-src
a(target='_blank' href='/img/'+file.filename)
object(data='/img/thumb-'+file.filename type=file.mimetype)
object(data=`/img/thumb-${file.filename.split('.')[0]}.png` width='128' height='128')
if post.message
if truncate
-

Loading…
Cancel
Save