]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/video/video-import.ts
Merge branch 'release/3.2.0' into develop
[github/Chocobozzz/PeerTube.git] / server / models / video / video-import.ts
CommitLineData
fbad87b0 1import {
ed31c059 2 AfterUpdate,
fbad87b0
C
3 AllowNull,
4 BelongsTo,
5 Column,
6 CreatedAt,
7 DataType,
8 Default,
9 DefaultScope,
10 ForeignKey,
11 Is,
12 Model,
13 Table,
14 UpdatedAt
15} from 'sequelize-typescript'
7d9ba5c0 16import { afterCommitIfTransaction } from '@server/helpers/database-utils'
b49f22d8 17import { MVideoImportDefault, MVideoImportFormattable } from '@server/types/models/video/video-import'
16c016e8 18import { AttributesOnly } from '@shared/core-utils'
fbad87b0 19import { VideoImport, VideoImportState } from '../../../shared'
b49f22d8 20import { isVideoImportStateValid, isVideoImportTargetUrlValid } from '../../helpers/custom-validators/video-imports'
ce33919c 21import { isVideoMagnetUriValid } from '../../helpers/custom-validators/videos'
b49f22d8 22import { CONSTRAINTS_FIELDS, VIDEO_IMPORT_STATES } from '../../initializers/constants'
7d9ba5c0 23import { UserModel } from '../user/user'
b49f22d8
C
24import { getSort, throwIfNotValid } from '../utils'
25import { ScopeNames as VideoModelScopeNames, VideoModel } from './video'
fbad87b0 26
3acc5084 27@DefaultScope(() => ({
fbad87b0
C
28 include: [
29 {
3acc5084 30 model: UserModel.unscoped(),
a84b8fa5
C
31 required: true
32 },
33 {
453e83ea
C
34 model: VideoModel.scope([
35 VideoModelScopeNames.WITH_ACCOUNT_DETAILS,
36 VideoModelScopeNames.WITH_TAGS,
37 VideoModelScopeNames.WITH_THUMBNAILS
38 ]),
a84b8fa5 39 required: false
fbad87b0
C
40 }
41 ]
3acc5084 42}))
fbad87b0
C
43
44@Table({
45 tableName: 'videoImport',
46 indexes: [
47 {
48 fields: [ 'videoId' ],
49 unique: true
a84b8fa5
C
50 },
51 {
52 fields: [ 'userId' ]
fbad87b0
C
53 }
54 ]
55})
16c016e8 56export class VideoImportModel extends Model<Partial<AttributesOnly<VideoImportModel>>> {
fbad87b0
C
57 @CreatedAt
58 createdAt: Date
59
60 @UpdatedAt
61 updatedAt: Date
62
ce33919c
C
63 @AllowNull(true)
64 @Default(null)
1735c825 65 @Is('VideoImportTargetUrl', value => throwIfNotValid(value, isVideoImportTargetUrlValid, 'targetUrl', true))
fbad87b0
C
66 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max))
67 targetUrl: string
68
ce33919c
C
69 @AllowNull(true)
70 @Default(null)
1735c825 71 @Is('VideoImportMagnetUri', value => throwIfNotValid(value, isVideoMagnetUriValid, 'magnetUri', true))
ce33919c
C
72 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.URL.max)) // Use the same constraints than URLs
73 magnetUri: string
74
75 @AllowNull(true)
76 @Default(null)
77 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_IMPORTS.TORRENT_NAME.max))
78 torrentName: string
79
fbad87b0
C
80 @AllowNull(false)
81 @Default(null)
82 @Is('VideoImportState', value => throwIfNotValid(value, isVideoImportStateValid, 'state'))
83 @Column
84 state: VideoImportState
85
86 @AllowNull(true)
87 @Default(null)
88 @Column(DataType.TEXT)
89 error: string
90
a84b8fa5
C
91 @ForeignKey(() => UserModel)
92 @Column
93 userId: number
94
95 @BelongsTo(() => UserModel, {
96 foreignKey: {
97 allowNull: false
98 },
99 onDelete: 'cascade'
100 })
101 User: UserModel
102
fbad87b0
C
103 @ForeignKey(() => VideoModel)
104 @Column
105 videoId: number
106
107 @BelongsTo(() => VideoModel, {
108 foreignKey: {
ed31c059 109 allowNull: true
fbad87b0 110 },
ed31c059 111 onDelete: 'set null'
fbad87b0
C
112 })
113 Video: VideoModel
114
ed31c059
C
115 @AfterUpdate
116 static deleteVideoIfFailed (instance: VideoImportModel, options) {
117 if (instance.state === VideoImportState.FAILED) {
44d1f7f2 118 return afterCommitIfTransaction(options.transaction, () => instance.Video.destroy())
ed31c059
C
119 }
120
121 return undefined
122 }
123
b49f22d8 124 static loadAndPopulateVideo (id: number): Promise<MVideoImportDefault> {
9b39106d 125 return VideoImportModel.findByPk(id)
fbad87b0
C
126 }
127
a84b8fa5 128 static listUserVideoImportsForApi (userId: number, start: number, count: number, sort: string) {
ed31c059 129 const query = {
590fb506 130 distinct: true,
ed31c059
C
131 include: [
132 {
652c6416 133 attributes: [ 'id' ],
a84b8fa5
C
134 model: UserModel.unscoped(), // FIXME: Without this, sequelize try to COUNT(DISTINCT(*)) which is an invalid SQL query
135 required: true
ed31c059 136 }
a84b8fa5
C
137 ],
138 offset: start,
139 limit: count,
140 order: getSort(sort),
141 where: {
142 userId
143 }
ed31c059
C
144 }
145
453e83ea 146 return VideoImportModel.findAndCountAll<MVideoImportDefault>(query)
ed31c059
C
147 .then(({ rows, count }) => {
148 return {
149 data: rows,
150 total: count
151 }
152 })
153 }
154
dc133480
C
155 getTargetIdentifier () {
156 return this.targetUrl || this.magnetUri || this.torrentName
157 }
158
1ca9f7c3 159 toFormattedJSON (this: MVideoImportFormattable): VideoImport {
fbad87b0 160 const videoFormatOptions = {
c39e86b8 161 completeDescription: true,
fbad87b0
C
162 additionalAttributes: { state: true, waitTranscoding: true, scheduledUpdate: true }
163 }
ed31c059 164 const video = this.Video
c39e86b8 165 ? Object.assign(this.Video.toFormattedJSON(videoFormatOptions), { tags: this.Video.Tags.map(t => t.name) })
ed31c059 166 : undefined
fbad87b0
C
167
168 return {
d7f83948 169 id: this.id,
990b6a0b 170
fbad87b0 171 targetUrl: this.targetUrl,
990b6a0b
C
172 magnetUri: this.magnetUri,
173 torrentName: this.torrentName,
174
ed31c059
C
175 state: {
176 id: this.state,
177 label: VideoImportModel.getStateLabel(this.state)
178 },
d7f83948 179 error: this.error,
ed31c059
C
180 updatedAt: this.updatedAt.toISOString(),
181 createdAt: this.createdAt.toISOString(),
fbad87b0
C
182 video
183 }
184 }
268eebed 185
ed31c059
C
186 private static getStateLabel (id: number) {
187 return VIDEO_IMPORT_STATES[id] || 'Unknown'
188 }
fbad87b0 189}