1
0
Fork 0
mirror of https://github.com/Chocobozzz/PeerTube.git synced 2025-10-05 02:39:33 +02:00

Move to promises

Closes https://github.com/Chocobozzz/PeerTube/issues/74
This commit is contained in:
Chocobozzz 2017-07-05 13:26:25 +02:00
parent 5fe7e89831
commit 6fcd19ba73
88 changed files with 1980 additions and 2505 deletions

View file

@ -24,16 +24,17 @@ function getLocalClient (req: express.Request, res: express.Response, next: expr
return res.type('json').status(403).end()
}
db.OAuthClient.loadFirstClient(function (err, client) {
if (err) return next(err)
if (!client) return next(new Error('No client available.'))
db.OAuthClient.loadFirstClient()
.then(client => {
if (!client) throw new Error('No client available.')
const json: OAuthClientLocal = {
client_id: client.clientId,
client_secret: client.clientSecret
}
res.json(json)
})
const json: OAuthClientLocal = {
client_id: client.clientId,
client_secret: client.clientSecret
}
res.json(json)
})
.catch(err => next(err))
}
// ---------------------------------------------------------------------------

View file

@ -1,5 +1,4 @@
import * as express from 'express'
import { waterfall } from 'async'
import { database as db } from '../../initializers/database'
import { CONFIG } from '../../initializers'
@ -57,65 +56,39 @@ export {
function addPods (req: express.Request, res: express.Response, next: express.NextFunction) {
const informations = req.body
waterfall<string, Error>([
function addPod (callback) {
const pod = db.Pod.build(informations)
pod.save().asCallback(function (err, podCreated) {
// Be sure about the number of parameters for the callback
return callback(err, podCreated)
})
},
function sendMyVideos (podCreated: PodInstance, callback) {
sendOwnedVideosToPod(podCreated.id)
callback(null)
},
function fetchMyCertificate (callback) {
getMyPublicCert(function (err, cert) {
if (err) {
logger.error('Cannot read cert file.')
return callback(err)
}
return callback(null, cert)
})
}
], function (err, cert) {
if (err) return next(err)
return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
})
const pod = db.Pod.build(informations)
pod.save()
.then(podCreated => {
return sendOwnedVideosToPod(podCreated.id)
})
.then(() => {
return getMyPublicCert()
})
.then(cert => {
return res.json({ cert: cert, email: CONFIG.ADMIN.EMAIL })
})
.catch(err => next(err))
}
function listPods (req: express.Request, res: express.Response, next: express.NextFunction) {
db.Pod.list(function (err, podsList) {
if (err) return next(err)
res.json(getFormatedObjects(podsList, podsList.length))
})
db.Pod.list()
.then(podsList => res.json(getFormatedObjects(podsList, podsList.length)))
.catch(err => next(err))
}
function makeFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
const hosts = req.body.hosts as string[]
makeFriends(hosts, function (err) {
if (err) {
logger.error('Could not make friends.', { error: err })
return
}
logger.info('Made friends!')
})
makeFriends(hosts)
.then(() => logger.info('Made friends!'))
.catch(err => logger.error('Could not make friends.', { error: err }))
// Don't wait the process that could be long
res.type('json').status(204).end()
}
function quitFriendsController (req: express.Request, res: express.Response, next: express.NextFunction) {
quitFriends(function (err) {
if (err) return next(err)
res.type('json').status(204).end()
})
quitFriends()
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
}

View file

@ -1,5 +1,4 @@
import * as express from 'express'
import * as waterfall from 'async/waterfall'
import { database as db } from '../../../initializers/database'
import { checkSignature, signatureValidator } from '../../../middlewares'
@ -24,17 +23,10 @@ export {
function removePods (req: express.Request, res: express.Response, next: express.NextFunction) {
const host = req.body.signature.host
waterfall([
function loadPod (callback) {
db.Pod.loadByHost(host, callback)
},
function deletePod (pod, callback) {
pod.destroy().asCallback(callback)
}
], function (err) {
if (err) return next(err)
return res.type('json').status(204).end()
})
db.Pod.loadByHost(host)
.then(pod => {
return pod.destroy()
})
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
}

View file

@ -1,6 +1,5 @@
import * as express from 'express'
import * as Sequelize from 'sequelize'
import { eachSeries, waterfall } from 'async'
import * as Promise from 'bluebird'
import { database as db } from '../../../initializers/database'
import {
@ -16,20 +15,14 @@ import {
remoteQaduVideosValidator,
remoteEventsVideosValidator
} from '../../../middlewares'
import {
logger,
commitTransaction,
retryTransactionWrapper,
rollbackTransaction,
startSerializableTransaction
} from '../../../helpers'
import { logger, retryTransactionWrapper } from '../../../helpers'
import { quickAndDirtyUpdatesVideoToFriends } from '../../../lib'
import { PodInstance, VideoInstance } from '../../../models'
const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
// Functions to call when processing a remote request
const functionsHash = {}
const functionsHash: { [ id: string ]: (...args) => Promise<any> } = {}
functionsHash[ENDPOINT_ACTIONS.ADD] = addRemoteVideoRetryWrapper
functionsHash[ENDPOINT_ACTIONS.UPDATE] = updateRemoteVideoRetryWrapper
functionsHash[ENDPOINT_ACTIONS.REMOVE] = removeRemoteVideo
@ -72,20 +65,19 @@ function remoteVideos (req: express.Request, res: express.Response, next: expres
// We need to process in the same order to keep consistency
// TODO: optimization
eachSeries(requests, function (request: any, callbackEach) {
Promise.mapSeries(requests, (request: any) => {
const data = request.data
// Get the function we need to call in order to process the request
const fun = functionsHash[request.type]
if (fun === undefined) {
logger.error('Unkown remote request type %s.', request.type)
return callbackEach(null)
return
}
fun.call(this, data, fromPod, callbackEach)
}, function (err) {
if (err) logger.error('Error managing remote videos.', { error: err })
return fun.call(this, data, fromPod)
})
.catch(err => logger.error('Error managing remote videos.', { error: err }))
// We don't need to keep the other pod waiting
return res.type('json').status(204).end()
@ -95,13 +87,12 @@ function remoteVideosQadu (req: express.Request, res: express.Response, next: ex
const requests = req.body.data
const fromPod = res.locals.secure.pod
eachSeries(requests, function (request: any, callbackEach) {
Promise.mapSeries(requests, (request: any) => {
const videoData = request.data
quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod, callbackEach)
}, function (err) {
if (err) logger.error('Error managing remote videos.', { error: err })
return quickAndDirtyUpdateVideoRetryWrapper(videoData, fromPod)
})
.catch(err => logger.error('Error managing remote videos.', { error: err }))
return res.type('json').status(204).end()
}
@ -110,414 +101,303 @@ function remoteVideosEvents (req: express.Request, res: express.Response, next:
const requests = req.body.data
const fromPod = res.locals.secure.pod
eachSeries(requests, function (request: any, callbackEach) {
Promise.mapSeries(requests, (request: any) => {
const eventData = request.data
processVideosEventsRetryWrapper(eventData, fromPod, callbackEach)
}, function (err) {
if (err) logger.error('Error managing remote videos.', { error: err })
return processVideosEventsRetryWrapper(eventData, fromPod)
})
.catch(err => logger.error('Error managing remote videos.', { error: err }))
return res.type('json').status(204).end()
}
function processVideosEventsRetryWrapper (eventData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
function processVideosEventsRetryWrapper (eventData: any, fromPod: PodInstance) {
const options = {
arguments: [ eventData, fromPod ],
errorMessage: 'Cannot process videos events with many retries.'
}
retryTransactionWrapper(processVideosEvents, options, finalCallback)
return retryTransactionWrapper(processVideosEvents, options)
}
function processVideosEvents (eventData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
waterfall([
startSerializableTransaction,
function processVideosEvents (eventData: any, fromPod: PodInstance) {
function findVideo (t, callback) {
fetchOwnedVideo(eventData.remoteId, function (err, videoInstance) {
return callback(err, t, videoInstance)
})
},
return db.sequelize.transaction(t => {
return fetchOwnedVideo(eventData.remoteId)
.then(videoInstance => {
const options = { transaction: t }
function updateVideoIntoDB (t, videoInstance, callback) {
const options = { transaction: t }
let columnToUpdate
let qaduType
let columnToUpdate
let qaduType
switch (eventData.eventType) {
case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
columnToUpdate = 'views'
qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
break
switch (eventData.eventType) {
case REQUEST_VIDEO_EVENT_TYPES.VIEWS:
columnToUpdate = 'views'
qaduType = REQUEST_VIDEO_QADU_TYPES.VIEWS
break
case REQUEST_VIDEO_EVENT_TYPES.LIKES:
columnToUpdate = 'likes'
qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
break
case REQUEST_VIDEO_EVENT_TYPES.LIKES:
columnToUpdate = 'likes'
qaduType = REQUEST_VIDEO_QADU_TYPES.LIKES
break
case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
columnToUpdate = 'dislikes'
qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
break
case REQUEST_VIDEO_EVENT_TYPES.DISLIKES:
columnToUpdate = 'dislikes'
qaduType = REQUEST_VIDEO_QADU_TYPES.DISLIKES
break
default:
return callback(new Error('Unknown video event type.'))
}
const query = {}
query[columnToUpdate] = eventData.count
videoInstance.increment(query, options).asCallback(function (err) {
return callback(err, t, videoInstance, qaduType)
})
},
function sendQaduToFriends (t, videoInstance, qaduType, callback) {
const qadusParams = [
{
videoId: videoInstance.id,
type: qaduType
default:
throw new Error('Unknown video event type.')
}
]
quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) {
return callback(err, t)
const query = {}
query[columnToUpdate] = eventData.count
return videoInstance.increment(query, options).then(() => ({ videoInstance, qaduType }))
})
},
.then(({ videoInstance, qaduType }) => {
const qadusParams = [
{
videoId: videoInstance.id,
type: qaduType
}
]
commitTransaction
], function (err: Error, t: Sequelize.Transaction) {
if (err) {
logger.debug('Cannot process a video event.', { error: err })
return rollbackTransaction(err, t, finalCallback)
}
logger.info('Remote video event processed for video %s.', eventData.remoteId)
return finalCallback(null)
return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
})
})
.then(() => logger.info('Remote video event processed for video %s.', eventData.remoteId))
.catch(err => {
logger.debug('Cannot process a video event.', { error: err })
throw err
})
}
function quickAndDirtyUpdateVideoRetryWrapper (videoData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
function quickAndDirtyUpdateVideoRetryWrapper (videoData: any, fromPod: PodInstance) {
const options = {
arguments: [ videoData, fromPod ],
errorMessage: 'Cannot update quick and dirty the remote video with many retries.'
}
retryTransactionWrapper(quickAndDirtyUpdateVideo, options, finalCallback)
return retryTransactionWrapper(quickAndDirtyUpdateVideo, options)
}
function quickAndDirtyUpdateVideo (videoData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
function quickAndDirtyUpdateVideo (videoData: any, fromPod: PodInstance) {
let videoName
waterfall([
startSerializableTransaction,
return db.sequelize.transaction(t => {
return fetchRemoteVideo(fromPod.host, videoData.remoteId)
.then(videoInstance => {
const options = { transaction: t }
function findVideo (t, callback) {
fetchRemoteVideo(fromPod.host, videoData.remoteId, function (err, videoInstance) {
return callback(err, t, videoInstance)
videoName = videoInstance.name
if (videoData.views) {
videoInstance.set('views', videoData.views)
}
if (videoData.likes) {
videoInstance.set('likes', videoData.likes)
}
if (videoData.dislikes) {
videoInstance.set('dislikes', videoData.dislikes)
}
return videoInstance.save(options)
})
},
function updateVideoIntoDB (t, videoInstance, callback) {
const options = { transaction: t }
videoName = videoInstance.name
if (videoData.views) {
videoInstance.set('views', videoData.views)
}
if (videoData.likes) {
videoInstance.set('likes', videoData.likes)
}
if (videoData.dislikes) {
videoInstance.set('dislikes', videoData.dislikes)
}
videoInstance.save(options).asCallback(function (err) {
return callback(err, t)
})
},
commitTransaction
], function (err: Error, t: Sequelize.Transaction) {
if (err) {
logger.debug('Cannot quick and dirty update the remote video.', { error: err })
return rollbackTransaction(err, t, finalCallback)
}
logger.info('Remote video %s quick and dirty updated', videoName)
return finalCallback(null)
})
.then(() => logger.info('Remote video %s quick and dirty updated', videoName))
.catch(err => logger.debug('Cannot quick and dirty update the remote video.', { error: err }))
}
// Handle retries on fail
function addRemoteVideoRetryWrapper (videoToCreateData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
function addRemoteVideoRetryWrapper (videoToCreateData: any, fromPod: PodInstance) {
const options = {
arguments: [ videoToCreateData, fromPod ],
errorMessage: 'Cannot insert the remote video with many retries.'
}
retryTransactionWrapper(addRemoteVideo, options, finalCallback)
return retryTransactionWrapper(addRemoteVideo, options)
}
function addRemoteVideo (videoToCreateData: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
function addRemoteVideo (videoToCreateData: any, fromPod: PodInstance) {
logger.debug('Adding remote video "%s".', videoToCreateData.remoteId)
waterfall([
return db.sequelize.transaction(t => {
return db.Video.loadByHostAndRemoteId(fromPod.host, videoToCreateData.remoteId)
.then(video => {
if (video) throw new Error('RemoteId and host pair is not unique.')
startSerializableTransaction,
function assertRemoteIdAndHostUnique (t, callback) {
db.Video.loadByHostAndRemoteId(fromPod.host, videoToCreateData.remoteId, function (err, video) {
if (err) return callback(err)
if (video) return callback(new Error('RemoteId and host pair is not unique.'))
return callback(null, t)
return undefined
})
},
.then(() => {
const name = videoToCreateData.author
const podId = fromPod.id
// This author is from another pod so we do not associate a user
const userId = null
function findOrCreateAuthor (t, callback) {
const name = videoToCreateData.author
const podId = fromPod.id
// This author is from another pod so we do not associate a user
const userId = null
db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
return callback(err, t, authorInstance)
return db.Author.findOrCreateAuthor(name, podId, userId, t)
})
},
.then(author => {
const tags = videoToCreateData.tags
function findOrCreateTags (t, author, callback) {
const tags = videoToCreateData.tags
db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
return callback(err, t, author, tagInstances)
return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances }))
})
},
function createVideoObject (t, author, tagInstances, callback) {
const videoData = {
name: videoToCreateData.name,
remoteId: videoToCreateData.remoteId,
extname: videoToCreateData.extname,
infoHash: videoToCreateData.infoHash,
category: videoToCreateData.category,
licence: videoToCreateData.licence,
language: videoToCreateData.language,
nsfw: videoToCreateData.nsfw,
description: videoToCreateData.description,
authorId: author.id,
duration: videoToCreateData.duration,
createdAt: videoToCreateData.createdAt,
// FIXME: updatedAt does not seems to be considered by Sequelize
updatedAt: videoToCreateData.updatedAt,
views: videoToCreateData.views,
likes: videoToCreateData.likes,
dislikes: videoToCreateData.dislikes
}
const video = db.Video.build(videoData)
return callback(null, t, tagInstances, video)
},
function generateThumbnail (t, tagInstances, video, callback) {
db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData, function (err) {
if (err) {
logger.error('Cannot generate thumbnail from data.', { error: err })
return callback(err)
.then(({ author, tagInstances }) => {
const videoData = {
name: videoToCreateData.name,
remoteId: videoToCreateData.remoteId,
extname: videoToCreateData.extname,
infoHash: videoToCreateData.infoHash,
category: videoToCreateData.category,
licence: videoToCreateData.licence,
language: videoToCreateData.language,
nsfw: videoToCreateData.nsfw,
description: videoToCreateData.description,
authorId: author.id,
duration: videoToCreateData.duration,
createdAt: videoToCreateData.createdAt,
// FIXME: updatedAt does not seems to be considered by Sequelize
updatedAt: videoToCreateData.updatedAt,
views: videoToCreateData.views,
likes: videoToCreateData.likes,
dislikes: videoToCreateData.dislikes
}
return callback(err, t, tagInstances, video)
const video = db.Video.build(videoData)
return { tagInstances, video }
})
},
function insertVideoIntoDB (t, tagInstances, video, callback) {
const options = {
transaction: t
}
video.save(options).asCallback(function (err, videoCreated) {
return callback(err, t, tagInstances, videoCreated)
.then(({ tagInstances, video }) => {
return db.Video.generateThumbnailFromData(video, videoToCreateData.thumbnailData).then(() => ({ tagInstances, video }))
})
},
.then(({ tagInstances, video }) => {
const options = {
transaction: t
}
function associateTagsToVideo (t, tagInstances, video, callback) {
const options = {
transaction: t
}
video.setTags(tagInstances, options).asCallback(function (err) {
return callback(err, t)
return video.save(options).then(videoCreated => ({ tagInstances, videoCreated }))
})
},
.then(({ tagInstances, videoCreated }) => {
const options = {
transaction: t
}
commitTransaction
], function (err: Error, t: Sequelize.Transaction) {
if (err) {
// This is just a debug because we will retry the insert
logger.debug('Cannot insert the remote video.', { error: err })
return rollbackTransaction(err, t, finalCallback)
}
logger.info('Remote video %s inserted.', videoToCreateData.name)
return finalCallback(null)
return videoCreated.setTags(tagInstances, options)
})
})
.then(() => logger.info('Remote video %s inserted.', videoToCreateData.name))
.catch(err => {
logger.debug('Cannot insert the remote video.', { error: err })
throw err
})
}
// Handle retries on fail
function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: any, fromPod: PodInstance) {
const options = {
arguments: [ videoAttributesToUpdate, fromPod ],
errorMessage: 'Cannot update the remote video with many retries'
}
retryTransactionWrapper(updateRemoteVideo, options, finalCallback)
return retryTransactionWrapper(updateRemoteVideo, options)
}
function updateRemoteVideo (videoAttributesToUpdate: any, fromPod: PodInstance, finalCallback: (err: Error) => void) {
function updateRemoteVideo (videoAttributesToUpdate: any, fromPod: PodInstance) {
logger.debug('Updating remote video "%s".', videoAttributesToUpdate.remoteId)
waterfall([
return db.sequelize.transaction(t => {
return fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId)
.then(videoInstance => {
const tags = videoAttributesToUpdate.tags
startSerializableTransaction,
function findVideo (t, callback) {
fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId, function (err, videoInstance) {
return callback(err, t, videoInstance)
return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ videoInstance, tagInstances }))
})
},
.then(({ videoInstance, tagInstances }) => {
const options = { transaction: t }
function findOrCreateTags (t, videoInstance, callback) {
const tags = videoAttributesToUpdate.tags
videoInstance.set('name', videoAttributesToUpdate.name)
videoInstance.set('category', videoAttributesToUpdate.category)
videoInstance.set('licence', videoAttributesToUpdate.licence)
videoInstance.set('language', videoAttributesToUpdate.language)
videoInstance.set('nsfw', videoAttributesToUpdate.nsfw)
videoInstance.set('description', videoAttributesToUpdate.description)
videoInstance.set('infoHash', videoAttributesToUpdate.infoHash)
videoInstance.set('duration', videoAttributesToUpdate.duration)
videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
videoInstance.set('extname', videoAttributesToUpdate.extname)
videoInstance.set('views', videoAttributesToUpdate.views)
videoInstance.set('likes', videoAttributesToUpdate.likes)
videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
return callback(err, t, videoInstance, tagInstances)
return videoInstance.save(options).then(() => ({ videoInstance, tagInstances }))
})
},
.then(({ videoInstance, tagInstances }) => {
const options = { transaction: t }
function updateVideoIntoDB (t, videoInstance, tagInstances, callback) {
const options = { transaction: t }
videoInstance.set('name', videoAttributesToUpdate.name)
videoInstance.set('category', videoAttributesToUpdate.category)
videoInstance.set('licence', videoAttributesToUpdate.licence)
videoInstance.set('language', videoAttributesToUpdate.language)
videoInstance.set('nsfw', videoAttributesToUpdate.nsfw)
videoInstance.set('description', videoAttributesToUpdate.description)
videoInstance.set('infoHash', videoAttributesToUpdate.infoHash)
videoInstance.set('duration', videoAttributesToUpdate.duration)
videoInstance.set('createdAt', videoAttributesToUpdate.createdAt)
videoInstance.set('updatedAt', videoAttributesToUpdate.updatedAt)
videoInstance.set('extname', videoAttributesToUpdate.extname)
videoInstance.set('views', videoAttributesToUpdate.views)
videoInstance.set('likes', videoAttributesToUpdate.likes)
videoInstance.set('dislikes', videoAttributesToUpdate.dislikes)
videoInstance.save(options).asCallback(function (err) {
return callback(err, t, videoInstance, tagInstances)
return videoInstance.setTags(tagInstances, options)
})
},
function associateTagsToVideo (t, videoInstance, tagInstances, callback) {
const options = { transaction: t }
videoInstance.setTags(tagInstances, options).asCallback(function (err) {
return callback(err, t)
})
},
commitTransaction
], function (err: Error, t: Sequelize.Transaction) {
if (err) {
// This is just a debug because we will retry the insert
logger.debug('Cannot update the remote video.', { error: err })
return rollbackTransaction(err, t, finalCallback)
}
logger.info('Remote video %s updated', videoAttributesToUpdate.name)
return finalCallback(null)
})
.then(() => logger.info('Remote video %s updated', videoAttributesToUpdate.name))
.catch(err => {
// This is just a debug because we will retry the insert
logger.debug('Cannot update the remote video.', { error: err })
throw err
})
}
function removeRemoteVideo (videoToRemoveData: any, fromPod: PodInstance, callback: (err: Error) => void) {
function removeRemoteVideo (videoToRemoveData: any, fromPod: PodInstance) {
// We need the instance because we have to remove some other stuffs (thumbnail etc)
fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId, function (err, video) {
// Do not return the error, continue the process
if (err) return callback(null)
logger.debug('Removing remote video %s.', video.remoteId)
video.destroy().asCallback(function (err) {
// Do not return the error, continue the process
if (err) {
logger.error('Cannot remove remote video with id %s.', videoToRemoveData.remoteId, { error: err })
}
return callback(null)
return fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId)
.then(video => {
logger.debug('Removing remote video %s.', video.remoteId)
return video.destroy()
})
.catch(err => {
logger.debug('Could not fetch remote video.', { host: fromPod.host, remoteId: videoToRemoveData.remoteId, error: err })
})
})
}
function reportAbuseRemoteVideo (reportData: any, fromPod: PodInstance, callback: (err: Error) => void) {
fetchOwnedVideo(reportData.videoRemoteId, function (err, video) {
if (err || !video) {
if (!err) err = new Error('video not found')
function reportAbuseRemoteVideo (reportData: any, fromPod: PodInstance) {
return fetchOwnedVideo(reportData.videoRemoteId)
.then(video => {
logger.debug('Reporting remote abuse for video %s.', video.id)
logger.error('Cannot load video from id.', { error: err, id: reportData.videoRemoteId })
// Do not return the error, continue the process
return callback(null)
}
logger.debug('Reporting remote abuse for video %s.', video.id)
const videoAbuseData = {
reporterUsername: reportData.reporterUsername,
reason: reportData.reportReason,
reporterPodId: fromPod.id,
videoId: video.id
}
db.VideoAbuse.create(videoAbuseData).asCallback(function (err) {
if (err) {
logger.error('Cannot create remote abuse video.', { error: err })
const videoAbuseData = {
reporterUsername: reportData.reporterUsername,
reason: reportData.reportReason,
reporterPodId: fromPod.id,
videoId: video.id
}
return callback(null)
return db.VideoAbuse.create(videoAbuseData)
})
})
.catch(err => logger.error('Cannot create remote abuse video.', { error: err }))
}
function fetchOwnedVideo (id: string, callback: (err: Error, video?: VideoInstance) => void) {
db.Video.load(id, function (err, video) {
if (err || !video) {
if (!err) err = new Error('video not found')
function fetchOwnedVideo (id: string) {
return db.Video.load(id)
.then(video => {
if (!video) throw new Error('Video not found')
return video
})
.catch(err => {
logger.error('Cannot load owned video from id.', { error: err, id })
return callback(err)
}
return callback(null, video)
})
throw err
})
}
function fetchRemoteVideo (podHost: string, remoteId: string, callback: (err: Error, video?: VideoInstance) => void) {
db.Video.loadByHostAndRemoteId(podHost, remoteId, function (err, video) {
if (err || !video) {
if (!err) err = new Error('video not found')
function fetchRemoteVideo (podHost: string, remoteId: string) {
return db.Video.loadByHostAndRemoteId(podHost, remoteId)
.then(video => {
if (!video) throw new Error('Video not found')
return video
})
.catch(err => {
logger.error('Cannot load video from host and remote id.', { error: err, podHost, remoteId })
return callback(err)
}
return callback(null, video)
})
throw err
})
}

View file

@ -1,5 +1,5 @@
import * as express from 'express'
import { parallel } from 'async'
import * as Promise from 'bluebird'
import {
AbstractRequestScheduler,
@ -27,33 +27,27 @@ export {
// ---------------------------------------------------------------------------
function getRequestSchedulersStats (req: express.Request, res: express.Response, next: express.NextFunction) {
parallel({
Promise.props({
requestScheduler: buildRequestSchedulerStats(getRequestScheduler()),
requestVideoQaduScheduler: buildRequestSchedulerStats(getRequestVideoQaduScheduler()),
requestVideoEventScheduler: buildRequestSchedulerStats(getRequestVideoEventScheduler())
}, function (err, result) {
if (err) return next(err)
return res.json(result)
})
.then(result => res.json(result))
.catch(err => next(err))
}
// ---------------------------------------------------------------------------
function buildRequestSchedulerStats (requestScheduler: AbstractRequestScheduler) {
return function (callback) {
requestScheduler.remainingRequestsCount(function (err, count) {
if (err) return callback(err)
function buildRequestSchedulerStats (requestScheduler: AbstractRequestScheduler<any>) {
return requestScheduler.remainingRequestsCount().then(count => {
const result: RequestSchedulerStatsAttributes = {
totalRequests: count,
requestsLimitPods: requestScheduler.limitPods,
requestsLimitPerPod: requestScheduler.limitPerPod,
remainingMilliSeconds: requestScheduler.remainingMilliSeconds(),
milliSecondsInterval: requestScheduler.requestInterval
}
const result: RequestSchedulerStatsAttributes = {
totalRequests: count,
requestsLimitPods: requestScheduler.limitPods,
requestsLimitPerPod: requestScheduler.limitPerPod,
remainingMilliSeconds: requestScheduler.remainingMilliSeconds(),
milliSecondsInterval: requestScheduler.requestInterval
}
return callback(null, result)
})
}
return result
})
}

View file

@ -1,8 +1,7 @@
import * as express from 'express'
import { waterfall } from 'async'
import { database as db } from '../../initializers/database'
import { CONFIG, USER_ROLES } from '../../initializers'
import { USER_ROLES } from '../../initializers'
import { logger, getFormatedObjects } from '../../helpers'
import {
authenticate,
@ -87,78 +86,61 @@ function createUser (req: express.Request, res: express.Response, next: express.
role: USER_ROLES.USER
})
user.save().asCallback(function (err) {
if (err) return next(err)
return res.type('json').status(204).end()
})
user.save()
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
}
function getUserInformation (req: express.Request, res: express.Response, next: express.NextFunction) {
db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) {
if (err) return next(err)
return res.json(user.toFormatedJSON())
})
db.User.loadByUsername(res.locals.oauth.token.user.username)
.then(user => res.json(user.toFormatedJSON()))
.catch(err => next(err))
}
function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoId = '' + req.params.videoId
const userId = +res.locals.oauth.token.User.id
db.UserVideoRate.load(userId, videoId, null, function (err, ratingObj) {
if (err) return next(err)
const rating = ratingObj ? ratingObj.type : 'none'
const json: FormatedUserVideoRate = {
videoId,
rating
}
res.json(json)
})
db.UserVideoRate.load(userId, videoId, null)
.then(ratingObj => {
const rating = ratingObj ? ratingObj.type : 'none'
const json: FormatedUserVideoRate = {
videoId,
rating
}
res.json(json)
})
.catch(err => next(err))
}
function listUsers (req: express.Request, res: express.Response, next: express.NextFunction) {
db.User.listForApi(req.query.start, req.query.count, req.query.sort, function (err, usersList, usersTotal) {
if (err) return next(err)
res.json(getFormatedObjects(usersList, usersTotal))
})
db.User.listForApi(req.query.start, req.query.count, req.query.sort)
.then(resultList => {
res.json(getFormatedObjects(resultList.data, resultList.total))
})
.catch(err => next(err))
}
function removeUser (req: express.Request, res: express.Response, next: express.NextFunction) {
waterfall([
function loadUser (callback) {
db.User.loadById(req.params.id, callback)
},
function deleteUser (user, callback) {
user.destroy().asCallback(callback)
}
], function andFinally (err) {
if (err) {
db.User.loadById(req.params.id)
.then(user => user.destroy())
.then(() => res.sendStatus(204))
.catch(err => {
logger.error('Errors when removed the user.', { error: err })
return next(err)
}
return res.sendStatus(204)
})
})
}
function updateUser (req: express.Request, res: express.Response, next: express.NextFunction) {
db.User.loadByUsername(res.locals.oauth.token.user.username, function (err, user) {
if (err) return next(err)
db.User.loadByUsername(res.locals.oauth.token.user.username)
.then(user => {
if (req.body.password) user.password = req.body.password
if (req.body.displayNSFW !== undefined) user.displayNSFW = req.body.displayNSFW
if (req.body.password) user.password = req.body.password
if (req.body.displayNSFW !== undefined) user.displayNSFW = req.body.displayNSFW
user.save().asCallback(function (err) {
if (err) return next(err)
return res.sendStatus(204)
return user.save()
})
})
.then(() => res.sendStatus(204))
.catch(err => next(err))
}
function success (req: express.Request, res: express.Response, next: express.NextFunction) {

View file

@ -1,16 +1,11 @@
import * as express from 'express'
import * as Sequelize from 'sequelize'
import { waterfall } from 'async'
import { database as db } from '../../../initializers/database'
import * as friends from '../../../lib/friends'
import {
logger,
getFormatedObjects,
retryTransactionWrapper,
startSerializableTransaction,
commitTransaction,
rollbackTransaction
retryTransactionWrapper
} from '../../../helpers'
import {
authenticate,
@ -21,6 +16,7 @@ import {
setVideoAbusesSort,
setPagination
} from '../../../middlewares'
import { VideoInstance } from '../../../models'
const abuseVideoRouter = express.Router()
@ -48,11 +44,9 @@ export {
// ---------------------------------------------------------------------------
function listVideoAbuses (req: express.Request, res: express.Response, next: express.NextFunction) {
db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort, function (err, abusesList, abusesTotal) {
if (err) return next(err)
res.json(getFormatedObjects(abusesList, abusesTotal))
})
db.VideoAbuse.listForApi(req.query.start, req.query.count, req.query.sort)
.then(result => res.json(getFormatedObjects(result.data, result.total)))
.catch(err => next(err))
}
function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
@ -61,14 +55,12 @@ function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Respon
errorMessage: 'Cannot report abuse to the video with many retries.'
}
retryTransactionWrapper(reportVideoAbuse, options, function (err) {
if (err) return next(err)
return res.type('json').status(204).end()
})
retryTransactionWrapper(reportVideoAbuse, options)
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
}
function reportVideoAbuse (req: express.Request, res: express.Response, finalCallback: (err: Error) => void) {
function reportVideoAbuse (req: express.Request, res: express.Response) {
const videoInstance = res.locals.video
const reporterUsername = res.locals.oauth.token.User.username
@ -79,40 +71,26 @@ function reportVideoAbuse (req: express.Request, res: express.Response, finalCal
reporterPodId: null // This is our pod that reported this abuse
}
waterfall([
return db.sequelize.transaction(t => {
return db.VideoAbuse.create(abuse, { transaction: t })
.then(abuse => {
// We send the information to the destination pod
if (videoInstance.isOwned() === false) {
const reportData = {
reporterUsername,
reportReason: abuse.reason,
videoRemoteId: videoInstance.remoteId
}
startSerializableTransaction,
function createAbuse (t, callback) {
db.VideoAbuse.create(abuse).asCallback(function (err, abuse) {
return callback(err, t, abuse)
})
},
function sendToFriendsIfNeeded (t, abuse, callback) {
// We send the information to the destination pod
if (videoInstance.isOwned() === false) {
const reportData = {
reporterUsername,
reportReason: abuse.reason,
videoRemoteId: videoInstance.remoteId
return friends.reportAbuseVideoToFriend(reportData, videoInstance, t).then(() => videoInstance)
}
friends.reportAbuseVideoToFriend(reportData, videoInstance)
}
return callback(null, t)
},
commitTransaction
], function andFinally (err: Error, t: Sequelize.Transaction) {
if (err) {
logger.debug('Cannot update the video.', { error: err })
return rollbackTransaction(err, t, finalCallback)
}
logger.info('Abuse report for video %s created.', videoInstance.name)
return finalCallback(null)
return videoInstance
})
})
.then((videoInstance: VideoInstance) => logger.info('Abuse report for video %s created.', videoInstance.name))
.catch(err => {
logger.debug('Cannot update the video.', { error: err })
throw err
})
}

View file

@ -32,12 +32,10 @@ function addVideoToBlacklist (req: express.Request, res: express.Response, next:
videoId: videoInstance.id
}
db.BlacklistedVideo.create(toCreate).asCallback(function (err) {
if (err) {
db.BlacklistedVideo.create(toCreate)
.then(() => res.type('json').status(204).end())
.catch(err => {
logger.error('Errors when blacklisting video ', { error: err })
return next(err)
}
return res.type('json').status(204).end()
})
})
}

View file

@ -1,9 +1,7 @@
import * as express from 'express'
import * as Sequelize from 'sequelize'
import * as fs from 'fs'
import * as Promise from 'bluebird'
import * as multer from 'multer'
import * as path from 'path'
import { waterfall } from 'async'
import { database as db } from '../../../initializers/database'
import {
@ -35,13 +33,12 @@ import {
} from '../../../middlewares'
import {
logger,
commitTransaction,
retryTransactionWrapper,
rollbackTransaction,
startSerializableTransaction,
generateRandomString,
getFormatedObjects
getFormatedObjects,
renamePromise
} from '../../../helpers'
import { TagInstance } from '../../../models'
import { abuseVideoRouter } from './abuse'
import { blacklistRouter } from './blacklist'
@ -60,10 +57,15 @@ const storage = multer.diskStorage({
if (file.mimetype === 'video/webm') extension = 'webm'
else if (file.mimetype === 'video/mp4') extension = 'mp4'
else if (file.mimetype === 'video/ogg') extension = 'ogv'
generateRandomString(16, function (err, randomString) {
const fieldname = err ? undefined : randomString
cb(null, fieldname + '.' + extension)
})
generateRandomString(16)
.then(randomString => {
const filename = randomString
cb(null, filename + '.' + extension)
})
.catch(err => {
logger.error('Cannot generate random string for file name.', { error: err })
throw err
})
}
})
@ -144,125 +146,97 @@ function addVideoRetryWrapper (req: express.Request, res: express.Response, next
errorMessage: 'Cannot insert the video with many retries.'
}
retryTransactionWrapper(addVideo, options, function (err) {
if (err) return next(err)
// TODO : include Location of the new video -> 201
return res.type('json').status(204).end()
})
retryTransactionWrapper(addVideo, options)
.then(() => {
// TODO : include Location of the new video -> 201
res.type('json').status(204).end()
})
.catch(err => next(err))
}
function addVideo (req: express.Request, res: express.Response, videoFile: Express.Multer.File, finalCallback: (err: Error) => void) {
function addVideo (req: express.Request, res: express.Response, videoFile: Express.Multer.File) {
const videoInfos = req.body
waterfall([
return db.sequelize.transaction(t => {
const user = res.locals.oauth.token.User
startSerializableTransaction,
const name = user.username
// null because it is OUR pod
const podId = null
const userId = user.id
function findOrCreateAuthor (t, callback) {
const user = res.locals.oauth.token.User
return db.Author.findOrCreateAuthor(name, podId, userId, t)
.then(author => {
const tags = videoInfos.tags
if (!tags) return { author, tagInstances: undefined }
const name = user.username
// null because it is OUR pod
const podId = null
const userId = user.id
db.Author.findOrCreateAuthor(name, podId, userId, t, function (err, authorInstance) {
return callback(err, t, authorInstance)
return db.Tag.findOrCreateTags(tags, t).then(tagInstances => ({ author, tagInstances }))
})
},
.then(({ author, tagInstances }) => {
const videoData = {
name: videoInfos.name,
remoteId: null,
extname: path.extname(videoFile.filename),
category: videoInfos.category,
licence: videoInfos.licence,
language: videoInfos.language,
nsfw: videoInfos.nsfw,
description: videoInfos.description,
duration: videoFile['duration'], // duration was added by a previous middleware
authorId: author.id
}
function findOrCreateTags (t, author, callback) {
const tags = videoInfos.tags
db.Tag.findOrCreateTags(tags, t, function (err, tagInstances) {
return callback(err, t, author, tagInstances)
const video = db.Video.build(videoData)
return { author, tagInstances, video }
})
},
.then(({ author, tagInstances, video }) => {
const videoDir = CONFIG.STORAGE.VIDEOS_DIR
const source = path.join(videoDir, videoFile.filename)
const destination = path.join(videoDir, video.getVideoFilename())
function createVideoObject (t, author, tagInstances, callback) {
const videoData = {
name: videoInfos.name,
remoteId: null,
extname: path.extname(videoFile.filename),
category: videoInfos.category,
licence: videoInfos.licence,
language: videoInfos.language,
nsfw: videoInfos.nsfw,
description: videoInfos.description,
duration: videoFile['duration'], // duration was added by a previous middleware
authorId: author.id
}
const video = db.Video.build(videoData)
return callback(null, t, author, tagInstances, video)
},
// Set the videoname the same as the id
function renameVideoFile (t, author, tagInstances, video, callback) {
const videoDir = CONFIG.STORAGE.VIDEOS_DIR
const source = path.join(videoDir, videoFile.filename)
const destination = path.join(videoDir, video.getVideoFilename())
fs.rename(source, destination, function (err) {
if (err) return callback(err)
// This is important in case if there is another attempt
videoFile.filename = video.getVideoFilename()
return callback(null, t, author, tagInstances, video)
return renamePromise(source, destination)
.then(() => {
// This is important in case if there is another attempt in the retry process
videoFile.filename = video.getVideoFilename()
return { author, tagInstances, video }
})
})
},
.then(({ author, tagInstances, video }) => {
const options = { transaction: t }
function insertVideoIntoDB (t, author, tagInstances, video, callback) {
const options = { transaction: t }
return video.save(options)
.then(videoCreated => {
// Do not forget to add Author informations to the created video
videoCreated.Author = author
// Add tags association
video.save(options).asCallback(function (err, videoCreated) {
if (err) return callback(err)
// Do not forget to add Author informations to the created video
videoCreated.Author = author
return callback(err, t, tagInstances, videoCreated)
return { tagInstances, video: videoCreated }
})
})
},
.then(({ tagInstances, video }) => {
if (!tagInstances) return video
function associateTagsToVideo (t, tagInstances, video, callback) {
const options = { transaction: t }
video.setTags(tagInstances, options).asCallback(function (err) {
video.Tags = tagInstances
return callback(err, t, video)
const options = { transaction: t }
return video.setTags(tagInstances, options)
.then(() => {
video.Tags = tagInstances
return video
})
})
},
.then(video => {
// Let transcoding job send the video to friends because the videofile extension might change
if (CONFIG.TRANSCODING.ENABLED === true) return undefined
function sendToFriends (t, video, callback) {
// Let transcoding job send the video to friends because the videofile extension might change
if (CONFIG.TRANSCODING.ENABLED === true) return callback(null, t)
video.toAddRemoteJSON(function (err, remoteVideo) {
if (err) return callback(err)
// Now we'll add the video's meta data to our friends
addVideoToFriends(remoteVideo, t, function (err) {
return callback(err, t)
})
return video.toAddRemoteJSON()
.then(remoteVideo => {
// Now we'll add the video's meta data to our friends
return addVideoToFriends(remoteVideo, t)
})
})
},
commitTransaction
], function andFinally (err: Error, t: Sequelize.Transaction) {
if (err) {
// This is just a debug because we will retry the insert
logger.debug('Cannot insert the video.', { error: err })
return rollbackTransaction(err, t, finalCallback)
}
logger.info('Video with name %s created.', videoInfos.name)
return finalCallback(null)
})
.then(() => logger.info('Video with name %s created.', videoInfos.name))
.catch((err: Error) => {
logger.debug('Cannot insert the video.', { error: err.stack })
throw err
})
}
@ -272,92 +246,75 @@ function updateVideoRetryWrapper (req: express.Request, res: express.Response, n
errorMessage: 'Cannot update the video with many retries.'
}
retryTransactionWrapper(updateVideo, options, function (err) {
if (err) return next(err)
// TODO : include Location of the new video -> 201
return res.type('json').status(204).end()
})
retryTransactionWrapper(updateVideo, options)
.then(() => {
// TODO : include Location of the new video -> 201
return res.type('json').status(204).end()
})
.catch(err => next(err))
}
function updateVideo (req: express.Request, res: express.Response, finalCallback: (err: Error) => void) {
function updateVideo (req: express.Request, res: express.Response) {
const videoInstance = res.locals.video
const videoFieldsSave = videoInstance.toJSON()
const videoInfosToUpdate = req.body
waterfall([
startSerializableTransaction,
function findOrCreateTags (t, callback) {
if (videoInfosToUpdate.tags) {
db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t, function (err, tagInstances) {
return callback(err, t, tagInstances)
})
} else {
return callback(null, t, null)
}
},
function updateVideoIntoDB (t, tagInstances, callback) {
const options = {
transaction: t
}
if (videoInfosToUpdate.name !== undefined) videoInstance.set('name', videoInfosToUpdate.name)
if (videoInfosToUpdate.category !== undefined) videoInstance.set('category', videoInfosToUpdate.category)
if (videoInfosToUpdate.licence !== undefined) videoInstance.set('licence', videoInfosToUpdate.licence)
if (videoInfosToUpdate.language !== undefined) videoInstance.set('language', videoInfosToUpdate.language)
if (videoInfosToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfosToUpdate.nsfw)
if (videoInfosToUpdate.description !== undefined) videoInstance.set('description', videoInfosToUpdate.description)
videoInstance.save(options).asCallback(function (err) {
return callback(err, t, tagInstances)
})
},
function associateTagsToVideo (t, tagInstances, callback) {
if (tagInstances) {
const options = { transaction: t }
videoInstance.setTags(tagInstances, options).asCallback(function (err) {
videoInstance.Tags = tagInstances
return callback(err, t)
})
} else {
return callback(null, t)
}
},
function sendToFriends (t, callback) {
const json = videoInstance.toUpdateRemoteJSON()
// Now we'll update the video's meta data to our friends
updateVideoToFriends(json, t, function (err) {
return callback(err, t)
})
},
commitTransaction
], function andFinally (err: Error, t: Sequelize.Transaction) {
if (err) {
logger.debug('Cannot update the video.', { error: err })
// Force fields we want to update
// If the transaction is retried, sequelize will think the object has not changed
// So it will skip the SQL request, even if the last one was ROLLBACKed!
Object.keys(videoFieldsSave).forEach(function (key) {
const value = videoFieldsSave[key]
videoInstance.set(key, value)
})
return rollbackTransaction(err, t, finalCallback)
return db.sequelize.transaction(t => {
let tagsPromise: Promise<TagInstance[]>
if (!videoInfosToUpdate.tags) {
tagsPromise = Promise.resolve(null)
} else {
tagsPromise = db.Tag.findOrCreateTags(videoInfosToUpdate.tags, t)
}
return tagsPromise
.then(tagInstances => {
const options = {
transaction: t
}
if (videoInfosToUpdate.name !== undefined) videoInstance.set('name', videoInfosToUpdate.name)
if (videoInfosToUpdate.category !== undefined) videoInstance.set('category', videoInfosToUpdate.category)
if (videoInfosToUpdate.licence !== undefined) videoInstance.set('licence', videoInfosToUpdate.licence)
if (videoInfosToUpdate.language !== undefined) videoInstance.set('language', videoInfosToUpdate.language)
if (videoInfosToUpdate.nsfw !== undefined) videoInstance.set('nsfw', videoInfosToUpdate.nsfw)
if (videoInfosToUpdate.description !== undefined) videoInstance.set('description', videoInfosToUpdate.description)
return videoInstance.save(options).then(() => tagInstances)
})
.then(tagInstances => {
if (!tagInstances) return
const options = { transaction: t }
return videoInstance.setTags(tagInstances, options)
.then(() => {
videoInstance.Tags = tagInstances
return
})
})
.then(() => {
const json = videoInstance.toUpdateRemoteJSON()
// Now we'll update the video's meta data to our friends
return updateVideoToFriends(json, t)
})
})
.then(() => {
logger.info('Video with name %s updated.', videoInstance.name)
return finalCallback(null)
})
.catch(err => {
logger.debug('Cannot update the video.', { error: err })
// Force fields we want to update
// If the transaction is retried, sequelize will think the object has not changed
// So it will skip the SQL request, even if the last one was ROLLBACKed!
Object.keys(videoFieldsSave).forEach(function (key) {
const value = videoFieldsSave[key]
videoInstance.set(key, value)
})
throw err
})
}
@ -366,20 +323,17 @@ function getVideo (req: express.Request, res: express.Response, next: express.Ne
if (videoInstance.isOwned()) {
// The increment is done directly in the database, not using the instance value
videoInstance.increment('views').asCallback(function (err) {
if (err) {
logger.error('Cannot add view to video %d.', videoInstance.id)
return
}
// FIXME: make a real view system
// For example, only add a view when a user watch a video during 30s etc
const qaduParams = {
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.VIEWS
}
quickAndDirtyUpdateVideoToFriends(qaduParams)
})
videoInstance.increment('views')
.then(() => {
// FIXME: make a real view system
// For example, only add a view when a user watch a video during 30s etc
const qaduParams = {
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.VIEWS
}
return quickAndDirtyUpdateVideoToFriends(qaduParams)
})
.catch(err => logger.error('Cannot add view to video %d.', videoInstance.id, { error: err }))
} else {
// Just send the event to our friends
const eventParams = {
@ -394,33 +348,24 @@ function getVideo (req: express.Request, res: express.Response, next: express.Ne
}
function listVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
db.Video.listForApi(req.query.start, req.query.count, req.query.sort, function (err, videosList, videosTotal) {
if (err) return next(err)
res.json(getFormatedObjects(videosList, videosTotal))
})
db.Video.listForApi(req.query.start, req.query.count, req.query.sort)
.then(result => res.json(getFormatedObjects(result.data, result.total)))
.catch(err => next(err))
}
function removeVideo (req: express.Request, res: express.Response, next: express.NextFunction) {
const videoInstance = res.locals.video
videoInstance.destroy().asCallback(function (err) {
if (err) {
videoInstance.destroy()
.then(() => res.type('json').status(204).end())
.catch(err => {
logger.error('Errors when removed the video.', { error: err })
return next(err)
}
return res.type('json').status(204).end()
})
})
}
function searchVideos (req: express.Request, res: express.Response, next: express.NextFunction) {
db.Video.searchAndPopulateAuthorAndPodAndTags(
req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort,
function (err, videosList, videosTotal) {
if (err) return next(err)
res.json(getFormatedObjects(videosList, videosTotal))
}
)
db.Video.searchAndPopulateAuthorAndPodAndTags(req.params.value, req.query.field, req.query.start, req.query.count, req.query.sort)
.then(result => res.json(getFormatedObjects(result.data, result.total)))
.catch(err => next(err))
}

View file

@ -1,14 +1,9 @@
import * as express from 'express'
import * as Sequelize from 'sequelize'
import { waterfall } from 'async'
import { database as db } from '../../../initializers/database'
import {
logger,
retryTransactionWrapper,
startSerializableTransaction,
commitTransaction,
rollbackTransaction
retryTransactionWrapper
} from '../../../helpers'
import {
VIDEO_RATE_TYPES,
@ -46,137 +41,109 @@ function rateVideoRetryWrapper (req: express.Request, res: express.Response, nex
errorMessage: 'Cannot update the user video rate.'
}
retryTransactionWrapper(rateVideo, options, function (err) {
if (err) return next(err)
return res.type('json').status(204).end()
})
retryTransactionWrapper(rateVideo, options)
.then(() => res.type('json').status(204).end())
.catch(err => next(err))
}
function rateVideo (req: express.Request, res: express.Response, finalCallback: (err: Error) => void) {
function rateVideo (req: express.Request, res: express.Response) {
const rateType = req.body.rating
const videoInstance = res.locals.video
const userInstance = res.locals.oauth.token.User
waterfall([
startSerializableTransaction,
return db.sequelize.transaction(t => {
return db.UserVideoRate.load(userInstance.id, videoInstance.id, t)
.then(previousRate => {
const options = { transaction: t }
function findPreviousRate (t, callback) {
db.UserVideoRate.load(userInstance.id, videoInstance.id, t, function (err, previousRate) {
return callback(err, t, previousRate)
let likesToIncrement = 0
let dislikesToIncrement = 0
if (rateType === VIDEO_RATE_TYPES.LIKE) likesToIncrement++
else if (rateType === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement++
// There was a previous rate, update it
if (previousRate) {
// We will remove the previous rate, so we will need to remove it from the video attribute
if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement--
else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement--
previousRate.type = rateType
return previousRate.save(options).then(() => ({ t, likesToIncrement, dislikesToIncrement }))
} else { // There was not a previous rate, insert a new one
const query = {
userId: userInstance.id,
videoId: videoInstance.id,
type: rateType
}
return db.UserVideoRate.create(query, options).then(() => ({ likesToIncrement, dislikesToIncrement }))
}
})
},
function insertUserRateIntoDB (t, previousRate, callback) {
const options = { transaction: t }
let likesToIncrement = 0
let dislikesToIncrement = 0
if (rateType === VIDEO_RATE_TYPES.LIKE) likesToIncrement++
else if (rateType === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement++
// There was a previous rate, update it
if (previousRate) {
// We will remove the previous rate, so we will need to remove it from the video attribute
if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement--
else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement--
previousRate.type = rateType
previousRate.save(options).asCallback(function (err) {
return callback(err, t, likesToIncrement, dislikesToIncrement)
})
} else { // There was not a previous rate, insert a new one
const query = {
userId: userInstance.id,
videoId: videoInstance.id,
type: rateType
.then(({ likesToIncrement, dislikesToIncrement }) => {
const options = { transaction: t }
const incrementQuery = {
likes: likesToIncrement,
dislikes: dislikesToIncrement
}
db.UserVideoRate.create(query, options).asCallback(function (err) {
return callback(err, t, likesToIncrement, dislikesToIncrement)
})
}
},
function updateVideoAttributeDB (t, likesToIncrement, dislikesToIncrement, callback) {
const options = { transaction: t }
const incrementQuery = {
likes: likesToIncrement,
dislikes: dislikesToIncrement
}
// Even if we do not own the video we increment the attributes
// It is usefull for the user to have a feedback
videoInstance.increment(incrementQuery, options).asCallback(function (err) {
return callback(err, t, likesToIncrement, dislikesToIncrement)
// Even if we do not own the video we increment the attributes
// It is usefull for the user to have a feedback
return videoInstance.increment(incrementQuery, options).then(() => ({ likesToIncrement, dislikesToIncrement }))
})
},
.then(({ likesToIncrement, dislikesToIncrement }) => {
// No need for an event type, we own the video
if (videoInstance.isOwned()) return { likesToIncrement, dislikesToIncrement }
function sendEventsToFriendsIfNeeded (t, likesToIncrement, dislikesToIncrement, callback) {
// No need for an event type, we own the video
if (videoInstance.isOwned()) return callback(null, t, likesToIncrement, dislikesToIncrement)
const eventsParams = []
const eventsParams = []
if (likesToIncrement !== 0) {
eventsParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_EVENT_TYPES.LIKES,
count: likesToIncrement
})
}
if (likesToIncrement !== 0) {
eventsParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_EVENT_TYPES.LIKES,
count: likesToIncrement
})
}
if (dislikesToIncrement !== 0) {
eventsParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_EVENT_TYPES.DISLIKES,
count: dislikesToIncrement
})
}
if (dislikesToIncrement !== 0) {
eventsParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_EVENT_TYPES.DISLIKES,
count: dislikesToIncrement
})
}
addEventsToRemoteVideo(eventsParams, t, function (err) {
return callback(err, t, likesToIncrement, dislikesToIncrement)
return addEventsToRemoteVideo(eventsParams, t).then(() => ({ likesToIncrement, dislikesToIncrement }))
})
},
.then(({ likesToIncrement, dislikesToIncrement }) => {
// We do not own the video, there is no need to send a quick and dirty update to friends
// Our rate was already sent by the addEvent function
if (videoInstance.isOwned() === false) return undefined
function sendQaduToFriendsIfNeeded (t, likesToIncrement, dislikesToIncrement, callback) {
// We do not own the video, there is no need to send a quick and dirty update to friends
// Our rate was already sent by the addEvent function
if (videoInstance.isOwned() === false) return callback(null, t)
const qadusParams = []
const qadusParams = []
if (likesToIncrement !== 0) {
qadusParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.LIKES
})
}
if (likesToIncrement !== 0) {
qadusParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.LIKES
})
}
if (dislikesToIncrement !== 0) {
qadusParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.DISLIKES
})
}
if (dislikesToIncrement !== 0) {
qadusParams.push({
videoId: videoInstance.id,
type: REQUEST_VIDEO_QADU_TYPES.DISLIKES
})
}
quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) {
return callback(err, t)
return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
})
},
commitTransaction
], function (err: Error, t: Sequelize.Transaction) {
if (err) {
// This is just a debug because we will retry the insert
logger.debug('Cannot add the user video rate.', { error: err })
return rollbackTransaction(err, t, finalCallback)
}
logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username)
return finalCallback(null)
})
.then(() => logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username))
.catch(err => {
// This is just a debug because we will retry the insert
logger.debug('Cannot add the user video rate.', { error: err })
throw err
})
}