mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-05 10:49:28 +02:00
Create and inject caption playlist in HLS master
This commit is contained in:
parent
a7be820abc
commit
6e44e7e29a
49 changed files with 1368 additions and 401 deletions
|
@ -23,11 +23,14 @@ import { VideoModel } from '@server/models/video/video.js'
|
|||
import {
|
||||
MStreamingPlaylistFiles,
|
||||
MThumbnail,
|
||||
MVideo, MVideoAP, MVideoCaption,
|
||||
MVideo,
|
||||
MVideoAP,
|
||||
MVideoCaption,
|
||||
MVideoCaptionLanguageUrl,
|
||||
MVideoChapter,
|
||||
MVideoFile,
|
||||
MVideoFullLight, MVideoLiveWithSetting,
|
||||
MVideoFullLight,
|
||||
MVideoLiveWithSetting,
|
||||
MVideoPassword
|
||||
} from '@server/types/models/index.js'
|
||||
import { MVideoSource } from '@server/types/models/video/video-source.js'
|
||||
|
@ -37,11 +40,12 @@ import { extname, join } from 'path'
|
|||
import { PassThrough, Readable } from 'stream'
|
||||
import { AbstractUserExporter, ExportResult } from './abstract-user-exporter.js'
|
||||
|
||||
export class VideosExporter extends AbstractUserExporter <VideoExportJSON> {
|
||||
|
||||
constructor (private readonly options: ConstructorParameters<typeof AbstractUserExporter<VideoExportJSON>>[0] & {
|
||||
withVideoFiles: boolean
|
||||
}) {
|
||||
export class VideosExporter extends AbstractUserExporter<VideoExportJSON> {
|
||||
constructor (
|
||||
private readonly options: ConstructorParameters<typeof AbstractUserExporter<VideoExportJSON>>[0] & {
|
||||
withVideoFiles: boolean
|
||||
}
|
||||
) {
|
||||
super(options)
|
||||
}
|
||||
|
||||
|
@ -89,10 +93,10 @@ export class VideosExporter extends AbstractUserExporter <VideoExportJSON> {
|
|||
|
||||
const live = video.isLive
|
||||
? await VideoLiveModel.loadByVideoIdWithSettings(videoId)
|
||||
: undefined;
|
||||
: undefined
|
||||
|
||||
// We already have captions, so we can set it to the video object
|
||||
(video as any).VideoCaptions = captions
|
||||
;(video as any).VideoCaptions = captions
|
||||
// Then fetch more attributes for AP serialization
|
||||
const videoAP = await video.lightAPToFullAP(undefined)
|
||||
|
||||
|
@ -320,7 +324,7 @@ export class VideosExporter extends AbstractUserExporter <VideoExportJSON> {
|
|||
const relativePathsFromJSON = {
|
||||
videoFile: null as string,
|
||||
thumbnail: null as string,
|
||||
captions: {} as { [ lang: string ]: string }
|
||||
captions: {} as { [lang: string]: string }
|
||||
}
|
||||
|
||||
if (this.options.withVideoFiles) {
|
||||
|
@ -333,9 +337,10 @@ export class VideosExporter extends AbstractUserExporter <VideoExportJSON> {
|
|||
archivePath: videoPath,
|
||||
|
||||
// Prefer using original file if possible
|
||||
readStreamFactory: () => source?.keptOriginalFilename
|
||||
? this.generateVideoSourceReadStream(source)
|
||||
: this.generateVideoFileReadStream({ video, videoFile, separatedAudioFile })
|
||||
readStreamFactory: () =>
|
||||
source?.keptOriginalFilename
|
||||
? this.generateVideoSourceReadStream(source)
|
||||
: this.generateVideoFileReadStream({ video, videoFile, separatedAudioFile })
|
||||
})
|
||||
|
||||
relativePathsFromJSON.videoFile = join(this.relativeStaticDirPath, videoPath)
|
||||
|
@ -407,7 +412,7 @@ export class VideosExporter extends AbstractUserExporter <VideoExportJSON> {
|
|||
|
||||
private async generateCaptionReadStream (caption: MVideoCaption): Promise<Readable> {
|
||||
if (caption.storage === FileStorage.FILE_SYSTEM) {
|
||||
return createReadStream(caption.getFSPath())
|
||||
return createReadStream(caption.getFSFilePath())
|
||||
}
|
||||
|
||||
const { stream } = await getCaptionReadStream({ filename: caption.filename, rangeHeader: undefined })
|
||||
|
|
|
@ -37,7 +37,7 @@ import { LocalVideoCreator, ThumbnailOptions } from '@server/lib/local-video-cre
|
|||
import { isLocalVideoFileAccepted } from '@server/lib/moderation.js'
|
||||
import { Hooks } from '@server/lib/plugins/hooks.js'
|
||||
import { isUserQuotaValid } from '@server/lib/user.js'
|
||||
import { createLocalCaption } from '@server/lib/video-captions.js'
|
||||
import { createLocalCaption, updateHLSMasterOnCaptionChange } from '@server/lib/video-captions.js'
|
||||
import { buildNextVideoState } from '@server/lib/video-state.js'
|
||||
import { VideoChannelModel } from '@server/models/video/video-channel.js'
|
||||
import { VideoModel } from '@server/models/video/video.js'
|
||||
|
@ -49,12 +49,33 @@ import { AbstractUserImporter } from './abstract-user-importer.js'
|
|||
const lTags = loggerTagsFactory('user-import')
|
||||
|
||||
type ImportObject = VideoExportJSON['videos'][0]
|
||||
type SanitizedObject = Pick<ImportObject, 'name' | 'duration' | 'channel' | 'privacy' | 'archiveFiles' | 'captions' | 'category' |
|
||||
'licence' | 'language' | 'description' | 'support' | 'nsfw' | 'isLive' | 'commentsPolicy' | 'downloadEnabled' | 'waitTranscoding' |
|
||||
'originallyPublishedAt' | 'tags' | 'live' | 'passwords' | 'source' | 'chapters'>
|
||||
|
||||
export class VideosImporter extends AbstractUserImporter <VideoExportJSON, ImportObject, SanitizedObject> {
|
||||
type SanitizedObject = Pick<
|
||||
ImportObject,
|
||||
| 'name'
|
||||
| 'duration'
|
||||
| 'channel'
|
||||
| 'privacy'
|
||||
| 'archiveFiles'
|
||||
| 'captions'
|
||||
| 'category'
|
||||
| 'licence'
|
||||
| 'language'
|
||||
| 'description'
|
||||
| 'support'
|
||||
| 'nsfw'
|
||||
| 'isLive'
|
||||
| 'commentsPolicy'
|
||||
| 'downloadEnabled'
|
||||
| 'waitTranscoding'
|
||||
| 'originallyPublishedAt'
|
||||
| 'tags'
|
||||
| 'live'
|
||||
| 'passwords'
|
||||
| 'source'
|
||||
| 'chapters'
|
||||
>
|
||||
|
||||
export class VideosImporter extends AbstractUserImporter<VideoExportJSON, ImportObject, SanitizedObject> {
|
||||
protected getImportObjects (json: VideoExportJSON) {
|
||||
return json.videos
|
||||
}
|
||||
|
@ -257,6 +278,7 @@ export class VideosImporter extends AbstractUserImporter <VideoExportJSON, Impor
|
|||
|
||||
private async importCaptions (video: MVideoFullLight, videoImportData: SanitizedObject) {
|
||||
const captionPaths: string[] = []
|
||||
let updateHLS = false
|
||||
|
||||
for (const captionImport of videoImportData.captions) {
|
||||
const relativeFilePath = videoImportData.archiveFiles?.captions?.[captionImport.language]
|
||||
|
@ -270,7 +292,7 @@ export class VideosImporter extends AbstractUserImporter <VideoExportJSON, Impor
|
|||
|
||||
if (!await this.isFileValidOrLog(absoluteFilePath, CONSTRAINTS_FIELDS.VIDEO_CAPTIONS.CAPTION_FILE.FILE_SIZE.max)) continue
|
||||
|
||||
await createLocalCaption({
|
||||
const caption = await createLocalCaption({
|
||||
video,
|
||||
language: captionImport.language,
|
||||
path: absoluteFilePath,
|
||||
|
@ -278,6 +300,12 @@ export class VideosImporter extends AbstractUserImporter <VideoExportJSON, Impor
|
|||
})
|
||||
|
||||
captionPaths.push(absoluteFilePath)
|
||||
|
||||
if (caption.m3u8Filename) updateHLS = true
|
||||
}
|
||||
|
||||
if (updateHLS && video.getHLSPlaylist()) {
|
||||
await updateHLSMasterOnCaptionChange(video, video.getHLSPlaylist())
|
||||
}
|
||||
|
||||
return captionPaths
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue