mirror of
https://github.com/Chocobozzz/PeerTube.git
synced 2025-10-05 10:49:28 +02:00
Separate HLS audio and video streams
Allows: * The HLS player to propose an "Audio only" resolution * The live to output an "Audio only" resolution * The live to ingest and output an "Audio only" stream This feature is under a config for VOD videos and is enabled by default for lives In the future we can imagine: * To propose multiple audio streams for a specific video * To ingest an audio only VOD and just output an audio only "video" (the player would play the audio file and PeerTube would not generate additional resolutions) This commit introduce a new way to download videos: * Add "/download/videos/generate/:videoId" endpoint where PeerTube can mux an audio only and a video only file to a mp4 container * The download client modal introduces a new default panel where the user can choose resolutions it wants to download
This commit is contained in:
parent
e77ba2dfbc
commit
816f346a60
186 changed files with 5748 additions and 2807 deletions
|
@ -3,7 +3,7 @@ import { logger } from '@server/helpers/logger.js'
|
|||
import { CONFIG } from '@server/initializers/config.js'
|
||||
import {
|
||||
JWT_TOKEN_USER_EXPORT_FILE_LIFETIME,
|
||||
STATIC_DOWNLOAD_PATHS,
|
||||
DOWNLOAD_PATHS,
|
||||
USER_EXPORT_FILE_PREFIX,
|
||||
USER_EXPORT_STATES,
|
||||
WEBSERVER
|
||||
|
@ -203,7 +203,7 @@ export class UserExportModel extends SequelizeModel<UserExportModel> {
|
|||
getFileDownloadUrl () {
|
||||
if (this.state !== UserExportState.COMPLETED) return null
|
||||
|
||||
return WEBSERVER.URL + join(STATIC_DOWNLOAD_PATHS.USER_EXPORTS, this.filename) + '?jwt=' + this.generateJWT()
|
||||
return WEBSERVER.URL + join(DOWNLOAD_PATHS.USER_EXPORTS, this.filename) + '?jwt=' + this.generateJWT()
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
|
@ -249,7 +249,10 @@ export function videoFilesModelToFormattedJSON (
|
|||
fileUrl: videoFile.getFileUrl(video),
|
||||
fileDownloadUrl: videoFile.getFileDownloadUrl(video),
|
||||
|
||||
metadataUrl: videoFile.metadataUrl ?? getLocalVideoFileMetadataUrl(video, videoFile)
|
||||
metadataUrl: videoFile.metadataUrl ?? getLocalVideoFileMetadataUrl(video, videoFile),
|
||||
|
||||
hasAudio: videoFile.hasAudio(),
|
||||
hasVideo: videoFile.hasVideo()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -91,6 +91,8 @@ export class VideoTableAttributes {
|
|||
'videoId',
|
||||
'width',
|
||||
'height',
|
||||
'formatFlags',
|
||||
'streams',
|
||||
'storage'
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
import { ActivityVideoUrlObject, FileStorage, VideoResolution, type FileStorageType } from '@peertube/peertube-models'
|
||||
import {
|
||||
ActivityVideoUrlObject,
|
||||
FileStorage,
|
||||
type FileStorageType,
|
||||
VideoFileFormatFlag,
|
||||
type VideoFileFormatFlagType,
|
||||
VideoFileStream,
|
||||
type VideoFileStreamType,
|
||||
VideoResolution
|
||||
} from '@peertube/peertube-models'
|
||||
import { logger } from '@server/helpers/logger.js'
|
||||
import { extractVideo } from '@server/helpers/video.js'
|
||||
import { CONFIG } from '@server/initializers/config.js'
|
||||
|
@ -39,10 +48,10 @@ import {
|
|||
isVideoFileSizeValid
|
||||
} from '../../helpers/custom-validators/videos.js'
|
||||
import {
|
||||
DOWNLOAD_PATHS,
|
||||
LAZY_STATIC_PATHS,
|
||||
MEMOIZE_LENGTH,
|
||||
MEMOIZE_TTL,
|
||||
STATIC_DOWNLOAD_PATHS,
|
||||
STATIC_PATHS,
|
||||
WEBSERVER
|
||||
} from '../../initializers/constants.js'
|
||||
|
@ -195,6 +204,14 @@ export class VideoFileModel extends SequelizeModel<VideoFileModel> {
|
|||
@Column
|
||||
fps: number
|
||||
|
||||
@AllowNull(false)
|
||||
@Column
|
||||
formatFlags: VideoFileFormatFlagType
|
||||
|
||||
@AllowNull(false)
|
||||
@Column
|
||||
streams: VideoFileStreamType
|
||||
|
||||
@AllowNull(true)
|
||||
@Column(DataType.JSONB)
|
||||
metadata: any
|
||||
|
@ -503,6 +520,8 @@ export class VideoFileModel extends SequelizeModel<VideoFileModel> {
|
|||
return extractVideo(this.getVideoOrStreamingPlaylist())
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
isAudio () {
|
||||
return this.resolution === VideoResolution.H_NOVIDEO
|
||||
}
|
||||
|
@ -515,6 +534,14 @@ export class VideoFileModel extends SequelizeModel<VideoFileModel> {
|
|||
return !!this.videoStreamingPlaylistId
|
||||
}
|
||||
|
||||
hasAudio () {
|
||||
return (this.streams & VideoFileStream.AUDIO) === VideoFileStream.AUDIO
|
||||
}
|
||||
|
||||
hasVideo () {
|
||||
return (this.streams & VideoFileStream.VIDEO) === VideoFileStream.VIDEO
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getObjectStorageUrl (video: MVideo) {
|
||||
|
@ -583,8 +610,8 @@ export class VideoFileModel extends SequelizeModel<VideoFileModel> {
|
|||
|
||||
getFileDownloadUrl (video: MVideoWithHost) {
|
||||
const path = this.isHLS()
|
||||
? join(STATIC_DOWNLOAD_PATHS.HLS_VIDEOS, `${video.uuid}-${this.resolution}-fragmented${this.extname}`)
|
||||
: join(STATIC_DOWNLOAD_PATHS.VIDEOS, `${video.uuid}-${this.resolution}${this.extname}`)
|
||||
? join(DOWNLOAD_PATHS.HLS_VIDEOS, `${video.uuid}-${this.resolution}-fragmented${this.extname}`)
|
||||
: join(DOWNLOAD_PATHS.WEB_VIDEOS, `${video.uuid}-${this.resolution}${this.extname}`)
|
||||
|
||||
if (video.isOwned()) return WEBSERVER.URL + path
|
||||
|
||||
|
@ -614,7 +641,7 @@ export class VideoFileModel extends SequelizeModel<VideoFileModel> {
|
|||
getTorrentDownloadUrl () {
|
||||
if (!this.torrentFilename) return null
|
||||
|
||||
return WEBSERVER.URL + join(STATIC_DOWNLOAD_PATHS.TORRENTS, this.torrentFilename)
|
||||
return WEBSERVER.URL + join(DOWNLOAD_PATHS.TORRENTS, this.torrentFilename)
|
||||
}
|
||||
|
||||
removeTorrent () {
|
||||
|
@ -645,6 +672,40 @@ export class VideoFileModel extends SequelizeModel<VideoFileModel> {
|
|||
toActivityPubObject (this: MVideoFile, video: MVideo): ActivityVideoUrlObject {
|
||||
const mimeType = getVideoFileMimeType(this.extname, false)
|
||||
|
||||
const attachment: ActivityVideoUrlObject['attachment'] = []
|
||||
|
||||
if (this.hasAudio()) {
|
||||
attachment.push({
|
||||
type: 'PropertyValue',
|
||||
name: 'ffprobe_codec_type',
|
||||
value: 'audio'
|
||||
})
|
||||
}
|
||||
|
||||
if (this.hasVideo()) {
|
||||
attachment.push({
|
||||
type: 'PropertyValue',
|
||||
name: 'ffprobe_codec_type',
|
||||
value: 'video'
|
||||
})
|
||||
}
|
||||
|
||||
if (this.formatFlags & VideoFileFormatFlag.FRAGMENTED) {
|
||||
attachment.push({
|
||||
type: 'PropertyValue',
|
||||
name: 'peertube_format_flag',
|
||||
value: 'fragmented'
|
||||
})
|
||||
}
|
||||
|
||||
if (this.formatFlags & VideoFileFormatFlag.WEB_VIDEO) {
|
||||
attachment.push({
|
||||
type: 'PropertyValue',
|
||||
name: 'peertube_format_flag',
|
||||
value: 'web-video'
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'Link',
|
||||
mediaType: mimeType as ActivityVideoUrlObject['mediaType'],
|
||||
|
@ -652,7 +713,8 @@ export class VideoFileModel extends SequelizeModel<VideoFileModel> {
|
|||
height: this.height || this.resolution,
|
||||
width: this.width,
|
||||
size: this.size,
|
||||
fps: this.fps
|
||||
fps: this.fps,
|
||||
attachment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { type FileStorageType, type VideoSource } from '@peertube/peertube-models'
|
||||
import { STATIC_DOWNLOAD_PATHS, WEBSERVER } from '@server/initializers/constants.js'
|
||||
import { ActivityVideoUrlObject, type FileStorageType, type VideoSource } from '@peertube/peertube-models'
|
||||
import { DOWNLOAD_PATHS, WEBSERVER } from '@server/initializers/constants.js'
|
||||
import { getVideoFileMimeType } from '@server/lib/video-file.js'
|
||||
import { MVideoSource } from '@server/types/models/video/video-source.js'
|
||||
import { join } from 'path'
|
||||
import { extname, join } from 'path'
|
||||
import { Transaction } from 'sequelize'
|
||||
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript'
|
||||
import { SequelizeModel, doesExist, getSort } from '../shared/index.js'
|
||||
|
@ -118,10 +119,25 @@ export class VideoSourceModel extends SequelizeModel<VideoSourceModel> {
|
|||
getFileDownloadUrl () {
|
||||
if (!this.keptOriginalFilename) return null
|
||||
|
||||
return WEBSERVER.URL + join(STATIC_DOWNLOAD_PATHS.ORIGINAL_VIDEO_FILE, this.keptOriginalFilename)
|
||||
return WEBSERVER.URL + join(DOWNLOAD_PATHS.ORIGINAL_VIDEO_FILE, this.keptOriginalFilename)
|
||||
}
|
||||
|
||||
toFormattedJSON (): VideoSource {
|
||||
toActivityPubObject (this: MVideoSource): ActivityVideoUrlObject {
|
||||
const mimeType = getVideoFileMimeType(extname(this.inputFilename), false)
|
||||
|
||||
return {
|
||||
type: 'Link',
|
||||
mediaType: mimeType as ActivityVideoUrlObject['mediaType'],
|
||||
href: null,
|
||||
height: this.height || this.resolution,
|
||||
width: this.width,
|
||||
size: this.size,
|
||||
fps: this.fps,
|
||||
attachment: []
|
||||
}
|
||||
}
|
||||
|
||||
toFormattedJSON (this: MVideoSource): VideoSource {
|
||||
return {
|
||||
filename: this.inputFilename,
|
||||
inputFilename: this.inputFilename,
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import {
|
||||
FileStorage,
|
||||
VideoResolution,
|
||||
VideoStreamingPlaylistType,
|
||||
type FileStorageType,
|
||||
type VideoStreamingPlaylistType_Type
|
||||
} from '@peertube/peertube-models'
|
||||
import { sha1 } from '@peertube/peertube-node-utils'
|
||||
import { logger } from '@server/helpers/logger.js'
|
||||
import { CONFIG } from '@server/initializers/config.js'
|
||||
import { getHLSPrivateFileUrl, getObjectStoragePublicFileUrl } from '@server/lib/object-storage/index.js'
|
||||
import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '@server/lib/paths.js'
|
||||
import { isVideoInPrivateDirectory } from '@server/lib/video-privacy.js'
|
||||
import { VideoFileModel } from '@server/models/video/video-file.js'
|
||||
import { MStreamingPlaylist, MStreamingPlaylistFilesVideo, MVideo } from '@server/types/models/index.js'
|
||||
import { MStreamingPlaylist, MStreamingPlaylistFiles, MStreamingPlaylistFilesVideo, MVideo } from '@server/types/models/index.js'
|
||||
import memoizee from 'memoizee'
|
||||
import { join } from 'path'
|
||||
import { Op, Transaction } from 'sequelize'
|
||||
|
@ -147,6 +149,8 @@ export class VideoStreamingPlaylistModel extends SequelizeModel<VideoStreamingPl
|
|||
hashes.push(sha1(`${P2P_MEDIA_LOADER_PEER_VERSION}${playlistUrl}+V${i}`))
|
||||
}
|
||||
|
||||
logger.debug('Assigned P2P Media Loader info hashes', { playlistUrl, hashes })
|
||||
|
||||
return hashes
|
||||
}
|
||||
|
||||
|
@ -292,6 +296,26 @@ export class VideoStreamingPlaylistModel extends SequelizeModel<VideoStreamingPl
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
hasAudioAndVideoSplitted (this: MStreamingPlaylistFiles) {
|
||||
// We need at least 2 files to have audio and video splitted
|
||||
if (this.VideoFiles.length === 1) return false
|
||||
|
||||
let hasAudio = false
|
||||
let hasVideo = false
|
||||
|
||||
for (const file of this.VideoFiles) {
|
||||
// File contains both streams: audio and video is not splitted
|
||||
if (file.hasAudio() && file.hasVideo()) return false
|
||||
|
||||
if (file.resolution === VideoResolution.H_NOVIDEO) hasAudio = true
|
||||
else if (file.hasVideo()) hasVideo = true
|
||||
|
||||
if (hasVideo && hasAudio) return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
getStringType () {
|
||||
if (this.type === VideoStreamingPlaylistType.HLS) return 'hls'
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { buildVideoEmbedPath, buildVideoWatchPath, maxBy, minBy, pick, wait } from '@peertube/peertube-core-utils'
|
||||
import { ffprobePromise, getAudioStream, getVideoStreamDimensionsInfo, getVideoStreamFPS, hasAudioStream } from '@peertube/peertube-ffmpeg'
|
||||
import { buildVideoEmbedPath, buildVideoWatchPath, maxBy, pick, sortBy, wait } from '@peertube/peertube-core-utils'
|
||||
import {
|
||||
FileStorage,
|
||||
ResultList,
|
||||
|
@ -8,6 +7,8 @@ import {
|
|||
Video,
|
||||
VideoDetails,
|
||||
VideoFile,
|
||||
VideoFileStream,
|
||||
VideoFileStreamType,
|
||||
VideoInclude,
|
||||
VideoIncludeType,
|
||||
VideoObject,
|
||||
|
@ -73,7 +74,7 @@ import {
|
|||
isVideoStateValid,
|
||||
isVideoSupportValid
|
||||
} from '../../helpers/custom-validators/videos.js'
|
||||
import { logger } from '../../helpers/logger.js'
|
||||
import { logger, loggerTagsFactory } from '../../helpers/logger.js'
|
||||
import { CONFIG } from '../../initializers/config.js'
|
||||
import { ACTIVITY_PUB, API_VERSION, CONSTRAINTS_FIELDS, WEBSERVER } from '../../initializers/constants.js'
|
||||
import { sendDeleteVideo } from '../../lib/activitypub/send/index.js'
|
||||
|
@ -162,6 +163,8 @@ import { VideoSourceModel } from './video-source.js'
|
|||
import { VideoStreamingPlaylistModel } from './video-streaming-playlist.js'
|
||||
import { VideoTagModel } from './video-tag.js'
|
||||
|
||||
const lTags = loggerTagsFactory('video')
|
||||
|
||||
export enum ScopeNames {
|
||||
FOR_API = 'FOR_API',
|
||||
WITH_ACCOUNT_DETAILS = 'WITH_ACCOUNT_DETAILS',
|
||||
|
@ -1735,8 +1738,43 @@ export class VideoModel extends SequelizeModel<VideoModel> {
|
|||
return this.VideoChannel.Account.Actor.Server?.isBlocked() || this.VideoChannel.Account.isBlocked()
|
||||
}
|
||||
|
||||
getQualityFileBy<T extends MVideoWithFile> (this: T, fun: (files: MVideoFile[], property: 'resolution') => MVideoFile) {
|
||||
const files = this.getAllFiles()
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getMaxQualityAudioAndVideoFiles <T extends MVideoWithFile> (this: T) {
|
||||
const videoFile = this.getMaxQualityFile(VideoFileStream.VIDEO)
|
||||
if (!videoFile) return { videoFile: undefined }
|
||||
|
||||
// File also has audio, we can return it
|
||||
if (videoFile.hasAudio()) return { videoFile }
|
||||
|
||||
const separatedAudioFile = this.getMaxQualityFile(VideoFileStream.AUDIO)
|
||||
if (!separatedAudioFile) return { videoFile }
|
||||
|
||||
return { videoFile, separatedAudioFile }
|
||||
}
|
||||
|
||||
getMaxQualityFile<T extends MVideoWithFile> (
|
||||
this: T,
|
||||
streamFilter: VideoFileStreamType
|
||||
): MVideoFileVideo | MVideoFileStreamingPlaylistVideo {
|
||||
return this.getQualityFileBy(streamFilter, maxBy)
|
||||
}
|
||||
|
||||
getMaxQualityBytes <T extends MVideoWithFile> (this: T) {
|
||||
const { videoFile, separatedAudioFile } = this.getMaxQualityAudioAndVideoFiles()
|
||||
|
||||
let size = videoFile.size
|
||||
if (separatedAudioFile) size += separatedAudioFile.size
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
getQualityFileBy<T extends MVideoWithFile> (
|
||||
this: T,
|
||||
streamFilter: VideoFileStreamType,
|
||||
fun: (files: MVideoFile[], property: 'resolution') => MVideoFile
|
||||
) {
|
||||
const files = this.getAllFiles().filter(f => f.streams & streamFilter)
|
||||
const file = fun(files, 'resolution')
|
||||
if (!file) return undefined
|
||||
|
||||
|
@ -1753,27 +1791,40 @@ export class VideoModel extends SequelizeModel<VideoModel> {
|
|||
throw new Error('File is not associated to a video of a playlist')
|
||||
}
|
||||
|
||||
getMaxQualityFile<T extends MVideoWithFile> (this: T): MVideoFileVideo | MVideoFileStreamingPlaylistVideo {
|
||||
return this.getQualityFileBy(maxBy)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getMaxFPS () {
|
||||
return this.getMaxQualityFile(VideoFileStream.VIDEO).fps
|
||||
}
|
||||
|
||||
getMinQualityFile<T extends MVideoWithFile> (this: T): MVideoFileVideo | MVideoFileStreamingPlaylistVideo {
|
||||
return this.getQualityFileBy(minBy)
|
||||
getMaxResolution () {
|
||||
return this.getMaxQualityFile(VideoFileStream.VIDEO).resolution
|
||||
}
|
||||
|
||||
getWebVideoFile<T extends MVideoWithFile> (this: T, resolution: number): MVideoFileVideo {
|
||||
hasAudio () {
|
||||
return !!this.getMaxQualityFile(VideoFileStream.AUDIO)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
getWebVideoFileMinResolution<T extends MVideoWithFile> (this: T, resolution: number): MVideoFileVideo {
|
||||
if (Array.isArray(this.VideoFiles) === false) return undefined
|
||||
|
||||
const file = this.VideoFiles.find(f => f.resolution === resolution)
|
||||
if (!file) return undefined
|
||||
for (const file of sortBy(this.VideoFiles, 'resolution')) {
|
||||
if (file.resolution < resolution) continue
|
||||
|
||||
return Object.assign(file, { Video: this })
|
||||
return Object.assign(file, { Video: this })
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
hasWebVideoFiles () {
|
||||
return Array.isArray(this.VideoFiles) === true && this.VideoFiles.length !== 0
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async addAndSaveThumbnail (thumbnail: MThumbnail, transaction?: Transaction) {
|
||||
thumbnail.videoId = this.id
|
||||
|
||||
|
@ -1787,21 +1838,21 @@ export class VideoModel extends SequelizeModel<VideoModel> {
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
hasMiniature () {
|
||||
hasMiniature (this: MVideoThumbnail) {
|
||||
return !!this.getMiniature()
|
||||
}
|
||||
|
||||
getMiniature () {
|
||||
getMiniature (this: MVideoThumbnail) {
|
||||
if (Array.isArray(this.Thumbnails) === false) return undefined
|
||||
|
||||
return this.Thumbnails.find(t => t.type === ThumbnailType.MINIATURE)
|
||||
}
|
||||
|
||||
hasPreview () {
|
||||
hasPreview (this: MVideoThumbnail) {
|
||||
return !!this.getPreview()
|
||||
}
|
||||
|
||||
getPreview () {
|
||||
getPreview (this: MVideoThumbnail) {
|
||||
if (Array.isArray(this.Thumbnails) === false) return undefined
|
||||
|
||||
return this.Thumbnails.find(t => t.type === ThumbnailType.PREVIEW)
|
||||
|
@ -1930,27 +1981,6 @@ export class VideoModel extends SequelizeModel<VideoModel> {
|
|||
return files
|
||||
}
|
||||
|
||||
probeMaxQualityFile () {
|
||||
const file = this.getMaxQualityFile()
|
||||
const videoOrPlaylist = file.getVideoOrStreamingPlaylist()
|
||||
|
||||
return VideoPathManager.Instance.makeAvailableVideoFile(file.withVideoOrPlaylist(videoOrPlaylist), async originalFilePath => {
|
||||
const probe = await ffprobePromise(originalFilePath)
|
||||
|
||||
const { audioStream } = await getAudioStream(originalFilePath, probe)
|
||||
const hasAudio = await hasAudioStream(originalFilePath, probe)
|
||||
const fps = await getVideoStreamFPS(originalFilePath, probe)
|
||||
|
||||
return {
|
||||
audioStream,
|
||||
hasAudio,
|
||||
fps,
|
||||
|
||||
...await getVideoStreamDimensionsInfo(originalFilePath, probe)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
getDescriptionAPIPath () {
|
||||
return `/api/${API_VERSION}/videos/${this.uuid}/description`
|
||||
}
|
||||
|
@ -1977,6 +2007,8 @@ export class VideoModel extends SequelizeModel<VideoModel> {
|
|||
.concat(toAdd)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
removeWebVideoFile (videoFile: MVideoFile, isRedundancy = false) {
|
||||
const filePath = isRedundancy
|
||||
? VideoPathManager.Instance.getFSRedundancyVideoFilePath(this, videoFile)
|
||||
|
@ -1989,6 +2021,8 @@ export class VideoModel extends SequelizeModel<VideoModel> {
|
|||
promises.push(removeWebVideoObjectStorage(videoFile))
|
||||
}
|
||||
|
||||
logger.debug(`Removing files associated to web video ${videoFile.filename}`, { videoFile, isRedundancy, ...lTags(this.uuid) })
|
||||
|
||||
return Promise.all(promises)
|
||||
}
|
||||
|
||||
|
@ -2029,6 +2063,11 @@ export class VideoModel extends SequelizeModel<VideoModel> {
|
|||
await removeHLSObjectStorage(streamingPlaylist.withVideo(this))
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
`Removing files associated to streaming playlist of video ${this.url}`,
|
||||
{ streamingPlaylist, isRedundancy, ...lTags(this.uuid) }
|
||||
)
|
||||
}
|
||||
|
||||
async removeStreamingPlaylistVideoFile (streamingPlaylist: MStreamingPlaylist, videoFile: MVideoFile) {
|
||||
|
@ -2043,6 +2082,11 @@ export class VideoModel extends SequelizeModel<VideoModel> {
|
|||
await removeHLSFileObjectStorageByFilename(streamingPlaylist.withVideo(this), videoFile.filename)
|
||||
await removeHLSFileObjectStorageByFilename(streamingPlaylist.withVideo(this), resolutionFilename)
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
`Removing files associated to streaming playlist video file ${videoFile.filename}`,
|
||||
{ streamingPlaylist, ...lTags(this.uuid) }
|
||||
)
|
||||
}
|
||||
|
||||
async removeStreamingPlaylistFile (streamingPlaylist: MStreamingPlaylist, filename: string) {
|
||||
|
@ -2052,6 +2096,8 @@ export class VideoModel extends SequelizeModel<VideoModel> {
|
|||
if (streamingPlaylist.storage === FileStorage.OBJECT_STORAGE) {
|
||||
await removeHLSFileObjectStorageByFilename(streamingPlaylist.withVideo(this), filename)
|
||||
}
|
||||
|
||||
logger.debug(`Removing streaming playlist file ${filename}`, lTags(this.uuid))
|
||||
}
|
||||
|
||||
async removeOriginalFile (videoSource: MVideoSource) {
|
||||
|
@ -2063,8 +2109,12 @@ export class VideoModel extends SequelizeModel<VideoModel> {
|
|||
if (videoSource.storage === FileStorage.OBJECT_STORAGE) {
|
||||
await removeOriginalFileObjectStorage(videoSource)
|
||||
}
|
||||
|
||||
logger.debug(`Removing original video file ${videoSource.keptOriginalFilename}`, lTags(this.uuid))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
isOutdated () {
|
||||
if (this.isOwned()) return false
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue