diff options
Diffstat (limited to 'server/initializers')
-rw-r--r-- | server/initializers/checker-after-init.ts | 23 | ||||
-rw-r--r-- | server/initializers/config.ts | 20 | ||||
-rw-r--r-- | server/initializers/constants.ts | 14 | ||||
-rw-r--r-- | server/initializers/database.ts | 4 | ||||
-rw-r--r-- | server/initializers/migrations/0065-video-file-size.ts | 28 | ||||
-rw-r--r-- | server/initializers/migrations/0660-object-storage.ts | 58 |
6 files changed, 114 insertions, 33 deletions
diff --git a/server/initializers/checker-after-init.ts b/server/initializers/checker-after-init.ts index 911734fa0..09f587274 100644 --- a/server/initializers/checker-after-init.ts +++ b/server/initializers/checker-after-init.ts | |||
@@ -153,6 +153,29 @@ function checkConfig () { | |||
153 | } | 153 | } |
154 | } | 154 | } |
155 | 155 | ||
156 | // Object storage | ||
157 | if (CONFIG.OBJECT_STORAGE.ENABLED === true) { | ||
158 | |||
159 | if (!CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME) { | ||
160 | return 'videos_bucket should be set when object storage support is enabled.' | ||
161 | } | ||
162 | |||
163 | if (!CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME) { | ||
164 | return 'streaming_playlists_bucket should be set when object storage support is enabled.' | ||
165 | } | ||
166 | |||
167 | if ( | ||
168 | CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME && | ||
169 | CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.PREFIX | ||
170 | ) { | ||
171 | if (CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === '') { | ||
172 | return 'Object storage bucket prefixes should be set when the same bucket is used for both types of video.' | ||
173 | } else { | ||
174 | return 'Object storage bucket prefixes should be set to different values when the same bucket is used for both types of video.' | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | |||
156 | return null | 179 | return null |
157 | } | 180 | } |
158 | 181 | ||
diff --git a/server/initializers/config.ts b/server/initializers/config.ts index 30a9823b9..0e684eef8 100644 --- a/server/initializers/config.ts +++ b/server/initializers/config.ts | |||
@@ -73,6 +73,26 @@ const CONFIG = { | |||
73 | PLUGINS_DIR: buildPath(config.get<string>('storage.plugins')), | 73 | PLUGINS_DIR: buildPath(config.get<string>('storage.plugins')), |
74 | CLIENT_OVERRIDES_DIR: buildPath(config.get<string>('storage.client_overrides')) | 74 | CLIENT_OVERRIDES_DIR: buildPath(config.get<string>('storage.client_overrides')) |
75 | }, | 75 | }, |
76 | OBJECT_STORAGE: { | ||
77 | ENABLED: config.get<boolean>('object_storage.enabled'), | ||
78 | MAX_UPLOAD_PART: bytes.parse(config.get<string>('object_storage.max_upload_part')), | ||
79 | ENDPOINT: config.get<string>('object_storage.endpoint'), | ||
80 | REGION: config.get<string>('object_storage.region'), | ||
81 | CREDENTIALS: { | ||
82 | ACCESS_KEY_ID: config.get<string>('object_storage.credentials.access_key_id'), | ||
83 | SECRET_ACCESS_KEY: config.get<string>('object_storage.credentials.secret_access_key') | ||
84 | }, | ||
85 | VIDEOS: { | ||
86 | BUCKET_NAME: config.get<string>('object_storage.videos.bucket_name'), | ||
87 | PREFIX: config.get<string>('object_storage.videos.prefix'), | ||
88 | BASE_URL: config.get<string>('object_storage.videos.base_url') | ||
89 | }, | ||
90 | STREAMING_PLAYLISTS: { | ||
91 | BUCKET_NAME: config.get<string>('object_storage.streaming_playlists.bucket_name'), | ||
92 | PREFIX: config.get<string>('object_storage.streaming_playlists.prefix'), | ||
93 | BASE_URL: config.get<string>('object_storage.streaming_playlists.base_url') | ||
94 | } | ||
95 | }, | ||
76 | WEBSERVER: { | 96 | WEBSERVER: { |
77 | SCHEME: config.get<boolean>('webserver.https') === true ? 'https' : 'http', | 97 | SCHEME: config.get<boolean>('webserver.https') === true ? 'https' : 'http', |
78 | WS: config.get<boolean>('webserver.https') === true ? 'wss' : 'ws', | 98 | WS: config.get<boolean>('webserver.https') === true ? 'wss' : 'ws', |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 5f121d9a4..8a1526ae8 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' | |||
24 | 24 | ||
25 | // --------------------------------------------------------------------------- | 25 | // --------------------------------------------------------------------------- |
26 | 26 | ||
27 | const LAST_MIGRATION_VERSION = 655 | 27 | const LAST_MIGRATION_VERSION = 660 |
28 | 28 | ||
29 | // --------------------------------------------------------------------------- | 29 | // --------------------------------------------------------------------------- |
30 | 30 | ||
@@ -147,7 +147,8 @@ const JOB_ATTEMPTS: { [id in JobType]: number } = { | |||
147 | 'videos-views': 1, | 147 | 'videos-views': 1, |
148 | 'activitypub-refresher': 1, | 148 | 'activitypub-refresher': 1, |
149 | 'video-redundancy': 1, | 149 | 'video-redundancy': 1, |
150 | 'video-live-ending': 1 | 150 | 'video-live-ending': 1, |
151 | 'move-to-object-storage': 3 | ||
151 | } | 152 | } |
152 | // Excluded keys are jobs that can be configured by admins | 153 | // Excluded keys are jobs that can be configured by admins |
153 | const JOB_CONCURRENCY: { [id in Exclude<JobType, 'video-transcoding' | 'video-import'>]: number } = { | 154 | const JOB_CONCURRENCY: { [id in Exclude<JobType, 'video-transcoding' | 'video-import'>]: number } = { |
@@ -162,7 +163,8 @@ const JOB_CONCURRENCY: { [id in Exclude<JobType, 'video-transcoding' | 'video-im | |||
162 | 'videos-views': 1, | 163 | 'videos-views': 1, |
163 | 'activitypub-refresher': 1, | 164 | 'activitypub-refresher': 1, |
164 | 'video-redundancy': 1, | 165 | 'video-redundancy': 1, |
165 | 'video-live-ending': 10 | 166 | 'video-live-ending': 10, |
167 | 'move-to-object-storage': 1 | ||
166 | } | 168 | } |
167 | const JOB_TTL: { [id in JobType]: number } = { | 169 | const JOB_TTL: { [id in JobType]: number } = { |
168 | 'activitypub-http-broadcast': 60000 * 10, // 10 minutes | 170 | 'activitypub-http-broadcast': 60000 * 10, // 10 minutes |
@@ -178,7 +180,8 @@ const JOB_TTL: { [id in JobType]: number } = { | |||
178 | 'videos-views': undefined, // Unlimited | 180 | 'videos-views': undefined, // Unlimited |
179 | 'activitypub-refresher': 60000 * 10, // 10 minutes | 181 | 'activitypub-refresher': 60000 * 10, // 10 minutes |
180 | 'video-redundancy': 1000 * 3600 * 3, // 3 hours | 182 | 'video-redundancy': 1000 * 3600 * 3, // 3 hours |
181 | 'video-live-ending': 1000 * 60 * 10 // 10 minutes | 183 | 'video-live-ending': 1000 * 60 * 10, // 10 minutes |
184 | 'move-to-object-storage': 1000 * 60 * 60 * 3 // 3 hours | ||
182 | } | 185 | } |
183 | const REPEAT_JOBS: { [ id: string ]: EveryRepeatOptions | CronRepeatOptions } = { | 186 | const REPEAT_JOBS: { [ id: string ]: EveryRepeatOptions | CronRepeatOptions } = { |
184 | 'videos-views': { | 187 | 'videos-views': { |
@@ -412,7 +415,8 @@ const VIDEO_STATES: { [ id in VideoState ]: string } = { | |||
412 | [VideoState.TO_TRANSCODE]: 'To transcode', | 415 | [VideoState.TO_TRANSCODE]: 'To transcode', |
413 | [VideoState.TO_IMPORT]: 'To import', | 416 | [VideoState.TO_IMPORT]: 'To import', |
414 | [VideoState.WAITING_FOR_LIVE]: 'Waiting for livestream', | 417 | [VideoState.WAITING_FOR_LIVE]: 'Waiting for livestream', |
415 | [VideoState.LIVE_ENDED]: 'Livestream ended' | 418 | [VideoState.LIVE_ENDED]: 'Livestream ended', |
419 | [VideoState.TO_MOVE_TO_EXTERNAL_STORAGE]: 'To move to an external storage' | ||
416 | } | 420 | } |
417 | 421 | ||
418 | const VIDEO_IMPORT_STATES: { [ id in VideoImportState ]: string } = { | 422 | const VIDEO_IMPORT_STATES: { [ id in VideoImportState ]: string } = { |
diff --git a/server/initializers/database.ts b/server/initializers/database.ts index 38e7a76d0..0e690f6ae 100644 --- a/server/initializers/database.ts +++ b/server/initializers/database.ts | |||
@@ -45,6 +45,7 @@ import { VideoTagModel } from '../models/video/video-tag' | |||
45 | import { VideoViewModel } from '../models/video/video-view' | 45 | import { VideoViewModel } from '../models/video/video-view' |
46 | import { CONFIG } from './config' | 46 | import { CONFIG } from './config' |
47 | import { ActorCustomPageModel } from '@server/models/account/actor-custom-page' | 47 | import { ActorCustomPageModel } from '@server/models/account/actor-custom-page' |
48 | import { VideoJobInfoModel } from '@server/models/video/video-job-info' | ||
48 | 49 | ||
49 | require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string | 50 | require('pg').defaults.parseInt8 = true // Avoid BIGINT to be converted to string |
50 | 51 | ||
@@ -143,7 +144,8 @@ async function initDatabaseModels (silent: boolean) { | |||
143 | TrackerModel, | 144 | TrackerModel, |
144 | VideoTrackerModel, | 145 | VideoTrackerModel, |
145 | PluginModel, | 146 | PluginModel, |
146 | ActorCustomPageModel | 147 | ActorCustomPageModel, |
148 | VideoJobInfoModel | ||
147 | ]) | 149 | ]) |
148 | 150 | ||
149 | // Check extensions exist in the database | 151 | // Check extensions exist in the database |
diff --git a/server/initializers/migrations/0065-video-file-size.ts b/server/initializers/migrations/0065-video-file-size.ts index 1aeb27f2d..ac952a98c 100644 --- a/server/initializers/migrations/0065-video-file-size.ts +++ b/server/initializers/migrations/0065-video-file-size.ts | |||
@@ -1,7 +1,4 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { stat } from 'fs-extra' | ||
3 | import { VideoModel } from '../../models/video/video' | ||
4 | import { getVideoFilePath } from '@server/lib/video-paths' | ||
5 | 2 | ||
6 | function up (utils: { | 3 | function up (utils: { |
7 | transaction: Sequelize.Transaction | 4 | transaction: Sequelize.Transaction |
@@ -9,30 +6,7 @@ function up (utils: { | |||
9 | sequelize: Sequelize.Sequelize | 6 | sequelize: Sequelize.Sequelize |
10 | db: any | 7 | db: any |
11 | }): Promise<void> { | 8 | }): Promise<void> { |
12 | return utils.db.Video.listOwnedAndPopulateAuthorAndTags() | 9 | throw new Error('Removed, please upgrade from a previous version first.') |
13 | .then((videos: VideoModel[]) => { | ||
14 | const tasks: Promise<any>[] = [] | ||
15 | |||
16 | videos.forEach(video => { | ||
17 | video.VideoFiles.forEach(videoFile => { | ||
18 | const p = new Promise((res, rej) => { | ||
19 | stat(getVideoFilePath(video, videoFile), (err, stats) => { | ||
20 | if (err) return rej(err) | ||
21 | |||
22 | videoFile.size = stats.size | ||
23 | videoFile.save().then(res).catch(rej) | ||
24 | }) | ||
25 | }) | ||
26 | |||
27 | tasks.push(p) | ||
28 | }) | ||
29 | }) | ||
30 | |||
31 | return tasks | ||
32 | }) | ||
33 | .then((tasks: Promise<any>[]) => { | ||
34 | return Promise.all(tasks) | ||
35 | }) | ||
36 | } | 10 | } |
37 | 11 | ||
38 | function down (options) { | 12 | function down (options) { |
diff --git a/server/initializers/migrations/0660-object-storage.ts b/server/initializers/migrations/0660-object-storage.ts new file mode 100644 index 000000000..c815c71c6 --- /dev/null +++ b/server/initializers/migrations/0660-object-storage.ts | |||
@@ -0,0 +1,58 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import { VideoStorage } from '@shared/models' | ||
3 | |||
4 | async function up (utils: { | ||
5 | transaction: Sequelize.Transaction | ||
6 | queryInterface: Sequelize.QueryInterface | ||
7 | sequelize: Sequelize.Sequelize | ||
8 | db: any | ||
9 | }): Promise<void> { | ||
10 | { | ||
11 | const query = ` | ||
12 | CREATE TABLE IF NOT EXISTS "videoJobInfo" ( | ||
13 | "id" serial, | ||
14 | "pendingMove" INTEGER NOT NULL, | ||
15 | "pendingTranscode" INTEGER NOT NULL, | ||
16 | "videoId" serial UNIQUE NOT NULL REFERENCES "video" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | ||
17 | "createdAt" timestamp WITH time zone NOT NULL, | ||
18 | "updatedAt" timestamp WITH time zone NOT NULL, | ||
19 | PRIMARY KEY ("id") | ||
20 | ); | ||
21 | ` | ||
22 | |||
23 | await utils.sequelize.query(query) | ||
24 | } | ||
25 | |||
26 | { | ||
27 | await utils.queryInterface.addColumn('videoFile', 'storage', { type: Sequelize.INTEGER, allowNull: true }) | ||
28 | } | ||
29 | { | ||
30 | await utils.sequelize.query( | ||
31 | `UPDATE "videoFile" SET "storage" = ${VideoStorage.FILE_SYSTEM}` | ||
32 | ) | ||
33 | } | ||
34 | { | ||
35 | await utils.queryInterface.changeColumn('videoFile', 'storage', { type: Sequelize.INTEGER, allowNull: false }) | ||
36 | } | ||
37 | |||
38 | { | ||
39 | await utils.queryInterface.addColumn('videoStreamingPlaylist', 'storage', { type: Sequelize.INTEGER, allowNull: true }) | ||
40 | } | ||
41 | { | ||
42 | await utils.sequelize.query( | ||
43 | `UPDATE "videoStreamingPlaylist" SET "storage" = ${VideoStorage.FILE_SYSTEM}` | ||
44 | ) | ||
45 | } | ||
46 | { | ||
47 | await utils.queryInterface.changeColumn('videoStreamingPlaylist', 'storage', { type: Sequelize.INTEGER, allowNull: false }) | ||
48 | } | ||
49 | } | ||
50 | |||
51 | function down (options) { | ||
52 | throw new Error('Not implemented.') | ||
53 | } | ||
54 | |||
55 | export { | ||
56 | up, | ||
57 | down | ||
58 | } | ||