Create a dedicated table to track video thumbnails
[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 { throwIfNotValid } from '../utils'
23 import { VideoModel } from './video'
24 import * as Sequelize from 'sequelize'
25 import { VideoRedundancyModel } from '../redundancy/video-redundancy'
26 import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
27
28 @Table({
29   tableName: 'videoFile',
30   indexes: [
31     {
32       fields: [ 'videoId' ]
33     },
34     {
35       fields: [ 'infoHash' ]
36     },
37     {
38       fields: [ 'videoId', 'resolution', 'fps' ],
39       unique: true
40     }
41   ]
42 })
43 export class VideoFileModel extends Model<VideoFileModel> {
44   @CreatedAt
45   createdAt: Date
46
47   @UpdatedAt
48   updatedAt: Date
49
50   @AllowNull(false)
51   @Is('VideoFileResolution', value => throwIfNotValid(value, isVideoFileResolutionValid, 'resolution'))
52   @Column
53   resolution: number
54
55   @AllowNull(false)
56   @Is('VideoFileSize', value => throwIfNotValid(value, isVideoFileSizeValid, 'size'))
57   @Column(DataType.BIGINT)
58   size: number
59
60   @AllowNull(false)
61   @Is('VideoFileExtname', value => throwIfNotValid(value, isVideoFileExtnameValid, 'extname'))
62   @Column
63   extname: string
64
65   @AllowNull(false)
66   @Is('VideoFileInfohash', value => throwIfNotValid(value, isVideoFileInfoHashValid, 'info hash'))
67   @Column
68   infoHash: string
69
70   @AllowNull(false)
71   @Default(-1)
72   @Is('VideoFileFPS', value => throwIfNotValid(value, isVideoFPSResolutionValid, 'fps'))
73   @Column
74   fps: number
75
76   @ForeignKey(() => VideoModel)
77   @Column
78   videoId: number
79
80   @BelongsTo(() => VideoModel, {
81     foreignKey: {
82       allowNull: false
83     },
84     onDelete: 'CASCADE'
85   })
86   Video: VideoModel
87
88   @HasMany(() => VideoRedundancyModel, {
89     foreignKey: {
90       allowNull: true
91     },
92     onDelete: 'CASCADE',
93     hooks: true
94   })
95   RedundancyVideos: VideoRedundancyModel[]
96
97   static doesInfohashExist (infoHash: string) {
98     const query = 'SELECT 1 FROM "videoFile" WHERE "infoHash" = $infoHash LIMIT 1'
99     const options = {
100       type: Sequelize.QueryTypes.SELECT,
101       bind: { infoHash },
102       raw: true
103     }
104
105     return VideoModel.sequelize.query(query, options)
106               .then(results => {
107                 return results.length === 1
108               })
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: Sequelize.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 async getStats () {
148     let totalLocalVideoFilesSize = await VideoFileModel.sum('size', {
149       include: [
150         {
151           attributes: [],
152           model: VideoModel.unscoped(),
153           where: {
154             remote: false
155           }
156         }
157       ]
158     } as any)
159     // Sequelize could return null...
160     if (!totalLocalVideoFilesSize) totalLocalVideoFilesSize = 0
161
162     return {
163       totalLocalVideoFilesSize
164     }
165   }
166
167   hasSameUniqueKeysThan (other: VideoFileModel) {
168     return this.fps === other.fps &&
169       this.resolution === other.resolution &&
170       this.videoId === other.videoId
171   }
172 }