diff options
Diffstat (limited to 'server/controllers/api/videos.js')
-rw-r--r-- | server/controllers/api/videos.js | 162 |
1 files changed, 158 insertions, 4 deletions
diff --git a/server/controllers/api/videos.js b/server/controllers/api/videos.js index 5a67d1121..9acdb8fd2 100644 --- a/server/controllers/api/videos.js +++ b/server/controllers/api/videos.js | |||
@@ -60,6 +60,12 @@ router.post('/:id/abuse', | |||
60 | reportVideoAbuseRetryWrapper | 60 | reportVideoAbuseRetryWrapper |
61 | ) | 61 | ) |
62 | 62 | ||
63 | router.put('/:id/rate', | ||
64 | oAuth.authenticate, | ||
65 | validatorsVideos.videoRate, | ||
66 | rateVideoRetryWrapper | ||
67 | ) | ||
68 | |||
63 | router.get('/', | 69 | router.get('/', |
64 | validatorsPagination.pagination, | 70 | validatorsPagination.pagination, |
65 | validatorsSort.videosSort, | 71 | validatorsSort.videosSort, |
@@ -104,6 +110,147 @@ module.exports = router | |||
104 | 110 | ||
105 | // --------------------------------------------------------------------------- | 111 | // --------------------------------------------------------------------------- |
106 | 112 | ||
113 | function rateVideoRetryWrapper (req, res, next) { | ||
114 | const options = { | ||
115 | arguments: [ req, res ], | ||
116 | errorMessage: 'Cannot update the user video rate.' | ||
117 | } | ||
118 | |||
119 | databaseUtils.retryTransactionWrapper(rateVideo, options, function (err) { | ||
120 | if (err) return next(err) | ||
121 | |||
122 | return res.type('json').status(204).end() | ||
123 | }) | ||
124 | } | ||
125 | |||
126 | function rateVideo (req, res, finalCallback) { | ||
127 | const rateType = req.body.rating | ||
128 | const videoInstance = res.locals.video | ||
129 | const userInstance = res.locals.oauth.token.User | ||
130 | |||
131 | waterfall([ | ||
132 | databaseUtils.startSerializableTransaction, | ||
133 | |||
134 | function findPreviousRate (t, callback) { | ||
135 | db.UserVideoRate.load(userInstance.id, videoInstance.id, t, function (err, previousRate) { | ||
136 | return callback(err, t, previousRate) | ||
137 | }) | ||
138 | }, | ||
139 | |||
140 | function insertUserRateIntoDB (t, previousRate, callback) { | ||
141 | const options = { transaction: t } | ||
142 | |||
143 | let likesToIncrement = 0 | ||
144 | let dislikesToIncrement = 0 | ||
145 | |||
146 | if (rateType === constants.VIDEO_RATE_TYPES.LIKE) likesToIncrement++ | ||
147 | else if (rateType === constants.VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement++ | ||
148 | |||
149 | // There was a previous rate, update it | ||
150 | if (previousRate) { | ||
151 | // We will remove the previous rate, so we will need to remove it from the video attribute | ||
152 | if (previousRate.type === constants.VIDEO_RATE_TYPES.LIKE) likesToIncrement-- | ||
153 | else if (previousRate.type === constants.VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement-- | ||
154 | |||
155 | previousRate.type = rateType | ||
156 | |||
157 | previousRate.save(options).asCallback(function (err) { | ||
158 | return callback(err, t, likesToIncrement, dislikesToIncrement) | ||
159 | }) | ||
160 | } else { // There was not a previous rate, insert a new one | ||
161 | const query = { | ||
162 | userId: userInstance.id, | ||
163 | videoId: videoInstance.id, | ||
164 | type: rateType | ||
165 | } | ||
166 | |||
167 | db.UserVideoRate.create(query, options).asCallback(function (err) { | ||
168 | return callback(err, t, likesToIncrement, dislikesToIncrement) | ||
169 | }) | ||
170 | } | ||
171 | }, | ||
172 | |||
173 | function updateVideoAttributeDB (t, likesToIncrement, dislikesToIncrement, callback) { | ||
174 | const options = { transaction: t } | ||
175 | const incrementQuery = { | ||
176 | likes: likesToIncrement, | ||
177 | dislikes: dislikesToIncrement | ||
178 | } | ||
179 | |||
180 | // Even if we do not own the video we increment the attributes | ||
181 | // It is usefull for the user to have a feedback | ||
182 | videoInstance.increment(incrementQuery, options).asCallback(function (err) { | ||
183 | return callback(err, t, likesToIncrement, dislikesToIncrement) | ||
184 | }) | ||
185 | }, | ||
186 | |||
187 | function sendEventsToFriendsIfNeeded (t, likesToIncrement, dislikesToIncrement, callback) { | ||
188 | // No need for an event type, we own the video | ||
189 | if (videoInstance.isOwned()) return callback(null, t, likesToIncrement, dislikesToIncrement) | ||
190 | |||
191 | const eventsParams = [] | ||
192 | |||
193 | if (likesToIncrement !== 0) { | ||
194 | eventsParams.push({ | ||
195 | videoId: videoInstance.id, | ||
196 | type: constants.REQUEST_VIDEO_EVENT_TYPES.LIKES, | ||
197 | count: likesToIncrement | ||
198 | }) | ||
199 | } | ||
200 | |||
201 | if (dislikesToIncrement !== 0) { | ||
202 | eventsParams.push({ | ||
203 | videoId: videoInstance.id, | ||
204 | type: constants.REQUEST_VIDEO_EVENT_TYPES.DISLIKES, | ||
205 | count: dislikesToIncrement | ||
206 | }) | ||
207 | } | ||
208 | |||
209 | friends.addEventsToRemoteVideo(eventsParams, t, function (err) { | ||
210 | return callback(err, t, likesToIncrement, dislikesToIncrement) | ||
211 | }) | ||
212 | }, | ||
213 | |||
214 | function sendQaduToFriendsIfNeeded (t, likesToIncrement, dislikesToIncrement, callback) { | ||
215 | // We do not own the video, there is no need to send a quick and dirty update to friends | ||
216 | // Our rate was already sent by the addEvent function | ||
217 | if (videoInstance.isOwned() === false) return callback(null, t) | ||
218 | |||
219 | const qadusParams = [] | ||
220 | |||
221 | if (likesToIncrement !== 0) { | ||
222 | qadusParams.push({ | ||
223 | videoId: videoInstance.id, | ||
224 | type: constants.REQUEST_VIDEO_QADU_TYPES.LIKES | ||
225 | }) | ||
226 | } | ||
227 | |||
228 | if (dislikesToIncrement !== 0) { | ||
229 | qadusParams.push({ | ||
230 | videoId: videoInstance.id, | ||
231 | type: constants.REQUEST_VIDEO_QADU_TYPES.DISLIKES | ||
232 | }) | ||
233 | } | ||
234 | |||
235 | friends.quickAndDirtyUpdatesVideoToFriends(qadusParams, t, function (err) { | ||
236 | return callback(err, t) | ||
237 | }) | ||
238 | }, | ||
239 | |||
240 | databaseUtils.commitTransaction | ||
241 | |||
242 | ], function (err, t) { | ||
243 | if (err) { | ||
244 | // This is just a debug because we will retry the insert | ||
245 | logger.debug('Cannot add the user video rate.', { error: err }) | ||
246 | return databaseUtils.rollbackTransaction(err, t, finalCallback) | ||
247 | } | ||
248 | |||
249 | logger.info('User video rate for video %s of user %s updated.', videoInstance.name, userInstance.username) | ||
250 | return finalCallback(null) | ||
251 | }) | ||
252 | } | ||
253 | |||
107 | // Wrapper to video add that retry the function if there is a database error | 254 | // Wrapper to video add that retry the function if there is a database error |
108 | // We need this because we run the transaction in SERIALIZABLE isolation that can fail | 255 | // We need this because we run the transaction in SERIALIZABLE isolation that can fail |
109 | function addVideoRetryWrapper (req, res, next) { | 256 | function addVideoRetryWrapper (req, res, next) { |
@@ -155,8 +302,7 @@ function addVideo (req, res, videoFile, finalCallback) { | |||
155 | extname: path.extname(videoFile.filename), | 302 | extname: path.extname(videoFile.filename), |
156 | description: videoInfos.description, | 303 | description: videoInfos.description, |
157 | duration: videoFile.duration, | 304 | duration: videoFile.duration, |
158 | authorId: author.id, | 305 | authorId: author.id |
159 | views: videoInfos.views | ||
160 | } | 306 | } |
161 | 307 | ||
162 | const video = db.Video.build(videoData) | 308 | const video = db.Video.build(videoData) |
@@ -332,11 +478,19 @@ function getVideo (req, res, next) { | |||
332 | 478 | ||
333 | // FIXME: make a real view system | 479 | // FIXME: make a real view system |
334 | // For example, only add a view when a user watch a video during 30s etc | 480 | // For example, only add a view when a user watch a video during 30s etc |
335 | friends.quickAndDirtyUpdateVideoToFriends(videoInstance.id, constants.REQUEST_VIDEO_QADU_TYPES.VIEWS) | 481 | const qaduParams = { |
482 | videoId: videoInstance.id, | ||
483 | type: constants.REQUEST_VIDEO_QADU_TYPES.VIEWS | ||
484 | } | ||
485 | friends.quickAndDirtyUpdateVideoToFriends(qaduParams) | ||
336 | }) | 486 | }) |
337 | } else { | 487 | } else { |
338 | // Just send the event to our friends | 488 | // Just send the event to our friends |
339 | friends.addEventToRemoteVideo(videoInstance.id, constants.REQUEST_VIDEO_EVENT_TYPES.VIEWS) | 489 | const eventParams = { |
490 | videoId: videoInstance.id, | ||
491 | type: constants.REQUEST_VIDEO_EVENT_TYPES.VIEWS | ||
492 | } | ||
493 | friends.addEventToRemoteVideo(eventParams) | ||
340 | } | 494 | } |
341 | 495 | ||
342 | // Do not wait the view system | 496 | // Do not wait the view system |