Video model refractoring -> use mongoose api
This commit is contained in:
parent
07d9320387
commit
aaf61f3810
|
@ -31,7 +31,6 @@ const logger = require('./server/helpers/logger')
|
||||||
const poolRequests = require('./server/lib/requestsScheduler')
|
const poolRequests = require('./server/lib/requestsScheduler')
|
||||||
const routes = require('./server/controllers')
|
const routes = require('./server/controllers')
|
||||||
const utils = require('./server/helpers/utils')
|
const utils = require('./server/helpers/utils')
|
||||||
const videos = require('./server/lib/videos')
|
|
||||||
const webtorrent = require('./server/lib/webtorrent')
|
const webtorrent = require('./server/lib/webtorrent')
|
||||||
|
|
||||||
// Get configurations
|
// Get configurations
|
||||||
|
@ -138,11 +137,11 @@ installer.installApplication(function (err) {
|
||||||
// Activate the pool requests
|
// Activate the pool requests
|
||||||
poolRequests.activate()
|
poolRequests.activate()
|
||||||
|
|
||||||
videos.seedAllExisting(function () {
|
// videos.seedAllExisting(function () {
|
||||||
logger.info('Seeded all the videos')
|
logger.info('Seeded all the videos')
|
||||||
logger.info('Server listening on port %d', port)
|
logger.info('Server listening on port %d', port)
|
||||||
app.emit('ready')
|
app.emit('ready')
|
||||||
})
|
// })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
const async = require('async')
|
const async = require('async')
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
|
||||||
const logger = require('../../../helpers/logger')
|
const logger = require('../../../helpers/logger')
|
||||||
const friends = require('../../../lib/friends')
|
const friends = require('../../../lib/friends')
|
||||||
|
@ -10,10 +11,9 @@ const Pods = require('../../../models/pods')
|
||||||
const oAuth2 = middlewares.oauth2
|
const oAuth2 = middlewares.oauth2
|
||||||
const reqValidator = middlewares.reqValidators.pods
|
const reqValidator = middlewares.reqValidators.pods
|
||||||
const signatureValidator = middlewares.reqValidators.remote.signature
|
const signatureValidator = middlewares.reqValidators.remote.signature
|
||||||
const videos = require('../../../lib/videos')
|
|
||||||
const Videos = require('../../../models/videos')
|
|
||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
const Video = mongoose.model('Video')
|
||||||
|
|
||||||
router.get('/', listPodsUrl)
|
router.get('/', listPodsUrl)
|
||||||
router.post('/', reqValidator.podsAdd, addPods)
|
router.post('/', reqValidator.podsAdd, addPods)
|
||||||
|
@ -86,7 +86,7 @@ function removePods (req, res, next) {
|
||||||
},
|
},
|
||||||
|
|
||||||
function (callback) {
|
function (callback) {
|
||||||
Videos.listFromUrl(url, function (err, videosList) {
|
Video.listByUrls([ url ], function (err, videosList) {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error('Cannot list videos from url.', { error: err })
|
logger.error('Cannot list videos from url.', { error: err })
|
||||||
return callback(err)
|
return callback(err)
|
||||||
|
@ -97,14 +97,9 @@ function removePods (req, res, next) {
|
||||||
},
|
},
|
||||||
|
|
||||||
function removeTheRemoteVideos (videosList, callback) {
|
function removeTheRemoteVideos (videosList, callback) {
|
||||||
videos.removeRemoteVideos(videosList, function (err) {
|
async.each(videosList, function (video, callbackEach) {
|
||||||
if (err) {
|
video.remove(callbackEach)
|
||||||
logger.error('Cannot remove remote videos.', { error: err })
|
}, callback)
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
], function (err) {
|
], function (err) {
|
||||||
if (err) return next(err)
|
if (err) return next(err)
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
|
|
||||||
const async = require('async')
|
const async = require('async')
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
|
||||||
const middlewares = require('../../../middlewares')
|
const middlewares = require('../../../middlewares')
|
||||||
const secureMiddleware = middlewares.secure
|
const secureMiddleware = middlewares.secure
|
||||||
const reqValidator = middlewares.reqValidators.remote
|
const reqValidator = middlewares.reqValidators.remote
|
||||||
const logger = require('../../../helpers/logger')
|
const logger = require('../../../helpers/logger')
|
||||||
const Videos = require('../../../models/videos')
|
|
||||||
const videos = require('../../../lib/videos')
|
|
||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
|
const Video = mongoose.model('Video')
|
||||||
|
|
||||||
router.post('/videos',
|
router.post('/videos',
|
||||||
reqValidator.signature,
|
reqValidator.signature,
|
||||||
|
@ -33,48 +33,39 @@ function remoteVideos (req, res, next) {
|
||||||
// We need to process in the same order to keep consistency
|
// We need to process in the same order to keep consistency
|
||||||
// TODO: optimization
|
// TODO: optimization
|
||||||
async.eachSeries(requests, function (request, callbackEach) {
|
async.eachSeries(requests, function (request, callbackEach) {
|
||||||
const video = request.data
|
const videoData = request.data
|
||||||
|
|
||||||
if (request.type === 'add') {
|
if (request.type === 'add') {
|
||||||
addRemoteVideo(video, callbackEach)
|
addRemoteVideo(videoData, callbackEach)
|
||||||
} else if (request.type === 'remove') {
|
} else if (request.type === 'remove') {
|
||||||
removeRemoteVideo(video, fromUrl, callbackEach)
|
removeRemoteVideo(videoData, fromUrl, callbackEach)
|
||||||
}
|
}
|
||||||
|
}, function (err) {
|
||||||
|
if (err) logger.error('Error managing remote videos.', { error: err })
|
||||||
})
|
})
|
||||||
|
|
||||||
// We don't need to keep the other pod waiting
|
// We don't need to keep the other pod waiting
|
||||||
return res.type('json').status(204).end()
|
return res.type('json').status(204).end()
|
||||||
}
|
}
|
||||||
|
|
||||||
function addRemoteVideo (videoToCreate, callback) {
|
function addRemoteVideo (videoToCreateData, callback) {
|
||||||
videos.createRemoteVideos([ videoToCreate ], function (err, remoteVideos) {
|
// Mongoose pre hook will automatically create the thumbnail on disk
|
||||||
if (err) {
|
videoToCreateData.thumbnail = videoToCreateData.thumbnailBase64
|
||||||
logger.error('Cannot create remote videos.', { error: err })
|
|
||||||
// Don't break the process
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback()
|
const video = new Video(videoToCreateData)
|
||||||
})
|
video.save(callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeRemoteVideo (videoToRemove, fromUrl, callback) {
|
function removeRemoteVideo (videoToRemoveData, fromUrl, callback) {
|
||||||
const magnetUris = [ videoToRemove.magnetUri ]
|
|
||||||
|
|
||||||
// We need the list because we have to remove some other stuffs (thumbnail etc)
|
// We need the list because we have to remove some other stuffs (thumbnail etc)
|
||||||
Videos.listFromUrlAndMagnets(fromUrl, magnetUris, function (err, videosList) {
|
Video.listByUrlAndMagnet(fromUrl, videoToRemoveData.magnetUri, function (err, videosList) {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error('Cannot list videos from url and magnets.', { error: err })
|
logger.error('Cannot list videos from url and magnets.', { error: err })
|
||||||
// Don't break the process
|
return callback(err)
|
||||||
return callback()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
videos.removeRemoteVideos(videosList, function (err) {
|
async.each(videosList, function (video, callbackEach) {
|
||||||
if (err) {
|
video.remove(callbackEach)
|
||||||
logger.error('Cannot remove remote videos.', { error: err })
|
}, callback)
|
||||||
// Don't break the process
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
const async = require('async')
|
const async = require('async')
|
||||||
const config = require('config')
|
const config = require('config')
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
const multer = require('multer')
|
const multer = require('multer')
|
||||||
|
|
||||||
const constants = require('../../../initializers/constants')
|
|
||||||
const logger = require('../../../helpers/logger')
|
const logger = require('../../../helpers/logger')
|
||||||
const friends = require('../../../lib/friends')
|
const friends = require('../../../lib/friends')
|
||||||
const middlewares = require('../../../middlewares')
|
const middlewares = require('../../../middlewares')
|
||||||
|
@ -18,12 +18,10 @@ const reqValidatorVideos = reqValidator.videos
|
||||||
const search = middlewares.search
|
const search = middlewares.search
|
||||||
const sort = middlewares.sort
|
const sort = middlewares.sort
|
||||||
const utils = require('../../../helpers/utils')
|
const utils = require('../../../helpers/utils')
|
||||||
const Videos = require('../../../models/videos') // model
|
|
||||||
const videos = require('../../../lib/videos')
|
|
||||||
const webtorrent = require('../../../lib/webtorrent')
|
|
||||||
|
|
||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
const uploads = config.get('storage.uploads')
|
const uploads = config.get('storage.uploads')
|
||||||
|
const Video = mongoose.model('Video')
|
||||||
|
|
||||||
// multer configuration
|
// multer configuration
|
||||||
const storage = multer.diskStorage({
|
const storage = multer.diskStorage({
|
||||||
|
@ -88,55 +86,27 @@ function addVideo (req, res, next) {
|
||||||
const videoInfos = req.body
|
const videoInfos = req.body
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function seedTheVideo (callback) {
|
|
||||||
videos.seed(videoFile.path, callback)
|
|
||||||
},
|
|
||||||
|
|
||||||
function createThumbnail (torrent, callback) {
|
function insertIntoDB (callback) {
|
||||||
videos.createVideoThumbnail(videoFile.path, function (err, thumbnailName) {
|
|
||||||
if (err) {
|
|
||||||
// TODO: unseed the video
|
|
||||||
logger.error('Cannot make a thumbnail of the video file.')
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, torrent, thumbnailName)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
function insertIntoDB (torrent, thumbnailName, callback) {
|
|
||||||
const videoData = {
|
const videoData = {
|
||||||
name: videoInfos.name,
|
name: videoInfos.name,
|
||||||
namePath: videoFile.filename,
|
namePath: videoFile.filename,
|
||||||
description: videoInfos.description,
|
description: videoInfos.description,
|
||||||
magnetUri: torrent.magnetURI,
|
|
||||||
author: res.locals.oauth.token.user.username,
|
author: res.locals.oauth.token.user.username,
|
||||||
duration: videoFile.duration,
|
duration: videoFile.duration,
|
||||||
thumbnail: thumbnailName,
|
|
||||||
tags: videoInfos.tags
|
tags: videoInfos.tags
|
||||||
}
|
}
|
||||||
|
|
||||||
Videos.add(videoData, function (err, insertedVideo) {
|
const video = new Video(videoData)
|
||||||
if (err) {
|
video.save(function (err, video) {
|
||||||
// TODO unseed the video
|
// Assert there are only one argument sent to the next function (video)
|
||||||
// TODO remove thumbnail
|
return callback(err, video)
|
||||||
logger.error('Cannot insert this video in the database.')
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, insertedVideo)
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
function sendToFriends (insertedVideo, callback) {
|
function sendToFriends (video, callback) {
|
||||||
videos.convertVideoToRemote(insertedVideo, function (err, remoteVideo) {
|
video.toRemoteJSON(function (err, remoteVideo) {
|
||||||
if (err) {
|
if (err) return callback(err)
|
||||||
// TODO unseed the video
|
|
||||||
// TODO remove thumbnail
|
|
||||||
// TODO delete from DB
|
|
||||||
logger.error('Cannot convert video to remote.')
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we'll add the video's meta data to our friends
|
// Now we'll add the video's meta data to our friends
|
||||||
friends.addVideoToFriends(remoteVideo)
|
friends.addVideoToFriends(remoteVideo)
|
||||||
|
@ -147,6 +117,9 @@ function addVideo (req, res, next) {
|
||||||
|
|
||||||
], function andFinally (err) {
|
], function andFinally (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
// TODO unseed the video
|
||||||
|
// TODO remove thumbnail
|
||||||
|
// TODO delete from DB
|
||||||
logger.error('Cannot insert the video.')
|
logger.error('Cannot insert the video.')
|
||||||
return next(err)
|
return next(err)
|
||||||
}
|
}
|
||||||
|
@ -157,23 +130,22 @@ function addVideo (req, res, next) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideo (req, res, next) {
|
function getVideo (req, res, next) {
|
||||||
Videos.get(req.params.id, function (err, videoObj) {
|
Video.load(req.params.id, function (err, video) {
|
||||||
if (err) return next(err)
|
if (err) return next(err)
|
||||||
|
|
||||||
const state = videos.getVideoState(videoObj)
|
if (!video) {
|
||||||
if (state.exist === false) {
|
|
||||||
return res.type('json').status(204).end()
|
return res.type('json').status(204).end()
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(getFormatedVideo(videoObj))
|
res.json(video.toFormatedJSON())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function listVideos (req, res, next) {
|
function listVideos (req, res, next) {
|
||||||
Videos.list(req.query.start, req.query.count, req.query.sort, function (err, videosList, totalVideos) {
|
Video.list(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) {
|
||||||
if (err) return next(err)
|
if (err) return next(err)
|
||||||
|
|
||||||
res.json(getFormatedVideos(videosList, totalVideos))
|
res.json(getFormatedVideos(videosList, videosTotal))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,31 +154,17 @@ function removeVideo (req, res, next) {
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function getVideo (callback) {
|
function getVideo (callback) {
|
||||||
Videos.get(videoId, callback)
|
Video.load(videoId, callback)
|
||||||
},
|
|
||||||
|
|
||||||
function removeVideoTorrent (video, callback) {
|
|
||||||
removeTorrent(video.magnetUri, function () {
|
|
||||||
return callback(null, video)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
function removeFromDB (video, callback) {
|
function removeFromDB (video, callback) {
|
||||||
Videos.removeOwned(req.params.id, function (err) {
|
video.remove(function (err) {
|
||||||
if (err) return callback(err)
|
if (err) return callback(err)
|
||||||
|
|
||||||
return callback(null, video)
|
return callback(null, video)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
function removeVideoData (video, callback) {
|
|
||||||
videos.removeVideosDataFromDisk([ video ], function (err) {
|
|
||||||
if (err) logger.error('Cannot remove video data from disk.', { video: video })
|
|
||||||
|
|
||||||
return callback(null, video)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
function sendInformationToFriends (video, callback) {
|
function sendInformationToFriends (video, callback) {
|
||||||
const params = {
|
const params = {
|
||||||
name: video.name,
|
name: video.name,
|
||||||
|
@ -228,53 +186,25 @@ function removeVideo (req, res, next) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function searchVideos (req, res, next) {
|
function searchVideos (req, res, next) {
|
||||||
Videos.search(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort,
|
Video.search(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort,
|
||||||
function (err, videosList, totalVideos) {
|
function (err, videosList, videosTotal) {
|
||||||
if (err) return next(err)
|
if (err) return next(err)
|
||||||
|
|
||||||
res.json(getFormatedVideos(videosList, totalVideos))
|
res.json(getFormatedVideos(videosList, videosTotal))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function getFormatedVideo (videoObj) {
|
function getFormatedVideos (videos, videosTotal) {
|
||||||
const formatedVideo = {
|
|
||||||
id: videoObj._id,
|
|
||||||
name: videoObj.name,
|
|
||||||
description: videoObj.description,
|
|
||||||
podUrl: videoObj.podUrl.replace(/^https?:\/\//, ''),
|
|
||||||
isLocal: videos.getVideoState(videoObj).owned,
|
|
||||||
magnetUri: videoObj.magnetUri,
|
|
||||||
author: videoObj.author,
|
|
||||||
duration: videoObj.duration,
|
|
||||||
tags: videoObj.tags,
|
|
||||||
thumbnailPath: constants.THUMBNAILS_STATIC_PATH + '/' + videoObj.thumbnail,
|
|
||||||
createdDate: videoObj.createdDate
|
|
||||||
}
|
|
||||||
|
|
||||||
return formatedVideo
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFormatedVideos (videosObj, totalVideos) {
|
|
||||||
const formatedVideos = []
|
const formatedVideos = []
|
||||||
|
|
||||||
videosObj.forEach(function (videoObj) {
|
videos.forEach(function (video) {
|
||||||
formatedVideos.push(getFormatedVideo(videoObj))
|
formatedVideos.push(video.toFormatedJSON())
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
total: totalVideos,
|
total: videosTotal,
|
||||||
data: formatedVideos
|
data: formatedVideos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maybe the torrent is not seeded, but we catch the error to don't stop the removing process
|
|
||||||
function removeTorrent (magnetUri, callback) {
|
|
||||||
try {
|
|
||||||
webtorrent.remove(magnetUri, callback)
|
|
||||||
} catch (err) {
|
|
||||||
logger.warn('Cannot remove the torrent from WebTorrent', { err: err })
|
|
||||||
return callback(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,7 +17,8 @@ const customValidators = {
|
||||||
isVideoNameValid: isVideoNameValid,
|
isVideoNameValid: isVideoNameValid,
|
||||||
isVideoPodUrlValid: isVideoPodUrlValid,
|
isVideoPodUrlValid: isVideoPodUrlValid,
|
||||||
isVideoTagsValid: isVideoTagsValid,
|
isVideoTagsValid: isVideoTagsValid,
|
||||||
isVideoThumbnailValid: isVideoThumbnailValid
|
isVideoThumbnailValid: isVideoThumbnailValid,
|
||||||
|
isVideoThumbnail64Valid: isVideoThumbnail64Valid
|
||||||
}
|
}
|
||||||
|
|
||||||
function exists (value) {
|
function exists (value) {
|
||||||
|
@ -37,7 +38,7 @@ function isEachRemoteVideosValid (requests) {
|
||||||
isVideoNameValid(video.name) &&
|
isVideoNameValid(video.name) &&
|
||||||
isVideoPodUrlValid(video.podUrl) &&
|
isVideoPodUrlValid(video.podUrl) &&
|
||||||
isVideoTagsValid(video.tags) &&
|
isVideoTagsValid(video.tags) &&
|
||||||
isVideoThumbnailValid(video.thumbnailBase64)
|
isVideoThumbnail64Valid(video.thumbnailBase64)
|
||||||
) ||
|
) ||
|
||||||
(
|
(
|
||||||
isRequestTypeRemoveValid(request.type) &&
|
isRequestTypeRemoveValid(request.type) &&
|
||||||
|
@ -97,8 +98,12 @@ function isVideoTagsValid (tags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isVideoThumbnailValid (value) {
|
function isVideoThumbnailValid (value) {
|
||||||
|
return validator.isLength(value, VIDEOS_CONSTRAINTS_FIELDS.THUMBNAIL)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVideoThumbnail64Valid (value) {
|
||||||
return validator.isBase64(value) &&
|
return validator.isBase64(value) &&
|
||||||
validator.isByteLength(value, VIDEOS_CONSTRAINTS_FIELDS.THUMBNAIL)
|
validator.isByteLength(value, VIDEOS_CONSTRAINTS_FIELDS.THUMBNAIL64)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -48,7 +48,8 @@ const VIDEOS_CONSTRAINTS_FIELDS = {
|
||||||
AUTHOR: { min: 3, max: 20 }, // Length
|
AUTHOR: { min: 3, max: 20 }, // Length
|
||||||
TAGS: { min: 1, max: 3 }, // Number of total tags
|
TAGS: { min: 1, max: 3 }, // Number of total tags
|
||||||
TAG: { min: 2, max: 10 }, // Length
|
TAG: { min: 2, max: 10 }, // Length
|
||||||
THUMBNAIL: { min: 0, max: 20000 } // Bytes
|
THUMBNAIL: { min: 2, max: 30 },
|
||||||
|
THUMBNAIL64: { min: 0, max: 20000 } // Bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special constants for a test instance
|
// Special constants for a test instance
|
||||||
|
|
|
@ -5,6 +5,9 @@ const mongoose = require('mongoose')
|
||||||
|
|
||||||
const logger = require('../helpers/logger')
|
const logger = require('../helpers/logger')
|
||||||
|
|
||||||
|
// Bootstrap models
|
||||||
|
require('../models/video')
|
||||||
|
|
||||||
const dbname = 'peertube' + config.get('database.suffix')
|
const dbname = 'peertube' + config.get('database.suffix')
|
||||||
const host = config.get('database.host')
|
const host = config.get('database.host')
|
||||||
const port = config.get('database.port')
|
const port = config.get('database.port')
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
const async = require('async')
|
const async = require('async')
|
||||||
const config = require('config')
|
const config = require('config')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
const request = require('request')
|
const request = require('request')
|
||||||
|
|
||||||
const constants = require('../initializers/constants')
|
const constants = require('../initializers/constants')
|
||||||
|
@ -11,12 +12,11 @@ const peertubeCrypto = require('../helpers/peertubeCrypto')
|
||||||
const Pods = require('../models/pods')
|
const Pods = require('../models/pods')
|
||||||
const requestsScheduler = require('../lib/requestsScheduler')
|
const requestsScheduler = require('../lib/requestsScheduler')
|
||||||
const requests = require('../helpers/requests')
|
const requests = require('../helpers/requests')
|
||||||
const videos = require('../lib/videos')
|
|
||||||
const Videos = require('../models/videos')
|
|
||||||
|
|
||||||
const http = config.get('webserver.https') ? 'https' : 'http'
|
const http = config.get('webserver.https') ? 'https' : 'http'
|
||||||
const host = config.get('webserver.host')
|
const host = config.get('webserver.host')
|
||||||
const port = config.get('webserver.port')
|
const port = config.get('webserver.port')
|
||||||
|
const Video = mongoose.model('Video')
|
||||||
|
|
||||||
const pods = {
|
const pods = {
|
||||||
addVideoToFriends: addVideoToFriends,
|
addVideoToFriends: addVideoToFriends,
|
||||||
|
@ -117,18 +117,13 @@ function quitFriends (callback) {
|
||||||
function listRemoteVideos (callbackAsync) {
|
function listRemoteVideos (callbackAsync) {
|
||||||
logger.info('Broke friends, so sad :(')
|
logger.info('Broke friends, so sad :(')
|
||||||
|
|
||||||
Videos.listFromRemotes(callbackAsync)
|
Video.listRemotes(callbackAsync)
|
||||||
},
|
},
|
||||||
|
|
||||||
function removeTheRemoteVideos (videosList, callbackAsync) {
|
function removeTheRemoteVideos (videosList, callbackAsync) {
|
||||||
videos.removeRemoteVideos(videosList, function (err) {
|
async.each(videosList, function (video, callbackEach) {
|
||||||
if (err) {
|
video.remove(callbackEach)
|
||||||
logger.error('Cannot remove remote videos.', { error: err })
|
}, callbackAsync)
|
||||||
return callbackAsync(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return callbackAsync(null)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
], function (err) {
|
], function (err) {
|
||||||
// Don't forget to re activate the scheduler, even if there was an error
|
// Don't forget to re activate the scheduler, even if there was an error
|
||||||
|
@ -146,14 +141,14 @@ function removeVideoToFriends (video) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendOwnedVideosToPod (podId) {
|
function sendOwnedVideosToPod (podId) {
|
||||||
Videos.listOwned(function (err, videosList) {
|
Video.listOwned(function (err, videosList) {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error('Cannot get the list of videos we own.')
|
logger.error('Cannot get the list of videos we own.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
videosList.forEach(function (video) {
|
videosList.forEach(function (video) {
|
||||||
videos.convertVideoToRemote(video, function (err, remoteVideo) {
|
video.toRemoteJSON(function (err, remoteVideo) {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error('Cannot convert video to remote.', { error: err })
|
logger.error('Cannot convert video to remote.', { error: err })
|
||||||
// Don't break the process
|
// Don't break the process
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
|
|
||||||
const async = require('async')
|
const async = require('async')
|
||||||
const map = require('lodash/map')
|
const map = require('lodash/map')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
|
||||||
const constants = require('../initializers/constants')
|
const constants = require('../initializers/constants')
|
||||||
const logger = require('../helpers/logger')
|
const logger = require('../helpers/logger')
|
||||||
const Pods = require('../models/pods')
|
const Pods = require('../models/pods')
|
||||||
const Requests = require('../models/requests')
|
const Requests = require('../models/requests')
|
||||||
const requests = require('../helpers/requests')
|
const requests = require('../helpers/requests')
|
||||||
const videos = require('../lib/videos')
|
|
||||||
const Videos = require('../models/videos')
|
const Video = mongoose.model('Video')
|
||||||
|
|
||||||
let timer = null
|
let timer = null
|
||||||
|
|
||||||
|
@ -210,7 +211,7 @@ function removeBadPods () {
|
||||||
const urls = map(pods, 'url')
|
const urls = map(pods, 'url')
|
||||||
const ids = map(pods, '_id')
|
const ids = map(pods, '_id')
|
||||||
|
|
||||||
Videos.listFromUrls(urls, function (err, videosList) {
|
Video.listByUrls(urls, function (err, videosList) {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error('Cannot list videos urls.', { error: err, urls: urls })
|
logger.error('Cannot list videos urls.', { error: err, urls: urls })
|
||||||
return callback(null, ids, [])
|
return callback(null, ids, [])
|
||||||
|
@ -224,9 +225,14 @@ function removeBadPods () {
|
||||||
// We don't have to remove pods, skip
|
// We don't have to remove pods, skip
|
||||||
if (typeof podIds === 'function') return podIds(null)
|
if (typeof podIds === 'function') return podIds(null)
|
||||||
|
|
||||||
// Remove the remote videos
|
async.each(videosList, function (video, callbackEach) {
|
||||||
videos.removeRemoteVideos(videosList, function (err) {
|
video.remove(callbackEach)
|
||||||
if (err) logger.error('Cannot remove remote videos.', { error: err })
|
}, function (err) {
|
||||||
|
if (err) {
|
||||||
|
// Don't stop the process
|
||||||
|
logger.error('Error while removing videos of bad pods.', { error: err })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
return callback(null, podIds)
|
return callback(null, podIds)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,199 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const async = require('async')
|
|
||||||
const config = require('config')
|
|
||||||
const ffmpeg = require('fluent-ffmpeg')
|
|
||||||
const fs = require('fs')
|
|
||||||
const map = require('lodash/map')
|
|
||||||
const pathUtils = require('path')
|
|
||||||
|
|
||||||
const constants = require('../initializers/constants')
|
|
||||||
const logger = require('../helpers/logger')
|
|
||||||
const utils = require('../helpers/utils')
|
|
||||||
const Videos = require('../models/videos')
|
|
||||||
const webtorrent = require('../lib/webtorrent')
|
|
||||||
|
|
||||||
const uploadDir = pathUtils.join(__dirname, '..', '..', config.get('storage.uploads'))
|
|
||||||
const thumbnailsDir = pathUtils.join(__dirname, '..', '..', config.get('storage.thumbnails'))
|
|
||||||
|
|
||||||
const videos = {
|
|
||||||
convertVideoToRemote: convertVideoToRemote,
|
|
||||||
createRemoteVideos: createRemoteVideos,
|
|
||||||
getVideoDuration: getVideoDuration,
|
|
||||||
getVideoState: getVideoState,
|
|
||||||
createVideoThumbnail: createVideoThumbnail,
|
|
||||||
removeVideosDataFromDisk: removeVideosDataFromDisk,
|
|
||||||
removeRemoteVideos: removeRemoteVideos,
|
|
||||||
seed: seed,
|
|
||||||
seedAllExisting: seedAllExisting
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertVideoToRemote (video, callback) {
|
|
||||||
fs.readFile(thumbnailsDir + video.thumbnail, function (err, thumbnailData) {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Cannot read the thumbnail of the video')
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
const remoteVideo = {
|
|
||||||
name: video.name,
|
|
||||||
description: video.description,
|
|
||||||
magnetUri: video.magnetUri,
|
|
||||||
author: video.author,
|
|
||||||
duration: video.duration,
|
|
||||||
thumbnailBase64: new Buffer(thumbnailData).toString('base64'),
|
|
||||||
tags: video.tags,
|
|
||||||
createdDate: video.createdDate,
|
|
||||||
podUrl: video.podUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, remoteVideo)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function createRemoteVideos (videos, callback) {
|
|
||||||
// Create the remote videos from the new pod
|
|
||||||
createRemoteVideoObjects(videos, function (err, remoteVideos) {
|
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
Videos.addRemotes(remoteVideos, callback)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getVideoDuration (videoPath, callback) {
|
|
||||||
ffmpeg.ffprobe(videoPath, function (err, metadata) {
|
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
return callback(null, Math.floor(metadata.format.duration))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function getVideoState (video) {
|
|
||||||
const exist = (video !== null)
|
|
||||||
let owned = false
|
|
||||||
if (exist === true) {
|
|
||||||
owned = (video.namePath !== null)
|
|
||||||
}
|
|
||||||
|
|
||||||
return { exist: exist, owned: owned }
|
|
||||||
}
|
|
||||||
|
|
||||||
function createVideoThumbnail (videoPath, callback) {
|
|
||||||
const filename = pathUtils.basename(videoPath) + '.jpg'
|
|
||||||
ffmpeg(videoPath)
|
|
||||||
.on('error', callback)
|
|
||||||
.on('end', function () {
|
|
||||||
callback(null, filename)
|
|
||||||
})
|
|
||||||
.thumbnail({
|
|
||||||
count: 1,
|
|
||||||
folder: thumbnailsDir,
|
|
||||||
size: constants.THUMBNAILS_SIZE,
|
|
||||||
filename: filename
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove video datas from disk (video file, thumbnail...)
|
|
||||||
function removeVideosDataFromDisk (videos, callback) {
|
|
||||||
async.each(videos, function (video, callbackEach) {
|
|
||||||
fs.unlink(thumbnailsDir + video.thumbnail, function (err) {
|
|
||||||
if (err) logger.error('Cannot remove the video thumbnail')
|
|
||||||
|
|
||||||
if (getVideoState(video).owned === true) {
|
|
||||||
fs.unlink(uploadDir + video.namePath, function (err) {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Cannot remove this video file.')
|
|
||||||
return callbackEach(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
callbackEach(null)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
callbackEach(null)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeRemoteVideos (videos, callback) {
|
|
||||||
Videos.removeByIds(map(videos, '_id'), function (err) {
|
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
removeVideosDataFromDisk(videos, callback)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function seed (path, callback) {
|
|
||||||
logger.info('Seeding %s...', path)
|
|
||||||
|
|
||||||
webtorrent.seed(path, function (torrent) {
|
|
||||||
logger.info('%s seeded (%s).', path, torrent.magnetURI)
|
|
||||||
|
|
||||||
return callback(null, torrent)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function seedAllExisting (callback) {
|
|
||||||
Videos.listOwned(function (err, videosList) {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Cannot get list of the videos to seed.')
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
async.each(videosList, function (video, callbackEach) {
|
|
||||||
seed(uploadDir + video.namePath, function (err) {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Cannot seed this video.')
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
callbackEach(null)
|
|
||||||
})
|
|
||||||
}, callback)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
module.exports = videos
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function createRemoteVideoObjects (videos, callback) {
|
|
||||||
const remoteVideos = []
|
|
||||||
|
|
||||||
async.each(videos, function (video, callbackEach) {
|
|
||||||
// Creating the thumbnail for this remote video
|
|
||||||
utils.generateRandomString(16, function (err, randomString) {
|
|
||||||
if (err) return callbackEach(err)
|
|
||||||
|
|
||||||
const thumbnailName = randomString + '.jpg'
|
|
||||||
createThumbnailFromBase64(thumbnailName, video.thumbnailBase64, function (err) {
|
|
||||||
if (err) return callbackEach(err)
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
name: video.name,
|
|
||||||
description: video.description,
|
|
||||||
magnetUri: video.magnetUri,
|
|
||||||
podUrl: video.podUrl,
|
|
||||||
duration: video.duration,
|
|
||||||
thumbnail: thumbnailName,
|
|
||||||
tags: video.tags,
|
|
||||||
author: video.author
|
|
||||||
}
|
|
||||||
remoteVideos.push(params)
|
|
||||||
|
|
||||||
callbackEach(null)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
callback(null, remoteVideos)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function createThumbnailFromBase64 (thumbnailName, data, callback) {
|
|
||||||
fs.writeFile(thumbnailsDir + thumbnailName, data, { encoding: 'base64' }, callback)
|
|
||||||
}
|
|
|
@ -22,7 +22,7 @@ function remoteVideos (req, res, next) {
|
||||||
req.checkBody('data').isArray()
|
req.checkBody('data').isArray()
|
||||||
req.checkBody('data').isEachRemoteVideosValid()
|
req.checkBody('data').isEachRemoteVideosValid()
|
||||||
|
|
||||||
logger.debug('Checking remoteVideosAdd parameters', { parameters: req.body })
|
logger.debug('Checking remoteVideos parameters', { parameters: req.body })
|
||||||
|
|
||||||
checkErrors(req, res, next)
|
checkErrors(req, res, next)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
|
||||||
const checkErrors = require('./utils').checkErrors
|
const checkErrors = require('./utils').checkErrors
|
||||||
const constants = require('../../initializers/constants')
|
const constants = require('../../initializers/constants')
|
||||||
const customValidators = require('../../helpers/customValidators')
|
const customValidators = require('../../helpers/customValidators')
|
||||||
const logger = require('../../helpers/logger')
|
const logger = require('../../helpers/logger')
|
||||||
const videos = require('../../lib/videos')
|
|
||||||
const Videos = require('../../models/videos')
|
const Video = mongoose.model('Video')
|
||||||
|
|
||||||
const reqValidatorsVideos = {
|
const reqValidatorsVideos = {
|
||||||
videosAdd: videosAdd,
|
videosAdd: videosAdd,
|
||||||
|
@ -26,7 +28,7 @@ function videosAdd (req, res, next) {
|
||||||
checkErrors(req, res, function () {
|
checkErrors(req, res, function () {
|
||||||
const videoFile = req.files.videofile[0]
|
const videoFile = req.files.videofile[0]
|
||||||
|
|
||||||
videos.getVideoDuration(videoFile.path, function (err, duration) {
|
Video.getDurationFromFile(videoFile.path, function (err, duration) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.status(400).send('Cannot retrieve metadata of the file.')
|
return res.status(400).send('Cannot retrieve metadata of the file.')
|
||||||
}
|
}
|
||||||
|
@ -47,14 +49,13 @@ function videosGet (req, res, next) {
|
||||||
logger.debug('Checking videosGet parameters', { parameters: req.params })
|
logger.debug('Checking videosGet parameters', { parameters: req.params })
|
||||||
|
|
||||||
checkErrors(req, res, function () {
|
checkErrors(req, res, function () {
|
||||||
Videos.get(req.params.id, function (err, video) {
|
Video.load(req.params.id, function (err, video) {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error('Error in videosGet request validator.', { error: err })
|
logger.error('Error in videosGet request validator.', { error: err })
|
||||||
return res.sendStatus(500)
|
return res.sendStatus(500)
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = videos.getVideoState(video)
|
if (!video) return res.status(404).send('Video not found')
|
||||||
if (state.exist === false) return res.status(404).send('Video not found')
|
|
||||||
|
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
@ -67,15 +68,14 @@ function videosRemove (req, res, next) {
|
||||||
logger.debug('Checking videosRemove parameters', { parameters: req.params })
|
logger.debug('Checking videosRemove parameters', { parameters: req.params })
|
||||||
|
|
||||||
checkErrors(req, res, function () {
|
checkErrors(req, res, function () {
|
||||||
Videos.get(req.params.id, function (err, video) {
|
Video.load(req.params.id, function (err, video) {
|
||||||
if (err) {
|
if (err) {
|
||||||
logger.error('Error in videosRemove request validator.', { error: err })
|
logger.error('Error in videosRemove request validator.', { error: err })
|
||||||
return res.sendStatus(500)
|
return res.sendStatus(500)
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = videos.getVideoState(video)
|
if (!video) return res.status(404).send('Video not found')
|
||||||
if (state.exist === false) return res.status(404).send('Video not found')
|
else if (video.isOwned() === false) return res.status(403).send('Cannot remove video of another pod')
|
||||||
else if (state.owned === false) return res.status(403).send('Cannot remove video of another pod')
|
|
||||||
|
|
||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,314 @@
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
const async = require('async')
|
||||||
|
const config = require('config')
|
||||||
|
const ffmpeg = require('fluent-ffmpeg')
|
||||||
|
const fs = require('fs')
|
||||||
|
const pathUtils = require('path')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
|
||||||
|
const constants = require('../initializers/constants')
|
||||||
|
const customValidators = require('../helpers/customValidators')
|
||||||
|
const logger = require('../helpers/logger')
|
||||||
|
const utils = require('../helpers/utils')
|
||||||
|
const webtorrent = require('../lib/webtorrent')
|
||||||
|
|
||||||
|
const http = config.get('webserver.https') === true ? 'https' : 'http'
|
||||||
|
const host = config.get('webserver.host')
|
||||||
|
const port = config.get('webserver.port')
|
||||||
|
const uploadsDir = pathUtils.join(__dirname, '..', '..', config.get('storage.uploads'))
|
||||||
|
const thumbnailsDir = pathUtils.join(__dirname, '..', '..', config.get('storage.thumbnails'))
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// TODO: add indexes on searchable columns
|
||||||
|
const VideoSchema = mongoose.Schema({
|
||||||
|
name: String,
|
||||||
|
namePath: String,
|
||||||
|
description: String,
|
||||||
|
magnetUri: String,
|
||||||
|
podUrl: String,
|
||||||
|
author: String,
|
||||||
|
duration: Number,
|
||||||
|
thumbnail: String,
|
||||||
|
tags: [ String ],
|
||||||
|
createdDate: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
VideoSchema.path('name').validate(customValidators.isVideoNameValid)
|
||||||
|
VideoSchema.path('description').validate(customValidators.isVideoDescriptionValid)
|
||||||
|
VideoSchema.path('magnetUri').validate(customValidators.isVideoMagnetUriValid)
|
||||||
|
VideoSchema.path('podUrl').validate(customValidators.isVideoPodUrlValid)
|
||||||
|
VideoSchema.path('author').validate(customValidators.isVideoAuthorValid)
|
||||||
|
VideoSchema.path('duration').validate(customValidators.isVideoDurationValid)
|
||||||
|
// The tumbnail can be the path or the data in base 64
|
||||||
|
// The pre save hook will convert the base 64 data in a file on disk and replace the thumbnail key by the filename
|
||||||
|
VideoSchema.path('thumbnail').validate(function (value) {
|
||||||
|
return customValidators.isVideoThumbnailValid(value) || customValidators.isVideoThumbnail64Valid(value)
|
||||||
|
})
|
||||||
|
VideoSchema.path('tags').validate(customValidators.isVideoTagsValid)
|
||||||
|
|
||||||
|
VideoSchema.methods = {
|
||||||
|
isOwned: isOwned,
|
||||||
|
toFormatedJSON: toFormatedJSON,
|
||||||
|
toRemoteJSON: toRemoteJSON
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSchema.statics = {
|
||||||
|
getDurationFromFile: getDurationFromFile,
|
||||||
|
list: list,
|
||||||
|
listByUrlAndMagnet: listByUrlAndMagnet,
|
||||||
|
listByUrls: listByUrls,
|
||||||
|
listOwned: listOwned,
|
||||||
|
listRemotes: listRemotes,
|
||||||
|
load: load,
|
||||||
|
search: search,
|
||||||
|
seedAllExisting: seedAllExisting
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoSchema.pre('remove', function (next) {
|
||||||
|
const video = this
|
||||||
|
const tasks = []
|
||||||
|
|
||||||
|
tasks.push(
|
||||||
|
function (callback) {
|
||||||
|
removeThumbnail(video, callback)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (video.isOwned()) {
|
||||||
|
tasks.push(
|
||||||
|
function (callback) {
|
||||||
|
removeFile(video, callback)
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
removeTorrent(video, callback)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async.parallel(tasks, next)
|
||||||
|
})
|
||||||
|
|
||||||
|
VideoSchema.pre('save', function (next) {
|
||||||
|
const video = this
|
||||||
|
const tasks = []
|
||||||
|
|
||||||
|
if (video.isOwned()) {
|
||||||
|
const videoPath = pathUtils.join(uploadsDir, video.namePath)
|
||||||
|
this.podUrl = http + '://' + host + ':' + port
|
||||||
|
|
||||||
|
tasks.push(
|
||||||
|
function (callback) {
|
||||||
|
seed(videoPath, callback)
|
||||||
|
},
|
||||||
|
function (callback) {
|
||||||
|
createThumbnail(videoPath, callback)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
async.parallel(tasks, function (err, results) {
|
||||||
|
if (err) return next(err)
|
||||||
|
|
||||||
|
video.magnetUri = results[0].magnetURI
|
||||||
|
video.thumbnail = results[1]
|
||||||
|
|
||||||
|
return next()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
generateThumbnailFromBase64(video.thumbnail, function (err, thumbnailName) {
|
||||||
|
if (err) return next(err)
|
||||||
|
|
||||||
|
video.thumbnail = thumbnailName
|
||||||
|
|
||||||
|
return next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mongoose.model('Video', VideoSchema)
|
||||||
|
|
||||||
|
// ------------------------------ METHODS ------------------------------
|
||||||
|
|
||||||
|
function isOwned () {
|
||||||
|
return this.namePath !== null
|
||||||
|
}
|
||||||
|
|
||||||
|
function toFormatedJSON () {
|
||||||
|
const json = {
|
||||||
|
id: this._id,
|
||||||
|
name: this.name,
|
||||||
|
description: this.description,
|
||||||
|
podUrl: this.podUrl.replace(/^https?:\/\//, ''),
|
||||||
|
isLocal: this.isOwned(),
|
||||||
|
magnetUri: this.magnetUri,
|
||||||
|
author: this.author,
|
||||||
|
duration: this.duration,
|
||||||
|
tags: this.tags,
|
||||||
|
thumbnailPath: constants.THUMBNAILS_STATIC_PATH + '/' + this.thumbnail,
|
||||||
|
createdDate: this.createdDate
|
||||||
|
}
|
||||||
|
|
||||||
|
return json
|
||||||
|
}
|
||||||
|
|
||||||
|
function toRemoteJSON (callback) {
|
||||||
|
const self = this
|
||||||
|
|
||||||
|
// Convert thumbnail to base64
|
||||||
|
fs.readFile(pathUtils.join(thumbnailsDir, this.thumbnail), function (err, thumbnailData) {
|
||||||
|
if (err) {
|
||||||
|
logger.error('Cannot read the thumbnail of the video')
|
||||||
|
return callback(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteVideo = {
|
||||||
|
name: self.name,
|
||||||
|
description: self.description,
|
||||||
|
magnetUri: self.magnetUri,
|
||||||
|
namePath: null,
|
||||||
|
author: self.author,
|
||||||
|
duration: self.duration,
|
||||||
|
thumbnailBase64: new Buffer(thumbnailData).toString('base64'),
|
||||||
|
tags: self.tags,
|
||||||
|
createdDate: self.createdDate,
|
||||||
|
podUrl: self.podUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback(null, remoteVideo)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------ STATICS ------------------------------
|
||||||
|
|
||||||
|
function getDurationFromFile (videoPath, callback) {
|
||||||
|
ffmpeg.ffprobe(videoPath, function (err, metadata) {
|
||||||
|
if (err) return callback(err)
|
||||||
|
|
||||||
|
return callback(null, Math.floor(metadata.format.duration))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function list (start, count, sort, callback) {
|
||||||
|
const query = {}
|
||||||
|
return findWithCount.call(this, query, start, count, sort, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function listByUrlAndMagnet (fromUrl, magnetUri, callback) {
|
||||||
|
this.find({ podUrl: fromUrl, magnetUri: magnetUri }, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function listByUrls (fromUrls, callback) {
|
||||||
|
this.find({ podUrl: { $in: fromUrls } }, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function listOwned (callback) {
|
||||||
|
// If namePath is not null this is *our* video
|
||||||
|
this.find({ namePath: { $ne: null } }, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function listRemotes (callback) {
|
||||||
|
this.find({ namePath: null }, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function load (id, callback) {
|
||||||
|
this.findById(id, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function search (value, field, start, count, sort, callback) {
|
||||||
|
const query = {}
|
||||||
|
// Make an exact search with the magnet
|
||||||
|
if (field === 'magnetUri' || field === 'tags') {
|
||||||
|
query[field] = value
|
||||||
|
} else {
|
||||||
|
query[field] = new RegExp(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
findWithCount.call(this, query, start, count, sort, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
function seedAllExisting () {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function findWithCount (query, start, count, sort, callback) {
|
||||||
|
const self = this
|
||||||
|
|
||||||
|
async.parallel([
|
||||||
|
function (asyncCallback) {
|
||||||
|
self.find(query).skip(start).limit(start + count).sort(sort).exec(asyncCallback)
|
||||||
|
},
|
||||||
|
function (asyncCallback) {
|
||||||
|
self.count(query, asyncCallback)
|
||||||
|
}
|
||||||
|
], function (err, results) {
|
||||||
|
if (err) return callback(err)
|
||||||
|
|
||||||
|
const videos = results[0]
|
||||||
|
const totalVideos = results[1]
|
||||||
|
return callback(null, videos, totalVideos)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeThumbnail (video, callback) {
|
||||||
|
fs.unlink(thumbnailsDir + video.thumbnail, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeFile (video, callback) {
|
||||||
|
fs.unlink(uploadsDir + video.namePath, callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maybe the torrent is not seeded, but we catch the error to don't stop the removing process
|
||||||
|
function removeTorrent (video, callback) {
|
||||||
|
try {
|
||||||
|
webtorrent.remove(video.magnetUri, callback)
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn('Cannot remove the torrent from WebTorrent', { err: err })
|
||||||
|
return callback(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createThumbnail (videoPath, callback) {
|
||||||
|
const filename = pathUtils.basename(videoPath) + '.jpg'
|
||||||
|
ffmpeg(videoPath)
|
||||||
|
.on('error', callback)
|
||||||
|
.on('end', function () {
|
||||||
|
callback(null, filename)
|
||||||
|
})
|
||||||
|
.thumbnail({
|
||||||
|
count: 1,
|
||||||
|
folder: thumbnailsDir,
|
||||||
|
size: constants.THUMBNAILS_SIZE,
|
||||||
|
filename: filename
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function seed (path, callback) {
|
||||||
|
logger.info('Seeding %s...', path)
|
||||||
|
|
||||||
|
webtorrent.seed(path, function (torrent) {
|
||||||
|
logger.info('%s seeded (%s).', path, torrent.magnetURI)
|
||||||
|
|
||||||
|
return callback(null, torrent)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateThumbnailFromBase64 (data, callback) {
|
||||||
|
// Creating the thumbnail for this remote video
|
||||||
|
utils.generateRandomString(16, function (err, randomString) {
|
||||||
|
if (err) return callback(err)
|
||||||
|
|
||||||
|
const thumbnailName = randomString + '.jpg'
|
||||||
|
fs.writeFile(thumbnailsDir + thumbnailName, data, { encoding: 'base64' }, function (err) {
|
||||||
|
if (err) return callback(err)
|
||||||
|
|
||||||
|
return callback(null, thumbnailName)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,162 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const async = require('async')
|
|
||||||
const config = require('config')
|
|
||||||
const mongoose = require('mongoose')
|
|
||||||
|
|
||||||
const logger = require('../helpers/logger')
|
|
||||||
|
|
||||||
const http = config.get('webserver.https') === true ? 'https' : 'http'
|
|
||||||
const host = config.get('webserver.host')
|
|
||||||
const port = config.get('webserver.port')
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// TODO: add indexes on searchable columns
|
|
||||||
const videosSchema = mongoose.Schema({
|
|
||||||
name: String,
|
|
||||||
namePath: String,
|
|
||||||
description: String,
|
|
||||||
magnetUri: String,
|
|
||||||
podUrl: String,
|
|
||||||
author: String,
|
|
||||||
duration: Number,
|
|
||||||
thumbnail: String,
|
|
||||||
tags: [ String ],
|
|
||||||
createdDate: {
|
|
||||||
type: Date,
|
|
||||||
default: Date.now
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const VideosDB = mongoose.model('videos', videosSchema)
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
const Videos = {
|
|
||||||
add: add,
|
|
||||||
addRemotes: addRemotes,
|
|
||||||
get: get,
|
|
||||||
list: list,
|
|
||||||
listFromUrl: listFromUrl,
|
|
||||||
listFromUrls: listFromUrls,
|
|
||||||
listFromUrlAndMagnets: listFromUrlAndMagnets,
|
|
||||||
listFromRemotes: listFromRemotes,
|
|
||||||
listOwned: listOwned,
|
|
||||||
removeOwned: removeOwned,
|
|
||||||
removeByIds: removeByIds,
|
|
||||||
search: search
|
|
||||||
}
|
|
||||||
|
|
||||||
function add (video, callback) {
|
|
||||||
logger.info('Adding %s video to database.', video.name)
|
|
||||||
|
|
||||||
const params = video
|
|
||||||
params.podUrl = http + '://' + host + ':' + port
|
|
||||||
|
|
||||||
VideosDB.create(params, function (err, insertedVideo) {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Cannot insert this video into database.')
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, insertedVideo)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function addRemotes (videos, callback) {
|
|
||||||
videos.forEach(function (video) {
|
|
||||||
// Ensure they are remote videos
|
|
||||||
video.namePath = null
|
|
||||||
})
|
|
||||||
|
|
||||||
VideosDB.create(videos, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
function get (id, callback) {
|
|
||||||
VideosDB.findById(id, function (err, video) {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Cannot get this video.')
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, video)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function list (start, count, sort, callback) {
|
|
||||||
const query = {}
|
|
||||||
return findWithCount(query, start, count, sort, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
function listFromUrl (fromUrl, callback) {
|
|
||||||
VideosDB.find({ podUrl: fromUrl }, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
function listFromUrls (fromUrls, callback) {
|
|
||||||
VideosDB.find({ podUrl: { $in: fromUrls } }, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
function listFromUrlAndMagnets (fromUrl, magnets, callback) {
|
|
||||||
VideosDB.find({ podUrl: fromUrl, magnetUri: { $in: magnets } }, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
function listFromRemotes (callback) {
|
|
||||||
VideosDB.find({ namePath: null }, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
function listOwned (callback) {
|
|
||||||
// If namePath is not null this is *our* video
|
|
||||||
VideosDB.find({ namePath: { $ne: null } }, function (err, videosList) {
|
|
||||||
if (err) {
|
|
||||||
logger.error('Cannot get the list of owned videos.')
|
|
||||||
return callback(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return callback(null, videosList)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the video in the callback
|
|
||||||
function removeOwned (id, callback) {
|
|
||||||
VideosDB.findByIdAndRemove(id, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the magnet Uri because the _id field is not the same on different servers
|
|
||||||
function removeByIds (ids, callback) {
|
|
||||||
VideosDB.remove({ _id: { $in: ids } }, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
function search (value, field, start, count, sort, callback) {
|
|
||||||
const query = {}
|
|
||||||
// Make an exact search with the magnet
|
|
||||||
if (field === 'magnetUri' || field === 'tags') {
|
|
||||||
query[field] = value
|
|
||||||
} else {
|
|
||||||
query[field] = new RegExp(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
findWithCount(query, start, count, sort, callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
module.exports = Videos
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function findWithCount (query, start, count, sort, callback) {
|
|
||||||
async.parallel([
|
|
||||||
function (asyncCallback) {
|
|
||||||
VideosDB.find(query).skip(start).limit(start + count).sort(sort).exec(asyncCallback)
|
|
||||||
},
|
|
||||||
function (asyncCallback) {
|
|
||||||
VideosDB.count(query, asyncCallback)
|
|
||||||
}
|
|
||||||
], function (err, results) {
|
|
||||||
if (err) return callback(err)
|
|
||||||
|
|
||||||
const videos = results[0]
|
|
||||||
const totalVideos = results[1]
|
|
||||||
return callback(null, videos, totalVideos)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -414,7 +414,7 @@ describe('Test multiple pods', function () {
|
||||||
|
|
||||||
// Keep the logs if the test failed
|
// Keep the logs if the test failed
|
||||||
if (this.ok) {
|
if (this.ok) {
|
||||||
utils.flushTests(done)
|
// utils.flushTests(done)
|
||||||
} else {
|
} else {
|
||||||
done()
|
done()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue