videoImportId: videoImport.id,
thumbnailUrl: youtubeDLInfo.thumbnailUrl,
downloadThumbnail: !thumbnailModel,
- downloadPreview: !previewModel
+ downloadPreview: !previewModel,
+ fileExt: youtubeDLInfo.fileExt
+ ? `.${youtubeDLInfo.fileExt}`
+ : '.mp4'
}
await JobQueue.Instance.createJobWithPromise({ type: 'video-import', payload })
import * as express from 'express'
import { extname } from 'path'
import { VideoCreate, VideoPrivacy, VideoState, VideoUpdate } from '../../../../shared'
-import { getVideoFileFPS, getVideoFileResolution, getMetadataFromFile } from '../../../helpers/ffmpeg-utils'
+import { getMetadataFromFile, getVideoFileFPS, getVideoFileResolution } from '../../../helpers/ffmpeg-utils'
import { logger } from '../../../helpers/logger'
import { auditLoggerFactory, getAuditIdFromRes, VideoAuditView } from '../../../helpers/audit-logger'
import { getFormattedObjects, getServerActor } from '../../../helpers/utils'
paginationValidator,
setDefaultPagination,
setDefaultSort,
+ videoFileMetadataGetValidator,
videosAddValidator,
videosCustomGetValidator,
videosGetValidator,
videosRemoveValidator,
videosSortValidator,
- videosUpdateValidator,
- videoFileMetadataGetValidator
+ videosUpdateValidator
} from '../../../middlewares'
import { TagModel } from '../../../models/video/tag'
import { VideoModel } from '../../../models/video/video'
import { sequelizeTypescript } from '../../../initializers/database'
import { createVideoMiniatureFromExisting, generateVideoMiniature } from '../../../lib/thumbnail'
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
-import { VideoTranscodingPayload } from '../../../lib/job-queue/handlers/video-transcoding'
import { Hooks } from '../../../lib/plugins/hooks'
import { MVideoDetails, MVideoFullLight } from '@server/typings/models'
import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
import { getVideoFilePath } from '@server/lib/video-paths'
import toInt from 'validator/lib/toInt'
+import { addOptimizeOrMergeAudioJob } from '@server/lib/videos'
const auditLogger = auditLoggerFactory('videos')
const videosRouter = express.Router()
Notifier.Instance.notifyOnNewVideoIfNeeded(videoCreated)
if (video.state === VideoState.TO_TRANSCODE) {
- // Put uuid because we don't have id auto incremented for now
- let dataInput: VideoTranscodingPayload
-
- if (videoFile.isAudio()) {
- dataInput = {
- type: 'merge-audio' as 'merge-audio',
- resolution: DEFAULT_AUDIO_RESOLUTION,
- videoUUID: videoCreated.uuid,
- isNewVideo: true
- }
- } else {
- dataInput = {
- type: 'optimize' as 'optimize',
- videoUUID: videoCreated.uuid,
- isNewVideo: true
- }
- }
-
- await JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput })
+ await addOptimizeOrMergeAudioJob(videoCreated, videoFile)
}
Hooks.runAction('action:api.video.uploaded', { video: videoCreated })
import { remove } from 'fs-extra'
import * as memoizee from 'memoizee'
import { CONFIG } from '../initializers/config'
+import { isVideoFileExtnameValid } from './custom-validators/videos'
function deleteFileAsync (path: string) {
remove(path)
return actor
}, { promise: true })
-function generateVideoImportTmpPath (target: string | ParseTorrent) {
- const id = typeof target === 'string' ? target : target.infoHash
+function generateVideoImportTmpPath (target: string | ParseTorrent, extensionArg?: string) {
+ const id = typeof target === 'string'
+ ? target
+ : target.infoHash
+
+ let extension = '.mp4'
+ if (extensionArg && isVideoFileExtnameValid(extensionArg)) {
+ extension = extensionArg
+ }
const hash = sha256(id)
- return join(CONFIG.STORAGE.TMP_DIR, hash + '-import.mp4')
+ return join(CONFIG.STORAGE.TMP_DIR, `${hash}-import${extension}`)
}
function getSecureTorrentName (originalName: string) {
nsfw?: boolean
tags?: string[]
thumbnailUrl?: string
+ fileExt?: string
originallyPublishedAt?: Date
}
})
}
-function downloadYoutubeDLVideo (url: string, timeout: number) {
- const path = generateVideoImportTmpPath(url)
+function downloadYoutubeDLVideo (url: string, extension: string, timeout: number) {
+ const path = generateVideoImportTmpPath(url, extension)
let timer
- logger.info('Importing youtubeDL video %s', url)
+ logger.info('Importing youtubeDL video %s to %s', url, path)
let options = [ '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best', '-o', path ]
options = wrapWithProxyOptions(options)
nsfw: isNSFW(obj),
tags: getTags(obj.tags),
thumbnailUrl: obj.thumbnail || undefined,
- originallyPublishedAt: buildOriginallyPublishedAt(obj)
+ originallyPublishedAt: buildOriginallyPublishedAt(obj),
+ fileExt: obj.ext
}
}
import { VideoFileModel } from '../../../models/video/video-file'
import { VIDEO_IMPORT_TIMEOUT } from '../../../initializers/constants'
import { VideoState } from '../../../../shared'
-import { JobQueue } from '../index'
import { federateVideoIfNeeded } from '../../activitypub'
import { VideoModel } from '../../../models/video/video'
import { createTorrentAndSetInfoHash, downloadWebTorrentVideo } from '../../../helpers/webtorrent'
import { MThumbnail } from '../../../typings/models/video/thumbnail'
import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/typings/models/video/video-import'
import { getVideoFilePath } from '@server/lib/video-paths'
+import { addOptimizeOrMergeAudioJob } from '@server/lib/videos'
type VideoImportYoutubeDLPayload = {
type: 'youtube-dl'
thumbnailUrl: string
downloadThumbnail: boolean
downloadPreview: boolean
+
+ fileExt?: string
}
type VideoImportTorrentPayload = {
generatePreview: false
}
- return processFile(() => downloadYoutubeDLVideo(videoImport.targetUrl, VIDEO_IMPORT_TIMEOUT), videoImport, options)
+ return processFile(() => downloadYoutubeDLVideo(videoImport.targetUrl, payload.fileExt, VIDEO_IMPORT_TIMEOUT), videoImport, options)
}
async function getVideoImportOrDie (videoImportId: number) {
// Process thumbnail
let thumbnailModel: MThumbnail
if (options.downloadThumbnail && options.thumbnailUrl) {
- thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImportWithFiles.Video, ThumbnailType.MINIATURE)
- } else if (options.generateThumbnail || options.downloadThumbnail) {
+ try {
+ thumbnailModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImportWithFiles.Video, ThumbnailType.MINIATURE)
+ } catch (err) {
+ logger.warn('Cannot generate video thumbnail %s for %s.', options.thumbnailUrl, videoImportWithFiles.Video.url, { err })
+ }
+ }
+
+ if (!thumbnailModel && (options.generateThumbnail || options.downloadThumbnail)) {
thumbnailModel = await generateVideoMiniature(videoImportWithFiles.Video, videoFile, ThumbnailType.MINIATURE)
}
// Process preview
let previewModel: MThumbnail
if (options.downloadPreview && options.thumbnailUrl) {
- previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImportWithFiles.Video, ThumbnailType.PREVIEW)
- } else if (options.generatePreview || options.downloadPreview) {
+ try {
+ previewModel = await createVideoMiniatureFromUrl(options.thumbnailUrl, videoImportWithFiles.Video, ThumbnailType.PREVIEW)
+ } catch (err) {
+ logger.warn('Cannot generate video preview %s for %s.', options.thumbnailUrl, videoImportWithFiles.Video.url, { err })
+ }
+ }
+
+ if (!previewModel && (options.generatePreview || options.downloadPreview)) {
previewModel = await generateVideoMiniature(videoImportWithFiles.Video, videoFile, ThumbnailType.PREVIEW)
}
// Create transcoding jobs?
if (video.state === VideoState.TO_TRANSCODE) {
- // Put uuid because we don't have id auto incremented for now
- const dataInput = {
- type: 'optimize' as 'optimize',
- videoUUID: videoImportUpdated.Video.uuid,
- isNewVideo: true
- }
-
- await JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput })
+ await addOptimizeOrMergeAudioJob(videoImportUpdated.Video, videoFile)
}
} catch (err) {
-import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo } from '@server/typings/models'
+import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile } from '@server/typings/models'
+import { VideoTranscodingPayload } from '@server/lib/job-queue/handlers/video-transcoding'
+import { DEFAULT_AUDIO_RESOLUTION } from '@server/initializers/constants'
+import { JobQueue } from '@server/lib/job-queue'
function extractVideo (videoOrPlaylist: MVideo | MStreamingPlaylistVideo) {
return isStreamingPlaylist(videoOrPlaylist)
: videoOrPlaylist
}
+function addOptimizeOrMergeAudioJob (video: MVideo, videoFile: MVideoFile) {
+ let dataInput: VideoTranscodingPayload
+
+ if (videoFile.isAudio()) {
+ dataInput = {
+ type: 'merge-audio' as 'merge-audio',
+ resolution: DEFAULT_AUDIO_RESOLUTION,
+ videoUUID: video.uuid,
+ isNewVideo: true
+ }
+ } else {
+ dataInput = {
+ type: 'optimize' as 'optimize',
+ videoUUID: video.uuid,
+ isNewVideo: true
+ }
+ }
+
+ return JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput })
+}
+
export {
+ addOptimizeOrMergeAudioJob,
extractVideo
}