From eaf6fccbbb0de085cbaf0dd0ce574b0f45662bc0 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 13 Feb 2025 09:55:30 +0100 Subject: [PATCH] Add playlistUrl metadata in json to hls files --- .../videos/video-streaming-playlist.model.ts | 2 +- .../tests/src/shared/streaming-playlists.ts | 2 ++ packages/tests/src/shared/videos.ts | 2 ++ .../video/formatter/video-api-format.ts | 25 ++++++++++++++++--- support/doc/api/openapi.yaml | 4 +++ 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/packages/models/src/videos/video-streaming-playlist.model.ts b/packages/models/src/videos/video-streaming-playlist.model.ts index 80aa70e3c..ce96acbf3 100644 --- a/packages/models/src/videos/video-streaming-playlist.model.ts +++ b/packages/models/src/videos/video-streaming-playlist.model.ts @@ -11,5 +11,5 @@ export interface VideoStreamingPlaylist { baseUrl: string }[] - files: VideoFile[] + files: (VideoFile & { playlistUrl: string })[] } diff --git a/packages/tests/src/shared/streaming-playlists.ts b/packages/tests/src/shared/streaming-playlists.ts index 62acaebd7..ae8651a21 100644 --- a/packages/tests/src/shared/streaming-playlists.ts +++ b/packages/tests/src/shared/streaming-playlists.ts @@ -257,6 +257,8 @@ export async function completeCheckHlsPlaylist (options: { expect(file.magnetUri).to.have.lengthOf.above(2) await checkWebTorrentWorks(file.magnetUri) + expect(file.playlistUrl).to.equal(file.fileUrl.replace(/-fragmented.mp4$/, '.m3u8')) + { const nameReg = `${uuidRegex}-${file.resolution.id}` diff --git a/packages/tests/src/shared/videos.ts b/packages/tests/src/shared/videos.ts index 6a66962d3..a0949da6f 100644 --- a/packages/tests/src/shared/videos.ts +++ b/packages/tests/src/shared/videos.ts @@ -64,6 +64,8 @@ export async function completeWebVideoFilesCheck (options: { expect(file.id).to.exist expect(file.magnetUri).to.have.lengthOf.above(2) + expect((file as any).playlistUrl).to.not.exist + if (server.internalServerNumber === originServer.internalServerNumber) { if (objectStorageBaseUrl) { expect(file.storage).to.equal(FileStorage.OBJECT_STORAGE) diff --git a/server/core/models/video/formatter/video-api-format.ts b/server/core/models/video/formatter/video-api-format.ts index c90bb80fe..4557053ef 100644 --- a/server/core/models/video/formatter/video-api-format.ts +++ b/server/core/models/video/formatter/video-api-format.ts @@ -12,6 +12,7 @@ import { import { uuidToShort } from '@peertube/peertube-node-utils' import { generateMagnetUri } from '@server/helpers/webtorrent.js' import { tracer } from '@server/lib/opentelemetry/tracing.js' +import { getHlsResolutionPlaylistFilename } from '@server/lib/paths.js' import { getLocalVideoFileMetadataUrl } from '@server/lib/video-urls.js' import { VideoViewsManager } from '@server/lib/views/video-views-manager.js' import { isArray } from '../../../helpers/custom-validators/misc.js' @@ -202,18 +203,30 @@ export function streamingPlaylistsModelToFormattedJSON ( ? playlist.RedundancyVideos.map(r => ({ baseUrl: r.fileUrl })) : [], - files: videoFilesModelToFormattedJSON(video, playlist.VideoFiles) + files: videoFilesModelToFormattedJSON(video, playlist.VideoFiles, { includePlaylistUrl: true }) })) } +// --------------------------------------------------------------------------- + +export function videoFilesModelToFormattedJSON ( + video: MVideoFormattable, + videoFiles: MVideoFile[], + options?: { + includePlaylistUrl?: true + includeMagnet?: boolean + } +): (VideoFile & { playlistUrl: string })[] + export function videoFilesModelToFormattedJSON ( video: MVideoFormattable, videoFiles: MVideoFile[], options: { + includePlaylistUrl?: boolean // default false includeMagnet?: boolean // default true } = {} ): VideoFile[] { - const { includeMagnet = true } = options + const { includePlaylistUrl = false, includeMagnet = true } = options if (isArray(videoFiles) === false) return [] @@ -225,6 +238,8 @@ export function videoFilesModelToFormattedJSON ( .filter(f => !f.isLive()) .sort(sortByResolutionDesc) .map(videoFile => { + const fileUrl = videoFile.getFileUrl(video) + return { id: videoFile.id, @@ -251,7 +266,7 @@ export function videoFilesModelToFormattedJSON ( torrentUrl: videoFile.getTorrentUrl(), torrentDownloadUrl: videoFile.getTorrentDownloadUrl(), - fileUrl: videoFile.getFileUrl(video), + fileUrl, fileDownloadUrl: videoFile.getFileDownloadUrl(video), metadataUrl: videoFile.metadataUrl ?? getLocalVideoFileMetadataUrl(video, videoFile), @@ -259,6 +274,10 @@ export function videoFilesModelToFormattedJSON ( hasAudio: videoFile.hasAudio(), hasVideo: videoFile.hasVideo(), + playlistUrl: includePlaylistUrl === true + ? getHlsResolutionPlaylistFilename(fileUrl) + : undefined, + storage: video.remote ? null : videoFile.storage diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index 5e2011aea..19a119686 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml @@ -8214,6 +8214,10 @@ components: type: string description: Direct URL of the video format: url + playlistUrl: + type: string + description: Playlist URL of the file if it is owned by a playlist + format: url fileDownloadUrl: type: string description: URL endpoint that transfers the video file as an attachment (so that the browser opens a download dialog)