+ static async getStats (strategy: VideoRedundancyStrategyWithManual) {
+ const actor = await getServerActor()
+
+ const sql = `WITH "tmp" AS ` +
+ `(` +
+ `SELECT "videoFile"."size" AS "videoFileSize", "videoStreamingFile"."size" AS "videoStreamingFileSize", ` +
+ `"videoFile"."videoId" AS "videoFileVideoId", "videoStreamingPlaylist"."videoId" AS "videoStreamingVideoId"` +
+ `FROM "videoRedundancy" AS "videoRedundancy" ` +
+ `LEFT JOIN "videoFile" AS "videoFile" ON "videoRedundancy"."videoFileId" = "videoFile"."id" ` +
+ `LEFT JOIN "videoStreamingPlaylist" ON "videoRedundancy"."videoStreamingPlaylistId" = "videoStreamingPlaylist"."id" ` +
+ `LEFT JOIN "videoFile" AS "videoStreamingFile" ` +
+ `ON "videoStreamingPlaylist"."id" = "videoStreamingFile"."videoStreamingPlaylistId" ` +
+ `WHERE "videoRedundancy"."strategy" = :strategy AND "videoRedundancy"."actorId" = :actorId` +
+ `), ` +
+ `"videoIds" AS (` +
+ `SELECT "videoFileVideoId" AS "videoId" FROM "tmp" ` +
+ `UNION SELECT "videoStreamingVideoId" AS "videoId" FROM "tmp" ` +
+ `) ` +
+ `SELECT ` +
+ `COALESCE(SUM("videoFileSize"), '0') + COALESCE(SUM("videoStreamingFileSize"), '0') AS "totalUsed", ` +
+ `(SELECT COUNT("videoIds"."videoId") FROM "videoIds") AS "totalVideos", ` +
+ `COUNT(*) AS "totalVideoFiles" ` +
+ `FROM "tmp"`
+
+ return VideoRedundancyModel.sequelize.query<any>(sql, {
+ replacements: { strategy, actorId: actor.id },
+ type: QueryTypes.SELECT
+ }).then(([ row ]) => ({
+ totalUsed: parseAggregateResult(row.totalUsed),
+ totalVideos: row.totalVideos,
+ totalVideoFiles: row.totalVideoFiles
+ }))
+ }
+
+ static toFormattedJSONStatic (video: MVideoForRedundancyAPI): VideoRedundancy {
+ const filesRedundancies: FileRedundancyInformation[] = []
+ const streamingPlaylistsRedundancies: StreamingPlaylistRedundancyInformation[] = []
+
+ for (const file of video.VideoFiles) {
+ for (const redundancy of file.RedundancyVideos) {
+ filesRedundancies.push({
+ id: redundancy.id,
+ fileUrl: redundancy.fileUrl,
+ strategy: redundancy.strategy,
+ createdAt: redundancy.createdAt,
+ updatedAt: redundancy.updatedAt,
+ expiresOn: redundancy.expiresOn,
+ size: file.size
+ })
+ }
+ }
+
+ for (const playlist of video.VideoStreamingPlaylists) {
+ const size = playlist.VideoFiles.reduce((a, b) => a + b.size, 0)
+
+ for (const redundancy of playlist.RedundancyVideos) {
+ streamingPlaylistsRedundancies.push({
+ id: redundancy.id,
+ fileUrl: redundancy.fileUrl,
+ strategy: redundancy.strategy,
+ createdAt: redundancy.createdAt,
+ updatedAt: redundancy.updatedAt,
+ expiresOn: redundancy.expiresOn,
+ size
+ })
+ }
+ }
+
+ return {
+ id: video.id,
+ name: video.name,
+ url: video.url,
+ uuid: video.uuid,
+
+ redundancies: {
+ files: filesRedundancies,
+ streamingPlaylists: streamingPlaylistsRedundancies
+ }
+ }
+ }
+
+ getVideo () {
+ if (this.VideoFile?.Video) return this.VideoFile.Video
+
+ if (this.VideoStreamingPlaylist?.Video) return this.VideoStreamingPlaylist.Video
+
+ return undefined
+ }
+
+ getVideoUUID () {
+ const video = this.getVideo()
+ if (!video) return undefined
+
+ return video.uuid
+ }
+
+ isOwned () {
+ return !!this.strategy
+ }
+
+ toActivityPubObject (this: MVideoRedundancyAP): CacheFileObject {
+ if (this.VideoStreamingPlaylist) {
+ return {
+ id: this.url,
+ type: 'CacheFile' as 'CacheFile',
+ object: this.VideoStreamingPlaylist.Video.url,
+ expires: this.expiresOn ? this.expiresOn.toISOString() : null,
+ url: {
+ type: 'Link',
+ mediaType: 'application/x-mpegURL',
+ href: this.fileUrl
+ }
+ }
+ }
+