if (options.generateHls || CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false) {
const resolutionsEnabled = options.resolution
? [ parseInt(options.resolution) ]
- : computeResolutionsToTranscode({ inputResolution: maxResolution, type: 'vod', includeInputResolution: true })
+ : computeResolutionsToTranscode({ input: maxResolution, type: 'vod', includeInput: true, strictLower: false })
for (const resolution of resolutionsEnabled) {
dataInput.push({
import { ClientHtml } from '../../lib/client-html'
import { asyncMiddleware, authenticate, ensureUserHasRight, openapiOperationDoc } from '../../middlewares'
import { customConfigUpdateValidator, ensureConfigIsEditable } from '../../middlewares/validators/config'
+import { logger } from '@server/helpers/logger'
const configRouter = express.Router()
const data = customConfig()
+ logger.info('coucou', { data })
auditLogger.update(
getAuditIdFromRes(res),
new CustomConfigAuditView(data),
const targetUrl = body.targetUrl
const user = res.locals.oauth.token.User
- const youtubeDL = new YoutubeDLWrapper(targetUrl, ServerConfigManager.Instance.getEnabledResolutions('vod'))
+ const youtubeDL = new YoutubeDLWrapper(
+ targetUrl,
+ ServerConfigManager.Instance.getEnabledResolutions('vod'),
+ CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION
+ )
// Get video infos
let youtubeDLInfo: YoutubeDLInfo
const { resolution: maxResolution, audioStream } = await video.probeMaxQualityFile()
const resolutions = await Hooks.wrapObject(
- computeResolutionsToTranscode({ inputResolution: maxResolution, type: 'vod', includeInputResolution: true }),
+ computeResolutionsToTranscode({ input: maxResolution, type: 'vod', includeInput: true, strictLower: false }),
'filter:transcoding.manual.resolutions-to-transcode.result',
body
)
// ---------------------------------------------------------------------------
function computeResolutionsToTranscode (options: {
- inputResolution: number
+ input: number
type: 'vod' | 'live'
- includeInputResolution: boolean
+ includeInput: boolean
+ strictLower: boolean
}) {
- const { inputResolution, type, includeInputResolution } = options
+ const { input, type, includeInput, strictLower } = options
const configResolutions = type === 'vod'
? CONFIG.TRANSCODING.RESOLUTIONS
]
for (const resolution of availableResolutions) {
- if (configResolutions[resolution + 'p'] === true && inputResolution > resolution) {
- resolutionsEnabled.add(resolution)
- }
+ // Resolution not enabled
+ if (configResolutions[resolution + 'p'] !== true) continue
+ // Too big resolution for input file
+ if (input < resolution) continue
+ // We only want lower resolutions than input file
+ if (strictLower && input === resolution) continue
+
+ resolutionsEnabled.add(resolution)
}
- if (includeInputResolution) {
- resolutionsEnabled.add(inputResolution)
+ if (includeInput) {
+ resolutionsEnabled.add(input)
}
return Array.from(resolutionsEnabled)
}
}
- static getYoutubeDLVideoFormat (enabledResolutions: VideoResolution[]) {
+ static getYoutubeDLVideoFormat (enabledResolutions: VideoResolution[], useBestFormat: boolean) {
/**
* list of format selectors in order or preference
* see https://github.com/ytdl-org/youtube-dl#format-selection
*
* in any case we avoid AV1, see https://github.com/Chocobozzz/PeerTube/issues/3499
**/
- const resolution = enabledResolutions.length === 0
- ? VideoResolution.H_720P
- : Math.max(...enabledResolutions)
-
- return [
- `bestvideo[vcodec^=avc1][height=${resolution}]+bestaudio[ext=m4a]`, // case #1
- `bestvideo[vcodec!*=av01][vcodec!*=vp9.2][height=${resolution}]+bestaudio`, // case #2
- `bestvideo[vcodec^=avc1][height<=${resolution}]+bestaudio[ext=m4a]`, // case #3
- `bestvideo[vcodec!*=av01][vcodec!*=vp9.2]+bestaudio`,
+
+ let result: string[] = []
+
+ if (!useBestFormat) {
+ const resolution = enabledResolutions.length === 0
+ ? VideoResolution.H_720P
+ : Math.max(...enabledResolutions)
+
+ result = [
+ `bestvideo[vcodec^=avc1][height=${resolution}]+bestaudio[ext=m4a]`, // case #1
+ `bestvideo[vcodec!*=av01][vcodec!*=vp9.2][height=${resolution}]+bestaudio`, // case #2
+ `bestvideo[vcodec^=avc1][height<=${resolution}]+bestaudio[ext=m4a]` // case #
+ ]
+ }
+
+ return result.concat([
+ 'bestvideo[vcodec!*=av01][vcodec!*=vp9.2]+bestaudio',
'best[vcodec!*=av01][vcodec!*=vp9.2]', // case fallback for known formats
'best' // Ultimate fallback
- ].join('/')
+ ]).join('/')
}
private constructor () {
class YoutubeDLWrapper {
- constructor (private readonly url: string = '', private readonly enabledResolutions: number[] = []) {
+ constructor (
+ private readonly url: string,
+ private readonly enabledResolutions: number[],
+ private readonly useBestFormat: boolean
+ ) {
}
const info = await youtubeDL.getInfo({
url: this.url,
- format: YoutubeDLCLI.getYoutubeDLVideoFormat(this.enabledResolutions),
+ format: YoutubeDLCLI.getYoutubeDLVideoFormat(this.enabledResolutions, this.useBestFormat),
additionalYoutubeDLArgs: youtubeDLArgs,
processOptions
})
try {
await youtubeDL.download({
url: this.url,
- format: YoutubeDLCLI.getYoutubeDLVideoFormat(this.enabledResolutions),
+ format: YoutubeDLCLI.getYoutubeDLVideoFormat(this.enabledResolutions, this.useBestFormat),
output: pathWithoutExtension,
timeout,
processOptions
import { move, remove, stat } from 'fs-extra'
import { retryTransactionWrapper } from '@server/helpers/database-utils'
import { YoutubeDLWrapper } from '@server/helpers/youtube-dl'
+import { CONFIG } from '@server/initializers/config'
import { isPostImportVideoAccepted } from '@server/lib/moderation'
import { generateWebTorrentVideoFilename } from '@server/lib/paths'
import { Hooks } from '@server/lib/plugins/hooks'
VideoResolution,
VideoState
} from '@shared/models'
-import { ffprobePromise, getVideoStreamDuration, getVideoStreamFPS, getVideoStreamDimensionsInfo } from '../../../helpers/ffmpeg'
+import { ffprobePromise, getVideoStreamDimensionsInfo, getVideoStreamDuration, getVideoStreamFPS } from '../../../helpers/ffmpeg'
import { logger } from '../../../helpers/logger'
import { getSecureTorrentName } from '../../../helpers/utils'
import { createTorrentAndSetInfoHash, downloadWebTorrentVideo } from '../../../helpers/webtorrent'
const options = { type: payload.type, videoImportId: videoImport.id }
- const youtubeDL = new YoutubeDLWrapper(videoImport.targetUrl, ServerConfigManager.Instance.getEnabledResolutions('vod'))
+ const youtubeDL = new YoutubeDLWrapper(
+ videoImport.targetUrl,
+ ServerConfigManager.Instance.getEnabledResolutions('vod'),
+ CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION
+ )
return processFile(
() => youtubeDL.downloadVideo(payload.fileExt, JOB_TTL['video-import']),
// Create transcoding jobs if there are enabled resolutions
const resolutionsEnabled = await Hooks.wrapObject(
- computeResolutionsToTranscode({ inputResolution: videoFileResolution, type: 'vod', includeInputResolution: false }),
+ computeResolutionsToTranscode({ input: videoFileResolution, type: 'vod', includeInput: false, strictLower: true }),
'filter:transcoding.auto.resolutions-to-transcode.result',
options
)
}
private buildAllResolutionsToTranscode (originResolution: number) {
- const includeInputResolution = CONFIG.LIVE.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION
+ const includeInput = CONFIG.LIVE.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION
const resolutionsEnabled = CONFIG.LIVE.TRANSCODING.ENABLED
- ? computeResolutionsToTranscode({ inputResolution: originResolution, type: 'live', includeInputResolution })
+ ? computeResolutionsToTranscode({ input: originResolution, type: 'live', includeInput, strictLower: false })
: []
if (resolutionsEnabled.length === 0) {
function buildOriginalFileResolution (inputResolution: number) {
if (CONFIG.TRANSCODING.ALWAYS_TRANSCODE_ORIGINAL_RESOLUTION === true) return toEven(inputResolution)
- const resolutions = computeResolutionsToTranscode({ inputResolution, type: 'vod', includeInputResolution: false })
+ const resolutions = computeResolutionsToTranscode({ input: inputResolution, type: 'vod', includeInput: false, strictLower: false })
if (resolutions.length === 0) return toEven(inputResolution)
return Math.max(...resolutions)
import { join } from 'path'
import { FIXTURE_URLS, testCaptionFile, testImage } from '@server/tests/shared'
import { areHttpImportTestsDisabled } from '@shared/core-utils'
-import { HttpStatusCode, Video, VideoImportState, VideoPrivacy, VideoResolution, VideoState } from '@shared/models'
+import { CustomConfig, HttpStatusCode, Video, VideoImportState, VideoPrivacy, VideoResolution, VideoState } from '@shared/models'
import {
cleanupTests,
createMultipleServers,
setDefaultVideoChannel,
waitJobs
} from '@shared/server-commands'
+import { DeepPartial } from '@shared/typescript-utils'
async function checkVideosServer1 (server: PeerTubeServer, idHttp: string, idMagnet: string, idTorrent: string) {
const videoHttp = await server.videos.get({ id: idHttp })
await setAccessTokensToServers(servers)
await setDefaultVideoChannel(servers)
+ for (const server of servers) {
+ await server.config.updateExistingSubConfig({
+ newConfig: {
+ transcoding: {
+ alwaysTranscodeOriginalResolution: false
+ }
+ }
+ })
+ }
+
await doubleFollow(servers[0], servers[1])
})
it('Should import no HDR version on a HDR video', async function () {
this.timeout(300_000)
- const config = {
+ const config: DeepPartial<CustomConfig> = {
transcoding: {
enabled: true,
resolutions: {
+ '0p': false,
'144p': true,
'240p': true,
'360p': false,
},
webtorrent: { enabled: true },
hls: { enabled: false }
- },
- import: {
- videos: {
- http: {
- enabled: true
- },
- torrent: {
- enabled: true
- }
- }
}
}
- await servers[0].config.updateCustomSubConfig({ newConfig: config })
+ await servers[0].config.updateExistingSubConfig({ newConfig: config })
const attributes = {
name: 'hdr video',
expect(maxResolution, 'expected max resolution not met').to.equals(VideoResolution.H_240P)
})
+ it('Should not import resolution higher than enabled transcoding resolution', async function () {
+ this.timeout(300_000)
+
+ const config: DeepPartial<CustomConfig> = {
+ transcoding: {
+ enabled: true,
+ resolutions: {
+ '0p': false,
+ '144p': true,
+ '240p': false,
+ '360p': false,
+ '480p': false,
+ '720p': false,
+ '1080p': false,
+ '1440p': false,
+ '2160p': false
+ },
+ alwaysTranscodeOriginalResolution: false
+ }
+ }
+ await servers[0].config.updateExistingSubConfig({ newConfig: config })
+
+ const attributes = {
+ name: 'small resolution video',
+ targetUrl: FIXTURE_URLS.youtube,
+ channelId: servers[0].store.channel.id,
+ privacy: VideoPrivacy.PUBLIC
+ }
+ const { video: videoImported } = await servers[0].imports.importVideo({ attributes })
+ const videoUUID = videoImported.uuid
+
+ await waitJobs(servers)
+
+ // test resolution
+ const video = await servers[0].videos.get({ id: videoUUID })
+ expect(video.name).to.equal('small resolution video')
+ expect(video.files).to.have.lengthOf(1)
+ expect(video.files[0].resolution.id).to.equal(144)
+ })
+
+ it('Should import resolution higher than enabled transcoding resolution', async function () {
+ this.timeout(300_000)
+
+ const config: DeepPartial<CustomConfig> = {
+ transcoding: {
+ alwaysTranscodeOriginalResolution: true
+ }
+ }
+ await servers[0].config.updateExistingSubConfig({ newConfig: config })
+
+ const attributes = {
+ name: 'bigger resolution video',
+ targetUrl: FIXTURE_URLS.youtube,
+ channelId: servers[0].store.channel.id,
+ privacy: VideoPrivacy.PUBLIC
+ }
+ const { video: videoImported } = await servers[0].imports.importVideo({ attributes })
+ const videoUUID = videoImported.uuid
+
+ await waitJobs(servers)
+
+ // test resolution
+ const video = await servers[0].videos.get({ id: videoUUID })
+ expect(video.name).to.equal('bigger resolution video')
+
+ expect(video.files).to.have.lengthOf(2)
+ expect(video.files.find(f => f.resolution.id === 240)).to.exist
+ expect(video.files.find(f => f.resolution.id === 144)).to.exist
+ })
+
it('Should import a peertube video', async function () {
this.timeout(120_000)