diff options
Diffstat (limited to 'server/helpers/youtube-dl.ts')
-rw-r--r-- | server/helpers/youtube-dl.ts | 53 |
1 files changed, 47 insertions, 6 deletions
diff --git a/server/helpers/youtube-dl.ts b/server/helpers/youtube-dl.ts index 74e5f896c..ebb788e8e 100644 --- a/server/helpers/youtube-dl.ts +++ b/server/helpers/youtube-dl.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants' | 1 | import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants' |
2 | import { logger } from './logger' | 2 | import { logger } from './logger' |
3 | import { generateVideoImportTmpPath } from './utils' | 3 | import { generateVideoImportTmpPath } from './utils' |
4 | import { getEnabledResolutions } from '../lib/video-transcoding' | ||
4 | import { join } from 'path' | 5 | import { join } from 'path' |
5 | import { peertubeTruncate, root } from './core-utils' | 6 | import { peertubeTruncate, root } from './core-utils' |
6 | import { ensureDir, remove, writeFile } from 'fs-extra' | 7 | import { ensureDir, remove, writeFile } from 'fs-extra' |
@@ -8,6 +9,7 @@ import * as request from 'request' | |||
8 | import { createWriteStream } from 'fs' | 9 | import { createWriteStream } from 'fs' |
9 | import { CONFIG } from '@server/initializers/config' | 10 | import { CONFIG } from '@server/initializers/config' |
10 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' | 11 | import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes' |
12 | import { VideoResolution } from '../../shared/models/videos' | ||
11 | 13 | ||
12 | export type YoutubeDLInfo = { | 14 | export type YoutubeDLInfo = { |
13 | name?: string | 15 | name?: string |
@@ -18,7 +20,8 @@ export type YoutubeDLInfo = { | |||
18 | nsfw?: boolean | 20 | nsfw?: boolean |
19 | tags?: string[] | 21 | tags?: string[] |
20 | thumbnailUrl?: string | 22 | thumbnailUrl?: string |
21 | fileExt?: string | 23 | ext?: string |
24 | mergeExt?: string | ||
22 | originallyPublishedAt?: Date | 25 | originallyPublishedAt?: Date |
23 | } | 26 | } |
24 | 27 | ||
@@ -41,12 +44,21 @@ function getYoutubeDLInfo (url: string, opts?: string[]): Promise<YoutubeDLInfo> | |||
41 | } | 44 | } |
42 | 45 | ||
43 | args = wrapWithProxyOptions(args) | 46 | args = wrapWithProxyOptions(args) |
47 | args = [ '-f', getYoutubeDLVideoFormat() ].concat(args) | ||
44 | 48 | ||
45 | safeGetYoutubeDL() | 49 | safeGetYoutubeDL() |
46 | .then(youtubeDL => { | 50 | .then(youtubeDL => { |
47 | youtubeDL.getInfo(url, args, processOptions, (err, info) => { | 51 | youtubeDL.getInfo(url, args, processOptions, (err, info) => { |
48 | if (err) return rej(err) | 52 | if (err) return rej(err) |
49 | if (info.is_live === true) return rej(new Error('Cannot download a live streaming.')) | 53 | if (info.is_live === true) return rej(new Error('Cannot download a live streaming.')) |
54 | if (info.format_id?.includes('+')) { | ||
55 | // this is a merge format and its extension will be appended | ||
56 | if (info.ext === 'mp4') { | ||
57 | info.mergeExt = 'mp4' | ||
58 | } else { | ||
59 | info.mergeExt = 'mkv' | ||
60 | } | ||
61 | } | ||
50 | 62 | ||
51 | const obj = buildVideoInfo(normalizeObject(info)) | 63 | const obj = buildVideoInfo(normalizeObject(info)) |
52 | if (obj.name && obj.name.length < CONSTRAINTS_FIELDS.VIDEOS.NAME.min) obj.name += ' video' | 64 | if (obj.name && obj.name.length < CONSTRAINTS_FIELDS.VIDEOS.NAME.min) obj.name += ' video' |
@@ -92,13 +104,40 @@ function getYoutubeDLSubs (url: string, opts?: object): Promise<YoutubeDLSubs> { | |||
92 | }) | 104 | }) |
93 | } | 105 | } |
94 | 106 | ||
95 | function downloadYoutubeDLVideo (url: string, extension: string, timeout: number) { | 107 | function getYoutubeDLVideoFormat () { |
108 | /** | ||
109 | * list of format selectors in order or preference | ||
110 | * see https://github.com/ytdl-org/youtube-dl#format-selection | ||
111 | * | ||
112 | * case #1 asks for a mp4 using h264 (avc1) and the exact resolution in the hope | ||
113 | * of being able to do a "quick-transcode" | ||
114 | * case #2 is the first fallback. No "quick-transcode" means we can get anything else (like vp9) | ||
115 | * case #3 is the resolution-degraded equivalent of #1, and already a pretty safe fallback | ||
116 | * | ||
117 | * in any case we avoid AV1, see https://github.com/Chocobozzz/PeerTube/issues/3499 | ||
118 | **/ | ||
119 | const enabledResolutions = getEnabledResolutions('vod') | ||
120 | const resolution = enabledResolutions.length === 0 | ||
121 | ? VideoResolution.H_720P | ||
122 | : Math.max(...enabledResolutions) | ||
123 | |||
124 | return [ | ||
125 | `bestvideo[vcodec^=avc1][height=${resolution}]+bestaudio[ext=m4a]`, // case #1 | ||
126 | `bestvideo[vcodec!*=av01][vcodec!*=vp9.2][height=${resolution}]+bestaudio`, // case #2 | ||
127 | `bestvideo[vcodec^=avc1][height<=${resolution}]+bestaudio[ext=m4a]`, // case #3 | ||
128 | `bestvideo[vcodec!*=av01][vcodec!*=vp9.2]+bestaudio`, | ||
129 | 'best[vcodec!*=av01][vcodec!*=vp9.2]' // case fallback | ||
130 | ].join('/') | ||
131 | } | ||
132 | |||
133 | function downloadYoutubeDLVideo (url: string, extension: string, timeout: number, mergeExtension?: string) { | ||
96 | const path = generateVideoImportTmpPath(url, extension) | 134 | const path = generateVideoImportTmpPath(url, extension) |
135 | const finalPath = mergeExtension ? path.replace(new RegExp(`${extension}$`), mergeExtension) : path | ||
97 | let timer | 136 | let timer |
98 | 137 | ||
99 | logger.info('Importing youtubeDL video %s to %s', url, path) | 138 | logger.info('Importing youtubeDL video %s to %s', url, finalPath) |
100 | 139 | ||
101 | let options = [ '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best', '-o', path ] | 140 | let options = [ '-f', getYoutubeDLVideoFormat(), '-o', path ] |
102 | options = wrapWithProxyOptions(options) | 141 | options = wrapWithProxyOptions(options) |
103 | 142 | ||
104 | if (process.env.FFMPEG_PATH) { | 143 | if (process.env.FFMPEG_PATH) { |
@@ -118,7 +157,7 @@ function downloadYoutubeDLVideo (url: string, extension: string, timeout: number | |||
118 | return rej(err) | 157 | return rej(err) |
119 | } | 158 | } |
120 | 159 | ||
121 | return res(path) | 160 | return res(finalPath) |
122 | }) | 161 | }) |
123 | 162 | ||
124 | timer = setTimeout(() => { | 163 | timer = setTimeout(() => { |
@@ -236,6 +275,7 @@ function buildOriginallyPublishedAt (obj: any) { | |||
236 | 275 | ||
237 | export { | 276 | export { |
238 | updateYoutubeDLBinary, | 277 | updateYoutubeDLBinary, |
278 | getYoutubeDLVideoFormat, | ||
239 | downloadYoutubeDLVideo, | 279 | downloadYoutubeDLVideo, |
240 | getYoutubeDLSubs, | 280 | getYoutubeDLSubs, |
241 | getYoutubeDLInfo, | 281 | getYoutubeDLInfo, |
@@ -275,7 +315,8 @@ function buildVideoInfo (obj: any): YoutubeDLInfo { | |||
275 | tags: getTags(obj.tags), | 315 | tags: getTags(obj.tags), |
276 | thumbnailUrl: obj.thumbnail || undefined, | 316 | thumbnailUrl: obj.thumbnail || undefined, |
277 | originallyPublishedAt: buildOriginallyPublishedAt(obj), | 317 | originallyPublishedAt: buildOriginallyPublishedAt(obj), |
278 | fileExt: obj.ext | 318 | ext: obj.ext, |
319 | mergeExt: obj.mergeExt | ||
279 | } | 320 | } |
280 | } | 321 | } |
281 | 322 | ||