diff options
author | Chocobozzz <me@florianbigard.com> | 2021-07-23 11:20:00 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2021-07-26 11:29:31 +0200 |
commit | 764b1a14fc494f2cfd7ea590d2f07b01df65c7ad (patch) | |
tree | 198ca5f242c63a205a05fa4cfd6d063277c541fd /server/lib | |
parent | 83903cb65d531a6b6b91715387493ba8312b264d (diff) | |
download | PeerTube-764b1a14fc494f2cfd7ea590d2f07b01df65c7ad.tar.gz PeerTube-764b1a14fc494f2cfd7ea590d2f07b01df65c7ad.tar.zst PeerTube-764b1a14fc494f2cfd7ea590d2f07b01df65c7ad.zip |
Use random names for VOD HLS playlists
Diffstat (limited to 'server/lib')
-rw-r--r-- | server/lib/activitypub/videos/shared/abstract-builder.ts | 12 | ||||
-rw-r--r-- | server/lib/activitypub/videos/shared/object-to-model-attributes.ts | 13 | ||||
-rw-r--r-- | server/lib/hls.ts | 35 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-file-import.ts | 3 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-live-ending.ts | 15 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/video-transcoding.ts | 3 | ||||
-rw-r--r-- | server/lib/live/live-manager.ts | 26 | ||||
-rw-r--r-- | server/lib/live/shared/muxing-session.ts | 7 | ||||
-rw-r--r-- | server/lib/schedulers/videos-redundancy-scheduler.ts | 7 | ||||
-rw-r--r-- | server/lib/transcoding/video-transcoding.ts | 71 | ||||
-rw-r--r-- | server/lib/video-paths.ts | 29 | ||||
-rw-r--r-- | server/lib/video.ts | 4 |
12 files changed, 131 insertions, 94 deletions
diff --git a/server/lib/activitypub/videos/shared/abstract-builder.ts b/server/lib/activitypub/videos/shared/abstract-builder.ts index e89c94bcd..f995fe637 100644 --- a/server/lib/activitypub/videos/shared/abstract-builder.ts +++ b/server/lib/activitypub/videos/shared/abstract-builder.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import { Transaction } from 'sequelize/types' | 1 | import { Transaction } from 'sequelize/types' |
2 | import { checkUrlsSameHost } from '@server/helpers/activitypub' | 2 | import { checkUrlsSameHost } from '@server/helpers/activitypub' |
3 | import { deleteNonExistingModels } from '@server/helpers/database-utils' | 3 | import { deleteAllModels, filterNonExistingModels } from '@server/helpers/database-utils' |
4 | import { logger, LoggerTagsFn } from '@server/helpers/logger' | 4 | import { logger, LoggerTagsFn } from '@server/helpers/logger' |
5 | import { updatePlaceholderThumbnail, updateVideoMiniatureFromUrl } from '@server/lib/thumbnail' | 5 | import { updatePlaceholderThumbnail, updateVideoMiniatureFromUrl } from '@server/lib/thumbnail' |
6 | import { setVideoTags } from '@server/lib/video' | 6 | import { setVideoTags } from '@server/lib/video' |
@@ -111,8 +111,7 @@ export abstract class APVideoAbstractBuilder { | |||
111 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) | 111 | const newVideoFiles = videoFileAttributes.map(a => new VideoFileModel(a)) |
112 | 112 | ||
113 | // Remove video files that do not exist anymore | 113 | // Remove video files that do not exist anymore |
114 | const destroyTasks = deleteNonExistingModels(video.VideoFiles || [], newVideoFiles, t) | 114 | await deleteAllModels(filterNonExistingModels(video.VideoFiles || [], newVideoFiles), t) |
115 | await Promise.all(destroyTasks) | ||
116 | 115 | ||
117 | // Update or add other one | 116 | // Update or add other one |
118 | const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'video', t)) | 117 | const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'video', t)) |
@@ -124,13 +123,11 @@ export abstract class APVideoAbstractBuilder { | |||
124 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) | 123 | const newStreamingPlaylists = streamingPlaylistAttributes.map(a => new VideoStreamingPlaylistModel(a)) |
125 | 124 | ||
126 | // Remove video playlists that do not exist anymore | 125 | // Remove video playlists that do not exist anymore |
127 | const destroyTasks = deleteNonExistingModels(video.VideoStreamingPlaylists || [], newStreamingPlaylists, t) | 126 | await deleteAllModels(filterNonExistingModels(video.VideoStreamingPlaylists || [], newStreamingPlaylists), t) |
128 | await Promise.all(destroyTasks) | ||
129 | 127 | ||
130 | video.VideoStreamingPlaylists = [] | 128 | video.VideoStreamingPlaylists = [] |
131 | 129 | ||
132 | for (const playlistAttributes of streamingPlaylistAttributes) { | 130 | for (const playlistAttributes of streamingPlaylistAttributes) { |
133 | |||
134 | const streamingPlaylistModel = await this.insertOrReplaceStreamingPlaylist(playlistAttributes, t) | 131 | const streamingPlaylistModel = await this.insertOrReplaceStreamingPlaylist(playlistAttributes, t) |
135 | streamingPlaylistModel.Video = video | 132 | streamingPlaylistModel.Video = video |
136 | 133 | ||
@@ -163,8 +160,7 @@ export abstract class APVideoAbstractBuilder { | |||
163 | 160 | ||
164 | const newVideoFiles: MVideoFile[] = getFileAttributesFromUrl(playlistModel, tagObjects).map(a => new VideoFileModel(a)) | 161 | const newVideoFiles: MVideoFile[] = getFileAttributesFromUrl(playlistModel, tagObjects).map(a => new VideoFileModel(a)) |
165 | 162 | ||
166 | const destroyTasks = deleteNonExistingModels(oldStreamingPlaylistFiles, newVideoFiles, t) | 163 | await deleteAllModels(filterNonExistingModels(oldStreamingPlaylistFiles, newVideoFiles), t) |
167 | await Promise.all(destroyTasks) | ||
168 | 164 | ||
169 | // Update or add other one | 165 | // Update or add other one |
170 | const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'streaming-playlist', t)) | 166 | const upsertTasks = newVideoFiles.map(f => VideoFileModel.customUpsert(f, 'streaming-playlist', t)) |
diff --git a/server/lib/activitypub/videos/shared/object-to-model-attributes.ts b/server/lib/activitypub/videos/shared/object-to-model-attributes.ts index 85548428c..1fa16295d 100644 --- a/server/lib/activitypub/videos/shared/object-to-model-attributes.ts +++ b/server/lib/activitypub/videos/shared/object-to-model-attributes.ts | |||
@@ -7,10 +7,11 @@ import { logger } from '@server/helpers/logger' | |||
7 | import { getExtFromMimetype } from '@server/helpers/video' | 7 | import { getExtFromMimetype } from '@server/helpers/video' |
8 | import { ACTIVITY_PUB, MIMETYPES, P2P_MEDIA_LOADER_PEER_VERSION, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '@server/initializers/constants' | 8 | import { ACTIVITY_PUB, MIMETYPES, P2P_MEDIA_LOADER_PEER_VERSION, PREVIEWS_SIZE, THUMBNAILS_SIZE } from '@server/initializers/constants' |
9 | import { generateTorrentFileName } from '@server/lib/video-paths' | 9 | import { generateTorrentFileName } from '@server/lib/video-paths' |
10 | import { VideoCaptionModel } from '@server/models/video/video-caption' | ||
10 | import { VideoFileModel } from '@server/models/video/video-file' | 11 | import { VideoFileModel } from '@server/models/video/video-file' |
11 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' | 12 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' |
12 | import { FilteredModelAttributes } from '@server/types' | 13 | import { FilteredModelAttributes } from '@server/types' |
13 | import { MChannelId, MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoId } from '@server/types/models' | 14 | import { isStreamingPlaylist, MChannelId, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoId } from '@server/types/models' |
14 | import { | 15 | import { |
15 | ActivityHashTagObject, | 16 | ActivityHashTagObject, |
16 | ActivityMagnetUrlObject, | 17 | ActivityMagnetUrlObject, |
@@ -23,7 +24,6 @@ import { | |||
23 | VideoPrivacy, | 24 | VideoPrivacy, |
24 | VideoStreamingPlaylistType | 25 | VideoStreamingPlaylistType |
25 | } from '@shared/models' | 26 | } from '@shared/models' |
26 | import { VideoCaptionModel } from '@server/models/video/video-caption' | ||
27 | 27 | ||
28 | function getThumbnailFromIcons (videoObject: VideoObject) { | 28 | function getThumbnailFromIcons (videoObject: VideoObject) { |
29 | let validIcons = videoObject.icon.filter(i => i.width > THUMBNAILS_SIZE.minWidth) | 29 | let validIcons = videoObject.icon.filter(i => i.width > THUMBNAILS_SIZE.minWidth) |
@@ -80,8 +80,8 @@ function getFileAttributesFromUrl ( | |||
80 | 80 | ||
81 | const extname = getExtFromMimetype(MIMETYPES.VIDEO.MIMETYPE_EXT, fileUrl.mediaType) | 81 | const extname = getExtFromMimetype(MIMETYPES.VIDEO.MIMETYPE_EXT, fileUrl.mediaType) |
82 | const resolution = fileUrl.height | 82 | const resolution = fileUrl.height |
83 | const videoId = (videoOrPlaylist as MStreamingPlaylist).playlistUrl ? null : videoOrPlaylist.id | 83 | const videoId = isStreamingPlaylist(videoOrPlaylist) ? null : videoOrPlaylist.id |
84 | const videoStreamingPlaylistId = (videoOrPlaylist as MStreamingPlaylist).playlistUrl ? videoOrPlaylist.id : null | 84 | const videoStreamingPlaylistId = isStreamingPlaylist(videoOrPlaylist) ? videoOrPlaylist.id : null |
85 | 85 | ||
86 | const attribute = { | 86 | const attribute = { |
87 | extname, | 87 | extname, |
@@ -130,8 +130,13 @@ function getStreamingPlaylistAttributesFromObject (video: MVideoId, videoObject: | |||
130 | 130 | ||
131 | const attribute = { | 131 | const attribute = { |
132 | type: VideoStreamingPlaylistType.HLS, | 132 | type: VideoStreamingPlaylistType.HLS, |
133 | |||
134 | playlistFilename: basename(playlistUrlObject.href), | ||
133 | playlistUrl: playlistUrlObject.href, | 135 | playlistUrl: playlistUrlObject.href, |
136 | |||
137 | segmentsSha256Filename: basename(segmentsSha256UrlObject.href), | ||
134 | segmentsSha256Url: segmentsSha256UrlObject.href, | 138 | segmentsSha256Url: segmentsSha256UrlObject.href, |
139 | |||
135 | p2pMediaLoaderInfohashes: VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlistUrlObject.href, files), | 140 | p2pMediaLoaderInfohashes: VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlistUrlObject.href, files), |
136 | p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION, | 141 | p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION, |
137 | videoId: video.id, | 142 | videoId: video.id, |
diff --git a/server/lib/hls.ts b/server/lib/hls.ts index 212bd095b..32b02bc26 100644 --- a/server/lib/hls.ts +++ b/server/lib/hls.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra' | 1 | import { close, ensureDir, move, open, outputJSON, pathExists, read, readFile, remove, writeFile } from 'fs-extra' |
2 | import { flatten, uniq } from 'lodash' | 2 | import { flatten, uniq } from 'lodash' |
3 | import { basename, dirname, join } from 'path' | 3 | import { basename, dirname, join } from 'path' |
4 | import { MVideoWithFile } from '@server/types/models' | 4 | import { MStreamingPlaylistFilesVideo, MVideoWithFile } from '@server/types/models' |
5 | import { sha256 } from '../helpers/core-utils' | 5 | import { sha256 } from '../helpers/core-utils' |
6 | import { getAudioStreamCodec, getVideoStreamCodec, getVideoStreamSize } from '../helpers/ffprobe-utils' | 6 | import { getAudioStreamCodec, getVideoStreamCodec, getVideoStreamSize } from '../helpers/ffprobe-utils' |
7 | import { logger } from '../helpers/logger' | 7 | import { logger } from '../helpers/logger' |
@@ -12,7 +12,7 @@ import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from | |||
12 | import { sequelizeTypescript } from '../initializers/database' | 12 | import { sequelizeTypescript } from '../initializers/database' |
13 | import { VideoFileModel } from '../models/video/video-file' | 13 | import { VideoFileModel } from '../models/video/video-file' |
14 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' | 14 | import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist' |
15 | import { getVideoFilePath } from './video-paths' | 15 | import { getHlsResolutionPlaylistFilename, getVideoFilePath } from './video-paths' |
16 | 16 | ||
17 | async function updateStreamingPlaylistsInfohashesIfNeeded () { | 17 | async function updateStreamingPlaylistsInfohashesIfNeeded () { |
18 | const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() | 18 | const playlistsToUpdate = await VideoStreamingPlaylistModel.listByIncorrectPeerVersion() |
@@ -22,27 +22,29 @@ async function updateStreamingPlaylistsInfohashesIfNeeded () { | |||
22 | await sequelizeTypescript.transaction(async t => { | 22 | await sequelizeTypescript.transaction(async t => { |
23 | const videoFiles = await VideoFileModel.listByStreamingPlaylist(playlist.id, t) | 23 | const videoFiles = await VideoFileModel.listByStreamingPlaylist(playlist.id, t) |
24 | 24 | ||
25 | playlist.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlist.playlistUrl, videoFiles) | 25 | playlist.assignP2PMediaLoaderInfoHashes(playlist.Video, videoFiles) |
26 | playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION | 26 | playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION |
27 | |||
27 | await playlist.save({ transaction: t }) | 28 | await playlist.save({ transaction: t }) |
28 | }) | 29 | }) |
29 | } | 30 | } |
30 | } | 31 | } |
31 | 32 | ||
32 | async function updateMasterHLSPlaylist (video: MVideoWithFile) { | 33 | async function updateMasterHLSPlaylist (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) { |
33 | const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | 34 | const directory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) |
35 | |||
34 | const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] | 36 | const masterPlaylists: string[] = [ '#EXTM3U', '#EXT-X-VERSION:3' ] |
35 | const masterPlaylistPath = join(directory, VideoStreamingPlaylistModel.getMasterHlsPlaylistFilename()) | ||
36 | const streamingPlaylist = video.getHLSPlaylist() | ||
37 | 37 | ||
38 | for (const file of streamingPlaylist.VideoFiles) { | 38 | const masterPlaylistPath = join(directory, playlist.playlistFilename) |
39 | const playlistFilename = VideoStreamingPlaylistModel.getHlsPlaylistFilename(file.resolution) | 39 | |
40 | for (const file of playlist.VideoFiles) { | ||
41 | const playlistFilename = getHlsResolutionPlaylistFilename(file.filename) | ||
40 | 42 | ||
41 | // If we did not generated a playlist for this resolution, skip | 43 | // If we did not generated a playlist for this resolution, skip |
42 | const filePlaylistPath = join(directory, playlistFilename) | 44 | const filePlaylistPath = join(directory, playlistFilename) |
43 | if (await pathExists(filePlaylistPath) === false) continue | 45 | if (await pathExists(filePlaylistPath) === false) continue |
44 | 46 | ||
45 | const videoFilePath = getVideoFilePath(streamingPlaylist, file) | 47 | const videoFilePath = getVideoFilePath(playlist, file) |
46 | 48 | ||
47 | const size = await getVideoStreamSize(videoFilePath) | 49 | const size = await getVideoStreamSize(videoFilePath) |
48 | 50 | ||
@@ -66,23 +68,22 @@ async function updateMasterHLSPlaylist (video: MVideoWithFile) { | |||
66 | await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') | 68 | await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n') |
67 | } | 69 | } |
68 | 70 | ||
69 | async function updateSha256VODSegments (video: MVideoWithFile) { | 71 | async function updateSha256VODSegments (video: MVideoWithFile, playlist: MStreamingPlaylistFilesVideo) { |
70 | const json: { [filename: string]: { [range: string]: string } } = {} | 72 | const json: { [filename: string]: { [range: string]: string } } = {} |
71 | 73 | ||
72 | const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | 74 | const playlistDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) |
73 | const hlsPlaylist = video.getHLSPlaylist() | ||
74 | 75 | ||
75 | // For all the resolutions available for this video | 76 | // For all the resolutions available for this video |
76 | for (const file of hlsPlaylist.VideoFiles) { | 77 | for (const file of playlist.VideoFiles) { |
77 | const rangeHashes: { [range: string]: string } = {} | 78 | const rangeHashes: { [range: string]: string } = {} |
78 | 79 | ||
79 | const videoPath = getVideoFilePath(hlsPlaylist, file) | 80 | const videoPath = getVideoFilePath(playlist, file) |
80 | const playlistPath = join(playlistDirectory, VideoStreamingPlaylistModel.getHlsPlaylistFilename(file.resolution)) | 81 | const resolutionPlaylistPath = join(playlistDirectory, getHlsResolutionPlaylistFilename(file.filename)) |
81 | 82 | ||
82 | // Maybe the playlist is not generated for this resolution yet | 83 | // Maybe the playlist is not generated for this resolution yet |
83 | if (!await pathExists(playlistPath)) continue | 84 | if (!await pathExists(resolutionPlaylistPath)) continue |
84 | 85 | ||
85 | const playlistContent = await readFile(playlistPath) | 86 | const playlistContent = await readFile(resolutionPlaylistPath) |
86 | const ranges = getRangesFromPlaylist(playlistContent.toString()) | 87 | const ranges = getRangesFromPlaylist(playlistContent.toString()) |
87 | 88 | ||
88 | const fd = await open(videoPath, 'r') | 89 | const fd = await open(videoPath, 'r') |
@@ -98,7 +99,7 @@ async function updateSha256VODSegments (video: MVideoWithFile) { | |||
98 | json[videoFilename] = rangeHashes | 99 | json[videoFilename] = rangeHashes |
99 | } | 100 | } |
100 | 101 | ||
101 | const outputPath = join(playlistDirectory, VideoStreamingPlaylistModel.getHlsSha256SegmentsFilename()) | 102 | const outputPath = join(playlistDirectory, playlist.segmentsSha256Filename) |
102 | await outputJSON(outputPath, json) | 103 | await outputJSON(outputPath, json) |
103 | } | 104 | } |
104 | 105 | ||
diff --git a/server/lib/job-queue/handlers/video-file-import.ts b/server/lib/job-queue/handlers/video-file-import.ts index 1783f206a..4d199f247 100644 --- a/server/lib/job-queue/handlers/video-file-import.ts +++ b/server/lib/job-queue/handlers/video-file-import.ts | |||
@@ -61,8 +61,7 @@ async function updateVideoFile (video: MVideoFullLight, inputFilePath: string) { | |||
61 | 61 | ||
62 | if (currentVideoFile) { | 62 | if (currentVideoFile) { |
63 | // Remove old file and old torrent | 63 | // Remove old file and old torrent |
64 | await video.removeFile(currentVideoFile) | 64 | await video.removeFileAndTorrent(currentVideoFile) |
65 | await currentVideoFile.removeTorrent() | ||
66 | // Remove the old video file from the array | 65 | // Remove the old video file from the array |
67 | video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) | 66 | video.VideoFiles = video.VideoFiles.filter(f => f !== currentVideoFile) |
68 | 67 | ||
diff --git a/server/lib/job-queue/handlers/video-live-ending.ts b/server/lib/job-queue/handlers/video-live-ending.ts index 9eba41bf8..386ccdc7b 100644 --- a/server/lib/job-queue/handlers/video-live-ending.ts +++ b/server/lib/job-queue/handlers/video-live-ending.ts | |||
@@ -7,12 +7,12 @@ import { buildConcatenatedName, cleanupLive, LiveSegmentShaStore } from '@server | |||
7 | import { generateVideoMiniature } from '@server/lib/thumbnail' | 7 | import { generateVideoMiniature } from '@server/lib/thumbnail' |
8 | import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/video-transcoding' | 8 | import { generateHlsPlaylistResolutionFromTS } from '@server/lib/transcoding/video-transcoding' |
9 | import { publishAndFederateIfNeeded } from '@server/lib/video' | 9 | import { publishAndFederateIfNeeded } from '@server/lib/video' |
10 | import { getHLSDirectory } from '@server/lib/video-paths' | 10 | import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getHLSDirectory } from '@server/lib/video-paths' |
11 | import { VideoModel } from '@server/models/video/video' | 11 | import { VideoModel } from '@server/models/video/video' |
12 | import { VideoFileModel } from '@server/models/video/video-file' | 12 | import { VideoFileModel } from '@server/models/video/video-file' |
13 | import { VideoLiveModel } from '@server/models/video/video-live' | 13 | import { VideoLiveModel } from '@server/models/video/video-live' |
14 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' | 14 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' |
15 | import { MVideo, MVideoLive } from '@server/types/models' | 15 | import { MStreamingPlaylist, MVideo, MVideoLive } from '@server/types/models' |
16 | import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models' | 16 | import { ThumbnailType, VideoLiveEndingPayload, VideoState } from '@shared/models' |
17 | import { logger } from '../../../helpers/logger' | 17 | import { logger } from '../../../helpers/logger' |
18 | 18 | ||
@@ -43,7 +43,7 @@ async function processVideoLiveEnding (job: Bull.Job) { | |||
43 | return cleanupLive(video, streamingPlaylist) | 43 | return cleanupLive(video, streamingPlaylist) |
44 | } | 44 | } |
45 | 45 | ||
46 | return saveLive(video, live) | 46 | return saveLive(video, live, streamingPlaylist) |
47 | } | 47 | } |
48 | 48 | ||
49 | // --------------------------------------------------------------------------- | 49 | // --------------------------------------------------------------------------- |
@@ -54,14 +54,14 @@ export { | |||
54 | 54 | ||
55 | // --------------------------------------------------------------------------- | 55 | // --------------------------------------------------------------------------- |
56 | 56 | ||
57 | async function saveLive (video: MVideo, live: MVideoLive) { | 57 | async function saveLive (video: MVideo, live: MVideoLive, streamingPlaylist: MStreamingPlaylist) { |
58 | const hlsDirectory = getHLSDirectory(video, false) | 58 | const hlsDirectory = getHLSDirectory(video, false) |
59 | const replayDirectory = join(hlsDirectory, VIDEO_LIVE.REPLAY_DIRECTORY) | 59 | const replayDirectory = join(hlsDirectory, VIDEO_LIVE.REPLAY_DIRECTORY) |
60 | 60 | ||
61 | const rootFiles = await readdir(hlsDirectory) | 61 | const rootFiles = await readdir(hlsDirectory) |
62 | 62 | ||
63 | const playlistFiles = rootFiles.filter(file => { | 63 | const playlistFiles = rootFiles.filter(file => { |
64 | return file.endsWith('.m3u8') && file !== 'master.m3u8' | 64 | return file.endsWith('.m3u8') && file !== streamingPlaylist.playlistFilename |
65 | }) | 65 | }) |
66 | 66 | ||
67 | await cleanupLiveFiles(hlsDirectory) | 67 | await cleanupLiveFiles(hlsDirectory) |
@@ -80,7 +80,12 @@ async function saveLive (video: MVideo, live: MVideoLive) { | |||
80 | 80 | ||
81 | const hlsPlaylist = videoWithFiles.getHLSPlaylist() | 81 | const hlsPlaylist = videoWithFiles.getHLSPlaylist() |
82 | await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id) | 82 | await VideoFileModel.removeHLSFilesOfVideoId(hlsPlaylist.id) |
83 | |||
84 | // Reset playlist | ||
83 | hlsPlaylist.VideoFiles = [] | 85 | hlsPlaylist.VideoFiles = [] |
86 | hlsPlaylist.playlistFilename = generateHLSMasterPlaylistFilename() | ||
87 | hlsPlaylist.segmentsSha256Filename = generateHlsSha256SegmentsFilename() | ||
88 | await hlsPlaylist.save() | ||
84 | 89 | ||
85 | let durationDone = false | 90 | let durationDone = false |
86 | 91 | ||
diff --git a/server/lib/job-queue/handlers/video-transcoding.ts b/server/lib/job-queue/handlers/video-transcoding.ts index f5ba6f435..36d9594af 100644 --- a/server/lib/job-queue/handlers/video-transcoding.ts +++ b/server/lib/job-queue/handlers/video-transcoding.ts | |||
@@ -125,8 +125,7 @@ async function onHlsPlaylistGeneration (video: MVideoFullLight, user: MUser, pay | |||
125 | if (payload.isMaxQuality && CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false) { | 125 | if (payload.isMaxQuality && CONFIG.TRANSCODING.WEBTORRENT.ENABLED === false) { |
126 | // Remove webtorrent files if not enabled | 126 | // Remove webtorrent files if not enabled |
127 | for (const file of video.VideoFiles) { | 127 | for (const file of video.VideoFiles) { |
128 | await video.removeFile(file) | 128 | await video.removeFileAndTorrent(file) |
129 | await file.removeTorrent() | ||
130 | await file.destroy() | 129 | await file.destroy() |
131 | } | 130 | } |
132 | 131 | ||
diff --git a/server/lib/live/live-manager.ts b/server/lib/live/live-manager.ts index da764e009..f106d69fb 100644 --- a/server/lib/live/live-manager.ts +++ b/server/lib/live/live-manager.ts | |||
@@ -4,16 +4,17 @@ import { isTestInstance } from '@server/helpers/core-utils' | |||
4 | import { computeResolutionsToTranscode, getVideoFileFPS, getVideoFileResolution } from '@server/helpers/ffprobe-utils' | 4 | import { computeResolutionsToTranscode, getVideoFileFPS, getVideoFileResolution } from '@server/helpers/ffprobe-utils' |
5 | import { logger, loggerTagsFactory } from '@server/helpers/logger' | 5 | import { logger, loggerTagsFactory } from '@server/helpers/logger' |
6 | import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' | 6 | import { CONFIG, registerConfigChangedHandler } from '@server/initializers/config' |
7 | import { P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE, VIEW_LIFETIME, WEBSERVER } from '@server/initializers/constants' | 7 | import { P2P_MEDIA_LOADER_PEER_VERSION, VIDEO_LIVE, VIEW_LIFETIME } from '@server/initializers/constants' |
8 | import { UserModel } from '@server/models/user/user' | 8 | import { UserModel } from '@server/models/user/user' |
9 | import { VideoModel } from '@server/models/video/video' | 9 | import { VideoModel } from '@server/models/video/video' |
10 | import { VideoLiveModel } from '@server/models/video/video-live' | 10 | import { VideoLiveModel } from '@server/models/video/video-live' |
11 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' | 11 | import { VideoStreamingPlaylistModel } from '@server/models/video/video-streaming-playlist' |
12 | import { MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoLiveVideo } from '@server/types/models' | 12 | import { MStreamingPlaylistVideo, MVideo, MVideoLiveVideo } from '@server/types/models' |
13 | import { VideoState, VideoStreamingPlaylistType } from '@shared/models' | 13 | import { VideoState, VideoStreamingPlaylistType } from '@shared/models' |
14 | import { federateVideoIfNeeded } from '../activitypub/videos' | 14 | import { federateVideoIfNeeded } from '../activitypub/videos' |
15 | import { JobQueue } from '../job-queue' | 15 | import { JobQueue } from '../job-queue' |
16 | import { PeerTubeSocket } from '../peertube-socket' | 16 | import { PeerTubeSocket } from '../peertube-socket' |
17 | import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename } from '../video-paths' | ||
17 | import { LiveQuotaStore } from './live-quota-store' | 18 | import { LiveQuotaStore } from './live-quota-store' |
18 | import { LiveSegmentShaStore } from './live-segment-sha-store' | 19 | import { LiveSegmentShaStore } from './live-segment-sha-store' |
19 | import { cleanupLive } from './live-utils' | 20 | import { cleanupLive } from './live-utils' |
@@ -392,19 +393,18 @@ class LiveManager { | |||
392 | return resolutionsEnabled.concat([ originResolution ]) | 393 | return resolutionsEnabled.concat([ originResolution ]) |
393 | } | 394 | } |
394 | 395 | ||
395 | private async createLivePlaylist (video: MVideo, allResolutions: number[]) { | 396 | private async createLivePlaylist (video: MVideo, allResolutions: number[]): Promise<MStreamingPlaylistVideo> { |
396 | const playlistUrl = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsMasterPlaylistStaticPath(video.uuid) | 397 | const playlist = await VideoStreamingPlaylistModel.loadOrGenerate(video) |
397 | const [ videoStreamingPlaylist ] = await VideoStreamingPlaylistModel.upsert({ | ||
398 | videoId: video.id, | ||
399 | playlistUrl, | ||
400 | segmentsSha256Url: WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsSha256SegmentsStaticPath(video.uuid, video.isLive), | ||
401 | p2pMediaLoaderInfohashes: VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes(playlistUrl, allResolutions), | ||
402 | p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION, | ||
403 | 398 | ||
404 | type: VideoStreamingPlaylistType.HLS | 399 | playlist.playlistFilename = generateHLSMasterPlaylistFilename(true) |
405 | }, { returning: true }) as [ MStreamingPlaylist, boolean ] | 400 | playlist.segmentsSha256Filename = generateHlsSha256SegmentsFilename(true) |
406 | 401 | ||
407 | return Object.assign(videoStreamingPlaylist, { Video: video }) | 402 | playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION |
403 | playlist.type = VideoStreamingPlaylistType.HLS | ||
404 | |||
405 | playlist.assignP2PMediaLoaderInfoHashes(video, allResolutions) | ||
406 | |||
407 | return playlist.save() | ||
408 | } | 408 | } |
409 | 409 | ||
410 | static get Instance () { | 410 | static get Instance () { |
diff --git a/server/lib/live/shared/muxing-session.ts b/server/lib/live/shared/muxing-session.ts index 26467f060..709d6c615 100644 --- a/server/lib/live/shared/muxing-session.ts +++ b/server/lib/live/shared/muxing-session.ts | |||
@@ -112,13 +112,16 @@ class MuxingSession extends EventEmitter { | |||
112 | this.ffmpegCommand = CONFIG.LIVE.TRANSCODING.ENABLED | 112 | this.ffmpegCommand = CONFIG.LIVE.TRANSCODING.ENABLED |
113 | ? await getLiveTranscodingCommand({ | 113 | ? await getLiveTranscodingCommand({ |
114 | rtmpUrl: this.rtmpUrl, | 114 | rtmpUrl: this.rtmpUrl, |
115 | |||
115 | outPath, | 116 | outPath, |
117 | masterPlaylistName: this.streamingPlaylist.playlistFilename, | ||
118 | |||
116 | resolutions: this.allResolutions, | 119 | resolutions: this.allResolutions, |
117 | fps: this.fps, | 120 | fps: this.fps, |
118 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), | 121 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), |
119 | profile: CONFIG.LIVE.TRANSCODING.PROFILE | 122 | profile: CONFIG.LIVE.TRANSCODING.PROFILE |
120 | }) | 123 | }) |
121 | : getLiveMuxingCommand(this.rtmpUrl, outPath) | 124 | : getLiveMuxingCommand(this.rtmpUrl, outPath, this.streamingPlaylist.playlistFilename) |
122 | 125 | ||
123 | logger.info('Running live muxing/transcoding for %s.', this.videoUUID, this.lTags) | 126 | logger.info('Running live muxing/transcoding for %s.', this.videoUUID, this.lTags) |
124 | 127 | ||
@@ -182,7 +185,7 @@ class MuxingSession extends EventEmitter { | |||
182 | } | 185 | } |
183 | 186 | ||
184 | private watchMasterFile (outPath: string) { | 187 | private watchMasterFile (outPath: string) { |
185 | this.masterWatcher = chokidar.watch(outPath + '/master.m3u8') | 188 | this.masterWatcher = chokidar.watch(outPath + '/' + this.streamingPlaylist.playlistFilename) |
186 | 189 | ||
187 | this.masterWatcher.on('add', async () => { | 190 | this.masterWatcher.on('add', async () => { |
188 | this.emit('master-playlist-created', { videoId: this.videoId }) | 191 | this.emit('master-playlist-created', { videoId: this.videoId }) |
diff --git a/server/lib/schedulers/videos-redundancy-scheduler.ts b/server/lib/schedulers/videos-redundancy-scheduler.ts index b5a5eb697..103ab1fab 100644 --- a/server/lib/schedulers/videos-redundancy-scheduler.ts +++ b/server/lib/schedulers/videos-redundancy-scheduler.ts | |||
@@ -267,7 +267,8 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
267 | logger.info('Duplicating %s streaming playlist in videos redundancy with "%s" strategy.', video.url, strategy) | 267 | logger.info('Duplicating %s streaming playlist in videos redundancy with "%s" strategy.', video.url, strategy) |
268 | 268 | ||
269 | const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid) | 269 | const destDirectory = join(HLS_REDUNDANCY_DIRECTORY, video.uuid) |
270 | await downloadPlaylistSegments(playlist.playlistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT) | 270 | const masterPlaylistUrl = playlist.getMasterPlaylistUrl(video) |
271 | await downloadPlaylistSegments(masterPlaylistUrl, destDirectory, VIDEO_IMPORT_TIMEOUT) | ||
271 | 272 | ||
272 | const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({ | 273 | const createdModel: MVideoRedundancyStreamingPlaylistVideo = await VideoRedundancyModel.create({ |
273 | expiresOn, | 274 | expiresOn, |
@@ -282,7 +283,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
282 | 283 | ||
283 | await sendCreateCacheFile(serverActor, video, createdModel) | 284 | await sendCreateCacheFile(serverActor, video, createdModel) |
284 | 285 | ||
285 | logger.info('Duplicated playlist %s -> %s.', playlist.playlistUrl, createdModel.url) | 286 | logger.info('Duplicated playlist %s -> %s.', masterPlaylistUrl, createdModel.url) |
286 | } | 287 | } |
287 | 288 | ||
288 | private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) { | 289 | private async extendsExpirationOf (redundancy: MVideoRedundancyVideo, expiresAfterMs: number) { |
@@ -330,7 +331,7 @@ export class VideosRedundancyScheduler extends AbstractScheduler { | |||
330 | private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) { | 331 | private buildEntryLogId (object: MVideoRedundancyFileVideo | MVideoRedundancyStreamingPlaylistVideo) { |
331 | if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` | 332 | if (isMVideoRedundancyFileVideo(object)) return `${object.VideoFile.Video.url}-${object.VideoFile.resolution}` |
332 | 333 | ||
333 | return `${object.VideoStreamingPlaylist.playlistUrl}` | 334 | return `${object.VideoStreamingPlaylist.getMasterPlaylistUrl(object.VideoStreamingPlaylist.Video)}` |
334 | } | 335 | } |
335 | 336 | ||
336 | private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylistFiles[]) { | 337 | private getTotalFileSizes (files: MVideoFile[], playlists: MStreamingPlaylistFiles[]) { |
diff --git a/server/lib/transcoding/video-transcoding.ts b/server/lib/transcoding/video-transcoding.ts index d70f7f474..d2a556360 100644 --- a/server/lib/transcoding/video-transcoding.ts +++ b/server/lib/transcoding/video-transcoding.ts | |||
@@ -10,11 +10,18 @@ import { transcode, TranscodeOptions, TranscodeOptionsType } from '../../helpers | |||
10 | import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../../helpers/ffprobe-utils' | 10 | import { canDoQuickTranscode, getDurationFromVideoFile, getMetadataFromFile, getVideoFileFPS } from '../../helpers/ffprobe-utils' |
11 | import { logger } from '../../helpers/logger' | 11 | import { logger } from '../../helpers/logger' |
12 | import { CONFIG } from '../../initializers/config' | 12 | import { CONFIG } from '../../initializers/config' |
13 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION, WEBSERVER } from '../../initializers/constants' | 13 | import { HLS_STREAMING_PLAYLIST_DIRECTORY, P2P_MEDIA_LOADER_PEER_VERSION } from '../../initializers/constants' |
14 | import { VideoFileModel } from '../../models/video/video-file' | 14 | import { VideoFileModel } from '../../models/video/video-file' |
15 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' | 15 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' |
16 | import { updateMasterHLSPlaylist, updateSha256VODSegments } from '../hls' | 16 | import { updateMasterHLSPlaylist, updateSha256VODSegments } from '../hls' |
17 | import { generateHLSVideoFilename, generateWebTorrentVideoFilename, getVideoFilePath } from '../video-paths' | 17 | import { |
18 | generateHLSMasterPlaylistFilename, | ||
19 | generateHlsSha256SegmentsFilename, | ||
20 | generateHLSVideoFilename, | ||
21 | generateWebTorrentVideoFilename, | ||
22 | getHlsResolutionPlaylistFilename, | ||
23 | getVideoFilePath | ||
24 | } from '../video-paths' | ||
18 | import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' | 25 | import { VideoTranscodingProfilesManager } from './video-transcoding-profiles' |
19 | 26 | ||
20 | /** | 27 | /** |
@@ -272,14 +279,14 @@ async function generateHlsPlaylistCommon (options: { | |||
272 | await ensureDir(videoTranscodedBasePath) | 279 | await ensureDir(videoTranscodedBasePath) |
273 | 280 | ||
274 | const videoFilename = generateHLSVideoFilename(resolution) | 281 | const videoFilename = generateHLSVideoFilename(resolution) |
275 | const playlistFilename = VideoStreamingPlaylistModel.getHlsPlaylistFilename(resolution) | 282 | const resolutionPlaylistFilename = getHlsResolutionPlaylistFilename(videoFilename) |
276 | const playlistFileTranscodePath = join(videoTranscodedBasePath, playlistFilename) | 283 | const resolutionPlaylistFileTranscodePath = join(videoTranscodedBasePath, resolutionPlaylistFilename) |
277 | 284 | ||
278 | const transcodeOptions = { | 285 | const transcodeOptions = { |
279 | type, | 286 | type, |
280 | 287 | ||
281 | inputPath, | 288 | inputPath, |
282 | outputPath: playlistFileTranscodePath, | 289 | outputPath: resolutionPlaylistFileTranscodePath, |
283 | 290 | ||
284 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), | 291 | availableEncoders: VideoTranscodingProfilesManager.Instance.getAvailableEncoders(), |
285 | profile: CONFIG.TRANSCODING.PROFILE, | 292 | profile: CONFIG.TRANSCODING.PROFILE, |
@@ -299,19 +306,23 @@ async function generateHlsPlaylistCommon (options: { | |||
299 | 306 | ||
300 | await transcode(transcodeOptions) | 307 | await transcode(transcodeOptions) |
301 | 308 | ||
302 | const playlistUrl = WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsMasterPlaylistStaticPath(video.uuid) | ||
303 | |||
304 | // Create or update the playlist | 309 | // Create or update the playlist |
305 | const [ videoStreamingPlaylist ] = await VideoStreamingPlaylistModel.upsert({ | 310 | const playlist = await VideoStreamingPlaylistModel.loadOrGenerate(video) |
306 | videoId: video.id, | 311 | |
307 | playlistUrl, | 312 | if (!playlist.playlistFilename) { |
308 | segmentsSha256Url: WEBSERVER.URL + VideoStreamingPlaylistModel.getHlsSha256SegmentsStaticPath(video.uuid, video.isLive), | 313 | playlist.playlistFilename = generateHLSMasterPlaylistFilename(video.isLive) |
309 | p2pMediaLoaderInfohashes: [], | 314 | } |
310 | p2pMediaLoaderPeerVersion: P2P_MEDIA_LOADER_PEER_VERSION, | 315 | |
316 | if (!playlist.segmentsSha256Filename) { | ||
317 | playlist.segmentsSha256Filename = generateHlsSha256SegmentsFilename(video.isLive) | ||
318 | } | ||
319 | |||
320 | playlist.p2pMediaLoaderInfohashes = [] | ||
321 | playlist.p2pMediaLoaderPeerVersion = P2P_MEDIA_LOADER_PEER_VERSION | ||
311 | 322 | ||
312 | type: VideoStreamingPlaylistType.HLS | 323 | playlist.type = VideoStreamingPlaylistType.HLS |
313 | }, { returning: true }) as [ MStreamingPlaylistFilesVideo, boolean ] | 324 | |
314 | videoStreamingPlaylist.Video = video | 325 | await playlist.save() |
315 | 326 | ||
316 | // Build the new playlist file | 327 | // Build the new playlist file |
317 | const extname = extnameUtil(videoFilename) | 328 | const extname = extnameUtil(videoFilename) |
@@ -321,18 +332,18 @@ async function generateHlsPlaylistCommon (options: { | |||
321 | size: 0, | 332 | size: 0, |
322 | filename: videoFilename, | 333 | filename: videoFilename, |
323 | fps: -1, | 334 | fps: -1, |
324 | videoStreamingPlaylistId: videoStreamingPlaylist.id | 335 | videoStreamingPlaylistId: playlist.id |
325 | }) | 336 | }) |
326 | 337 | ||
327 | const videoFilePath = getVideoFilePath(videoStreamingPlaylist, newVideoFile) | 338 | const videoFilePath = getVideoFilePath(playlist, newVideoFile) |
328 | 339 | ||
329 | // Move files from tmp transcoded directory to the appropriate place | 340 | // Move files from tmp transcoded directory to the appropriate place |
330 | const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) | 341 | const baseHlsDirectory = join(HLS_STREAMING_PLAYLIST_DIRECTORY, video.uuid) |
331 | await ensureDir(baseHlsDirectory) | 342 | await ensureDir(baseHlsDirectory) |
332 | 343 | ||
333 | // Move playlist file | 344 | // Move playlist file |
334 | const playlistPath = join(baseHlsDirectory, playlistFilename) | 345 | const resolutionPlaylistPath = join(baseHlsDirectory, resolutionPlaylistFilename) |
335 | await move(playlistFileTranscodePath, playlistPath, { overwrite: true }) | 346 | await move(resolutionPlaylistFileTranscodePath, resolutionPlaylistPath, { overwrite: true }) |
336 | // Move video file | 347 | // Move video file |
337 | await move(join(videoTranscodedBasePath, videoFilename), videoFilePath, { overwrite: true }) | 348 | await move(join(videoTranscodedBasePath, videoFilename), videoFilePath, { overwrite: true }) |
338 | 349 | ||
@@ -342,20 +353,20 @@ async function generateHlsPlaylistCommon (options: { | |||
342 | newVideoFile.fps = await getVideoFileFPS(videoFilePath) | 353 | newVideoFile.fps = await getVideoFileFPS(videoFilePath) |
343 | newVideoFile.metadata = await getMetadataFromFile(videoFilePath) | 354 | newVideoFile.metadata = await getMetadataFromFile(videoFilePath) |
344 | 355 | ||
345 | await createTorrentAndSetInfoHash(videoStreamingPlaylist, newVideoFile) | 356 | await createTorrentAndSetInfoHash(playlist, newVideoFile) |
346 | 357 | ||
347 | await VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined) | 358 | await VideoFileModel.customUpsert(newVideoFile, 'streaming-playlist', undefined) |
348 | videoStreamingPlaylist.VideoFiles = await videoStreamingPlaylist.$get('VideoFiles') | ||
349 | 359 | ||
350 | videoStreamingPlaylist.p2pMediaLoaderInfohashes = VideoStreamingPlaylistModel.buildP2PMediaLoaderInfoHashes( | 360 | const playlistWithFiles = playlist as MStreamingPlaylistFilesVideo |
351 | playlistUrl, videoStreamingPlaylist.VideoFiles | 361 | playlistWithFiles.VideoFiles = await playlist.$get('VideoFiles') |
352 | ) | 362 | playlist.assignP2PMediaLoaderInfoHashes(video, playlistWithFiles.VideoFiles) |
353 | await videoStreamingPlaylist.save() | 363 | |
364 | await playlist.save() | ||
354 | 365 | ||
355 | video.setHLSPlaylist(videoStreamingPlaylist) | 366 | video.setHLSPlaylist(playlist) |
356 | 367 | ||
357 | await updateMasterHLSPlaylist(video) | 368 | await updateMasterHLSPlaylist(video, playlistWithFiles) |
358 | await updateSha256VODSegments(video) | 369 | await updateSha256VODSegments(video, playlistWithFiles) |
359 | 370 | ||
360 | return playlistPath | 371 | return resolutionPlaylistPath |
361 | } | 372 | } |
diff --git a/server/lib/video-paths.ts b/server/lib/video-paths.ts index b7068190c..1e4382108 100644 --- a/server/lib/video-paths.ts +++ b/server/lib/video-paths.ts | |||
@@ -4,19 +4,16 @@ import { CONFIG } from '@server/initializers/config' | |||
4 | import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY, STATIC_PATHS, WEBSERVER } from '@server/initializers/constants' | 4 | import { HLS_REDUNDANCY_DIRECTORY, HLS_STREAMING_PLAYLIST_DIRECTORY, STATIC_PATHS, WEBSERVER } from '@server/initializers/constants' |
5 | import { isStreamingPlaylist, MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/types/models' | 5 | import { isStreamingPlaylist, MStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoFile, MVideoUUID } from '@server/types/models' |
6 | import { buildUUID } from '@server/helpers/uuid' | 6 | import { buildUUID } from '@server/helpers/uuid' |
7 | import { removeFragmentedMP4Ext } from '@shared/core-utils' | ||
7 | 8 | ||
8 | // ################## Video file name ################## | 9 | // ################## Video file name ################## |
9 | 10 | ||
10 | function generateWebTorrentVideoFilename (resolution: number, extname: string) { | 11 | function generateWebTorrentVideoFilename (resolution: number, extname: string) { |
11 | const uuid = buildUUID() | 12 | return buildUUID() + '-' + resolution + extname |
12 | |||
13 | return uuid + '-' + resolution + extname | ||
14 | } | 13 | } |
15 | 14 | ||
16 | function generateHLSVideoFilename (resolution: number) { | 15 | function generateHLSVideoFilename (resolution: number) { |
17 | const uuid = buildUUID() | 16 | return `${buildUUID()}-${resolution}-fragmented.mp4` |
18 | |||
19 | return `${uuid}-${resolution}-fragmented.mp4` | ||
20 | } | 17 | } |
21 | 18 | ||
22 | function getVideoFilePath (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile, isRedundancy = false) { | 19 | function getVideoFilePath (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, videoFile: MVideoFile, isRedundancy = false) { |
@@ -54,6 +51,23 @@ function getHLSDirectory (video: MVideoUUID, isRedundancy = false) { | |||
54 | return join(baseDir, video.uuid) | 51 | return join(baseDir, video.uuid) |
55 | } | 52 | } |
56 | 53 | ||
54 | function getHlsResolutionPlaylistFilename (videoFilename: string) { | ||
55 | // Video file name already contain resolution | ||
56 | return removeFragmentedMP4Ext(videoFilename) + '.m3u8' | ||
57 | } | ||
58 | |||
59 | function generateHLSMasterPlaylistFilename (isLive = false) { | ||
60 | if (isLive) return 'master.m3u8' | ||
61 | |||
62 | return buildUUID() + '-master.m3u8' | ||
63 | } | ||
64 | |||
65 | function generateHlsSha256SegmentsFilename (isLive = false) { | ||
66 | if (isLive) return 'segments-sha256.json' | ||
67 | |||
68 | return buildUUID() + '-segments-sha256.json' | ||
69 | } | ||
70 | |||
57 | // ################## Torrents ################## | 71 | // ################## Torrents ################## |
58 | 72 | ||
59 | function generateTorrentFileName (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, resolution: number) { | 73 | function generateTorrentFileName (videoOrPlaylist: MVideo | MStreamingPlaylistVideo, resolution: number) { |
@@ -91,6 +105,9 @@ export { | |||
91 | getTorrentFilePath, | 105 | getTorrentFilePath, |
92 | 106 | ||
93 | getHLSDirectory, | 107 | getHLSDirectory, |
108 | generateHLSMasterPlaylistFilename, | ||
109 | generateHlsSha256SegmentsFilename, | ||
110 | getHlsResolutionPlaylistFilename, | ||
94 | 111 | ||
95 | getLocalVideoFileMetadataUrl, | 112 | getLocalVideoFileMetadataUrl, |
96 | 113 | ||
diff --git a/server/lib/video.ts b/server/lib/video.ts index daf998704..61fee4949 100644 --- a/server/lib/video.ts +++ b/server/lib/video.ts | |||
@@ -5,7 +5,7 @@ import { sequelizeTypescript } from '@server/initializers/database' | |||
5 | import { TagModel } from '@server/models/video/tag' | 5 | import { TagModel } from '@server/models/video/tag' |
6 | import { VideoModel } from '@server/models/video/video' | 6 | import { VideoModel } from '@server/models/video/video' |
7 | import { FilteredModelAttributes } from '@server/types' | 7 | import { FilteredModelAttributes } from '@server/types' |
8 | import { MThumbnail, MUserId, MVideo, MVideoFile, MVideoTag, MVideoThumbnail, MVideoUUID } from '@server/types/models' | 8 | import { MThumbnail, MUserId, MVideoFile, MVideoTag, MVideoThumbnail, MVideoUUID } from '@server/types/models' |
9 | import { ThumbnailType, VideoCreate, VideoPrivacy, VideoTranscodingPayload } from '@shared/models' | 9 | import { ThumbnailType, VideoCreate, VideoPrivacy, VideoTranscodingPayload } from '@shared/models' |
10 | import { federateVideoIfNeeded } from './activitypub/videos' | 10 | import { federateVideoIfNeeded } from './activitypub/videos' |
11 | import { JobQueue } from './job-queue/job-queue' | 11 | import { JobQueue } from './job-queue/job-queue' |
@@ -105,7 +105,7 @@ async function publishAndFederateIfNeeded (video: MVideoUUID, wasLive = false) { | |||
105 | } | 105 | } |
106 | } | 106 | } |
107 | 107 | ||
108 | async function addOptimizeOrMergeAudioJob (video: MVideo, videoFile: MVideoFile, user: MUserId) { | 108 | async function addOptimizeOrMergeAudioJob (video: MVideoUUID, videoFile: MVideoFile, user: MUserId) { |
109 | let dataInput: VideoTranscodingPayload | 109 | let dataInput: VideoTranscodingPayload |
110 | 110 | ||
111 | if (videoFile.isAudio()) { | 111 | if (videoFile.isAudio()) { |