aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/config.ts2
-rw-r--r--server/controllers/api/videos/import.ts6
-rw-r--r--server/controllers/api/videos/transcoding.ts2
-rw-r--r--server/helpers/ffmpeg/ffprobe-utils.ts22
-rw-r--r--server/helpers/youtube-dl/youtube-dl-cli.ts30
-rw-r--r--server/helpers/youtube-dl/youtube-dl-wrapper.ts10
-rw-r--r--server/lib/job-queue/handlers/video-import.ts9
-rw-r--r--server/lib/job-queue/handlers/video-transcoding.ts2
-rw-r--r--server/lib/live/live-manager.ts4
-rw-r--r--server/lib/transcoding/transcoding.ts2
-rw-r--r--server/tests/api/videos/video-imports.ts98
11 files changed, 144 insertions, 43 deletions
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts
index ff2fa9d86..19bd2888c 100644
--- a/server/controllers/api/config.ts
+++ b/server/controllers/api/config.ts
@@ -10,6 +10,7 @@ import { CONFIG, reloadConfig } from '../../initializers/config'
10import { ClientHtml } from '../../lib/client-html' 10import { ClientHtml } from '../../lib/client-html'
11import { asyncMiddleware, authenticate, ensureUserHasRight, openapiOperationDoc } from '../../middlewares' 11import { asyncMiddleware, authenticate, ensureUserHasRight, openapiOperationDoc } from '../../middlewares'
12import { customConfigUpdateValidator, ensureConfigIsEditable } from '../../middlewares/validators/config' 12import { customConfigUpdateValidator, ensureConfigIsEditable } from '../../middlewares/validators/config'
13import { logger } from '@server/helpers/logger'
13 14
14const configRouter = express.Router() 15const configRouter = express.Router()
15 16
@@ -112,6 +113,7 @@ async function updateCustomConfig (req: express.Request, res: express.Response)
112 113
113 const data = customConfig() 114 const data = customConfig()
114 115
116 logger.info('coucou', { data })
115 auditLogger.update( 117 auditLogger.update(
116 getAuditIdFromRes(res), 118 getAuditIdFromRes(res),
117 new CustomConfigAuditView(data), 119 new CustomConfigAuditView(data),
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts
index 7576e77e7..b12953630 100644
--- a/server/controllers/api/videos/import.ts
+++ b/server/controllers/api/videos/import.ts
@@ -175,7 +175,11 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
175 const targetUrl = body.targetUrl 175 const targetUrl = body.targetUrl
176 const user = res.locals.oauth.token.User 176 const user = res.locals.oauth.token.User
177 177
178 const youtubeDL = new YoutubeDLWrapper(targetUrl, ServerConfigManager.Instance.getEnabledResolutions('vod')) 178 const youtubeDL = new YoutubeDLWrapper(
179 targetUrl,
180 ServerConfigManager.Instance.getEnabledResolutions('vod'),
181 CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION
182 )
179 183
180 // Get video infos 184 // Get video infos
181 let youtubeDLInfo: YoutubeDLInfo 185 let youtubeDLInfo: YoutubeDLInfo
diff --git a/server/controllers/api/videos/transcoding.ts b/server/controllers/api/videos/transcoding.ts
index 7d924c5ac..b2b71a870 100644
--- a/server/controllers/api/videos/transcoding.ts
+++ b/server/controllers/api/videos/transcoding.ts
@@ -32,7 +32,7 @@ async function createTranscoding (req: express.Request, res: express.Response) {
32 32
33 const { resolution: maxResolution, audioStream } = await video.probeMaxQualityFile() 33 const { resolution: maxResolution, audioStream } = await video.probeMaxQualityFile()
34 const resolutions = await Hooks.wrapObject( 34 const resolutions = await Hooks.wrapObject(
35 computeResolutionsToTranscode({ inputResolution: maxResolution, type: 'vod', includeInputResolution: true }), 35 computeResolutionsToTranscode({ input: maxResolution, type: 'vod', includeInput: true, strictLower: false }),
36 'filter:transcoding.manual.resolutions-to-transcode.result', 36 'filter:transcoding.manual.resolutions-to-transcode.result',
37 body 37 body
38 ) 38 )
diff --git a/server/helpers/ffmpeg/ffprobe-utils.ts b/server/helpers/ffmpeg/ffprobe-utils.ts
index 7bcd27665..c45f9ec99 100644
--- a/server/helpers/ffmpeg/ffprobe-utils.ts
+++ b/server/helpers/ffmpeg/ffprobe-utils.ts
@@ -91,11 +91,12 @@ async function getAudioStreamCodec (path: string, existingProbe?: FfprobeData) {
91// --------------------------------------------------------------------------- 91// ---------------------------------------------------------------------------
92 92
93function computeResolutionsToTranscode (options: { 93function computeResolutionsToTranscode (options: {
94 inputResolution: number 94 input: number
95 type: 'vod' | 'live' 95 type: 'vod' | 'live'
96 includeInputResolution: boolean 96 includeInput: boolean
97 strictLower: boolean
97}) { 98}) {
98 const { inputResolution, type, includeInputResolution } = options 99 const { input, type, includeInput, strictLower } = options
99 100
100 const configResolutions = type === 'vod' 101 const configResolutions = type === 'vod'
101 ? CONFIG.TRANSCODING.RESOLUTIONS 102 ? CONFIG.TRANSCODING.RESOLUTIONS
@@ -117,13 +118,18 @@ function computeResolutionsToTranscode (options: {
117 ] 118 ]
118 119
119 for (const resolution of availableResolutions) { 120 for (const resolution of availableResolutions) {
120 if (configResolutions[resolution + 'p'] === true && inputResolution > resolution) { 121 // Resolution not enabled
121 resolutionsEnabled.add(resolution) 122 if (configResolutions[resolution + 'p'] !== true) continue
122 } 123 // Too big resolution for input file
124 if (input < resolution) continue
125 // We only want lower resolutions than input file
126 if (strictLower && input === resolution) continue
127
128 resolutionsEnabled.add(resolution)
123 } 129 }
124 130
125 if (includeInputResolution) { 131 if (includeInput) {
126 resolutionsEnabled.add(inputResolution) 132 resolutionsEnabled.add(input)
127 } 133 }
128 134
129 return Array.from(resolutionsEnabled) 135 return Array.from(resolutionsEnabled)
diff --git a/server/helpers/youtube-dl/youtube-dl-cli.ts b/server/helpers/youtube-dl/youtube-dl-cli.ts
index 728f096b5..13c990a1e 100644
--- a/server/helpers/youtube-dl/youtube-dl-cli.ts
+++ b/server/helpers/youtube-dl/youtube-dl-cli.ts
@@ -57,7 +57,7 @@ export class YoutubeDLCLI {
57 } 57 }
58 } 58 }
59 59
60 static getYoutubeDLVideoFormat (enabledResolutions: VideoResolution[]) { 60 static getYoutubeDLVideoFormat (enabledResolutions: VideoResolution[], useBestFormat: boolean) {
61 /** 61 /**
62 * list of format selectors in order or preference 62 * list of format selectors in order or preference
63 * see https://github.com/ytdl-org/youtube-dl#format-selection 63 * see https://github.com/ytdl-org/youtube-dl#format-selection
@@ -69,18 +69,26 @@ export class YoutubeDLCLI {
69 * 69 *
70 * in any case we avoid AV1, see https://github.com/Chocobozzz/PeerTube/issues/3499 70 * in any case we avoid AV1, see https://github.com/Chocobozzz/PeerTube/issues/3499
71 **/ 71 **/
72 const resolution = enabledResolutions.length === 0 72
73 ? VideoResolution.H_720P 73 let result: string[] = []
74 : Math.max(...enabledResolutions) 74
75 75 if (!useBestFormat) {
76 return [ 76 const resolution = enabledResolutions.length === 0
77 `bestvideo[vcodec^=avc1][height=${resolution}]+bestaudio[ext=m4a]`, // case #1 77 ? VideoResolution.H_720P
78 `bestvideo[vcodec!*=av01][vcodec!*=vp9.2][height=${resolution}]+bestaudio`, // case #2 78 : Math.max(...enabledResolutions)
79 `bestvideo[vcodec^=avc1][height<=${resolution}]+bestaudio[ext=m4a]`, // case #3 79
80 `bestvideo[vcodec!*=av01][vcodec!*=vp9.2]+bestaudio`, 80 result = [
81 `bestvideo[vcodec^=avc1][height=${resolution}]+bestaudio[ext=m4a]`, // case #1
82 `bestvideo[vcodec!*=av01][vcodec!*=vp9.2][height=${resolution}]+bestaudio`, // case #2
83 `bestvideo[vcodec^=avc1][height<=${resolution}]+bestaudio[ext=m4a]` // case #
84 ]
85 }
86
87 return result.concat([
88 'bestvideo[vcodec!*=av01][vcodec!*=vp9.2]+bestaudio',
81 'best[vcodec!*=av01][vcodec!*=vp9.2]', // case fallback for known formats 89 'best[vcodec!*=av01][vcodec!*=vp9.2]', // case fallback for known formats
82 'best' // Ultimate fallback 90 'best' // Ultimate fallback
83 ].join('/') 91 ]).join('/')
84 } 92 }
85 93
86 private constructor () { 94 private constructor () {
diff --git a/server/helpers/youtube-dl/youtube-dl-wrapper.ts b/server/helpers/youtube-dl/youtube-dl-wrapper.ts
index d585e9a95..176cf3b69 100644
--- a/server/helpers/youtube-dl/youtube-dl-wrapper.ts
+++ b/server/helpers/youtube-dl/youtube-dl-wrapper.ts
@@ -21,7 +21,11 @@ const processOptions = {
21 21
22class YoutubeDLWrapper { 22class YoutubeDLWrapper {
23 23
24 constructor (private readonly url: string = '', private readonly enabledResolutions: number[] = []) { 24 constructor (
25 private readonly url: string,
26 private readonly enabledResolutions: number[],
27 private readonly useBestFormat: boolean
28 ) {
25 29
26 } 30 }
27 31
@@ -30,7 +34,7 @@ class YoutubeDLWrapper {
30 34
31 const info = await youtubeDL.getInfo({ 35 const info = await youtubeDL.getInfo({
32 url: this.url, 36 url: this.url,
33 format: YoutubeDLCLI.getYoutubeDLVideoFormat(this.enabledResolutions), 37 format: YoutubeDLCLI.getYoutubeDLVideoFormat(this.enabledResolutions, this.useBestFormat),
34 additionalYoutubeDLArgs: youtubeDLArgs, 38 additionalYoutubeDLArgs: youtubeDLArgs,
35 processOptions 39 processOptions
36 }) 40 })
@@ -80,7 +84,7 @@ class YoutubeDLWrapper {
80 try { 84 try {
81 await youtubeDL.download({ 85 await youtubeDL.download({
82 url: this.url, 86 url: this.url,
83 format: YoutubeDLCLI.getYoutubeDLVideoFormat(this.enabledResolutions), 87 format: YoutubeDLCLI.getYoutubeDLVideoFormat(this.enabledResolutions, this.useBestFormat),
84 output: pathWithoutExtension, 88 output: pathWithoutExtension,
85 timeout, 89 timeout,
86 processOptions 90 processOptions
diff --git a/server/lib/job-queue/handlers/video-import.ts b/server/lib/job-queue/handlers/video-import.ts
index 40804e82e..4cde26aef 100644
--- a/server/lib/job-queue/handlers/video-import.ts
+++ b/server/lib/job-queue/handlers/video-import.ts
@@ -2,6 +2,7 @@ import { Job } from 'bull'
2import { move, remove, stat } from 'fs-extra' 2import { move, remove, stat } from 'fs-extra'
3import { retryTransactionWrapper } from '@server/helpers/database-utils' 3import { retryTransactionWrapper } from '@server/helpers/database-utils'
4import { YoutubeDLWrapper } from '@server/helpers/youtube-dl' 4import { YoutubeDLWrapper } from '@server/helpers/youtube-dl'
5import { CONFIG } from '@server/initializers/config'
5import { isPostImportVideoAccepted } from '@server/lib/moderation' 6import { isPostImportVideoAccepted } from '@server/lib/moderation'
6import { generateWebTorrentVideoFilename } from '@server/lib/paths' 7import { generateWebTorrentVideoFilename } from '@server/lib/paths'
7import { Hooks } from '@server/lib/plugins/hooks' 8import { Hooks } from '@server/lib/plugins/hooks'
@@ -25,7 +26,7 @@ import {
25 VideoResolution, 26 VideoResolution,
26 VideoState 27 VideoState
27} from '@shared/models' 28} from '@shared/models'
28import { ffprobePromise, getVideoStreamDuration, getVideoStreamFPS, getVideoStreamDimensionsInfo } from '../../../helpers/ffmpeg' 29import { ffprobePromise, getVideoStreamDimensionsInfo, getVideoStreamDuration, getVideoStreamFPS } from '../../../helpers/ffmpeg'
29import { logger } from '../../../helpers/logger' 30import { logger } from '../../../helpers/logger'
30import { getSecureTorrentName } from '../../../helpers/utils' 31import { getSecureTorrentName } from '../../../helpers/utils'
31import { createTorrentAndSetInfoHash, downloadWebTorrentVideo } from '../../../helpers/webtorrent' 32import { createTorrentAndSetInfoHash, downloadWebTorrentVideo } from '../../../helpers/webtorrent'
@@ -80,7 +81,11 @@ async function processYoutubeDLImport (job: Job, videoImport: MVideoImportDefaul
80 81
81 const options = { type: payload.type, videoImportId: videoImport.id } 82 const options = { type: payload.type, videoImportId: videoImport.id }
82 83
83 const youtubeDL = new YoutubeDLWrapper(videoImport.targetUrl, ServerConfigManager.Instance.getEnabledResolutions('vod')) 84 const youtubeDL = new YoutubeDLWrapper(
85 videoImport.targetUrl,
86 ServerConfigManager.Instance.getEnabledResolutions('vod'),
87 CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION
88 )
84 89
85 return processFile( 90 return processFile(
86 () => youtubeDL.downloadVideo(payload.fileExt, JOB_TTL['video-import']), 91 () => youtubeDL.downloadVideo(payload.fileExt, JOB_TTL['video-import']),
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts
index 890d34e3b..4e5e97919 100644
--- a/server/lib/job-queue/handlers/video-transcoding.ts
+++ b/server/lib/job-queue/handlers/video-transcoding.ts
@@ -265,7 +265,7 @@ async function createLowerResolutionsJobs (options: {
265 265
266 // Create transcoding jobs if there are enabled resolutions 266 // Create transcoding jobs if there are enabled resolutions
267 const resolutionsEnabled = await Hooks.wrapObject( 267 const resolutionsEnabled = await Hooks.wrapObject(
268 computeResolutionsToTranscode({ inputResolution: videoFileResolution, type: 'vod', includeInputResolution: false }), 268 computeResolutionsToTranscode({ input: videoFileResolution, type: 'vod', includeInput: false, strictLower: true }),
269 'filter:transcoding.auto.resolutions-to-transcode.result', 269 'filter:transcoding.auto.resolutions-to-transcode.result',
270 options 270 options
271 ) 271 )
diff --git a/server/lib/live/live-manager.ts b/server/lib/live/live-manager.ts
index 3ac57fa44..1410889a2 100644
--- a/server/lib/live/live-manager.ts
+++ b/server/lib/live/live-manager.ts
@@ -456,10 +456,10 @@ class LiveManager {
456 } 456 }
457 457
458 private buildAllResolutionsToTranscode (originResolution: number) { 458 private buildAllResolutionsToTranscode (originResolution: number) {
459 const includeInputResolution = CONFIG.LIVE.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION 459 const includeInput = CONFIG.LIVE.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION
460 460
461 const resolutionsEnabled = CONFIG.LIVE.TRANSCODING.ENABLED 461 const resolutionsEnabled = CONFIG.LIVE.TRANSCODING.ENABLED
462 ? computeResolutionsToTranscode({ inputResolution: originResolution, type: 'live', includeInputResolution }) 462 ? computeResolutionsToTranscode({ input: originResolution, type: 'live', includeInput, strictLower: false })
463 : [] 463 : []
464 464
465 if (resolutionsEnabled.length === 0) { 465 if (resolutionsEnabled.length === 0) {
diff --git a/server/lib/transcoding/transcoding.ts b/server/lib/transcoding/transcoding.ts
index 3681de994..070c7ebda 100644
--- a/server/lib/transcoding/transcoding.ts
+++ b/server/lib/transcoding/transcoding.ts
@@ -366,7 +366,7 @@ async function generateHlsPlaylistCommon (options: {
366function buildOriginalFileResolution (inputResolution: number) { 366function buildOriginalFileResolution (inputResolution: number) {
367 if (CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION === true) return toEven(inputResolution) 367 if (CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION === true) return toEven(inputResolution)
368 368
369 const resolutions = computeResolutionsToTranscode({ inputResolution, type: 'vod', includeInputResolution: false }) 369 const resolutions = computeResolutionsToTranscode({ input: inputResolution, type: 'vod', includeInput: false, strictLower: false })
370 if (resolutions.length === 0) return toEven(inputResolution) 370 if (resolutions.length === 0) return toEven(inputResolution)
371 371
372 return Math.max(...resolutions) 372 return Math.max(...resolutions)
diff --git a/server/tests/api/videos/video-imports.ts b/server/tests/api/videos/video-imports.ts
index cf9f7d0cb..603e2d234 100644
--- a/server/tests/api/videos/video-imports.ts
+++ b/server/tests/api/videos/video-imports.ts
@@ -6,7 +6,7 @@ import { pathExists, readdir, remove } from 'fs-extra'
6import { join } from 'path' 6import { join } from 'path'
7import { FIXTURE_URLS, testCaptionFile, testImage } from '@server/tests/shared' 7import { FIXTURE_URLS, testCaptionFile, testImage } from '@server/tests/shared'
8import { areHttpImportTestsDisabled } from '@shared/core-utils' 8import { areHttpImportTestsDisabled } from '@shared/core-utils'
9import { HttpStatusCode, Video, VideoImportState, VideoPrivacy, VideoResolution, VideoState } from '@shared/models' 9import { CustomConfig, HttpStatusCode, Video, VideoImportState, VideoPrivacy, VideoResolution, VideoState } from '@shared/models'
10import { 10import {
11 cleanupTests, 11 cleanupTests,
12 createMultipleServers, 12 createMultipleServers,
@@ -17,6 +17,7 @@ import {
17 setDefaultVideoChannel, 17 setDefaultVideoChannel,
18 waitJobs 18 waitJobs
19} from '@shared/server-commands' 19} from '@shared/server-commands'
20import { DeepPartial } from '@shared/typescript-utils'
20 21
21async function checkVideosServer1 (server: PeerTubeServer, idHttp: string, idMagnet: string, idTorrent: string) { 22async function checkVideosServer1 (server: PeerTubeServer, idHttp: string, idMagnet: string, idTorrent: string) {
22 const videoHttp = await server.videos.get({ id: idHttp }) 23 const videoHttp = await server.videos.get({ id: idHttp })
@@ -105,6 +106,16 @@ describe('Test video imports', function () {
105 await setAccessTokensToServers(servers) 106 await setAccessTokensToServers(servers)
106 await setDefaultVideoChannel(servers) 107 await setDefaultVideoChannel(servers)
107 108
109 for (const server of servers) {
110 await server.config.updateExistingSubConfig({
111 newConfig: {
112 transcoding: {
113 alwaysTranscodeOriginalResolution: false
114 }
115 }
116 })
117 }
118
108 await doubleFollow(servers[0], servers[1]) 119 await doubleFollow(servers[0], servers[1])
109 }) 120 })
110 121
@@ -306,10 +317,11 @@ describe('Test video imports', function () {
306 it('Should import no HDR version on a HDR video', async function () { 317 it('Should import no HDR version on a HDR video', async function () {
307 this.timeout(300_000) 318 this.timeout(300_000)
308 319
309 const config = { 320 const config: DeepPartial<CustomConfig> = {
310 transcoding: { 321 transcoding: {
311 enabled: true, 322 enabled: true,
312 resolutions: { 323 resolutions: {
324 '0p': false,
313 '144p': true, 325 '144p': true,
314 '240p': true, 326 '240p': true,
315 '360p': false, 327 '360p': false,
@@ -321,19 +333,9 @@ describe('Test video imports', function () {
321 }, 333 },
322 webtorrent: { enabled: true }, 334 webtorrent: { enabled: true },
323 hls: { enabled: false } 335 hls: { enabled: false }
324 },
325 import: {
326 videos: {
327 http: {
328 enabled: true
329 },
330 torrent: {
331 enabled: true
332 }
333 }
334 } 336 }
335 } 337 }
336 await servers[0].config.updateCustomSubConfig({ newConfig: config }) 338 await servers[0].config.updateExistingSubConfig({ newConfig: config })
337 339
338 const attributes = { 340 const attributes = {
339 name: 'hdr video', 341 name: 'hdr video',
@@ -353,6 +355,76 @@ describe('Test video imports', function () {
353 expect(maxResolution, 'expected max resolution not met').to.equals(VideoResolution.H_240P) 355 expect(maxResolution, 'expected max resolution not met').to.equals(VideoResolution.H_240P)
354 }) 356 })
355 357
358 it('Should not import resolution higher than enabled transcoding resolution', async function () {
359 this.timeout(300_000)
360
361 const config: DeepPartial<CustomConfig> = {
362 transcoding: {
363 enabled: true,
364 resolutions: {
365 '0p': false,
366 '144p': true,
367 '240p': false,
368 '360p': false,
369 '480p': false,
370 '720p': false,
371 '1080p': false,
372 '1440p': false,
373 '2160p': false
374 },
375 alwaysTranscodeOriginalResolution: false
376 }
377 }
378 await servers[0].config.updateExistingSubConfig({ newConfig: config })
379
380 const attributes = {
381 name: 'small resolution video',
382 targetUrl: FIXTURE_URLS.youtube,
383 channelId: servers[0].store.channel.id,
384 privacy: VideoPrivacy.PUBLIC
385 }
386 const { video: videoImported } = await servers[0].imports.importVideo({ attributes })
387 const videoUUID = videoImported.uuid
388
389 await waitJobs(servers)
390
391 // test resolution
392 const video = await servers[0].videos.get({ id: videoUUID })
393 expect(video.name).to.equal('small resolution video')
394 expect(video.files).to.have.lengthOf(1)
395 expect(video.files[0].resolution.id).to.equal(144)
396 })
397
398 it('Should import resolution higher than enabled transcoding resolution', async function () {
399 this.timeout(300_000)
400
401 const config: DeepPartial<CustomConfig> = {
402 transcoding: {
403 alwaysTranscodeOriginalResolution: true
404 }
405 }
406 await servers[0].config.updateExistingSubConfig({ newConfig: config })
407
408 const attributes = {
409 name: 'bigger resolution video',
410 targetUrl: FIXTURE_URLS.youtube,
411 channelId: servers[0].store.channel.id,
412 privacy: VideoPrivacy.PUBLIC
413 }
414 const { video: videoImported } = await servers[0].imports.importVideo({ attributes })
415 const videoUUID = videoImported.uuid
416
417 await waitJobs(servers)
418
419 // test resolution
420 const video = await servers[0].videos.get({ id: videoUUID })
421 expect(video.name).to.equal('bigger resolution video')
422
423 expect(video.files).to.have.lengthOf(2)
424 expect(video.files.find(f => f.resolution.id === 240)).to.exist
425 expect(video.files.find(f => f.resolution.id === 144)).to.exist
426 })
427
356 it('Should import a peertube video', async function () { 428 it('Should import a peertube video', async function () {
357 this.timeout(120_000) 429 this.timeout(120_000)
358 430