mirror of https://github.com/Chocobozzz/PeerTube
Add thumbnail / preview generation from url on the fly (#2646)
* Add thumbnails generation on the fly to URL import * Display generated preview to import first edit * Use ternary to get type inference * Move preview/thumbnail test just after import Co-authored-by: kimsible <kimsible@users.noreply.github.com>pull/2669/head
parent
8f31261f77
commit
b1770a0af4
|
@ -11,7 +11,7 @@ import { VideoEdit } from '@app/shared/video/video-edit.model'
|
|||
import { FormValidatorService } from '@app/shared'
|
||||
import { VideoCaptionService } from '@app/shared/video-caption'
|
||||
import { VideoImportService } from '@app/shared/video-import'
|
||||
import { scrollToTop } from '@app/shared/misc/utils'
|
||||
import { scrollToTop, getAbsoluteAPIUrl } from '@app/shared/misc/utils'
|
||||
import { switchMap, map } from 'rxjs/operators'
|
||||
|
||||
@Component({
|
||||
|
@ -95,12 +95,22 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
|
|||
this.isImportingVideo = false
|
||||
this.hasImportedVideo = true
|
||||
|
||||
const absoluteAPIUrl = getAbsoluteAPIUrl()
|
||||
|
||||
const thumbnailUrl = video.thumbnailPath
|
||||
? absoluteAPIUrl + video.thumbnailPath
|
||||
: null
|
||||
|
||||
const previewUrl = video.previewPath
|
||||
? absoluteAPIUrl + video.previewPath
|
||||
: null
|
||||
|
||||
this.video = new VideoEdit(Object.assign(video, {
|
||||
commentsEnabled: videoUpdate.commentsEnabled,
|
||||
downloadEnabled: videoUpdate.downloadEnabled,
|
||||
support: null,
|
||||
thumbnailUrl: null,
|
||||
previewUrl: null
|
||||
thumbnailUrl,
|
||||
previewUrl
|
||||
}))
|
||||
|
||||
this.videoCaptions = videoCaptions
|
||||
|
@ -147,5 +157,26 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
|
|||
|
||||
private hydrateFormFromVideo () {
|
||||
this.form.patchValue(this.video.toFormPatch())
|
||||
|
||||
const objects = [
|
||||
{
|
||||
url: 'thumbnailUrl',
|
||||
name: 'thumbnailfile'
|
||||
},
|
||||
{
|
||||
url: 'previewUrl',
|
||||
name: 'previewfile'
|
||||
}
|
||||
]
|
||||
|
||||
for (const obj of objects) {
|
||||
fetch(this.video[obj.url])
|
||||
.then(response => response.blob())
|
||||
.then(data => {
|
||||
this.form.patchValue({
|
||||
[ obj.name ]: data
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import { move, readFile } from 'fs-extra'
|
|||
import { autoBlacklistVideoIfNeeded } from '../../../lib/video-blacklist'
|
||||
import { CONFIG } from '../../../initializers/config'
|
||||
import { sequelizeTypescript } from '../../../initializers/database'
|
||||
import { createVideoMiniatureFromExisting } from '../../../lib/thumbnail'
|
||||
import { createVideoMiniatureFromExisting, createVideoMiniatureFromUrl } from '../../../lib/thumbnail'
|
||||
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
|
||||
import {
|
||||
MChannelAccountDefault,
|
||||
|
@ -153,8 +153,25 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
|
|||
|
||||
const video = buildVideo(res.locals.videoChannel.id, body, youtubeDLInfo)
|
||||
|
||||
const thumbnailModel = await processThumbnail(req, video)
|
||||
const previewModel = await processPreview(req, video)
|
||||
let thumbnailModel: MThumbnail
|
||||
|
||||
// Process video thumbnail from request.files
|
||||
thumbnailModel = await processThumbnail(req, video)
|
||||
|
||||
// Process video thumbnail from url if processing from request.files failed
|
||||
if (!thumbnailModel) {
|
||||
thumbnailModel = await processThumbnailFromUrl(youtubeDLInfo.thumbnailUrl, video)
|
||||
}
|
||||
|
||||
let previewModel: MThumbnail
|
||||
|
||||
// Process video preview from request.files
|
||||
previewModel = await processPreview(req, video)
|
||||
|
||||
// Process video preview from url if processing from request.files failed
|
||||
if (!previewModel) {
|
||||
previewModel = await processPreviewFromUrl(youtubeDLInfo.thumbnailUrl, video)
|
||||
}
|
||||
|
||||
const tags = body.tags || youtubeDLInfo.tags
|
||||
const videoImportAttributes = {
|
||||
|
@ -200,9 +217,8 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
|
|||
const payload = {
|
||||
type: 'youtube-dl' as 'youtube-dl',
|
||||
videoImportId: videoImport.id,
|
||||
thumbnailUrl: youtubeDLInfo.thumbnailUrl,
|
||||
downloadThumbnail: !thumbnailModel,
|
||||
downloadPreview: !previewModel,
|
||||
generateThumbnail: !thumbnailModel,
|
||||
generatePreview: !previewModel,
|
||||
fileExt: youtubeDLInfo.fileExt
|
||||
? `.${youtubeDLInfo.fileExt}`
|
||||
: '.mp4'
|
||||
|
@ -261,6 +277,24 @@ async function processPreview (req: express.Request, video: VideoModel) {
|
|||
return undefined
|
||||
}
|
||||
|
||||
async function processThumbnailFromUrl (url: string, video: VideoModel) {
|
||||
try {
|
||||
return createVideoMiniatureFromUrl(url, video, ThumbnailType.MINIATURE)
|
||||
} catch (err) {
|
||||
logger.warn('Cannot generate video thumbnail %s for %s.', url, video.url, { err })
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
async function processPreviewFromUrl (url: string, video: VideoModel) {
|
||||
try {
|
||||
return createVideoMiniatureFromUrl(url, video, ThumbnailType.PREVIEW)
|
||||
} catch (err) {
|
||||
logger.warn('Cannot generate video preview %s for %s.', url, video.url, { err })
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function insertIntoDB (parameters: {
|
||||
video: MVideoThumbnailAccountDefault
|
||||
thumbnailModel: MThumbnail
|
||||
|
|
|
@ -16,7 +16,7 @@ import { move, remove, stat } from 'fs-extra'
|
|||
import { Notifier } from '../../notifier'
|
||||
import { CONFIG } from '../../../initializers/config'
|
||||
import { sequelizeTypescript } from '../../../initializers/database'
|
||||
import { createVideoMiniatureFromUrl, generateVideoMiniature } from '../../thumbnail'
|
||||
import { generateVideoMiniature } from '../../thumbnail'
|
||||
import { ThumbnailType } from '../../../../shared/models/videos/thumbnail.type'
|
||||
import { MThumbnail } from '../../../typings/models/video/thumbnail'
|
||||
import { MVideoImportDefault, MVideoImportDefaultFiles, MVideoImportVideo } from '@server/typings/models/video/video-import'
|
||||
|
@ -27,9 +27,8 @@ type VideoImportYoutubeDLPayload = {
|
|||
type: 'youtube-dl'
|
||||
videoImportId: number
|
||||
|
||||
thumbnailUrl: string
|
||||
downloadThumbnail: boolean
|
||||
downloadPreview: boolean
|
||||
generateThumbnail: boolean
|
||||
generatePreview: boolean
|
||||
|
||||
fileExt?: string
|
||||
}
|
||||
|
@ -64,9 +63,6 @@ async function processTorrentImport (job: Bull.Job, payload: VideoImportTorrentP
|
|||
const options = {
|
||||
videoImportId: payload.videoImportId,
|
||||
|
||||
downloadThumbnail: false,
|
||||
downloadPreview: false,
|
||||
|
||||
generateThumbnail: true,
|
||||
generatePreview: true
|
||||
}
|
||||
|
@ -84,12 +80,8 @@ async function processYoutubeDLImport (job: Bull.Job, payload: VideoImportYoutub
|
|||
const options = {
|
||||
videoImportId: videoImport.id,
|
||||
|
||||
downloadThumbnail: payload.downloadThumbnail,
|
||||
downloadPreview: payload.downloadPreview,
|
||||
thumbnailUrl: payload.thumbnailUrl,
|
||||
|
||||
generateThumbnail: false,
|
||||
generatePreview: false
|
||||
generateThumbnail: payload.generateThumbnail,
|
||||
generatePreview: payload.generatePreview
|
||||
}
|
||||
|
||||
return processFile(() => downloadYoutubeDLVideo(videoImport.targetUrl, payload.fileExt, VIDEO_IMPORT_TIMEOUT), videoImport, options)
|
||||
|
@ -107,10 +99,6 @@ async function getVideoImportOrDie (videoImportId: number) {
|
|||
type ProcessFileOptions = {
|
||||
videoImportId: number
|
||||
|
||||
downloadThumbnail: boolean
|
||||
downloadPreview: boolean
|
||||
thumbnailUrl?: string
|
||||
|
||||
generateThumbnail: boolean
|
||||
generatePreview: boolean
|
||||
}
|
||||
|
@ -155,29 +143,13 @@ async function processFile (downloader: () => Promise<string>, videoImport: MVid
|
|||
|
||||
// Process thumbnail
|
||||
let thumbnailModel: MThumbnail
|
||||
if (options.downloadThumbnail && options.thumbnailUrl) {
|
||||
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)) {
|
||||
if (options.generateThumbnail) {
|
||||
thumbnailModel = await generateVideoMiniature(videoImportWithFiles.Video, videoFile, ThumbnailType.MINIATURE)
|
||||
}
|
||||
|
||||
// Process preview
|
||||
let previewModel: MThumbnail
|
||||
if (options.downloadPreview && options.thumbnailUrl) {
|
||||
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)) {
|
||||
if (options.generatePreview) {
|
||||
previewModel = await generateVideoMiniature(videoImportWithFiles.Video, videoFile, ThumbnailType.PREVIEW)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
} from '../../../../shared/extra-utils'
|
||||
import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
|
||||
import { getMagnetURI, getMyVideoImports, getYoutubeVideoUrl, importVideo } from '../../../../shared/extra-utils/videos/video-imports'
|
||||
import { testImage } from '../../../../shared/extra-utils/miscs/miscs'
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
|
@ -118,6 +119,10 @@ describe('Test video imports', function () {
|
|||
const attributes = immutableAssign(baseAttributes, { targetUrl: getYoutubeVideoUrl() })
|
||||
const res = await importVideo(servers[0].url, servers[0].accessToken, attributes)
|
||||
expect(res.body.video.name).to.equal('small video - youtube')
|
||||
expect(res.body.video.thumbnailPath).to.equal(`/static/thumbnails/${res.body.video.uuid}.jpg`)
|
||||
expect(res.body.video.previewPath).to.equal(`/static/previews/${res.body.video.uuid}.jpg`)
|
||||
await testImage(servers[0].url, 'video_import_thumbnail', res.body.video.thumbnailPath)
|
||||
await testImage(servers[0].url, 'video_import_preview', res.body.video.previewPath)
|
||||
|
||||
const resCaptions = await listVideoCaptions(servers[0].url, res.body.video.id)
|
||||
const videoCaptions: VideoCaption[] = resCaptions.body.data
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
Loading…
Reference in New Issue