]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/tests/shared/streaming-playlists.ts
Try to have more robust live tests
[github/Chocobozzz/PeerTube.git] / server / tests / shared / streaming-playlists.ts
index 75e135c4e5877d6a39c1600628ca5b92572cd837..e4f88bc25283b5a3c60e46b47f38c6beb496704c 100644 (file)
@@ -4,10 +4,11 @@ import { expect } from 'chai'
 import { basename, dirname, join } from 'path'
 import { removeFragmentedMP4Ext, uuidRegex } from '@shared/core-utils'
 import { sha256 } from '@shared/extra-utils'
-import { HttpStatusCode, VideoStreamingPlaylist, VideoStreamingPlaylistType } from '@shared/models'
-import { makeRawRequest, PeerTubeServer, webtorrentAdd } from '@shared/server-commands'
+import { HttpStatusCode, VideoPrivacy, VideoResolution, VideoStreamingPlaylist, VideoStreamingPlaylistType } from '@shared/models'
+import { makeRawRequest, PeerTubeServer } from '@shared/server-commands'
 import { expectStartWith } from './checks'
 import { hlsInfohashExist } from './tracker'
+import { checkWebTorrentWorks } from './webtorrent'
 
 async function checkSegmentHash (options: {
   server: PeerTubeServer
@@ -15,14 +16,15 @@ async function checkSegmentHash (options: {
   baseUrlSegment: string
   resolution: number
   hlsPlaylist: VideoStreamingPlaylist
+  token?: string
 }) {
-  const { server, baseUrlPlaylist, baseUrlSegment, resolution, hlsPlaylist } = options
+  const { server, baseUrlPlaylist, baseUrlSegment, resolution, hlsPlaylist, token } = options
   const command = server.streamingPlaylists
 
   const file = hlsPlaylist.files.find(f => f.resolution.id === resolution)
   const videoName = basename(file.fileUrl)
 
-  const playlist = await command.get({ url: `${baseUrlPlaylist}/${removeFragmentedMP4Ext(videoName)}.m3u8` })
+  const playlist = await command.get({ url: `${baseUrlPlaylist}/${removeFragmentedMP4Ext(videoName)}.m3u8`, token })
 
   const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist)
 
@@ -33,11 +35,12 @@ async function checkSegmentHash (options: {
   const segmentBody = await command.getFragmentedSegment({
     url: `${baseUrlSegment}/${videoName}`,
     expectedStatus: HttpStatusCode.PARTIAL_CONTENT_206,
-    range: `bytes=${range}`
+    range: `bytes=${range}`,
+    token
   })
 
-  const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url })
-  expect(sha256(segmentBody)).to.equal(shaBody[videoName][range])
+  const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url, token })
+  expect(sha256(segmentBody)).to.equal(shaBody[videoName][range], `Invalid sha256 result for ${videoName} range ${range}`)
 }
 
 // ---------------------------------------------------------------------------
@@ -48,12 +51,13 @@ async function checkLiveSegmentHash (options: {
   videoUUID: string
   segmentName: string
   hlsPlaylist: VideoStreamingPlaylist
+  withRetry?: boolean
 }) {
-  const { server, baseUrlSegment, videoUUID, segmentName, hlsPlaylist } = options
+  const { server, baseUrlSegment, videoUUID, segmentName, hlsPlaylist, withRetry = false } = options
   const command = server.streamingPlaylists
 
-  const segmentBody = await command.getFragmentedSegment({ url: `${baseUrlSegment}/${videoUUID}/${segmentName}` })
-  const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url })
+  const segmentBody = await command.getFragmentedSegment({ url: `${baseUrlSegment}/${videoUUID}/${segmentName}`, withRetry })
+  const shaBody = await command.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url, withRetry })
 
   expect(sha256(segmentBody)).to.equal(shaBody[segmentName])
 }
@@ -64,19 +68,24 @@ async function checkResolutionsInMasterPlaylist (options: {
   server: PeerTubeServer
   playlistUrl: string
   resolutions: number[]
+  token?: string
   transcoded?: boolean // default true
   withRetry?: boolean // default false
 }) {
-  const { server, playlistUrl, resolutions, withRetry = false, transcoded = true } = options
+  const { server, playlistUrl, resolutions, token, withRetry = false, transcoded = true } = options
 
-  const masterPlaylist = await server.streamingPlaylists.get({ url: playlistUrl, withRetry })
+  const masterPlaylist = await server.streamingPlaylists.get({ url: playlistUrl, token, withRetry })
 
   for (const resolution of resolutions) {
-    const reg = transcoded
-      ? new RegExp('#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + ',(FRAME-RATE=\\d+,)?CODECS="avc1.64001f,mp4a.40.2"')
-      : new RegExp('#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution + '')
-
-    expect(masterPlaylist).to.match(reg)
+    const base = '#EXT-X-STREAM-INF:BANDWIDTH=\\d+,RESOLUTION=\\d+x' + resolution
+
+    if (resolution === VideoResolution.H_NOVIDEO) {
+      expect(masterPlaylist).to.match(new RegExp(`${base},CODECS="mp4a.40.2"`))
+    } else if (transcoded) {
+      expect(masterPlaylist).to.match(new RegExp(`${base},(FRAME-RATE=\\d+,)?CODECS="avc1.64001f,mp4a.40.2"`))
+    } else {
+      expect(masterPlaylist).to.match(new RegExp(`${base}`))
+    }
   }
 
   const playlistsLength = masterPlaylist.split('\n').filter(line => line.startsWith('#EXT-X-STREAM-INF:BANDWIDTH='))
@@ -89,14 +98,23 @@ async function completeCheckHlsPlaylist (options: {
   hlsOnly: boolean
 
   resolutions?: number[]
-  objectStorageBaseUrl: string
+  objectStorageBaseUrl?: string
 }) {
   const { videoUUID, hlsOnly, objectStorageBaseUrl } = options
 
   const resolutions = options.resolutions ?? [ 240, 360, 480, 720 ]
 
   for (const server of options.servers) {
-    const videoDetails = await server.videos.get({ id: videoUUID })
+    const videoDetails = await server.videos.getWithToken({ id: videoUUID })
+    const requiresAuth = videoDetails.privacy.id === VideoPrivacy.PRIVATE || videoDetails.privacy.id === VideoPrivacy.INTERNAL
+
+    const privatePath = requiresAuth
+      ? 'private/'
+      : ''
+    const token = requiresAuth
+      ? server.accessToken
+      : undefined
+
     const baseUrl = `http://${videoDetails.account.host}`
 
     expect(videoDetails.streamingPlaylists).to.have.lengthOf(1)
@@ -115,35 +133,55 @@ async function completeCheckHlsPlaylist (options: {
       const file = hlsFiles.find(f => f.resolution.id === resolution)
       expect(file).to.not.be.undefined
 
-      expect(file.magnetUri).to.have.lengthOf.above(2)
-      expect(file.torrentUrl).to.match(
-        new RegExp(`${server.url}/lazy-static/torrents/${uuidRegex}-${file.resolution.id}-hls.torrent`)
-      )
-
-      if (objectStorageBaseUrl) {
-        expectStartWith(file.fileUrl, objectStorageBaseUrl)
+      if (file.resolution.id === VideoResolution.H_NOVIDEO) {
+        expect(file.resolution.label).to.equal('Audio')
       } else {
-        expect(file.fileUrl).to.match(
-          new RegExp(`${baseUrl}/static/streaming-playlists/hls/${videoDetails.uuid}/${uuidRegex}-${file.resolution.id}-fragmented.mp4`)
-        )
+        expect(file.resolution.label).to.equal(resolution + 'p')
       }
 
-      expect(file.resolution.label).to.equal(resolution + 'p')
-
-      await makeRawRequest({ url: file.torrentUrl, expectedStatus: HttpStatusCode.OK_200 })
-      await makeRawRequest({ url: file.fileUrl, expectedStatus: HttpStatusCode.OK_200 })
+      expect(file.magnetUri).to.have.lengthOf.above(2)
+      await checkWebTorrentWorks(file.magnetUri)
+
+      {
+        const nameReg = `${uuidRegex}-${file.resolution.id}`
+
+        expect(file.torrentUrl).to.match(new RegExp(`${server.url}/lazy-static/torrents/${nameReg}-hls.torrent`))
+
+        if (objectStorageBaseUrl && requiresAuth) {
+          // eslint-disable-next-line max-len
+          expect(file.fileUrl).to.match(new RegExp(`${server.url}/object-storage-proxy/streaming-playlists/hls/${privatePath}${videoDetails.uuid}/${nameReg}-fragmented.mp4`))
+        } else if (objectStorageBaseUrl) {
+          expectStartWith(file.fileUrl, objectStorageBaseUrl)
+        } else {
+          expect(file.fileUrl).to.match(
+            new RegExp(`${baseUrl}/static/streaming-playlists/hls/${privatePath}${videoDetails.uuid}/${nameReg}-fragmented.mp4`)
+          )
+        }
+      }
 
-      const torrent = await webtorrentAdd(file.magnetUri, true)
-      expect(torrent.files).to.be.an('array')
-      expect(torrent.files.length).to.equal(1)
-      expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+      {
+        await Promise.all([
+          makeRawRequest({ url: file.torrentUrl, token, expectedStatus: HttpStatusCode.OK_200 }),
+          makeRawRequest({ url: file.torrentDownloadUrl, token, expectedStatus: HttpStatusCode.OK_200 }),
+          makeRawRequest({ url: file.metadataUrl, token, expectedStatus: HttpStatusCode.OK_200 }),
+          makeRawRequest({ url: file.fileUrl, token, expectedStatus: HttpStatusCode.OK_200 }),
+
+          makeRawRequest({
+            url: file.fileDownloadUrl,
+            token,
+            expectedStatus: objectStorageBaseUrl
+              ? HttpStatusCode.FOUND_302
+              : HttpStatusCode.OK_200
+          })
+        ])
+      }
     }
 
     // Check master playlist
     {
-      await checkResolutionsInMasterPlaylist({ server, playlistUrl: hlsPlaylist.playlistUrl, resolutions })
+      await checkResolutionsInMasterPlaylist({ server, token, playlistUrl: hlsPlaylist.playlistUrl, resolutions })
 
-      const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl })
+      const masterPlaylist = await server.streamingPlaylists.get({ url: hlsPlaylist.playlistUrl, token })
 
       let i = 0
       for (const resolution of resolutions) {
@@ -163,11 +201,16 @@ async function completeCheckHlsPlaylist (options: {
         const file = hlsFiles.find(f => f.resolution.id === resolution)
         const playlistName = removeFragmentedMP4Ext(basename(file.fileUrl)) + '.m3u8'
 
-        const url = objectStorageBaseUrl
-          ? `${objectStorageBaseUrl}hls/${videoUUID}/${playlistName}`
-          : `${baseUrl}/static/streaming-playlists/hls/${videoUUID}/${playlistName}`
+        let url: string
+        if (objectStorageBaseUrl && requiresAuth) {
+          url = `${baseUrl}/object-storage-proxy/streaming-playlists/hls/${privatePath}${videoUUID}/${playlistName}`
+        } else if (objectStorageBaseUrl) {
+          url = `${objectStorageBaseUrl}hls/${videoUUID}/${playlistName}`
+        } else {
+          url = `${baseUrl}/static/streaming-playlists/hls/${privatePath}${videoUUID}/${playlistName}`
+        }
 
-        const subPlaylist = await server.streamingPlaylists.get({ url })
+        const subPlaylist = await server.streamingPlaylists.get({ url, token })
 
         expect(subPlaylist).to.match(new RegExp(`${uuidRegex}-${resolution}-fragmented.mp4`))
         expect(subPlaylist).to.contain(basename(file.fileUrl))
@@ -175,13 +218,19 @@ async function completeCheckHlsPlaylist (options: {
     }
 
     {
-      const baseUrlAndPath = objectStorageBaseUrl
-        ? objectStorageBaseUrl + 'hls/' + videoUUID
-        : baseUrl + '/static/streaming-playlists/hls/' + videoUUID
+      let baseUrlAndPath: string
+      if (objectStorageBaseUrl && requiresAuth) {
+        baseUrlAndPath = `${baseUrl}/object-storage-proxy/streaming-playlists/hls/${privatePath}${videoUUID}`
+      } else if (objectStorageBaseUrl) {
+        baseUrlAndPath = `${objectStorageBaseUrl}hls/${videoUUID}`
+      } else {
+        baseUrlAndPath = `${baseUrl}/static/streaming-playlists/hls/${privatePath}${videoUUID}`
+      }
 
       for (const resolution of resolutions) {
         await checkSegmentHash({
           server,
+          token,
           baseUrlPlaylist: baseUrlAndPath,
           baseUrlSegment: baseUrlAndPath,
           resolution,
@@ -214,7 +263,7 @@ async function checkVideoFileTokenReinjection (options: {
       ? i
       : `-${resolution}`
 
-    expect(text).to.contain(`${suffix}.m3u8?videoFileToken=${videoFileToken}`)
+    expect(text).to.contain(`${suffix}.m3u8?videoFileToken=${videoFileToken}&reinjectVideoFileToken=true`)
   }
 
   const resolutionPlaylists = extractResolutionPlaylistUrls(hls.playlistUrl, text)
@@ -228,6 +277,7 @@ async function checkVideoFileTokenReinjection (options: {
       : '.mp4'
 
     expect(text).to.contain(`${extension}?videoFileToken=${videoFileToken}`)
+    expect(text).not.to.contain(`reinjectVideoFileToken=true`)
   }
 }