Fix (again) youtube import
This commit is contained in:
parent
d487a997c8
commit
805b8619c1
|
@ -220,8 +220,7 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
|
||||||
videoImportId: videoImport.id,
|
videoImportId: videoImport.id,
|
||||||
generateThumbnail: !thumbnailModel,
|
generateThumbnail: !thumbnailModel,
|
||||||
generatePreview: !previewModel,
|
generatePreview: !previewModel,
|
||||||
fileExt: `.${youtubeDLInfo.ext || 'mp4'}`,
|
fileExt: `.${youtubeDLInfo.ext || 'mp4'}`
|
||||||
mergeExt: youtubeDLInfo.mergeExt ? `.${youtubeDLInfo.mergeExt}` : ''
|
|
||||||
}
|
}
|
||||||
await JobQueue.Instance.createJobWithPromise({ type: 'video-import', payload })
|
await JobQueue.Instance.createJobWithPromise({ type: 'video-import', payload })
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
|
import { remove } from 'fs-extra'
|
||||||
|
import { Instance as ParseTorrent } from 'parse-torrent'
|
||||||
|
import { join } from 'path'
|
||||||
import { ResultList } from '../../shared'
|
import { ResultList } from '../../shared'
|
||||||
|
import { CONFIG } from '../initializers/config'
|
||||||
import { execPromise, execPromise2, randomBytesPromise, sha256 } from './core-utils'
|
import { execPromise, execPromise2, randomBytesPromise, sha256 } from './core-utils'
|
||||||
import { logger } from './logger'
|
import { logger } from './logger'
|
||||||
import { join } from 'path'
|
|
||||||
import { Instance as ParseTorrent } from 'parse-torrent'
|
|
||||||
import { remove } from 'fs-extra'
|
|
||||||
import { CONFIG } from '../initializers/config'
|
|
||||||
import { isVideoFileExtnameValid } from './custom-validators/videos'
|
|
||||||
|
|
||||||
function deleteFileAsync (path: string) {
|
function deleteFileAsync (path: string) {
|
||||||
remove(path)
|
remove(path)
|
||||||
|
@ -31,16 +30,11 @@ function getFormattedObjects<U, V, T extends FormattableToJSON<U, V>> (objects:
|
||||||
} as ResultList<V>
|
} as ResultList<V>
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateVideoImportTmpPath (target: string | ParseTorrent, extensionArg?: string) {
|
function generateVideoImportTmpPath (target: string | ParseTorrent, extension = '.mp4') {
|
||||||
const id = typeof target === 'string'
|
const id = typeof target === 'string'
|
||||||
? target
|
? target
|
||||||
: target.infoHash
|
: target.infoHash
|
||||||
|
|
||||||
let extension = '.mp4'
|
|
||||||
if (extensionArg && isVideoFileExtnameValid(extensionArg)) {
|
|
||||||
extension = extensionArg
|
|
||||||
}
|
|
||||||
|
|
||||||
const hash = sha256(id)
|
const hash = sha256(id)
|
||||||
return join(CONFIG.STORAGE.TMP_DIR, `${hash}-import${extension}`)
|
return join(CONFIG.STORAGE.TMP_DIR, `${hash}-import${extension}`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants'
|
|
||||||
import { logger } from './logger'
|
|
||||||
import { generateVideoImportTmpPath } from './utils'
|
|
||||||
import { getEnabledResolutions } from '../lib/video-transcoding'
|
|
||||||
import { join } from 'path'
|
|
||||||
import { peertubeTruncate, root } from './core-utils'
|
|
||||||
import { ensureDir, remove, writeFile } from 'fs-extra'
|
|
||||||
import * as request from 'request'
|
|
||||||
import { createWriteStream } from 'fs'
|
import { createWriteStream } from 'fs'
|
||||||
|
import { ensureDir, pathExists, remove, writeFile } from 'fs-extra'
|
||||||
|
import { join } from 'path'
|
||||||
|
import * as request from 'request'
|
||||||
import { CONFIG } from '@server/initializers/config'
|
import { CONFIG } from '@server/initializers/config'
|
||||||
import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
|
import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
|
||||||
import { VideoResolution } from '../../shared/models/videos'
|
import { VideoResolution } from '../../shared/models/videos'
|
||||||
|
import { CONSTRAINTS_FIELDS, VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES } from '../initializers/constants'
|
||||||
|
import { getEnabledResolutions } from '../lib/video-transcoding'
|
||||||
|
import { peertubeTruncate, root } from './core-utils'
|
||||||
|
import { isVideoFileExtnameValid } from './custom-validators/videos'
|
||||||
|
import { logger } from './logger'
|
||||||
|
import { generateVideoImportTmpPath } from './utils'
|
||||||
|
|
||||||
export type YoutubeDLInfo = {
|
export type YoutubeDLInfo = {
|
||||||
name?: string
|
name?: string
|
||||||
|
@ -21,7 +22,6 @@ export type YoutubeDLInfo = {
|
||||||
tags?: string[]
|
tags?: string[]
|
||||||
thumbnailUrl?: string
|
thumbnailUrl?: string
|
||||||
ext?: string
|
ext?: string
|
||||||
mergeExt?: string
|
|
||||||
originallyPublishedAt?: Date
|
originallyPublishedAt?: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,16 +51,6 @@ function getYoutubeDLInfo (url: string, opts?: string[]): Promise<YoutubeDLInfo>
|
||||||
youtubeDL.getInfo(url, args, processOptions, (err, info) => {
|
youtubeDL.getInfo(url, args, processOptions, (err, info) => {
|
||||||
if (err) return rej(err)
|
if (err) return rej(err)
|
||||||
if (info.is_live === true) return rej(new Error('Cannot download a live streaming.'))
|
if (info.is_live === true) return rej(new Error('Cannot download a live streaming.'))
|
||||||
if (info.format_id?.includes('+')) {
|
|
||||||
// this is a merge format and its extension will be appended
|
|
||||||
if (info.ext === 'mp4') {
|
|
||||||
info.mergeExt = 'mp4'
|
|
||||||
} else if (info.ext === 'webm') {
|
|
||||||
info.mergeExt = 'webm'
|
|
||||||
} else {
|
|
||||||
info.mergeExt = 'mkv'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const obj = buildVideoInfo(normalizeObject(info))
|
const obj = buildVideoInfo(normalizeObject(info))
|
||||||
if (obj.name && obj.name.length < CONSTRAINTS_FIELDS.VIDEOS.NAME.min) obj.name += ' video'
|
if (obj.name && obj.name.length < CONSTRAINTS_FIELDS.VIDEOS.NAME.min) obj.name += ' video'
|
||||||
|
@ -133,18 +123,15 @@ function getYoutubeDLVideoFormat () {
|
||||||
].join('/')
|
].join('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadYoutubeDLVideo (url: string, extension: string, timeout: number, mergeExtension?: string) {
|
function downloadYoutubeDLVideo (url: string, fileExt: string, timeout: number) {
|
||||||
let path = generateVideoImportTmpPath(url, extension)
|
// Leave empty the extension, youtube-dl will add it
|
||||||
|
const pathWithoutExtension = generateVideoImportTmpPath(url, '')
|
||||||
path = mergeExtension
|
|
||||||
? path.replace(new RegExp(`${extension}$`), mergeExtension)
|
|
||||||
: path
|
|
||||||
|
|
||||||
let timer
|
let timer
|
||||||
|
|
||||||
logger.info('Importing youtubeDL video %s to %s', url, path)
|
logger.info('Importing youtubeDL video %s to %s', url, pathWithoutExtension)
|
||||||
|
|
||||||
let options = [ '-f', getYoutubeDLVideoFormat(), '-o', path ]
|
let options = [ '-f', getYoutubeDLVideoFormat(), '-o', pathWithoutExtension ]
|
||||||
options = wrapWithProxyOptions(options)
|
options = wrapWithProxyOptions(options)
|
||||||
|
|
||||||
if (process.env.FFMPEG_PATH) {
|
if (process.env.FFMPEG_PATH) {
|
||||||
|
@ -156,26 +143,33 @@ function downloadYoutubeDLVideo (url: string, extension: string, timeout: number
|
||||||
return new Promise<string>((res, rej) => {
|
return new Promise<string>((res, rej) => {
|
||||||
safeGetYoutubeDL()
|
safeGetYoutubeDL()
|
||||||
.then(youtubeDL => {
|
.then(youtubeDL => {
|
||||||
youtubeDL.exec(url, options, processOptions, err => {
|
youtubeDL.exec(url, options, processOptions, async err => {
|
||||||
clearTimeout(timer)
|
clearTimeout(timer)
|
||||||
|
|
||||||
if (err) {
|
try {
|
||||||
remove(path)
|
const path = await guessVideoPathWithExtension(pathWithoutExtension, fileExt)
|
||||||
.catch(err => logger.error('Cannot delete path on YoutubeDL error.', { err }))
|
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
remove(path)
|
||||||
|
.catch(err => logger.error('Cannot delete path on YoutubeDL error.', { err }))
|
||||||
|
|
||||||
|
return rej(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res(path)
|
||||||
|
} catch (err) {
|
||||||
return rej(err)
|
return rej(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res(path)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
timer = setTimeout(() => {
|
timer = setTimeout(() => {
|
||||||
const err = new Error('YoutubeDL download timeout.')
|
const err = new Error('YoutubeDL download timeout.')
|
||||||
|
|
||||||
remove(path)
|
guessVideoPathWithExtension(pathWithoutExtension, fileExt)
|
||||||
|
.then(path => remove(path))
|
||||||
.finally(() => rej(err))
|
.finally(() => rej(err))
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
logger.error('Cannot remove %s in youtubeDL timeout.', path, { err })
|
logger.error('Cannot remove file in youtubeDL timeout.', { err })
|
||||||
return rej(err)
|
return rej(err)
|
||||||
})
|
})
|
||||||
}, timeout)
|
}, timeout)
|
||||||
|
@ -294,6 +288,22 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async function guessVideoPathWithExtension (tmpPath: string, sourceExt: string) {
|
||||||
|
if (!isVideoFileExtnameValid(sourceExt)) {
|
||||||
|
throw new Error('Invalid video extension ' + sourceExt)
|
||||||
|
}
|
||||||
|
|
||||||
|
const extensions = [ sourceExt, '.mp4', '.mkv', '.webm' ]
|
||||||
|
|
||||||
|
for (const extension of extensions) {
|
||||||
|
const path = tmpPath + extension
|
||||||
|
|
||||||
|
if (await pathExists(path)) return path
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Cannot guess path of ' + tmpPath)
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeObject (obj: any) {
|
function normalizeObject (obj: any) {
|
||||||
const newObj: any = {}
|
const newObj: any = {}
|
||||||
|
|
||||||
|
@ -324,8 +334,7 @@ function buildVideoInfo (obj: any): YoutubeDLInfo {
|
||||||
tags: getTags(obj.tags),
|
tags: getTags(obj.tags),
|
||||||
thumbnailUrl: obj.thumbnail || undefined,
|
thumbnailUrl: obj.thumbnail || undefined,
|
||||||
originallyPublishedAt: buildOriginallyPublishedAt(obj),
|
originallyPublishedAt: buildOriginallyPublishedAt(obj),
|
||||||
ext: obj.ext,
|
ext: obj.ext
|
||||||
mergeExt: obj.mergeExt
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ async function processYoutubeDLImport (job: Bull.Job, payload: VideoImportYoutub
|
||||||
}
|
}
|
||||||
|
|
||||||
return processFile(
|
return processFile(
|
||||||
() => downloadYoutubeDLVideo(videoImport.targetUrl, payload.fileExt, VIDEO_IMPORT_TIMEOUT, payload.mergeExt),
|
() => downloadYoutubeDLVideo(videoImport.targetUrl, payload.fileExt, VIDEO_IMPORT_TIMEOUT),
|
||||||
videoImport,
|
videoImport,
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
|
|
|
@ -80,9 +80,7 @@ export type VideoImportYoutubeDLPayload = {
|
||||||
|
|
||||||
generateThumbnail: boolean
|
generateThumbnail: boolean
|
||||||
generatePreview: boolean
|
generatePreview: boolean
|
||||||
|
|
||||||
fileExt?: string
|
fileExt?: string
|
||||||
mergeExt?: string
|
|
||||||
}
|
}
|
||||||
export type VideoImportTorrentPayload = {
|
export type VideoImportTorrentPayload = {
|
||||||
type: VideoImportTorrentPayloadType
|
type: VideoImportTorrentPayloadType
|
||||||
|
|
Loading…
Reference in New Issue