aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/initializers
diff options
context:
space:
mode:
Diffstat (limited to 'server/initializers')
-rw-r--r--server/initializers/checker-after-init.ts188
-rw-r--r--server/initializers/checker-before-init.ts11
-rw-r--r--server/initializers/config.ts7
-rw-r--r--server/initializers/constants.ts60
-rw-r--r--server/initializers/installer.ts12
-rw-r--r--server/initializers/migrations/0075-video-resolutions.ts8
-rw-r--r--server/initializers/migrations/0685-multiple-actor-images.ts62
7 files changed, 235 insertions, 113 deletions
diff --git a/server/initializers/checker-after-init.ts b/server/initializers/checker-after-init.ts
index 57ef0d218..635a32010 100644
--- a/server/initializers/checker-after-init.ts
+++ b/server/initializers/checker-after-init.ts
@@ -1,7 +1,7 @@
1import config from 'config' 1import config from 'config'
2import { uniq } from 'lodash' 2import { uniq } from 'lodash'
3import { URL } from 'url' 3import { URL } from 'url'
4import { getFFmpegVersion } from '@server/helpers/ffmpeg-utils' 4import { getFFmpegVersion } from '@server/helpers/ffmpeg'
5import { VideoRedundancyConfigFilter } from '@shared/models/redundancy/video-redundancy-config-filter.type' 5import { VideoRedundancyConfigFilter } from '@shared/models/redundancy/video-redundancy-config-filter.type'
6import { RecentlyAddedStrategy } from '../../shared/models/redundancy' 6import { RecentlyAddedStrategy } from '../../shared/models/redundancy'
7import { isProdInstance, isTestInstance, parseSemVersion } from '../helpers/core-utils' 7import { isProdInstance, isTestInstance, parseSemVersion } from '../helpers/core-utils'
@@ -31,8 +31,7 @@ async function checkActivityPubUrls () {
31 } 31 }
32} 32}
33 33
34// Some checks on configuration files 34// Some checks on configuration files or throw if there is an error
35// Return an error message, or null if everything is okay
36function checkConfig () { 35function checkConfig () {
37 36
38 // Moved configuration keys 37 // Moved configuration keys
@@ -40,61 +39,124 @@ function checkConfig () {
40 logger.warn('services.csp-logger configuration has been renamed to csp.report_uri. Please update your configuration file.') 39 logger.warn('services.csp-logger configuration has been renamed to csp.report_uri. Please update your configuration file.')
41 } 40 }
42 41
43 // Email verification 42 checkEmailConfig()
43 checkNSFWPolicyConfig()
44 checkLocalRedundancyConfig()
45 checkRemoteRedundancyConfig()
46 checkStorageConfig()
47 checkTranscodingConfig()
48 checkBroadcastMessageConfig()
49 checkSearchConfig()
50 checkLiveConfig()
51 checkObjectStorageConfig()
52 checkVideoEditorConfig()
53}
54
55// We get db by param to not import it in this file (import orders)
56async function clientsExist () {
57 const totalClients = await OAuthClientModel.countTotal()
58
59 return totalClients !== 0
60}
61
62// We get db by param to not import it in this file (import orders)
63async function usersExist () {
64 const totalUsers = await UserModel.countTotal()
65
66 return totalUsers !== 0
67}
68
69// We get db by param to not import it in this file (import orders)
70async function applicationExist () {
71 const totalApplication = await ApplicationModel.countTotal()
72
73 return totalApplication !== 0
74}
75
76async function checkFFmpegVersion () {
77 const version = await getFFmpegVersion()
78 const { major, minor } = parseSemVersion(version)
79
80 if (major < 4 || (major === 4 && minor < 1)) {
81 logger.warn('Your ffmpeg version (%s) is outdated. PeerTube supports ffmpeg >= 4.1. Please upgrade.', version)
82 }
83}
84
85// ---------------------------------------------------------------------------
86
87export {
88 checkConfig,
89 clientsExist,
90 checkFFmpegVersion,
91 usersExist,
92 applicationExist,
93 checkActivityPubUrls
94}
95
96// ---------------------------------------------------------------------------
97
98function checkEmailConfig () {
44 if (!isEmailEnabled()) { 99 if (!isEmailEnabled()) {
45 if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) { 100 if (CONFIG.SIGNUP.ENABLED && CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
46 return 'Emailer is disabled but you require signup email verification.' 101 throw new Error('Emailer is disabled but you require signup email verification.')
47 } 102 }
48 103
49 if (CONFIG.CONTACT_FORM.ENABLED) { 104 if (CONFIG.CONTACT_FORM.ENABLED) {
50 logger.warn('Emailer is disabled so the contact form will not work.') 105 logger.warn('Emailer is disabled so the contact form will not work.')
51 } 106 }
52 } 107 }
108}
53 109
54 // NSFW policy 110function checkNSFWPolicyConfig () {
55 const defaultNSFWPolicy = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY 111 const defaultNSFWPolicy = CONFIG.INSTANCE.DEFAULT_NSFW_POLICY
56 { 112
57 const available = [ 'do_not_list', 'blur', 'display' ] 113 const available = [ 'do_not_list', 'blur', 'display' ]
58 if (available.includes(defaultNSFWPolicy) === false) { 114 if (available.includes(defaultNSFWPolicy) === false) {
59 return 'NSFW policy setting should be ' + available.join(' or ') + ' instead of ' + defaultNSFWPolicy 115 throw new Error('NSFW policy setting should be ' + available.join(' or ') + ' instead of ' + defaultNSFWPolicy)
60 }
61 } 116 }
117}
62 118
63 // Redundancies 119function checkLocalRedundancyConfig () {
64 const redundancyVideos = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES 120 const redundancyVideos = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES
121
65 if (isArray(redundancyVideos)) { 122 if (isArray(redundancyVideos)) {
66 const available = [ 'most-views', 'trending', 'recently-added' ] 123 const available = [ 'most-views', 'trending', 'recently-added' ]
124
67 for (const r of redundancyVideos) { 125 for (const r of redundancyVideos) {
68 if (available.includes(r.strategy) === false) { 126 if (available.includes(r.strategy) === false) {
69 return 'Videos redundancy should have ' + available.join(' or ') + ' strategy instead of ' + r.strategy 127 throw new Error('Videos redundancy should have ' + available.join(' or ') + ' strategy instead of ' + r.strategy)
70 } 128 }
71 129
72 // Lifetime should not be < 10 hours 130 // Lifetime should not be < 10 hours
73 if (!isTestInstance() && r.minLifetime < 1000 * 3600 * 10) { 131 if (!isTestInstance() && r.minLifetime < 1000 * 3600 * 10) {
74 return 'Video redundancy minimum lifetime should be >= 10 hours for strategy ' + r.strategy 132 throw new Error('Video redundancy minimum lifetime should be >= 10 hours for strategy ' + r.strategy)
75 } 133 }
76 } 134 }
77 135
78 const filtered = uniq(redundancyVideos.map(r => r.strategy)) 136 const filtered = uniq(redundancyVideos.map(r => r.strategy))
79 if (filtered.length !== redundancyVideos.length) { 137 if (filtered.length !== redundancyVideos.length) {
80 return 'Redundancy video entries should have unique strategies' 138 throw new Error('Redundancy video entries should have unique strategies')
81 } 139 }
82 140
83 const recentlyAddedStrategy = redundancyVideos.find(r => r.strategy === 'recently-added') as RecentlyAddedStrategy 141 const recentlyAddedStrategy = redundancyVideos.find(r => r.strategy === 'recently-added') as RecentlyAddedStrategy
84 if (recentlyAddedStrategy && isNaN(recentlyAddedStrategy.minViews)) { 142 if (recentlyAddedStrategy && isNaN(recentlyAddedStrategy.minViews)) {
85 return 'Min views in recently added strategy is not a number' 143 throw new Error('Min views in recently added strategy is not a number')
86 } 144 }
87 } else { 145 } else {
88 return 'Videos redundancy should be an array (you must uncomment lines containing - too)' 146 throw new Error('Videos redundancy should be an array (you must uncomment lines containing - too)')
89 } 147 }
148}
90 149
91 // Remote redundancies 150function checkRemoteRedundancyConfig () {
92 const acceptFrom = CONFIG.REMOTE_REDUNDANCY.VIDEOS.ACCEPT_FROM 151 const acceptFrom = CONFIG.REMOTE_REDUNDANCY.VIDEOS.ACCEPT_FROM
93 const acceptFromValues = new Set<VideoRedundancyConfigFilter>([ 'nobody', 'anybody', 'followings' ]) 152 const acceptFromValues = new Set<VideoRedundancyConfigFilter>([ 'nobody', 'anybody', 'followings' ])
153
94 if (acceptFromValues.has(acceptFrom) === false) { 154 if (acceptFromValues.has(acceptFrom) === false) {
95 return 'remote_redundancy.videos.accept_from has an incorrect value' 155 throw new Error('remote_redundancy.videos.accept_from has an incorrect value')
96 } 156 }
157}
97 158
159function checkStorageConfig () {
98 // Check storage directory locations 160 // Check storage directory locations
99 if (isProdInstance()) { 161 if (isProdInstance()) {
100 const configStorage = config.get('storage') 162 const configStorage = config.get('storage')
@@ -111,71 +173,76 @@ function checkConfig () {
111 if (CONFIG.STORAGE.VIDEOS_DIR === CONFIG.STORAGE.REDUNDANCY_DIR) { 173 if (CONFIG.STORAGE.VIDEOS_DIR === CONFIG.STORAGE.REDUNDANCY_DIR) {
112 logger.warn('Redundancy directory should be different than the videos folder.') 174 logger.warn('Redundancy directory should be different than the videos folder.')
113 } 175 }
176}
114 177
115 // Transcoding 178function checkTranscodingConfig () {
116 if (CONFIG.TRANSCODING.ENABLED) { 179 if (CONFIG.TRANSCODING.ENABLED) {
117 if (CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false && CONFIG.TRANSCODING.HLS.ENABLED === false) { 180 if (CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false && CONFIG.TRANSCODING.HLS.ENABLED === false) {
118 return 'You need to enable at least WebTorrent transcoding or HLS transcoding.' 181 throw new Error('You need to enable at least WebTorrent transcoding or HLS transcoding.')
119 } 182 }
120 183
121 if (CONFIG.TRANSCODING.CONCURRENCY <= 0) { 184 if (CONFIG.TRANSCODING.CONCURRENCY <= 0) {
122 return 'Transcoding concurrency should be > 0' 185 throw new Error('Transcoding concurrency should be > 0')
123 } 186 }
124 } 187 }
125 188
126 if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED || CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED) { 189 if (CONFIG.IMPORT.VIDEOS.HTTP.ENABLED || CONFIG.IMPORT.VIDEOS.TORRENT.ENABLED) {
127 if (CONFIG.IMPORT.VIDEOS.CONCURRENCY <= 0) { 190 if (CONFIG.IMPORT.VIDEOS.CONCURRENCY <= 0) {
128 return 'Video import concurrency should be > 0' 191 throw new Error('Video import concurrency should be > 0')
129 } 192 }
130 } 193 }
194}
131 195
132 // Broadcast message 196function checkBroadcastMessageConfig () {
133 if (CONFIG.BROADCAST_MESSAGE.ENABLED) { 197 if (CONFIG.BROADCAST_MESSAGE.ENABLED) {
134 const currentLevel = CONFIG.BROADCAST_MESSAGE.LEVEL 198 const currentLevel = CONFIG.BROADCAST_MESSAGE.LEVEL
135 const available = [ 'info', 'warning', 'error' ] 199 const available = [ 'info', 'warning', 'error' ]
136 200
137 if (available.includes(currentLevel) === false) { 201 if (available.includes(currentLevel) === false) {
138 return 'Broadcast message level should be ' + available.join(' or ') + ' instead of ' + currentLevel 202 throw new Error('Broadcast message level should be ' + available.join(' or ') + ' instead of ' + currentLevel)
139 } 203 }
140 } 204 }
205}
141 206
142 // Search index 207function checkSearchConfig () {
143 if (CONFIG.SEARCH.SEARCH_INDEX.ENABLED === true) { 208 if (CONFIG.SEARCH.SEARCH_INDEX.ENABLED === true) {
144 if (CONFIG.SEARCH.REMOTE_URI.USERS === false) { 209 if (CONFIG.SEARCH.REMOTE_URI.USERS === false) {
145 return 'You cannot enable search index without enabling remote URI search for users.' 210 throw new Error('You cannot enable search index without enabling remote URI search for users.')
146 } 211 }
147 } 212 }
213}
148 214
149 // Live 215function checkLiveConfig () {
150 if (CONFIG.LIVE.ENABLED === true) { 216 if (CONFIG.LIVE.ENABLED === true) {
151 if (CONFIG.LIVE.ALLOW_REPLAY === true && CONFIG.TRANSCODING.ENABLED === false) { 217 if (CONFIG.LIVE.ALLOW_REPLAY === true && CONFIG.TRANSCODING.ENABLED === false) {
152 return 'Live allow replay cannot be enabled if transcoding is not enabled.' 218 throw new Error('Live allow replay cannot be enabled if transcoding is not enabled.')
153 } 219 }
154 220
155 if (CONFIG.LIVE.RTMP.ENABLED === false && CONFIG.LIVE.RTMPS.ENABLED === false) { 221 if (CONFIG.LIVE.RTMP.ENABLED === false && CONFIG.LIVE.RTMPS.ENABLED === false) {
156 return 'You must enable at least RTMP or RTMPS' 222 throw new Error('You must enable at least RTMP or RTMPS')
157 } 223 }
158 224
159 if (CONFIG.LIVE.RTMPS.ENABLED) { 225 if (CONFIG.LIVE.RTMPS.ENABLED) {
160 if (!CONFIG.LIVE.RTMPS.KEY_FILE) { 226 if (!CONFIG.LIVE.RTMPS.KEY_FILE) {
161 return 'You must specify a key file to enabled RTMPS' 227 throw new Error('You must specify a key file to enabled RTMPS')
162 } 228 }
163 229
164 if (!CONFIG.LIVE.RTMPS.CERT_FILE) { 230 if (!CONFIG.LIVE.RTMPS.CERT_FILE) {
165 return 'You must specify a cert file to enable RTMPS' 231 throw new Error('You must specify a cert file to enable RTMPS')
166 } 232 }
167 } 233 }
168 } 234 }
235}
169 236
170 // Object storage 237function checkObjectStorageConfig () {
171 if (CONFIG.OBJECT_STORAGE.ENABLED === true) { 238 if (CONFIG.OBJECT_STORAGE.ENABLED === true) {
172 239
173 if (!CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME) { 240 if (!CONFIG.OBJECT_STORAGE.VIDEOS.BUCKET_NAME) {
174 return 'videos_bucket should be set when object storage support is enabled.' 241 throw new Error('videos_bucket should be set when object storage support is enabled.')
175 } 242 }
176 243
177 if (!CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME) { 244 if (!CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.BUCKET_NAME) {
178 return 'streaming_playlists_bucket should be set when object storage support is enabled.' 245 throw new Error('streaming_playlists_bucket should be set when object storage support is enabled.')
179 } 246 }
180 247
181 if ( 248 if (
@@ -183,53 +250,18 @@ function checkConfig () {
183 CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.PREFIX 250 CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === CONFIG.OBJECT_STORAGE.STREAMING_PLAYLISTS.PREFIX
184 ) { 251 ) {
185 if (CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === '') { 252 if (CONFIG.OBJECT_STORAGE.VIDEOS.PREFIX === '') {
186 return 'Object storage bucket prefixes should be set when the same bucket is used for both types of video.' 253 throw new Error('Object storage bucket prefixes should be set when the same bucket is used for both types of video.')
187 } else {
188 return 'Object storage bucket prefixes should be set to different values when the same bucket is used for both types of video.'
189 } 254 }
255
256 throw new Error(
257 'Object storage bucket prefixes should be set to different values when the same bucket is used for both types of video.'
258 )
190 } 259 }
191 } 260 }
192
193 return null
194}
195
196// We get db by param to not import it in this file (import orders)
197async function clientsExist () {
198 const totalClients = await OAuthClientModel.countTotal()
199
200 return totalClients !== 0
201}
202
203// We get db by param to not import it in this file (import orders)
204async function usersExist () {
205 const totalUsers = await UserModel.countTotal()
206
207 return totalUsers !== 0
208} 261}
209 262
210// We get db by param to not import it in this file (import orders) 263function checkVideoEditorConfig () {
211async function applicationExist () { 264 if (CONFIG.VIDEO_EDITOR.ENABLED === true && CONFIG.TRANSCODING.ENABLED === false) {
212 const totalApplication = await ApplicationModel.countTotal() 265 throw new Error('Video editor cannot be enabled if transcoding is disabled')
213
214 return totalApplication !== 0
215}
216
217async function checkFFmpegVersion () {
218 const version = await getFFmpegVersion()
219 const { major, minor } = parseSemVersion(version)
220
221 if (major < 4 || (major === 4 && minor < 1)) {
222 logger.warn('Your ffmpeg version (%s) is outdated. PeerTube supports ffmpeg >= 4.1. Please upgrade.', version)
223 } 266 }
224} 267}
225
226// ---------------------------------------------------------------------------
227
228export {
229 checkConfig,
230 clientsExist,
231 checkFFmpegVersion,
232 usersExist,
233 applicationExist,
234 checkActivityPubUrls
235}
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts
index 458005b98..36401f95c 100644
--- a/server/initializers/checker-before-init.ts
+++ b/server/initializers/checker-before-init.ts
@@ -30,7 +30,7 @@ function checkMissedConfig () {
30 'transcoding.profile', 'transcoding.concurrency', 30 'transcoding.profile', 'transcoding.concurrency',
31 'transcoding.resolutions.0p', 'transcoding.resolutions.144p', 'transcoding.resolutions.240p', 'transcoding.resolutions.360p', 31 'transcoding.resolutions.0p', 'transcoding.resolutions.144p', 'transcoding.resolutions.240p', 'transcoding.resolutions.360p',
32 'transcoding.resolutions.480p', 'transcoding.resolutions.720p', 'transcoding.resolutions.1080p', 'transcoding.resolutions.1440p', 32 'transcoding.resolutions.480p', 'transcoding.resolutions.720p', 'transcoding.resolutions.1080p', 'transcoding.resolutions.1440p',
33 'transcoding.resolutions.2160p', 33 'transcoding.resolutions.2160p', 'video_editor.enabled',
34 'import.videos.http.enabled', 'import.videos.torrent.enabled', 'import.videos.concurrency', 'auto_blacklist.videos.of_users.enabled', 34 'import.videos.http.enabled', 'import.videos.torrent.enabled', 'import.videos.concurrency', 'auto_blacklist.videos.of_users.enabled',
35 'trending.videos.interval_days', 35 'trending.videos.interval_days',
36 'client.videos.miniature.prefer_author_display_name', 'client.menu.login.redirect_on_single_external_auth', 36 'client.videos.miniature.prefer_author_display_name', 'client.menu.login.redirect_on_single_external_auth',
@@ -49,7 +49,8 @@ function checkMissedConfig () {
49 'search.remote_uri.users', 'search.remote_uri.anonymous', 'search.search_index.enabled', 'search.search_index.url', 49 'search.remote_uri.users', 'search.remote_uri.anonymous', 'search.search_index.enabled', 'search.search_index.url',
50 'search.search_index.disable_local_search', 'search.search_index.is_default_search', 50 'search.search_index.disable_local_search', 'search.search_index.is_default_search',
51 'live.enabled', 'live.allow_replay', 'live.max_duration', 'live.max_user_lives', 'live.max_instance_lives', 51 'live.enabled', 'live.allow_replay', 'live.max_duration', 'live.max_user_lives', 'live.max_instance_lives',
52 'live.rtmp.enabled', 'live.rtmp.port', 'live.rtmps.enabled', 'live.rtmps.port', 'live.rtmps.key_file', 'live.rtmps.cert_file', 52 'live.rtmp.enabled', 'live.rtmp.port', 'live.rtmp.hostname',
53 'live.rtmps.enabled', 'live.rtmps.port', 'live.rtmps.hostname', 'live.rtmps.key_file', 'live.rtmps.cert_file',
53 'live.transcoding.enabled', 'live.transcoding.threads', 'live.transcoding.profile', 54 'live.transcoding.enabled', 'live.transcoding.threads', 'live.transcoding.profile',
54 'live.transcoding.resolutions.144p', 'live.transcoding.resolutions.240p', 'live.transcoding.resolutions.360p', 55 'live.transcoding.resolutions.144p', 'live.transcoding.resolutions.240p', 'live.transcoding.resolutions.360p',
55 'live.transcoding.resolutions.480p', 'live.transcoding.resolutions.720p', 'live.transcoding.resolutions.1080p', 56 'live.transcoding.resolutions.480p', 'live.transcoding.resolutions.720p', 'live.transcoding.resolutions.1080p',
@@ -116,12 +117,8 @@ function checkNodeVersion () {
116 117
117 logger.debug('Checking NodeJS version %s.', v) 118 logger.debug('Checking NodeJS version %s.', v)
118 119
119 if (major <= 10) {
120 throw new Error('Your NodeJS version ' + v + ' is not supported. Please upgrade.')
121 }
122
123 if (major <= 12) { 120 if (major <= 12) {
124 logger.warn('Your NodeJS version ' + v + ' is deprecated. Please upgrade.') 121 throw new Error('Your NodeJS version ' + v + ' is not supported. Please upgrade.')
125 } 122 }
126} 123}
127 124
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index fb6f7ae62..63056b41d 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -297,12 +297,14 @@ const CONFIG = {
297 297
298 RTMP: { 298 RTMP: {
299 get ENABLED () { return config.get<boolean>('live.rtmp.enabled') }, 299 get ENABLED () { return config.get<boolean>('live.rtmp.enabled') },
300 get PORT () { return config.get<number>('live.rtmp.port') } 300 get PORT () { return config.get<number>('live.rtmp.port') },
301 get HOSTNAME () { return config.get<number>('live.rtmp.hostname') }
301 }, 302 },
302 303
303 RTMPS: { 304 RTMPS: {
304 get ENABLED () { return config.get<boolean>('live.rtmps.enabled') }, 305 get ENABLED () { return config.get<boolean>('live.rtmps.enabled') },
305 get PORT () { return config.get<number>('live.rtmps.port') }, 306 get PORT () { return config.get<number>('live.rtmps.port') },
307 get HOSTNAME () { return config.get<number>('live.rtmps.hostname') },
306 get KEY_FILE () { return config.get<string>('live.rtmps.key_file') }, 308 get KEY_FILE () { return config.get<string>('live.rtmps.key_file') },
307 get CERT_FILE () { return config.get<string>('live.rtmps.cert_file') } 309 get CERT_FILE () { return config.get<string>('live.rtmps.cert_file') }
308 }, 310 },
@@ -324,6 +326,9 @@ const CONFIG = {
324 } 326 }
325 } 327 }
326 }, 328 },
329 VIDEO_EDITOR: {
330 get ENABLED () { return config.get<boolean>('video_editor.enabled') }
331 },
327 IMPORT: { 332 IMPORT: {
328 VIDEOS: { 333 VIDEOS: {
329 get CONCURRENCY () { return config.get<number>('import.videos.concurrency') }, 334 get CONCURRENCY () { return config.get<number>('import.videos.concurrency') },
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 1c47d43f0..3069e2353 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -14,7 +14,7 @@ import {
14 VideoTranscodingFPS 14 VideoTranscodingFPS
15} from '../../shared/models' 15} from '../../shared/models'
16import { ActivityPubActorType } from '../../shared/models/activitypub' 16import { ActivityPubActorType } from '../../shared/models/activitypub'
17import { FollowState } from '../../shared/models/actors' 17import { ActorImageType, FollowState } from '../../shared/models/actors'
18import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type' 18import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
19import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model' 19import { VideoPlaylistPrivacy } from '../../shared/models/videos/playlist/video-playlist-privacy.model'
20import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model' 20import { VideoPlaylistType } from '../../shared/models/videos/playlist/video-playlist-type.model'
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config'
24 24
25// --------------------------------------------------------------------------- 25// ---------------------------------------------------------------------------
26 26
27const LAST_MIGRATION_VERSION = 680 27const LAST_MIGRATION_VERSION = 685
28 28
29// --------------------------------------------------------------------------- 29// ---------------------------------------------------------------------------
30 30
@@ -152,6 +152,7 @@ const JOB_ATTEMPTS: { [id in JobType]: number } = {
152 'activitypub-refresher': 1, 152 'activitypub-refresher': 1,
153 'video-redundancy': 1, 153 'video-redundancy': 1,
154 'video-live-ending': 1, 154 'video-live-ending': 1,
155 'video-edition': 1,
155 'move-to-object-storage': 3 156 'move-to-object-storage': 3
156} 157}
157// Excluded keys are jobs that can be configured by admins 158// Excluded keys are jobs that can be configured by admins
@@ -168,6 +169,7 @@ const JOB_CONCURRENCY: { [id in Exclude<JobType, 'video-transcoding' | 'video-im
168 'activitypub-refresher': 1, 169 'activitypub-refresher': 1,
169 'video-redundancy': 1, 170 'video-redundancy': 1,
170 'video-live-ending': 10, 171 'video-live-ending': 10,
172 'video-edition': 1,
171 'move-to-object-storage': 1 173 'move-to-object-storage': 1
172} 174}
173const JOB_TTL: { [id in JobType]: number } = { 175const JOB_TTL: { [id in JobType]: number } = {
@@ -178,6 +180,7 @@ const JOB_TTL: { [id in JobType]: number } = {
178 'activitypub-cleaner': 1000 * 3600, // 1 hour 180 'activitypub-cleaner': 1000 * 3600, // 1 hour
179 'video-file-import': 1000 * 3600, // 1 hour 181 'video-file-import': 1000 * 3600, // 1 hour
180 'video-transcoding': 1000 * 3600 * 48, // 2 days, transcoding could be long 182 'video-transcoding': 1000 * 3600 * 48, // 2 days, transcoding could be long
183 'video-edition': 1000 * 3600 * 10, // 10 hours
181 'video-import': 1000 * 3600 * 2, // 2 hours 184 'video-import': 1000 * 3600 * 2, // 2 hours
182 'email': 60000 * 10, // 10 minutes 185 'email': 60000 * 10, // 10 minutes
183 'actor-keys': 60000 * 20, // 20 minutes 186 'actor-keys': 60000 * 20, // 20 minutes
@@ -351,6 +354,10 @@ const CONSTRAINTS_FIELDS = {
351 }, 354 },
352 COMMONS: { 355 COMMONS: {
353 URL: { min: 5, max: 2000 } // Length 356 URL: { min: 5, max: 2000 } // Length
357 },
358 VIDEO_EDITOR: {
359 TASKS: { min: 1, max: 10 }, // Number of tasks
360 CUT_TIME: { min: 0 } // Value
354 } 361 }
355} 362}
356 363
@@ -365,6 +372,7 @@ const VIDEO_TRANSCODING_FPS: VideoTranscodingFPS = {
365 MIN: 1, 372 MIN: 1,
366 STANDARD: [ 24, 25, 30 ], 373 STANDARD: [ 24, 25, 30 ],
367 HD_STANDARD: [ 50, 60 ], 374 HD_STANDARD: [ 50, 60 ],
375 AUDIO_MERGE: 25,
368 AVERAGE: 30, 376 AVERAGE: 30,
369 MAX: 60, 377 MAX: 60,
370 KEEP_ORIGIN_FPS_RESOLUTION_MIN: 720 // We keep the original FPS on high resolutions (720 minimum) 378 KEEP_ORIGIN_FPS_RESOLUTION_MIN: 720 // We keep the original FPS on high resolutions (720 minimum)
@@ -434,7 +442,8 @@ const VIDEO_STATES: { [ id in VideoState ]: string } = {
434 [VideoState.LIVE_ENDED]: 'Livestream ended', 442 [VideoState.LIVE_ENDED]: 'Livestream ended',
435 [VideoState.TO_MOVE_TO_EXTERNAL_STORAGE]: 'To move to an external storage', 443 [VideoState.TO_MOVE_TO_EXTERNAL_STORAGE]: 'To move to an external storage',
436 [VideoState.TRANSCODING_FAILED]: 'Transcoding failed', 444 [VideoState.TRANSCODING_FAILED]: 'Transcoding failed',
437 [VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED]: 'External storage move failed' 445 [VideoState.TO_MOVE_TO_EXTERNAL_STORAGE_FAILED]: 'External storage move failed',
446 [VideoState.TO_EDIT]: 'To edit*'
438} 447}
439 448
440const VIDEO_IMPORT_STATES: { [ id in VideoImportState ]: string } = { 449const VIDEO_IMPORT_STATES: { [ id in VideoImportState ]: string } = {
@@ -633,15 +642,23 @@ const PREVIEWS_SIZE = {
633 height: 480, 642 height: 480,
634 minWidth: 400 643 minWidth: 400
635} 644}
636const ACTOR_IMAGES_SIZE = { 645const ACTOR_IMAGES_SIZE: { [key in ActorImageType]: { width: number, height: number }[]} = {
637 AVATARS: { 646 [ActorImageType.AVATAR]: [
638 width: 120, 647 {
639 height: 120 648 width: 120,
640 }, 649 height: 120
641 BANNERS: { 650 },
642 width: 1920, 651 {
643 height: 317 // 6/1 ratio 652 width: 48,
644 } 653 height: 48
654 }
655 ],
656 [ActorImageType.BANNER]: [
657 {
658 width: 1920,
659 height: 317 // 6/1 ratio
660 }
661 ]
645} 662}
646 663
647const EMBED_SIZE = { 664const EMBED_SIZE = {
@@ -701,10 +718,12 @@ const MEMOIZE_TTL = {
701 OVERVIEWS_SAMPLE: 1000 * 3600 * 4, // 4 hours 718 OVERVIEWS_SAMPLE: 1000 * 3600 * 4, // 4 hours
702 INFO_HASH_EXISTS: 1000 * 3600 * 12, // 12 hours 719 INFO_HASH_EXISTS: 1000 * 3600 * 12, // 12 hours
703 LIVE_ABLE_TO_UPLOAD: 1000 * 60, // 1 minute 720 LIVE_ABLE_TO_UPLOAD: 1000 * 60, // 1 minute
704 LIVE_CHECK_SOCKET_HEALTH: 1000 * 60 // 1 minute 721 LIVE_CHECK_SOCKET_HEALTH: 1000 * 60, // 1 minute
722 MD_TO_PLAIN_TEXT_CLIENT_HTML: 1000 * 60 // 1 minute
705} 723}
706 724
707const MEMOIZE_LENGTH = { 725const MEMOIZE_LENGTH = {
726 MD_TO_PLAIN_TEXT_CLIENT_HTML: 100,
708 INFO_HASH_EXISTS: 200 727 INFO_HASH_EXISTS: 200
709} 728}
710 729
@@ -847,6 +866,16 @@ const FILES_CONTENT_HASH = {
847 866
848// --------------------------------------------------------------------------- 867// ---------------------------------------------------------------------------
849 868
869const VIDEO_FILTERS = {
870 WATERMARK: {
871 SIZE_RATIO: 1 / 10,
872 HORIZONTAL_MARGIN_RATIO: 1 / 20,
873 VERTICAL_MARGIN_RATIO: 1 / 20
874 }
875}
876
877// ---------------------------------------------------------------------------
878
850export { 879export {
851 WEBSERVER, 880 WEBSERVER,
852 API_VERSION, 881 API_VERSION,
@@ -885,6 +914,7 @@ export {
885 PLUGIN_GLOBAL_CSS_FILE_NAME, 914 PLUGIN_GLOBAL_CSS_FILE_NAME,
886 PLUGIN_GLOBAL_CSS_PATH, 915 PLUGIN_GLOBAL_CSS_PATH,
887 PRIVATE_RSA_KEY_SIZE, 916 PRIVATE_RSA_KEY_SIZE,
917 VIDEO_FILTERS,
888 ROUTE_CACHE_LIFETIME, 918 ROUTE_CACHE_LIFETIME,
889 SORTABLE_COLUMNS, 919 SORTABLE_COLUMNS,
890 HLS_STREAMING_PLAYLIST_DIRECTORY, 920 HLS_STREAMING_PLAYLIST_DIRECTORY,
@@ -1016,8 +1046,8 @@ function updateWebserverUrls () {
1016 WEBSERVER.HOSTNAME = CONFIG.WEBSERVER.HOSTNAME 1046 WEBSERVER.HOSTNAME = CONFIG.WEBSERVER.HOSTNAME
1017 WEBSERVER.PORT = CONFIG.WEBSERVER.PORT 1047 WEBSERVER.PORT = CONFIG.WEBSERVER.PORT
1018 1048
1019 WEBSERVER.RTMP_URL = 'rtmp://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.LIVE.RTMP.PORT + '/' + VIDEO_LIVE.RTMP.BASE_PATH 1049 WEBSERVER.RTMP_URL = 'rtmp://' + CONFIG.LIVE.RTMP.HOSTNAME + ':' + CONFIG.LIVE.RTMP.PORT + '/' + VIDEO_LIVE.RTMP.BASE_PATH
1020 WEBSERVER.RTMPS_URL = 'rtmps://' + CONFIG.WEBSERVER.HOSTNAME + ':' + CONFIG.LIVE.RTMPS.PORT + '/' + VIDEO_LIVE.RTMP.BASE_PATH 1050 WEBSERVER.RTMPS_URL = 'rtmps://' + CONFIG.LIVE.RTMPS.HOSTNAME + ':' + CONFIG.LIVE.RTMPS.PORT + '/' + VIDEO_LIVE.RTMP.BASE_PATH
1021} 1051}
1022 1052
1023function updateWebserverConfig () { 1053function updateWebserverConfig () {
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts
index 7e321fb76..0517e0084 100644
--- a/server/initializers/installer.ts
+++ b/server/initializers/installer.ts
@@ -2,10 +2,9 @@ import { ensureDir, remove } from 'fs-extra'
2import passwordGenerator from 'password-generator' 2import passwordGenerator from 'password-generator'
3import { UserRole } from '@shared/models' 3import { UserRole } from '@shared/models'
4import { logger } from '../helpers/logger' 4import { logger } from '../helpers/logger'
5import { createApplicationActor, createUserAccountAndChannelAndPlaylist } from '../lib/user' 5import { buildUser, createApplicationActor, createUserAccountAndChannelAndPlaylist } from '../lib/user'
6import { ApplicationModel } from '../models/application/application' 6import { ApplicationModel } from '../models/application/application'
7import { OAuthClientModel } from '../models/oauth/oauth-client' 7import { OAuthClientModel } from '../models/oauth/oauth-client'
8import { UserModel } from '../models/user/user'
9import { applicationExist, clientsExist, usersExist } from './checker-after-init' 8import { applicationExist, clientsExist, usersExist } from './checker-after-init'
10import { CONFIG } from './config' 9import { CONFIG } from './config'
11import { FILES_CACHE, HLS_STREAMING_PLAYLIST_DIRECTORY, LAST_MIGRATION_VERSION, RESUMABLE_UPLOAD_DIRECTORY } from './constants' 10import { FILES_CACHE, HLS_STREAMING_PLAYLIST_DIRECTORY, LAST_MIGRATION_VERSION, RESUMABLE_UPLOAD_DIRECTORY } from './constants'
@@ -137,18 +136,15 @@ async function createOAuthAdminIfNotExist () {
137 password = passwordGenerator(16, true) 136 password = passwordGenerator(16, true)
138 } 137 }
139 138
140 const userData = { 139 const user = buildUser({
141 username, 140 username,
142 email, 141 email,
143 password, 142 password,
144 role, 143 role,
145 verified: true, 144 emailVerified: true,
146 nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
147 p2pEnabled: CONFIG.DEFAULTS.P2P.WEBAPP.ENABLED,
148 videoQuota: -1, 145 videoQuota: -1,
149 videoQuotaDaily: -1 146 videoQuotaDaily: -1
150 } 147 })
151 const user = new UserModel(userData)
152 148
153 await createUserAccountAndChannelAndPlaylist({ userToCreate: user, channelNames: undefined, validateUser: validatePassword }) 149 await createUserAccountAndChannelAndPlaylist({ userToCreate: user, channelNames: undefined, validateUser: validatePassword })
154 logger.info('Username: ' + username) 150 logger.info('Username: ' + username)
diff --git a/server/initializers/migrations/0075-video-resolutions.ts b/server/initializers/migrations/0075-video-resolutions.ts
index 6e8e47acb..8cd47496e 100644
--- a/server/initializers/migrations/0075-video-resolutions.ts
+++ b/server/initializers/migrations/0075-video-resolutions.ts
@@ -1,8 +1,8 @@
1import * as Sequelize from 'sequelize' 1import { readdir, rename } from 'fs-extra'
2import { join } from 'path' 2import { join } from 'path'
3import * as Sequelize from 'sequelize'
4import { getVideoStreamDimensionsInfo } from '../../helpers/ffmpeg/ffprobe-utils'
3import { CONFIG } from '../../initializers/config' 5import { CONFIG } from '../../initializers/config'
4import { getVideoFileResolution } from '../../helpers/ffprobe-utils'
5import { readdir, rename } from 'fs-extra'
6 6
7function up (utils: { 7function up (utils: {
8 transaction: Sequelize.Transaction 8 transaction: Sequelize.Transaction
@@ -26,7 +26,7 @@ function up (utils: {
26 const uuid = matches[1] 26 const uuid = matches[1]
27 const ext = matches[2] 27 const ext = matches[2]
28 28
29 const p = getVideoFileResolution(join(videoFileDir, videoFile)) 29 const p = getVideoStreamDimensionsInfo(join(videoFileDir, videoFile))
30 .then(async ({ resolution }) => { 30 .then(async ({ resolution }) => {
31 const oldTorrentName = uuid + '.torrent' 31 const oldTorrentName = uuid + '.torrent'
32 const newTorrentName = uuid + '-' + resolution + '.torrent' 32 const newTorrentName = uuid + '-' + resolution + '.torrent'
diff --git a/server/initializers/migrations/0685-multiple-actor-images.ts b/server/initializers/migrations/0685-multiple-actor-images.ts
new file mode 100644
index 000000000..c656f7e28
--- /dev/null
+++ b/server/initializers/migrations/0685-multiple-actor-images.ts
@@ -0,0 +1,62 @@
1import * as Sequelize from 'sequelize'
2
3async function up (utils: {
4 transaction: Sequelize.Transaction
5 queryInterface: Sequelize.QueryInterface
6 sequelize: Sequelize.Sequelize
7 db: any
8}): Promise<void> {
9 {
10 await utils.queryInterface.addColumn('actorImage', 'actorId', {
11 type: Sequelize.INTEGER,
12 defaultValue: null,
13 allowNull: true,
14 references: {
15 model: 'actor',
16 key: 'id'
17 },
18 onDelete: 'CASCADE'
19 }, { transaction: utils.transaction })
20
21 // Avatars
22 {
23 const query = `UPDATE "actorImage" SET "actorId" = (SELECT "id" FROM "actor" WHERE "actor"."avatarId" = "actorImage"."id") ` +
24 `WHERE "type" = 1`
25 await utils.sequelize.query(query, { type: Sequelize.QueryTypes.UPDATE, transaction: utils.transaction })
26 }
27
28 // Banners
29 {
30 const query = `UPDATE "actorImage" SET "actorId" = (SELECT "id" FROM "actor" WHERE "actor"."bannerId" = "actorImage"."id") ` +
31 `WHERE "type" = 2`
32 await utils.sequelize.query(query, { type: Sequelize.QueryTypes.UPDATE, transaction: utils.transaction })
33 }
34
35 // Remove orphans
36 {
37 const query = `DELETE FROM "actorImage" WHERE id NOT IN (` +
38 `SELECT "bannerId" FROM actor WHERE "bannerId" IS NOT NULL ` +
39 `UNION select "avatarId" FROM actor WHERE "avatarId" IS NOT NULL` +
40 `);`
41
42 await utils.sequelize.query(query, { type: Sequelize.QueryTypes.DELETE, transaction: utils.transaction })
43 }
44
45 await utils.queryInterface.changeColumn('actorImage', 'actorId', {
46 type: Sequelize.INTEGER,
47 allowNull: false
48 }, { transaction: utils.transaction })
49
50 await utils.queryInterface.removeColumn('actor', 'avatarId', { transaction: utils.transaction })
51 await utils.queryInterface.removeColumn('actor', 'bannerId', { transaction: utils.transaction })
52 }
53}
54
55function down () {
56 throw new Error('Not implemented.')
57}
58
59export {
60 up,
61 down
62}