Add getSubs to YoutubeDL video import
This commit is contained in:
parent
5def04e17f
commit
50ad0a1c16
|
@ -12,6 +12,7 @@ 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 { switchMap, map } from 'rxjs/operators'
|
||||
|
||||
@Component({
|
||||
selector: 'my-video-import-url',
|
||||
|
@ -76,31 +77,44 @@ export class VideoImportUrlComponent extends VideoSend implements OnInit, CanCom
|
|||
|
||||
this.loadingBar.start()
|
||||
|
||||
this.videoImportService.importVideoUrl(this.targetUrl, videoUpdate).subscribe(
|
||||
res => {
|
||||
this.loadingBar.complete()
|
||||
this.firstStepDone.emit(res.video.name)
|
||||
this.isImportingVideo = false
|
||||
this.hasImportedVideo = true
|
||||
this.videoImportService
|
||||
.importVideoUrl(this.targetUrl, videoUpdate)
|
||||
.pipe(
|
||||
switchMap(res => {
|
||||
return this.videoCaptionService
|
||||
.listCaptions(res.video.id)
|
||||
.pipe(
|
||||
map(result => ({ video: res.video, videoCaptions: result.data }))
|
||||
)
|
||||
})
|
||||
)
|
||||
.subscribe(
|
||||
({ video, videoCaptions }) => {
|
||||
this.loadingBar.complete()
|
||||
this.firstStepDone.emit(video.name)
|
||||
this.isImportingVideo = false
|
||||
this.hasImportedVideo = true
|
||||
|
||||
this.video = new VideoEdit(Object.assign(res.video, {
|
||||
commentsEnabled: videoUpdate.commentsEnabled,
|
||||
downloadEnabled: videoUpdate.downloadEnabled,
|
||||
support: null,
|
||||
thumbnailUrl: null,
|
||||
previewUrl: null
|
||||
}))
|
||||
this.video = new VideoEdit(Object.assign(video, {
|
||||
commentsEnabled: videoUpdate.commentsEnabled,
|
||||
downloadEnabled: videoUpdate.downloadEnabled,
|
||||
support: null,
|
||||
thumbnailUrl: null,
|
||||
previewUrl: null
|
||||
}))
|
||||
|
||||
this.hydrateFormFromVideo()
|
||||
},
|
||||
this.videoCaptions = videoCaptions
|
||||
|
||||
err => {
|
||||
this.loadingBar.complete()
|
||||
this.isImportingVideo = false
|
||||
this.firstStepError.emit()
|
||||
this.notifier.error(err.message)
|
||||
}
|
||||
)
|
||||
this.hydrateFormFromVideo()
|
||||
},
|
||||
|
||||
err => {
|
||||
this.loadingBar.complete()
|
||||
this.isImportingVideo = false
|
||||
this.firstStepError.emit()
|
||||
this.notifier.error(err.message)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
updateSecondStep () {
|
||||
|
|
|
@ -3,11 +3,13 @@ import * as magnetUtil from 'magnet-uri'
|
|||
import { auditLoggerFactory, getAuditIdFromRes, VideoImportAuditView } from '../../../helpers/audit-logger'
|
||||
import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoImportAddValidator } from '../../../middlewares'
|
||||
import { MIMETYPES } from '../../../initializers/constants'
|
||||
import { getYoutubeDLInfo, YoutubeDLInfo } from '../../../helpers/youtube-dl'
|
||||
import { getYoutubeDLInfo, YoutubeDLInfo, getYoutubeDLSubs } from '../../../helpers/youtube-dl'
|
||||
import { createReqFiles } from '../../../helpers/express-utils'
|
||||
import { logger } from '../../../helpers/logger'
|
||||
import { VideoImportCreate, VideoImportState, VideoPrivacy, VideoState } from '../../../../shared'
|
||||
import { VideoModel } from '../../../models/video/video'
|
||||
import { VideoCaptionModel } from '../../../models/video/video-caption'
|
||||
import { moveAndProcessCaptionFile } from '../../../helpers/captions-utils'
|
||||
import { getVideoActivityPubUrl } from '../../../lib/activitypub'
|
||||
import { TagModel } from '../../../models/video/tag'
|
||||
import { VideoImportModel } from '../../../models/video/video-import'
|
||||
|
@ -28,6 +30,7 @@ import {
|
|||
MThumbnail,
|
||||
MUser,
|
||||
MVideoAccountDefault,
|
||||
MVideoCaptionVideo,
|
||||
MVideoTag,
|
||||
MVideoThumbnailAccountDefault,
|
||||
MVideoWithBlacklistLight
|
||||
|
@ -136,6 +139,7 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
|
|||
const targetUrl = body.targetUrl
|
||||
const user = res.locals.oauth.token.User
|
||||
|
||||
// Get video infos
|
||||
let youtubeDLInfo: YoutubeDLInfo
|
||||
try {
|
||||
youtubeDLInfo = await getYoutubeDLInfo(targetUrl)
|
||||
|
@ -168,6 +172,29 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
|
|||
user
|
||||
})
|
||||
|
||||
|
||||
// Get video subtitles
|
||||
try {
|
||||
const subtitles = await getYoutubeDLSubs(targetUrl)
|
||||
|
||||
for (const subtitle of subtitles) {
|
||||
const videoCaption = new VideoCaptionModel({
|
||||
videoId: video.id,
|
||||
language: subtitle.language
|
||||
}) as MVideoCaptionVideo
|
||||
videoCaption.Video = video
|
||||
|
||||
// Move physical file
|
||||
await moveAndProcessCaptionFile(subtitle, videoCaption)
|
||||
|
||||
await sequelizeTypescript.transaction(async t => {
|
||||
await VideoCaptionModel.insertOrReplaceLanguage(video.id, subtitle.language, null, t)
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn('Cannot get video subtitles.', { err })
|
||||
}
|
||||
|
||||
// Create job to import the video
|
||||
const payload = {
|
||||
type: 'youtube-dl' as 'youtube-dl',
|
||||
|
|
|
@ -20,6 +20,12 @@ export type YoutubeDLInfo = {
|
|||
originallyPublishedAt?: Date
|
||||
}
|
||||
|
||||
export type YoutubeDLSubs = {
|
||||
language: string,
|
||||
filename: string,
|
||||
path: string
|
||||
}[]
|
||||
|
||||
const processOptions = {
|
||||
maxBuffer: 1024 * 1024 * 10 // 10MB
|
||||
}
|
||||
|
@ -45,6 +51,35 @@ function getYoutubeDLInfo (url: string, opts?: string[]): Promise<YoutubeDLInfo>
|
|||
})
|
||||
}
|
||||
|
||||
function getYoutubeDLSubs (url: string, opts?: object): Promise<YoutubeDLSubs> {
|
||||
return new Promise<YoutubeDLSubs>((res, rej) => {
|
||||
const cwd = CONFIG.STORAGE.TMP_DIR
|
||||
const options = opts || { all: true, format: 'vtt', cwd }
|
||||
|
||||
safeGetYoutubeDL()
|
||||
.then(youtubeDL => {
|
||||
youtubeDL.getSubs(url, options, (err, files) => {
|
||||
if (err) return rej(err)
|
||||
|
||||
const subtitles = files.reduce((acc, filename) => {
|
||||
const matched = filename.match(/\.([a-z]{2})\.(vtt|ttml)/i)
|
||||
|
||||
if (matched[1]) {
|
||||
return [...acc, {
|
||||
language: matched[1],
|
||||
path: join(cwd, filename),
|
||||
filename
|
||||
}]
|
||||
}
|
||||
}, [])
|
||||
|
||||
return res(subtitles)
|
||||
})
|
||||
})
|
||||
.catch(err => rej(err))
|
||||
})
|
||||
}
|
||||
|
||||
function downloadYoutubeDLVideo (url: string, extension: string, timeout: number) {
|
||||
const path = generateVideoImportTmpPath(url, extension)
|
||||
let timer
|
||||
|
@ -185,6 +220,7 @@ function buildOriginallyPublishedAt (obj: any) {
|
|||
export {
|
||||
updateYoutubeDLBinary,
|
||||
downloadYoutubeDLVideo,
|
||||
getYoutubeDLSubs,
|
||||
getYoutubeDLInfo,
|
||||
safeGetYoutubeDL,
|
||||
buildOriginallyPublishedAt
|
||||
|
|
Loading…
Reference in New Issue