aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/+admin/config/edit-custom-config/edit-configuration.service.ts4
-rw-r--r--config/default.yaml2
-rw-r--r--config/production.yaml.example2
-rw-r--r--config/test.yaml2
-rw-r--r--server/controllers/api/config.ts2
-rw-r--r--server/helpers/ffprobe-utils.ts1
-rw-r--r--server/initializers/checker-before-init.ts11
-rw-r--r--server/initializers/config.ts2
-rw-r--r--server/middlewares/validators/config.ts2
-rw-r--r--server/tests/api/check-params/config.ts2
-rw-r--r--server/tests/api/live/live-constraints.ts2
-rw-r--r--server/tests/api/live/live.ts1
-rw-r--r--server/tests/api/object-storage/live.ts2
-rw-r--r--server/tests/api/object-storage/video-imports.ts4
-rw-r--r--server/tests/api/server/config.ts6
-rw-r--r--server/tests/api/server/stats.ts1
-rw-r--r--server/tests/api/videos/audio-only.ts1
-rw-r--r--server/tests/api/videos/video-hls.ts1
-rw-r--r--server/tests/api/videos/video-imports.ts1
-rw-r--r--server/tests/api/videos/video-transcoder.ts29
-rw-r--r--server/tests/cli/create-transcoding-job.ts4
-rw-r--r--server/tests/helpers/core-utils.ts2
-rw-r--r--shared/core-utils/videos/bitrate.ts3
-rw-r--r--shared/extra-utils/server/config-command.ts3
-rw-r--r--shared/models/server/custom-config.model.ts1
-rw-r--r--shared/models/videos/video-resolution.enum.ts1
-rw-r--r--support/doc/api/openapi.yaml2
-rw-r--r--support/docker/production/config/custom-environment-variables.yaml3
28 files changed, 74 insertions, 23 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-configuration.service.ts b/client/src/app/+admin/config/edit-custom-config/edit-configuration.service.ts
index 63e0343fa..9b55cb43c 100644
--- a/client/src/app/+admin/config/edit-custom-config/edit-configuration.service.ts
+++ b/client/src/app/+admin/config/edit-custom-config/edit-configuration.service.ts
@@ -18,6 +18,10 @@ export class EditConfigurationService {
18 description: $localize`A <code>.mp4</code> that keeps the original audio track, with no video` 18 description: $localize`A <code>.mp4</code> that keeps the original audio track, with no video`
19 }, 19 },
20 { 20 {
21 id: '144p',
22 label: $localize`144p`
23 },
24 {
21 id: '240p', 25 id: '240p',
22 label: $localize`240p` 26 label: $localize`240p`
23 }, 27 },
diff --git a/config/default.yaml b/config/default.yaml
index cf9d69a62..c70e6a205 100644
--- a/config/default.yaml
+++ b/config/default.yaml
@@ -323,6 +323,7 @@ transcoding:
323 323
324 resolutions: # Only created if the original video has a higher resolution, uses more storage! 324 resolutions: # Only created if the original video has a higher resolution, uses more storage!
325 0p: false # audio-only (creates mp4 without video stream, always created when enabled) 325 0p: false # audio-only (creates mp4 without video stream, always created when enabled)
326 144p: false
326 240p: false 327 240p: false
327 360p: false 328 360p: false
328 480p: false 329 480p: false
@@ -382,6 +383,7 @@ live:
382 profile: 'default' 383 profile: 'default'
383 384
384 resolutions: 385 resolutions:
386 144p: false
385 240p: false 387 240p: false
386 360p: false 388 360p: false
387 480p: false 389 480p: false
diff --git a/config/production.yaml.example b/config/production.yaml.example
index 70993bf57..0da96f612 100644
--- a/config/production.yaml.example
+++ b/config/production.yaml.example
@@ -333,6 +333,7 @@ transcoding:
333 333
334 resolutions: # Only created if the original video has a higher resolution, uses more storage! 334 resolutions: # Only created if the original video has a higher resolution, uses more storage!
335 0p: false # audio-only (creates mp4 without video stream, always created when enabled) 335 0p: false # audio-only (creates mp4 without video stream, always created when enabled)
336 144p: false
336 240p: false 337 240p: false
337 360p: false 338 360p: false
338 480p: false 339 480p: false
@@ -392,6 +393,7 @@ live:
392 profile: 'default' 393 profile: 'default'
393 394
394 resolutions: 395 resolutions:
396 144p: false
395 240p: false 397 240p: false
396 360p: false 398 360p: false
397 480p: false 399 480p: false
diff --git a/config/test.yaml b/config/test.yaml
index 3eb2f04d8..e9731d863 100644
--- a/config/test.yaml
+++ b/config/test.yaml
@@ -80,6 +80,7 @@ transcoding:
80 concurrency: 2 80 concurrency: 2
81 resolutions: 81 resolutions:
82 0p: false 82 0p: false
83 144p: false
83 240p: true 84 240p: true
84 360p: true 85 360p: true
85 480p: true 86 480p: true
@@ -105,6 +106,7 @@ live:
105 threads: 2 106 threads: 2
106 107
107 resolutions: 108 resolutions:
109 144p: false
108 240p: false 110 240p: false
109 360p: false 111 360p: false
110 480p: false 112 480p: false
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts
index 8965cacc9..805ad99c7 100644
--- a/server/controllers/api/config.ts
+++ b/server/controllers/api/config.ts
@@ -208,6 +208,7 @@ function customConfig (): CustomConfig {
208 profile: CONFIG.TRANSCODING.PROFILE, 208 profile: CONFIG.TRANSCODING.PROFILE,
209 resolutions: { 209 resolutions: {
210 '0p': CONFIG.TRANSCODING.RESOLUTIONS['0p'], 210 '0p': CONFIG.TRANSCODING.RESOLUTIONS['0p'],
211 '144p': CONFIG.TRANSCODING.RESOLUTIONS['144p'],
211 '240p': CONFIG.TRANSCODING.RESOLUTIONS['240p'], 212 '240p': CONFIG.TRANSCODING.RESOLUTIONS['240p'],
212 '360p': CONFIG.TRANSCODING.RESOLUTIONS['360p'], 213 '360p': CONFIG.TRANSCODING.RESOLUTIONS['360p'],
213 '480p': CONFIG.TRANSCODING.RESOLUTIONS['480p'], 214 '480p': CONFIG.TRANSCODING.RESOLUTIONS['480p'],
@@ -234,6 +235,7 @@ function customConfig (): CustomConfig {
234 threads: CONFIG.LIVE.TRANSCODING.THREADS, 235 threads: CONFIG.LIVE.TRANSCODING.THREADS,
235 profile: CONFIG.LIVE.TRANSCODING.PROFILE, 236 profile: CONFIG.LIVE.TRANSCODING.PROFILE,
236 resolutions: { 237 resolutions: {
238 '144p': CONFIG.LIVE.TRANSCODING.RESOLUTIONS['144p'],
237 '240p': CONFIG.LIVE.TRANSCODING.RESOLUTIONS['240p'], 239 '240p': CONFIG.LIVE.TRANSCODING.RESOLUTIONS['240p'],
238 '360p': CONFIG.LIVE.TRANSCODING.RESOLUTIONS['360p'], 240 '360p': CONFIG.LIVE.TRANSCODING.RESOLUTIONS['360p'],
239 '480p': CONFIG.LIVE.TRANSCODING.RESOLUTIONS['480p'], 241 '480p': CONFIG.LIVE.TRANSCODING.RESOLUTIONS['480p'],
diff --git a/server/helpers/ffprobe-utils.ts b/server/helpers/ffprobe-utils.ts
index 767f37f9c..907f13651 100644
--- a/server/helpers/ffprobe-utils.ts
+++ b/server/helpers/ffprobe-utils.ts
@@ -220,6 +220,7 @@ function computeResolutionsToTranscode (videoFileResolution: number, type: 'vod'
220 VideoResolution.H_360P, 220 VideoResolution.H_360P,
221 VideoResolution.H_720P, 221 VideoResolution.H_720P,
222 VideoResolution.H_240P, 222 VideoResolution.H_240P,
223 VideoResolution.H_144P,
223 VideoResolution.H_1080P, 224 VideoResolution.H_1080P,
224 VideoResolution.H_1440P, 225 VideoResolution.H_1440P,
225 VideoResolution.H_4K 226 VideoResolution.H_4K
diff --git a/server/initializers/checker-before-init.ts b/server/initializers/checker-before-init.ts
index 72acdd422..39f0cebf6 100644
--- a/server/initializers/checker-before-init.ts
+++ b/server/initializers/checker-before-init.ts
@@ -28,8 +28,9 @@ function checkMissedConfig () {
28 'redundancy.videos.strategies', 'redundancy.videos.check_interval', 28 'redundancy.videos.strategies', 'redundancy.videos.check_interval',
29 'transcoding.enabled', 'transcoding.threads', 'transcoding.allow_additional_extensions', 'transcoding.hls.enabled', 29 'transcoding.enabled', 'transcoding.threads', 'transcoding.allow_additional_extensions', 'transcoding.hls.enabled',
30 'transcoding.profile', 'transcoding.concurrency', 30 'transcoding.profile', 'transcoding.concurrency',
31 'transcoding.resolutions.0p', 'transcoding.resolutions.240p', 'transcoding.resolutions.360p', 'transcoding.resolutions.480p', 31 'transcoding.resolutions.0p', 'transcoding.resolutions.144p', 'transcoding.resolutions.240p', 'transcoding.resolutions.360p',
32 'transcoding.resolutions.720p', 'transcoding.resolutions.1080p', 'transcoding.resolutions.1440p', 'transcoding.resolutions.2160p', 32 'transcoding.resolutions.480p', 'transcoding.resolutions.720p', 'transcoding.resolutions.1080p', 'transcoding.resolutions.1440p',
33 'transcoding.resolutions.2160p',
33 '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',
34 'trending.videos.interval_days', 35 'trending.videos.interval_days',
35 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route', 36 'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route',
@@ -47,9 +48,9 @@ function checkMissedConfig () {
47 'search.search_index.disable_local_search', 'search.search_index.is_default_search', 48 'search.search_index.disable_local_search', 'search.search_index.is_default_search',
48 'live.enabled', 'live.allow_replay', 'live.max_duration', 'live.max_user_lives', 'live.max_instance_lives', 49 'live.enabled', 'live.allow_replay', 'live.max_duration', 'live.max_user_lives', 'live.max_instance_lives',
49 'live.transcoding.enabled', 'live.transcoding.threads', 'live.transcoding.profile', 50 'live.transcoding.enabled', 'live.transcoding.threads', 'live.transcoding.profile',
50 'live.transcoding.resolutions.240p', 'live.transcoding.resolutions.360p', 'live.transcoding.resolutions.480p', 51 'live.transcoding.resolutions.144p', 'live.transcoding.resolutions.240p', 'live.transcoding.resolutions.360p',
51 'live.transcoding.resolutions.720p', 'live.transcoding.resolutions.1080p', 'live.transcoding.resolutions.1440p', 52 'live.transcoding.resolutions.480p', 'live.transcoding.resolutions.720p', 'live.transcoding.resolutions.1080p',
52 'live.transcoding.resolutions.2160p' 53 'live.transcoding.resolutions.1440p', 'live.transcoding.resolutions.2160p'
53 ] 54 ]
54 55
55 const requiredAlternatives = [ 56 const requiredAlternatives = [
diff --git a/server/initializers/config.ts b/server/initializers/config.ts
index 8375bf430..0d5e29499 100644
--- a/server/initializers/config.ts
+++ b/server/initializers/config.ts
@@ -245,6 +245,7 @@ const CONFIG = {
245 get PROFILE () { return config.get<string>('transcoding.profile') }, 245 get PROFILE () { return config.get<string>('transcoding.profile') },
246 RESOLUTIONS: { 246 RESOLUTIONS: {
247 get '0p' () { return config.get<boolean>('transcoding.resolutions.0p') }, 247 get '0p' () { return config.get<boolean>('transcoding.resolutions.0p') },
248 get '144p' () { return config.get<boolean>('transcoding.resolutions.144p') },
248 get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') }, 249 get '240p' () { return config.get<boolean>('transcoding.resolutions.240p') },
249 get '360p' () { return config.get<boolean>('transcoding.resolutions.360p') }, 250 get '360p' () { return config.get<boolean>('transcoding.resolutions.360p') },
250 get '480p' () { return config.get<boolean>('transcoding.resolutions.480p') }, 251 get '480p' () { return config.get<boolean>('transcoding.resolutions.480p') },
@@ -279,6 +280,7 @@ const CONFIG = {
279 get PROFILE () { return config.get<string>('live.transcoding.profile') }, 280 get PROFILE () { return config.get<string>('live.transcoding.profile') },
280 281
281 RESOLUTIONS: { 282 RESOLUTIONS: {
283 get '144p' () { return config.get<boolean>('live.transcoding.resolutions.144p') },
282 get '240p' () { return config.get<boolean>('live.transcoding.resolutions.240p') }, 284 get '240p' () { return config.get<boolean>('live.transcoding.resolutions.240p') },
283 get '360p' () { return config.get<boolean>('live.transcoding.resolutions.360p') }, 285 get '360p' () { return config.get<boolean>('live.transcoding.resolutions.360p') },
284 get '480p' () { return config.get<boolean>('live.transcoding.resolutions.480p') }, 286 get '480p' () { return config.get<boolean>('live.transcoding.resolutions.480p') },
diff --git a/server/middlewares/validators/config.ts b/server/middlewares/validators/config.ts
index da52b14ba..8b14feb3c 100644
--- a/server/middlewares/validators/config.ts
+++ b/server/middlewares/validators/config.ts
@@ -45,6 +45,7 @@ const customConfigUpdateValidator = [
45 body('transcoding.threads').isInt().withMessage('Should have a valid transcoding threads number'), 45 body('transcoding.threads').isInt().withMessage('Should have a valid transcoding threads number'),
46 body('transcoding.concurrency').isInt({ min: 1 }).withMessage('Should have a valid transcoding concurrency number'), 46 body('transcoding.concurrency').isInt({ min: 1 }).withMessage('Should have a valid transcoding concurrency number'),
47 body('transcoding.resolutions.0p').isBoolean().withMessage('Should have a valid transcoding 0p resolution enabled boolean'), 47 body('transcoding.resolutions.0p').isBoolean().withMessage('Should have a valid transcoding 0p resolution enabled boolean'),
48 body('transcoding.resolutions.144p').isBoolean().withMessage('Should have a valid transcoding 144p resolution enabled boolean'),
48 body('transcoding.resolutions.240p').isBoolean().withMessage('Should have a valid transcoding 240p resolution enabled boolean'), 49 body('transcoding.resolutions.240p').isBoolean().withMessage('Should have a valid transcoding 240p resolution enabled boolean'),
49 body('transcoding.resolutions.360p').isBoolean().withMessage('Should have a valid transcoding 360p resolution enabled boolean'), 50 body('transcoding.resolutions.360p').isBoolean().withMessage('Should have a valid transcoding 360p resolution enabled boolean'),
50 body('transcoding.resolutions.480p').isBoolean().withMessage('Should have a valid transcoding 480p resolution enabled boolean'), 51 body('transcoding.resolutions.480p').isBoolean().withMessage('Should have a valid transcoding 480p resolution enabled boolean'),
@@ -80,6 +81,7 @@ const customConfigUpdateValidator = [
80 body('live.maxUserLives').custom(isIntOrNull).withMessage('Should have a valid max user lives'), 81 body('live.maxUserLives').custom(isIntOrNull).withMessage('Should have a valid max user lives'),
81 body('live.transcoding.enabled').isBoolean().withMessage('Should have a valid live transcoding enabled boolean'), 82 body('live.transcoding.enabled').isBoolean().withMessage('Should have a valid live transcoding enabled boolean'),
82 body('live.transcoding.threads').isInt().withMessage('Should have a valid live transcoding threads'), 83 body('live.transcoding.threads').isInt().withMessage('Should have a valid live transcoding threads'),
84 body('live.transcoding.resolutions.144p').isBoolean().withMessage('Should have a valid transcoding 144p resolution enabled boolean'),
83 body('live.transcoding.resolutions.240p').isBoolean().withMessage('Should have a valid transcoding 240p resolution enabled boolean'), 85 body('live.transcoding.resolutions.240p').isBoolean().withMessage('Should have a valid transcoding 240p resolution enabled boolean'),
84 body('live.transcoding.resolutions.360p').isBoolean().withMessage('Should have a valid transcoding 360p resolution enabled boolean'), 86 body('live.transcoding.resolutions.360p').isBoolean().withMessage('Should have a valid transcoding 360p resolution enabled boolean'),
85 body('live.transcoding.resolutions.480p').isBoolean().withMessage('Should have a valid transcoding 480p resolution enabled boolean'), 87 body('live.transcoding.resolutions.480p').isBoolean().withMessage('Should have a valid transcoding 480p resolution enabled boolean'),
diff --git a/server/tests/api/check-params/config.ts b/server/tests/api/check-params/config.ts
index 273b1f718..d0cd7722b 100644
--- a/server/tests/api/check-params/config.ts
+++ b/server/tests/api/check-params/config.ts
@@ -93,6 +93,7 @@ describe('Test config API validators', function () {
93 profile: 'vod_profile', 93 profile: 'vod_profile',
94 resolutions: { 94 resolutions: {
95 '0p': false, 95 '0p': false,
96 '144p': false,
96 '240p': false, 97 '240p': false,
97 '360p': true, 98 '360p': true,
98 '480p': true, 99 '480p': true,
@@ -121,6 +122,7 @@ describe('Test config API validators', function () {
121 threads: 4, 122 threads: 4,
122 profile: 'live_profile', 123 profile: 'live_profile',
123 resolutions: { 124 resolutions: {
125 '144p': true,
124 '240p': true, 126 '240p': true,
125 '360p': true, 127 '360p': true,
126 '480p': true, 128 '480p': true,
diff --git a/server/tests/api/live/live-constraints.ts b/server/tests/api/live/live-constraints.ts
index 4acde3cc5..6a6a11796 100644
--- a/server/tests/api/live/live-constraints.ts
+++ b/server/tests/api/live/live-constraints.ts
@@ -168,7 +168,7 @@ describe('Test live constraints', function () {
168 await waitUntilLivePublishedOnAllServers(userVideoLiveoId) 168 await waitUntilLivePublishedOnAllServers(userVideoLiveoId)
169 await waitJobs(servers) 169 await waitJobs(servers)
170 170
171 await checkSaveReplay(userVideoLiveoId, [ 720, 480, 360, 240 ]) 171 await checkSaveReplay(userVideoLiveoId, [ 720, 480, 360, 240, 144 ])
172 }) 172 })
173 173
174 after(async function () { 174 after(async function () {
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts
index 0b405dd94..619602d0b 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -421,6 +421,7 @@ describe('Test live', function () {
421 transcoding: { 421 transcoding: {
422 enabled: true, 422 enabled: true,
423 resolutions: { 423 resolutions: {
424 '144p': resolutions.includes(144),
424 '240p': resolutions.includes(240), 425 '240p': resolutions.includes(240),
425 '360p': resolutions.includes(360), 426 '360p': resolutions.includes(360),
426 '480p': resolutions.includes(480), 427 '480p': resolutions.includes(480),
diff --git a/server/tests/api/object-storage/live.ts b/server/tests/api/object-storage/live.ts
index d3e6777f2..3726a717b 100644
--- a/server/tests/api/object-storage/live.ts
+++ b/server/tests/api/object-storage/live.ts
@@ -123,7 +123,7 @@ describe('Object storage for lives', function () {
123 expect(video.streamingPlaylists).to.have.lengthOf(1) 123 expect(video.streamingPlaylists).to.have.lengthOf(1)
124 124
125 const files = video.streamingPlaylists[0].files 125 const files = video.streamingPlaylists[0].files
126 expect(files).to.have.lengthOf(4) 126 expect(files).to.have.lengthOf(5)
127 127
128 await checkFiles(files) 128 await checkFiles(files)
129 } 129 }
diff --git a/server/tests/api/object-storage/video-imports.ts b/server/tests/api/object-storage/video-imports.ts
index efc01f550..363fe3b5b 100644
--- a/server/tests/api/object-storage/video-imports.ts
+++ b/server/tests/api/object-storage/video-imports.ts
@@ -88,9 +88,9 @@ describe('Object storage for video import', function () {
88 88
89 const video = await server.videos.get({ id: uuid }) 89 const video = await server.videos.get({ id: uuid })
90 90
91 expect(video.files).to.have.lengthOf(4) 91 expect(video.files).to.have.lengthOf(5)
92 expect(video.streamingPlaylists).to.have.lengthOf(1) 92 expect(video.streamingPlaylists).to.have.lengthOf(1)
93 expect(video.streamingPlaylists[0].files).to.have.lengthOf(4) 93 expect(video.streamingPlaylists[0].files).to.have.lengthOf(5)
94 94
95 for (const file of video.files) { 95 for (const file of video.files) {
96 expectStartWith(file.fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl()) 96 expectStartWith(file.fileUrl, ObjectStorageCommand.getWebTorrentBaseUrl())
diff --git a/server/tests/api/server/config.ts b/server/tests/api/server/config.ts
index 8d5b3ac7f..ea524723c 100644
--- a/server/tests/api/server/config.ts
+++ b/server/tests/api/server/config.ts
@@ -66,6 +66,7 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
66 expect(data.transcoding.threads).to.equal(2) 66 expect(data.transcoding.threads).to.equal(2)
67 expect(data.transcoding.concurrency).to.equal(2) 67 expect(data.transcoding.concurrency).to.equal(2)
68 expect(data.transcoding.profile).to.equal('default') 68 expect(data.transcoding.profile).to.equal('default')
69 expect(data.transcoding.resolutions['144p']).to.be.false
69 expect(data.transcoding.resolutions['240p']).to.be.true 70 expect(data.transcoding.resolutions['240p']).to.be.true
70 expect(data.transcoding.resolutions['360p']).to.be.true 71 expect(data.transcoding.resolutions['360p']).to.be.true
71 expect(data.transcoding.resolutions['480p']).to.be.true 72 expect(data.transcoding.resolutions['480p']).to.be.true
@@ -84,6 +85,7 @@ function checkInitialConfig (server: PeerTubeServer, data: CustomConfig) {
84 expect(data.live.transcoding.enabled).to.be.false 85 expect(data.live.transcoding.enabled).to.be.false
85 expect(data.live.transcoding.threads).to.equal(2) 86 expect(data.live.transcoding.threads).to.equal(2)
86 expect(data.live.transcoding.profile).to.equal('default') 87 expect(data.live.transcoding.profile).to.equal('default')
88 expect(data.live.transcoding.resolutions['144p']).to.be.false
87 expect(data.live.transcoding.resolutions['240p']).to.be.false 89 expect(data.live.transcoding.resolutions['240p']).to.be.false
88 expect(data.live.transcoding.resolutions['360p']).to.be.false 90 expect(data.live.transcoding.resolutions['360p']).to.be.false
89 expect(data.live.transcoding.resolutions['480p']).to.be.false 91 expect(data.live.transcoding.resolutions['480p']).to.be.false
@@ -163,6 +165,7 @@ function checkUpdatedConfig (data: CustomConfig) {
163 expect(data.transcoding.allowAdditionalExtensions).to.be.true 165 expect(data.transcoding.allowAdditionalExtensions).to.be.true
164 expect(data.transcoding.allowAudioFiles).to.be.true 166 expect(data.transcoding.allowAudioFiles).to.be.true
165 expect(data.transcoding.profile).to.equal('vod_profile') 167 expect(data.transcoding.profile).to.equal('vod_profile')
168 expect(data.transcoding.resolutions['144p']).to.be.false
166 expect(data.transcoding.resolutions['240p']).to.be.false 169 expect(data.transcoding.resolutions['240p']).to.be.false
167 expect(data.transcoding.resolutions['360p']).to.be.true 170 expect(data.transcoding.resolutions['360p']).to.be.true
168 expect(data.transcoding.resolutions['480p']).to.be.true 171 expect(data.transcoding.resolutions['480p']).to.be.true
@@ -180,6 +183,7 @@ function checkUpdatedConfig (data: CustomConfig) {
180 expect(data.live.transcoding.enabled).to.be.true 183 expect(data.live.transcoding.enabled).to.be.true
181 expect(data.live.transcoding.threads).to.equal(4) 184 expect(data.live.transcoding.threads).to.equal(4)
182 expect(data.live.transcoding.profile).to.equal('live_profile') 185 expect(data.live.transcoding.profile).to.equal('live_profile')
186 expect(data.live.transcoding.resolutions['144p']).to.be.true
183 expect(data.live.transcoding.resolutions['240p']).to.be.true 187 expect(data.live.transcoding.resolutions['240p']).to.be.true
184 expect(data.live.transcoding.resolutions['360p']).to.be.true 188 expect(data.live.transcoding.resolutions['360p']).to.be.true
185 expect(data.live.transcoding.resolutions['480p']).to.be.true 189 expect(data.live.transcoding.resolutions['480p']).to.be.true
@@ -281,6 +285,7 @@ const newCustomConfig: CustomConfig = {
281 profile: 'vod_profile', 285 profile: 'vod_profile',
282 resolutions: { 286 resolutions: {
283 '0p': false, 287 '0p': false,
288 '144p': false,
284 '240p': false, 289 '240p': false,
285 '360p': true, 290 '360p': true,
286 '480p': true, 291 '480p': true,
@@ -307,6 +312,7 @@ const newCustomConfig: CustomConfig = {
307 threads: 4, 312 threads: 4,
308 profile: 'live_profile', 313 profile: 'live_profile',
309 resolutions: { 314 resolutions: {
315 '144p': true,
310 '240p': true, 316 '240p': true,
311 '360p': true, 317 '360p': true,
312 '480p': true, 318 '480p': true,
diff --git a/server/tests/api/server/stats.ts b/server/tests/api/server/stats.ts
index 5ec771429..efc80463c 100644
--- a/server/tests/api/server/stats.ts
+++ b/server/tests/api/server/stats.ts
@@ -198,6 +198,7 @@ describe('Test stats (excluding redundancy)', function () {
198 }, 198 },
199 resolutions: { 199 resolutions: {
200 '0p': false, 200 '0p': false,
201 '144p': false,
201 '240p': false, 202 '240p': false,
202 '360p': false, 203 '360p': false,
203 '480p': false, 204 '480p': false,
diff --git a/server/tests/api/videos/audio-only.ts b/server/tests/api/videos/audio-only.ts
index 7fac6e738..f4b635bd5 100644
--- a/server/tests/api/videos/audio-only.ts
+++ b/server/tests/api/videos/audio-only.ts
@@ -21,6 +21,7 @@ describe('Test audio only video transcoding', function () {
21 enabled: true, 21 enabled: true,
22 resolutions: { 22 resolutions: {
23 '0p': true, 23 '0p': true,
24 '144p': false,
24 '240p': true, 25 '240p': true,
25 '360p': false, 26 '360p': false,
26 '480p': false, 27 '480p': false,
diff --git a/server/tests/api/videos/video-hls.ts b/server/tests/api/videos/video-hls.ts
index 91124725f..a18c3d672 100644
--- a/server/tests/api/videos/video-hls.ts
+++ b/server/tests/api/videos/video-hls.ts
@@ -243,6 +243,7 @@ describe('Test HLS videos', function () {
243 enabled: true, 243 enabled: true,
244 allowAudioFiles: true, 244 allowAudioFiles: true,
245 resolutions: { 245 resolutions: {
246 '144p': false,
246 '240p': true, 247 '240p': true,
247 '360p': true, 248 '360p': true,
248 '480p': true, 249 '480p': true,
diff --git a/server/tests/api/videos/video-imports.ts b/server/tests/api/videos/video-imports.ts
index bb1627a27..987f34e97 100644
--- a/server/tests/api/videos/video-imports.ts
+++ b/server/tests/api/videos/video-imports.ts
@@ -300,6 +300,7 @@ describe('Test video imports', function () {
300 transcoding: { 300 transcoding: {
301 enabled: true, 301 enabled: true,
302 resolutions: { 302 resolutions: {
303 '144p': true,
303 '240p': true, 304 '240p': true,
304 '360p': false, 305 '360p': false,
305 '480p': false, 306 '480p': false,
diff --git a/server/tests/api/videos/video-transcoder.ts b/server/tests/api/videos/video-transcoder.ts
index 21609fd82..b3226dbf7 100644
--- a/server/tests/api/videos/video-transcoder.ts
+++ b/server/tests/api/videos/video-transcoder.ts
@@ -40,6 +40,7 @@ function updateConfigForTranscoding (server: PeerTubeServer) {
40 webtorrent: { enabled: true }, 40 webtorrent: { enabled: true },
41 resolutions: { 41 resolutions: {
42 '0p': false, 42 '0p': false,
43 '144p': true,
43 '240p': true, 44 '240p': true,
44 '360p': true, 45 '360p': true,
45 '480p': true, 46 '480p': true,
@@ -119,7 +120,7 @@ describe('Test video transcoding', function () {
119 const video = data.find(v => v.name === attributes.name) 120 const video = data.find(v => v.name === attributes.name)
120 const videoDetails = await server.videos.get({ id: video.id }) 121 const videoDetails = await server.videos.get({ id: video.id })
121 122
122 expect(videoDetails.files).to.have.lengthOf(4) 123 expect(videoDetails.files).to.have.lengthOf(5)
123 124
124 const magnetUri = videoDetails.files[0].magnetUri 125 const magnetUri = videoDetails.files[0].magnetUri
125 expect(magnetUri).to.match(/\.mp4/) 126 expect(magnetUri).to.match(/\.mp4/)
@@ -205,7 +206,7 @@ describe('Test video transcoding', function () {
205 206
206 const video = data.find(v => v.name === attributes.name) 207 const video = data.find(v => v.name === attributes.name)
207 const videoDetails = await server.videos.get({ id: video.id }) 208 const videoDetails = await server.videos.get({ id: video.id })
208 expect(videoDetails.files).to.have.lengthOf(4) 209 expect(videoDetails.files).to.have.lengthOf(5)
209 210
210 const magnetUri = videoDetails.files[0].magnetUri 211 const magnetUri = videoDetails.files[0].magnetUri
211 expect(magnetUri).to.contain('.mp4') 212 expect(magnetUri).to.contain('.mp4')
@@ -226,7 +227,7 @@ describe('Test video transcoding', function () {
226 227
227 await waitJobs(servers) 228 await waitJobs(servers)
228 229
229 const resolutions = [ 240, 360, 480, 720, 1080, 1440, 2160 ] 230 const resolutions = [ 144, 240, 360, 480, 720, 1080, 1440, 2160 ]
230 231
231 for (const server of servers) { 232 for (const server of servers) {
232 const videoDetails = await server.videos.get({ id: video4k }) 233 const videoDetails = await server.videos.get({ id: video4k })
@@ -259,7 +260,7 @@ describe('Test video transcoding', function () {
259 const video = data.find(v => v.name === attributes.name) 260 const video = data.find(v => v.name === attributes.name)
260 const videoDetails = await server.videos.get({ id: video.id }) 261 const videoDetails = await server.videos.get({ id: video.id })
261 262
262 expect(videoDetails.files).to.have.lengthOf(4) 263 expect(videoDetails.files).to.have.lengthOf(5)
263 264
264 const file = videoDetails.files.find(f => f.resolution.id === 240) 265 const file = videoDetails.files.find(f => f.resolution.id === 240)
265 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 266 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
@@ -316,7 +317,7 @@ describe('Test video transcoding', function () {
316 const video = data.find(v => v.name === attributes.name) 317 const video = data.find(v => v.name === attributes.name)
317 const videoDetails = await server.videos.get({ id: video.id }) 318 const videoDetails = await server.videos.get({ id: video.id })
318 319
319 expect(videoDetails.files).to.have.lengthOf(4) 320 expect(videoDetails.files).to.have.lengthOf(5)
320 321
321 const fixturePath = buildAbsoluteFixturePath(attributes.fixture) 322 const fixturePath = buildAbsoluteFixturePath(attributes.fixture)
322 const fixtureVideoProbe = await getAudioStream(fixturePath) 323 const fixtureVideoProbe = await getAudioStream(fixturePath)
@@ -348,6 +349,7 @@ describe('Test video transcoding', function () {
348 webtorrent: { enabled: true }, 349 webtorrent: { enabled: true },
349 resolutions: { 350 resolutions: {
350 '0p': false, 351 '0p': false,
352 '144p': false,
351 '240p': false, 353 '240p': false,
352 '360p': false, 354 '360p': false,
353 '480p': false, 355 '480p': false,
@@ -419,6 +421,7 @@ describe('Test video transcoding', function () {
419 webtorrent: { enabled: true }, 421 webtorrent: { enabled: true },
420 resolutions: { 422 resolutions: {
421 '0p': true, 423 '0p': true,
424 '144p': false,
422 '240p': false, 425 '240p': false,
423 '360p': false 426 '360p': false
424 } 427 }
@@ -473,13 +476,14 @@ describe('Test video transcoding', function () {
473 const video = data.find(v => v.name === attributes.name) 476 const video = data.find(v => v.name === attributes.name)
474 const videoDetails = await server.videos.get({ id: video.id }) 477 const videoDetails = await server.videos.get({ id: video.id })
475 478
476 expect(videoDetails.files).to.have.lengthOf(4) 479 expect(videoDetails.files).to.have.lengthOf(5)
477 expect(videoDetails.files[0].fps).to.be.above(58).and.below(62) 480 expect(videoDetails.files[0].fps).to.be.above(58).and.below(62)
478 expect(videoDetails.files[1].fps).to.be.below(31) 481 expect(videoDetails.files[1].fps).to.be.below(31)
479 expect(videoDetails.files[2].fps).to.be.below(31) 482 expect(videoDetails.files[2].fps).to.be.below(31)
480 expect(videoDetails.files[3].fps).to.be.below(31) 483 expect(videoDetails.files[3].fps).to.be.below(31)
484 expect(videoDetails.files[4].fps).to.be.below(31)
481 485
482 for (const resolution of [ 240, 360, 480 ]) { 486 for (const resolution of [ 144, 240, 360, 480 ]) {
483 const file = videoDetails.files.find(f => f.resolution.id === resolution) 487 const file = videoDetails.files.find(f => f.resolution.id === resolution)
484 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl) 488 const path = servers[1].servers.buildWebTorrentFilePath(file.fileUrl)
485 const fps = await getVideoFileFPS(path) 489 const fps = await getVideoFileFPS(path)
@@ -586,6 +590,7 @@ describe('Test video transcoding', function () {
586 transcoding: { 590 transcoding: {
587 enabled: true, 591 enabled: true,
588 resolutions: { 592 resolutions: {
593 '144p': true,
589 '240p': true, 594 '240p': true,
590 '360p': true, 595 '360p': true,
591 '480p': true, 596 '480p': true,
@@ -667,7 +672,7 @@ describe('Test video transcoding', function () {
667 672
668 const videoFiles = videoDetails.files 673 const videoFiles = videoDetails.files
669 .concat(videoDetails.streamingPlaylists[0].files) 674 .concat(videoDetails.streamingPlaylists[0].files)
670 expect(videoFiles).to.have.lengthOf(8) 675 expect(videoFiles).to.have.lengthOf(10)
671 676
672 for (const file of videoFiles) { 677 for (const file of videoFiles) {
673 expect(file.metadata).to.be.undefined 678 expect(file.metadata).to.be.undefined
@@ -695,21 +700,21 @@ describe('Test video transcoding', function () {
695 const body = await servers[1].jobs.list({ 700 const body = await servers[1].jobs.list({
696 start: 0, 701 start: 0,
697 count: 100, 702 count: 100,
698 sort: '-createdAt', 703 sort: 'createdAt',
699 jobType: 'video-transcoding' 704 jobType: 'video-transcoding'
700 }) 705 })
701 706
702 const jobs = body.data 707 const jobs = body.data
703 const transcodingJobs = jobs.filter(j => j.data.videoUUID === video4k) 708 const transcodingJobs = jobs.filter(j => j.data.videoUUID === video4k)
704 709
705 expect(transcodingJobs).to.have.lengthOf(14) 710 expect(transcodingJobs).to.have.lengthOf(16)
706 711
707 const hlsJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-hls') 712 const hlsJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-hls')
708 const webtorrentJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-webtorrent') 713 const webtorrentJobs = transcodingJobs.filter(j => j.data.type === 'new-resolution-to-webtorrent')
709 const optimizeJobs = transcodingJobs.filter(j => j.data.type === 'optimize-to-webtorrent') 714 const optimizeJobs = transcodingJobs.filter(j => j.data.type === 'optimize-to-webtorrent')
710 715
711 expect(hlsJobs).to.have.lengthOf(7) 716 expect(hlsJobs).to.have.lengthOf(8)
712 expect(webtorrentJobs).to.have.lengthOf(6) 717 expect(webtorrentJobs).to.have.lengthOf(7)
713 expect(optimizeJobs).to.have.lengthOf(1) 718 expect(optimizeJobs).to.have.lengthOf(1)
714 719
715 for (const j of optimizeJobs.concat(hlsJobs.concat(webtorrentJobs))) { 720 for (const j of optimizeJobs.concat(hlsJobs.concat(webtorrentJobs))) {
diff --git a/server/tests/cli/create-transcoding-job.ts b/server/tests/cli/create-transcoding-job.ts
index 2b388ab0c..fb9c2584f 100644
--- a/server/tests/cli/create-transcoding-job.ts
+++ b/server/tests/cli/create-transcoding-job.ts
@@ -220,9 +220,9 @@ function runTests (objectStorage: boolean) {
220 for (const server of servers) { 220 for (const server of servers) {
221 const videoDetails = await server.videos.get({ id: videosUUID[4] }) 221 const videoDetails = await server.videos.get({ id: videosUUID[4] })
222 222
223 expect(videoDetails.files).to.have.lengthOf(4) 223 expect(videoDetails.files).to.have.lengthOf(5)
224 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1) 224 expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
225 expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(4) 225 expect(videoDetails.streamingPlaylists[0].files).to.have.lengthOf(5)
226 226
227 if (objectStorage) { 227 if (objectStorage) {
228 await checkFilesInObjectStorage(videoDetails.files, 'webtorrent') 228 await checkFilesInObjectStorage(videoDetails.files, 'webtorrent')
diff --git a/server/tests/helpers/core-utils.ts b/server/tests/helpers/core-utils.ts
index a6bf5b4c5..fa0a71341 100644
--- a/server/tests/helpers/core-utils.ts
+++ b/server/tests/helpers/core-utils.ts
@@ -104,6 +104,7 @@ describe('Bitrate', function () {
104 104
105 it('Should get appropriate max bitrate', function () { 105 it('Should get appropriate max bitrate', function () {
106 const tests = [ 106 const tests = [
107 { resolution: VideoResolution.H_144P, ratio: 16 / 9, fps: 24, min: 200, max: 400 },
107 { resolution: VideoResolution.H_240P, ratio: 16 / 9, fps: 24, min: 600, max: 800 }, 108 { resolution: VideoResolution.H_240P, ratio: 16 / 9, fps: 24, min: 600, max: 800 },
108 { resolution: VideoResolution.H_360P, ratio: 16 / 9, fps: 24, min: 1200, max: 1600 }, 109 { resolution: VideoResolution.H_360P, ratio: 16 / 9, fps: 24, min: 1200, max: 1600 },
109 { resolution: VideoResolution.H_480P, ratio: 16 / 9, fps: 24, min: 2000, max: 2300 }, 110 { resolution: VideoResolution.H_480P, ratio: 16 / 9, fps: 24, min: 2000, max: 2300 },
@@ -119,6 +120,7 @@ describe('Bitrate', function () {
119 120
120 it('Should get appropriate average bitrate', function () { 121 it('Should get appropriate average bitrate', function () {
121 const tests = [ 122 const tests = [
123 { resolution: VideoResolution.H_144P, ratio: 16 / 9, fps: 24, min: 50, max: 300 },
122 { resolution: VideoResolution.H_240P, ratio: 16 / 9, fps: 24, min: 350, max: 450 }, 124 { resolution: VideoResolution.H_240P, ratio: 16 / 9, fps: 24, min: 350, max: 450 },
123 { resolution: VideoResolution.H_360P, ratio: 16 / 9, fps: 24, min: 700, max: 900 }, 125 { resolution: VideoResolution.H_360P, ratio: 16 / 9, fps: 24, min: 700, max: 900 },
124 { resolution: VideoResolution.H_480P, ratio: 16 / 9, fps: 24, min: 1100, max: 1300 }, 126 { resolution: VideoResolution.H_480P, ratio: 16 / 9, fps: 24, min: 1100, max: 1300 },
diff --git a/shared/core-utils/videos/bitrate.ts b/shared/core-utils/videos/bitrate.ts
index a6712f8a4..18c6e2f6c 100644
--- a/shared/core-utils/videos/bitrate.ts
+++ b/shared/core-utils/videos/bitrate.ts
@@ -6,6 +6,7 @@ type BitPerPixel = { [ id in VideoResolution ]: number }
6 6
7const averageBitPerPixel: BitPerPixel = { 7const averageBitPerPixel: BitPerPixel = {
8 [VideoResolution.H_NOVIDEO]: 0, 8 [VideoResolution.H_NOVIDEO]: 0,
9 [VideoResolution.H_144P]: 0.19,
9 [VideoResolution.H_240P]: 0.17, 10 [VideoResolution.H_240P]: 0.17,
10 [VideoResolution.H_360P]: 0.15, 11 [VideoResolution.H_360P]: 0.15,
11 [VideoResolution.H_480P]: 0.12, 12 [VideoResolution.H_480P]: 0.12,
@@ -17,6 +18,7 @@ const averageBitPerPixel: BitPerPixel = {
17 18
18const maxBitPerPixel: BitPerPixel = { 19const maxBitPerPixel: BitPerPixel = {
19 [VideoResolution.H_NOVIDEO]: 0, 20 [VideoResolution.H_NOVIDEO]: 0,
21 [VideoResolution.H_144P]: 0.32,
20 [VideoResolution.H_240P]: 0.29, 22 [VideoResolution.H_240P]: 0.29,
21 [VideoResolution.H_360P]: 0.26, 23 [VideoResolution.H_360P]: 0.26,
22 [VideoResolution.H_480P]: 0.22, 24 [VideoResolution.H_480P]: 0.22,
@@ -73,6 +75,7 @@ function calculateBitrate (options: {
73 VideoResolution.H_480P, 75 VideoResolution.H_480P,
74 VideoResolution.H_360P, 76 VideoResolution.H_360P,
75 VideoResolution.H_240P, 77 VideoResolution.H_240P,
78 VideoResolution.H_144P,
76 VideoResolution.H_NOVIDEO 79 VideoResolution.H_NOVIDEO
77 ] 80 ]
78 81
diff --git a/shared/extra-utils/server/config-command.ts b/shared/extra-utils/server/config-command.ts
index 2746e9ac4..7a768b4df 100644
--- a/shared/extra-utils/server/config-command.ts
+++ b/shared/extra-utils/server/config-command.ts
@@ -8,6 +8,7 @@ export class ConfigCommand extends AbstractCommand {
8 8
9 static getCustomConfigResolutions (enabled: boolean) { 9 static getCustomConfigResolutions (enabled: boolean) {
10 return { 10 return {
11 '144p': enabled,
11 '240p': enabled, 12 '240p': enabled,
12 '360p': enabled, 13 '360p': enabled,
13 '480p': enabled, 14 '480p': enabled,
@@ -232,6 +233,7 @@ export class ConfigCommand extends AbstractCommand {
232 profile: 'default', 233 profile: 'default',
233 resolutions: { 234 resolutions: {
234 '0p': false, 235 '0p': false,
236 '144p': false,
235 '240p': false, 237 '240p': false,
236 '360p': true, 238 '360p': true,
237 '480p': true, 239 '480p': true,
@@ -258,6 +260,7 @@ export class ConfigCommand extends AbstractCommand {
258 threads: 4, 260 threads: 4,
259 profile: 'default', 261 profile: 'default',
260 resolutions: { 262 resolutions: {
263 '144p': true,
261 '240p': true, 264 '240p': true,
262 '360p': true, 265 '360p': true,
263 '480p': true, 266 '480p': true,
diff --git a/shared/models/server/custom-config.model.ts b/shared/models/server/custom-config.model.ts
index 322fbb797..3ed932494 100644
--- a/shared/models/server/custom-config.model.ts
+++ b/shared/models/server/custom-config.model.ts
@@ -2,6 +2,7 @@ import { NSFWPolicyType } from '../videos/nsfw-policy.type'
2import { BroadcastMessageLevel } from './broadcast-message-level.type' 2import { BroadcastMessageLevel } from './broadcast-message-level.type'
3 3
4export type ConfigResolutions = { 4export type ConfigResolutions = {
5 '144p': boolean
5 '240p': boolean 6 '240p': boolean
6 '360p': boolean 7 '360p': boolean
7 '480p': boolean 8 '480p': boolean
diff --git a/shared/models/videos/video-resolution.enum.ts b/shared/models/videos/video-resolution.enum.ts
index 24cd2d04d..5b48ad353 100644
--- a/shared/models/videos/video-resolution.enum.ts
+++ b/shared/models/videos/video-resolution.enum.ts
@@ -1,5 +1,6 @@
1export const enum VideoResolution { 1export const enum VideoResolution {
2 H_NOVIDEO = 0, 2 H_NOVIDEO = 0,
3 H_144P = 144,
3 H_240P = 240, 4 H_240P = 240,
4 H_360P = 360, 5 H_360P = 360,
5 H_480P = 480, 6 H_480P = 480,
diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml
index ec246bca0..48109664a 100644
--- a/support/doc/api/openapi.yaml
+++ b/support/doc/api/openapi.yaml
@@ -6167,6 +6167,8 @@ components:
6167 properties: 6167 properties:
6168 0p: 6168 0p:
6169 type: boolean 6169 type: boolean
6170 144p:
6171 type: boolean
6170 240p: 6172 240p:
6171 type: boolean 6173 type: boolean
6172 360p: 6174 360p:
diff --git a/support/docker/production/config/custom-environment-variables.yaml b/support/docker/production/config/custom-environment-variables.yaml
index c7cd28e65..d5304b0dd 100644
--- a/support/docker/production/config/custom-environment-variables.yaml
+++ b/support/docker/production/config/custom-environment-variables.yaml
@@ -130,6 +130,9 @@ transcoding:
130 __name: "PEERTUBE_TRANSCODING_THREADS" 130 __name: "PEERTUBE_TRANSCODING_THREADS"
131 __format: "json" 131 __format: "json"
132 resolutions: 132 resolutions:
133 144p:
134 __name: "PEERTUBE_TRANSCODING_144P"
135 __format: "json"
133 240p: 136 240p:
134 __name: "PEERTUBE_TRANSCODING_240P" 137 __name: "PEERTUBE_TRANSCODING_240P"
135 __format: "json" 138 __format: "json"