diff options
author | Chocobozzz <florian.bigard@gmail.com> | 2017-07-11 16:01:56 +0200 |
---|---|---|
committer | Chocobozzz <florian.bigard@gmail.com> | 2017-07-11 16:01:56 +0200 |
commit | 0a6658fdcbd779ada8f3758048c326e997902d5a (patch) | |
tree | 5de40bf901db0299011104b1344783637b964eb0 | |
parent | e6d4b0ff2404dcf0b3a755c3fcc415ffeb6e754d (diff) | |
download | PeerTube-0a6658fdcbd779ada8f3758048c326e997902d5a.tar.gz PeerTube-0a6658fdcbd779ada8f3758048c326e997902d5a.tar.zst PeerTube-0a6658fdcbd779ada8f3758048c326e997902d5a.zip |
Use global uuid instead of remoteId for videos
58 files changed, 450 insertions, 176 deletions
diff --git a/client/src/app/shared/video-abuse/video-abuse.service.ts b/client/src/app/shared/video-abuse/video-abuse.service.ts index 32f13b430..636a02084 100644 --- a/client/src/app/shared/video-abuse/video-abuse.service.ts +++ b/client/src/app/shared/video-abuse/video-abuse.service.ts | |||
@@ -22,7 +22,7 @@ export class VideoAbuseService { | |||
22 | return new RestDataSource(this.authHttp, VideoAbuseService.BASE_VIDEO_ABUSE_URL + 'abuse') | 22 | return new RestDataSource(this.authHttp, VideoAbuseService.BASE_VIDEO_ABUSE_URL + 'abuse') |
23 | } | 23 | } |
24 | 24 | ||
25 | reportVideo (id: string, reason: string) { | 25 | reportVideo (id: number, reason: string) { |
26 | const body = { | 26 | const body = { |
27 | reason | 27 | reason |
28 | } | 28 | } |
diff --git a/client/src/app/videos/shared/video.model.ts b/client/src/app/videos/shared/video.model.ts index f5e16fc13..9ed6a0641 100644 --- a/client/src/app/videos/shared/video.model.ts +++ b/client/src/app/videos/shared/video.model.ts | |||
@@ -14,7 +14,8 @@ export class Video implements VideoServerModel { | |||
14 | description: string | 14 | description: string |
15 | duration: number | 15 | duration: number |
16 | durationLabel: string | 16 | durationLabel: string |
17 | id: string | 17 | id: number |
18 | uuid: string | ||
18 | isLocal: boolean | 19 | isLocal: boolean |
19 | magnetUri: string | 20 | magnetUri: string |
20 | name: string | 21 | name: string |
@@ -51,7 +52,8 @@ export class Video implements VideoServerModel { | |||
51 | language: number | 52 | language: number |
52 | description: string, | 53 | description: string, |
53 | duration: number | 54 | duration: number |
54 | id: string, | 55 | id: number, |
56 | uuid: string, | ||
55 | isLocal: boolean, | 57 | isLocal: boolean, |
56 | magnetUri: string, | 58 | magnetUri: string, |
57 | name: string, | 59 | name: string, |
@@ -75,6 +77,7 @@ export class Video implements VideoServerModel { | |||
75 | this.duration = hash.duration | 77 | this.duration = hash.duration |
76 | this.durationLabel = Video.createDurationString(hash.duration) | 78 | this.durationLabel = Video.createDurationString(hash.duration) |
77 | this.id = hash.id | 79 | this.id = hash.id |
80 | this.uuid = hash.uuid | ||
78 | this.isLocal = hash.isLocal | 81 | this.isLocal = hash.isLocal |
79 | this.magnetUri = hash.magnetUri | 82 | this.magnetUri = hash.magnetUri |
80 | this.name = hash.name | 83 | this.name = hash.name |
diff --git a/client/src/app/videos/shared/video.service.ts b/client/src/app/videos/shared/video.service.ts index dc12c0637..67091a8d8 100644 --- a/client/src/app/videos/shared/video.service.ts +++ b/client/src/app/videos/shared/video.service.ts | |||
@@ -52,8 +52,8 @@ export class VideoService { | |||
52 | return this.loadVideoAttributeEnum('languages', this.videoLanguages) | 52 | return this.loadVideoAttributeEnum('languages', this.videoLanguages) |
53 | } | 53 | } |
54 | 54 | ||
55 | getVideo (id: string): Observable<Video> { | 55 | getVideo (uuid: string): Observable<Video> { |
56 | return this.http.get(VideoService.BASE_VIDEO_URL + id) | 56 | return this.http.get(VideoService.BASE_VIDEO_URL + uuid) |
57 | .map(this.restExtractor.extractDataGet) | 57 | .map(this.restExtractor.extractDataGet) |
58 | .map(videoHash => new Video(videoHash)) | 58 | .map(videoHash => new Video(videoHash)) |
59 | .catch((res) => this.restExtractor.handleError(res)) | 59 | .catch((res) => this.restExtractor.handleError(res)) |
@@ -89,7 +89,7 @@ export class VideoService { | |||
89 | .catch((res) => this.restExtractor.handleError(res)) | 89 | .catch((res) => this.restExtractor.handleError(res)) |
90 | } | 90 | } |
91 | 91 | ||
92 | removeVideo (id: string) { | 92 | removeVideo (id: number) { |
93 | return this.authHttp.delete(VideoService.BASE_VIDEO_URL + id) | 93 | return this.authHttp.delete(VideoService.BASE_VIDEO_URL + id) |
94 | .map(this.restExtractor.extractDataBool) | 94 | .map(this.restExtractor.extractDataBool) |
95 | .catch((res) => this.restExtractor.handleError(res)) | 95 | .catch((res) => this.restExtractor.handleError(res)) |
@@ -106,7 +106,7 @@ export class VideoService { | |||
106 | .catch((res) => this.restExtractor.handleError(res)) | 106 | .catch((res) => this.restExtractor.handleError(res)) |
107 | } | 107 | } |
108 | 108 | ||
109 | reportVideo (id: string, reason: string) { | 109 | reportVideo (id: number, reason: string) { |
110 | const url = VideoService.BASE_VIDEO_URL + id + '/abuse' | 110 | const url = VideoService.BASE_VIDEO_URL + id + '/abuse' |
111 | const body: VideoAbuseCreate = { | 111 | const body: VideoAbuseCreate = { |
112 | reason | 112 | reason |
@@ -117,15 +117,15 @@ export class VideoService { | |||
117 | .catch((res) => this.restExtractor.handleError(res)) | 117 | .catch((res) => this.restExtractor.handleError(res)) |
118 | } | 118 | } |
119 | 119 | ||
120 | setVideoLike (id: string) { | 120 | setVideoLike (id: number) { |
121 | return this.setVideoRate(id, 'like') | 121 | return this.setVideoRate(id, 'like') |
122 | } | 122 | } |
123 | 123 | ||
124 | setVideoDislike (id: string) { | 124 | setVideoDislike (id: number) { |
125 | return this.setVideoRate(id, 'dislike') | 125 | return this.setVideoRate(id, 'dislike') |
126 | } | 126 | } |
127 | 127 | ||
128 | getUserVideoRating (id: string): Observable<UserVideoRate> { | 128 | getUserVideoRating (id: number): Observable<UserVideoRate> { |
129 | const url = UserService.BASE_USERS_URL + '/me/videos/' + id + '/rating' | 129 | const url = UserService.BASE_USERS_URL + '/me/videos/' + id + '/rating' |
130 | 130 | ||
131 | return this.authHttp.get(url) | 131 | return this.authHttp.get(url) |
@@ -133,13 +133,13 @@ export class VideoService { | |||
133 | .catch((res) => this.restExtractor.handleError(res)) | 133 | .catch((res) => this.restExtractor.handleError(res)) |
134 | } | 134 | } |
135 | 135 | ||
136 | blacklistVideo (id: string) { | 136 | blacklistVideo (id: number) { |
137 | return this.authHttp.post(VideoService.BASE_VIDEO_URL + id + '/blacklist', {}) | 137 | return this.authHttp.post(VideoService.BASE_VIDEO_URL + id + '/blacklist', {}) |
138 | .map(this.restExtractor.extractDataBool) | 138 | .map(this.restExtractor.extractDataBool) |
139 | .catch((res) => this.restExtractor.handleError(res)) | 139 | .catch((res) => this.restExtractor.handleError(res)) |
140 | } | 140 | } |
141 | 141 | ||
142 | private setVideoRate (id: string, rateType: VideoRateType) { | 142 | private setVideoRate (id: number, rateType: VideoRateType) { |
143 | const url = VideoService.BASE_VIDEO_URL + id + '/rate' | 143 | const url = VideoService.BASE_VIDEO_URL + id + '/rate' |
144 | const body: UserVideoRateUpdate = { | 144 | const body: UserVideoRateUpdate = { |
145 | rating: rateType | 145 | rating: rateType |
diff --git a/client/src/app/videos/video-edit/video-update.component.ts b/client/src/app/videos/video-edit/video-update.component.ts index 9ee7ca6a8..6c57a23d8 100644 --- a/client/src/app/videos/video-edit/video-update.component.ts +++ b/client/src/app/videos/video-edit/video-update.component.ts | |||
@@ -85,8 +85,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { | |||
85 | this.videoLicences = this.videoService.videoLicences | 85 | this.videoLicences = this.videoService.videoLicences |
86 | this.videoLanguages = this.videoService.videoLanguages | 86 | this.videoLanguages = this.videoService.videoLanguages |
87 | 87 | ||
88 | const id = this.route.snapshot.params['id'] | 88 | const uuid: string = this.route.snapshot.params['uuid'] |
89 | this.videoService.getVideo(id) | 89 | this.videoService.getVideo(uuid) |
90 | .subscribe( | 90 | .subscribe( |
91 | video => { | 91 | video => { |
92 | this.video = video | 92 | this.video = video |
@@ -118,7 +118,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit { | |||
118 | .subscribe( | 118 | .subscribe( |
119 | () => { | 119 | () => { |
120 | this.notificationsService.success('Success', 'Video updated.') | 120 | this.notificationsService.success('Success', 'Video updated.') |
121 | this.router.navigate([ '/videos/watch', this.video.id ]) | 121 | this.router.navigate([ '/videos/watch', this.video.uuid ]) |
122 | }, | 122 | }, |
123 | 123 | ||
124 | err => { | 124 | err => { |
diff --git a/client/src/app/videos/video-list/video-miniature.component.html b/client/src/app/videos/video-list/video-miniature.component.html index db6c4d6fb..6525b67bf 100644 --- a/client/src/app/videos/video-list/video-miniature.component.html +++ b/client/src/app/videos/video-list/video-miniature.component.html | |||
@@ -1,6 +1,6 @@ | |||
1 | <div class="video-miniature"> | 1 | <div class="video-miniature"> |
2 | <a | 2 | <a |
3 | [routerLink]="['/videos/watch', video.id]" [attr.title]="video.description" | 3 | [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.description" |
4 | class="video-miniature-thumbnail" | 4 | class="video-miniature-thumbnail" |
5 | > | 5 | > |
6 | <img *ngIf="isVideoNSFWForThisUser() === false" [attr.src]="video.thumbnailUrl" alt="video thumbnail" /> | 6 | <img *ngIf="isVideoNSFWForThisUser() === false" [attr.src]="video.thumbnailUrl" alt="video thumbnail" /> |
@@ -16,7 +16,7 @@ | |||
16 | 16 | ||
17 | <div class="video-miniature-informations"> | 17 | <div class="video-miniature-informations"> |
18 | <span class="video-miniature-name"> | 18 | <span class="video-miniature-name"> |
19 | <a [routerLink]="['/videos/watch', video.id]" [attr.title]="getVideoName()" class="video-miniature-name">{{ getVideoName() }}</a> | 19 | <a [routerLink]="['/videos/watch', video.uuid]" [attr.title]="getVideoName()" class="video-miniature-name">{{ getVideoName() }}</a> |
20 | </span> | 20 | </span> |
21 | 21 | ||
22 | <div class="video-miniature-tags"> | 22 | <div class="video-miniature-tags"> |
diff --git a/client/src/app/videos/video-watch/video-share.component.ts b/client/src/app/videos/video-watch/video-share.component.ts index bbd25f5ef..133f93498 100644 --- a/client/src/app/videos/video-watch/video-share.component.ts +++ b/client/src/app/videos/video-watch/video-share.component.ts | |||
@@ -27,7 +27,7 @@ export class VideoShareComponent { | |||
27 | 27 | ||
28 | getVideoIframeCode () { | 28 | getVideoIframeCode () { |
29 | return '<iframe width="560" height="315" ' + | 29 | return '<iframe width="560" height="315" ' + |
30 | 'src="' + window.location.origin + '/videos/embed/' + this.video.id + '" ' + | 30 | 'src="' + window.location.origin + '/videos/embed/' + this.video.uuid + '" ' + |
31 | 'frameborder="0" allowfullscreen>' + | 31 | 'frameborder="0" allowfullscreen>' + |
32 | '</iframe>' | 32 | '</iframe>' |
33 | } | 33 | } |
diff --git a/client/src/app/videos/video-watch/video-watch.component.html b/client/src/app/videos/video-watch/video-watch.component.html index 8676b5b72..88863131a 100644 --- a/client/src/app/videos/video-watch/video-watch.component.html +++ b/client/src/app/videos/video-watch/video-watch.component.html | |||
@@ -65,7 +65,7 @@ | |||
65 | 65 | ||
66 | <ul *dropdownMenu class="dropdown-menu" id="more-menu" role="menu" aria-labelledby="single-button"> | 66 | <ul *dropdownMenu class="dropdown-menu" id="more-menu" role="menu" aria-labelledby="single-button"> |
67 | <li *ngIf="canUserUpdateVideo()" role="menuitem"> | 67 | <li *ngIf="canUserUpdateVideo()" role="menuitem"> |
68 | <a class="dropdown-item" title="Update this video" href="#" [routerLink]="[ '/videos/edit', video.id ]"> | 68 | <a class="dropdown-item" title="Update this video" href="#" [routerLink]="[ '/videos/edit', video.uuid ]"> |
69 | <span class="glyphicon glyphicon-pencil"></span> Update | 69 | <span class="glyphicon glyphicon-pencil"></span> Update |
70 | </a> | 70 | </a> |
71 | </li> | 71 | </li> |
diff --git a/client/src/app/videos/video-watch/video-watch.component.ts b/client/src/app/videos/video-watch/video-watch.component.ts index 6bd6c1f7e..104ba0db6 100644 --- a/client/src/app/videos/video-watch/video-watch.component.ts +++ b/client/src/app/videos/video-watch/video-watch.component.ts | |||
@@ -58,8 +58,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy { | |||
58 | 58 | ||
59 | ngOnInit () { | 59 | ngOnInit () { |
60 | this.paramsSub = this.route.params.subscribe(routeParams => { | 60 | this.paramsSub = this.route.params.subscribe(routeParams => { |
61 | let id = routeParams['id'] | 61 | let uuid = routeParams['uuid'] |
62 | this.videoService.getVideo(id).subscribe( | 62 | this.videoService.getVideo(uuid).subscribe( |
63 | video => this.onVideoFetched(video), | 63 | video => this.onVideoFetched(video), |
64 | 64 | ||
65 | error => { | 65 | error => { |
diff --git a/client/src/app/videos/videos-routing.module.ts b/client/src/app/videos/videos-routing.module.ts index e18c1cec0..715671ba7 100644 --- a/client/src/app/videos/videos-routing.module.ts +++ b/client/src/app/videos/videos-routing.module.ts | |||
@@ -33,7 +33,7 @@ const videosRoutes: Routes = [ | |||
33 | } | 33 | } |
34 | }, | 34 | }, |
35 | { | 35 | { |
36 | path: 'edit/:id', | 36 | path: 'edit/:uuid', |
37 | component: VideoUpdateComponent, | 37 | component: VideoUpdateComponent, |
38 | data: { | 38 | data: { |
39 | meta: { | 39 | meta: { |
@@ -42,11 +42,11 @@ const videosRoutes: Routes = [ | |||
42 | } | 42 | } |
43 | }, | 43 | }, |
44 | { | 44 | { |
45 | path: ':id', | 45 | path: ':uuid', |
46 | redirectTo: 'watch/:id' | 46 | redirectTo: 'watch/:uuid' |
47 | }, | 47 | }, |
48 | { | 48 | { |
49 | path: 'watch/:id', | 49 | path: 'watch/:uuid', |
50 | component: VideoWatchComponent | 50 | component: VideoWatchComponent |
51 | } | 51 | } |
52 | ] | 52 | ] |
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 | |||
133 | function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) { | 133 | function 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 | ||
234 | function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) { | 234 | function 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 | ||
316 | function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) { | 317 | function 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 | ||
360 | function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) { | 361 | function 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 | ||
372 | function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) { | 373 | function 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 | ||
389 | function fetchOwnedVideo (id: string) { | 390 | function 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 | ||
402 | function fetchRemoteVideo (podHost: string, remoteId: string) { | 403 | function 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 | ||
102 | function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) { | 102 | function 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 | ||
64 | function reportVideoAbuse (req: express.Request, res: express.Response) { | 64 | function 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 | ||
90 | function generateWatchHtmlPage (req: express.Request, res: express.Response, next: express.NextFunction) { | 90 | function 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' | |||
9 | import { | 9 | import { |
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 | |||
17 | const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES | 17 | const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES |
18 | const VIDEO_EVENTS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_EVENTS | 18 | const VIDEO_EVENTS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_EVENTS |
19 | 19 | ||
20 | function isVideoIdOrUUIDValid (value: string) { | ||
21 | return validator.isInt(value) || isVideoUUIDValid(value) | ||
22 | } | ||
23 | |||
20 | function isVideoAuthorValid (value: string) { | 24 | function 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 | ||
80 | function isVideoRemoteIdValid (value: string) { | 84 | function isVideoUUIDValid (value: string) { |
81 | return exists(value) && validator.isUUID(value, 4) | 85 | return exists(value) && validator.isUUID('' + value, 4) |
82 | } | 86 | } |
83 | 87 | ||
84 | function isVideoAbuseReasonValid (value: string) { | 88 | function isVideoAbuseReasonValid (value: string) { |
@@ -127,6 +131,7 @@ function isVideoFile (value: string, files: { [ fieldname: string ]: Express.Mul | |||
127 | // --------------------------------------------------------------------------- | 131 | // --------------------------------------------------------------------------- |
128 | 132 | ||
129 | export { | 133 | export { |
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 { | |||
155 | declare global { | 160 | declare 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 | ||
18 | const LAST_MIGRATION_VERSION = 50 | 18 | const 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 | ||
29 | function down (options, callback) { | 29 | function 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 | ||
28 | function down (options, callback) { | 28 | function 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 | ||
20 | function down (options, callback) { | 20 | function 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 | ||
20 | function down (options, callback) { | 20 | function 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 | ||
20 | function down (options, callback) { | 20 | function 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 | ||
25 | function down (options, callback) { | 25 | function 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 | ||
24 | function down (options, callback) { | 24 | function 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 | ||
25 | function down (options, callback) { | 25 | function 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 | ||
20 | function down (options, callback) { | 20 | function 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 | ||
20 | function down (options, callback) { | 20 | function 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 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import * as Promise from 'bluebird' | ||
3 | |||
4 | function 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 | |||
105 | function down (options) { | ||
106 | throw new Error('Not implemented.') | ||
107 | } | ||
108 | |||
109 | function removeForeignKey (sequelize: Sequelize.Sequelize, tableName: string) { | ||
110 | const query = 'ALTER TABLE "' + tableName + '" DROP CONSTRAINT "' + tableName + '_videoId_fkey' + '"' | ||
111 | return sequelize.query(query) | ||
112 | } | ||
113 | |||
114 | function 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 | |||
154 | export { | ||
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 | ||
46 | type QaduParam = { videoId: string, type: RequestVideoQaduType } | 46 | type QaduParam = { videoId: number, type: RequestVideoQaduType } |
47 | type EventParam = { videoId: string, type: RequestVideoEventType } | 47 | type EventParam = { videoId: number, type: RequestVideoEventType } |
48 | 48 | ||
49 | const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS] | 49 | const 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' | |||
3 | import { addVideoToFriends } from '../../../lib' | 3 | import { addVideoToFriends } from '../../../lib' |
4 | import { VideoInstance } from '../../../models' | 4 | import { VideoInstance } from '../../../models' |
5 | 5 | ||
6 | function process (data: { id: string }) { | 6 | function 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 | ||
13 | export type RequestVideoEventSchedulerOptions = { | 13 | export 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 | ||
34 | export type RequestVideoQaduSchedulerOptions = { | 34 | export 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 @@ | |||
1 | import 'express-validator' | 1 | import 'express-validator' |
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import * as Promise from 'bluebird' | ||
4 | import * as validator from 'validator' | ||
3 | 5 | ||
4 | import { database as db } from '../../initializers/database' | 6 | import { database as db } from '../../initializers/database' |
5 | import { checkErrors } from './utils' | 7 | import { checkErrors } from './utils' |
6 | import { logger } from '../../helpers' | 8 | import { logger } from '../../helpers' |
9 | import { VideoInstance } from '../../models' | ||
7 | 10 | ||
8 | function usersAddValidator (req: express.Request, res: express.Response, next: express.NextFunction) { | 11 | function 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 | ||
61 | function usersVideoRatingValidator (req: express.Request, res: express.Response, next: express.NextFunction) { | 64 | function 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 @@ | |||
1 | import 'express-validator' | 1 | import 'express-validator' |
2 | import * as express from 'express' | 2 | import * as express from 'express' |
3 | import * as Promise from 'bluebird' | ||
4 | import * as validator from 'validator' | ||
3 | 5 | ||
4 | import { database as db } from '../../initializers/database' | 6 | import { database as db } from '../../initializers/database' |
5 | import { checkErrors } from './utils' | 7 | import { checkErrors } from './utils' |
6 | import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers' | 8 | import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers' |
7 | import { logger, isVideoDurationValid } from '../../helpers' | 9 | import { logger, isVideoDurationValid } from '../../helpers' |
10 | import { VideoInstance } from '../../models' | ||
8 | 11 | ||
9 | function videosAddValidator (req: express.Request, res: express.Response, next: express.NextFunction) { | 12 | function 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 | ||
42 | function videosUpdateValidator (req: express.Request, res: express.Response, next: express.NextFunction) { | 45 | function 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 | ||
70 | function videosGetValidator (req: express.Request, res: express.Response, next: express.NextFunction) { | 73 | function 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 | ||
80 | function videosRemoveValidator (req: express.Request, res: express.Response, next: express.NextFunction) { | 83 | function 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 | ||
107 | function videoAbuseReportValidator (req: express.Request, res: express.Response, next: express.NextFunction) { | 110 | function 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 | ||
118 | function videoRateValidator (req: express.Request, res: express.Response, next: express.NextFunction) { | 121 | function 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 | ||
129 | function videosBlacklistValidator (req: express.Request, res: express.Response, next: express.NextFunction) { | 132 | function 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 | ||
159 | function checkVideoExists (id: string, res: express.Response, callback: () => void) { | 162 | function 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' | |||
4 | import { VideoRateType } from '../../../shared/models/videos/video-rate.type' | 4 | import { VideoRateType } from '../../../shared/models/videos/video-rate.type' |
5 | 5 | ||
6 | export namespace UserVideoRateMethods { | 6 | export 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 | ||
10 | export interface UserVideoRateClass { | 10 | export 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 | ||
68 | load = function (userId: number, videoId: string, transaction: Sequelize.Transaction) { | 68 | load = 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 { | |||
20 | export interface VideoAbuseAttributes { | 20 | export interface VideoAbuseAttributes { |
21 | reporterUsername: string | 21 | reporterUsername: string |
22 | reason: string | 22 | reason: string |
23 | videoId: string | 23 | videoId: number |
24 | } | 24 | } |
25 | 25 | ||
26 | export interface VideoAbuseInstance extends VideoAbuseClass, VideoAbuseAttributes, Sequelize.Instance<VideoAbuseAttributes> { | 26 | export 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 | ||
23 | export interface BlacklistedVideoClass { | 23 | export interface BlacklistedVideoClass { |
@@ -30,7 +30,7 @@ export interface BlacklistedVideoClass { | |||
30 | } | 30 | } |
31 | 31 | ||
32 | export interface BlacklistedVideoAttributes { | 32 | export interface BlacklistedVideoAttributes { |
33 | videoId: string | 33 | videoId: number |
34 | } | 34 | } |
35 | 35 | ||
36 | export interface BlacklistedVideoInstance | 36 | export 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 | ||
61 | function associate (models) { | 61 | function 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 | ||
95 | loadByVideoId = function (id: string) { | 98 | loadByVideoId = 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 | |||
9 | import { ResultList } from '../../../shared/models/result-list.model' | 9 | import { ResultList } from '../../../shared/models/result-list.model' |
10 | 10 | ||
11 | export type FormatedAddRemoteVideo = { | 11 | export 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 | ||
32 | export type FormatedUpdateRemoteVideo = { | 32 | export 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 | ||
89 | export interface VideoClass { | 91 | export 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 | ||
114 | export interface VideoAttributes { | 118 | export 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 | ||
133 | export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> { | 138 | export 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 | |||
62 | let getDurationFromFile: VideoMethods.GetDurationFromFile | 62 | let getDurationFromFile: VideoMethods.GetDurationFromFile |
63 | let list: VideoMethods.List | 63 | let list: VideoMethods.List |
64 | let listForApi: VideoMethods.ListForApi | 64 | let listForApi: VideoMethods.ListForApi |
65 | let loadByHostAndRemoteId: VideoMethods.LoadByHostAndRemoteId | 65 | let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID |
66 | let listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags | 66 | let listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags |
67 | let listOwnedByAuthor: VideoMethods.ListOwnedByAuthor | 67 | let listOwnedByAuthor: VideoMethods.ListOwnedByAuthor |
68 | let load: VideoMethods.Load | 68 | let load: VideoMethods.Load |
69 | let loadByUUID: VideoMethods.LoadByUUID | ||
69 | let loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor | 70 | let loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor |
70 | let loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags | 71 | let loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags |
72 | let loadByUUIDAndPopulateAuthorAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAuthorAndPodAndTags | ||
71 | let searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags | 73 | let searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags |
72 | 74 | ||
73 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | 75 | export 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 | ||
383 | getVideoFilename = function (this: VideoInstance) { | 386 | getVideoFilename = 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 | ||
389 | getThumbnailName = function (this: VideoInstance) { | 390 | getThumbnailName = 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 | ||
394 | getPreviewName = function (this: VideoInstance) { | 396 | getPreviewName = 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 | ||
402 | getTorrentName = function (this: VideoInstance) { | 401 | getTorrentName = 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 | ||
410 | isOwned = function (this: VideoInstance) { | 406 | isOwned = function (this: VideoInstance) { |
411 | return this.remoteId === null | 407 | return this.remote === false |
412 | } | 408 | } |
413 | 409 | ||
414 | toFormatedJSON = function (this: VideoInstance) { | 410 | toFormatedJSON = 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 | ||
494 | toUpdateRemoteJSON = function (this: VideoInstance) { | 491 | toUpdateRemoteJSON = 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 | ||
618 | loadByHostAndRemoteId = function (fromHost: string, remoteId: string) { | 615 | loadByHostAndUUID = 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 | ||
642 | listOwnedAndPopulateAuthorAndTags = function () { | 639 | listOwnedAndPopulateAuthorAndTags = 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 () { | |||
654 | listOwnedByAuthor = function (author: string) { | 650 | listOwnedByAuthor = 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 | ||
672 | load = function (id: string) { | 668 | load = function (id: number) { |
673 | return Video.findById(id) | 669 | return Video.findById(id) |
674 | } | 670 | } |
675 | 671 | ||
676 | loadAndPopulateAuthor = function (id: string) { | 672 | loadByUUID = function (uuid: string) { |
673 | const query = { | ||
674 | where: { | ||
675 | uuid | ||
676 | } | ||
677 | } | ||
678 | return Video.findOne(query) | ||
679 | } | ||
680 | |||
681 | loadAndPopulateAuthor = 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 | ||
684 | loadAndPopulateAuthorAndPodAndTags = function (id: string) { | 689 | loadAndPopulateAuthorAndPodAndTags = 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 | ||
703 | loadByUUIDAndPopulateAuthorAndPodAndTags = 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 | |||
698 | searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) { | 720 | searchAndPopulateAuthorAndPodAndTags = 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') | |||
20 | describe('Test multiple pods', function () { | 20 | describe('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') | |||
19 | describe('Test a single pod', function () { | 19 | describe('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 | }) |
diff --git a/shared/models/pods/remote-video/remote-qadu-video-request.model.ts b/shared/models/pods/remote-video/remote-qadu-video-request.model.ts index 0f84e3f13..6128c5801 100644 --- a/shared/models/pods/remote-video/remote-qadu-video-request.model.ts +++ b/shared/models/pods/remote-video/remote-qadu-video-request.model.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | export interface RemoteQaduVideoData { | 1 | export interface RemoteQaduVideoData { |
2 | remoteId: string | 2 | uuid: string |
3 | views?: number | 3 | views?: number |
4 | likes?: number | 4 | likes?: number |
5 | dislikes?: number | 5 | dislikes?: number |
diff --git a/shared/models/pods/remote-video/remote-video-create-request.model.ts b/shared/models/pods/remote-video/remote-video-create-request.model.ts index 6babb0083..b6a570e42 100644 --- a/shared/models/pods/remote-video/remote-video-create-request.model.ts +++ b/shared/models/pods/remote-video/remote-video-create-request.model.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { RemoteVideoRequest } from './remote-video-request.model' | 1 | import { RemoteVideoRequest } from './remote-video-request.model' |
2 | 2 | ||
3 | export interface RemoteVideoCreateData { | 3 | export interface RemoteVideoCreateData { |
4 | remoteId: string | 4 | uuid: string |
5 | author: string | 5 | author: string |
6 | tags: string[] | 6 | tags: string[] |
7 | name: string | 7 | name: string |
diff --git a/shared/models/pods/remote-video/remote-video-event-request.model.ts b/shared/models/pods/remote-video/remote-video-event-request.model.ts index 84df1ca2f..ecc374750 100644 --- a/shared/models/pods/remote-video/remote-video-event-request.model.ts +++ b/shared/models/pods/remote-video/remote-video-event-request.model.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | export type RemoteVideoEventType = 'views' | 'likes' | 'dislikes' | 1 | export type RemoteVideoEventType = 'views' | 'likes' | 'dislikes' |
2 | 2 | ||
3 | export interface RemoteVideoEventData { | 3 | export interface RemoteVideoEventData { |
4 | remoteId: string | 4 | uuid: string |
5 | eventType: RemoteVideoEventType | 5 | eventType: RemoteVideoEventType |
6 | count: number | 6 | count: number |
7 | } | 7 | } |
diff --git a/shared/models/pods/remote-video/remote-video-remove-request.model.ts b/shared/models/pods/remote-video/remote-video-remove-request.model.ts index 95ee38661..0686dc8ab 100644 --- a/shared/models/pods/remote-video/remote-video-remove-request.model.ts +++ b/shared/models/pods/remote-video/remote-video-remove-request.model.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { RemoteVideoRequest } from './remote-video-request.model' | 1 | import { RemoteVideoRequest } from './remote-video-request.model' |
2 | 2 | ||
3 | export interface RemoteVideoRemoveData { | 3 | export interface RemoteVideoRemoveData { |
4 | remoteId: string | 4 | uuid: string |
5 | } | 5 | } |
6 | 6 | ||
7 | export interface RemoteVideoRemoveRequest extends RemoteVideoRequest { | 7 | export interface RemoteVideoRemoveRequest extends RemoteVideoRequest { |
diff --git a/shared/models/pods/remote-video/remote-video-report-abuse-request.model.ts b/shared/models/pods/remote-video/remote-video-report-abuse-request.model.ts index 9059c92e0..05ad132cf 100644 --- a/shared/models/pods/remote-video/remote-video-report-abuse-request.model.ts +++ b/shared/models/pods/remote-video/remote-video-report-abuse-request.model.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { RemoteVideoRequest } from './remote-video-request.model' | 1 | import { RemoteVideoRequest } from './remote-video-request.model' |
2 | 2 | ||
3 | export interface RemoteVideoReportAbuseData { | 3 | export interface RemoteVideoReportAbuseData { |
4 | videoRemoteId: string | 4 | videoUUID: string |
5 | reporterUsername: string | 5 | reporterUsername: string |
6 | reportReason: string | 6 | reportReason: string |
7 | } | 7 | } |
diff --git a/shared/models/pods/remote-video/remote-video-update-request.model.ts b/shared/models/pods/remote-video/remote-video-update-request.model.ts index 67ddbc1e4..805548563 100644 --- a/shared/models/pods/remote-video/remote-video-update-request.model.ts +++ b/shared/models/pods/remote-video/remote-video-update-request.model.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | export interface RemoteVideoUpdateData { | 1 | export interface RemoteVideoUpdateData { |
2 | remoteId: string | 2 | uuid: string |
3 | tags: string[] | 3 | tags: string[] |
4 | name: string | 4 | name: string |
5 | extname: string | 5 | extname: string |
diff --git a/shared/models/videos/user-video-rate.model.ts b/shared/models/videos/user-video-rate.model.ts index 4b9c574d8..d39a1c3d5 100644 --- a/shared/models/videos/user-video-rate.model.ts +++ b/shared/models/videos/user-video-rate.model.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { UserVideoRateType } from './user-video-rate.type' | 1 | import { UserVideoRateType } from './user-video-rate.type' |
2 | 2 | ||
3 | export interface UserVideoRate { | 3 | export interface UserVideoRate { |
4 | videoId: string | 4 | videoId: number |
5 | rating: UserVideoRateType | 5 | rating: UserVideoRateType |
6 | } | 6 | } |
diff --git a/shared/models/videos/video-abuse.model.ts b/shared/models/videos/video-abuse.model.ts index 72e32cbc7..e005a1fd5 100644 --- a/shared/models/videos/video-abuse.model.ts +++ b/shared/models/videos/video-abuse.model.ts | |||
@@ -3,6 +3,6 @@ export interface VideoAbuse { | |||
3 | reporterPodHost: string | 3 | reporterPodHost: string |
4 | reason: string | 4 | reason: string |
5 | reporterUsername: string | 5 | reporterUsername: string |
6 | videoId: string | 6 | videoId: number |
7 | createdAt: Date | 7 | createdAt: Date |
8 | } | 8 | } |
diff --git a/shared/models/videos/video-blacklist.model.ts b/shared/models/videos/video-blacklist.model.ts index f894bb065..6086250ac 100644 --- a/shared/models/videos/video-blacklist.model.ts +++ b/shared/models/videos/video-blacklist.model.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | export interface BlacklistedVideo { | 1 | export interface BlacklistedVideo { |
2 | id: number | 2 | id: number |
3 | videoId: string | 3 | videoId: number |
4 | createdAt: Date | 4 | createdAt: Date |
5 | } | 5 | } |
diff --git a/shared/models/videos/video.model.ts b/shared/models/videos/video.model.ts index 2e35f005c..d472cc8fb 100644 --- a/shared/models/videos/video.model.ts +++ b/shared/models/videos/video.model.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | export interface Video { | 1 | export interface Video { |
2 | id: string | 2 | id: number |
3 | uuid: string | ||
3 | author: string | 4 | author: string |
4 | createdAt: Date | 5 | createdAt: Date |
5 | categoryLabel: string | 6 | categoryLabel: string |