15 } from 'sequelize-typescript'
16 import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import'
17 import { VideoImport, VideoImportState } from '../../../shared'
18 import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports'
19 import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos'
20 import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers/constants'
21 import { UserModel } from '../account/user'
22 import { getSort, throwIfNotValid } from '../utils'
23 import { ScopeNames as VideoModelScopeNames, VideoModel } from './video'
24 import { afterCommitIfTransaction } from '@server/helpers/database-utils'
26 @DefaultScope(() => ({
29 model: UserModel.unscoped(),
33 model: VideoModel.scope([
34 VideoModelScopeNames.WITH_ACCOUNT_DETAILS,
35 VideoModelScopeNames.WITH_TAGS,
36 VideoModelScopeNames.WITH_THUMBNAILS
44 tableName: 'videoImport',
47 fields: [ 'videoId' ],
55 export class VideoImportModel extends Model {
64 @Is('VideoImportTargetUrl', value => throwIfNotValid(value, isVideoImportTargetUrlValid, 'targetUrl', true))
65 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max))
70 @Is('VideoImportMagnetUri', value => throwIfNotValid(value, isVideoMagnetUriValid, 'magnetUri', true))
71 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max)) // Use the same constraints than URLs
76 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.TORRENT_NAME.max))
81 @Is('VideoImportState', value => throwIfNotValid(value, isVideoImportStateValid, 'state'))
83 state: VideoImportState
87 @Column(DataType.TEXT)
90 @ForeignKey(() => UserModel)
94 @BelongsTo(() => UserModel, {
102 @ForeignKey(() => VideoModel)
106 @BelongsTo(() => VideoModel, {
115 static deleteVideoIfFailed (instance: VideoImportModel, options) {
116 if (instance.state === VideoImportState.FAILED) {
117 return afterCommitIfTransaction(options.transaction, () => instance.Video.destroy())
123 static loadAndPopulateVideo (id: number): Promise<MVideoImportDefault> {
124 return VideoImportModel.findByPk(id)
127 static listUserVideoImportsForApi (userId: number, start: number, count: number, sort: string) {
132 attributes: [ 'id' ],
133 model: UserModel.unscoped(), // FIXME: Without this, sequelize try to COUNT(DISTINCT(*)) which is an invalid SQL query
139 order: getSort(sort),
145 return VideoImportModel.findAndCountAll<MVideoImportDefault>(query)
146 .then(({ rows, count }) => {
154 getTargetIdentifier () {
155 return this.targetUrl || this.magnetUri || this.torrentName
158 toFormattedJSON (this: MVideoImportFormattable): VideoImport {
159 const videoFormatOptions = {
160 completeDescription: true,
161 additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true }
163 const video = this.Video
164 ? Object.assign(this.Video.toFormattedJSON(videoFormatOptions), { tags: this.Video.Tags.map(t => t.name) })
170 targetUrl: this.targetUrl,
171 magnetUri: this.magnetUri,
172 torrentName: this.torrentName,
176 label: VideoImportModel.getStateLabel(this.state)
179 updatedAt: this.updatedAt.toISOString(),
180 createdAt: this.createdAt.toISOString(),
185 private static getStateLabel (id: number) {
186 return VIDEO_IMPORT_STATES[id] || 'Unknown'