mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-04 10:19:35 +02:00
Add video aspect ratio in server
This commit is contained in:
parent
c75381208f
commit
b6b1aaa56f
52 changed files with 345 additions and 237 deletions
|
@ -1,20 +1,19 @@
|
|||
import { MutexInterface } from 'async-mutex'
|
||||
import { Job } from 'bullmq'
|
||||
import { ensureDir, move } from 'fs-extra/esm'
|
||||
import { stat } from 'fs/promises'
|
||||
import { basename, extname as extnameUtil, join } from 'path'
|
||||
import { join } from 'path'
|
||||
import { pick } from '@peertube/peertube-core-utils'
|
||||
import { retryTransactionWrapper } from '@server/helpers/database-utils.js'
|
||||
import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent.js'
|
||||
import { sequelizeTypescript } from '@server/initializers/database.js'
|
||||
import { MVideo, MVideoFile } from '@server/types/models/index.js'
|
||||
import { getVideoStreamDuration, getVideoStreamFPS } from '@peertube/peertube-ffmpeg'
|
||||
import { MVideo } from '@server/types/models/index.js'
|
||||
import { getVideoStreamDuration } from '@peertube/peertube-ffmpeg'
|
||||
import { CONFIG } from '../../initializers/config.js'
|
||||
import { VideoFileModel } from '../../models/video/video-file.js'
|
||||
import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist.js'
|
||||
import { updatePlaylistAfterFileChange } from '../hls.js'
|
||||
import { renameVideoFileInPlaylist, updatePlaylistAfterFileChange } from '../hls.js'
|
||||
import { generateHLSVideoFilename, getHlsResolutionPlaylistFilename } from '../paths.js'
|
||||
import { buildFileMetadata } from '../video-file.js'
|
||||
import { buildNewFile } from '../video-file.js'
|
||||
import { VideoPathManager } from '../video-path-manager.js'
|
||||
import { buildFFmpegVOD } from './shared/index.js'
|
||||
|
||||
|
@ -55,12 +54,11 @@ export function generateHlsPlaylistResolution (options: {
|
|||
|
||||
export async function onHLSVideoFileTranscoding (options: {
|
||||
video: MVideo
|
||||
videoFile: MVideoFile
|
||||
videoOutputPath: string
|
||||
m3u8OutputPath: string
|
||||
filesLockedInParent?: boolean // default false
|
||||
}) {
|
||||
const { video, videoFile, videoOutputPath, m3u8OutputPath, filesLockedInParent = false } = options
|
||||
const { video, videoOutputPath, m3u8OutputPath, filesLockedInParent = false } = options
|
||||
|
||||
// Create or update the playlist
|
||||
const playlist = await retryTransactionWrapper(() => {
|
||||
|
@ -68,7 +66,9 @@ export async function onHLSVideoFileTranscoding (options: {
|
|||
return VideoStreamingPlaylistModel.loadOrGenerate(video, transaction)
|
||||
})
|
||||
})
|
||||
videoFile.videoStreamingPlaylistId = playlist.id
|
||||
|
||||
const newVideoFile = await buildNewFile({ mode: 'hls', path: videoOutputPath })
|
||||
newVideoFile.videoStreamingPlaylistId = playlist.id
|
||||
|
||||
const mutexReleaser = !filesLockedInParent
|
||||
? await VideoPathManager.Instance.lockFiles(video.uuid)
|
||||
|
@ -77,33 +77,33 @@ export async function onHLSVideoFileTranscoding (options: {
|
|||
try {
|
||||
await video.reload()
|
||||
|
||||
const videoFilePath = VideoPathManager.Instance.getFSVideoFileOutputPath(playlist, videoFile)
|
||||
const videoFilePath = VideoPathManager.Instance.getFSVideoFileOutputPath(playlist, newVideoFile)
|
||||
await ensureDir(VideoPathManager.Instance.getFSHLSOutputPath(video))
|
||||
|
||||
// Move playlist file
|
||||
const resolutionPlaylistPath = VideoPathManager.Instance.getFSHLSOutputPath(video, basename(m3u8OutputPath))
|
||||
const resolutionPlaylistPath = VideoPathManager.Instance.getFSHLSOutputPath(
|
||||
video,
|
||||
getHlsResolutionPlaylistFilename(newVideoFile.filename)
|
||||
)
|
||||
await move(m3u8OutputPath, resolutionPlaylistPath, { overwrite: true })
|
||||
|
||||
// Move video file
|
||||
await move(videoOutputPath, videoFilePath, { overwrite: true })
|
||||
|
||||
await renameVideoFileInPlaylist(resolutionPlaylistPath, newVideoFile.filename)
|
||||
|
||||
// Update video duration if it was not set (in case of a live for example)
|
||||
if (!video.duration) {
|
||||
video.duration = await getVideoStreamDuration(videoFilePath)
|
||||
await video.save()
|
||||
}
|
||||
|
||||
const stats = await stat(videoFilePath)
|
||||
|
||||
videoFile.size = stats.size
|
||||
videoFile.fps = await getVideoStreamFPS(videoFilePath)
|
||||
videoFile.metadata = await buildFileMetadata(videoFilePath)
|
||||
|
||||
await createTorrentAndSetInfoHash(playlist, videoFile)
|
||||
await createTorrentAndSetInfoHash(playlist, newVideoFile)
|
||||
|
||||
const oldFile = await VideoFileModel.loadHLSFile({
|
||||
playlistId: playlist.id,
|
||||
fps: videoFile.fps,
|
||||
resolution: videoFile.resolution
|
||||
fps: newVideoFile.fps,
|
||||
resolution: newVideoFile.resolution
|
||||
})
|
||||
|
||||
if (oldFile) {
|
||||
|
@ -111,7 +111,7 @@ export async function onHLSVideoFileTranscoding (options: {
|
|||
await oldFile.destroy()
|
||||
}
|
||||
|
||||
const savedVideoFile = await VideoFileModel.customUpsert(videoFile, 'streaming-playlist', undefined)
|
||||
const savedVideoFile = await VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined)
|
||||
|
||||
await updatePlaylistAfterFileChange(video, playlist)
|
||||
|
||||
|
@ -171,17 +171,8 @@ async function generateHlsPlaylistCommon (options: {
|
|||
|
||||
await buildFFmpegVOD(job).transcode(transcodeOptions)
|
||||
|
||||
const newVideoFile = new VideoFileModel({
|
||||
resolution,
|
||||
extname: extnameUtil(videoFilename),
|
||||
size: 0,
|
||||
filename: videoFilename,
|
||||
fps: -1
|
||||
})
|
||||
|
||||
await onHLSVideoFileTranscoding({
|
||||
video,
|
||||
videoFile: newVideoFile,
|
||||
videoOutputPath,
|
||||
m3u8OutputPath,
|
||||
filesLockedInParent: !inputFileMutexReleaser
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
import { Job } from 'bullmq'
|
||||
import { move, remove } from 'fs-extra/esm'
|
||||
import { copyFile, stat } from 'fs/promises'
|
||||
import { copyFile } from 'fs/promises'
|
||||
import { basename, join } from 'path'
|
||||
import { FileStorage } from '@peertube/peertube-models'
|
||||
import { computeOutputFPS } from '@server/helpers/ffmpeg/index.js'
|
||||
import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent.js'
|
||||
import { VideoModel } from '@server/models/video/video.js'
|
||||
import { MVideoFile, MVideoFullLight } from '@server/types/models/index.js'
|
||||
import { ffprobePromise, getVideoStreamDuration, getVideoStreamFPS, TranscodeVODOptionsType } from '@peertube/peertube-ffmpeg'
|
||||
import { getVideoStreamDuration, TranscodeVODOptionsType } from '@peertube/peertube-ffmpeg'
|
||||
import { CONFIG } from '../../initializers/config.js'
|
||||
import { VideoFileModel } from '../../models/video/video-file.js'
|
||||
import { JobQueue } from '../job-queue/index.js'
|
||||
import { generateWebVideoFilename } from '../paths.js'
|
||||
import { buildFileMetadata } from '../video-file.js'
|
||||
import { buildNewFile } from '../video-file.js'
|
||||
import { VideoPathManager } from '../video-path-manager.js'
|
||||
import { buildFFmpegVOD } from './shared/index.js'
|
||||
import { buildOriginalFileResolution } from './transcoding-resolutions.js'
|
||||
import { buildStoryboardJobIfNeeded } from '../video-jobs.js'
|
||||
import { buildAspectRatio } from '@peertube/peertube-core-utils'
|
||||
|
||||
// Optimize the original video file and replace it. The resolution is not changed.
|
||||
export async function optimizeOriginalVideofile (options: {
|
||||
|
@ -62,19 +62,7 @@ export async function optimizeOriginalVideofile (options: {
|
|||
fps
|
||||
})
|
||||
|
||||
// Important to do this before getVideoFilename() to take in account the new filename
|
||||
inputVideoFile.resolution = resolution
|
||||
inputVideoFile.extname = newExtname
|
||||
inputVideoFile.filename = generateWebVideoFilename(resolution, newExtname)
|
||||
inputVideoFile.storage = FileStorage.FILE_SYSTEM
|
||||
|
||||
const { videoFile } = await onWebVideoFileTranscoding({
|
||||
video,
|
||||
videoFile: inputVideoFile,
|
||||
videoOutputPath
|
||||
})
|
||||
|
||||
await remove(videoInputPath)
|
||||
const { videoFile } = await onWebVideoFileTranscoding({ video, videoOutputPath, deleteWebInputVideoFile: inputVideoFile })
|
||||
|
||||
return { transcodeType, videoFile }
|
||||
})
|
||||
|
@ -104,15 +92,8 @@ export async function transcodeNewWebVideoResolution (options: {
|
|||
const file = video.getMaxQualityFile().withVideoOrPlaylist(video)
|
||||
|
||||
const result = await VideoPathManager.Instance.makeAvailableVideoFile(file, async videoInputPath => {
|
||||
const newVideoFile = new VideoFileModel({
|
||||
resolution,
|
||||
extname: newExtname,
|
||||
filename: generateWebVideoFilename(resolution, newExtname),
|
||||
size: 0,
|
||||
videoId: video.id
|
||||
})
|
||||
|
||||
const videoOutputPath = join(transcodeDirectory, newVideoFile.filename)
|
||||
const filename = generateWebVideoFilename(resolution, newExtname)
|
||||
const videoOutputPath = join(transcodeDirectory, filename)
|
||||
|
||||
const transcodeOptions = {
|
||||
type: 'video' as 'video',
|
||||
|
@ -128,7 +109,7 @@ export async function transcodeNewWebVideoResolution (options: {
|
|||
|
||||
await buildFFmpegVOD(job).transcode(transcodeOptions)
|
||||
|
||||
return onWebVideoFileTranscoding({ video, videoFile: newVideoFile, videoOutputPath })
|
||||
return onWebVideoFileTranscoding({ video, videoOutputPath })
|
||||
})
|
||||
|
||||
return result
|
||||
|
@ -188,20 +169,10 @@ export async function mergeAudioVideofile (options: {
|
|||
throw err
|
||||
}
|
||||
|
||||
// Important to do this before getVideoFilename() to take in account the new file extension
|
||||
inputVideoFile.extname = newExtname
|
||||
inputVideoFile.resolution = resolution
|
||||
inputVideoFile.filename = generateWebVideoFilename(inputVideoFile.resolution, newExtname)
|
||||
|
||||
// ffmpeg generated a new video file, so update the video duration
|
||||
// See https://trac.ffmpeg.org/ticket/5456
|
||||
video.duration = await getVideoStreamDuration(videoOutputPath)
|
||||
await video.save()
|
||||
|
||||
return onWebVideoFileTranscoding({
|
||||
await onWebVideoFileTranscoding({
|
||||
video,
|
||||
videoFile: inputVideoFile,
|
||||
videoOutputPath,
|
||||
deleteWebInputVideoFile: inputVideoFile,
|
||||
wasAudioFile: true
|
||||
})
|
||||
})
|
||||
|
@ -214,36 +185,42 @@ export async function mergeAudioVideofile (options: {
|
|||
|
||||
export async function onWebVideoFileTranscoding (options: {
|
||||
video: MVideoFullLight
|
||||
videoFile: MVideoFile
|
||||
videoOutputPath: string
|
||||
wasAudioFile?: boolean // default false
|
||||
deleteWebInputVideoFile?: MVideoFile
|
||||
}) {
|
||||
const { video, videoFile, videoOutputPath, wasAudioFile } = options
|
||||
const { video, videoOutputPath, wasAudioFile, deleteWebInputVideoFile } = options
|
||||
|
||||
const mutexReleaser = await VideoPathManager.Instance.lockFiles(video.uuid)
|
||||
|
||||
const videoFile = await buildNewFile({ mode: 'web-video', path: videoOutputPath })
|
||||
videoFile.videoId = video.id
|
||||
|
||||
try {
|
||||
await video.reload()
|
||||
|
||||
// ffmpeg generated a new video file, so update the video duration
|
||||
// See https://trac.ffmpeg.org/ticket/5456
|
||||
if (wasAudioFile) {
|
||||
video.duration = await getVideoStreamDuration(videoOutputPath)
|
||||
video.aspectRatio = buildAspectRatio({ width: videoFile.width, height: videoFile.height })
|
||||
await video.save()
|
||||
}
|
||||
|
||||
const outputPath = VideoPathManager.Instance.getFSVideoFileOutputPath(video, videoFile)
|
||||
|
||||
const stats = await stat(videoOutputPath)
|
||||
|
||||
const probe = await ffprobePromise(videoOutputPath)
|
||||
const fps = await getVideoStreamFPS(videoOutputPath, probe)
|
||||
const metadata = await buildFileMetadata(videoOutputPath, probe)
|
||||
|
||||
await move(videoOutputPath, outputPath, { overwrite: true })
|
||||
|
||||
videoFile.size = stats.size
|
||||
videoFile.fps = fps
|
||||
videoFile.metadata = metadata
|
||||
|
||||
await createTorrentAndSetInfoHash(video, videoFile)
|
||||
|
||||
const oldFile = await VideoFileModel.loadWebVideoFile({ videoId: video.id, fps: videoFile.fps, resolution: videoFile.resolution })
|
||||
if (oldFile) await video.removeWebVideoFile(oldFile)
|
||||
|
||||
if (deleteWebInputVideoFile) {
|
||||
await video.removeWebVideoFile(deleteWebInputVideoFile)
|
||||
await deleteWebInputVideoFile.destroy()
|
||||
}
|
||||
|
||||
await VideoFileModel.customUpsert(videoFile, 'video', undefined)
|
||||
video.VideoFiles = await video.$get('VideoFiles')
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue