aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/video/video-caption.ts
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-07-12 19:02:00 +0200
committerChocobozzz <me@florianbigard.com>2018-07-16 11:50:08 +0200
commit40e87e9ecc54e3513fb586928330a7855eb192c6 (patch)
treeaf1111ecba85f9cd8286811ff332a67cf21be2f6 /server/models/video/video-caption.ts
parentd4557fd3ecc8d4ed4fb0e5c868929bc36c959ed2 (diff)
downloadPeerTube-40e87e9ecc54e3513fb586928330a7855eb192c6.tar.gz
PeerTube-40e87e9ecc54e3513fb586928330a7855eb192c6.tar.zst
PeerTube-40e87e9ecc54e3513fb586928330a7855eb192c6.zip
Implement captions/subtitles
Diffstat (limited to 'server/models/video/video-caption.ts')
-rw-r--r--server/models/video/video-caption.ts173
1 files changed, 173 insertions, 0 deletions
diff --git a/server/models/video/video-caption.ts b/server/models/video/video-caption.ts
new file mode 100644
index 000000000..9920dfc7c
--- /dev/null
+++ b/server/models/video/video-caption.ts
@@ -0,0 +1,173 @@
1import * as Sequelize from 'sequelize'
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'
15import { throwIfNotValid } from '../utils'
16import { VideoModel } from './video'
17import { isVideoCaptionLanguageValid } from '../../helpers/custom-validators/video-captions'
18import { VideoCaption } from '../../../shared/models/videos/video-caption.model'
19import { CONFIG, STATIC_PATHS, VIDEO_LANGUAGES } from '../../initializers'
20import { join } from 'path'
21import { logger } from '../../helpers/logger'
22import { unlinkPromise } from '../../helpers/core-utils'
23
24export enum ScopeNames {
25 WITH_VIDEO_UUID_AND_REMOTE = 'WITH_VIDEO_UUID_AND_REMOTE'
26}
27
28@Scopes({
29 [ScopeNames.WITH_VIDEO_UUID_AND_REMOTE]: {
30 include: [
31 {
32 attributes: [ 'uuid', 'remote' ],
33 model: () => VideoModel.unscoped(),
34 required: true
35 }
36 ]
37 }
38})
39
40@Table({
41 tableName: 'videoCaption',
42 indexes: [
43 {
44 fields: [ 'videoId' ]
45 },
46 {
47 fields: [ 'videoId', 'language' ],
48 unique: true
49 }
50 ]
51})
52export class VideoCaptionModel extends Model<VideoCaptionModel> {
53 @CreatedAt
54 createdAt: Date
55
56 @UpdatedAt
57 updatedAt: Date
58
59 @AllowNull(false)
60 @Is('VideoCaptionLanguage', value => throwIfNotValid(value, isVideoCaptionLanguageValid, 'language'))
61 @Column
62 language: string
63
64 @ForeignKey(() => VideoModel)
65 @Column
66 videoId: number
67
68 @BelongsTo(() => VideoModel, {
69 foreignKey: {
70 allowNull: false
71 },
72 onDelete: 'CASCADE'
73 })
74 Video: VideoModel
75
76 @BeforeDestroy
77 static async removeFiles (instance: VideoCaptionModel) {
78
79 if (instance.isOwned()) {
80 if (!instance.Video) {
81 instance.Video = await instance.$get('Video') as VideoModel
82 }
83
84 logger.debug('Removing captions %s of video %s.', instance.Video.uuid, instance.language)
85 return instance.removeCaptionFile()
86 }
87
88 return undefined
89 }
90
91 static loadByVideoIdAndLanguage (videoId: string | number, language: string) {
92 const videoInclude = {
93 model: VideoModel.unscoped(),
94 attributes: [ 'id', 'remote', 'uuid' ],
95 where: { }
96 }
97
98 if (typeof videoId === 'string') videoInclude.where['uuid'] = videoId
99 else videoInclude.where['id'] = videoId
100
101 const query = {
102 where: {
103 language
104 },
105 include: [
106 videoInclude
107 ]
108 }
109
110 return VideoCaptionModel.findOne(query)
111 }
112
113 static insertOrReplaceLanguage (videoId: number, language: string, transaction: Sequelize.Transaction) {
114 const values = {
115 videoId,
116 language
117 }
118
119 return VideoCaptionModel.upsert(values, { transaction })
120 }
121
122 static listVideoCaptions (videoId: number) {
123 const query = {
124 order: [ [ 'language', 'ASC' ] ],
125 where: {
126 videoId
127 }
128 }
129
130 return VideoCaptionModel.scope(ScopeNames.WITH_VIDEO_UUID_AND_REMOTE).findAll(query)
131 }
132
133 static getLanguageLabel (language: string) {
134 return VIDEO_LANGUAGES[language] || 'Unknown'
135 }
136
137 static deleteAllCaptionsOfRemoteVideo (videoId: number, transaction: Sequelize.Transaction) {
138 const query = {
139 where: {
140 videoId
141 },
142 transaction
143 }
144
145 return VideoCaptionModel.destroy(query)
146 }
147
148 isOwned () {
149 return this.Video.remote === false
150 }
151
152 toFormattedJSON (): VideoCaption {
153 return {
154 language: {
155 id: this.language,
156 label: VideoCaptionModel.getLanguageLabel(this.language)
157 },
158 captionPath: this.getCaptionStaticPath()
159 }
160 }
161
162 getCaptionStaticPath () {
163 return join(STATIC_PATHS.VIDEO_CAPTIONS, this.getCaptionName())
164 }
165
166 getCaptionName () {
167 return `${this.Video.uuid}-${this.language}.vtt`
168 }
169
170 removeCaptionFile () {
171 return unlinkPromise(CONFIG.STORAGE.CAPTIONS_DIR + this.getCaptionName())
172 }
173}