]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/redundancy/video-redundancy.ts
Stronger model typings
[github/Chocobozzz/PeerTube.git] / server / models / redundancy / video-redundancy.ts
index b722bed1482f62bd68b15e7c6cfcd2d7fc483286..1c216b30082e7a18be828965b19b1223799b991d 100644 (file)
@@ -13,9 +13,9 @@ import {
   UpdatedAt
 } from 'sequelize-typescript'
 import { ActorModel } from '../activitypub/actor'
-import { getVideoSort, throwIfNotValid } from '../utils'
+import { getVideoSort, parseAggregateResult, throwIfNotValid } from '../utils'
 import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc'
-import { CONFIG, CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers'
+import { CONSTRAINTS_FIELDS, MIMETYPES } from '../../initializers/constants'
 import { VideoFileModel } from '../video/video-file'
 import { getServerActor } from '../../helpers/utils'
 import { VideoModel } from '../video/video'
@@ -27,39 +27,41 @@ import { ServerModel } from '../server/server'
 import { sample } from 'lodash'
 import { isTestInstance } from '../../helpers/core-utils'
 import * as Bluebird from 'bluebird'
-import * as Sequelize from 'sequelize'
+import { col, FindOptions, fn, literal, Op, Transaction } from 'sequelize'
 import { VideoStreamingPlaylistModel } from '../video/video-streaming-playlist'
+import { CONFIG } from '../../initializers/config'
+import { MVideoRedundancy, MVideoRedundancyVideo } from '@server/typings/models'
 
 export enum ScopeNames {
   WITH_VIDEO = 'WITH_VIDEO'
 }
 
-@Scopes({
+@Scopes(() => ({
   [ ScopeNames.WITH_VIDEO ]: {
     include: [
       {
-        model: () => VideoFileModel,
+        model: VideoFileModel,
         required: false,
         include: [
           {
-            model: () => VideoModel,
+            model: VideoModel,
             required: true
           }
         ]
       },
       {
-        model: () => VideoStreamingPlaylistModel,
+        model: VideoStreamingPlaylistModel,
         required: false,
         include: [
           {
-            model: () => VideoModel,
+            model: VideoModel,
             required: true
           }
         ]
       }
     ]
   }
-})
+}))
 
 @Table({
   tableName: 'videoRedundancy',
@@ -165,7 +167,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
     return undefined
   }
 
-  static async loadLocalByFileId (videoFileId: number) {
+  static async loadLocalByFileId (videoFileId: number): Promise<MVideoRedundancyVideo> {
     const actor = await getServerActor()
 
     const query = {
@@ -178,7 +180,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
     return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query)
   }
 
-  static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number) {
+  static async loadLocalByStreamingPlaylistId (videoStreamingPlaylistId: number): Promise<MVideoRedundancyVideo> {
     const actor = await getServerActor()
 
     const query = {
@@ -191,7 +193,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
     return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO).findOne(query)
   }
 
-  static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
+  static loadByUrl (url: string, transaction?: Transaction): Bluebird<MVideoRedundancy> {
     const query = {
       where: {
         url
@@ -236,6 +238,8 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
 
   static async getVideoSample (p: Bluebird<VideoModel[]>) {
     const rows = await p
+    if (rows.length === 0) return undefined
+
     const ids = rows.map(r => r.id)
     const id = sample(ids)
 
@@ -291,7 +295,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
       where: {
         privacy: VideoPrivacy.PUBLIC,
         views: {
-          [ Sequelize.Op.gte ]: minViews
+          [ Op.gte ]: minViews
         }
       },
       include: [
@@ -303,7 +307,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
     return VideoRedundancyModel.getVideoSample(VideoModel.unscoped().findAll(query))
   }
 
-  static async loadOldestLocalThatAlreadyExpired (strategy: VideoRedundancyStrategy, expiresAfterMs: number) {
+  static async loadOldestLocalExpired (strategy: VideoRedundancyStrategy, expiresAfterMs: number): Promise<MVideoRedundancyVideo> {
     const expiredDate = new Date()
     expiredDate.setMilliseconds(expiredDate.getMilliseconds() - expiresAfterMs)
 
@@ -314,7 +318,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
         actorId: actor.id,
         strategy,
         createdAt: {
-          [ Sequelize.Op.lt ]: expiredDate
+          [ Op.lt ]: expiredDate
         }
       }
     }
@@ -324,27 +328,46 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
 
   static async getTotalDuplicated (strategy: VideoRedundancyStrategy) {
     const actor = await getServerActor()
+    const redundancyInclude = {
+      attributes: [],
+      model: VideoRedundancyModel,
+      required: true,
+      where: {
+        actorId: actor.id,
+        strategy
+      }
+    }
 
-    const options = {
+    const queryFiles: FindOptions = {
+      include: [ redundancyInclude ]
+    }
+
+    const queryStreamingPlaylists: FindOptions = {
       include: [
         {
           attributes: [],
-          model: VideoRedundancyModel,
+          model: VideoModel.unscoped(),
           required: true,
-          where: {
-            actorId: actor.id,
-            strategy
-          }
+          include: [
+            {
+              required: true,
+              attributes: [],
+              model: VideoStreamingPlaylistModel.unscoped(),
+              include: [
+                redundancyInclude
+              ]
+            }
+          ]
         }
       ]
     }
 
-    return VideoFileModel.sum('size', options as any) // FIXME: typings
-      .then(v => {
-        if (!v || isNaN(v)) return 0
-
-        return v
-      })
+    return Promise.all([
+      VideoFileModel.aggregate('size', 'SUM', queryFiles),
+      VideoFileModel.aggregate('size', 'SUM', queryStreamingPlaylists)
+    ]).then(([ r1, r2 ]) => {
+      return parseAggregateResult(r1) + parseAggregateResult(r2)
+    })
   }
 
   static async listLocalExpired () {
@@ -354,7 +377,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
       where: {
         actorId: actor.id,
         expiresOn: {
-          [ Sequelize.Op.lt ]: new Date()
+          [ Op.lt ]: new Date()
         }
       }
     }
@@ -368,10 +391,10 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
     const query = {
       where: {
         actorId: {
-          [Sequelize.Op.ne]: actor.id
+          [Op.ne]: actor.id
         },
         expiresOn: {
-          [ Sequelize.Op.lt ]: new Date()
+          [ Op.lt ]: new Date()
         }
       }
     }
@@ -427,12 +450,12 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
   static async getStats (strategy: VideoRedundancyStrategy) {
     const actor = await getServerActor()
 
-    const query = {
+    const query: FindOptions = {
       raw: true,
       attributes: [
-        [ Sequelize.fn('COALESCE', Sequelize.fn('SUM', Sequelize.col('VideoFile.size')), '0'), 'totalUsed' ],
-        [ Sequelize.fn('COUNT', Sequelize.fn('DISTINCT', Sequelize.col('videoId'))), 'totalVideos' ],
-        [ Sequelize.fn('COUNT', Sequelize.col('videoFileId')), 'totalVideoFiles' ]
+        [ fn('COALESCE', fn('SUM', col('VideoFile.size')), '0'), 'totalUsed' ],
+        [ fn('COUNT', fn('DISTINCT', col('videoId'))), 'totalVideos' ],
+        [ fn('COUNT', col('videoFileId')), 'totalVideoFiles' ]
       ],
       where: {
         strategy,
@@ -447,9 +470,9 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
       ]
     }
 
-    return VideoRedundancyModel.findOne(query as any) // FIXME: typings
+    return VideoRedundancyModel.findOne(query)
       .then((r: any) => ({
-        totalUsed: parseInt(r.totalUsed.toString(), 10),
+        totalUsed: parseAggregateResult(r.totalUsed),
         totalVideos: r.totalVideos,
         totalVideoFiles: r.totalVideoFiles
       }))
@@ -502,7 +525,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
   private static async buildVideoFileForDuplication () {
     const actor = await getServerActor()
 
-    const notIn = Sequelize.literal(
+    const notIn = literal(
       '(' +
         `SELECT "videoFileId" FROM "videoRedundancy" WHERE "actorId" = ${actor.id} AND "videoFileId" IS NOT NULL` +
       ')'
@@ -514,7 +537,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
       required: true,
       where: {
         id: {
-          [ Sequelize.Op.notIn ]: notIn
+          [ Op.notIn ]: notIn
         }
       }
     }