aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api/videos.js
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2017-03-08 21:35:43 +0100
committerChocobozzz <florian.bigard@gmail.com>2017-03-08 21:35:43 +0100
commitd38b82810638b9f664c9016fac2684454c273a77 (patch)
tree9465c367e5033675309efca4d66790c6fdd5230d /server/controllers/api/videos.js
parent8f9064432122cba0f518a24ac4378357dadec589 (diff)
downloadPeerTube-d38b82810638b9f664c9016fac2684454c273a77.tar.gz
PeerTube-d38b82810638b9f664c9016fac2684454c273a77.tar.zst
PeerTube-d38b82810638b9f664c9016fac2684454c273a77.zip
Add like/dislike system for videos
Diffstat (limited to 'server/controllers/api/videos.js')
-rw-r--r--server/controllers/api/videos.js162
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
63router.put('/:id/rate',
64 oAuth.authenticate,
65 validatorsVideos.videoRate,
66 rateVideoRetryWrapper
67)
68
63router.get('/', 69router.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
113function 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
126function 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
109function addVideoRetryWrapper (req, res, next) { 256function 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