diff options
Diffstat (limited to 'server/lib/schedulers')
-rw-r--r-- | server/lib/schedulers/videos-redundancy-scheduler.ts | 64 | ||||
-rw-r--r-- | server/lib/schedulers/youtube-dl-update-scheduler.ts | 65 |
2 files changed, 32 insertions, 97 deletions
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts index ee9ba1766..960651712 100644 --- a/server/lib/schedulers/videos-redundancy-scheduler.ts +++ b/server/lib/schedulers/videos-redundancy-scheduler.ts | |||
@@ -1,10 +1,9 @@ | |||
1 | import { AbstractScheduler } from './abstract-scheduler' | 1 | import { AbstractScheduler } from './abstract-scheduler' |
2 | import { CONFIG, JOB_TTL, REDUNDANCY, SCHEDULER_INTERVALS_MS } from '../../initializers' | 2 | import { CONFIG, JOB_TTL, REDUNDANCY, SCHEDULER_INTERVALS_MS } from '../../initializers' |
3 | import { logger } from '../../helpers/logger' | 3 | import { logger } from '../../helpers/logger' |
4 | import { VideoRedundancyStrategy } from '../../../shared/models/redundancy' | 4 | import { VideoRedundancyStrategy, VideosRedundancy } from '../../../shared/models/redundancy' |
5 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' | 5 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' |
6 | import { VideoFileModel } from '../../models/video/video-file' | 6 | import { VideoFileModel } from '../../models/video/video-file' |
7 | import { sortBy } from 'lodash' | ||
8 | import { downloadWebTorrentVideo } from '../../helpers/webtorrent' | 7 | import { downloadWebTorrentVideo } from '../../helpers/webtorrent' |
9 | import { join } from 'path' | 8 | import { join } from 'path' |
10 | import { rename } from 'fs-extra' | 9 | import { rename } from 'fs-extra' |
@@ -12,7 +11,6 @@ import { getServerActor } from '../../helpers/utils' | |||
12 | import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send' | 11 | import { sendCreateCacheFile, sendUpdateCacheFile } from '../activitypub/send' |
13 | import { VideoModel } from '../../models/video/video' | 12 | import { VideoModel } from '../../models/video/video' |
14 | import { getVideoCacheFileActivityPubUrl } from '../activitypub/url' | 13 | import { getVideoCacheFileActivityPubUrl } from '../activitypub/url' |
15 | import { removeVideoRedundancy } from '../redundancy' | ||
16 | import { isTestInstance } from '../../helpers/core-utils' | 14 | import { isTestInstance } from '../../helpers/core-utils' |
17 | 15 | ||
18 | export class VideosRedundancyScheduler extends AbstractScheduler { | 16 | export class VideosRedundancyScheduler extends AbstractScheduler { |
@@ -20,7 +18,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
20 | private static instance: AbstractScheduler | 18 | private static instance: AbstractScheduler |
21 | private executing = false | 19 | private executing = false |
22 | 20 | ||
23 | protected schedulerIntervalMs = SCHEDULER_INTERVALS_MS.videosRedundancy | 21 | protected schedulerIntervalMs = CONFIG.REDUNDANCY.VIDEOS.CHECK_INTERVAL |
24 | 22 | ||
25 | private constructor () { | 23 | private constructor () { |
26 | super() | 24 | super() |
@@ -31,17 +29,15 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
31 | 29 | ||
32 | this.executing = true | 30 | this.executing = true |
33 | 31 | ||
34 | for (const obj of CONFIG.REDUNDANCY.VIDEOS) { | 32 | for (const obj of CONFIG.REDUNDANCY.VIDEOS.STRATEGIES) { |
35 | |||
36 | try { | 33 | try { |
37 | const videoToDuplicate = await this.findVideoToDuplicate(obj.strategy) | 34 | const videoToDuplicate = await this.findVideoToDuplicate(obj) |
38 | if (!videoToDuplicate) continue | 35 | if (!videoToDuplicate) continue |
39 | 36 | ||
40 | const videoFiles = videoToDuplicate.VideoFiles | 37 | const videoFiles = videoToDuplicate.VideoFiles |
41 | videoFiles.forEach(f => f.Video = videoToDuplicate) | 38 | videoFiles.forEach(f => f.Video = videoToDuplicate) |
42 | 39 | ||
43 | const videosRedundancy = await VideoRedundancyModel.getVideoFiles(obj.strategy) | 40 | if (await this.isTooHeavy(obj.strategy, videoFiles, obj.size)) { |
44 | if (this.isTooHeavy(videosRedundancy, videoFiles, obj.size)) { | ||
45 | if (!isTestInstance()) logger.info('Video %s is too big for our cache, skipping.', videoToDuplicate.url) | 41 | if (!isTestInstance()) logger.info('Video %s is too big for our cache, skipping.', videoToDuplicate.url) |
46 | continue | 42 | continue |
47 | } | 43 | } |
@@ -54,6 +50,16 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
54 | } | 50 | } |
55 | } | 51 | } |
56 | 52 | ||
53 | await this.removeExpired() | ||
54 | |||
55 | this.executing = false | ||
56 | } | ||
57 | |||
58 | static get Instance () { | ||
59 | return this.instance || (this.instance = new this()) | ||
60 | } | ||
61 | |||
62 | private async removeExpired () { | ||
57 | const expired = await VideoRedundancyModel.listAllExpired() | 63 | const expired = await VideoRedundancyModel.listAllExpired() |
58 | 64 | ||
59 | for (const m of expired) { | 65 | for (const m of expired) { |
@@ -65,16 +71,21 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
65 | logger.error('Cannot remove %s video from our redundancy system.', this.buildEntryLogId(m)) | 71 | logger.error('Cannot remove %s video from our redundancy system.', this.buildEntryLogId(m)) |
66 | } | 72 | } |
67 | } | 73 | } |
68 | |||
69 | this.executing = false | ||
70 | } | 74 | } |
71 | 75 | ||
72 | static get Instance () { | 76 | private findVideoToDuplicate (cache: VideosRedundancy) { |
73 | return this.instance || (this.instance = new this()) | 77 | if (cache.strategy === 'most-views') { |
74 | } | 78 | return VideoRedundancyModel.findMostViewToDuplicate(REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR) |
79 | } | ||
80 | |||
81 | if (cache.strategy === 'trending') { | ||
82 | return VideoRedundancyModel.findTrendingToDuplicate(REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR) | ||
83 | } | ||
75 | 84 | ||
76 | private findVideoToDuplicate (strategy: VideoRedundancyStrategy) { | 85 | if (cache.strategy === 'recently-added') { |
77 | if (strategy === 'most-views') return VideoRedundancyModel.findMostViewToDuplicate(REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR) | 86 | const minViews = cache.minViews |
87 | return VideoRedundancyModel.findRecentlyAddedToDuplicate(REDUNDANCY.VIDEOS.RANDOMIZED_FACTOR, minViews) | ||
88 | } | ||
78 | } | 89 | } |
79 | 90 | ||
80 | private async createVideoRedundancy (strategy: VideoRedundancyStrategy, filesToDuplicate: VideoFileModel[]) { | 91 | private async createVideoRedundancy (strategy: VideoRedundancyStrategy, filesToDuplicate: VideoFileModel[]) { |
@@ -120,27 +131,10 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
120 | } | 131 | } |
121 | } | 132 | } |
122 | 133 | ||
123 | // Unused, but could be useful in the future, with a custom strategy | 134 | private async isTooHeavy (strategy: VideoRedundancyStrategy, filesToDuplicate: VideoFileModel[], maxSizeArg: number) { |
124 | private async purgeVideosIfNeeded (videosRedundancy: VideoRedundancyModel[], filesToDuplicate: VideoFileModel[], maxSize: number) { | ||
125 | const sortedVideosRedundancy = sortBy(videosRedundancy, 'createdAt') | ||
126 | |||
127 | while (this.isTooHeavy(sortedVideosRedundancy, filesToDuplicate, maxSize)) { | ||
128 | const toDelete = sortedVideosRedundancy.shift() | ||
129 | |||
130 | const videoFile = toDelete.VideoFile | ||
131 | logger.info('Purging video %s (resolution %d) from our redundancy system.', videoFile.Video.url, videoFile.resolution) | ||
132 | |||
133 | await removeVideoRedundancy(toDelete, undefined) | ||
134 | } | ||
135 | |||
136 | return sortedVideosRedundancy | ||
137 | } | ||
138 | |||
139 | private isTooHeavy (videosRedundancy: VideoRedundancyModel[], filesToDuplicate: VideoFileModel[], maxSizeArg: number) { | ||
140 | const maxSize = maxSizeArg - this.getTotalFileSizes(filesToDuplicate) | 135 | const maxSize = maxSizeArg - this.getTotalFileSizes(filesToDuplicate) |
141 | 136 | ||
142 | const redundancyReducer = (previous: number, current: VideoRedundancyModel) => previous + current.VideoFile.size | 137 | const totalDuplicated = await VideoRedundancyModel.getTotalDuplicated(strategy) |
143 | const totalDuplicated = videosRedundancy.reduce(redundancyReducer, 0) | ||
144 | 138 | ||
145 | return totalDuplicated > maxSize | 139 | return totalDuplicated > maxSize |
146 | } | 140 | } |
diff --git a/server/lib/schedulers/youtube-dl-update-scheduler.ts b/server/lib/schedulers/youtube-dl-update-scheduler.ts index faadb4334..461cd045e 100644 --- a/server/lib/schedulers/youtube-dl-update-scheduler.ts +++ b/server/lib/schedulers/youtube-dl-update-scheduler.ts | |||
@@ -1,13 +1,6 @@ | |||
1 | // Thanks: https://github.com/przemyslawpluta/node-youtube-dl/blob/master/lib/downloader.js | ||
2 | // We rewrote it to avoid sync calls | ||
3 | |||
4 | import { AbstractScheduler } from './abstract-scheduler' | 1 | import { AbstractScheduler } from './abstract-scheduler' |
5 | import { SCHEDULER_INTERVALS_MS } from '../../initializers' | 2 | import { SCHEDULER_INTERVALS_MS } from '../../initializers' |
6 | import { logger } from '../../helpers/logger' | 3 | import { updateYoutubeDLBinary } from '../../helpers/youtube-dl' |
7 | import * as request from 'request' | ||
8 | import { createWriteStream, ensureDir, writeFile } from 'fs-extra' | ||
9 | import { join } from 'path' | ||
10 | import { root } from '../../helpers/core-utils' | ||
11 | 4 | ||
12 | export class YoutubeDlUpdateScheduler extends AbstractScheduler { | 5 | export class YoutubeDlUpdateScheduler extends AbstractScheduler { |
13 | 6 | ||
@@ -19,60 +12,8 @@ export class YoutubeDlUpdateScheduler extends AbstractScheduler { | |||
19 | super() | 12 | super() |
20 | } | 13 | } |
21 | 14 | ||
22 | async execute () { | 15 | execute () { |
23 | logger.info('Updating youtubeDL binary.') | 16 | return updateYoutubeDLBinary() |
24 | |||
25 | const binDirectory = join(root(), 'node_modules', 'youtube-dl', 'bin') | ||
26 | const bin = join(binDirectory, 'youtube-dl') | ||
27 | const detailsPath = join(binDirectory, 'details') | ||
28 | const url = 'https://yt-dl.org/downloads/latest/youtube-dl' | ||
29 | |||
30 | await ensureDir(binDirectory) | ||
31 | |||
32 | return new Promise(res => { | ||
33 | request.get(url, { followRedirect: false }, (err, result) => { | ||
34 | if (err) { | ||
35 | logger.error('Cannot update youtube-dl.', { err }) | ||
36 | return res() | ||
37 | } | ||
38 | |||
39 | if (result.statusCode !== 302) { | ||
40 | logger.error('youtube-dl update error: did not get redirect for the latest version link. Status %d', result.statusCode) | ||
41 | return res() | ||
42 | } | ||
43 | |||
44 | const url = result.headers.location | ||
45 | const downloadFile = request.get(url) | ||
46 | const newVersion = /yt-dl\.org\/downloads\/(\d{4}\.\d\d\.\d\d(\.\d)?)\/youtube-dl/.exec(url)[ 1 ] | ||
47 | |||
48 | downloadFile.on('response', result => { | ||
49 | if (result.statusCode !== 200) { | ||
50 | logger.error('Cannot update youtube-dl: new version response is not 200, it\'s %d.', result.statusCode) | ||
51 | return res() | ||
52 | } | ||
53 | |||
54 | downloadFile.pipe(createWriteStream(bin, { mode: 493 })) | ||
55 | }) | ||
56 | |||
57 | downloadFile.on('error', err => { | ||
58 | logger.error('youtube-dl update error.', { err }) | ||
59 | return res() | ||
60 | }) | ||
61 | |||
62 | downloadFile.on('end', () => { | ||
63 | const details = JSON.stringify({ version: newVersion, path: bin, exec: 'youtube-dl' }) | ||
64 | writeFile(detailsPath, details, { encoding: 'utf8' }, err => { | ||
65 | if (err) { | ||
66 | logger.error('youtube-dl update error: cannot write details.', { err }) | ||
67 | return res() | ||
68 | } | ||
69 | |||
70 | logger.info('youtube-dl updated to version %s.', newVersion) | ||
71 | return res() | ||
72 | }) | ||
73 | }) | ||
74 | }) | ||
75 | }) | ||
76 | } | 17 | } |
77 | 18 | ||
78 | static get Instance () { | 19 | static get Instance () { |