Use global uuid instead of remoteId for videos
authorChocobozzz <florian.bigard@gmail.com>
Tue, 11 Jul 2017 14:01:56 +0000 (16:01 +0200)
committerChocobozzz <florian.bigard@gmail.com>
Tue, 11 Jul 2017 14:01:56 +0000 (16:01 +0200)
58 files changed:
client/src/app/shared/video-abuse/video-abuse.service.ts
client/src/app/videos/shared/video.model.ts
client/src/app/videos/shared/video.service.ts
client/src/app/videos/video-edit/video-update.component.ts
client/src/app/videos/video-list/video-miniature.component.html
client/src/app/videos/video-watch/video-share.component.ts
client/src/app/videos/video-watch/video-watch.component.html
client/src/app/videos/video-watch/video-watch.component.ts
client/src/app/videos/videos-routing.module.ts
server/controllers/api/remote/videos.ts
server/controllers/api/users.ts
server/controllers/api/videos/abuse.ts
server/controllers/api/videos/index.ts
server/controllers/api/videos/rate.ts
server/controllers/client.ts
server/helpers/custom-validators/remote/videos.ts
server/helpers/custom-validators/videos.ts
server/initializers/constants.ts
server/initializers/migrations/0005-email-pod.ts
server/initializers/migrations/0010-email-user.ts
server/initializers/migrations/0015-video-views.ts
server/initializers/migrations/0020-video-likes.ts
server/initializers/migrations/0025-video-dislikes.ts
server/initializers/migrations/0030-video-category.ts
server/initializers/migrations/0035-video-licence.ts
server/initializers/migrations/0040-video-nsfw.ts
server/initializers/migrations/0045-user-display-nsfw.ts
server/initializers/migrations/0050-video-language.ts
server/initializers/migrations/0055-video-uuid.ts [new file with mode: 0644]
server/initializers/migrator.ts
server/lib/friends.ts
server/lib/jobs/handlers/video-transcoder.ts
server/lib/request/request-video-event-scheduler.ts
server/lib/request/request-video-qadu-scheduler.ts
server/middlewares/validators/users.ts
server/middlewares/validators/videos.ts
server/models/user/user-video-rate-interface.ts
server/models/user/user-video-rate.ts
server/models/video/tag.ts
server/models/video/video-abuse-interface.ts
server/models/video/video-abuse.ts
server/models/video/video-blacklist-interface.ts
server/models/video/video-blacklist.ts
server/models/video/video-interface.ts
server/models/video/video.ts
server/tests/api/check-params/remotes.js
server/tests/api/multiple-pods.js
server/tests/api/single-pod.js
shared/models/pods/remote-video/remote-qadu-video-request.model.ts
shared/models/pods/remote-video/remote-video-create-request.model.ts
shared/models/pods/remote-video/remote-video-event-request.model.ts
shared/models/pods/remote-video/remote-video-remove-request.model.ts
shared/models/pods/remote-video/remote-video-report-abuse-request.model.ts
shared/models/pods/remote-video/remote-video-update-request.model.ts
shared/models/videos/user-video-rate.model.ts
shared/models/videos/video-abuse.model.ts
shared/models/videos/video-blacklist.model.ts
shared/models/videos/video.model.ts

index 32f13b4300cb4a946e80439363667db7e9bc2951..636a02084ccbf72f962b6e9e14b191b6a7d0e258 100644 (file)
@@ -22,7 +22,7 @@ export class VideoAbuseService {
     return new RestDataSource(this.authHttp, VideoAbuseService.BASE_VIDEO_ABUSE_URL + 'abuse')
   }
 
-  reportVideo (id: string, reason: string) {
+  reportVideo (id: number, reason: string) {
     const body = {
       reason
     }
index f5e16fc13f9ffc1ef10c4bf3c56ddcb5858dbc56..9ed6a064195389a8a4cda150bbf637dcfc2472bc 100644 (file)
@@ -14,7 +14,8 @@ export class Video implements VideoServerModel {
   description: string
   duration: number
   durationLabel: string
-  id: string
+  id: number
+  uuid: string
   isLocal: boolean
   magnetUri: string
   name: string
@@ -51,7 +52,8 @@ export class Video implements VideoServerModel {
     language: number
     description: string,
     duration: number
-    id: string,
+    id: number,
+    uuid: string,
     isLocal: boolean,
     magnetUri: string,
     name: string,
@@ -75,6 +77,7 @@ export class Video implements VideoServerModel {
     this.duration = hash.duration
     this.durationLabel = Video.createDurationString(hash.duration)
     this.id = hash.id
+    this.uuid = hash.uuid
     this.isLocal = hash.isLocal
     this.magnetUri = hash.magnetUri
     this.name = hash.name
index dc12c0637b22f0ef3b6cf02db96accadc96f5be4..67091a8d852287594e784cf4934f003cf3cff990 100644 (file)
@@ -52,8 +52,8 @@ export class VideoService {
     return this.loadVideoAttributeEnum('languages', this.videoLanguages)
   }
 
-  getVideo (id: string): Observable<Video> {
-    return this.http.get(VideoService.BASE_VIDEO_URL + id)
+  getVideo (uuid: string): Observable<Video> {
+    return this.http.get(VideoService.BASE_VIDEO_URL + uuid)
                     .map(this.restExtractor.extractDataGet)
                     .map(videoHash => new Video(videoHash))
                     .catch((res) => this.restExtractor.handleError(res))
@@ -89,7 +89,7 @@ export class VideoService {
                     .catch((res) => this.restExtractor.handleError(res))
   }
 
-  removeVideo (id: string) {
+  removeVideo (id: number) {
     return this.authHttp.delete(VideoService.BASE_VIDEO_URL + id)
                         .map(this.restExtractor.extractDataBool)
                         .catch((res) => this.restExtractor.handleError(res))
@@ -106,7 +106,7 @@ export class VideoService {
                     .catch((res) => this.restExtractor.handleError(res))
   }
 
-  reportVideo (id: string, reason: string) {
+  reportVideo (id: number, reason: string) {
     const url = VideoService.BASE_VIDEO_URL + id + '/abuse'
     const body: VideoAbuseCreate = {
       reason
@@ -117,15 +117,15 @@ export class VideoService {
                         .catch((res) => this.restExtractor.handleError(res))
   }
 
-  setVideoLike (id: string) {
+  setVideoLike (id: number) {
     return this.setVideoRate(id, 'like')
   }
 
-  setVideoDislike (id: string) {
+  setVideoDislike (id: number) {
     return this.setVideoRate(id, 'dislike')
   }
 
-  getUserVideoRating (id: string): Observable<UserVideoRate> {
+  getUserVideoRating (id: number): Observable<UserVideoRate> {
     const url = UserService.BASE_USERS_URL + '/me/videos/' + id + '/rating'
 
     return this.authHttp.get(url)
@@ -133,13 +133,13 @@ export class VideoService {
                         .catch((res) => this.restExtractor.handleError(res))
   }
 
-  blacklistVideo (id: string) {
+  blacklistVideo (id: number) {
     return this.authHttp.post(VideoService.BASE_VIDEO_URL + id + '/blacklist', {})
                         .map(this.restExtractor.extractDataBool)
                         .catch((res) => this.restExtractor.handleError(res))
   }
 
-  private setVideoRate (id: string, rateType: VideoRateType) {
+  private setVideoRate (id: number, rateType: VideoRateType) {
     const url = VideoService.BASE_VIDEO_URL + id + '/rate'
     const body: UserVideoRateUpdate = {
       rating: rateType
index 9ee7ca6a8cb81b0df17ee4125c36c046a90929c9..6c57a23d8c8e5783cffaaf69e7f3c8394dc298f0 100644 (file)
@@ -85,8 +85,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
     this.videoLicences = this.videoService.videoLicences
     this.videoLanguages = this.videoService.videoLanguages
 
-    const id = this.route.snapshot.params['id']
-    this.videoService.getVideo(id)
+    const uuid: string = this.route.snapshot.params['uuid']
+    this.videoService.getVideo(uuid)
                      .subscribe(
                        video => {
                          this.video = video
@@ -118,7 +118,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
                      .subscribe(
                        () => {
                          this.notificationsService.success('Success', 'Video updated.')
-                         this.router.navigate([ '/videos/watch', this.video.id ])
+                         this.router.navigate([ '/videos/watch', this.video.uuid ])
                        },
 
                        err => {
index db6c4d6fbb6d67739300e86457441b0cc480a1ef..6525b67bfb32f0aeb2304827c7cb1172df0059f9 100644 (file)
@@ -1,6 +1,6 @@
 <div class="video-miniature">
   <a
-    [routerLink]="['/videos/watch', video.id]" [attr.title]="video.description"
+    [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.description"
     class="video-miniature-thumbnail"
   >
     <img *ngIf="isVideoNSFWForThisUser() === false" [attr.src]="video.thumbnailUrl" alt="video thumbnail" />
@@ -16,7 +16,7 @@
 
   <div class="video-miniature-informations">
     <span class="video-miniature-name">
-      <a [routerLink]="['/videos/watch', video.id]" [attr.title]="getVideoName()" class="video-miniature-name">{{ getVideoName() }}</a>
+      <a [routerLink]="['/videos/watch', video.uuid]" [attr.title]="getVideoName()" class="video-miniature-name">{{ getVideoName() }}</a>
     </span>
 
     <div class="video-miniature-tags">
index bbd25f5ef0e8963a8fe093f3339d1caebe742342..133f9349809d5e0b4efbb958f055d24656aa2e0a 100644 (file)
@@ -27,7 +27,7 @@ export class VideoShareComponent {
 
   getVideoIframeCode () {
     return '<iframe width="560" height="315" ' +
-           'src="' + window.location.origin + '/videos/embed/' + this.video.id + '" ' +
+           'src="' + window.location.origin + '/videos/embed/' + this.video.uuid + '" ' +
            'frameborder="0" allowfullscreen>' +
            '</iframe>'
   }
index 8676b5b72e329f559c4579cb6f3650c030e41bbe..88863131af5018639ae78f43947ad84ed7c1b26b 100644 (file)
@@ -65,7 +65,7 @@
 
         <ul *dropdownMenu class="dropdown-menu" id="more-menu" role="menu" aria-labelledby="single-button">
           <li *ngIf="canUserUpdateVideo()" role="menuitem">
-            <a class="dropdown-item" title="Update this video" href="#" [routerLink]="[ '/videos/edit', video.id ]">
+            <a class="dropdown-item" title="Update this video" href="#" [routerLink]="[ '/videos/edit', video.uuid ]">
               <span class="glyphicon glyphicon-pencil"></span> Update
             </a>
           </li>
index 6bd6c1f7eaff4123d0ae2920f6809dde53c47c45..104ba0db6d8dbc055588eb1e90b503664792c241 100644 (file)
@@ -58,8 +58,8 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
 
   ngOnInit () {
     this.paramsSub = this.route.params.subscribe(routeParams => {
-      let id = routeParams['id']
-      this.videoService.getVideo(id).subscribe(
+      let uuid = routeParams['uuid']
+      this.videoService.getVideo(uuid).subscribe(
         video => this.onVideoFetched(video),
 
         error => {
index e18c1cec00d6b9acbe4b25169ef4e02d53b697da..715671ba734be670dc402dd5956e9f49672f9849 100644 (file)
@@ -33,7 +33,7 @@ const videosRoutes: Routes = [
         }
       },
       {
-        path: 'edit/:id',
+        path: 'edit/:uuid',
         component: VideoUpdateComponent,
         data: {
           meta: {
@@ -42,11 +42,11 @@ const videosRoutes: Routes = [
         }
       },
       {
-        path: ':id',
-        redirectTo: 'watch/:id'
+        path: ':uuid',
+        redirectTo: 'watch/:uuid'
       },
       {
-        path: 'watch/:id',
+        path: 'watch/:uuid',
         component: VideoWatchComponent
       }
     ]
index 96eab6d52c058f2bfcfef0acc3a4d07a27f57a8b..30771d8c4f01b6147d2dae2160b065fc6da8eed4 100644 (file)
@@ -133,7 +133,7 @@ function processVideosEventsRetryWrapper (eventData: RemoteVideoEventData, fromP
 function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInstance) {
 
   return db.sequelize.transaction(t => {
-    return fetchOwnedVideo(eventData.remoteId)
+    return fetchVideoByUUID(eventData.uuid)
       .then(videoInstance => {
         const options = { transaction: t }
 
@@ -176,7 +176,7 @@ function processVideosEvents (eventData: RemoteVideoEventData, fromPod: PodInsta
         return quickAndDirtyUpdatesVideoToFriends(qadusParams, t)
       })
   })
-  .then(() => logger.info('Remote video event processed for video %s.', eventData.remoteId))
+  .then(() => logger.info('Remote video event processed for video %s.', eventData.uuid))
   .catch(err => {
     logger.debug('Cannot process a video event.', err)
     throw err
@@ -196,7 +196,7 @@ function quickAndDirtyUpdateVideo (videoData: RemoteQaduVideoData, fromPod: PodI
   let videoName
 
   return db.sequelize.transaction(t => {
-    return fetchRemoteVideo(fromPod.host, videoData.remoteId)
+    return fetchVideoByHostAndUUID(fromPod.host, videoData.uuid)
       .then(videoInstance => {
         const options = { transaction: t }
 
@@ -232,12 +232,12 @@ function addRemoteVideoRetryWrapper (videoToCreateData: RemoteVideoCreateData, f
 }
 
 function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodInstance) {
-  logger.debug('Adding remote video "%s".', videoToCreateData.remoteId)
+  logger.debug('Adding remote video "%s".', videoToCreateData.uuid)
 
   return db.sequelize.transaction(t => {
-    return db.Video.loadByHostAndRemoteId(fromPod.host, videoToCreateData.remoteId)
+    return db.Video.loadByUUID(videoToCreateData.uuid)
       .then(video => {
-        if (video) throw new Error('RemoteId and host pair is not unique.')
+        if (video) throw new Error('UUID already exists.')
 
         return undefined
       })
@@ -257,7 +257,7 @@ function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodI
       .then(({ author, tagInstances }) => {
         const videoData = {
           name: videoToCreateData.name,
-          remoteId: videoToCreateData.remoteId,
+          uuid: videoToCreateData.uuid,
           extname: videoToCreateData.extname,
           infoHash: videoToCreateData.infoHash,
           category: videoToCreateData.category,
@@ -272,7 +272,8 @@ function addRemoteVideo (videoToCreateData: RemoteVideoCreateData, fromPod: PodI
           updatedAt: videoToCreateData.updatedAt,
           views: videoToCreateData.views,
           likes: videoToCreateData.likes,
-          dislikes: videoToCreateData.dislikes
+          dislikes: videoToCreateData.dislikes,
+          remote: true
         }
 
         const video = db.Video.build(videoData)
@@ -314,10 +315,10 @@ function updateRemoteVideoRetryWrapper (videoAttributesToUpdate: RemoteVideoUpda
 }
 
 function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, fromPod: PodInstance) {
-  logger.debug('Updating remote video "%s".', videoAttributesToUpdate.remoteId)
+  logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
 
   return db.sequelize.transaction(t => {
-    return fetchRemoteVideo(fromPod.host, videoAttributesToUpdate.remoteId)
+    return fetchVideoByHostAndUUID(fromPod.host, videoAttributesToUpdate.uuid)
       .then(videoInstance => {
         const tags = videoAttributesToUpdate.tags
 
@@ -359,18 +360,18 @@ function updateRemoteVideo (videoAttributesToUpdate: RemoteVideoUpdateData, from
 
 function removeRemoteVideo (videoToRemoveData: RemoteVideoRemoveData, fromPod: PodInstance) {
   // We need the instance because we have to remove some other stuffs (thumbnail etc)
-  return fetchRemoteVideo(fromPod.host, videoToRemoveData.remoteId)
+  return fetchVideoByHostAndUUID(fromPod.host, videoToRemoveData.uuid)
     .then(video => {
-      logger.debug('Removing remote video %s.', video.remoteId)
+      logger.debug('Removing remote video %s.', video.uuid)
       return video.destroy()
     })
     .catch(err => {
-      logger.debug('Could not fetch remote video.', { host: fromPod.host, remoteId: videoToRemoveData.remoteId, error: err.stack })
+      logger.debug('Could not fetch remote video.', { host: fromPod.host, uuid: videoToRemoveData.uuid, error: err.stack })
     })
 }
 
 function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod: PodInstance) {
-  return fetchOwnedVideo(reportData.videoRemoteId)
+  return fetchVideoByUUID(reportData.videoUUID)
     .then(video => {
       logger.debug('Reporting remote abuse for video %s.', video.id)
 
@@ -386,8 +387,8 @@ function reportAbuseRemoteVideo (reportData: RemoteVideoReportAbuseData, fromPod
     .catch(err => logger.error('Cannot create remote abuse video.', err))
 }
 
-function fetchOwnedVideo (id: string) {
-  return db.Video.load(id)
+function fetchVideoByUUID (id: string) {
+  return db.Video.loadByUUID(id)
     .then(video => {
       if (!video) throw new Error('Video not found')
 
@@ -399,15 +400,15 @@ function fetchOwnedVideo (id: string) {
     })
 }
 
-function fetchRemoteVideo (podHost: string, remoteId: string) {
-  return db.Video.loadByHostAndRemoteId(podHost, remoteId)
+function fetchVideoByHostAndUUID (podHost: string, uuid: string) {
+  return db.Video.loadByHostAndUUID(podHost, uuid)
     .then(video => {
       if (!video) throw new Error('Video not found')
 
       return video
     })
     .catch(err => {
-      logger.error('Cannot load video from host and remote id.', { error: err.stack, podHost, remoteId })
+      logger.error('Cannot load video from host and uuid.', { error: err.stack, podHost, uuid })
       throw err
     })
 }
index e794805214084d8b03e69919e759f5a627915557..6c375cc5b1b1d2aeb9f017b71c4001e91d669017 100644 (file)
@@ -100,7 +100,7 @@ function getUserInformation (req: express.Request, res: express.Response, next:
 }
 
 function getUserVideoRating (req: express.Request, res: express.Response, next: express.NextFunction) {
-  const videoId = '' + req.params.videoId
+  const videoId = +req.params.videoId
   const userId = +res.locals.oauth.token.User.id
 
   db.UserVideoRate.load(userId, videoId, null)
index 7d2e3bcfbd2c792907cc430678bc9a6f0d24e583..5cf0303fbf92bee0722a9aca39fceaa858fd1d7d 100644 (file)
@@ -62,7 +62,7 @@ function reportVideoAbuseRetryWrapper (req: express.Request, res: express.Respon
 }
 
 function reportVideoAbuse (req: express.Request, res: express.Response) {
-  const videoInstance = res.locals.video
+  const videoInstance = res.locals.video as VideoInstance
   const reporterUsername = res.locals.oauth.token.User.username
   const body: VideoAbuseCreate = req.body
 
@@ -81,7 +81,7 @@ function reportVideoAbuse (req: express.Request, res: express.Response) {
           const reportData = {
             reporterUsername,
             reportReason: abuse.reason,
-            videoRemoteId: videoInstance.remoteId
+            videoUUID: videoInstance.uuid
           }
 
           return friends.reportAbuseVideoToFriend(reportData, videoInstance, t).then(() => videoInstance)
index 4ae7ea2edbd90f9e878fa80968703cf2b01dfdb3..e70a5319ec0b1b3ca6790ef99acfbca6fd070906 100644 (file)
@@ -176,7 +176,7 @@ function addVideo (req: express.Request, res: express.Response, videoFile: Expre
       .then(({ author, tagInstances }) => {
         const videoData = {
           name: videoInfos.name,
-          remoteId: null,
+          remote: false,
           extname: path.extname(videoFile.filename),
           category: videoInfos.category,
           licence: videoInfos.licence,
index 8456cbaf24ec48685b6d2ed0348f57545fdefcaf..6ddc698173f4e2d2d66cfc99190dad7b31a3a437 100644 (file)
@@ -69,7 +69,7 @@ function rateVideo (req: express.Request, res: express.Response) {
 
         // There was a previous rate, update it
         if (previousRate) {
-          // We will remove the previous rate, so we will need to remove it from the video attribute
+          // We will remove the previous rate, so we will need to update the video count attribute
           if (previousRate.type === VIDEO_RATE_TYPES.LIKE) likesToIncrement--
           else if (previousRate.type === VIDEO_RATE_TYPES.DISLIKE) dislikesToIncrement--
 
index d42e8396df39be22621d0bbcbb0d540bbc506a9b..ac722a578936f1b1adc392756477210cdc694bc2 100644 (file)
@@ -78,7 +78,7 @@ function addOpenGraphTags (htmlStringPage: string, video: VideoInstance) {
   }
 
   let tagsString = ''
-  Object.keys(metaTags).forEach(function (tagName) {
+  Object.keys(metaTags).forEach(tagName => {
     const tagValue = metaTags[tagName]
 
     tagsString += '<meta property="' + tagName + '" content="' + tagValue + '" />'
@@ -89,13 +89,20 @@ function addOpenGraphTags (htmlStringPage: string, video: VideoInstance) {
 
 function generateWatchHtmlPage (req: express.Request, res: express.Response, next: express.NextFunction) {
   const videoId = '' + req.params.id
+  let videoPromise: Promise<VideoInstance>
 
   // Let Angular application handle errors
-  if (!validator.isUUID(videoId, 4)) return res.sendFile(indexPath)
+  if (validator.isUUID(videoId, 4)) {
+    videoPromise = db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(videoId)
+  } else if (validator.isInt(videoId)) {
+    videoPromise = db.Video.loadAndPopulateAuthorAndPodAndTags(+videoId)
+  } else {
+    return res.sendFile(indexPath)
+  }
 
   Promise.all([
     readFileBufferPromise(indexPath),
-    db.Video.loadAndPopulateAuthorAndPodAndTags(videoId)
+    videoPromise
   ])
   .then(([ file, video ]) => {
     file = file as Buffer
index 1df7316aa8cbc2cc7cb766bfa8ea19ed31143adb..e14673cb36a717844b72b1ddd8396754291af4b2 100644 (file)
@@ -9,7 +9,7 @@ import { isArray } from '../misc'
 import {
   isVideoAuthorValid,
   isVideoThumbnailDataValid,
-  isVideoRemoteIdValid,
+  isVideoUUIDValid,
   isVideoAbuseReasonValid,
   isVideoAbuseReporterUsernameValid,
   isVideoViewsValid,
@@ -50,11 +50,11 @@ function isEachRemoteRequestVideosValid (requests: any[]) {
       ) ||
       (
         isRequestTypeRemoveValid(request.type) &&
-        isVideoRemoteIdValid(video.remoteId)
+        isVideoUUIDValid(video.uuid)
       ) ||
       (
         isRequestTypeReportAbuseValid(request.type) &&
-        isVideoRemoteIdValid(request.data.videoRemoteId) &&
+        isVideoUUIDValid(request.data.videoUUID) &&
         isVideoAbuseReasonValid(request.data.reportReason) &&
         isVideoAbuseReporterUsernameValid(request.data.reporterUsername)
       )
@@ -69,7 +69,7 @@ function isEachRemoteRequestVideosQaduValid (requests: any[]) {
       if (!video) return false
 
       return (
-        isVideoRemoteIdValid(video.remoteId) &&
+        isVideoUUIDValid(video.uuid) &&
         (has(video, 'views') === false || isVideoViewsValid(video.views)) &&
         (has(video, 'likes') === false || isVideoLikesValid(video.likes)) &&
         (has(video, 'dislikes') === false || isVideoDislikesValid(video.dislikes))
@@ -85,7 +85,7 @@ function isEachRemoteRequestVideosEventsValid (requests: any[]) {
       if (!eventData) return false
 
       return (
-        isVideoRemoteIdValid(eventData.remoteId) &&
+        isVideoUUIDValid(eventData.uuid) &&
         values(REQUEST_VIDEO_EVENT_TYPES).indexOf(eventData.eventType) !== -1 &&
         isVideoEventCountValid(eventData.count)
       )
@@ -124,7 +124,7 @@ function isCommonVideoAttributesValid (video: any) {
          isVideoInfoHashValid(video.infoHash) &&
          isVideoNameValid(video.name) &&
          isVideoTagsValid(video.tags) &&
-         isVideoRemoteIdValid(video.remoteId) &&
+         isVideoUUIDValid(video.uuid) &&
          isVideoExtnameValid(video.extname) &&
          isVideoViewsValid(video.views) &&
          isVideoLikesValid(video.likes) &&
index 72d226e81e6813802b369e197cddb4ed9d01877e..e335b09d19d4e9b9be914fd0fca84370f8954f7a 100644 (file)
@@ -17,6 +17,10 @@ const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
 const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
 const VIDEO_EVENTS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_EVENTS
 
+function isVideoIdOrUUIDValid (value: string) {
+  return validator.isInt(value) || isVideoUUIDValid(value)
+}
+
 function isVideoAuthorValid (value: string) {
   return isUserUsernameValid(value)
 }
@@ -77,8 +81,8 @@ function isVideoThumbnailDataValid (value: string) {
   return exists(value) && validator.isByteLength(value, VIDEOS_CONSTRAINTS_FIELDS.THUMBNAIL_DATA)
 }
 
-function isVideoRemoteIdValid (value: string) {
-  return exists(value) && validator.isUUID(value, 4)
+function isVideoUUIDValid (value: string) {
+  return exists(value) && validator.isUUID('' + value, 4)
 }
 
 function isVideoAbuseReasonValid (value: string) {
@@ -127,6 +131,7 @@ function isVideoFile (value: string, files: { [ fieldname: string ]: Express.Mul
 // ---------------------------------------------------------------------------
 
 export {
+  isVideoIdOrUUIDValid,
   isVideoAuthorValid,
   isVideoDateValid,
   isVideoCategoryValid,
@@ -141,7 +146,7 @@ export {
   isVideoThumbnailValid,
   isVideoThumbnailDataValid,
   isVideoExtnameValid,
-  isVideoRemoteIdValid,
+  isVideoUUIDValid,
   isVideoAbuseReasonValid,
   isVideoAbuseReporterUsernameValid,
   isVideoFile,
@@ -155,6 +160,7 @@ export {
 declare global {
   namespace ExpressValidator {
     export interface Validator {
+      isVideoIdOrUUIDValid,
       isVideoAuthorValid,
       isVideoDateValid,
       isVideoCategoryValid,
@@ -169,7 +175,7 @@ declare global {
       isVideoThumbnailValid,
       isVideoThumbnailDataValid,
       isVideoExtnameValid,
-      isVideoRemoteIdValid,
+      isVideoUUIDValid,
       isVideoAbuseReasonValid,
       isVideoAbuseReporterUsernameValid,
       isVideoFile,
index 2792d32283b5e8a259111df22734b1466385a052..f087b7476a95d8779fe2f6fa32abda5a3ea8760d 100644 (file)
@@ -15,7 +15,7 @@ import {
 
 // ---------------------------------------------------------------------------
 
-const LAST_MIGRATION_VERSION = 50
+const LAST_MIGRATION_VERSION = 55
 
 // ---------------------------------------------------------------------------
 
index ceefaad4a5f1b059cb20c03ad3ae0eb1e6773c98..ab60f3adb4c9af5fa31eca8abc7b6534d031539f 100644 (file)
@@ -26,7 +26,7 @@ function up (utils: {
     })
 }
 
-function down (options, callback) {
+function down (options) {
   throw new Error('Not implemented.')
 }
 
index e8865acdb9e2ffceb576ca48b8e825242db8a4a6..33d13ce5518c6ff9d2ac658132788ce426965210 100644 (file)
@@ -25,7 +25,7 @@ function up (utils: {
     })
 }
 
-function down (options, callback) {
+function down (options) {
   throw new Error('Not implemented.')
 }
 
index df274d81740d78b12cf419dc1e7bf738074720c6..25164ff4d55c5827eb475ff56634741d54b60ad9 100644 (file)
@@ -17,7 +17,7 @@ function up (utils: {
   return q.addColumn('Videos', 'views', data)
 }
 
-function down (options, callback) {
+function down (options) {
   throw new Error('Not implemented.')
 }
 
index 3d7182d0a0d459d62b30c7ce0111b089e419832f..945be5a98d1ff1f1259c24976104eab9fcb0b28f 100644 (file)
@@ -17,7 +17,7 @@ function up (utils: {
   return q.addColumn('Videos', 'likes', data)
 }
 
-function down (options, callback) {
+function down (options) {
   throw new Error('Not implemented.')
 }
 
index ed41095dcc7537896e7025201fdd263311312486..27144c4373b21c800a42f10638cb4c7547de3841 100644 (file)
@@ -17,7 +17,7 @@ function up (utils: {
   return q.addColumn('Videos', 'dislikes', data)
 }
 
-function down (options, callback) {
+function down (options) {
   throw new Error('Not implemented.')
 }
 
index f5adee8f9122ace8e67614b21316fc9e492b8e6b..41bc1aa9821e37048015e0b61b6e57aecfe9605e 100644 (file)
@@ -22,7 +22,7 @@ function up (utils: {
     })
 }
 
-function down (options, callback) {
+function down (options) {
   throw new Error('Not implemented.')
 }
 
index 00c64d8e7a4068aa0d69ceecd770ecdff5921869..7ab49e1476663e7d6e5619203ab46f2f5c9254e9 100644 (file)
@@ -21,7 +21,7 @@ function up (utils: {
     })
 }
 
-function down (options, callback) {
+function down (options) {
   throw new Error('Not implemented.')
 }
 
index 046876b61ebe7d2c520dcbfa789f0d0d88da15aa..0460e661da4a7bd0201c7d5873ddd6e195bad8f8 100644 (file)
@@ -22,7 +22,7 @@ function up (utils: {
     })
 }
 
-function down (options, callback) {
+function down (options) {
   throw new Error('Not implemented.')
 }
 
index 75bd3bbeaccc01ce43739bcf72fc978bd241e4ce..aef420f0e40a9d79d1e53ef6a5a4cac5add6f514 100644 (file)
@@ -17,7 +17,7 @@ function up (utils: {
   return q.addColumn('Users', 'displayNSFW', data)
 }
 
-function down (options, callback) {
+function down (options) {
   throw new Error('Not implemented.')
 }
 
index ed08f5866efc9431dd64cf5d4e29dabb8d5d26c5..796fa5f9597d8738eedaedfb3438217077bcd016 100644 (file)
@@ -17,7 +17,7 @@ function up (utils: {
   return q.addColumn('Videos', 'language', data)
 }
 
-function down (options, callback) {
+function down (options) {
   throw new Error('Not implemented.')
 }
 
diff --git a/server/initializers/migrations/0055-video-uuid.ts b/server/initializers/migrations/0055-video-uuid.ts
new file mode 100644 (file)
index 0000000..9bc6591
--- /dev/null
@@ -0,0 +1,157 @@
+import * as Sequelize from 'sequelize'
+import * as Promise from 'bluebird'
+
+function up (utils: {
+  transaction: Sequelize.Transaction,
+  queryInterface: Sequelize.QueryInterface,
+  sequelize: Sequelize.Sequelize
+}): Promise<void> {
+  const q = utils.queryInterface
+
+  const dataUUID = {
+    type: Sequelize.UUID,
+    defaultValue: Sequelize.UUIDV4,
+    allowNull: true
+  }
+
+  return q.addColumn('Videos', 'uuid', dataUUID)
+    .then(() => {
+      const query = 'UPDATE "Videos" SET "uuid" = "id" WHERE "remoteId" IS NULL'
+      return utils.sequelize.query(query)
+    })
+    .then(() => {
+      const query = 'UPDATE "Videos" SET "uuid" = "remoteId" WHERE "remoteId" IS NOT NULL'
+      return utils.sequelize.query(query)
+    })
+    .then(() => {
+      dataUUID.defaultValue = null
+
+      return q.changeColumn('Videos', 'uuid', dataUUID)
+    })
+    .then(() => {
+      return removeForeignKey(utils.sequelize, 'RequestVideoQadus')
+    })
+    .then(() => {
+      return removeForeignKey(utils.sequelize, 'RequestVideoEvents')
+    })
+    .then(() => {
+      return removeForeignKey(utils.sequelize, 'BlacklistedVideos')
+    })
+    .then(() => {
+      return removeForeignKey(utils.sequelize, 'UserVideoRates')
+    })
+    .then(() => {
+      return removeForeignKey(utils.sequelize, 'VideoAbuses')
+    })
+    .then(() => {
+      return removeForeignKey(utils.sequelize, 'VideoTags')
+    })
+    .then(() => {
+      const query = 'ALTER TABLE "Videos" DROP CONSTRAINT "Videos_pkey"'
+      return utils.sequelize.query(query)
+    })
+    .then(() => {
+      const query = 'ALTER TABLE "Videos" ADD COLUMN "id2" SERIAL PRIMARY KEY'
+      return utils.sequelize.query(query)
+    })
+    .then(() => {
+      return q.renameColumn('Videos', 'id', 'oldId')
+    })
+    .then(() => {
+      return q.renameColumn('Videos', 'id2', 'id')
+    })
+    .then(() => {
+      return changeForeignKey(q, utils.sequelize, 'RequestVideoQadus', false)
+    })
+    .then(() => {
+      return changeForeignKey(q, utils.sequelize, 'RequestVideoEvents', false)
+    })
+    .then(() => {
+      return changeForeignKey(q, utils.sequelize, 'BlacklistedVideos', false)
+    })
+    .then(() => {
+      return changeForeignKey(q, utils.sequelize, 'UserVideoRates', false)
+    })
+    .then(() => {
+      return changeForeignKey(q, utils.sequelize, 'VideoAbuses', false)
+    })
+    .then(() => {
+      return changeForeignKey(q, utils.sequelize, 'VideoTags', true)
+    })
+    .then(() => {
+      return q.removeColumn('Videos', 'oldId')
+    })
+    .then(() => {
+      const dataRemote = {
+        type: Sequelize.BOOLEAN,
+        defaultValue: false,
+        allowNull: false
+      }
+      return q.addColumn('Videos', 'remote', dataRemote)
+    })
+    .then(() => {
+      const query = 'UPDATE "Videos" SET "remote" = false WHERE "remoteId" IS NULL'
+      return utils.sequelize.query(query)
+    })
+    .then(() => {
+      const query = 'UPDATE "Videos" SET "remote" = true WHERE "remoteId" IS NOT NULL'
+      return utils.sequelize.query(query)
+    })
+    .then(() => {
+      return q.removeColumn('Videos', 'remoteId')
+    })
+}
+
+function down (options) {
+  throw new Error('Not implemented.')
+}
+
+function removeForeignKey (sequelize: Sequelize.Sequelize, tableName: string) {
+  const query = 'ALTER TABLE "' + tableName + '" DROP CONSTRAINT "' + tableName + '_videoId_fkey' + '"'
+  return sequelize.query(query)
+}
+
+function changeForeignKey (q: Sequelize.QueryInterface, sequelize: Sequelize.Sequelize, tableName: string, allowNull: boolean) {
+  const data = {
+    type: Sequelize.INTEGER,
+    allowNull: true
+  }
+
+  return q.addColumn(tableName, 'videoId2', data)
+    .then(() => {
+      const query = 'UPDATE "' + tableName + '" SET "videoId2" = ' +
+                    '(SELECT "id" FROM "Videos" WHERE "' + tableName + '"."videoId" = "Videos"."oldId")'
+      return sequelize.query(query)
+    })
+    .then(() => {
+      if (allowNull === false) {
+        data.allowNull = false
+
+        return q.changeColumn(tableName, 'videoId2', data)
+      }
+
+      return Promise.resolve()
+    })
+    .then(() => {
+      return q.removeColumn(tableName, 'videoId')
+    })
+    .then(() => {
+      return q.renameColumn(tableName, 'videoId2', 'videoId')
+    })
+    .then(() => {
+      return q.addIndex(tableName, [ 'videoId' ])
+    })
+    .then(() => {
+      const constraintName = tableName + '_videoId_fkey'
+      const query = 'ALTER TABLE "' + tableName + '" ' +
+                    ' ADD CONSTRAINT "' + constraintName + '"' +
+                    ' FOREIGN KEY ("videoId") REFERENCES "Videos" ON DELETE CASCADE'
+
+      return sequelize.query(query)
+    })
+}
+
+export {
+  up,
+  down
+}
index 3184ec920824d47ed4076bc22cc19629fb282ac5..4b3be6d166fb901ded0be147491b127fb3320e4f 100644 (file)
@@ -96,10 +96,10 @@ function executeMigration (actualVersion: number, entity: { version: string, scr
       sequelize: db.sequelize
     }
 
-    migrationScript.up(options)
+    return migrationScript.up(options)
       .then(() => {
         // Update the new migration version
-        db.Application.updateMigrationVersion(versionScript, t)
+        return db.Application.updateMigrationVersion(versionScript, t)
       })
   })
 }
index a658201913d9a080c1e34b097d25f7a08b5c8f26..cbdc60441b0fd17543c2aa7aab037a2f88647d6c 100644 (file)
@@ -43,8 +43,8 @@ import {
   Pod as FormatedPod
 } from '../../shared'
 
-type QaduParam = { videoId: string, type: RequestVideoQaduType }
-type EventParam = { videoId: string, type: RequestVideoEventType }
+type QaduParam = { videoId: number, type: RequestVideoQaduType }
+type EventParam = { videoId: number, type: RequestVideoEventType }
 
 const ENDPOINT_ACTIONS = REQUEST_ENDPOINT_ACTIONS[REQUEST_ENDPOINTS.VIDEOS]
 
index eeb2d57b04d0d04b19ff60d1b8174630c292ba5c..0d32dfd2fcc043de2109601888ff987b5b76d0cc 100644 (file)
@@ -3,8 +3,8 @@ import { logger } from '../../../helpers'
 import { addVideoToFriends } from '../../../lib'
 import { VideoInstance } from '../../../models'
 
-function process (data: { id: string }) {
-  return db.Video.loadAndPopulateAuthorAndPodAndTags(data.id).then(video => {
+function process (data: { videoUUID: string }) {
+  return db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(data.videoUUID).then(video => {
     return video.transcodeVideofile().then(() => video)
   })
 }
index 8a008c51bbae9d3c15bb0bfc5152285c28c6c4e5..68023273299f5c7c5cf6c783c86a14a8b76e72ef 100644 (file)
@@ -12,7 +12,7 @@ import { RequestVideoEventType, RemoteVideoEventRequest, RemoteVideoEventType }
 
 export type RequestVideoEventSchedulerOptions = {
   type: RequestVideoEventType
-  videoId: string
+  videoId: number
   count?: number
   transaction?: Sequelize.Transaction
 }
@@ -49,7 +49,7 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler<RequestsVideoE
     */
     const eventsPerVideoPerPod: {
       [ podId: string ]: {
-        [ videoRemoteId: string ]: {
+        [ videoUUID: string ]: {
           views?: number
           likes?: number
           dislikes?: number
@@ -74,10 +74,10 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler<RequestsVideoE
         requestsToMakeGrouped[toPodId].ids.push(eventToProcess.id)
 
         const eventsPerVideo = eventsPerVideoPerPod[toPodId]
-        const remoteId = eventToProcess.video.remoteId
-        if (!eventsPerVideo[remoteId]) eventsPerVideo[remoteId] = {}
+        const uuid = eventToProcess.video.uuid
+        if (!eventsPerVideo[uuid]) eventsPerVideo[uuid] = {}
 
-        const events = eventsPerVideo[remoteId]
+        const events = eventsPerVideo[uuid]
         if (!events[eventToProcess.type]) events[eventToProcess.type] = 0
 
         events[eventToProcess.type] += eventToProcess.count
@@ -88,13 +88,13 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler<RequestsVideoE
     Object.keys(eventsPerVideoPerPod).forEach(toPodId => {
       const eventsForPod = eventsPerVideoPerPod[toPodId]
 
-      Object.keys(eventsForPod).forEach(remoteId => {
-        const eventsForVideo = eventsForPod[remoteId]
+      Object.keys(eventsForPod).forEach(uuid => {
+        const eventsForVideo = eventsForPod[uuid]
 
         Object.keys(eventsForVideo).forEach(eventType => {
           requestsToMakeGrouped[toPodId].datas.push({
             data: {
-              remoteId,
+              uuid,
               eventType: eventType as RemoteVideoEventType,
               count: +eventsForVideo[eventType]
             }
index 98816517015b7bc4ea81347e4f6fb6ff6464a21f..afb9d5c23ba9cabb5e13e26bc1c4a425d27d1c0a 100644 (file)
@@ -21,8 +21,8 @@ interface RequestsObjectsCustom<U> extends RequestsObjects<U> {
     datas: U[]
 
     videos: {
-      [ id: string ]: {
-        remoteId: string
+      [ uuid: string ]: {
+        uuid: string
         likes?: number
         dislikes?: number
         views?: number
@@ -33,7 +33,7 @@ interface RequestsObjectsCustom<U> extends RequestsObjects<U> {
 
 export type RequestVideoQaduSchedulerOptions = {
   type: RequestVideoQaduType
-  videoId: string
+  videoId: number
   transaction?: Sequelize.Transaction
 }
 
@@ -78,7 +78,7 @@ class RequestVideoQaduScheduler extends AbstractRequestScheduler<RequestsVideoQa
 
         // Maybe another attribute was filled for this video
         let videoData = requestsToMakeGrouped[hashKey].videos[video.id]
-        if (!videoData) videoData = { remoteId: null }
+        if (!videoData) videoData = { uuid: null }
 
         switch (request.type) {
           case REQUEST_VIDEO_QADU_TYPES.LIKES:
@@ -98,8 +98,8 @@ class RequestVideoQaduScheduler extends AbstractRequestScheduler<RequestsVideoQa
             return
         }
 
-        // Do not forget the remoteId so the remote pod can identify the video
-        videoData.remoteId = video.id
+        // Do not forget the uuid so the remote pod can identify the video
+        videoData.uuid = video.uuid
         requestsToMakeGrouped[hashKey].ids.push(request.id)
 
         // Maybe there are multiple quick and dirty update for the same video
@@ -110,8 +110,8 @@ class RequestVideoQaduScheduler extends AbstractRequestScheduler<RequestsVideoQa
 
     // Now we deduped similar quick and dirty updates, we can build our requests datas
     Object.keys(requestsToMakeGrouped).forEach(hashKey => {
-      Object.keys(requestsToMakeGrouped[hashKey].videos).forEach(videoId => {
-        const videoData = requestsToMakeGrouped[hashKey].videos[videoId]
+      Object.keys(requestsToMakeGrouped[hashKey].videos).forEach(videoUUID => {
+        const videoData = requestsToMakeGrouped[hashKey].videos[videoUUID]
 
         requestsToMakeGrouped[hashKey].datas.push({
           data: videoData
index 9db4fff7750954028b261accd4a5bcd401c52d51..90a46752c644632e91efcbd0ae1111a1c7449b76 100644 (file)
@@ -1,9 +1,12 @@
 import 'express-validator'
 import * as express from 'express'
+import * as Promise from 'bluebird'
+import * as validator from 'validator'
 
 import { database as db } from '../../initializers/database'
 import { checkErrors } from './utils'
 import { logger } from '../../helpers'
+import { VideoInstance } from '../../models'
 
 function usersAddValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
   req.checkBody('username', 'Should have a valid username').isUserUsernameValid()
@@ -59,12 +62,20 @@ function usersUpdateValidator (req: express.Request, res: express.Response, next
 }
 
 function usersVideoRatingValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
-  req.checkParams('videoId', 'Should have a valid video id').notEmpty().isUUID(4)
+  req.checkParams('videoId', 'Should have a valid video id').notEmpty().isVideoIdOrUUIDValid()
 
   logger.debug('Checking usersVideoRating parameters', { parameters: req.params })
 
   checkErrors(req, res, function () {
-    db.Video.load(req.params.videoId)
+    let videoPromise: Promise<VideoInstance>
+
+    if (validator.isUUID(req.params.videoId)) {
+      videoPromise = db.Video.loadByUUID(req.params.videoId)
+    } else {
+      videoPromise = db.Video.load(req.params.videoId)
+    }
+
+    videoPromise
       .then(video => {
         if (!video) return res.status(404).send('Video not found')
 
index 0134664877c801aebc4d60e44b92143855d6aade..0a88e064e54a84b3fbebeadefdb489073d661375 100644 (file)
@@ -1,10 +1,13 @@
 import 'express-validator'
 import * as express from 'express'
+import * as Promise from 'bluebird'
+import * as validator from 'validator'
 
 import { database as db } from '../../initializers/database'
 import { checkErrors } from './utils'
 import { CONSTRAINTS_FIELDS, SEARCHABLE_COLUMNS } from '../../initializers'
 import { logger, isVideoDurationValid } from '../../helpers'
+import { VideoInstance } from '../../models'
 
 function videosAddValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
   // 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:
 }
 
 function videosUpdateValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
-  req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
+  req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
   req.checkBody('name', 'Should have a valid name').optional().isVideoNameValid()
   req.checkBody('category', 'Should have a valid category').optional().isVideoCategoryValid()
   req.checkBody('licence', 'Should have a valid licence').optional().isVideoLicenceValid()
@@ -68,7 +71,7 @@ function videosUpdateValidator (req: express.Request, res: express.Response, nex
 }
 
 function videosGetValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
-  req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
+  req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
 
   logger.debug('Checking videosGet parameters', { parameters: req.params })
 
@@ -78,7 +81,7 @@ function videosGetValidator (req: express.Request, res: express.Response, next:
 }
 
 function videosRemoveValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
-  req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
+  req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
 
   logger.debug('Checking videosRemove parameters', { parameters: req.params })
 
@@ -105,7 +108,7 @@ function videosSearchValidator (req: express.Request, res: express.Response, nex
 }
 
 function videoAbuseReportValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
-  req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
+  req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
   req.checkBody('reason', 'Should have a valid reason').isVideoAbuseReasonValid()
 
   logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
@@ -116,7 +119,7 @@ function videoAbuseReportValidator (req: express.Request, res: express.Response,
 }
 
 function videoRateValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
-  req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
+  req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
   req.checkBody('rating', 'Should have a valid rate type').isVideoRatingTypeValid()
 
   logger.debug('Checking videoRate parameters', { parameters: req.body })
@@ -127,7 +130,7 @@ function videoRateValidator (req: express.Request, res: express.Response, next:
 }
 
 function videosBlacklistValidator (req: express.Request, res: express.Response, next: express.NextFunction) {
-  req.checkParams('id', 'Should have a valid id').notEmpty().isUUID(4)
+  req.checkParams('id', 'Should have a valid id').notEmpty().isVideoIdOrUUIDValid()
 
   logger.debug('Checking videosBlacklist parameters', { parameters: req.params })
 
@@ -157,7 +160,14 @@ export {
 // ---------------------------------------------------------------------------
 
 function checkVideoExists (id: string, res: express.Response, callback: () => void) {
-  db.Video.loadAndPopulateAuthorAndPodAndTags(id).then(video => {
+  let promise: Promise<VideoInstance>
+  if (validator.isInt(id)) {
+    promise = db.Video.loadAndPopulateAuthorAndPodAndTags(+id)
+  } else { // UUID
+    promise = db.Video.loadByUUIDAndPopulateAuthorAndPodAndTags(id)
+  }
+
+  promise.then(video => {
     if (!video) return res.status(404).send('Video not found')
 
     res.locals.video = video
index f501f08b7630f37c4b870330caf94817ff0795de..4e6efc01ab7c64c2389dc1355d677060bb730d32 100644 (file)
@@ -4,7 +4,7 @@ import * as Promise from 'bluebird'
 import { VideoRateType } from '../../../shared/models/videos/video-rate.type'
 
 export namespace UserVideoRateMethods {
-  export type Load = (userId: number, videoId: string, transaction: Sequelize.Transaction) => Promise<UserVideoRateInstance>
+  export type Load = (userId: number, videoId: number, transaction: Sequelize.Transaction) => Promise<UserVideoRateInstance>
 }
 
 export interface UserVideoRateClass {
index 37d0222cfa121ebae34b287bc2e420131f62f914..c14598650d7f21fc34d3740b737b842532bbcc85 100644 (file)
@@ -65,7 +65,7 @@ function associate (models) {
   })
 }
 
-load = function (userId: number, videoId: string, transaction: Sequelize.Transaction) {
+load = function (userId: number, videoId: number, transaction: Sequelize.Transaction) {
   const options: Sequelize.FindOptions = {
     where: {
       userId,
index 2992da56db8f92cc6eed4ab7cb50cfd96d48bc09..0c0757fc809265152633e6e31da95aa3513e1bda 100644 (file)
@@ -47,7 +47,7 @@ function associate (models) {
   Tag.belongsToMany(models.Video, {
     foreignKey: 'tagId',
     through: models.VideoTag,
-    onDelete: 'cascade'
+    onDelete: 'CASCADE'
   })
 }
 
index d6724d36f3fe4b16c56cbc2e6fe96c6785817855..fa45aa5f907d32534734da590e8b2fc23d9c74a6 100644 (file)
@@ -20,7 +20,7 @@ export interface VideoAbuseClass {
 export interface VideoAbuseAttributes {
   reporterUsername: string
   reason: string
-  videoId: string
+  videoId: number
 }
 
 export interface VideoAbuseInstance extends VideoAbuseClass, VideoAbuseAttributes, Sequelize.Instance<VideoAbuseAttributes> {
index ab1a3ea7d393d6b3970e7e1b8a9ffdbee70209a4..f55a25e6a16bc7d8cce7fb70f5ba8509e6ef5cfa 100644 (file)
@@ -96,7 +96,7 @@ function associate (models) {
       name: 'reporterPodId',
       allowNull: true
     },
-    onDelete: 'cascade'
+    onDelete: 'CASCADE'
   })
 
   VideoAbuse.belongsTo(models.Video, {
@@ -104,7 +104,7 @@ function associate (models) {
       name: 'videoId',
       allowNull: false
     },
-    onDelete: 'cascade'
+    onDelete: 'CASCADE'
   })
 }
 
index 47a510231b5183cb83a6b9693ef1f90caec535b0..cd9f19363cbac42cd497eb039c2db0f1dd271255 100644 (file)
@@ -17,7 +17,7 @@ export namespace BlacklistedVideoMethods {
 
   export type LoadById = (id: number) => Promise<BlacklistedVideoInstance>
 
-  export type LoadByVideoId = (id: string) => Promise<BlacklistedVideoInstance>
+  export type LoadByVideoId = (id: number) => Promise<BlacklistedVideoInstance>
 }
 
 export interface BlacklistedVideoClass {
@@ -30,7 +30,7 @@ export interface BlacklistedVideoClass {
 }
 
 export interface BlacklistedVideoAttributes {
-  videoId: string
+  videoId: number
 }
 
 export interface BlacklistedVideoInstance
index 8c42dbc21fbb2dc74681e5bb7c9ea7e9cf17cd72..4d1b45aa57d91a7626055a76db6f8557a9403501 100644 (file)
@@ -60,8 +60,11 @@ toFormatedJSON = function (this: BlacklistedVideoInstance) {
 
 function associate (models) {
   BlacklistedVideo.belongsTo(models.Video, {
-    foreignKey: 'videoId',
-    onDelete: 'cascade'
+    foreignKey: {
+      name: 'videoId',
+      allowNull: false
+    },
+    onDelete: 'CASCADE'
   })
 }
 
@@ -92,7 +95,7 @@ loadById = function (id: number) {
   return BlacklistedVideo.findById(id)
 }
 
-loadByVideoId = function (id: string) {
+loadByVideoId = function (id: number) {
   const query = {
     where: {
       videoId: id
index b836d6da6e8ed79f7ae4fba73d326a2bb01539db..2fabcd9065111b94ba71bcffa52020158ae4c39b 100644 (file)
@@ -9,6 +9,7 @@ import { Video as FormatedVideo } from '../../../shared/models/videos/video.mode
 import { ResultList } from '../../../shared/models/result-list.model'
 
 export type FormatedAddRemoteVideo = {
+  uuid: string
   name: string
   category: number
   licence: number
@@ -16,7 +17,6 @@ export type FormatedAddRemoteVideo = {
   nsfw: boolean
   description: string
   infoHash: string
-  remoteId: string
   author: string
   duration: number
   thumbnailData: string
@@ -30,6 +30,7 @@ export type FormatedAddRemoteVideo = {
 }
 
 export type FormatedUpdateRemoteVideo = {
+  uuid: string
   name: string
   category: number
   licence: number
@@ -37,7 +38,6 @@ export type FormatedUpdateRemoteVideo = {
   nsfw: boolean
   description: string
   infoHash: string
-  remoteId: string
   author: string
   duration: number
   tags: string[]
@@ -80,10 +80,12 @@ export namespace VideoMethods {
     sort: string
   ) => Promise< ResultList<VideoInstance> >
 
-  export type Load = (id: string) => Promise<VideoInstance>
-  export type LoadByHostAndRemoteId = (fromHost: string, remoteId: string) => Promise<VideoInstance>
-  export type LoadAndPopulateAuthor = (id: string) => Promise<VideoInstance>
-  export type LoadAndPopulateAuthorAndPodAndTags = (id: string) => Promise<VideoInstance>
+  export type Load = (id: number) => Promise<VideoInstance>
+  export type LoadByUUID = (uuid: string) => Promise<VideoInstance>
+  export type LoadByHostAndUUID = (fromHost: string, uuid: string) => Promise<VideoInstance>
+  export type LoadAndPopulateAuthor = (id: number) => Promise<VideoInstance>
+  export type LoadAndPopulateAuthorAndPodAndTags = (id: number) => Promise<VideoInstance>
+  export type LoadByUUIDAndPopulateAuthorAndPodAndTags = (uuid: string) => Promise<VideoInstance>
 }
 
 export interface VideoClass {
@@ -102,19 +104,21 @@ export interface VideoClass {
   getDurationFromFile: VideoMethods.GetDurationFromFile
   list: VideoMethods.List
   listForApi: VideoMethods.ListForApi
-  loadByHostAndRemoteId: VideoMethods.LoadByHostAndRemoteId
+  loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
   listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags
   listOwnedByAuthor: VideoMethods.ListOwnedByAuthor
   load: VideoMethods.Load
+  loadByUUID: VideoMethods.LoadByUUID
   loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor
   loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags
+  loadByUUIDAndPopulateAuthorAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAuthorAndPodAndTags
   searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags
 }
 
 export interface VideoAttributes {
+  uuid?: string
   name: string
   extname: string
-  remoteId: string
   category: number
   licence: number
   language: number
@@ -125,13 +129,14 @@ export interface VideoAttributes {
   views?: number
   likes?: number
   dislikes?: number
+  remote: boolean
 
   Author?: AuthorInstance
   Tags?: TagInstance[]
 }
 
 export interface VideoInstance extends VideoClass, VideoAttributes, Sequelize.Instance<VideoAttributes> {
-  id: string
+  id: number
   createdAt: Date
   updatedAt: Date
 
index 496385b358a0a6fbfeb1ae03f6c4841bd5a15ab4..3bb74bf6d658734c26cddf659602eb283248ee6f 100644 (file)
@@ -62,21 +62,23 @@ let generateThumbnailFromData: VideoMethods.GenerateThumbnailFromData
 let getDurationFromFile: VideoMethods.GetDurationFromFile
 let list: VideoMethods.List
 let listForApi: VideoMethods.ListForApi
-let loadByHostAndRemoteId: VideoMethods.LoadByHostAndRemoteId
+let loadByHostAndUUID: VideoMethods.LoadByHostAndUUID
 let listOwnedAndPopulateAuthorAndTags: VideoMethods.ListOwnedAndPopulateAuthorAndTags
 let listOwnedByAuthor: VideoMethods.ListOwnedByAuthor
 let load: VideoMethods.Load
+let loadByUUID: VideoMethods.LoadByUUID
 let loadAndPopulateAuthor: VideoMethods.LoadAndPopulateAuthor
 let loadAndPopulateAuthorAndPodAndTags: VideoMethods.LoadAndPopulateAuthorAndPodAndTags
+let loadByUUIDAndPopulateAuthorAndPodAndTags: VideoMethods.LoadByUUIDAndPopulateAuthorAndPodAndTags
 let searchAndPopulateAuthorAndPodAndTags: VideoMethods.SearchAndPopulateAuthorAndPodAndTags
 
 export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) {
   Video = sequelize.define<VideoInstance, VideoAttributes>('Video',
     {
-      id: {
+      uuid: {
         type: DataTypes.UUID,
         defaultValue: DataTypes.UUIDV4,
-        primaryKey: true,
+        allowNull: false,
         validate: {
           isUUID: 4
         }
@@ -95,13 +97,6 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
         type: DataTypes.ENUM(values(CONSTRAINTS_FIELDS.VIDEOS.EXTNAME)),
         allowNull: false
       },
-      remoteId: {
-        type: DataTypes.UUID,
-        allowNull: true,
-        validate: {
-          isUUID: 4
-        }
-      },
       category: {
         type: DataTypes.INTEGER,
         allowNull: false,
@@ -199,6 +194,11 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
           min: 0,
           isInt: true
         }
+      },
+      remote: {
+        type: DataTypes.BOOLEAN,
+        allowNull: false,
+        defaultValue: false
       }
     },
     {
@@ -206,9 +206,6 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
         {
           fields: [ 'authorId' ]
         },
-        {
-          fields: [ 'remoteId' ]
-        },
         {
           fields: [ 'name' ]
         },
@@ -226,6 +223,9 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
         },
         {
           fields: [ 'likes' ]
+        },
+        {
+          fields: [ 'uuid' ]
         }
       ],
       hooks: {
@@ -246,9 +246,11 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da
     listOwnedAndPopulateAuthorAndTags,
     listOwnedByAuthor,
     load,
-    loadByHostAndRemoteId,
+    loadByUUID,
+    loadByHostAndUUID,
     loadAndPopulateAuthor,
     loadAndPopulateAuthorAndPodAndTags,
+    loadByUUIDAndPopulateAuthorAndPodAndTags,
     searchAndPopulateAuthorAndPodAndTags,
     removeFromBlacklist
   ]
@@ -289,8 +291,9 @@ function beforeCreate (video: VideoInstance, options: { transaction: Sequelize.T
     )
 
     if (CONFIG.TRANSCODING.ENABLED === true) {
+      // Put uuid because we don't have id auto incremented for now
       const dataInput = {
-        id: video.id
+        videoUUID: video.uuid
       }
 
       tasks.push(
@@ -313,7 +316,7 @@ function afterDestroy (video: VideoInstance) {
 
   if (video.isOwned()) {
     const removeVideoToFriendsParams = {
-      remoteId: video.id
+      uuid: video.uuid
     }
 
     tasks.push(
@@ -381,34 +384,27 @@ generateMagnetUri = function (this: VideoInstance) {
 }
 
 getVideoFilename = function (this: VideoInstance) {
-  if (this.isOwned()) return this.id + this.extname
-
-  return this.remoteId + this.extname
+  return this.uuid + this.extname
 }
 
 getThumbnailName = function (this: VideoInstance) {
   // We always have a copy of the thumbnail
-  return this.id + '.jpg'
+  const extension = '.jpg'
+  return this.uuid + extension
 }
 
 getPreviewName = function (this: VideoInstance) {
   const extension = '.jpg'
-
-  if (this.isOwned()) return this.id + extension
-
-  return this.remoteId + extension
+  return this.uuid + extension
 }
 
 getTorrentName = function (this: VideoInstance) {
   const extension = '.torrent'
-
-  if (this.isOwned()) return this.id + extension
-
-  return this.remoteId + extension
+  return this.uuid + extension
 }
 
 isOwned = function (this: VideoInstance) {
-  return this.remoteId === null
+  return this.remote === false
 }
 
 toFormatedJSON = function (this: VideoInstance) {
@@ -435,6 +431,7 @@ toFormatedJSON = function (this: VideoInstance) {
 
   const json = {
     id: this.id,
+    uuid: this.uuid,
     name: this.name,
     category: this.category,
     categoryLabel,
@@ -467,6 +464,7 @@ toAddRemoteJSON = function (this: VideoInstance) {
 
   return readFileBufferPromise(thumbnailPath).then(thumbnailData => {
     const remoteVideo = {
+      uuid: this.uuid,
       name: this.name,
       category: this.category,
       licence: this.licence,
@@ -474,7 +472,6 @@ toAddRemoteJSON = function (this: VideoInstance) {
       nsfw: this.nsfw,
       description: this.description,
       infoHash: this.infoHash,
-      remoteId: this.id,
       author: this.Author.name,
       duration: this.duration,
       thumbnailData: thumbnailData.toString('binary'),
@@ -493,6 +490,7 @@ toAddRemoteJSON = function (this: VideoInstance) {
 
 toUpdateRemoteJSON = function (this: VideoInstance) {
   const json = {
+    uuid: this.uuid,
     name: this.name,
     category: this.category,
     licence: this.licence,
@@ -500,7 +498,6 @@ toUpdateRemoteJSON = function (this: VideoInstance) {
     nsfw: this.nsfw,
     description: this.description,
     infoHash: this.infoHash,
-    remoteId: this.id,
     author: this.Author.name,
     duration: this.duration,
     tags: map<TagInstance, string>(this.Tags, 'name'),
@@ -615,10 +612,10 @@ listForApi = function (start: number, count: number, sort: string) {
   })
 }
 
-loadByHostAndRemoteId = function (fromHost: string, remoteId: string) {
+loadByHostAndUUID = function (fromHost: string, uuid: string) {
   const query = {
     where: {
-      remoteId: remoteId
+      uuid
     },
     include: [
       {
@@ -640,10 +637,9 @@ loadByHostAndRemoteId = function (fromHost: string, remoteId: string) {
 }
 
 listOwnedAndPopulateAuthorAndTags = function () {
-  // If remoteId is null this is *our* video
   const query = {
     where: {
-      remoteId: null
+      remote: false
     },
     include: [ Video['sequelize'].models.Author, Video['sequelize'].models.Tag ]
   }
@@ -654,7 +650,7 @@ listOwnedAndPopulateAuthorAndTags = function () {
 listOwnedByAuthor = function (author: string) {
   const query = {
     where: {
-      remoteId: null
+      remote: false
     },
     include: [
       {
@@ -669,11 +665,20 @@ listOwnedByAuthor = function (author: string) {
   return Video.findAll(query)
 }
 
-load = function (id: string) {
+load = function (id: number) {
   return Video.findById(id)
 }
 
-loadAndPopulateAuthor = function (id: string) {
+loadByUUID = function (uuid: string) {
+  const query = {
+    where: {
+      uuid
+    }
+  }
+  return Video.findOne(query)
+}
+
+loadAndPopulateAuthor = function (id: number) {
   const options = {
     include: [ Video['sequelize'].models.Author ]
   }
@@ -681,7 +686,7 @@ loadAndPopulateAuthor = function (id: string) {
   return Video.findById(id, options)
 }
 
-loadAndPopulateAuthorAndPodAndTags = function (id: string) {
+loadAndPopulateAuthorAndPodAndTags = function (id: number) {
   const options = {
     include: [
       {
@@ -695,6 +700,23 @@ loadAndPopulateAuthorAndPodAndTags = function (id: string) {
   return Video.findById(id, options)
 }
 
+loadByUUIDAndPopulateAuthorAndPodAndTags = function (uuid: string) {
+  const options = {
+    where: {
+      uuid
+    },
+    include: [
+      {
+        model: Video['sequelize'].models.Author,
+        include: [ { model: Video['sequelize'].models.Pod, required: false } ]
+      },
+      Video['sequelize'].models.Tag
+    ]
+  }
+
+  return Video.findOne(options)
+}
+
 searchAndPopulateAuthorAndPodAndTags = function (value: string, field: string, start: number, count: number, sort: string) {
   const podInclude: Sequelize.IncludeOptions = {
     model: Video['sequelize'].models.Pod,
index 96361c64396ed0cd062ddcf2ae5a46d4ea340ba3..7cb99c4aefb0d5eed495a74d1dda5df40902f818 100644 (file)
@@ -44,7 +44,7 @@ describe('Test remote videos API validators', function () {
   describe('When adding a video', function () {
     it('Should check when adding a video')
 
-    it('Should not add an existing remoteId and host pair')
+    it('Should not add an existing uuid')
   })
 
   describe('When removing a video', function () {
index c3ee77f0c98f73d2b07b364679909c69bcdf58f3..1bc6157e82923d0c871c4b5adffc4ed454b91626 100644 (file)
@@ -20,6 +20,7 @@ const videosUtils = require('../utils/videos')
 describe('Test multiple pods', function () {
   let servers = []
   const toRemove = []
+  let videoUUID = ''
 
   before(function (done) {
     this.timeout(120000)
@@ -746,6 +747,36 @@ describe('Test multiple pods', function () {
           expect(videos[0].name).not.to.equal(toRemove[1].name)
           expect(videos[1].name).not.to.equal(toRemove[1].name)
 
+          videoUUID = videos[0].uuid
+
+          callback()
+        })
+      }, done)
+    })
+
+    it('Should get the same video by UUID on each pod', function (done) {
+      let baseVideo = null
+      each(servers, function (server, callback) {
+        videosUtils.getVideo(server.url, videoUUID, function (err, res) {
+          if (err) throw err
+
+          const video = res.body
+
+          if (baseVideo === null) {
+            baseVideo = video
+            return callback()
+          }
+
+          expect(baseVideo.name).to.equal(video.name)
+          expect(baseVideo.uuid).to.equal(video.uuid)
+          expect(baseVideo.category).to.equal(video.category)
+          expect(baseVideo.language).to.equal(video.language)
+          expect(baseVideo.licence).to.equal(video.licence)
+          expect(baseVideo.category).to.equal(video.category)
+          expect(baseVideo.nsfw).to.equal(video.nsfw)
+          expect(baseVideo.author).to.equal(video.author)
+          expect(baseVideo.tags).to.deep.equal(video.tags)
+
           callback()
         })
       }, done)
index e08da05172b0fe1a018210444a8bc7591920bd3c..0dac9a1833a036f471e3f0ef78fc97fa9727c6bf 100644 (file)
@@ -19,6 +19,7 @@ const videosUtils = require('../utils/videos')
 describe('Test a single pod', function () {
   let server = null
   let videoId = -1
+  let videoUUID = ''
   let videosListBase = null
 
   before(function (done) {
@@ -140,6 +141,7 @@ describe('Test a single pod', function () {
         expect(test).to.equal(true)
 
         videoId = video.id
+        videoUUID = video.uuid
 
         webtorrent.add(video.magnetUri, function (torrent) {
           expect(torrent.files).to.exist
@@ -181,18 +183,33 @@ describe('Test a single pod', function () {
         if (err) throw err
         expect(test).to.equal(true)
 
-       // Wait the async views increment
+        // Wait the async views increment
         setTimeout(done, 500)
       })
     })
   })
 
+  it('Should get the video by UUID', function (done) {
+    // Yes, this could be long
+    this.timeout(60000)
+
+    videosUtils.getVideo(server.url, videoUUID, function (err, res) {
+      if (err) throw err
+
+      const video = res.body
+      expect(video.name).to.equal('my super name')
+
+      // Wait the async views increment
+      setTimeout(done, 500)
+    })
+  })
+
   it('Should have the views updated', function (done) {
     videosUtils.getVideo(server.url, videoId, function (err, res) {
       if (err) throw err
 
       const video = res.body
-      expect(video.views).to.equal(1)
+      expect(video.views).to.equal(2)
 
       done()
     })
index 0f84e3f13185a22aee7561cd0c75fbf3557621c7..6128c58013b87d63103140660cebcec662e9c217 100644 (file)
@@ -1,5 +1,5 @@
 export interface RemoteQaduVideoData {
-  remoteId: string
+  uuid: string
   views?: number
   likes?: number
   dislikes?: number
index 6babb00830e635aa1d4ab2ec2a15b21463621910..b6a570e4298499c2b4bd2421a0e2973333d56686 100644 (file)
@@ -1,7 +1,7 @@
 import { RemoteVideoRequest } from './remote-video-request.model'
 
 export interface RemoteVideoCreateData {
-  remoteId: string
+  uuid: string
   author: string
   tags: string[]
   name: string
index 84df1ca2fdd268ca3341968fdfcb6645debbe621..ecc3747504e498c4ac49003447f8f9eab632db60 100644 (file)
@@ -1,7 +1,7 @@
 export type RemoteVideoEventType = 'views' | 'likes' | 'dislikes'
 
 export interface RemoteVideoEventData {
-  remoteId: string
+  uuid: string
   eventType: RemoteVideoEventType
   count: number
 }
index 95ee38661476fdd2d519ecd121904b1b50f31b6f..0686dc8ab963bbf34080923bab8c532e8dc5b0f8 100644 (file)
@@ -1,7 +1,7 @@
 import { RemoteVideoRequest } from './remote-video-request.model'
 
 export interface RemoteVideoRemoveData {
-  remoteId: string
+  uuid: string
 }
 
 export interface RemoteVideoRemoveRequest extends RemoteVideoRequest {
index 9059c92e08140dd52386713e11385b42467579e4..05ad132cf8b179f77c0d31f17efaa3b0efe22f8c 100644 (file)
@@ -1,7 +1,7 @@
 import { RemoteVideoRequest } from './remote-video-request.model'
 
 export interface RemoteVideoReportAbuseData {
-  videoRemoteId: string
+  videoUUID: string
   reporterUsername: string
   reportReason: string
 }
index 67ddbc1e4b0a6a73879916b0dd1937bd79d52752..80554856342e9cdda5ad465edd40f391039873b5 100644 (file)
@@ -1,5 +1,5 @@
 export interface RemoteVideoUpdateData {
-  remoteId: string
+  uuid: string
   tags: string[]
   name: string
   extname: string
index 4b9c574d823a94a613abe554f7c1ddfdf220ccbd..d39a1c3d546483f129f8cecf2e0561fd6bc200ea 100644 (file)
@@ -1,6 +1,6 @@
 import { UserVideoRateType } from './user-video-rate.type'
 
 export interface UserVideoRate {
-  videoId: string
+  videoId: number
   rating: UserVideoRateType
 }
index 72e32cbc7292e8a34a15e2c1f1450f755f710e1c..e005a1fd52b0d104797f3970eedcb2b9d83ce309 100644 (file)
@@ -3,6 +3,6 @@ export interface VideoAbuse {
   reporterPodHost: string
   reason: string
   reporterUsername: string
-  videoId: string
+  videoId: number
   createdAt: Date
 }
index f894bb06523ffc0ec8aab0f4abc096aaec700bca..6086250ac3ccfc906f810d15a56bee042402f1e0 100644 (file)
@@ -1,5 +1,5 @@
 export interface BlacklistedVideo {
   id: number
-  videoId: string
+  videoId: number
   createdAt: Date
 }
index 2e35f005c97d2feab42755a2164f09719565ed14..d472cc8fbf32a19d7feaa4d15a4024bdf91ac00e 100644 (file)
@@ -1,5 +1,6 @@
 export interface Video {
-  id: string
+  id: number
+  uuid: string
   author: string
   createdAt: Date
   categoryLabel: string