change video/audio handling, allows "audio" files with a video mime type to be uploaded properly, and get treated like audio file. might need some testing to make sure nothing broke :)

merge-requests/208/head
Thomas Lynch 4 years ago
parent 01d11f9491
commit 8629143610
  1. 8
      controllers/forms/makepost.js
  2. 183
      models/forms/makepost.js

@ -3,7 +3,8 @@
const makePost = require(__dirname+'/../../models/forms/makepost.js') const makePost = require(__dirname+'/../../models/forms/makepost.js')
, deleteTempFiles = require(__dirname+'/../../helpers/files/deletetempfiles.js') , deleteTempFiles = require(__dirname+'/../../helpers/files/deletetempfiles.js')
, dynamicResponse = require(__dirname+'/../../helpers/dynamic.js') , dynamicResponse = require(__dirname+'/../../helpers/dynamic.js')
, { globalLimits, disableOnionFilePosting } = require(__dirname+'/../../configs/main.js') , pruneFiles = require(__dirname+'/../../schedules/prune.js')
, { pruneImmediately, globalLimits, disableOnionFilePosting } = require(__dirname+'/../../configs/main.js')
, { Files } = require(__dirname+'/../../db/'); , { Files } = require(__dirname+'/../../db/');
module.exports = async (req, res, next) => { module.exports = async (req, res, next) => {
@ -88,7 +89,10 @@ module.exports = async (req, res, next) => {
} catch (err) { } catch (err) {
await deleteTempFiles(req).catch(e => console.error); await deleteTempFiles(req).catch(e => console.error);
if (res.locals.numFiles > 0) { if (res.locals.numFiles > 0) {
await Files.decrement(req.files.file.filter(x => x.inced === true && x.filename != null).map(x => x.filename)).catch(e => console.error); const incedFiles = req.files.file.filter(x => x.inced === true && x.filename != null);
const incedFileNames = incedFiles.map(x => x.filename);
await Files.decrement(incedFileNames).catch(e => console.error);
await pruneFiles(incedFileNames);
} }
return next(err); return next(err);
} }

@ -241,139 +241,110 @@ ${res.locals.numFiles > 0 ? req.files.file.map(f => f.name+'|'+(f.phash || '')).
}; };
//type and subtype //type and subtype
const [type, subtype] = processedFile.mimetype.split('/'); let [type, subtype] = processedFile.mimetype.split('/');
let imageData;
let firstFrameOnly = true;
if (type === 'image') {
processedFile.thumbextension = thumbExtension;
///detect images with opacity for PNG thumbnails, set thumbextension before increment
try {
imageData = await imageIdentify(req.files.file[i].tempFilePath, null, true);
} catch (e) {
await deleteTempFiles(req).catch(e => console.error);
return dynamicResponse(req, res, 400, 'message', {
'title': 'Bad request',
'message': `The server failed to process "${req.files.file[i].name}". Possible unsupported or corrupt file.`,
'redirect': redirect
});
}
if (imageData['Channel Statistics'] && imageData['Channel Statistics']['Opacity']) {//does this depend on GM version or anything?
const opacityMaximum = imageData['Channel Statistics']['Opacity']['Maximum'];
if (opacityMaximum !== '0.00 (0.0000)') {
processedFile.thumbextension = '.png';
}
}
processedFile.geometry = imageData.size;
processedFile.geometryString = imageData.Geometry;
const lteThumbSize = (processedFile.geometry.height <= thumbSize
&& processedFile.geometry.width <= thumbSize);
processedFile.hasThumb = !(mimeTypes.allowed(file.mimetype, {image: true})
&& subtype !== 'png'
&& lteThumbSize);
if (processedFile.hasThumb //if it needs thumbnailing
&& (!lteThumbSize //and its big enough
&& file.mimetype === 'image/gif' //and its a gif
&& (imageData['Delay'] != null || imageData['Iterations'] != null) //and its not a static gif (naive check)
&& animatedGifThumbnails === true)) { //and animated thumbnails for gifs are enabled
firstFrameOnly = false;
processedFile.thumbextension = '.gif';
}
} else if (type === 'audio') {
if (audioThumbnails) {
// waveform has a transparent background, so force png
processedFile.thumbextension = '.png';
}
} else {
processedFile.thumbextension = thumbExtension;
}
//increment file count
await Files.increment(processedFile);
req.files.file[i].inced = true;
//check if already exists //check if already exists
const existsFull = await pathExists(`${uploadDirectory}/file/${processedFile.filename}`); const existsFull = await pathExists(`${uploadDirectory}/file/${processedFile.filename}`);
processedFile.sizeString = formatSize(processedFile.size) processedFile.sizeString = formatSize(processedFile.size)
const saveFull = async () => {
if (!existsFull) {
await Files.increment(processedFile);
req.files.file[i].inced = true;
await moveUpload(file, processedFile.filename, 'file');
}
}
if (mimeTypes.other.has(processedFile.mimetype)) { if (mimeTypes.other.has(processedFile.mimetype)) {
//"other" mimes from config, overrides main type to avoid codec issues in browser or ffmpeg for unsupported filetypes //"other" mimes from config, overrides main type to avoid codec issues in browser or ffmpeg for unsupported filetypes
processedFile.hasThumb = false; processedFile.hasThumb = false;
processedFile.attachment = true; processedFile.attachment = true;
if (!existsFull) { await saveFull();
await moveUpload(file, processedFile.filename, 'file');
}
} else { } else {
const existsThumb = await pathExists(`${uploadDirectory}/file/thumb-${processedFile.hash}${processedFile.thumbextension}`); const existsThumb = await pathExists(`${uploadDirectory}/file/thumb-${processedFile.hash}${processedFile.thumbextension}`);
switch (type) { switch (type) {
case 'image': { case 'image': {
if (!existsFull) { processedFile.thumbextension = thumbExtension;
await moveUpload(file, processedFile.filename, 'file'); ///detect images with opacity for PNG thumbnails, set thumbextension before increment
} let imageData;
if (!existsThumb) { try {
await imageThumbnail(processedFile, firstFrameOnly); imageData = await imageIdentify(req.files.file[i].tempFilePath, null, true);
} } catch (e) {
processedFile = fixGifs(processedFile);
break;
}
case 'video': {
//video metadata
const videoData = await ffprobe(req.files.file[i].tempFilePath, null, true);
videoData.streams = videoData.streams.filter(stream => stream.width != null); //filter to only video streams or something with a resolution
if (videoData.streams.length <= 0) {
//corrupt, or audio only?
await deleteTempFiles(req).catch(e => console.error); await deleteTempFiles(req).catch(e => console.error);
return dynamicResponse(req, res, 400, 'message', { return dynamicResponse(req, res, 400, 'message', {
'title': 'Bad request', 'title': 'Bad request',
'message': 'Audio only video file not supported', 'message': `The server failed to process "${req.files.file[i].name}". Possible unsupported or corrupt file.`,
'redirect': redirect 'redirect': redirect
}); });
} }
processedFile.duration = videoData.format.duration; if (imageData['Channel Statistics'] && imageData['Channel Statistics']['Opacity']) {
processedFile.durationString = timeUtils.durationString(videoData.format.duration*1000); //does this depend on GM version or anything?
processedFile.geometry = {width: videoData.streams[0].coded_width, height: videoData.streams[0].coded_height}; const opacityMaximum = imageData['Channel Statistics']['Opacity']['Maximum'];
processedFile.geometryString = `${processedFile.geometry.width}x${processedFile.geometry.height}` if (opacityMaximum !== '0.00 (0.0000)') {
processedFile.hasThumb = true; processedFile.thumbextension = '.png';
if (!existsFull) { }
await moveUpload(file, processedFile.filename, 'file'); }
processedFile.geometry = imageData.size;
processedFile.geometryString = imageData.Geometry;
const lteThumbSize = (processedFile.geometry.height <= thumbSize
&& processedFile.geometry.width <= thumbSize);
processedFile.hasThumb = !(mimeTypes.allowed(file.mimetype, {image: true})
&& subtype !== 'png'
&& lteThumbSize);
let firstFrameOnly = true;
if (processedFile.hasThumb //if it needs thumbnailing
&& (!lteThumbSize //and its big enough
&& file.mimetype === 'image/gif' //and its a gif
&& (imageData['Delay'] != null || imageData['Iterations'] != null) //and its not a static gif (naive check)
&& animatedGifThumbnails === true)) { //and animated thumbnails for gifs are enabled
firstFrameOnly = false;
processedFile.thumbextension = '.gif';
} }
await saveFull();
if (!existsThumb) { if (!existsThumb) {
const numFrames = videoData.streams[0].nb_frames; await imageThumbnail(processedFile, firstFrameOnly);
if (numFrames === 'N/A' && subtype === 'webm') {
await videoThumbnail(processedFile, processedFile.geometry, videoThumbPercentage+'%');
let videoThumbStat = null;
try {
videoThumbStat = await fsStat(`${uploadDirectory}/file/thumb-${processedFile.hash}${processedFile.thumbextension}`);
} catch (err) { /*ENOENT, the thumb failed to create. No need to handle this.*/ }
if (!videoThumbStat || videoThumbStat.size === 0) {
await videoThumbnail(processedFile, processedFile.geometry, 0);
}
} else {
await videoThumbnail(processedFile, processedFile.geometry, ((numFrames === 'N/A' || numFrames <= 1) ? 0 : videoThumbPercentage+'%'));
}
} }
processedFile = fixGifs(processedFile);
break; break;
} }
case 'audio': { case 'audio':
//audio metadata case 'video':
const audioData = await ffprobe(req.files.file[i].tempFilePath, null, true); //video metadata
processedFile.duration = audioData.format.duration; const audioVideoData = await ffprobe(req.files.file[i].tempFilePath, null, true);
processedFile.durationString = timeUtils.durationString(audioData.format.duration*1000); processedFile.duration = audioVideoData.format.duration;
processedFile.hasThumb = audioThumbnails; processedFile.durationString = timeUtils.durationString(audioVideoData.format.duration*1000);
if (!existsFull) { const videoStreams = audioVideoData.streams.filter(stream => stream.width != null); //filter to only video streams or something with a resolution
await moveUpload(file, processedFile.filename, 'file'); if (videoStreams.length > 0) {
} processedFile.thumbextension = thumbExtension;
if (audioThumbnails) { processedFile.geometry = {width: videoStreams[0].coded_width, height: videoStreams[0].coded_height};
// audio thumbnail is always thumbSize x thumbSize processedFile.geometryString = `${processedFile.geometry.width}x${processedFile.geometry.height}`
processedFile.geometry = { processedFile.hasThumb = true;
thumbwidth: thumbSize, thumbheight: thumbSize, await saveFull();
}; if (!existsThumb) {
const numFrames = videoStreams[0].nb_frames;
if (numFrames === 'N/A' && subtype === 'webm') {
await videoThumbnail(processedFile, processedFile.geometry, videoThumbPercentage+'%');
let videoThumbStat = null;
try {
videoThumbStat = await fsStat(`${uploadDirectory}/file/thumb-${processedFile.hash}${processedFile.thumbextension}`);
} catch (err) { /*ENOENT, the thumb failed to create. No need to handle this.*/ }
if (!videoThumbStat || videoThumbStat.size === 0) {
await videoThumbnail(processedFile, processedFile.geometry, 0);
}
} else {
await videoThumbnail(processedFile, processedFile.geometry, ((numFrames === 'N/A' || numFrames <= 1) ? 0 : videoThumbPercentage+'%'));
}
}
} else {
//audio file, or video with only audio streams
type = 'audio';
processedFile.mimetype = `audio/${subtype}`;
processedFile.thumbextension = '.png';
processedFile.hasThumb = audioThumbnails;
processedFile.geometry = { thumbwidth: thumbSize, thumbheight: thumbSize };
await saveFull();
if (!existsThumb) { if (!existsThumb) {
await audioThumbnail(processedFile); await audioThumbnail(processedFile);
} }
} }
break; break;
}
default: default:
throw new Error(`invalid file mime type: ${processedFile}`); throw new Error(`invalid file mime type: ${processedFile}`);
} }

Loading…
Cancel
Save