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