Use global uuid instead of remoteId for videos

pull/76/head
Chocobozzz 2017-07-11 16:01:56 +02:00
parent e6d4b0ff24
commit 0a6658fdcb
58 changed files with 450 additions and 176 deletions

View 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
}

View 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

View 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

View 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 => {

View 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">

View 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>'
}

View 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>

View 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 => {

View 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
}
]

View 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
})
}

View 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)

View 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)

View 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,

View 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--

View 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

View 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) &&

View 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,

View File

@ -15,7 +15,7 @@ import {
// ---------------------------------------------------------------------------
const LAST_MIGRATION_VERSION = 50
const LAST_MIGRATION_VERSION = 55
// ---------------------------------------------------------------------------

View File

@ -26,7 +26,7 @@ function up (utils: {
})
}
function down (options, callback) {
function down (options) {
throw new Error('Not implemented.')
}

View File

@ -25,7 +25,7 @@ function up (utils: {
})
}
function down (options, callback) {
function down (options) {
throw new Error('Not implemented.')
}

View 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.')
}

View 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.')
}

View 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.')
}

View File

@ -22,7 +22,7 @@ function up (utils: {
})
}
function down (options, callback) {
function down (options) {
throw new Error('Not implemented.')
}

View File

@ -21,7 +21,7 @@ function up (utils: {
})
}
function down (options, callback) {
function down (options) {
throw new Error('Not implemented.')
}

View File

@ -22,7 +22,7 @@ function up (utils: {
})
}
function down (options, callback) {
function down (options) {
throw new Error('Not implemented.')
}

View 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.')
}

View 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.')
}

View File

@ -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
}

View 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)
})
})
}

View 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]

View 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)
})
}

View 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]
}

View 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

View 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')

View 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

View 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 {

View 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,

View File

@ -47,7 +47,7 @@ function associate (models) {
Tag.belongsToMany(models.Video, {
foreignKey: 'tagId',
through: models.VideoTag,
onDelete: 'cascade'
onDelete: 'CASCADE'
})
}

View 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> {

View 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'
})
}

View 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

View 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

View 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

View 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,

View 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 () {

View 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)

View 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()
})

View File

@ -1,5 +1,5 @@
export interface RemoteQaduVideoData {
remoteId: string
uuid: string
views?: number
likes?: number
dislikes?: number

View 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

View File

@ -1,7 +1,7 @@
export type RemoteVideoEventType = 'views' | 'likes' | 'dislikes'
export interface RemoteVideoEventData {
remoteId: string
uuid: string
eventType: RemoteVideoEventType
count: number
}

View File

@ -1,7 +1,7 @@
import { RemoteVideoRequest } from './remote-video-request.model'
export interface RemoteVideoRemoveData {
remoteId: string
uuid: string
}
export interface RemoteVideoRemoveRequest extends RemoteVideoRequest {

View File

@ -1,7 +1,7 @@
import { RemoteVideoRequest } from './remote-video-request.model'
export interface RemoteVideoReportAbuseData {
videoRemoteId: string
videoUUID: string
reporterUsername: string
reportReason: string
}

View File

@ -1,5 +1,5 @@
export interface RemoteVideoUpdateData {
remoteId: string
uuid: string
tags: string[]
name: string
extname: string

View File

@ -1,6 +1,6 @@
import { UserVideoRateType } from './user-video-rate.type'
export interface UserVideoRate {
videoId: string
videoId: number
rating: UserVideoRateType
}

View File

@ -3,6 +3,6 @@ export interface VideoAbuse {
reporterPodHost: string
reason: string
reporterUsername: string
videoId: string
videoId: number
createdAt: Date
}

View File

@ -1,5 +1,5 @@
export interface BlacklistedVideo {
id: number
videoId: string
videoId: number
createdAt: Date
}

View File

@ -1,5 +1,6 @@
export interface Video {
id: string
id: number
uuid: string
author: string
createdAt: Date
categoryLabel: string