X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Fvideo%2Fvideo-import.ts;h=1d82960609904c3c872da37bdf1fafa494d96eb3;hb=fba911e2c89708a166636e3a93fcd8fcbc3de7e1;hp=55fca28b8c99c57bacac6aac05bfe05b57a2af37;hpb=ce33919c24e7402d92d81f3cd8e545df52d98240;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/video/video-import.ts b/server/models/video/video-import.ts index 55fca28b8..1d8296060 100644 --- a/server/models/video/video-import.ts +++ b/server/models/video/video-import.ts @@ -1,3 +1,4 @@ +import { WhereOptions } from 'sequelize' import { AfterUpdate, AllowNull, @@ -13,39 +14,33 @@ import { Table, UpdatedAt } from 'sequelize-typescript' -import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers' -import { getSort, throwIfNotValid } from '../utils' -import { VideoModel } from './video' +import { afterCommitIfTransaction } from '@server/helpers/database-utils' +import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import' +import { VideoImport, VideoImportState } from '@shared/models' +import { AttributesOnly } from '@shared/typescript-utils' import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports' -import { VideoImport, VideoImportState } from '../../../shared' -import { VideoChannelModel } from './video-channel' -import { AccountModel } from '../account/account' -import { TagModel } from './tag' import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' +import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers/constants' +import { UserModel } from '../user/user' +import { getSort, throwIfNotValid } from '../utils' +import { ScopeNames as VideoModelScopeNames, VideoModel } from './video' -@DefaultScope({ +@DefaultScope(() => ({ include: [ { - model: () => VideoModel, - required: false, - include: [ - { - model: () => VideoChannelModel, - required: true, - include: [ - { - model: () => AccountModel, - required: true - } - ] - }, - { - model: () => TagModel - } - ] + model: UserModel.unscoped(), + required: true + }, + { + model: VideoModel.scope([ + VideoModelScopeNames.WITH_ACCOUNT_DETAILS, + VideoModelScopeNames.WITH_TAGS, + VideoModelScopeNames.WITH_THUMBNAILS + ]), + required: false } ] -}) +})) @Table({ tableName: 'videoImport', @@ -53,10 +48,13 @@ import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos' { fields: [ 'videoId' ], unique: true + }, + { + fields: [ 'userId' ] } ] }) -export class VideoImportModel extends Model { +export class VideoImportModel extends Model>> { @CreatedAt createdAt: Date @@ -65,13 +63,13 @@ export class VideoImportModel extends Model { @AllowNull(true) @Default(null) - @Is('VideoImportTargetUrl', value => throwIfNotValid(value, isVideoImportTargetUrlValid, 'targetUrl')) + @Is('VideoImportTargetUrl', value => throwIfNotValid(value, isVideoImportTargetUrlValid, 'targetUrl', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max)) targetUrl: string @AllowNull(true) @Default(null) - @Is('VideoImportMagnetUri', value => throwIfNotValid(value, isVideoMagnetUriValid, 'magnetUri')) + @Is('VideoImportMagnetUri', value => throwIfNotValid(value, isVideoMagnetUriValid, 'magnetUri', true)) @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max)) // Use the same constraints than URLs magnetUri: string @@ -91,6 +89,18 @@ export class VideoImportModel extends Model { @Column(DataType.TEXT) error: string + @ForeignKey(() => UserModel) + @Column + userId: number + + @BelongsTo(() => UserModel, { + foreignKey: { + allowNull: false + }, + onDelete: 'cascade' + }) + User: UserModel + @ForeignKey(() => VideoModel) @Column videoId: number @@ -106,72 +116,71 @@ export class VideoImportModel extends Model { @AfterUpdate static deleteVideoIfFailed (instance: VideoImportModel, options) { if (instance.state === VideoImportState.FAILED) { - return instance.Video.destroy({ transaction: options.transaction }) + return afterCommitIfTransaction(options.transaction, () => instance.Video.destroy()) } return undefined } - static loadAndPopulateVideo (id: number) { - return VideoImportModel.findById(id) + static loadAndPopulateVideo (id: number): Promise { + return VideoImportModel.findByPk(id) } - static listUserVideoImportsForApi (accountId: number, start: number, count: number, sort: string) { + static listUserVideoImportsForApi (options: { + userId: number + start: number + count: number + sort: string + + targetUrl?: string + }) { + const { userId, start, count, sort, targetUrl } = options + + const where: WhereOptions = { userId } + + if (targetUrl) where['targetUrl'] = targetUrl + const query = { distinct: true, - offset: start, - limit: count, - order: getSort(sort), include: [ { - model: VideoModel, - required: false, - include: [ - { - model: VideoChannelModel, - required: true, - include: [ - { - model: AccountModel, - required: true, - where: { - id: accountId - } - } - ] - }, - { - model: TagModel, - required: false - } - ] + attributes: [ 'id' ], + model: UserModel.unscoped(), // FIXME: Without this, sequelize try to COUNT(DISTINCT(*)) which is an invalid SQL query + required: true } - ] + ], + offset: start, + limit: count, + order: getSort(sort), + where } - return VideoImportModel.unscoped() - .findAndCountAll(query) - .then(({ rows, count }) => { - return { - data: rows, - total: count - } - }) + return Promise.all([ + VideoImportModel.unscoped().count(query), + VideoImportModel.findAll(query) + ]).then(([ total, data ]) => ({ total, data })) + } + + getTargetIdentifier () { + return this.targetUrl || this.magnetUri || this.torrentName } - toFormattedJSON (): VideoImport { + toFormattedJSON (this: MVideoImportFormattable): VideoImport { const videoFormatOptions = { + completeDescription: true, additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true } } const video = this.Video - ? Object.assign(this.Video.toFormattedJSON(videoFormatOptions), { - tags: this.Video.Tags.map(t => t.name) - }) + ? Object.assign(this.Video.toFormattedJSON(videoFormatOptions), { tags: this.Video.Tags.map(t => t.name) }) : undefined return { id: this.id, + targetUrl: this.targetUrl, + magnetUri: this.magnetUri, + torrentName: this.torrentName, + state: { id: this.state, label: VideoImportModel.getStateLabel(this.state) @@ -182,6 +191,7 @@ export class VideoImportModel extends Model { video } } + private static getStateLabel (id: number) { return VIDEO_IMPORT_STATES[id] || 'Unknown' }