aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/redundancy/video-redundancy.ts
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/redundancy/video-redundancy.ts')
-rw-r--r--server/models/redundancy/video-redundancy.ts139
1 files changed, 106 insertions, 33 deletions
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts
index 8f2ef2d9a..b722bed14 100644
--- a/server/models/redundancy/video-redundancy.ts
+++ b/server/models/redundancy/video-redundancy.ts
@@ -28,6 +28,7 @@ import { sample } from 'lodash'
28import { isTestInstance } from '../../helpers/core-utils' 28import { isTestInstance } from '../../helpers/core-utils'
29import * as Bluebird from 'bluebird' 29import * as Bluebird from 'bluebird'
30import * as Sequelize from 'sequelize' 30import * as Sequelize from 'sequelize'
31import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist'
31 32
32export enum ScopeNames { 33export enum ScopeNames {
33 WITH_VIDEO = 'WITH_VIDEO' 34 WITH_VIDEO = 'WITH_VIDEO'
@@ -38,7 +39,17 @@ export enum ScopeNames {
38 include: [ 39 include: [
39 { 40 {
40 model: () => VideoFileModel, 41 model: () => VideoFileModel,
41 required: true, 42 required: false,
43 include: [
44 {
45 model: () => VideoModel,
46 required: true
47 }
48 ]
49 },
50 {
51 model: () => VideoStreamingPlaylistModel,
52 required: false,
42 include: [ 53 include: [
43 { 54 {
44 model: () => VideoModel, 55 model: () => VideoModel,
@@ -97,12 +108,24 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
97 108
98 @BelongsTo(() => VideoFileModel, { 109 @BelongsTo(() => VideoFileModel, {
99 foreignKey: { 110 foreignKey: {
100 allowNull: false 111 allowNull: true
101 }, 112 },
102 onDelete: 'cascade' 113 onDelete: 'cascade'
103 }) 114 })
104 VideoFile: VideoFileModel 115 VideoFile: VideoFileModel
105 116
117 @ForeignKey(() => VideoStreamingPlaylistModel)
118 @Column
119 videoStreamingPlaylistId: number
120
121 @BelongsTo(() => VideoStreamingPlaylistModel, {
122 foreignKey: {
123 allowNull: true
124 },
125 onDelete: 'cascade'
126 })
127 VideoStreamingPlaylist: VideoStreamingPlaylistModel
128
106 @ForeignKey(() => ActorModel) 129 @ForeignKey(() => ActorModel)
107 @Column 130 @Column
108 actorId: number 131 actorId: number
@@ -119,13 +142,25 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
119 static async removeFile (instance: VideoRedundancyModel) { 142 static async removeFile (instance: VideoRedundancyModel) {
120 if (!instance.isOwned()) return 143 if (!instance.isOwned()) return
121 144
122 const videoFile = await VideoFileModel.loadWithVideo(instance.videoFileId) 145 if (instance.videoFileId) {
146 const videoFile = await VideoFileModel.loadWithVideo(instance.videoFileId)
123 147
124 const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}` 148 const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}`
125 logger.info('Removing duplicated video file %s.', logIdentifier) 149 logger.info('Removing duplicated video file %s.', logIdentifier)
126 150
127 videoFile.Video.removeFile(videoFile, true) 151 videoFile.Video.removeFile(videoFile, true)
128 .catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err })) 152 .catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err }))
153 }
154
155 if (instance.videoStreamingPlaylistId) {
156 const videoStreamingPlaylist = await VideoStreamingPlaylistModel.loadWithVideo(instance.videoStreamingPlaylistId)
157
158 const videoUUID = videoStreamingPlaylist.Video.uuid
159 logger.info('Removing duplicated video streaming playlist %s.', videoUUID)
160
161 videoStreamingPlaylist.Video.removeStreamingPlaylist(true)
162 .catch(err => logger.error('Cannot delete video streaming playlist files of %s.', videoUUID, { err }))
163 }
129 164
130 return undefined 165 return undefined
131 } 166 }
@@ -143,6 +178,19 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
143 return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query) 178 return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query)
144 } 179 }
145 180
181 static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number) {
182 const actor = await getServerActor()
183
184 const query = {
185 where: {
186 actorId: actor.id,
187 videoStreamingPlaylistId
188 }
189 }
190
191 return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query)
192 }
193
146 static loadByUrl (url: string, transaction?: Sequelize.Transaction) { 194 static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
147 const query = { 195 const query = {
148 where: { 196 where: {
@@ -191,7 +239,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
191 const ids = rows.map(r => r.id) 239 const ids = rows.map(r => r.id)
192 const id = sample(ids) 240 const id = sample(ids)
193 241
194 return VideoModel.loadWithFile(id, undefined, !isTestInstance()) 242 return VideoModel.loadWithFiles(id, undefined, !isTestInstance())
195 } 243 }
196 244
197 static async findMostViewToDuplicate (randomizedFactor: number) { 245 static async findMostViewToDuplicate (randomizedFactor: number) {
@@ -333,40 +381,44 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
333 381
334 static async listLocalOfServer (serverId: number) { 382 static async listLocalOfServer (serverId: number) {
335 const actor = await getServerActor() 383 const actor = await getServerActor()
336 384 const buildVideoInclude = () => ({
337 const query = { 385 model: VideoModel,
338 where: { 386 required: true,
339 actorId: actor.id
340 },
341 include: [ 387 include: [
342 { 388 {
343 model: VideoFileModel, 389 attributes: [],
390 model: VideoChannelModel.unscoped(),
344 required: true, 391 required: true,
345 include: [ 392 include: [
346 { 393 {
347 model: VideoModel, 394 attributes: [],
395 model: ActorModel.unscoped(),
348 required: true, 396 required: true,
349 include: [ 397 where: {
350 { 398 serverId
351 attributes: [], 399 }
352 model: VideoChannelModel.unscoped(),
353 required: true,
354 include: [
355 {
356 attributes: [],
357 model: ActorModel.unscoped(),
358 required: true,
359 where: {
360 serverId
361 }
362 }
363 ]
364 }
365 ]
366 } 400 }
367 ] 401 ]
368 } 402 }
369 ] 403 ]
404 })
405
406 const query = {
407 where: {
408 actorId: actor.id
409 },
410 include: [
411 {
412 model: VideoFileModel,
413 required: false,
414 include: [ buildVideoInclude() ]
415 },
416 {
417 model: VideoStreamingPlaylistModel,
418 required: false,
419 include: [ buildVideoInclude() ]
420 }
421 ]
370 } 422 }
371 423
372 return VideoRedundancyModel.findAll(query) 424 return VideoRedundancyModel.findAll(query)
@@ -403,11 +455,32 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
403 })) 455 }))
404 } 456 }
405 457
458 getVideo () {
459 if (this.VideoFile) return this.VideoFile.Video
460
461 return this.VideoStreamingPlaylist.Video
462 }
463
406 isOwned () { 464 isOwned () {
407 return !!this.strategy 465 return !!this.strategy
408 } 466 }
409 467
410 toActivityPubObject (): CacheFileObject { 468 toActivityPubObject (): CacheFileObject {
469 if (this.VideoStreamingPlaylist) {
470 return {
471 id: this.url,
472 type: 'CacheFile' as 'CacheFile',
473 object: this.VideoStreamingPlaylist.Video.url,
474 expires: this.expiresOn.toISOString(),
475 url: {
476 type: 'Link',
477 mimeType: 'application/x-mpegURL',
478 mediaType: 'application/x-mpegURL',
479 href: this.fileUrl
480 }
481 }
482 }
483
411 return { 484 return {
412 id: this.url, 485 id: this.url,
413 type: 'CacheFile' as 'CacheFile', 486 type: 'CacheFile' as 'CacheFile',
@@ -431,7 +504,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
431 504
432 const notIn = Sequelize.literal( 505 const notIn = Sequelize.literal(
433 '(' + 506 '(' +
434 `SELECT "videoFileId" FROM "videoRedundancy" WHERE "actorId" = ${actor.id}` + 507 `SELECT "videoFileId" FROM "videoRedundancy" WHERE "actorId" = ${actor.id} AND "videoFileId" IS NOT NULL` +
435 ')' 508 ')'
436 ) 509 )
437 510