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.ts189
1 files changed, 141 insertions, 48 deletions
diff --git a/server/models/redundancy/video-redundancy.ts b/server/models/redundancy/video-redundancy.ts
index 48ec77206..fb07287a8 100644
--- a/server/models/redundancy/video-redundancy.ts
+++ b/server/models/redundancy/video-redundancy.ts
@@ -14,11 +14,10 @@ import {
14 UpdatedAt 14 UpdatedAt
15} from 'sequelize-typescript' 15} from 'sequelize-typescript'
16import { ActorModel } from '../activitypub/actor' 16import { ActorModel } from '../activitypub/actor'
17import { throwIfNotValid } from '../utils' 17import { getVideoSort, throwIfNotValid } from '../utils'
18import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc' 18import { isActivityPubUrlValid, isUrlValid } from '../../helpers/custom-validators/activitypub/misc'
19import { CONSTRAINTS_FIELDS, VIDEO_EXT_MIMETYPE } from '../../initializers' 19import { CONFIG, CONSTRAINTS_FIELDS, VIDEO_EXT_MIMETYPE } from '../../initializers'
20import { VideoFileModel } from '../video/video-file' 20import { VideoFileModel } from '../video/video-file'
21import { isDateValid } from '../../helpers/custom-validators/misc'
22import { getServerActor } from '../../helpers/utils' 21import { getServerActor } from '../../helpers/utils'
23import { VideoModel } from '../video/video' 22import { VideoModel } from '../video/video'
24import { VideoRedundancyStrategy } from '../../../shared/models/redundancy' 23import { VideoRedundancyStrategy } from '../../../shared/models/redundancy'
@@ -28,6 +27,7 @@ import { VideoChannelModel } from '../video/video-channel'
28import { ServerModel } from '../server/server' 27import { ServerModel } from '../server/server'
29import { sample } from 'lodash' 28import { sample } from 'lodash'
30import { isTestInstance } from '../../helpers/core-utils' 29import { isTestInstance } from '../../helpers/core-utils'
30import * as Bluebird from 'bluebird'
31 31
32export enum ScopeNames { 32export enum ScopeNames {
33 WITH_VIDEO = 'WITH_VIDEO' 33 WITH_VIDEO = 'WITH_VIDEO'
@@ -145,65 +145,90 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
145 return VideoRedundancyModel.findOne(query) 145 return VideoRedundancyModel.findOne(query)
146 } 146 }
147 147
148 static async getVideoSample (p: Bluebird<VideoModel[]>) {
149 const rows = await p
150 const ids = rows.map(r => r.id)
151 const id = sample(ids)
152
153 return VideoModel.loadWithFile(id, undefined, !isTestInstance())
154 }
155
148 static async findMostViewToDuplicate (randomizedFactor: number) { 156 static async findMostViewToDuplicate (randomizedFactor: number) {
149 // On VideoModel! 157 // On VideoModel!
150 const query = { 158 const query = {
159 attributes: [ 'id', 'views' ],
151 logging: !isTestInstance(), 160 logging: !isTestInstance(),
152 limit: randomizedFactor, 161 limit: randomizedFactor,
153 order: [ [ 'views', 'DESC' ] ], 162 order: getVideoSort('-views'),
154 include: [ 163 include: [
155 { 164 await VideoRedundancyModel.buildVideoFileForDuplication(),
156 model: VideoFileModel.unscoped(), 165 VideoRedundancyModel.buildServerRedundancyInclude()
157 required: true,
158 where: {
159 id: {
160 [ Sequelize.Op.notIn ]: await VideoRedundancyModel.buildExcludeIn()
161 }
162 }
163 },
164 {
165 attributes: [],
166 model: VideoChannelModel.unscoped(),
167 required: true,
168 include: [
169 {
170 attributes: [],
171 model: ActorModel.unscoped(),
172 required: true,
173 include: [
174 {
175 attributes: [],
176 model: ServerModel.unscoped(),
177 required: true,
178 where: {
179 redundancyAllowed: true
180 }
181 }
182 ]
183 }
184 ]
185 }
186 ] 166 ]
187 } 167 }
188 168
189 const rows = await VideoModel.unscoped().findAll(query) 169 return VideoRedundancyModel.getVideoSample(VideoModel.unscoped().findAll(query))
170 }
190 171
191 return sample(rows) 172 static async findTrendingToDuplicate (randomizedFactor: number) {
173 // On VideoModel!
174 const query = {
175 attributes: [ 'id', 'views' ],
176 subQuery: false,
177 logging: !isTestInstance(),
178 group: 'VideoModel.id',
179 limit: randomizedFactor,
180 order: getVideoSort('-trending'),
181 include: [
182 await VideoRedundancyModel.buildVideoFileForDuplication(),
183 VideoRedundancyModel.buildServerRedundancyInclude(),
184
185 VideoModel.buildTrendingQuery(CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS)
186 ]
187 }
188
189 return VideoRedundancyModel.getVideoSample(VideoModel.unscoped().findAll(query))
192 } 190 }
193 191
194 static async getVideoFiles (strategy: VideoRedundancyStrategy) { 192 static async findRecentlyAddedToDuplicate (randomizedFactor: number, minViews: number) {
193 // On VideoModel!
194 const query = {
195 attributes: [ 'id', 'publishedAt' ],
196 logging: !isTestInstance(),
197 limit: randomizedFactor,
198 order: getVideoSort('-publishedAt'),
199 where: {
200 views: {
201 [ Sequelize.Op.gte ]: minViews
202 }
203 },
204 include: [
205 await VideoRedundancyModel.buildVideoFileForDuplication(),
206 VideoRedundancyModel.buildServerRedundancyInclude()
207 ]
208 }
209
210 return VideoRedundancyModel.getVideoSample(VideoModel.unscoped().findAll(query))
211 }
212
213 static async getTotalDuplicated (strategy: VideoRedundancyStrategy) {
195 const actor = await getServerActor() 214 const actor = await getServerActor()
196 215
197 const queryVideoFiles = { 216 const options = {
198 logging: !isTestInstance(), 217 logging: !isTestInstance(),
199 where: { 218 include: [
200 actorId: actor.id, 219 {
201 strategy 220 attributes: [],
202 } 221 model: VideoRedundancyModel,
222 required: true,
223 where: {
224 actorId: actor.id,
225 strategy
226 }
227 }
228 ]
203 } 229 }
204 230
205 return VideoRedundancyModel.scope(ScopeNames.WITH_VIDEO) 231 return VideoFileModel.sum('size', options)
206 .findAll(queryVideoFiles)
207 } 232 }
208 233
209 static listAllExpired () { 234 static listAllExpired () {
@@ -211,7 +236,7 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
211 logging: !isTestInstance(), 236 logging: !isTestInstance(),
212 where: { 237 where: {
213 expiresOn: { 238 expiresOn: {
214 [Sequelize.Op.lt]: new Date() 239 [ Sequelize.Op.lt ]: new Date()
215 } 240 }
216 } 241 }
217 } 242 }
@@ -220,6 +245,37 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
220 .findAll(query) 245 .findAll(query)
221 } 246 }
222 247
248 static async getStats (strategy: VideoRedundancyStrategy) {
249 const actor = await getServerActor()
250
251 const query = {
252 raw: true,
253 attributes: [
254 [ Sequelize.fn('COALESCE', Sequelize.fn('SUM', Sequelize.col('VideoFile.size')), '0'), 'totalUsed' ],
255 [ Sequelize.fn('COUNT', Sequelize.fn('DISTINCT', 'videoId')), 'totalVideos' ],
256 [ Sequelize.fn('COUNT', 'videoFileId'), 'totalVideoFiles' ]
257 ],
258 where: {
259 strategy,
260 actorId: actor.id
261 },
262 include: [
263 {
264 attributes: [],
265 model: VideoFileModel,
266 required: true
267 }
268 ]
269 }
270
271 return VideoRedundancyModel.find(query as any) // FIXME: typings
272 .then((r: any) => ({
273 totalUsed: parseInt(r.totalUsed.toString(), 10),
274 totalVideos: r.totalVideos,
275 totalVideoFiles: r.totalVideoFiles
276 }))
277 }
278
223 toActivityPubObject (): CacheFileObject { 279 toActivityPubObject (): CacheFileObject {
224 return { 280 return {
225 id: this.url, 281 id: this.url,
@@ -237,13 +293,50 @@ export class VideoRedundancyModel extends Model<VideoRedundancyModel> {
237 } 293 }
238 } 294 }
239 295
240 private static async buildExcludeIn () { 296 // Don't include video files we already duplicated
297 private static async buildVideoFileForDuplication () {
241 const actor = await getServerActor() 298 const actor = await getServerActor()
242 299
243 return Sequelize.literal( 300 const notIn = Sequelize.literal(
244 '(' + 301 '(' +
245 `SELECT "videoFileId" FROM "videoRedundancy" WHERE "actorId" = ${actor.id} AND "expiresOn" >= NOW()` + 302 `SELECT "videoFileId" FROM "videoRedundancy" WHERE "actorId" = ${actor.id} AND "expiresOn" >= NOW()` +
246 ')' 303 ')'
247 ) 304 )
305
306 return {
307 attributes: [],
308 model: VideoFileModel.unscoped(),
309 required: true,
310 where: {
311 id: {
312 [ Sequelize.Op.notIn ]: notIn
313 }
314 }
315 }
316 }
317
318 private static buildServerRedundancyInclude () {
319 return {
320 attributes: [],
321 model: VideoChannelModel.unscoped(),
322 required: true,
323 include: [
324 {
325 attributes: [],
326 model: ActorModel.unscoped(),
327 required: true,
328 include: [
329 {
330 attributes: [],
331 model: ServerModel.unscoped(),
332 required: true,
333 where: {
334 redundancyAllowed: true
335 }
336 }
337 ]
338 }
339 ]
340 }
248 } 341 }
249} 342}