aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2017-07-11 16:01:56 +0200
committerChocobozzz <florian.bigard@gmail.com>2017-07-11 16:01:56 +0200
commit0a6658fdcbd779ada8f3758048c326e997902d5a (patch)
tree5de40bf901db0299011104b1344783637b964eb0 /server
parente6d4b0ff2404dcf0b3a755c3fcc415ffeb6e754d (diff)
downloadPeerTube-0a6658fdcbd779ada8f3758048c326e997902d5a.tar.gz
PeerTube-0a6658fdcbd779ada8f3758048c326e997902d5a.tar.zst
PeerTube-0a6658fdcbd779ada8f3758048c326e997902d5a.zip
Use global uuid instead of remoteId for videos
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/remote/videos.ts39
-rw-r--r--server/controllers/api/users.ts2
-rw-r--r--server/controllers/api/videos/abuse.ts4
-rw-r--r--server/controllers/api/videos/index.ts2
-rw-r--r--server/controllers/api/videos/rate.ts2
-rw-r--r--server/controllers/client.ts13
-rw-r--r--server/helpers/custom-validators/remote/videos.ts12
-rw-r--r--server/helpers/custom-validators/videos.ts14
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/initializers/migrations/0005-email-pod.ts2
-rw-r--r--server/initializers/migrations/0010-email-user.ts2
-rw-r--r--server/initializers/migrations/0015-video-views.ts2
-rw-r--r--server/initializers/migrations/0020-video-likes.ts2
-rw-r--r--server/initializers/migrations/0025-video-dislikes.ts2
-rw-r--r--server/initializers/migrations/0030-video-category.ts2
-rw-r--r--server/initializers/migrations/0035-video-licence.ts2
-rw-r--r--server/initializers/migrations/0040-video-nsfw.ts2
-rw-r--r--server/initializers/migrations/0045-user-display-nsfw.ts2
-rw-r--r--server/initializers/migrations/0050-video-language.ts2
-rw-r--r--server/initializers/migrations/0055-video-uuid.ts157
-rw-r--r--server/initializers/migrator.ts4
-rw-r--r--server/lib/friends.ts4
-rw-r--r--server/lib/jobs/handlers/video-transcoder.ts4
-rw-r--r--server/lib/request/request-video-event-scheduler.ts16
-rw-r--r--server/lib/request/request-video-qadu-scheduler.ts16
-rw-r--r--server/middlewares/validators/users.ts15
-rw-r--r--server/middlewares/validators/videos.ts24
-rw-r--r--server/models/user/user-video-rate-interface.ts2
-rw-r--r--server/models/user/user-video-rate.ts2
-rw-r--r--server/models/video/tag.ts2
-rw-r--r--server/models/video/video-abuse-interface.ts2
-rw-r--r--server/models/video/video-abuse.ts4
-rw-r--r--server/models/video/video-blacklist-interface.ts4
-rw-r--r--server/models/video/video-blacklist.ts9
-rw-r--r--server/models/video/video-interface.ts23
-rw-r--r--server/models/video/video.ts100
-rw-r--r--server/tests/api/check-params/remotes.js2
-rw-r--r--server/tests/api/multiple-pods.js31
-rw-r--r--server/tests/api/single-pod.js21
39 files changed, 411 insertions, 141 deletions
diff --git a/server/controllers/api/remote/videos.ts b/server/controllers/api/remote/videos.ts
index 96eab6d52..30771d8c4 100644
--- a/server/controllers/api/remote/videos.ts
+++ b/server/controllers/api/remote/videos.ts
@@ -133,7 +133,7 @@ function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromP
133function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) { 133function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) {
134 134
135 return db.sequelize.transaction(t => { 135 return db.sequelize.transaction(t => {
136 return fetchOwnedVideo(eventData.remoteId) 136 return fetchVideoByUUID(eventData.uuid)
137 .then(videoInstance => { 137 .then(videoInstance => {
138 const options = { transaction: t } 138 const options = { transaction: t }
139 139
@@ -176,7 +176,7 @@ function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInsta
176 return quickAndDirtyUpdatesVideoToFriends(qadusParams, t) 176 return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
177 }) 177 })
178 }) 178 })
179 .then(() => logger.info('Remote video event processed for video %s.', eventData.remoteId)) 179 .then(() => logger.info('Remote video event processed for video %s.', eventData.uuid))
180 .catch(err => { 180 .catch(err => {
181 logger.debug('Cannot process a video event.', err) 181 logger.debug('Cannot process a video event.', err)
182 throw err 182 throw err
@@ -196,7 +196,7 @@ function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodI
196 let videoName 196 let videoName
197 197
198 return db.sequelize.transaction(t => { 198 return db.sequelize.transaction(t => {
199 return fetchRemoteVideo(fromPod.host, videoData.remoteId) 199 return fetchVideoByHostAndUUID(fromPod.host, videoData.uuid)
200 .then(videoInstance => { 200 .then(videoInstance => {
201 const options = { transaction: t } 201 const options = { transaction: t }
202 202
@@ -232,12 +232,12 @@ function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, f
232} 232}
233 233
234function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) { 234function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
235 logger.debug('Adding remote video "%s".', videoToCreateData.remoteId) 235 logger.debug('Adding remote video "%s".', videoToCreateData.uuid)
236 236
237 return db.sequelize.transaction(t => { 237 return db.sequelize.transaction(t => {
238 return db.Video.loadByHostAndRemoteId(fromPod.host, videoToCreateData.remoteId) 238 return db.Video.loadByUUID(videoToCreateData.uuid)
239 .then(video => { 239 .then(video => {
240 if (video) throw new Error('RemoteId and host pair is not unique.') 240 if (video) throw new Error('UUID already exists.')
241 241
242 return undefined 242 return undefined
243 }) 243 })
@@ -257,7 +257,7 @@ function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodI
257 .then(({ author, tagInstances }) => { 257 .then(({ author, tagInstances }) => {
258 const videoData = { 258 const videoData = {
259 name: videoToCreateData.name, 259 name: videoToCreateData.name,
260 remoteId: videoToCreateData.remoteId, 260 uuid: videoToCreateData.uuid,
261 extname: videoToCreateData.extname, 261 extname: videoToCreateData.extname,
262 infoHash: videoToCreateData.infoHash, 262 infoHash: videoToCreateData.infoHash,
263 category: videoToCreateData.category, 263 category: videoToCreateData.category,
@@ -272,7 +272,8 @@ function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodI
272 updatedAt: videoToCreateData.updatedAt, 272 updatedAt: videoToCreateData.updatedAt,
273 views: videoToCreateData.views, 273 views: videoToCreateData.views,
274 likes: videoToCreateData.likes, 274 likes: videoToCreateData.likes,
275 dislikes: videoToCreateData.dislikes 275 dislikes: videoToCreateData.dislikes,
276 remote: true
276 } 277 }
277 278
278 const video = db.Video.build(videoData) 279 const video = db.Video.build(videoData)
@@ -314,10 +315,10 @@ function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpda
314} 315}
315 316
316function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) { 317function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
317 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.remoteId) 318 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
318 319
319 return db.sequelize.transaction(t => { 320 return db.sequelize.transaction(t => {
320 return fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId) 321 return fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid)
321 .then(videoInstance => { 322 .then(videoInstance => {
322 const tags = videoAttributesToUpdate.tags 323 const tags = videoAttributesToUpdate.tags
323 324
@@ -359,18 +360,18 @@ function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, from
359 360
360function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { 361function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
361 // We need the instance because we have to remove some other stuffs (thumbnail etc) 362 // We need the instance because we have to remove some other stuffs (thumbnail etc)
362 return fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId) 363 return fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid)
363 .then(video => { 364 .then(video => {
364 logger.debug('Removing remote video %s.', video.remoteId) 365 logger.debug('Removing remote video %s.', video.uuid)
365 return video.destroy() 366 return video.destroy()
366 }) 367 })
367 .catch(err => { 368 .catch(err => {
368 logger.debug('Could not fetch remote video.', { host: fromPod.host, remoteId: videoToRemoveData.remoteId, error: err.stack }) 369 logger.debug('Could not fetch remote video.', { host: fromPod.host, uuid: videoToRemoveData.uuid, error: err.stack })
369 }) 370 })
370} 371}
371 372
372function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { 373function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
373 return fetchOwnedVideo(reportData.videoRemoteId) 374 return fetchVideoByUUID(reportData.videoUUID)
374 .then(video => { 375 .then(video => {
375 logger.debug('Reporting remote abuse for video %s.', video.id) 376 logger.debug('Reporting remote abuse for video %s.', video.id)
376 377
@@ -386,8 +387,8 @@ function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod
386 .catch(err => logger.error('Cannot create remote abuse video.', err)) 387 .catch(err => logger.error('Cannot create remote abuse video.', err))
387} 388}
388 389
389function fetchOwnedVideo (id: string) { 390function fetchVideoByUUID (id: string) {
390 return db.Video.load(id) 391 return db.Video.loadByUUID(id)
391 .then(video => { 392 .then(video => {
392 if (!video) throw new Error('Video not found') 393 if (!video) throw new Error('Video not found')
393 394
@@ -399,15 +400,15 @@ function fetchOwnedVideo (id: string) {
399 }) 400 })
400} 401}
401 402
402function fetchRemoteVideo (podHost: string, remoteId: string) { 403function fetchVideoByHostAndUUID (podHost: string, uuid: string) {
403 return db.Video.loadByHostAndRemoteId(podHost, remoteId) 404 return db.Video.loadByHostAndUUID(podHost, uuid)
404 .then(video => { 405 .then(video => {
405 if (!video) throw new Error('Video not found') 406 if (!video) throw new Error('Video not found')
406 407
407 return video 408 return video
408 }) 409 })
409 .catch(err => { 410 .catch(err => {
410 logger.error('Cannot load video from host and remote id.', { error: err.stack, podHost, remoteId }) 411 logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid })
411 throw err 412 throw err
412 }) 413 })
413} 414}
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts
index e79480521..6c375cc5b 100644
--- a/server/controllers/api/users.ts
+++ b/server/controllers/api/users.ts
@@ -100,7 +100,7 @@ function getUserInformation (req: express.Request, res: express.Response, next:
100} 100}
101 101
102function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) { 102function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
103 const videoId = '' + req.params.videoId 103 const videoId = +req.params.videoId
104 const userId = +res.locals.oauth.token.User.id 104 const userId = +res.locals.oauth.token.User.id
105 105
106 db.UserVideoRate.load(userId, videoId, null) 106 db.UserVideoRate.load(userId, videoId, null)
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts
index 7d2e3bcfb..5cf0303fb 100644
--- a/server/controllers/api/videos/abuse.ts
+++ b/server/controllers/api/videos/abuse.ts
@@ -62,7 +62,7 @@ function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Respon
62} 62}
63 63
64function reportVideoAbuse (req: express.Request, res: express.Response) { 64function reportVideoAbuse (req: express.Request, res: express.Response) {
65 const videoInstance = res.locals.video 65 const videoInstance = res.locals.video as VideoInstance
66 const reporterUsername = res.locals.oauth.token.User.username 66 const reporterUsername = res.locals.oauth.token.User.username
67 const body: VideoAbuseCreate = req.body 67 const body: VideoAbuseCreate = req.body
68 68
@@ -81,7 +81,7 @@ function reportVideoAbuse (req: express.Request, res: express.Response) {
81 const reportData = { 81 const reportData = {
82 reporterUsername, 82 reporterUsername,
83 reportReason: abuse.reason, 83 reportReason: abuse.reason,
84 videoRemoteId: videoInstance.remoteId 84 videoUUID: videoInstance.uuid
85 } 85 }
86 86
87 return friends.reportAbuseVideoToFriend(reportData, videoInstance, t).then(() => videoInstance) 87 return friends.reportAbuseVideoToFriend(reportData, videoInstance, t).then(() => videoInstance)
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 4ae7ea2ed..e70a5319e 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -176,7 +176,7 @@ function addVideo (req: express.Request, res: express.Response, videoFile: Expre
176 .then(({ author, tagInstances }) => { 176 .then(({ author, tagInstances }) => {
177 const videoData = { 177 const videoData = {
178 name: videoInfos.name, 178 name: videoInfos.name,
179 remoteId: null, 179 remote: false,
180 extname: path.extname(videoFile.filename), 180 extname: path.extname(videoFile.filename),
181 category: videoInfos.category, 181 category: videoInfos.category,
182 licence: videoInfos.licence, 182 licence: videoInfos.licence,
diff --git a/server/controllers/api/videos/rate.ts b/server/controllers/api/videos/rate.ts
index 8456cbaf2..6ddc69817 100644
--- a/server/controllers/api/videos/rate.ts
+++ b/server/controllers/api/videos/rate.ts
@@ -69,7 +69,7 @@ function rateVideo (req: express.Request, res: express.Response) {
69 69
70 // There was a previous rate, update it 70 // There was a previous rate, update it
71 if (previousRate) { 71 if (previousRate) {
72 // We will remove the previous rate, so we will need to remove it from the video attribute 72 // We will remove the previous rate, so we will need to update the video count attribute
73 if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement-- 73 if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement--
74 else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement-- 74 else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement--
75 75
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index d42e8396d..ac722a578 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -78,7 +78,7 @@ function addOpenGraphTags (htmlStringPage: string, video: VideoInstance) {
78 } 78 }
79 79
80 let tagsString = '' 80 let tagsString = ''
81 Object.keys(metaTags).forEach(function (tagName) { 81 Object.keys(metaTags).forEach(tagName => {
82 const tagValue = metaTags[tagName] 82 const tagValue = metaTags[tagName]
83 83
84 tagsString += '<meta property="' + tagName + '" content="' + tagValue + '" />' 84 tagsString += '<meta property="' + tagName + '" content="' + tagValue + '" />'
@@ -89,13 +89,20 @@ function addOpenGraphTags (htmlStringPage: string, video: VideoInstance) {
89 89
90function generateWatchHtmlPage (req: express.Request, res: express.Response, next: express.NextFunction) { 90function generateWatchHtmlPage (req: express.Request, res: express.Response, next: express.NextFunction) {
91 const videoId = '' + req.params.id 91 const videoId = '' + req.params.id
92 let videoPromise: Promise<VideoInstance>
92 93
93 // Let Angular application handle errors 94 // Let Angular application handle errors
94 if (!validator.isUUID(videoId, 4)) return res.sendFile(indexPath) 95 if (validator.isUUID(videoId, 4)) {
96 videoPromise = db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(videoId)
97 } else if (validator.isInt(videoId)) {
98 videoPromise = db.Video.loadAndPopulateAuthorAndPodAndTags(+videoId)
99 } else {
100 return res.sendFile(indexPath)
101 }
95 102
96 Promise.all([ 103 Promise.all([
97 readFileBufferPromise(indexPath), 104 readFileBufferPromise(indexPath),
98 db.Video.loadAndPopulateAuthorAndPodAndTags(videoId) 105 videoPromise
99 ]) 106 ])
100 .then(([ file, video ]) => { 107 .then(([ file, video ]) => {
101 file = file as Buffer 108 file = file as Buffer
diff --git a/server/helpers/custom-validators/remote/videos.ts b/server/helpers/custom-validators/remote/videos.ts
index 1df7316aa..e14673cb3 100644
--- a/server/helpers/custom-validators/remote/videos.ts
+++ b/server/helpers/custom-validators/remote/videos.ts
@@ -9,7 +9,7 @@ import { isArray } from '../misc'
9import { 9import {
10 isVideoAuthorValid, 10 isVideoAuthorValid,
11 isVideoThumbnailDataValid, 11 isVideoThumbnailDataValid,
12 isVideoRemoteIdValid, 12 isVideoUUIDValid,
13 isVideoAbuseReasonValid, 13 isVideoAbuseReasonValid,
14 isVideoAbuseReporterUsernameValid, 14 isVideoAbuseReporterUsernameValid,
15 isVideoViewsValid, 15 isVideoViewsValid,
@@ -50,11 +50,11 @@ function isEachRemoteRequestVideosValid (requests: any[]) {
50 ) || 50 ) ||
51 ( 51 (
52 isRequestTypeRemoveValid(request.type) && 52 isRequestTypeRemoveValid(request.type) &&
53 isVideoRemoteIdValid(video.remoteId) 53 isVideoUUIDValid(video.uuid)
54 ) || 54 ) ||
55 ( 55 (
56 isRequestTypeReportAbuseValid(request.type) && 56 isRequestTypeReportAbuseValid(request.type) &&
57 isVideoRemoteIdValid(request.data.videoRemoteId) && 57 isVideoUUIDValid(request.data.videoUUID) &&
58 isVideoAbuseReasonValid(request.data.reportReason) && 58 isVideoAbuseReasonValid(request.data.reportReason) &&
59 isVideoAbuseReporterUsernameValid(request.data.reporterUsername) 59 isVideoAbuseReporterUsernameValid(request.data.reporterUsername)
60 ) 60 )
@@ -69,7 +69,7 @@ function isEachRemoteRequestVideosQaduValid (requests: any[]) {
69 if (!video) return false 69 if (!video) return false
70 70
71 return ( 71 return (
72 isVideoRemoteIdValid(video.remoteId) && 72 isVideoUUIDValid(video.uuid) &&
73 (has(video, 'views') === false || isVideoViewsValid(video.views)) && 73 (has(video, 'views') === false || isVideoViewsValid(video.views)) &&
74 (has(video, 'likes') === false || isVideoLikesValid(video.likes)) && 74 (has(video, 'likes') === false || isVideoLikesValid(video.likes)) &&
75 (has(video, 'dislikes') === false || isVideoDislikesValid(video.dislikes)) 75 (has(video, 'dislikes') === false || isVideoDislikesValid(video.dislikes))
@@ -85,7 +85,7 @@ function isEachRemoteRequestVideosEventsValid (requests: any[]) {
85 if (!eventData) return false 85 if (!eventData) return false
86 86
87 return ( 87 return (
88 isVideoRemoteIdValid(eventData.remoteId) && 88 isVideoUUIDValid(eventData.uuid) &&
89 values(REQUEST_VIDEO_EVENT_TYPES).indexOf(eventData.eventType) !== -1 && 89 values(REQUEST_VIDEO_EVENT_TYPES).indexOf(eventData.eventType) !== -1 &&
90 isVideoEventCountValid(eventData.count) 90 isVideoEventCountValid(eventData.count)
91 ) 91 )
@@ -124,7 +124,7 @@ function isCommonVideoAttributesValid (video: any) {
124 isVideoInfoHashValid(video.infoHash) && 124 isVideoInfoHashValid(video.infoHash) &&
125 isVideoNameValid(video.name) && 125 isVideoNameValid(video.name) &&
126 isVideoTagsValid(video.tags) && 126 isVideoTagsValid(video.tags) &&
127 isVideoRemoteIdValid(video.remoteId) && 127 isVideoUUIDValid(video.uuid) &&
128 isVideoExtnameValid(video.extname) && 128 isVideoExtnameValid(video.extname) &&
129 isVideoViewsValid(video.views) && 129 isVideoViewsValid(video.views) &&
130 isVideoLikesValid(video.likes) && 130 isVideoLikesValid(video.likes) &&
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts
index 72d226e81..e335b09d1 100644
--- a/server/helpers/custom-validators/videos.ts
+++ b/server/helpers/custom-validators/videos.ts
@@ -17,6 +17,10 @@ const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
17const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES 17const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
18const VIDEO_EVENTS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_EVENTS 18const VIDEO_EVENTS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_EVENTS
19 19
20function isVideoIdOrUUIDValid (value: string) {
21 return validator.isInt(value) || isVideoUUIDValid(value)
22}
23
20function isVideoAuthorValid (value: string) { 24function isVideoAuthorValid (value: string) {
21 return isUserUsernameValid(value) 25 return isUserUsernameValid(value)
22} 26}
@@ -77,8 +81,8 @@ function isVideoThumbnailDataValid (value: string) {
77 return exists(value) && validator.isByteLength(value, VIDEOS_CONSTRAINTS_FIELDS.THUMBNAIL_DATA) 81 return exists(value) && validator.isByteLength(value, VIDEOS_CONSTRAINTS_FIELDS.THUMBNAIL_DATA)
78} 82}
79 83
80function isVideoRemoteIdValid (value: string) { 84function isVideoUUIDValid (value: string) {
81 return exists(value) && validator.isUUID(value, 4) 85 return exists(value) && validator.isUUID('' + value, 4)
82} 86}
83 87
84function isVideoAbuseReasonValid (value: string) { 88function isVideoAbuseReasonValid (value: string) {
@@ -127,6 +131,7 @@ function isVideoFile (value: string, files: { [ fieldname: string ]: Express.Mul
127// --------------------------------------------------------------------------- 131// ---------------------------------------------------------------------------
128 132
129export { 133export {
134 isVideoIdOrUUIDValid,
130 isVideoAuthorValid, 135 isVideoAuthorValid,
131 isVideoDateValid, 136 isVideoDateValid,
132 isVideoCategoryValid, 137 isVideoCategoryValid,
@@ -141,7 +146,7 @@ export {
141 isVideoThumbnailValid, 146 isVideoThumbnailValid,
142 isVideoThumbnailDataValid, 147 isVideoThumbnailDataValid,
143 isVideoExtnameValid, 148 isVideoExtnameValid,
144 isVideoRemoteIdValid, 149 isVideoUUIDValid,
145 isVideoAbuseReasonValid, 150 isVideoAbuseReasonValid,
146 isVideoAbuseReporterUsernameValid, 151 isVideoAbuseReporterUsernameValid,
147 isVideoFile, 152 isVideoFile,
@@ -155,6 +160,7 @@ export {
155declare global { 160declare global {
156 namespace ExpressValidator { 161 namespace ExpressValidator {
157 export interface Validator { 162 export interface Validator {
163 isVideoIdOrUUIDValid,
158 isVideoAuthorValid, 164 isVideoAuthorValid,
159 isVideoDateValid, 165 isVideoDateValid,
160 isVideoCategoryValid, 166 isVideoCategoryValid,
@@ -169,7 +175,7 @@ declare global {
169 isVideoThumbnailValid, 175 isVideoThumbnailValid,
170 isVideoThumbnailDataValid, 176 isVideoThumbnailDataValid,
171 isVideoExtnameValid, 177 isVideoExtnameValid,
172 isVideoRemoteIdValid, 178 isVideoUUIDValid,
173 isVideoAbuseReasonValid, 179 isVideoAbuseReasonValid,
174 isVideoAbuseReporterUsernameValid, 180 isVideoAbuseReporterUsernameValid,
175 isVideoFile, 181 isVideoFile,
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 2792d3228..f087b7476 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -15,7 +15,7 @@ import {
15 15
16// --------------------------------------------------------------------------- 16// ---------------------------------------------------------------------------
17 17
18const LAST_MIGRATION_VERSION = 50 18const LAST_MIGRATION_VERSION = 55
19 19
20// --------------------------------------------------------------------------- 20// ---------------------------------------------------------------------------
21 21
diff --git a/server/initializers/migrations/0005-email-pod.ts b/server/initializers/migrations/0005-email-pod.ts
index ceefaad4a..ab60f3adb 100644
--- a/server/initializers/migrations/0005-email-pod.ts
+++ b/server/initializers/migrations/0005-email-pod.ts
@@ -26,7 +26,7 @@ function up (utils: {
26 }) 26 })
27} 27}
28 28
29function down (options, callback) { 29function down (options) {
30 throw new Error('Not implemented.') 30 throw new Error('Not implemented.')
31} 31}
32 32
diff --git a/server/initializers/migrations/0010-email-user.ts b/server/initializers/migrations/0010-email-user.ts
index e8865acdb..33d13ce55 100644
--- a/server/initializers/migrations/0010-email-user.ts
+++ b/server/initializers/migrations/0010-email-user.ts
@@ -25,7 +25,7 @@ function up (utils: {
25 }) 25 })
26} 26}
27 27
28function down (options, callback) { 28function down (options) {
29 throw new Error('Not implemented.') 29 throw new Error('Not implemented.')
30} 30}
31 31
diff --git a/server/initializers/migrations/0015-video-views.ts b/server/initializers/migrations/0015-video-views.ts
index df274d817..25164ff4d 100644
--- a/server/initializers/migrations/0015-video-views.ts
+++ b/server/initializers/migrations/0015-video-views.ts
@@ -17,7 +17,7 @@ function up (utils: {
17 return q.addColumn('Videos', 'views', data) 17 return q.addColumn('Videos', 'views', data)
18} 18}
19 19
20function down (options, callback) { 20function down (options) {
21 throw new Error('Not implemented.') 21 throw new Error('Not implemented.')
22} 22}
23 23
diff --git a/server/initializers/migrations/0020-video-likes.ts b/server/initializers/migrations/0020-video-likes.ts
index 3d7182d0a..945be5a98 100644
--- a/server/initializers/migrations/0020-video-likes.ts
+++ b/server/initializers/migrations/0020-video-likes.ts
@@ -17,7 +17,7 @@ function up (utils: {
17 return q.addColumn('Videos', 'likes', data) 17 return q.addColumn('Videos', 'likes', data)
18} 18}
19 19
20function down (options, callback) { 20function down (options) {
21 throw new Error('Not implemented.') 21 throw new Error('Not implemented.')
22} 22}
23 23
diff --git a/server/initializers/migrations/0025-video-dislikes.ts b/server/initializers/migrations/0025-video-dislikes.ts
index ed41095dc..27144c437 100644
--- a/server/initializers/migrations/0025-video-dislikes.ts
+++ b/server/initializers/migrations/0025-video-dislikes.ts
@@ -17,7 +17,7 @@ function up (utils: {
17 return q.addColumn('Videos', 'dislikes', data) 17 return q.addColumn('Videos', 'dislikes', data)
18} 18}
19 19
20function down (options, callback) { 20function down (options) {
21 throw new Error('Not implemented.') 21 throw new Error('Not implemented.')
22} 22}
23 23
diff --git a/server/initializers/migrations/0030-video-category.ts b/server/initializers/migrations/0030-video-category.ts
index f5adee8f9..41bc1aa98 100644
--- a/server/initializers/migrations/0030-video-category.ts
+++ b/server/initializers/migrations/0030-video-category.ts
@@ -22,7 +22,7 @@ function up (utils: {
22 }) 22 })
23} 23}
24 24
25function down (options, callback) { 25function down (options) {
26 throw new Error('Not implemented.') 26 throw new Error('Not implemented.')
27} 27}
28 28
diff --git a/server/initializers/migrations/0035-video-licence.ts b/server/initializers/migrations/0035-video-licence.ts
index 00c64d8e7..7ab49e147 100644
--- a/server/initializers/migrations/0035-video-licence.ts
+++ b/server/initializers/migrations/0035-video-licence.ts
@@ -21,7 +21,7 @@ function up (utils: {
21 }) 21 })
22} 22}
23 23
24function down (options, callback) { 24function down (options) {
25 throw new Error('Not implemented.') 25 throw new Error('Not implemented.')
26} 26}
27 27
diff --git a/server/initializers/migrations/0040-video-nsfw.ts b/server/initializers/migrations/0040-video-nsfw.ts
index 046876b61..0460e661d 100644
--- a/server/initializers/migrations/0040-video-nsfw.ts
+++ b/server/initializers/migrations/0040-video-nsfw.ts
@@ -22,7 +22,7 @@ function up (utils: {
22 }) 22 })
23} 23}
24 24
25function down (options, callback) { 25function down (options) {
26 throw new Error('Not implemented.') 26 throw new Error('Not implemented.')
27} 27}
28 28
diff --git a/server/initializers/migrations/0045-user-display-nsfw.ts b/server/initializers/migrations/0045-user-display-nsfw.ts
index 75bd3bbea..aef420f0e 100644
--- a/server/initializers/migrations/0045-user-display-nsfw.ts
+++ b/server/initializers/migrations/0045-user-display-nsfw.ts
@@ -17,7 +17,7 @@ function up (utils: {
17 return q.addColumn('Users', 'displayNSFW', data) 17 return q.addColumn('Users', 'displayNSFW', data)
18} 18}
19 19
20function down (options, callback) { 20function down (options) {
21 throw new Error('Not implemented.') 21 throw new Error('Not implemented.')
22} 22}
23 23
diff --git a/server/initializers/migrations/0050-video-language.ts b/server/initializers/migrations/0050-video-language.ts
index ed08f5866..796fa5f95 100644
--- a/server/initializers/migrations/0050-video-language.ts
+++ b/server/initializers/migrations/0050-video-language.ts
@@ -17,7 +17,7 @@ function up (utils: {
17 return q.addColumn('Videos', 'language', data) 17 return q.addColumn('Videos', 'language', data)
18} 18}
19 19
20function down (options, callback) { 20function down (options) {
21 throw new Error('Not implemented.') 21 throw new Error('Not implemented.')
22} 22}
23 23
diff --git a/server/initializers/migrations/0055-video-uuid.ts b/server/initializers/migrations/0055-video-uuid.ts
new file mode 100644
index 000000000..9bc65917c
--- /dev/null
+++ b/server/initializers/migrations/0055-video-uuid.ts
@@ -0,0 +1,157 @@
1import * as Sequelize from 'sequelize'
2import * as Promise from 'bluebird'
3
4function up (utils: {
5 transaction: Sequelize.Transaction,
6 queryInterface: Sequelize.QueryInterface,
7 sequelize: Sequelize.Sequelize
8}): Promise<void> {
9 const q = utils.queryInterface
10
11 const dataUUID = {
12 type: Sequelize.UUID,
13 defaultValue: Sequelize.UUIDV4,
14 allowNull: true
15 }
16
17 return q.addColumn('Videos', 'uuid', dataUUID)
18 .then(() => {
19 const query = 'UPDATE "Videos" SET "uuid" = "id" WHERE "remoteId" IS NULL'
20 return utils.sequelize.query(query)
21 })
22 .then(() => {
23 const query = 'UPDATE "Videos" SET "uuid" = "remoteId" WHERE "remoteId" IS NOT NULL'
24 return utils.sequelize.query(query)
25 })
26 .then(() => {
27 dataUUID.defaultValue = null
28
29 return q.changeColumn('Videos', 'uuid', dataUUID)
30 })
31 .then(() => {
32 return removeForeignKey(utils.sequelize, 'RequestVideoQadus')
33 })
34 .then(() => {
35 return removeForeignKey(utils.sequelize, 'RequestVideoEvents')
36 })
37 .then(() => {
38 return removeForeignKey(utils.sequelize, 'BlacklistedVideos')
39 })
40 .then(() => {
41 return removeForeignKey(utils.sequelize, 'UserVideoRates')
42 })
43 .then(() => {
44 return removeForeignKey(utils.sequelize, 'VideoAbuses')
45 })
46 .then(() => {
47 return removeForeignKey(utils.sequelize, 'VideoTags')
48 })
49 .then(() => {
50 const query = 'ALTER TABLE "Videos" DROP CONSTRAINT "Videos_pkey"'
51 return utils.sequelize.query(query)
52 })
53 .then(() => {
54 const query = 'ALTER TABLE "Videos" ADD COLUMN "id2" SERIAL PRIMARY KEY'
55 return utils.sequelize.query(query)
56 })
57 .then(() => {
58 return q.renameColumn('Videos', 'id', 'oldId')
59 })
60 .then(() => {
61 return q.renameColumn('Videos', 'id2', 'id')
62 })
63 .then(() => {
64 return changeForeignKey(q, utils.sequelize, 'RequestVideoQadus', false)
65 })
66 .then(() => {
67 return changeForeignKey(q, utils.sequelize, 'RequestVideoEvents', false)
68 })
69 .then(() => {
70 return changeForeignKey(q, utils.sequelize, 'BlacklistedVideos', false)
71 })
72 .then(() => {
73 return changeForeignKey(q, utils.sequelize, 'UserVideoRates', false)
74 })
75 .then(() => {
76 return changeForeignKey(q, utils.sequelize, 'VideoAbuses', false)
77 })
78 .then(() => {
79 return changeForeignKey(q, utils.sequelize, 'VideoTags', true)
80 })
81 .then(() => {
82 return q.removeColumn('Videos', 'oldId')
83 })
84 .then(() => {
85 const dataRemote = {
86 type: Sequelize.BOOLEAN,
87 defaultValue: false,
88 allowNull: false
89 }
90 return q.addColumn('Videos', 'remote', dataRemote)
91 })
92 .then(() => {
93 const query = 'UPDATE "Videos" SET "remote" = false WHERE "remoteId" IS NULL'
94 return utils.sequelize.query(query)
95 })
96 .then(() => {
97 const query = 'UPDATE "Videos" SET "remote" = true WHERE "remoteId" IS NOT NULL'
98 return utils.sequelize.query(query)
99 })
100 .then(() => {
101 return q.removeColumn('Videos', 'remoteId')
102 })
103}
104
105function down (options) {
106 throw new Error('Not implemented.')
107}
108
109function removeForeignKey (sequelize: Sequelize.Sequelize, tableName: string) {
110 const query = 'ALTER TABLE "' + tableName + '" DROP CONSTRAINT "' + tableName + '_videoId_fkey' + '"'
111 return sequelize.query(query)
112}
113
114function changeForeignKey (q: Sequelize.QueryInterface, sequelize: Sequelize.Sequelize, tableName: string, allowNull: boolean) {
115 const data = {
116 type: Sequelize.INTEGER,
117 allowNull: true
118 }
119
120 return q.addColumn(tableName, 'videoId2', data)
121 .then(() => {
122 const query = 'UPDATE "' + tableName + '" SET "videoId2" = ' +
123 '(SELECT "id" FROM "Videos" WHERE "' + tableName + '"."videoId" = "Videos"."oldId")'
124 return sequelize.query(query)
125 })
126 .then(() => {
127 if (allowNull === false) {
128 data.allowNull = false
129
130 return q.changeColumn(tableName, 'videoId2', data)
131 }
132
133 return Promise.resolve()
134 })
135 .then(() => {
136 return q.removeColumn(tableName, 'videoId')
137 })
138 .then(() => {
139 return q.renameColumn(tableName, 'videoId2', 'videoId')
140 })
141 .then(() => {
142 return q.addIndex(tableName, [ 'videoId' ])
143 })
144 .then(() => {
145 const constraintName = tableName + '_videoId_fkey'
146 const query = 'ALTER TABLE "' + tableName + '" ' +
147 ' ADD CONSTRAINT "' + constraintName + '"' +
148 ' FOREIGN KEY ("videoId") REFERENCES "Videos" ON DELETE CASCADE'
149
150 return sequelize.query(query)
151 })
152}
153
154export {
155 up,
156 down
157}
diff --git a/server/initializers/migrator.ts b/server/initializers/migrator.ts
index 3184ec920..4b3be6d16 100644
--- a/server/initializers/migrator.ts
+++ b/server/initializers/migrator.ts
@@ -96,10 +96,10 @@ function executeMigration (actualVersion: number, entity: { version: string, scr
96 sequelize: db.sequelize 96 sequelize: db.sequelize
97 } 97 }
98 98
99 migrationScript.up(options) 99 return migrationScript.up(options)
100 .then(() => { 100 .then(() => {
101 // Update the new migration version 101 // Update the new migration version
102 db.Application.updateMigrationVersion(versionScript, t) 102 return db.Application.updateMigrationVersion(versionScript, t)
103 }) 103 })
104 }) 104 })
105} 105}
diff --git a/server/lib/friends.ts b/server/lib/friends.ts
index a65820191..cbdc60441 100644
--- a/server/lib/friends.ts
+++ b/server/lib/friends.ts
@@ -43,8 +43,8 @@ import {
43 Pod as FormatedPod 43 Pod as FormatedPod
44} from '../../shared' 44} from '../../shared'
45 45
46type QaduParam = { videoId: string, type: RequestVideoQaduType } 46type QaduParam = { videoId: number, type: RequestVideoQaduType }
47type EventParam = { videoId: string, type: RequestVideoEventType } 47type EventParam = { videoId: number, type: RequestVideoEventType }
48 48
49const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS] 49const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
50 50
diff --git a/server/lib/jobs/handlers/video-transcoder.ts b/server/lib/jobs/handlers/video-transcoder.ts
index eeb2d57b0..0d32dfd2f 100644
--- a/server/lib/jobs/handlers/video-transcoder.ts
+++ b/server/lib/jobs/handlers/video-transcoder.ts
@@ -3,8 +3,8 @@ import { logger } from '../../../helpers'
3import { addVideoToFriends } from '../../../lib' 3import { addVideoToFriends } from '../../../lib'
4import { VideoInstance } from '../../../models' 4import { VideoInstance } from '../../../models'
5 5
6function process (data: { id: string }) { 6function process (data: { videoUUID: string }) {
7 return db.Video.loadAndPopulateAuthorAndPodAndTags(data.id).then(video => { 7 return db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(data.videoUUID).then(video => {
8 return video.transcodeVideofile().then(() => video) 8 return video.transcodeVideofile().then(() => video)
9 }) 9 })
10} 10}
diff --git a/server/lib/request/request-video-event-scheduler.ts b/server/lib/request/request-video-event-scheduler.ts
index 8a008c51b..680232732 100644
--- a/server/lib/request/request-video-event-scheduler.ts
+++ b/server/lib/request/request-video-event-scheduler.ts
@@ -12,7 +12,7 @@ import { RequestVideoEventType, RemoteVideoEventRequest, RemoteVideoEventType }
12 12
13export type RequestVideoEventSchedulerOptions = { 13export type RequestVideoEventSchedulerOptions = {
14 type: RequestVideoEventType 14 type: RequestVideoEventType
15 videoId: string 15 videoId: number
16 count?: number 16 count?: number
17 transaction?: Sequelize.Transaction 17 transaction?: Sequelize.Transaction
18} 18}
@@ -49,7 +49,7 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler<RequestsVideoE
49 */ 49 */
50 const eventsPerVideoPerPod: { 50 const eventsPerVideoPerPod: {
51 [ podId: string ]: { 51 [ podId: string ]: {
52 [ videoRemoteId: string ]: { 52 [ videoUUID: string ]: {
53 views?: number 53 views?: number
54 likes?: number 54 likes?: number
55 dislikes?: number 55 dislikes?: number
@@ -74,10 +74,10 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler<RequestsVideoE
74 requestsToMakeGrouped[toPodId].ids.push(eventToProcess.id) 74 requestsToMakeGrouped[toPodId].ids.push(eventToProcess.id)
75 75
76 const eventsPerVideo = eventsPerVideoPerPod[toPodId] 76 const eventsPerVideo = eventsPerVideoPerPod[toPodId]
77 const remoteId = eventToProcess.video.remoteId 77 const uuid = eventToProcess.video.uuid
78 if (!eventsPerVideo[remoteId]) eventsPerVideo[remoteId] = {} 78 if (!eventsPerVideo[uuid]) eventsPerVideo[uuid] = {}
79 79
80 const events = eventsPerVideo[remoteId] 80 const events = eventsPerVideo[uuid]
81 if (!events[eventToProcess.type]) events[eventToProcess.type] = 0 81 if (!events[eventToProcess.type]) events[eventToProcess.type] = 0
82 82
83 events[eventToProcess.type] += eventToProcess.count 83 events[eventToProcess.type] += eventToProcess.count
@@ -88,13 +88,13 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler<RequestsVideoE
88 Object.keys(eventsPerVideoPerPod).forEach(toPodId => { 88 Object.keys(eventsPerVideoPerPod).forEach(toPodId => {
89 const eventsForPod = eventsPerVideoPerPod[toPodId] 89 const eventsForPod = eventsPerVideoPerPod[toPodId]
90 90
91 Object.keys(eventsForPod).forEach(remoteId => { 91 Object.keys(eventsForPod).forEach(uuid => {
92 const eventsForVideo = eventsForPod[remoteId] 92 const eventsForVideo = eventsForPod[uuid]
93 93
94 Object.keys(eventsForVideo).forEach(eventType => { 94 Object.keys(eventsForVideo).forEach(eventType => {
95 requestsToMakeGrouped[toPodId].datas.push({ 95 requestsToMakeGrouped[toPodId].datas.push({
96 data: { 96 data: {
97 remoteId, 97 uuid,
98 eventType: eventType as RemoteVideoEventType, 98 eventType: eventType as RemoteVideoEventType,
99 count: +eventsForVideo[eventType] 99 count: +eventsForVideo[eventType]
100 } 100 }
diff --git a/server/lib/request/request-video-qadu-scheduler.ts b/server/lib/request/request-video-qadu-scheduler.ts
index 988165170..afb9d5c23 100644
--- a/server/lib/request/request-video-qadu-scheduler.ts
+++ b/server/lib/request/request-video-qadu-scheduler.ts
@@ -21,8 +21,8 @@ interface RequestsObjectsCustom<U> extends RequestsObjects<U> {
21 datas: U[] 21 datas: U[]
22 22
23 videos: { 23 videos: {
24 [ id: string ]: { 24 [ uuid: string ]: {
25 remoteId: string 25 uuid: string
26 likes?: number 26 likes?: number
27 dislikes?: number 27 dislikes?: number
28 views?: number 28 views?: number
@@ -33,7 +33,7 @@ interface RequestsObjectsCustom<U> extends RequestsObjects<U> {
33 33
34export type RequestVideoQaduSchedulerOptions = { 34export type RequestVideoQaduSchedulerOptions = {
35 type: RequestVideoQaduType 35 type: RequestVideoQaduType
36 videoId: string 36 videoId: number
37 transaction?: Sequelize.Transaction 37 transaction?: Sequelize.Transaction
38} 38}
39 39
@@ -78,7 +78,7 @@ class RequestVideoQaduScheduler extends AbstractRequestScheduler<RequestsVideoQa
78 78
79 // Maybe another attribute was filled for this video 79 // Maybe another attribute was filled for this video
80 let videoData = requestsToMakeGrouped[hashKey].videos[video.id] 80 let videoData = requestsToMakeGrouped[hashKey].videos[video.id]
81 if (!videoData) videoData = { remoteId: null } 81 if (!videoData) videoData = { uuid: null }
82 82
83 switch (request.type) { 83 switch (request.type) {
84 case REQUEST_VIDEO_QADU_TYPES.LIKES: 84 case REQUEST_VIDEO_QADU_TYPES.LIKES:
@@ -98,8 +98,8 @@ class RequestVideoQaduScheduler extends AbstractRequestScheduler<RequestsVideoQa
98 return 98 return
99 } 99 }
100 100
101 // Do not forget the remoteId so the remote pod can identify the video 101 // Do not forget the uuid so the remote pod can identify the video
102 videoData.remoteId = video.id 102 videoData.uuid = video.uuid
103 requestsToMakeGrouped[hashKey].ids.push(request.id) 103 requestsToMakeGrouped[hashKey].ids.push(request.id)
104 104
105 // Maybe there are multiple quick and dirty update for the same video 105 // Maybe there are multiple quick and dirty update for the same video
@@ -110,8 +110,8 @@ class RequestVideoQaduScheduler extends AbstractRequestScheduler<RequestsVideoQa
110 110
111 // Now we deduped similar quick and dirty updates, we can build our requests datas 111 // Now we deduped similar quick and dirty updates, we can build our requests datas
112 Object.keys(requestsToMakeGrouped).forEach(hashKey => { 112 Object.keys(requestsToMakeGrouped).forEach(hashKey => {
113 Object.keys(requestsToMakeGrouped[hashKey].videos).forEach(videoId => { 113 Object.keys(requestsToMakeGrouped[hashKey].videos).forEach(videoUUID => {
114 const videoData = requestsToMakeGrouped[hashKey].videos[videoId] 114 const videoData = requestsToMakeGrouped[hashKey].videos[videoUUID]
115 115
116 requestsToMakeGrouped[hashKey].datas.push({ 116 requestsToMakeGrouped[hashKey].datas.push({
117 data: videoData 117 data: videoData
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts
index 9db4fff77..90a46752c 100644
--- a/server/middlewares/validators/users.ts
+++ b/server/middlewares/validators/users.ts
@@ -1,9 +1,12 @@
1import 'express-validator' 1import 'express-validator'
2import * as express from 'express' 2import * as express from 'express'
3import * as Promise from 'bluebird'
4import * as validator from 'validator'
3 5
4import { database as db } from '../../initializers/database' 6import { database as db } from '../../initializers/database'
5import { checkErrors } from './utils' 7import { checkErrors } from './utils'
6import { logger } from '../../helpers' 8import { logger } from '../../helpers'
9import { VideoInstance } from '../../models'
7 10
8function usersAddValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 11function usersAddValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
9 req.checkBody('username', 'Should have a valid username').isUserUsernameValid() 12 req.checkBody('username', 'Should have a valid username').isUserUsernameValid()
@@ -59,12 +62,20 @@ function usersUpdateValidator (req: express.Request, res: express.Response, next
59} 62}
60 63
61function usersVideoRatingValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 64function usersVideoRatingValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
62 req.checkParams('videoId', 'Should have a valid video id').notEmpty().isUUID(4) 65 req.checkParams('videoId', 'Should have a valid video id').notEmpty().isVideoIdOrUUIDValid()
63 66
64 logger.debug('Checking usersVideoRating parameters', { parameters: req.params }) 67 logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
65 68
66 checkErrors(req, res, function () { 69 checkErrors(req, res, function () {
67 db.Video.load(req.params.videoId) 70 let videoPromise: Promise<VideoInstance>
71
72 if (validator.isUUID(req.params.videoId)) {
73 videoPromise = db.Video.loadByUUID(req.params.videoId)
74 } else {
75 videoPromise = db.Video.load(req.params.videoId)
76 }
77
78 videoPromise
68 .then(video => { 79 .then(video => {
69 if (!video) return res.status(404).send('Video not found') 80 if (!video) return res.status(404).send('Video not found')
70 81
diff --git a/server/middlewares/validators/videos.ts b/server/middlewares/validators/videos.ts
index 013466487..0a88e064e 100644
--- a/server/middlewares/validators/videos.ts
+++ b/server/middlewares/validators/videos.ts
@@ -1,10 +1,13 @@
1import 'express-validator' 1import 'express-validator'
2import * as express from 'express' 2import * as express from 'express'
3import * as Promise from 'bluebird'
4import * as validator from 'validator'
3 5
4import { database as db } from '../../initializers/database' 6import { database as db } from '../../initializers/database'
5import { checkErrors } from './utils' 7import { checkErrors } from './utils'
6import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers' 8import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers'
7import { logger, isVideoDurationValid } from '../../helpers' 9import { logger, isVideoDurationValid } from '../../helpers'
10import { VideoInstance } from '../../models'
8 11
9function videosAddValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 12function videosAddValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
10 // FIXME: Don't write an error message, it seems there is a bug with express-validator 13 // FIXME: Don't write an error message, it seems there is a bug with express-validator
@@ -40,7 +43,7 @@ function videosAddValidator (req: express.Request, res: express.Response, next:
40} 43}
41 44
42function videosUpdateValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 45function videosUpdateValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
43 req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4) 46 req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
44 req.checkBody('name', 'Should have a valid name').optional().isVideoNameValid() 47 req.checkBody('name', 'Should have a valid name').optional().isVideoNameValid()
45 req.checkBody('category', 'Should have a valid category').optional().isVideoCategoryValid() 48 req.checkBody('category', 'Should have a valid category').optional().isVideoCategoryValid()
46 req.checkBody('licence', 'Should have a valid licence').optional().isVideoLicenceValid() 49 req.checkBody('licence', 'Should have a valid licence').optional().isVideoLicenceValid()
@@ -68,7 +71,7 @@ function videosUpdateValidator (req: express.Request, res: express.Response, nex
68} 71}
69 72
70function videosGetValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 73function videosGetValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
71 req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4) 74 req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
72 75
73 logger.debug('Checking videosGet parameters', { parameters: req.params }) 76 logger.debug('Checking videosGet parameters', { parameters: req.params })
74 77
@@ -78,7 +81,7 @@ function videosGetValidator (req: express.Request, res: express.Response, next:
78} 81}
79 82
80function videosRemoveValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 83function videosRemoveValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
81 req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4) 84 req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
82 85
83 logger.debug('Checking videosRemove parameters', { parameters: req.params }) 86 logger.debug('Checking videosRemove parameters', { parameters: req.params })
84 87
@@ -105,7 +108,7 @@ function videosSearchValidator (req: express.Request, res: express.Response, nex
105} 108}
106 109
107function videoAbuseReportValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 110function videoAbuseReportValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
108 req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4) 111 req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
109 req.checkBody('reason', 'Should have a valid reason').isVideoAbuseReasonValid() 112 req.checkBody('reason', 'Should have a valid reason').isVideoAbuseReasonValid()
110 113
111 logger.debug('Checking videoAbuseReport parameters', { parameters: req.body }) 114 logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
@@ -116,7 +119,7 @@ function videoAbuseReportValidator (req: express.Request, res: express.Response,
116} 119}
117 120
118function videoRateValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 121function videoRateValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
119 req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4) 122 req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
120 req.checkBody('rating', 'Should have a valid rate type').isVideoRatingTypeValid() 123 req.checkBody('rating', 'Should have a valid rate type').isVideoRatingTypeValid()
121 124
122 logger.debug('Checking videoRate parameters', { parameters: req.body }) 125 logger.debug('Checking videoRate parameters', { parameters: req.body })
@@ -127,7 +130,7 @@ function videoRateValidator (req: express.Request, res: express.Response, next:
127} 130}
128 131
129function videosBlacklistValidator (req: express.Request, res: express.Response, next: express.NextFunction) { 132function videosBlacklistValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
130 req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4) 133 req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
131 134
132 logger.debug('Checking videosBlacklist parameters', { parameters: req.params }) 135 logger.debug('Checking videosBlacklist parameters', { parameters: req.params })
133 136
@@ -157,7 +160,14 @@ export {
157// --------------------------------------------------------------------------- 160// ---------------------------------------------------------------------------
158 161
159function checkVideoExists (id: string, res: express.Response, callback: () => void) { 162function checkVideoExists (id: string, res: express.Response, callback: () => void) {
160 db.Video.loadAndPopulateAuthorAndPodAndTags(id).then(video => { 163 let promise: Promise<VideoInstance>
164 if (validator.isInt(id)) {
165 promise = db.Video.loadAndPopulateAuthorAndPodAndTags(+id)
166 } else { // UUID
167 promise = db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(id)
168 }
169
170 promise.then(video => {
161 if (!video) return res.status(404).send('Video not found') 171 if (!video) return res.status(404).send('Video not found')
162 172
163 res.locals.video = video 173 res.locals.video = video
diff --git a/server/models/user/user-video-rate-interface.ts b/server/models/user/user-video-rate-interface.ts
index f501f08b7..4e6efc01a 100644
--- a/server/models/user/user-video-rate-interface.ts
+++ b/server/models/user/user-video-rate-interface.ts
@@ -4,7 +4,7 @@ import * as Promise from 'bluebird'
4import { VideoRateType } from '../../../shared/models/videos/video-rate.type' 4import { VideoRateType } from '../../../shared/models/videos/video-rate.type'
5 5
6export namespace UserVideoRateMethods { 6export namespace UserVideoRateMethods {
7 export type Load = (userId: number, videoId: string, transaction: Sequelize.Transaction) => Promise<UserVideoRateInstance> 7 export type Load = (userId: number, videoId: number, transaction: Sequelize.Transaction) => Promise<UserVideoRateInstance>
8} 8}
9 9
10export interface UserVideoRateClass { 10export interface UserVideoRateClass {
diff --git a/server/models/user/user-video-rate.ts b/server/models/user/user-video-rate.ts
index 37d0222cf..c14598650 100644
--- a/server/models/user/user-video-rate.ts
+++ b/server/models/user/user-video-rate.ts
@@ -65,7 +65,7 @@ function associate (models) {
65 }) 65 })
66} 66}
67 67
68load = function (userId: number, videoId: string, transaction: Sequelize.Transaction) { 68load = function (userId: number, videoId: number, transaction: Sequelize.Transaction) {
69 const options: Sequelize.FindOptions = { 69 const options: Sequelize.FindOptions = {
70 where: { 70 where: {
71 userId, 71 userId,
diff --git a/server/models/video/tag.ts b/server/models/video/tag.ts
index 2992da56d..0c0757fc8 100644
--- a/server/models/video/tag.ts
+++ b/server/models/video/tag.ts
@@ -47,7 +47,7 @@ function associate (models) {
47 Tag.belongsToMany(models.Video, { 47 Tag.belongsToMany(models.Video, {
48 foreignKey: 'tagId', 48 foreignKey: 'tagId',
49 through: models.VideoTag, 49 through: models.VideoTag,
50 onDelete: 'cascade' 50 onDelete: 'CASCADE'
51 }) 51 })
52} 52}
53 53
diff --git a/server/models/video/video-abuse-interface.ts b/server/models/video/video-abuse-interface.ts
index d6724d36f..fa45aa5f9 100644
--- a/server/models/video/video-abuse-interface.ts
+++ b/server/models/video/video-abuse-interface.ts
@@ -20,7 +20,7 @@ export interface VideoAbuseClass {
20export interface VideoAbuseAttributes { 20export interface VideoAbuseAttributes {
21 reporterUsername: string 21 reporterUsername: string
22 reason: string 22 reason: string
23 videoId: string 23 videoId: number
24} 24}
25 25
26export interface VideoAbuseInstance extends VideoAbuseClass, VideoAbuseAttributes, Sequelize.Instance<VideoAbuseAttributes> { 26export interface VideoAbuseInstance extends VideoAbuseClass, VideoAbuseAttributes, Sequelize.Instance<VideoAbuseAttributes> {
diff --git a/server/models/video/video-abuse.ts b/server/models/video/video-abuse.ts
index ab1a3ea7d..f55a25e6a 100644
--- a/server/models/video/video-abuse.ts
+++ b/server/models/video/video-abuse.ts
@@ -96,7 +96,7 @@ function associate (models) {
96 name: 'reporterPodId', 96 name: 'reporterPodId',
97 allowNull: true 97 allowNull: true
98 }, 98 },
99 onDelete: 'cascade' 99 onDelete: 'CASCADE'
100 }) 100 })
101 101
102 VideoAbuse.belongsTo(models.Video, { 102 VideoAbuse.belongsTo(models.Video, {
@@ -104,7 +104,7 @@ function associate (models) {
104 name: 'videoId', 104 name: 'videoId',
105 allowNull: false 105 allowNull: false
106 }, 106 },
107 onDelete: 'cascade' 107 onDelete: 'CASCADE'
108 }) 108 })
109} 109}
110 110
diff --git a/server/models/video/video-blacklist-interface.ts b/server/models/video/video-blacklist-interface.ts
index 47a510231..cd9f19363 100644
--- a/server/models/video/video-blacklist-interface.ts
+++ b/server/models/video/video-blacklist-interface.ts
@@ -17,7 +17,7 @@ export namespace BlacklistedVideoMethods {
17 17
18 export type LoadById = (id: number) => Promise<BlacklistedVideoInstance> 18 export type LoadById = (id: number) => Promise<BlacklistedVideoInstance>
19 19
20 export type LoadByVideoId = (id: string) => Promise<BlacklistedVideoInstance> 20 export type LoadByVideoId = (id: number) => Promise<BlacklistedVideoInstance>
21} 21}
22 22
23export interface BlacklistedVideoClass { 23export interface BlacklistedVideoClass {
@@ -30,7 +30,7 @@ export interface BlacklistedVideoClass {
30} 30}
31 31
32export interface BlacklistedVideoAttributes { 32export interface BlacklistedVideoAttributes {
33 videoId: string 33 videoId: number
34} 34}
35 35
36export interface BlacklistedVideoInstance 36export interface BlacklistedVideoInstance
diff --git a/server/models/video/video-blacklist.ts b/server/models/video/video-blacklist.ts
index 8c42dbc21..4d1b45aa5 100644
--- a/server/models/video/video-blacklist.ts
+++ b/server/models/video/video-blacklist.ts
@@ -60,8 +60,11 @@ toFormatedJSON = function (this: BlacklistedVideoInstance) {
60 60
61function associate (models) { 61function associate (models) {
62 BlacklistedVideo.belongsTo(models.Video, { 62 BlacklistedVideo.belongsTo(models.Video, {
63 foreignKey: 'videoId', 63 foreignKey: {
64 onDelete: 'cascade' 64 name: 'videoId',
65 allowNull: false
66 },
67 onDelete: 'CASCADE'
65 }) 68 })
66} 69}
67 70
@@ -92,7 +95,7 @@ loadById = function (id: number) {
92 return BlacklistedVideo.findById(id) 95 return BlacklistedVideo.findById(id)
93} 96}
94 97
95loadByVideoId = function (id: string) { 98loadByVideoId = function (id: number) {
96 const query = { 99 const query = {
97 where: { 100 where: {
98 videoId: id 101 videoId: id
diff --git a/server/models/video/video-interface.ts b/server/models/video/video-interface.ts
index b836d6da6..2fabcd906 100644
--- a/server/models/video/video-interface.ts
+++ b/server/models/video/video-interface.ts
@@ -9,6 +9,7 @@ import { Video as FormatedVideo } from '../../../shared/models/videos/video.mode
9import { ResultList } from '../../../shared/models/result-list.model' 9import { ResultList } from '../../../shared/models/result-list.model'
10 10
11export type FormatedAddRemoteVideo = { 11export type FormatedAddRemoteVideo = {
12 uuid: string
12 name: string 13 name: string
13 category: number 14 category: number
14 licence: number 15 licence: number
@@ -16,7 +17,6 @@ export type FormatedAddRemoteVideo = {
16 nsfw: boolean 17 nsfw: boolean
17 description: string 18 description: string
18 infoHash: string 19 infoHash: string
19 remoteId: string
20 author: string 20 author: string
21 duration: number 21 duration: number
22 thumbnailData: string 22 thumbnailData: string
@@ -30,6 +30,7 @@ export type FormatedAddRemoteVideo = {
30} 30}
31 31
32export type FormatedUpdateRemoteVideo = { 32export type FormatedUpdateRemoteVideo = {
33 uuid: string
33 name: string 34 name: string
34 category: number 35 category: number
35 licence: number 36 licence: number
@@ -37,7 +38,6 @@ export type FormatedUpdateRemoteVideo = {
37 nsfw: boolean 38 nsfw: boolean
38 description: string 39 description: string
39 infoHash: string 40 infoHash: string
40 remoteId: string
41 author: string 41 author: string
42 duration: number 42 duration: number
43 tags: string[] 43 tags: string[]
@@ -80,10 +80,12 @@ export namespace VideoMethods {
80 sort: string 80 sort: string
81 ) => Promise< ResultList<VideoInstance> > 81 ) => Promise< ResultList<VideoInstance> >
82 82
83 export type Load = (id: string) => Promise<VideoInstance> 83 export type Load = (id: number) => Promise<VideoInstance>
84 export type LoadByHostAndRemoteId = (fromHost: string, remoteId: string) => Promise<VideoInstance> 84 export type LoadByUUID = (uuid: string) => Promise<VideoInstance>
85 export type LoadAndPopulateAuthor = (id: string) => Promise<VideoInstance> 85 export type LoadByHostAndUUID = (fromHost: string, uuid: string) => Promise<VideoInstance>
86 export type LoadAndPopulateAuthorAndPodAndTags = (id: string) => Promise<VideoInstance> 86 export type LoadAndPopulateAuthor = (id: number) => Promise<VideoInstance>
87 export type LoadAndPopulateAuthorAndPodAndTags = (id: number) => Promise<VideoInstance>
88 export type LoadByUUIDAndPopulateAuthorAndPodAndTags = (uuid: string) => Promise<VideoInstance>
87} 89}
88 90
89export interface VideoClass { 91export interface VideoClass {
@@ -102,19 +104,21 @@ export interface VideoClass {
102 getDurationFromFile: VideoMethods.GetDurationFromFile 104 getDurationFromFile: VideoMethods.GetDurationFromFile
103 list: VideoMethods.List 105 list: VideoMethods.List
104 listForApi: VideoMethods.ListForApi 106 listForApi: VideoMethods.ListForApi
105 loadByHostAndRemoteId: VideoMethods.LoadByHostAndRemoteId 107 loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
106 listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags 108 listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags
107 listOwnedByAuthor: VideoMethods.ListOwnedByAuthor 109 listOwnedByAuthor: VideoMethods.ListOwnedByAuthor
108 load: VideoMethods.Load 110 load: VideoMethods.Load
111 loadByUUID: VideoMethods.LoadByUUID
109 loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor 112 loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor
110 loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags 113 loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags
114 loadByUUIDAndPopulateAuthorAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAuthorAndPodAndTags
111 searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags 115 searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags
112} 116}
113 117
114export interface VideoAttributes { 118export interface VideoAttributes {
119 uuid?: string
115 name: string 120 name: string
116 extname: string 121 extname: string
117 remoteId: string
118 category: number 122 category: number
119 licence: number 123 licence: number
120 language: number 124 language: number
@@ -125,13 +129,14 @@ export interface VideoAttributes {
125 views?: number 129 views?: number
126 likes?: number 130 likes?: number
127 dislikes?: number 131 dislikes?: number
132 remote: boolean
128 133
129 Author?: AuthorInstance 134 Author?: AuthorInstance
130 Tags?: TagInstance[] 135 Tags?: TagInstance[]
131} 136}
132 137
133export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> { 138export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
134 id: string 139 id: number
135 createdAt: Date 140 createdAt: Date
136 updatedAt: Date 141 updatedAt: Date
137 142
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 496385b35..3bb74bf6d 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -62,21 +62,23 @@ let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
62let getDurationFromFile: VideoMethods.GetDurationFromFile 62let getDurationFromFile: VideoMethods.GetDurationFromFile
63let list: VideoMethods.List 63let list: VideoMethods.List
64let listForApi: VideoMethods.ListForApi 64let listForApi: VideoMethods.ListForApi
65let loadByHostAndRemoteId: VideoMethods.LoadByHostAndRemoteId 65let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
66let listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags 66let listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags
67let listOwnedByAuthor: VideoMethods.ListOwnedByAuthor 67let listOwnedByAuthor: VideoMethods.ListOwnedByAuthor
68let load: VideoMethods.Load 68let load: VideoMethods.Load
69let loadByUUID: VideoMethods.LoadByUUID
69let loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor 70let loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor
70let loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags 71let loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags
72let loadByUUIDAndPopulateAuthorAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAuthorAndPodAndTags
71let searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags 73let searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags
72 74
73export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { 75export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
74 Video = sequelize.define<VideoInstance, VideoAttributes>('Video', 76 Video = sequelize.define<VideoInstance, VideoAttributes>('Video',
75 { 77 {
76 id: { 78 uuid: {
77 type: DataTypes.UUID, 79 type: DataTypes.UUID,
78 defaultValue: DataTypes.UUIDV4, 80 defaultValue: DataTypes.UUIDV4,
79 primaryKey: true, 81 allowNull: false,
80 validate: { 82 validate: {
81 isUUID: 4 83 isUUID: 4
82 } 84 }
@@ -95,13 +97,6 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
95 type: DataTypes.ENUM(values(CONSTRAINTS_FIELDS.VIDEOS.EXTNAME)), 97 type: DataTypes.ENUM(values(CONSTRAINTS_FIELDS.VIDEOS.EXTNAME)),
96 allowNull: false 98 allowNull: false
97 }, 99 },
98 remoteId: {
99 type: DataTypes.UUID,
100 allowNull: true,
101 validate: {
102 isUUID: 4
103 }
104 },
105 category: { 100 category: {
106 type: DataTypes.INTEGER, 101 type: DataTypes.INTEGER,
107 allowNull: false, 102 allowNull: false,
@@ -199,6 +194,11 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
199 min: 0, 194 min: 0,
200 isInt: true 195 isInt: true
201 } 196 }
197 },
198 remote: {
199 type: DataTypes.BOOLEAN,
200 allowNull: false,
201 defaultValue: false
202 } 202 }
203 }, 203 },
204 { 204 {
@@ -207,9 +207,6 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
207 fields: [ 'authorId' ] 207 fields: [ 'authorId' ]
208 }, 208 },
209 { 209 {
210 fields: [ 'remoteId' ]
211 },
212 {
213 fields: [ 'name' ] 210 fields: [ 'name' ]
214 }, 211 },
215 { 212 {
@@ -226,6 +223,9 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
226 }, 223 },
227 { 224 {
228 fields: [ 'likes' ] 225 fields: [ 'likes' ]
226 },
227 {
228 fields: [ 'uuid' ]
229 } 229 }
230 ], 230 ],
231 hooks: { 231 hooks: {
@@ -246,9 +246,11 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
246 listOwnedAndPopulateAuthorAndTags, 246 listOwnedAndPopulateAuthorAndTags,
247 listOwnedByAuthor, 247 listOwnedByAuthor,
248 load, 248 load,
249 loadByHostAndRemoteId, 249 loadByUUID,
250 loadByHostAndUUID,
250 loadAndPopulateAuthor, 251 loadAndPopulateAuthor,
251 loadAndPopulateAuthorAndPodAndTags, 252 loadAndPopulateAuthorAndPodAndTags,
253 loadByUUIDAndPopulateAuthorAndPodAndTags,
252 searchAndPopulateAuthorAndPodAndTags, 254 searchAndPopulateAuthorAndPodAndTags,
253 removeFromBlacklist 255 removeFromBlacklist
254 ] 256 ]
@@ -289,8 +291,9 @@ function beforeCreate (video: VideoInstance, options: { transaction: Sequelize.T
289 ) 291 )
290 292
291 if (CONFIG.TRANSCODING.ENABLED === true) { 293 if (CONFIG.TRANSCODING.ENABLED === true) {
294 // Put uuid because we don't have id auto incremented for now
292 const dataInput = { 295 const dataInput = {
293 id: video.id 296 videoUUID: video.uuid
294 } 297 }
295 298
296 tasks.push( 299 tasks.push(
@@ -313,7 +316,7 @@ function afterDestroy (video: VideoInstance) {
313 316
314 if (video.isOwned()) { 317 if (video.isOwned()) {
315 const removeVideoToFriendsParams = { 318 const removeVideoToFriendsParams = {
316 remoteId: video.id 319 uuid: video.uuid
317 } 320 }
318 321
319 tasks.push( 322 tasks.push(
@@ -381,34 +384,27 @@ generateMagnetUri = function (this: VideoInstance) {
381} 384}
382 385
383getVideoFilename = function (this: VideoInstance) { 386getVideoFilename = function (this: VideoInstance) {
384 if (this.isOwned()) return this.id + this.extname 387 return this.uuid + this.extname
385
386 return this.remoteId + this.extname
387} 388}
388 389
389getThumbnailName = function (this: VideoInstance) { 390getThumbnailName = function (this: VideoInstance) {
390 // We always have a copy of the thumbnail 391 // We always have a copy of the thumbnail
391 return this.id + '.jpg' 392 const extension = '.jpg'
393 return this.uuid + extension
392} 394}
393 395
394getPreviewName = function (this: VideoInstance) { 396getPreviewName = function (this: VideoInstance) {
395 const extension = '.jpg' 397 const extension = '.jpg'
396 398 return this.uuid + extension
397 if (this.isOwned()) return this.id + extension
398
399 return this.remoteId + extension
400} 399}
401 400
402getTorrentName = function (this: VideoInstance) { 401getTorrentName = function (this: VideoInstance) {
403 const extension = '.torrent' 402 const extension = '.torrent'
404 403 return this.uuid + extension
405 if (this.isOwned()) return this.id + extension
406
407 return this.remoteId + extension
408} 404}
409 405
410isOwned = function (this: VideoInstance) { 406isOwned = function (this: VideoInstance) {
411 return this.remoteId === null 407 return this.remote === false
412} 408}
413 409
414toFormatedJSON = function (this: VideoInstance) { 410toFormatedJSON = function (this: VideoInstance) {
@@ -435,6 +431,7 @@ toFormatedJSON = function (this: VideoInstance) {
435 431
436 const json = { 432 const json = {
437 id: this.id, 433 id: this.id,
434 uuid: this.uuid,
438 name: this.name, 435 name: this.name,
439 category: this.category, 436 category: this.category,
440 categoryLabel, 437 categoryLabel,
@@ -467,6 +464,7 @@ toAddRemoteJSON = function (this: VideoInstance) {
467 464
468 return readFileBufferPromise(thumbnailPath).then(thumbnailData => { 465 return readFileBufferPromise(thumbnailPath).then(thumbnailData => {
469 const remoteVideo = { 466 const remoteVideo = {
467 uuid: this.uuid,
470 name: this.name, 468 name: this.name,
471 category: this.category, 469 category: this.category,
472 licence: this.licence, 470 licence: this.licence,
@@ -474,7 +472,6 @@ toAddRemoteJSON = function (this: VideoInstance) {
474 nsfw: this.nsfw, 472 nsfw: this.nsfw,
475 description: this.description, 473 description: this.description,
476 infoHash: this.infoHash, 474 infoHash: this.infoHash,
477 remoteId: this.id,
478 author: this.Author.name, 475 author: this.Author.name,
479 duration: this.duration, 476 duration: this.duration,
480 thumbnailData: thumbnailData.toString('binary'), 477 thumbnailData: thumbnailData.toString('binary'),
@@ -493,6 +490,7 @@ toAddRemoteJSON = function (this: VideoInstance) {
493 490
494toUpdateRemoteJSON = function (this: VideoInstance) { 491toUpdateRemoteJSON = function (this: VideoInstance) {
495 const json = { 492 const json = {
493 uuid: this.uuid,
496 name: this.name, 494 name: this.name,
497 category: this.category, 495 category: this.category,
498 licence: this.licence, 496 licence: this.licence,
@@ -500,7 +498,6 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
500 nsfw: this.nsfw, 498 nsfw: this.nsfw,
501 description: this.description, 499 description: this.description,
502 infoHash: this.infoHash, 500 infoHash: this.infoHash,
503 remoteId: this.id,
504 author: this.Author.name, 501 author: this.Author.name,
505 duration: this.duration, 502 duration: this.duration,
506 tags: map<TagInstance, string>(this.Tags, 'name'), 503 tags: map<TagInstance, string>(this.Tags, 'name'),
@@ -615,10 +612,10 @@ listForApi = function (start: number, count: number, sort: string) {
615 }) 612 })
616} 613}
617 614
618loadByHostAndRemoteId = function (fromHost: string, remoteId: string) { 615loadByHostAndUUID = function (fromHost: string, uuid: string) {
619 const query = { 616 const query = {
620 where: { 617 where: {
621 remoteId: remoteId 618 uuid
622 }, 619 },
623 include: [ 620 include: [
624 { 621 {
@@ -640,10 +637,9 @@ loadByHostAndRemoteId = function (fromHost: string, remoteId: string) {
640} 637}
641 638
642listOwnedAndPopulateAuthorAndTags = function () { 639listOwnedAndPopulateAuthorAndTags = function () {
643 // If remoteId is null this is *our* video
644 const query = { 640 const query = {
645 where: { 641 where: {
646 remoteId: null 642 remote: false
647 }, 643 },
648 include: [ Video['sequelize'].models.Author, Video['sequelize'].models.Tag ] 644 include: [ Video['sequelize'].models.Author, Video['sequelize'].models.Tag ]
649 } 645 }
@@ -654,7 +650,7 @@ listOwnedAndPopulateAuthorAndTags = function () {
654listOwnedByAuthor = function (author: string) { 650listOwnedByAuthor = function (author: string) {
655 const query = { 651 const query = {
656 where: { 652 where: {
657 remoteId: null 653 remote: false
658 }, 654 },
659 include: [ 655 include: [
660 { 656 {
@@ -669,11 +665,20 @@ listOwnedByAuthor = function (author: string) {
669 return Video.findAll(query) 665 return Video.findAll(query)
670} 666}
671 667
672load = function (id: string) { 668load = function (id: number) {
673 return Video.findById(id) 669 return Video.findById(id)
674} 670}
675 671
676loadAndPopulateAuthor = function (id: string) { 672loadByUUID = function (uuid: string) {
673 const query = {
674 where: {
675 uuid
676 }
677 }
678 return Video.findOne(query)
679}
680
681loadAndPopulateAuthor = function (id: number) {
677 const options = { 682 const options = {
678 include: [ Video['sequelize'].models.Author ] 683 include: [ Video['sequelize'].models.Author ]
679 } 684 }
@@ -681,7 +686,7 @@ loadAndPopulateAuthor = function (id: string) {
681 return Video.findById(id, options) 686 return Video.findById(id, options)
682} 687}
683 688
684loadAndPopulateAuthorAndPodAndTags = function (id: string) { 689loadAndPopulateAuthorAndPodAndTags = function (id: number) {
685 const options = { 690 const options = {
686 include: [ 691 include: [
687 { 692 {
@@ -695,6 +700,23 @@ loadAndPopulateAuthorAndPodAndTags = function (id: string) {
695 return Video.findById(id, options) 700 return Video.findById(id, options)
696} 701}
697 702
703loadByUUIDAndPopulateAuthorAndPodAndTags = function (uuid: string) {
704 const options = {
705 where: {
706 uuid
707 },
708 include: [
709 {
710 model: Video['sequelize'].models.Author,
711 include: [ { model: Video['sequelize'].models.Pod, required: false } ]
712 },
713 Video['sequelize'].models.Tag
714 ]
715 }
716
717 return Video.findOne(options)
718}
719
698searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) { 720searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) {
699 const podInclude: Sequelize.IncludeOptions = { 721 const podInclude: Sequelize.IncludeOptions = {
700 model: Video['sequelize'].models.Pod, 722 model: Video['sequelize'].models.Pod,
diff --git a/server/tests/api/check-params/remotes.js b/server/tests/api/check-params/remotes.js
index 96361c643..7cb99c4ae 100644
--- a/server/tests/api/check-params/remotes.js
+++ b/server/tests/api/check-params/remotes.js
@@ -44,7 +44,7 @@ describe('Test remote videos API validators', function () {
44 describe('When adding a video', function () { 44 describe('When adding a video', function () {
45 it('Should check when adding a video') 45 it('Should check when adding a video')
46 46
47 it('Should not add an existing remoteId and host pair') 47 it('Should not add an existing uuid')
48 }) 48 })
49 49
50 describe('When removing a video', function () { 50 describe('When removing a video', function () {
diff --git a/server/tests/api/multiple-pods.js b/server/tests/api/multiple-pods.js
index c3ee77f0c..1bc6157e8 100644
--- a/server/tests/api/multiple-pods.js
+++ b/server/tests/api/multiple-pods.js
@@ -20,6 +20,7 @@ const videosUtils = require('../utils/videos')
20describe('Test multiple pods', function () { 20describe('Test multiple pods', function () {
21 let servers = [] 21 let servers = []
22 const toRemove = [] 22 const toRemove = []
23 let videoUUID = ''
23 24
24 before(function (done) { 25 before(function (done) {
25 this.timeout(120000) 26 this.timeout(120000)
@@ -746,6 +747,36 @@ describe('Test multiple pods', function () {
746 expect(videos[0].name).not.to.equal(toRemove[1].name) 747 expect(videos[0].name).not.to.equal(toRemove[1].name)
747 expect(videos[1].name).not.to.equal(toRemove[1].name) 748 expect(videos[1].name).not.to.equal(toRemove[1].name)
748 749
750 videoUUID = videos[0].uuid
751
752 callback()
753 })
754 }, done)
755 })
756
757 it('Should get the same video by UUID on each pod', function (done) {
758 let baseVideo = null
759 each(servers, function (server, callback) {
760 videosUtils.getVideo(server.url, videoUUID, function (err, res) {
761 if (err) throw err
762
763 const video = res.body
764
765 if (baseVideo === null) {
766 baseVideo = video
767 return callback()
768 }
769
770 expect(baseVideo.name).to.equal(video.name)
771 expect(baseVideo.uuid).to.equal(video.uuid)
772 expect(baseVideo.category).to.equal(video.category)
773 expect(baseVideo.language).to.equal(video.language)
774 expect(baseVideo.licence).to.equal(video.licence)
775 expect(baseVideo.category).to.equal(video.category)
776 expect(baseVideo.nsfw).to.equal(video.nsfw)
777 expect(baseVideo.author).to.equal(video.author)
778 expect(baseVideo.tags).to.deep.equal(video.tags)
779
749 callback() 780 callback()
750 }) 781 })
751 }, done) 782 }, done)
diff --git a/server/tests/api/single-pod.js b/server/tests/api/single-pod.js
index e08da0517..0dac9a183 100644
--- a/server/tests/api/single-pod.js
+++ b/server/tests/api/single-pod.js
@@ -19,6 +19,7 @@ const videosUtils = require('../utils/videos')
19describe('Test a single pod', function () { 19describe('Test a single pod', function () {
20 let server = null 20 let server = null
21 let videoId = -1 21 let videoId = -1
22 let videoUUID = ''
22 let videosListBase = null 23 let videosListBase = null
23 24
24 before(function (done) { 25 before(function (done) {
@@ -140,6 +141,7 @@ describe('Test a single pod', function () {
140 expect(test).to.equal(true) 141 expect(test).to.equal(true)
141 142
142 videoId = video.id 143 videoId = video.id
144 videoUUID = video.uuid
143 145
144 webtorrent.add(video.magnetUri, function (torrent) { 146 webtorrent.add(video.magnetUri, function (torrent) {
145 expect(torrent.files).to.exist 147 expect(torrent.files).to.exist
@@ -181,18 +183,33 @@ describe('Test a single pod', function () {
181 if (err) throw err 183 if (err) throw err
182 expect(test).to.equal(true) 184 expect(test).to.equal(true)
183 185
184 // Wait the async views increment 186 // Wait the async views increment
185 setTimeout(done, 500) 187 setTimeout(done, 500)
186 }) 188 })
187 }) 189 })
188 }) 190 })
189 191
192 it('Should get the video by UUID', function (done) {
193 // Yes, this could be long
194 this.timeout(60000)
195
196 videosUtils.getVideo(server.url, videoUUID, function (err, res) {
197 if (err) throw err
198
199 const video = res.body
200 expect(video.name).to.equal('my super name')
201
202 // Wait the async views increment
203 setTimeout(done, 500)
204 })
205 })
206
190 it('Should have the views updated', function (done) { 207 it('Should have the views updated', function (done) {
191 videosUtils.getVideo(server.url, videoId, function (err, res) { 208 videosUtils.getVideo(server.url, videoId, function (err, res) {
192 if (err) throw err 209 if (err) throw err
193 210
194 const video = res.body 211 const video = res.body
195 expect(video.views).to.equal(1) 212 expect(video.views).to.equal(2)
196 213
197 done() 214 done()
198 }) 215 })