diff --git a/client/package.json b/client/package.json
index 58ad417..e70821a 100644
--- a/client/package.json
+++ b/client/package.json
@@ -53,6 +53,7 @@
"formik": "^2.2.9",
"history": "^5.3.0",
"lodash": "^4.17.21",
+ "moment": "^2.29.4",
"numeral": "^2.0.6",
"prop-types": "^15.8.1",
"react": "^18.2.0",
@@ -60,6 +61,7 @@
"react-dom": "^18.2.0",
"react-helmet-async": "^1.3.0",
"react-hook-form": "^7.37.0",
+ "react-moment": "^1.1.3",
"react-player": "^2.11.0",
"react-router-dom": "^6.4.2",
"react-scripts": "^5.0.1",
@@ -69,7 +71,6 @@
"yup": "^0.32.11"
},
"devDependencies": {
- "serve": "^14.2.0",
"@babel/core": "^7.19.3",
"@babel/eslint-parser": "^7.19.1",
"@svgr/webpack": "^6.5.0",
@@ -83,7 +84,8 @@
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.31.10",
"eslint-plugin-react-hooks": "^4.6.0",
- "prettier": "^2.7.1"
+ "prettier": "^2.7.1",
+ "serve": "^14.2.0"
},
"overrides": {
"@svgr/webpack": "^6.5.0"
diff --git a/client/src/pages/ProductsPage.js b/client/src/pages/ProductsPage.js
index f9568ff..b779597 100644
--- a/client/src/pages/ProductsPage.js
+++ b/client/src/pages/ProductsPage.js
@@ -3,7 +3,7 @@ import { useState } from 'react';
// @mui
import { Container, Stack, Typography } from '@mui/material';
// components
-import { ProductSort, ProductList, ProductCartWidget, ProductFilterSidebar } from '../sections/@dashboard/products';
+import { ProductSort, VideoList, ProductCartWidget, ProductFilterSidebar } from '../sections/@dashboard/products';
// mock
import PRODUCTS from '../_mock/products';
@@ -42,7 +42,7 @@ export default function ProductsPage() {
-
+
>
diff --git a/client/src/pages/VideosPage.js b/client/src/pages/VideosPage.js
index 8ae4cd0..8fdbe96 100644
--- a/client/src/pages/VideosPage.js
+++ b/client/src/pages/VideosPage.js
@@ -8,7 +8,7 @@ import { Container, Stack, Typography } from '@mui/material';
// components
import {
ProductSort,
- ProductList,
+ VideoList,
ProductCartWidget,
ProductFilterSidebar,
} from '../sections/@dashboard/products';
@@ -66,7 +66,7 @@ export default function ProductsPage() {
-
+
>
diff --git a/client/src/sections/@dashboard/products/ProductList.js b/client/src/sections/@dashboard/products/ProductList.js
deleted file mode 100644
index 1494145..0000000
--- a/client/src/sections/@dashboard/products/ProductList.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import PropTypes from 'prop-types';
-// @mui
-import { Grid } from '@mui/material';
-import ShopProductCard from './ProductCard';
-
-// ----------------------------------------------------------------------
-
-ProductList.propTypes = {
- products: PropTypes.array.isRequired,
-};
-
-export default function ProductList({ products = [], ...other }) {
- return (
-
- {products.map((product) => (
-
-
-
- ))}
-
- );
-}
diff --git a/client/src/sections/@dashboard/products/ProductCard.js b/client/src/sections/@dashboard/products/VideoCard.js
similarity index 57%
rename from client/src/sections/@dashboard/products/ProductCard.js
rename to client/src/sections/@dashboard/products/VideoCard.js
index 46a743f..244f9b8 100644
--- a/client/src/sections/@dashboard/products/ProductCard.js
+++ b/client/src/sections/@dashboard/products/VideoCard.js
@@ -4,6 +4,7 @@ import { Box, Card, Stack, Typography } from '@mui/material';
import { styled } from '@mui/material/styles';
import { Link } from "react-router-dom";
+import Moment from 'react-moment';
// utils
// components
@@ -21,17 +22,18 @@ const StyledProductImg = styled('img')({
// ----------------------------------------------------------------------
-ShopProductCard.propTypes = {
- product: PropTypes.object,
+VideoCard.propTypes = {
+ video: PropTypes.object,
};
-export default function ShopProductCard({ video }) {
+export default function VideoCard({ video }) {
const {
title: name,
thumbnailUrl: cover,
viewCount,
- duration: status,
- publishedAt,
+ duration,
+ status,
+ recordingDate,
_id: id,
} = video;
@@ -39,6 +41,8 @@ export default function ShopProductCard({ video }) {
console.log('clicked', video);
};
+ const videoDuration = duration ?? 0;
+
return (
@@ -61,20 +65,23 @@ export default function ShopProductCard({ video }) {
-
-
- {name}
-
-
-
-
- {publishedAt}
- {viewCount} views
-
+
+
+
+ {name}
+
+
+
+ {recordingDate}
+
+
+
+
+ {viewCount} views
+
+ {videoDuration*1000}
+
+
);
diff --git a/client/src/sections/@dashboard/products/VideoList.js b/client/src/sections/@dashboard/products/VideoList.js
new file mode 100644
index 0000000..f4b19ca
--- /dev/null
+++ b/client/src/sections/@dashboard/products/VideoList.js
@@ -0,0 +1,22 @@
+import PropTypes from 'prop-types';
+// @mui
+import { Grid } from '@mui/material';
+import VideoCard from './VideoCard';
+
+// ----------------------------------------------------------------------
+
+VideoList.propTypes = {
+ videos: PropTypes.array.isRequired,
+};
+
+export default function VideoList({ videos = [], ...other }) {
+ return (
+
+ {videos.map((video) => (
+
+
+
+ ))}
+
+ );
+}
diff --git a/client/src/sections/@dashboard/products/index.js b/client/src/sections/@dashboard/products/index.js
index 43784e1..5a7e980 100644
--- a/client/src/sections/@dashboard/products/index.js
+++ b/client/src/sections/@dashboard/products/index.js
@@ -1,5 +1,5 @@
-export { default as ProductCard } from './ProductCard';
-export { default as ProductList } from './ProductList';
+export { default as VideoCard } from './VideoCard';
+export { default as VideoList } from './VideoList';
export { default as ProductSort } from './ProductSort';
export { default as ProductCartWidget } from './ProductCartWidget';
export { default as ProductFilterSidebar } from './ProductFilterSidebar';
diff --git a/client/yarn.lock b/client/yarn.lock
index 02937b8..f64eac1 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -6681,6 +6681,11 @@ mkdirp@~0.5.1:
dependencies:
minimist "^1.2.6"
+moment@^2.29.4:
+ version "2.29.4"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
+ integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
+
ms@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
@@ -7967,6 +7972,11 @@ react-is@^18.0.0, react-is@^18.2.0:
resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
+react-moment@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/react-moment/-/react-moment-1.1.3.tgz#829b21dfb279aa6db47ce4f1ac2555af17a1bcdc"
+ integrity sha512-8EPvlUL8u6EknPp1ISF5MQ3wx2OHJVXIP/iZc4wRh3iV3XozftZERDv9ANZeAtMlhNNQHdFoqcZHFUkBSTONfA==
+
react-player@^2.11.0:
version "2.11.0"
resolved "https://registry.yarnpkg.com/react-player/-/react-player-2.11.0.tgz#9afc75314eb915238e8d6615b2891fbe7170aeaa"
diff --git a/server/package-lock.json b/server/package-lock.json
index 674161c..b169254 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -82,7 +82,127 @@
"integrity": "sha512-Drt5u2vzDnIONf4ZEkKtFlbvwj6rI3kxw1Ck9fpudmtgaZIHD4ucsWB2lCZBXRxJgXR+2IMSti+4rtM4C4rXgg==",
"optional": true
},
- "@hapi/hoek": {
+ "node_modules/@ffprobe-installer/darwin-arm64": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@ffprobe-installer/darwin-arm64/-/darwin-arm64-5.0.1.tgz",
+ "integrity": "sha512-vwNCNjokH8hfkbl6m95zICHwkSzhEvDC3GVBcUp5HX8+4wsX10SP3B+bGur7XUzTIZ4cQpgJmEIAx6TUwRepMg==",
+ "cpu": [
+ "arm64"
+ ],
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@ffprobe-installer/darwin-x64": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@ffprobe-installer/darwin-x64/-/darwin-x64-5.1.0.tgz",
+ "integrity": "sha512-J+YGscZMpQclFg31O4cfVRGmDpkVsQ2fZujoUdMAAYcP0NtqpC49Hs3SWJpBdsGB4VeqOt5TTm1vSZQzs1NkhA==",
+ "cpu": [
+ "x64"
+ ],
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@ffprobe-installer/ffprobe": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@ffprobe-installer/ffprobe/-/ffprobe-2.1.1.tgz",
+ "integrity": "sha512-v4nAFp70Gv7yyEvjvlGTOrYn1atjFi6Z9vpGpGvWAiCXL6vnL88gBeIyJ4hY01G/XOw1LVuq1vjUM3gQzxMmIA==",
+ "engines": {
+ "node": ">=14.21.2"
+ },
+ "optionalDependencies": {
+ "@ffprobe-installer/darwin-arm64": "5.0.1",
+ "@ffprobe-installer/darwin-x64": "5.1.0",
+ "@ffprobe-installer/linux-arm": "5.1.0",
+ "@ffprobe-installer/linux-arm64": "5.1.0",
+ "@ffprobe-installer/linux-ia32": "5.1.0",
+ "@ffprobe-installer/linux-x64": "5.1.0",
+ "@ffprobe-installer/win32-ia32": "5.1.0",
+ "@ffprobe-installer/win32-x64": "5.1.0"
+ }
+ },
+ "node_modules/@ffprobe-installer/linux-arm": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-arm/-/linux-arm-5.1.0.tgz",
+ "integrity": "sha512-y34AEive/M5/++RcuRJZciICYYRkmTh5gK6th7raydgfQhIYy/8AXAIGKqD5Hn855i6o/RpEIPjk7DWpEuwrdA==",
+ "cpu": [
+ "arm"
+ ],
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@ffprobe-installer/linux-arm64": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-arm64/-/linux-arm64-5.1.0.tgz",
+ "integrity": "sha512-u5b3r3/39eZmEV0X4OKUfp6gO3lOyVIpRO4WU+5Eb6omWYy+k0OkVbRffK4qJF1Uz8irwzyiDsmUZ6vE13/abQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@ffprobe-installer/linux-ia32": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-ia32/-/linux-ia32-5.1.0.tgz",
+ "integrity": "sha512-Uqk4sYYQxz0KQmEQ2xxbVu/KiDX7Pw6wRDpCYv0sW49GI3wpX2Umqi/Kmtr0tpCvxctVoCdf/U71EAxH2Lztdg==",
+ "cpu": [
+ "ia32"
+ ],
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@ffprobe-installer/linux-x64": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@ffprobe-installer/linux-x64/-/linux-x64-5.1.0.tgz",
+ "integrity": "sha512-r7cGOjNb8AMnKAEvz5f8N/WsWsre02LhAfDKZX0m5J0bsrYgs2HUlnnQiwjRCH9CYXYerjYqq592o/GXvxDS+Q==",
+ "cpu": [
+ "x64"
+ ],
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@ffprobe-installer/win32-ia32": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@ffprobe-installer/win32-ia32/-/win32-ia32-5.1.0.tgz",
+ "integrity": "sha512-5O3vOoNRxmut0/Nu9vSazTdSHasrr+zPT2B3Hm7kjmO3QVFcIfVImS6ReQnZeSy8JPJOqXts5kX5x/3KOX54XQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@ffprobe-installer/win32-x64": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@ffprobe-installer/win32-x64/-/win32-x64-5.1.0.tgz",
+ "integrity": "sha512-jMGYeAgkrdn4e2vvYt/qakgHRE3CPju4bn5TmdPfoAm1BlX1mY9cyMd8gf5vSzI8gH8Zq5WQAyAkmekX/8TSTg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@hapi/hoek": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
"integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ=="
diff --git a/server/package.json b/server/package.json
index 4e21224..cf9c433 100644
--- a/server/package.json
+++ b/server/package.json
@@ -24,6 +24,7 @@
"license": "ISC",
"dependencies": {
"@ffmpeg-installer/ffmpeg": "^1.1.0",
+ "@ffprobe-installer/ffprobe": "^2.1.1",
"bcrypt": "^5.1.0",
"bullmq": "^3.5.11",
"compression": "^1.7.4",
@@ -33,6 +34,7 @@
"express": "^4.18.2",
"express-pino-logger": "^7.0.0",
"express-rate-limit": "^6.7.0",
+ "ffprobe": "^1.1.2",
"fluent-ffmpeg": "^2.1.2",
"helmet": "^6.0.1",
"joi": "^17.7.0",
diff --git a/server/scripts/seed-data/video.js b/server/scripts/seed-data/video.js
index acadfd1..75654ec 100644
--- a/server/scripts/seed-data/video.js
+++ b/server/scripts/seed-data/video.js
@@ -1,5 +1,6 @@
const { faker } = require('@faker-js/faker');
const { MongoManager } = require('../../src/modules/db/mongo');
+const { VIDEO_STATUS, VIDEO_VISIBILITIES } = require('../../src/modules/db/constant');
const getFakeVideosData = (x) => {
const videos = [];
@@ -7,7 +8,7 @@ const getFakeVideosData = (x) => {
for (let i = 0; i < 1000; i++) {
videos.push({
title: faker.lorem.sentence(5),
- visibility: faker.helpers.arrayElement(['Public', 'Private', 'Unlisted']),
+ visibility: faker.helpers.arrayElement(Object.values(VIDEO_VISIBILITIES)),
category: faker.helpers.arrayElement([
'education',
'entertainment',
@@ -18,7 +19,8 @@ const getFakeVideosData = (x) => {
recordingDate: faker.date.past(),
publishedAt: faker.date.past(),
fileName: faker.lorem.sentence(5),
-
+ status: faker.helpers.arrayElement(Object.values(VIDEO_STATUS)),
+ isDeleted : faker.datatype.boolean(),
thumbnailUrl: faker.image.imageUrl(),
duration: parseInt(faker.random.numeric()),
viewCount: parseInt(faker.random.numeric()),
diff --git a/server/src/modules/db/constant.js b/server/src/modules/db/constant.js
new file mode 100644
index 0000000..6fc6065
--- /dev/null
+++ b/server/src/modules/db/constant.js
@@ -0,0 +1,18 @@
+const VIDEO_VISIBILITIES = {
+ PUBLIC : 'Public',
+ PRIVATE : 'Private',
+ UNLISTED : 'Unlisted',
+};
+
+
+const VIDEO_STATUS = {
+ PENDING : "pending",
+ PROCESSED : "processed",
+ PUBLISHED : "published"
+};
+
+
+module.exports = {
+ VIDEO_STATUS,
+ VIDEO_VISIBILITIES
+}
\ No newline at end of file
diff --git a/server/src/modules/db/schemas/videos.js b/server/src/modules/db/schemas/videos.js
index 9648e40..f88f349 100644
--- a/server/src/modules/db/schemas/videos.js
+++ b/server/src/modules/db/schemas/videos.js
@@ -1,6 +1,6 @@
const { baseSchema, ensureCollection } = require('./common');
-const VIDEO_VISIBILITIES = ['Public', 'Private', 'Unlisted'];
+const { VIDEO_STATUS, VIDEO_VISIBILITIES } = require('../constant');
const name = 'videos';
@@ -20,6 +20,7 @@ const updateSchema = async (db) => {
'fileName',
'originalName',
'visibility',
+ 'status',
'recordingDate',
'videoLink',
...Object.keys(baseSchema),
@@ -40,7 +41,7 @@ const updateSchema = async (db) => {
description: 'must be an integer',
},
visibility: {
- enum: VIDEO_VISIBILITIES,
+ enum: Object.values(VIDEO_VISIBILITIES),
description: 'can only be one of the enum values and is required',
},
duration: {
@@ -48,6 +49,10 @@ const updateSchema = async (db) => {
minimum: 1,
description: 'must be an integer',
},
+ status: {
+ enum: Object.values(VIDEO_STATUS),
+ description: "can only be one of the enum values and is required",
+ },
playlistId: {
bsonType: 'objectId',
description: 'must be an objectId and is required',
@@ -127,6 +132,10 @@ const updateSchema = async (db) => {
key: { viewCount: -1 },
name: 'custom_viewCount_index',
},
+ {
+ key: { status: -1 },
+ name: "custom_status_index",
+ },
];
await ensureCollection({ db, name, validator, indexes });
diff --git a/server/src/modules/models/video/controller.js b/server/src/modules/models/video/controller.js
index 53cb58c..0f50cff 100644
--- a/server/src/modules/models/video/controller.js
+++ b/server/src/modules/models/video/controller.js
@@ -4,11 +4,13 @@ const {
search,
update,
getById,
+ updateViewCount,
deleteById,
} = require('./service');
const { validate } = require('./request');
const { VIDEO_QUEUE_EVENTS: QUEUE_EVENTS } = require('../../queues/constants');
const { addQueueItem } = require('../../queues/queue');
+const { getVideoDurationAndResolution } = require('../../queues/video-processor');
const BASE_URL = `/api/videos`;
@@ -29,7 +31,7 @@ const setupRoutes = (app) => {
app.get(`${BASE_URL}/detail/:id`, async (req, res) => {
console.log(`GET`, req.params);
- const video = await getById(req.params.id);
+ const video = await updateViewCount(req.params.id);
if (video instanceof Error) {
return res.status(400).json(JSON.parse(video.message));
}
@@ -133,14 +135,17 @@ const setupRoutes = (app) => {
app.post(`${BASE_URL}/upload`, uploadProcessor, async (req, res) => {
try {
- console.log('POST upload', JSON.stringify(req.body));
+
+ const { videoDuration } = await getVideoDurationAndResolution(`./${req.file.path}`)
+
const dbPayload = {
...req.body,
fileName: req.file.filename,
- originalName: req.file.originalname,
+ originalName: req.file.originalname,
recordingDate: new Date(),
videoLink: req.file.path,
- viewCount: 0,
+ viewCount:0,
+ duration:videoDuration
};
console.log('dbPayload', dbPayload);
// TODO: save the file info and get the id from the database
diff --git a/server/src/modules/models/video/handler.js b/server/src/modules/models/video/handler.js
index c8aa605..d275d8d 100644
--- a/server/src/modules/models/video/handler.js
+++ b/server/src/modules/models/video/handler.js
@@ -1,16 +1,19 @@
const eventEmitter = require('../../../event-manager').getInstance();
const { VIDEO_QUEUE_EVENTS } = require('../../queues/constants');
-const { updateHistory } = require('./service');
+const { updateHistory, update } = require('./service');
+const { VIDEO_STATUS } = require('../../db/constant')
const setup = () => {
// eventEmitter.on(VIDEO_QUEUE_EVENTS.VIDEO_UPLOADED, (data) => {
// console.log('VIDEO_QUEUE_EVENTS.VIDEO_UPLOADED Event handler', data);
// });
- console.log('registering video queue events');
+
const SERVER_URL = process.env.SERVER_URL;
+
Object.values(VIDEO_QUEUE_EVENTS).forEach((eventName) => {
+
eventEmitter.on(eventName, async (data) => {
- console.log(`models/video/handler.js - ${eventName}`, data);
+
if (eventName === VIDEO_QUEUE_EVENTS.VIDEO_PROCESSED) {
await updateHistory(data.id, {
history: { status: eventName, createdAt: new Date() },
@@ -18,13 +21,21 @@ const setup = () => {
});
return;
}
+
if (eventName === VIDEO_QUEUE_EVENTS.VIDEO_HLS_CONVERTED) {
await updateHistory(data.id, {
history: { status: eventName, createdAt: new Date() },
hlsPath: data.path,
});
+
+ await update({
+ _id: data.id,
+ status: VIDEO_STATUS.PUBLISHED
+ });
+
return;
}
+
if (eventName === VIDEO_QUEUE_EVENTS.VIDEO_THUMBNAIL_GENERATED) {
await updateHistory(data.id, {
history: { status: eventName, createdAt: new Date() },
diff --git a/server/src/modules/models/video/service.js b/server/src/modules/models/video/service.js
index 8693e26..6033c47 100644
--- a/server/src/modules/models/video/service.js
+++ b/server/src/modules/models/video/service.js
@@ -1,9 +1,13 @@
const { ObjectId } = require('mongodb');
const { Video } = require('../../db/collections');
+const { VIDEO_STATUS } = require('../../db/constant')
+
+
+// TODO: add logging
const insert = async (document) => {
try {
- return await Video.insert(document);
+ return await Video.insert({status: VIDEO_STATUS.PENDING, ...document}); // assigning default satus for all new videos
} catch (error) {
return error;
}
@@ -13,7 +17,6 @@ const update = async (document) => {
try {
return await Video.update(document);
} catch (error) {
- console.error(error);
return error;
}
};
@@ -34,6 +37,7 @@ const search = async (searchObject) => {
category: 1,
duration: 1,
viewCount: 1,
+ status : 1
};
const sort = searchObject.sort || { viewCount: -1 };
@@ -81,6 +85,7 @@ const deleteById = async (id) => {
}
};
+
module.exports = {
insert,
search,
diff --git a/server/src/modules/queues/video-processor.js b/server/src/modules/queues/video-processor.js
index 32f32db..b6592e6 100644
--- a/server/src/modules/queues/video-processor.js
+++ b/server/src/modules/queues/video-processor.js
@@ -1,8 +1,12 @@
/** execute function will take a filePath and run ffmpeg command to convert it to mp4 */
-const ffmpegInstaller = require("@ffmpeg-installer/ffmpeg");
const ffmpeg = require("fluent-ffmpeg");
+
+const ffmpegInstaller = require("@ffmpeg-installer/ffmpeg");
ffmpeg.setFfmpegPath(ffmpegInstaller.path);
-console.log(ffmpegInstaller.path, ffmpegInstaller.version);
+
+const ffprobeInstaller = require('@ffprobe-installer/ffprobe');
+ffmpeg.setFfprobePath(ffprobeInstaller.path);
+
const path = require("path");
const { VIDEO_QUEUE_EVENTS: QUEUE_EVENTS } = require("./constants");
const { addQueueItem } = require("./queue");
@@ -102,4 +106,31 @@ const processMp4ToHls = async (filePath, outputFolder, jobData) => {
return;
};
-module.exports = { processRawFileToMp4, processMp4ToHls, generateThumbnail };
+const getVideoDurationAndResolution = (filePath) => {
+
+ // if any error occour return error
+ // else return video meta data
+ return new Promise((resolve,reject) => {
+ let videoDuration = 0
+ let videoResolution = {
+ height : 0,
+ width : 0
+ }
+ ffmpeg.ffprobe(filePath, function(err, metadata) {
+ if(!err){
+ videoDuration = parseInt(metadata.format.duration);
+ videoResolution ={
+ height : metadata.streams[0].coded_height,
+ width : metadata.streams[0].coded_width
+ }
+ };
+
+ resolve({videoDuration, videoResolution});
+ });
+ })
+
+
+}
+
+
+module.exports = { processRawFileToMp4, processMp4ToHls, generateThumbnail, getVideoDurationAndResolution };
\ No newline at end of file