]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/lib/hls.ts
Fix tests
[github/Chocobozzz/PeerTube.git] / server / lib / hls.ts
index 20754219f22c1b7f402bdbd86674d0e94d7abccd..fc1d7e1b0cc613f3707083a0ae6706ed42cddf76 100644 (file)
@@ -1,11 +1,13 @@
 import { close, ensureDir, move, open, outputJSON, read, readFile, remove, stat, writeFile } from 'fs-extra'
-import { flatten, uniq } from 'lodash'
+import { flatten } from 'lodash'
 import PQueue from 'p-queue'
 import { basename, dirname, join } from 'path'
 import { MStreamingPlaylist, MStreamingPlaylistFilesVideo, MVideo } from '@server/types/models'
+import { uniqify, uuidRegex } from '@shared/core-utils'
 import { sha256 } from '@shared/extra-utils'
+import { getVideoStreamDimensionsInfo } from '@shared/ffmpeg'
 import { VideoStorage } from '@shared/models'
-import { getAudioStreamCodec, getVideoStreamCodec, getVideoStreamDimensionsInfo } from '../helpers/ffmpeg'
+import { getAudioStreamCodec, getVideoStreamCodec } from '../helpers/ffmpeg'
 import { logger } from '../helpers/logger'
 import { doRequest, doRequestAndSaveToFile } from '../helpers/requests'
 import { generateRandomString } from '../helpers/utils'
@@ -14,7 +16,7 @@ import { P2P_MEDIA_LOADER_PEER_VERSION, REQUEST_TIMEOUTS } from '../initializers
 import { sequelizeTypescript } from '../initializers/database'
 import { VideoFileModel } from '../models/video/video-file'
 import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
-import { storeHLSFile } from './object-storage'
+import { storeHLSFileFromFilename } from './object-storage'
 import { generateHLSMasterPlaylistFilename, generateHlsSha256SegmentsFilename, getHlsResolutionPlaylistFilename } from './paths'
 import { VideoPathManager } from './video-path-manager'
 
@@ -35,15 +37,19 @@ async function updateStreamingPlaylistsInfohashesIfNeeded () {
 }
 
 async function updatePlaylistAfterFileChange (video: MVideo, playlist: MStreamingPlaylist) {
-  let playlistWithFiles = await updateMasterHLSPlaylist(video, playlist)
-  playlistWithFiles = await updateSha256VODSegments(video, playlist)
-
-  // Refresh playlist, operations can take some time
-  playlistWithFiles = await VideoStreamingPlaylistModel.loadWithVideoAndFiles(playlist.id)
-  playlistWithFiles.assignP2PMediaLoaderInfoHashes(video, playlistWithFiles.VideoFiles)
-  await playlistWithFiles.save()
-
-  video.setHLSPlaylist(playlistWithFiles)
+  try {
+    let playlistWithFiles = await updateMasterHLSPlaylist(video, playlist)
+    playlistWithFiles = await updateSha256VODSegments(video, playlist)
+
+    // Refresh playlist, operations can take some time
+    playlistWithFiles = await VideoStreamingPlaylistModel.loadWithVideoAndFiles(playlist.id)
+    playlistWithFiles.assignP2PMediaLoaderInfoHashes(video, playlistWithFiles.VideoFiles)
+    await playlistWithFiles.save()
+
+    video.setHLSPlaylist(playlistWithFiles)
+  } catch (err) {
+    logger.info('Cannot update playlist after file change. Maybe due to concurrent transcoding', { err })
+  }
 }
 
 // ---------------------------------------------------------------------------
@@ -90,7 +96,7 @@ function updateMasterHLSPlaylist (video: MVideo, playlistArg: MStreamingPlaylist
     await writeFile(masterPlaylistPath, masterPlaylists.join('\n') + '\n')
 
     if (playlist.storage === VideoStorage.OBJECT_STORAGE) {
-      playlist.playlistUrl = await storeHLSFile(playlist, playlist.playlistFilename)
+      playlist.playlistUrl = await storeHLSFileFromFilename(playlist, playlist.playlistFilename)
       await remove(masterPlaylistPath)
     }
 
@@ -100,7 +106,7 @@ function updateMasterHLSPlaylist (video: MVideo, playlistArg: MStreamingPlaylist
 
 // ---------------------------------------------------------------------------
 
-async function updateSha256VODSegments (video: MVideo, playlistArg: MStreamingPlaylist): Promise<MStreamingPlaylistFilesVideo> {
+function updateSha256VODSegments (video: MVideo, playlistArg: MStreamingPlaylist): Promise<MStreamingPlaylistFilesVideo> {
   return playlistFilesQueue.add(async () => {
     const json: { [filename: string]: { [range: string]: string } } = {}
 
@@ -141,7 +147,7 @@ async function updateSha256VODSegments (video: MVideo, playlistArg: MStreamingPl
     await outputJSON(outputPath, json)
 
     if (playlist.storage === VideoStorage.OBJECT_STORAGE) {
-      playlist.segmentsSha256Url = await storeHLSFile(playlist, playlist.segmentsSha256Filename)
+      playlist.segmentsSha256Url = await storeHLSFileFromFilename(playlist, playlist.segmentsSha256Filename)
       await remove(outputPath)
     }
 
@@ -178,7 +184,7 @@ function downloadPlaylistSegments (playlistUrl: string, destinationDir: string,
       const subPlaylistUrls = await fetchUniqUrls(playlistUrl)
 
       const subRequests = subPlaylistUrls.map(u => fetchUniqUrls(u))
-      const fileUrls = uniq(flatten(await Promise.all(subRequests)))
+      const fileUrls = uniqify(flatten(await Promise.all(subRequests)))
 
       logger.debug('Will download %d HLS files.', fileUrls.length, { fileUrls })
 
@@ -223,19 +229,37 @@ function downloadPlaylistSegments (playlistUrl: string, destinationDir: string,
         return `${dirname(playlistUrl)}/${url}`
       })
 
-    return uniq(urls)
+    return uniqify(urls)
   }
 }
 
 // ---------------------------------------------------------------------------
 
+async function renameVideoFileInPlaylist (playlistPath: string, newVideoFilename: string) {
+  const content = await readFile(playlistPath, 'utf8')
+
+  const newContent = content.replace(new RegExp(`${uuidRegex}-\\d+-fragmented.mp4`, 'g'), newVideoFilename)
+
+  await writeFile(playlistPath, newContent, 'utf8')
+}
+
+// ---------------------------------------------------------------------------
+
+function injectQueryToPlaylistUrls (content: string, queryString: string) {
+  return content.replace(/\.(m3u8|ts|mp4)/gm, '.$1?' + queryString)
+}
+
+// ---------------------------------------------------------------------------
+
 export {
   updateMasterHLSPlaylist,
   updateSha256VODSegments,
   buildSha256Segment,
   downloadPlaylistSegments,
   updateStreamingPlaylistsInfohashesIfNeeded,
-  updatePlaylistAfterFileChange
+  updatePlaylistAfterFileChange,
+  injectQueryToPlaylistUrls,
+  renameVideoFileInPlaylist
 }
 
 // ---------------------------------------------------------------------------