Update server dependencies
[oweals/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, QueryTypes, Transaction } from 'sequelize'
27 import { MIMETYPES } from '../../initializers/constants'
28 import { MVideoFile } from '@server/typings/models'
29
30 @Table({
31   tableName: 'videoFile',
32   indexes: [
33     {
34       fields: [ 'videoId' ]
35     },
36     {
37       fields: [ 'infoHash' ]
38     },
39     {
40       fields: [ 'videoId', 'resolution', 'fps' ],
41       unique: true
42     }
43   ]
44 })
45 export class VideoFileModel extends Model<VideoFileModel> {
46   @CreatedAt
47   createdAt: Date
48
49   @UpdatedAt
50   updatedAt: Date
51
52   @AllowNull(false)
53   @Is('VideoFileResolution', value => throwIfNotValid(value, isVideoFileResolutionValid, 'resolution'))
54   @Column
55   resolution: number
56
57   @AllowNull(false)
58   @Is('VideoFileSize', value => throwIfNotValid(value, isVideoFileSizeValid, 'size'))
59   @Column(DataType.BIGINT)
60   size: number
61
62   @AllowNull(false)
63   @Is('VideoFileExtname', value => throwIfNotValid(value, isVideoFileExtnameValid, 'extname'))
64   @Column
65   extname: string
66
67   @AllowNull(false)
68   @Is('VideoFileInfohash', value => throwIfNotValid(value, isVideoFileInfoHashValid, 'info hash'))
69   @Column
70   infoHash: string
71
72   @AllowNull(false)
73   @Default(-1)
74   @Is('VideoFileFPS', value => throwIfNotValid(value, isVideoFPSResolutionValid, 'fps'))
75   @Column
76   fps: number
77
78   @ForeignKey(() => VideoModel)
79   @Column
80   videoId: number
81
82   @BelongsTo(() => VideoModel, {
83     foreignKey: {
84       allowNull: false
85     },
86     onDelete: 'CASCADE'
87   })
88   Video: VideoModel
89
90   @HasMany(() => VideoRedundancyModel, {
91     foreignKey: {
92       allowNull: true
93     },
94     onDelete: 'CASCADE',
95     hooks: true
96   })
97   RedundancyVideos: VideoRedundancyModel[]
98
99   static doesInfohashExist (infoHash: string) {
100     const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1'
101     const options = {
102       type: QueryTypes.SELECT as QueryTypes.SELECT,
103       bind: { infoHash },
104       raw: true
105     }
106
107     return VideoModel.sequelize.query(query, options)
108               .then(results => results.length === 1)
109   }
110
111   static loadWithVideo (id: number) {
112     const options = {
113       include: [
114         {
115           model: VideoModel.unscoped(),
116           required: true
117         }
118       ]
119     }
120
121     return VideoFileModel.findByPk(id, options)
122   }
123
124   static listByStreamingPlaylist (streamingPlaylistId: number, transaction: Transaction) {
125     const query = {
126       include: [
127         {
128           model: VideoModel.unscoped(),
129           required: true,
130           include: [
131             {
132               model: VideoStreamingPlaylistModel.unscoped(),
133               required: true,
134               where: {
135                 id: streamingPlaylistId
136               }
137             }
138           ]
139         }
140       ],
141       transaction
142     }
143
144     return VideoFileModel.findAll(query)
145   }
146
147   static getStats () {
148     const query: FindOptions = {
149       include: [
150         {
151           attributes: [],
152           model: VideoModel.unscoped(),
153           where: {
154             remote: false
155           }
156         }
157       ]
158     }
159
160     return VideoFileModel.aggregate('size', 'SUM', query)
161       .then(result => ({
162         totalLocalVideoFilesSize: parseAggregateResult(result)
163       }))
164   }
165
166   isAudio () {
167     return !!MIMETYPES.AUDIO.EXT_MIMETYPE[this.extname]
168   }
169
170   hasSameUniqueKeysThan (other: MVideoFile) {
171     return this.fps === other.fps &&
172       this.resolution === other.resolution &&
173       this.videoId === other.videoId
174   }
175 }