From 0a6658fdcbd779ada8f3758048c326e997902d5a Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 11 Jul 2017 16:01:56 +0200 Subject: Use global uuid instead of remoteId for videos --- server/controllers/api/remote/videos.ts | 39 ++--- server/controllers/api/users.ts | 2 +- server/controllers/api/videos/abuse.ts | 4 +- server/controllers/api/videos/index.ts | 2 +- server/controllers/api/videos/rate.ts | 2 +- server/controllers/client.ts | 13 +- server/helpers/custom-validators/remote/videos.ts | 12 +- server/helpers/custom-validators/videos.ts | 14 +- server/initializers/constants.ts | 2 +- server/initializers/migrations/0005-email-pod.ts | 2 +- server/initializers/migrations/0010-email-user.ts | 2 +- server/initializers/migrations/0015-video-views.ts | 2 +- server/initializers/migrations/0020-video-likes.ts | 2 +- .../initializers/migrations/0025-video-dislikes.ts | 2 +- .../initializers/migrations/0030-video-category.ts | 2 +- .../initializers/migrations/0035-video-licence.ts | 2 +- server/initializers/migrations/0040-video-nsfw.ts | 2 +- .../migrations/0045-user-display-nsfw.ts | 2 +- .../initializers/migrations/0050-video-language.ts | 2 +- server/initializers/migrations/0055-video-uuid.ts | 157 +++++++++++++++++++++ server/initializers/migrator.ts | 4 +- server/lib/friends.ts | 4 +- server/lib/jobs/handlers/video-transcoder.ts | 4 +- .../lib/request/request-video-event-scheduler.ts | 16 +-- server/lib/request/request-video-qadu-scheduler.ts | 16 +-- server/middlewares/validators/users.ts | 15 +- server/middlewares/validators/videos.ts | 24 +++- server/models/user/user-video-rate-interface.ts | 2 +- server/models/user/user-video-rate.ts | 2 +- server/models/video/tag.ts | 2 +- server/models/video/video-abuse-interface.ts | 2 +- server/models/video/video-abuse.ts | 4 +- server/models/video/video-blacklist-interface.ts | 4 +- server/models/video/video-blacklist.ts | 9 +- server/models/video/video-interface.ts | 23 +-- server/models/video/video.ts | 100 ++++++++----- server/tests/api/check-params/remotes.js | 2 +- server/tests/api/multiple-pods.js | 31 ++++ server/tests/api/single-pod.js | 21 ++- 39 files changed, 411 insertions(+), 141 deletions(-) create mode 100644 server/initializers/migrations/0055-video-uuid.ts (limited to 'server') 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 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 }) } 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: } 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) 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 } 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) 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 .then(({ author, tagInstances }) => { const videoData = { name: videoInfos.name, - remoteId: null, + remote: false, extname: path.extname(videoFile.filename), category: videoInfos.category, 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) { // 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-- 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) { } let tagsString = '' - Object.keys(metaTags).forEach(function (tagName) { + Object.keys(metaTags).forEach(tagName => { const tagValue = metaTags[tagName] tagsString += '' @@ -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 // 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 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' 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) && 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 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, 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 { // --------------------------------------------------------------------------- -const LAST_MIGRATION_VERSION = 50 +const LAST_MIGRATION_VERSION = 55 // --------------------------------------------------------------------------- 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: { }) } -function down (options, callback) { +function down (options) { throw new Error('Not implemented.') } 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: { }) } -function down (options, callback) { +function down (options) { throw new Error('Not implemented.') } 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: { return q.addColumn('Videos', 'views', data) } -function down (options, callback) { +function down (options) { throw new Error('Not implemented.') } 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: { return q.addColumn('Videos', 'likes', data) } -function down (options, callback) { +function down (options) { throw new Error('Not implemented.') } 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: { return q.addColumn('Videos', 'dislikes', data) } -function down (options, callback) { +function down (options) { throw new Error('Not implemented.') } 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: { }) } -function down (options, callback) { +function down (options) { throw new Error('Not implemented.') } 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: { }) } -function down (options, callback) { +function down (options) { throw new Error('Not implemented.') } 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: { }) } -function down (options, callback) { +function down (options) { throw new Error('Not implemented.') } 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: { return q.addColumn('Users', 'displayNSFW', data) } -function down (options, callback) { +function down (options) { throw new Error('Not implemented.') } 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: { 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 index 000000000..9bc65917c --- /dev/null +++ b/server/initializers/migrations/0055-video-uuid.ts @@ -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 { + 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 +} 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 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) }) }) } 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 { 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] 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' 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) }) } 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 } export type RequestVideoEventSchedulerOptions = { type: RequestVideoEventType - videoId: string + videoId: number count?: number transaction?: Sequelize.Transaction } @@ -49,7 +49,7 @@ class RequestVideoEventScheduler extends AbstractRequestScheduler { 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] } 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 extends RequestsObjects { datas: U[] videos: { - [ id: string ]: { - remoteId: string + [ uuid: string ]: { + uuid: string likes?: number dislikes?: number views?: number @@ -33,7 +33,7 @@ interface RequestsObjectsCustom extends RequestsObjects { export type RequestVideoQaduSchedulerOptions = { type: RequestVideoQaduType - videoId: string + videoId: number transaction?: Sequelize.Transaction } @@ -78,7 +78,7 @@ class RequestVideoQaduScheduler extends AbstractRequestScheduler { - 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 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 @@ 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 + + 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') 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 @@ 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 + 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 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' import { VideoRateType } from '../../../shared/models/videos/video-rate.type' export namespace UserVideoRateMethods { - export type Load = (userId: number, videoId: string, transaction: Sequelize.Transaction) => Promise + export type Load = (userId: number, videoId: number, transaction: Sequelize.Transaction) => Promise } 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) { }) } -load = function (userId: number, videoId: string, transaction: Sequelize.Transaction) { +load = function (userId: number, videoId: number, transaction: Sequelize.Transaction) { const options: Sequelize.FindOptions = { where: { 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) { Tag.belongsToMany(models.Video, { foreignKey: 'tagId', through: models.VideoTag, - onDelete: 'cascade' + onDelete: 'CASCADE' }) } 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 { export interface VideoAbuseAttributes { reporterUsername: string reason: string - videoId: string + videoId: number } export interface VideoAbuseInstance extends VideoAbuseClass, VideoAbuseAttributes, Sequelize.Instance { 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) { 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' }) } 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 { export type LoadById = (id: number) => Promise - export type LoadByVideoId = (id: string) => Promise + export type LoadByVideoId = (id: number) => Promise } export interface BlacklistedVideoClass { @@ -30,7 +30,7 @@ export interface BlacklistedVideoClass { } export interface BlacklistedVideoAttributes { - videoId: string + videoId: number } 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) { 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 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 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 > - export type Load = (id: string) => Promise - export type LoadByHostAndRemoteId = (fromHost: string, remoteId: string) => Promise - export type LoadAndPopulateAuthor = (id: string) => Promise - export type LoadAndPopulateAuthorAndPodAndTags = (id: string) => Promise + export type Load = (id: number) => Promise + export type LoadByUUID = (uuid: string) => Promise + export type LoadByHostAndUUID = (fromHost: string, uuid: string) => Promise + export type LoadAndPopulateAuthor = (id: number) => Promise + export type LoadAndPopulateAuthorAndPodAndTags = (id: number) => Promise + export type LoadByUUIDAndPopulateAuthorAndPodAndTags = (uuid: string) => Promise } 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 { - id: string + id: number createdAt: Date updatedAt: Date 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 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('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(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, 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 () { 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 () { 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') 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) 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') 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() }) -- cgit v1.2.3