]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/models/video/video-file.ts
Add ability to disable webtorrent
[github/Chocobozzz/PeerTube.git] / server / models / video / video-file.ts
1 import {
2 AllowNull,
3 BelongsTo,
4 Column,
5 CreatedAt,
6 DataType,
7 Default,
8 ForeignKey,
9 HasMany,
10 Is,
11 Model,
12 Table,
13 UpdatedAt
14 } from 'sequelize-typescript'
15 import {
16 isVideoFileExtnameValid,
17 isVideoFileInfoHashValid,
18 isVideoFileResolutionValid,
19 isVideoFileSizeValid,
20 isVideoFPSResolutionValid
21 } from '../../helpers/custom-validators/videos'
22 import { parseAggregateResult, throwIfNotValid } from '../utils'
23 import { VideoModel } from './video'
24 import { VideoRedundancyModel } from '../redundancy/video-redundancy'
25 import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
26 import { FindOptions, Op, QueryTypes, Transaction } from 'sequelize'
27 import { MIMETYPES } from '../../initializers/constants'
28 import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../typings/models/video/video-file'
29 import { MStreamingPlaylist, MStreamingPlaylistVideo, MVideo } from '@server/typings/models'
30
31 @Table({
32 tableName: 'videoFile',
33 indexes: [
34 {
35 fields: [ 'videoId' ],
36 where: {
37 videoId: {
38 [Op.ne]: null
39 }
40 }
41 },
42 {
43 fields: [ 'videoStreamingPlaylistId' ],
44 where: {
45 videoStreamingPlaylistId: {
46 [Op.ne]: null
47 }
48 }
49 },
50
51 {
52 fields: [ 'infoHash' ]
53 },
54
55 {
56 fields: [ 'videoId', 'resolution', 'fps' ],
57 unique: true,
58 where: {
59 videoId: {
60 [Op.ne]: null
61 }
62 }
63 },
64 {
65 fields: [ 'videoStreamingPlaylistId', 'resolution', 'fps' ],
66 unique: true,
67 where: {
68 videoStreamingPlaylistId: {
69 [Op.ne]: null
70 }
71 }
72 }
73 ]
74 })
75 export class VideoFileModel extends Model<VideoFileModel> {
76 @CreatedAt
77 createdAt: Date
78
79 @UpdatedAt
80 updatedAt: Date
81
82 @AllowNull(false)
83 @Is('VideoFileResolution', value => throwIfNotValid(value, isVideoFileResolutionValid, 'resolution'))
84 @Column
85 resolution: number
86
87 @AllowNull(false)
88 @Is('VideoFileSize', value => throwIfNotValid(value, isVideoFileSizeValid, 'size'))
89 @Column(DataType.BIGINT)
90 size: number
91
92 @AllowNull(false)
93 @Is('VideoFileExtname', value => throwIfNotValid(value, isVideoFileExtnameValid, 'extname'))
94 @Column
95 extname: string
96
97 @AllowNull(false)
98 @Is('VideoFileInfohash', value => throwIfNotValid(value, isVideoFileInfoHashValid, 'info hash'))
99 @Column
100 infoHash: string
101
102 @AllowNull(false)
103 @Default(-1)
104 @Is('VideoFileFPS', value => throwIfNotValid(value, isVideoFPSResolutionValid, 'fps'))
105 @Column
106 fps: number
107
108 @ForeignKey(() => VideoModel)
109 @Column
110 videoId: number
111
112 @BelongsTo(() => VideoModel, {
113 foreignKey: {
114 allowNull: true
115 },
116 onDelete: 'CASCADE'
117 })
118 Video: VideoModel
119
120 @ForeignKey(() => VideoStreamingPlaylistModel)
121 @Column
122 videoStreamingPlaylistId: number
123
124 @BelongsTo(() => VideoStreamingPlaylistModel, {
125 foreignKey: {
126 allowNull: true
127 },
128 onDelete: 'CASCADE'
129 })
130 VideoStreamingPlaylist: VideoStreamingPlaylistModel
131
132 @HasMany(() => VideoRedundancyModel, {
133 foreignKey: {
134 allowNull: true
135 },
136 onDelete: 'CASCADE',
137 hooks: true
138 })
139 RedundancyVideos: VideoRedundancyModel[]
140
141 static doesInfohashExist (infoHash: string) {
142 const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1'
143 const options = {
144 type: QueryTypes.SELECT as QueryTypes.SELECT,
145 bind: { infoHash },
146 raw: true
147 }
148
149 return VideoModel.sequelize.query(query, options)
150 .then(results => results.length === 1)
151 }
152
153 static loadWithVideo (id: number) {
154 const options = {
155 include: [
156 {
157 model: VideoModel.unscoped(),
158 required: true
159 }
160 ]
161 }
162
163 return VideoFileModel.findByPk(id, options)
164 }
165
166 static listByStreamingPlaylist (streamingPlaylistId: number, transaction: Transaction) {
167 const query = {
168 include: [
169 {
170 model: VideoModel.unscoped(),
171 required: true,
172 include: [
173 {
174 model: VideoStreamingPlaylistModel.unscoped(),
175 required: true,
176 where: {
177 id: streamingPlaylistId
178 }
179 }
180 ]
181 }
182 ],
183 transaction
184 }
185
186 return VideoFileModel.findAll(query)
187 }
188
189 static getStats () {
190 const query: FindOptions = {
191 include: [
192 {
193 attributes: [],
194 model: VideoModel.unscoped(),
195 where: {
196 remote: false
197 }
198 }
199 ]
200 }
201
202 return VideoFileModel.aggregate('size', 'SUM', query)
203 .then(result => ({
204 totalLocalVideoFilesSize: parseAggregateResult(result)
205 }))
206 }
207
208 // Redefine upsert because sequelize does not use an appropriate where clause in the update query with 2 unique indexes
209 static async customUpsert (
210 videoFile: MVideoFile,
211 mode: 'streaming-playlist' | 'video',
212 transaction: Transaction
213 ) {
214 const baseWhere = {
215 fps: videoFile.fps,
216 resolution: videoFile.resolution
217 }
218
219 if (mode === 'streaming-playlist') Object.assign(baseWhere, { videoStreamingPlaylistId: videoFile.videoStreamingPlaylistId })
220 else Object.assign(baseWhere, { videoId: videoFile.videoId })
221
222 const element = await VideoFileModel.findOne({ where: baseWhere, transaction })
223 if (!element) return videoFile.save({ transaction })
224
225 for (const k of Object.keys(videoFile.toJSON())) {
226 element[k] = videoFile[k]
227 }
228
229 return element.save({ transaction })
230 }
231
232 getVideoOrStreamingPlaylist (this: MVideoFileVideo | MVideoFileStreamingPlaylistVideo): MVideo | MStreamingPlaylistVideo {
233 if (this.videoId) return (this as MVideoFileVideo).Video
234
235 return (this as MVideoFileStreamingPlaylistVideo).VideoStreamingPlaylist
236 }
237
238 isAudio () {
239 return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname]
240 }
241
242 hasSameUniqueKeysThan (other: MVideoFile) {
243 return this.fps === other.fps &&
244 this.resolution === other.resolution &&
245 (
246 (this.videoId !== null && this.videoId === other.videoId) ||
247 (this.videoStreamingPlaylistId !== null && this.videoStreamingPlaylistId === other.videoStreamingPlaylistId)
248 )
249 }
250 }