diff --git a/src/api/utils/ThumbUtil.js b/src/api/utils/ThumbUtil.js index 5cfd9c0..2931d3b 100644 --- a/src/api/utils/ThumbUtil.js +++ b/src/api/utils/ThumbUtil.js @@ -2,7 +2,7 @@ const jetpack = require('fs-jetpack'); const path = require('path'); const sharp = require('sharp'); const ffmpeg = require('fluent-ffmpeg'); -const previewUtil = require('./PreviewUtil'); +const previewUtil = require('./videoPreview/FragmentPreview'); const log = require('./Log'); diff --git a/src/api/utils/videoPreview/FragmentPreview.js b/src/api/utils/videoPreview/FragmentPreview.js new file mode 100644 index 0000000..8815392 --- /dev/null +++ b/src/api/utils/videoPreview/FragmentPreview.js @@ -0,0 +1,87 @@ +const ffmpeg = require('fluent-ffmpeg'); +const probe = require('ffmpeg-probe'); + +const noop = () => {}; + +const getRandomInt = (min, max) => { + const minInt = Math.ceil(min); + const maxInt = Math.floor(max); + + // eslint-disable-next-line no-mixed-operators + return Math.floor(Math.random() * (maxInt - minInt + 1) + minInt); +}; + +const getStartTime = (vDuration, fDuration, ignoreBeforePercent, ignoreAfterPercent) => { + // by subtracting the fragment duration we can be sure that the resulting + // start time + fragment duration will be less than the video duration + const safeVDuration = vDuration - fDuration; + + // if the fragment duration is longer than the video duration + if (safeVDuration <= 0) { + return 0; + } + + return getRandomInt(ignoreBeforePercent * safeVDuration, ignoreAfterPercent * safeVDuration); +}; + +module.exports = async opts => { + const { + log = noop, + + // general output options + quality = 2, + width, + height, + input, + output, + + fragmentDurationSecond = 3, + ignoreBeforePercent = 0.25, + ignoreAfterPercent = 0.75 + } = opts; + + const info = await probe(input); + + let { duration } = info.format; + duration = parseInt(duration, 10); + + const startTime = getStartTime(duration, fragmentDurationSecond, ignoreBeforePercent, ignoreAfterPercent); + + const result = { startTime, duration }; + + await new Promise((resolve, reject) => { + let scale = null; + + if (width && height) { + result.width = width | 0; + result.height = height | 0; + scale = `scale=${width}:${height}`; + } else if (width) { + result.width = width | 0; + result.height = ((info.height * width) / info.width) | 0; + scale = `scale=${width}:-1`; + } else if (height) { + result.height = height | 0; + result.width = ((info.width * height) / info.height) | 0; + scale = `scale=-1:${height}`; + } else { + result.width = info.width; + result.height = info.height; + } + + return ffmpeg() + .input(input) + .inputOptions([`-ss ${startTime}`]) + .outputOptions(['-vsync', 'vfr']) + .outputOptions(['-q:v', quality, '-vf', scale]) + .outputOptions([`-t ${fragmentDurationSecond}`]) + .noAudio() + .output(output) + .on('start', cmd => log && log({ cmd })) + .on('end', resolve) + .on('error', reject) + .run(); + }); + + return result; +}; diff --git a/src/api/utils/PreviewUtil.js b/src/api/utils/videoPreview/FrameIntervalPreview.js similarity index 97% rename from src/api/utils/PreviewUtil.js rename to src/api/utils/videoPreview/FrameIntervalPreview.js index bf3a480..75f6d2b 100644 --- a/src/api/utils/PreviewUtil.js +++ b/src/api/utils/videoPreview/FrameIntervalPreview.js @@ -1,8 +1,6 @@ const ffmpeg = require('fluent-ffmpeg'); const probe = require('ffmpeg-probe'); -const path = require('path'); - const noop = () => {}; module.exports = async opts => { @@ -61,7 +59,7 @@ module.exports = async opts => { ffmpeg(input) .outputOptions(['-vsync', 'vfr']) .outputOptions(['-q:v', quality, '-vf', filter]) - .outputOption('-an') + .noAudio() .outputFormat('webm') .output(output) .on('start', cmd => log && log({ cmd })) diff --git a/src/site/assets/styles/_colors.scss b/src/site/assets/styles/_colors.scss index a141447..b8861d2 100644 --- a/src/site/assets/styles/_colors.scss +++ b/src/site/assets/styles/_colors.scss @@ -1,13 +1,13 @@ // $basePink: #EC1A55; $base-1: #292e39; -$base-2: #2E3440; -$base-3: #3B4252; -$base-4: #434C5E; -$base-5: #4C566A; +$base-2: #2e3440; +$base-3: #3b4252; +$base-4: #434c5e; +$base-5: #4c566a; $background: #1e2430; $backgroundAccent: #20222b; -$backgroundAccentLighter: #53555E; +$backgroundAccentLighter: #53555e; $backgroundLight1: #f5f6f8; // customize navbar @@ -23,16 +23,15 @@ $input-hover-color: $textColor; $basePink: #ff015b; $basePinkHover: rgb(196, 4, 71); -$baseBlue: #30A9ED; +$baseBlue: #30a9ed; $baseBlueHover: rgb(21, 135, 201); $uploaderDropdownColor: #797979; -$boxShadow: 0 10px 15px rgba(4,39,107,0.2); +$boxShadow: 0 10px 15px rgba(4, 39, 107, 0.2); $boxShadowLight: rgba(15, 17, 21, 0.35) 0px 6px 9px 0px; // pagination - $pagination-color: $defaultTextColor; $pagination-focus-color: $textColorHighlight; @@ -45,4 +44,9 @@ $pagination-hover-color: $textColorHighlight; $pagination-hover-border-color: $textColorHighlight; $pagination-current-background-color: $base-3; -$pagination-current-border-color: $base-2; \ No newline at end of file +$pagination-current-border-color: $base-2; + +// loading + +$loading-background: rgba(0, 0, 0, 0.8); +$loading-background: rgba(40, 40, 40, 0.66);