]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/redundancy/video-redundancy.ts
Add hls support on server
[github/Chocobozzz/PeerTube.git] / server / models / redundancy / video-redundancy.ts
index 8f2ef2d9ac4206ebe1b8ce3905fed6ad4f3bc968..b722bed1482f62bd68b15e7c6cfcd2d7fc483286 100644 (file)
@@ -28,6 +28,7 @@ import { sample } from 'lodash'
 import { isTestInstance } from '../../helpers/core-utils'
 import * as Bluebird from 'bluebird'
 import * as Sequelize from 'sequelize'
+import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist'
 
 export enum ScopeNames {
   WITH_VIDEO = 'WITH_VIDEO'
@@ -38,7 +39,17 @@ export enum ScopeNames {
     include: [
       {
         model: () => VideoFileModel,
-        required: true,
+        required: false,
+        include: [
+          {
+            model: () => VideoModel,
+            required: true
+          }
+        ]
+      },
+      {
+        model: () => VideoStreamingPlaylistModel,
+        required: false,
         include: [
           {
             model: () => VideoModel,
@@ -97,12 +108,24 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
 
   @BelongsTo(() => VideoFileModel, {
     foreignKey: {
-      allowNull: false
+      allowNull: true
     },
     onDelete: 'cascade'
   })
   VideoFile: VideoFileModel
 
+  @ForeignKey(() => VideoStreamingPlaylistModel)
+  @Column
+  videoStreamingPlaylistId: number
+
+  @BelongsTo(() => VideoStreamingPlaylistModel, {
+    foreignKey: {
+      allowNull: true
+    },
+    onDelete: 'cascade'
+  })
+  VideoStreamingPlaylist: VideoStreamingPlaylistModel
+
   @ForeignKey(() => ActorModel)
   @Column
   actorId: number
@@ -119,13 +142,25 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
   static async removeFile (instance: VideoRedundancyModel) {
     if (!instance.isOwned()) return
 
-    const videoFile = await VideoFileModel.loadWithVideo(instance.videoFileId)
+    if (instance.videoFileId) {
+      const videoFile = await VideoFileModel.loadWithVideo(instance.videoFileId)
 
-    const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}`
-    logger.info('Removing duplicated video file %s.', logIdentifier)
+      const logIdentifier = `${videoFile.Video.uuid}-${videoFile.resolution}`
+      logger.info('Removing duplicated video file %s.', logIdentifier)
 
-    videoFile.Video.removeFile(videoFile, true)
-             .catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err }))
+      videoFile.Video.removeFile(videoFile, true)
+               .catch(err => logger.error('Cannot delete %s files.', logIdentifier, { err }))
+    }
+
+    if (instance.videoStreamingPlaylistId) {
+      const videoStreamingPlaylist = await VideoStreamingPlaylistModel.loadWithVideo(instance.videoStreamingPlaylistId)
+
+      const videoUUID = videoStreamingPlaylist.Video.uuid
+      logger.info('Removing duplicated video streaming playlist %s.', videoUUID)
+
+      videoStreamingPlaylist.Video.removeStreamingPlaylist(true)
+               .catch(err => logger.error('Cannot delete video streaming playlist files of %s.', videoUUID, { err }))
+    }
 
     return undefined
   }
@@ -143,6 +178,19 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
     return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query)
   }
 
+  static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number) {
+    const actor = await getServerActor()
+
+    const query = {
+      where: {
+        actorId: actor.id,
+        videoStreamingPlaylistId
+      }
+    }
+
+    return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query)
+  }
+
   static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
     const query = {
       where: {
@@ -191,7 +239,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
     const ids = rows.map(r => r.id)
     const id = sample(ids)
 
-    return VideoModel.loadWithFile(id, undefined, !isTestInstance())
+    return VideoModel.loadWithFiles(id, undefined, !isTestInstance())
   }
 
   static async findMostViewToDuplicate (randomizedFactor: number) {
@@ -333,40 +381,44 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
 
   static async listLocalOfServer (serverId: number) {
     const actor = await getServerActor()
-
-    const query = {
-      where: {
-        actorId: actor.id
-      },
+    const buildVideoInclude = () => ({
+      model: VideoModel,
+      required: true,
       include: [
         {
-          model: VideoFileModel,
+          attributes: [],
+          model: VideoChannelModel.unscoped(),
           required: true,
           include: [
             {
-              model: VideoModel,
+              attributes: [],
+              model: ActorModel.unscoped(),
               required: true,
-              include: [
-                {
-                  attributes: [],
-                  model: VideoChannelModel.unscoped(),
-                  required: true,
-                  include: [
-                    {
-                      attributes: [],
-                      model: ActorModel.unscoped(),
-                      required: true,
-                      where: {
-                        serverId
-                      }
-                    }
-                  ]
-                }
-              ]
+              where: {
+                serverId
+              }
             }
           ]
         }
       ]
+    })
+
+    const query = {
+      where: {
+        actorId: actor.id
+      },
+      include: [
+        {
+          model: VideoFileModel,
+          required: false,
+          include: [ buildVideoInclude() ]
+        },
+        {
+          model: VideoStreamingPlaylistModel,
+          required: false,
+          include: [ buildVideoInclude() ]
+        }
+      ]
     }
 
     return VideoRedundancyModel.findAll(query)
@@ -403,11 +455,32 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
       }))
   }
 
+  getVideo () {
+    if (this.VideoFile) return this.VideoFile.Video
+
+    return this.VideoStreamingPlaylist.Video
+  }
+
   isOwned () {
     return !!this.strategy
   }
 
   toActivityPubObject (): CacheFileObject {
+    if (this.VideoStreamingPlaylist) {
+      return {
+        id: this.url,
+        type: 'CacheFile' as 'CacheFile',
+        object: this.VideoStreamingPlaylist.Video.url,
+        expires: this.expiresOn.toISOString(),
+        url: {
+          type: 'Link',
+          mimeType: 'application/x-mpegURL',
+          mediaType: 'application/x-mpegURL',
+          href: this.fileUrl
+        }
+      }
+    }
+
     return {
       id: this.url,
       type: 'CacheFile' as 'CacheFile',
@@ -431,7 +504,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
 
     const notIn = Sequelize.literal(
       '(' +
-        `SELECT "videoFileId" FROM "videoRedundancy" WHERE "actorId" = ${actor.id}` +
+        `SELECT "videoFileId" FROM "videoRedundancy" WHERE "actorId" = ${actor.id} AND "videoFileId" IS NOT NULL` +
       ')'
     )