]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/video/video-caption.ts
Stronger model typings
[github/Chocobozzz/PeerTube.git] / server / models / video / video-caption.ts
CommitLineData
1735c825 1import { OrderItem, Transaction } from 'sequelize'
40e87e9e
C
2import {
3 AllowNull,
4 BeforeDestroy,
5 BelongsTo,
6 Column,
7 CreatedAt,
8 ForeignKey,
9 Is,
10 Model,
11 Scopes,
12 Table,
13 UpdatedAt
14} from 'sequelize-typescript'
3acc5084 15import { buildWhereIdOrUUID, throwIfNotValid } from '../utils'
40e87e9e
C
16import { VideoModel } from './video'
17import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions'
59c76ffa 18import { VideoCaption } from '../../../shared/models/videos/caption/video-caption.model'
557b13ae 19import { LAZY_STATIC_PATHS, VIDEO_LANGUAGES } from '../../initializers/constants'
40e87e9e
C
20import { join } from 'path'
21import { logger } from '../../helpers/logger'
62689b94 22import { remove } from 'fs-extra'
6dd9de95 23import { CONFIG } from '../../initializers/config'
453e83ea
C
24import * as Bluebird from 'bluebird'
25import { MVideoCaptionVideo } from '@server/typings/models'
40e87e9e
C
26
27export enum ScopeNames {
28 WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE'
29}
30
3acc5084 31@Scopes(() => ({
40e87e9e
C
32 [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: {
33 include: [
34 {
453e83ea 35 attributes: [ 'id', 'uuid', 'remote' ],
3acc5084 36 model: VideoModel.unscoped(),
40e87e9e
C
37 required: true
38 }
39 ]
40 }
3acc5084 41}))
40e87e9e
C
42
43@Table({
44 tableName: 'videoCaption',
45 indexes: [
46 {
47 fields: [ 'videoId' ]
48 },
49 {
50 fields: [ 'videoId', 'language' ],
51 unique: true
52 }
53 ]
54})
55export class VideoCaptionModel extends Model<VideoCaptionModel> {
56 @CreatedAt
57 createdAt: Date
58
59 @UpdatedAt
60 updatedAt: Date
61
62 @AllowNull(false)
63 @Is('VideoCaptionLanguage', value => throwIfNotValid(value, isVideoCaptionLanguageValid, 'language'))
64 @Column
65 language: string
66
67 @ForeignKey(() => VideoModel)
68 @Column
69 videoId: number
70
71 @BelongsTo(() => VideoModel, {
72 foreignKey: {
73 allowNull: false
74 },
75 onDelete: 'CASCADE'
76 })
77 Video: VideoModel
78
79 @BeforeDestroy
80 static async removeFiles (instance: VideoCaptionModel) {
f4001cf4
C
81 if (!instance.Video) {
82 instance.Video = await instance.$get('Video') as VideoModel
83 }
40e87e9e
C
84
85 if (instance.isOwned()) {
8e0fd45e 86 logger.info('Removing captions %s of video %s.', instance.Video.uuid, instance.language)
f4001cf4
C
87
88 try {
89 await instance.removeCaptionFile()
90 } catch (err) {
91 logger.error('Cannot remove caption file of video %s.', instance.Video.uuid)
92 }
40e87e9e
C
93 }
94
95 return undefined
96 }
97
453e83ea 98 static loadByVideoIdAndLanguage (videoId: string | number, language: string): Bluebird<MVideoCaptionVideo> {
40e87e9e
C
99 const videoInclude = {
100 model: VideoModel.unscoped(),
101 attributes: [ 'id', 'remote', 'uuid' ],
3acc5084 102 where: buildWhereIdOrUUID(videoId)
40e87e9e
C
103 }
104
40e87e9e
C
105 const query = {
106 where: {
107 language
108 },
109 include: [
110 videoInclude
111 ]
112 }
113
114 return VideoCaptionModel.findOne(query)
115 }
116
1735c825 117 static insertOrReplaceLanguage (videoId: number, language: string, transaction: Transaction) {
40e87e9e
C
118 const values = {
119 videoId,
120 language
121 }
122
1735c825 123 return (VideoCaptionModel.upsert<VideoCaptionModel>(values, { transaction, returning: true }) as any) // FIXME: typings
d382f4e9 124 .then(([ caption ]) => caption)
40e87e9e
C
125 }
126
453e83ea 127 static listVideoCaptions (videoId: number): Bluebird<MVideoCaptionVideo[]> {
40e87e9e 128 const query = {
1735c825 129 order: [ [ 'language', 'ASC' ] ] as OrderItem[],
40e87e9e
C
130 where: {
131 videoId
132 }
133 }
134
135 return VideoCaptionModel.scope(ScopeNames.WITH_VIDEO_UUID_AND_REMOTE).findAll(query)
136 }
137
138 static getLanguageLabel (language: string) {
139 return VIDEO_LANGUAGES[language] || 'Unknown'
140 }
141
1735c825 142 static deleteAllCaptionsOfRemoteVideo (videoId: number, transaction: Transaction) {
40e87e9e
C
143 const query = {
144 where: {
145 videoId
146 },
147 transaction
148 }
149
150 return VideoCaptionModel.destroy(query)
151 }
152
153 isOwned () {
154 return this.Video.remote === false
155 }
156
157 toFormattedJSON (): VideoCaption {
158 return {
159 language: {
160 id: this.language,
161 label: VideoCaptionModel.getLanguageLabel(this.language)
162 },
163 captionPath: this.getCaptionStaticPath()
164 }
165 }
166
167 getCaptionStaticPath () {
557b13ae 168 return join(LAZY_STATIC_PATHS.VIDEO_CAPTIONS, this.getCaptionName())
40e87e9e
C
169 }
170
171 getCaptionName () {
172 return `${this.Video.uuid}-${this.language}.vtt`
173 }
174
175 removeCaptionFile () {
62689b94 176 return remove(CONFIG.STORAGE.CAPTIONS_DIR + this.getCaptionName())
40e87e9e
C
177 }
178}