mirror of https://github.com/Chocobozzz/PeerTube
				
				
				
			
		
			
				
	
	
		
			181 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
			
		
		
	
	
			181 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			TypeScript
		
	
	
/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
 | 
						|
 | 
						|
import { expect } from 'chai'
 | 
						|
import { pathExists, readdir } from 'fs-extra'
 | 
						|
import { join } from 'path'
 | 
						|
import { sha1 } from '@shared/extra-utils'
 | 
						|
import { LiveVideo, VideoStreamingPlaylistType } from '@shared/models'
 | 
						|
import { ObjectStorageCommand, PeerTubeServer } from '@shared/server-commands'
 | 
						|
import { checkLiveSegmentHash, checkResolutionsInMasterPlaylist } from './streaming-playlists'
 | 
						|
 | 
						|
async function checkLiveCleanup (options: {
 | 
						|
  server: PeerTubeServer
 | 
						|
  videoUUID: string
 | 
						|
  permanent: boolean
 | 
						|
  savedResolutions?: number[]
 | 
						|
}) {
 | 
						|
  const { server, videoUUID, permanent, savedResolutions = [] } = options
 | 
						|
 | 
						|
  const basePath = server.servers.buildDirectory('streaming-playlists')
 | 
						|
  const hlsPath = join(basePath, 'hls', videoUUID)
 | 
						|
 | 
						|
  if (permanent) {
 | 
						|
    if (!await pathExists(hlsPath)) return
 | 
						|
 | 
						|
    const files = await readdir(hlsPath)
 | 
						|
    expect(files).to.have.lengthOf(0)
 | 
						|
    return
 | 
						|
  }
 | 
						|
 | 
						|
  if (savedResolutions.length === 0) {
 | 
						|
    return checkUnsavedLiveCleanup(server, videoUUID, hlsPath)
 | 
						|
  }
 | 
						|
 | 
						|
  return checkSavedLiveCleanup(hlsPath, savedResolutions)
 | 
						|
}
 | 
						|
 | 
						|
// ---------------------------------------------------------------------------
 | 
						|
 | 
						|
async function testVideoResolutions (options: {
 | 
						|
  originServer: PeerTubeServer
 | 
						|
  servers: PeerTubeServer[]
 | 
						|
  liveVideoId: string
 | 
						|
  resolutions: number[]
 | 
						|
  transcoded: boolean
 | 
						|
 | 
						|
  objectStorage: boolean
 | 
						|
  objectStorageBaseUrl?: string
 | 
						|
}) {
 | 
						|
  const {
 | 
						|
    originServer,
 | 
						|
    servers,
 | 
						|
    liveVideoId,
 | 
						|
    resolutions,
 | 
						|
    transcoded,
 | 
						|
    objectStorage,
 | 
						|
    objectStorageBaseUrl = ObjectStorageCommand.getMockPlaylistBaseUrl()
 | 
						|
  } = options
 | 
						|
 | 
						|
  for (const server of servers) {
 | 
						|
    const { data } = await server.videos.list()
 | 
						|
    expect(data.find(v => v.uuid === liveVideoId)).to.exist
 | 
						|
 | 
						|
    const video = await server.videos.get({ id: liveVideoId })
 | 
						|
    expect(video.streamingPlaylists).to.have.lengthOf(1)
 | 
						|
 | 
						|
    const hlsPlaylist = video.streamingPlaylists.find(s => s.type === VideoStreamingPlaylistType.HLS)
 | 
						|
    expect(hlsPlaylist).to.exist
 | 
						|
    expect(hlsPlaylist.files).to.have.lengthOf(0) // Only fragmented mp4 files are displayed
 | 
						|
 | 
						|
    await checkResolutionsInMasterPlaylist({
 | 
						|
      server,
 | 
						|
      playlistUrl: hlsPlaylist.playlistUrl,
 | 
						|
      resolutions,
 | 
						|
      transcoded,
 | 
						|
      withRetry: objectStorage
 | 
						|
    })
 | 
						|
 | 
						|
    if (objectStorage) {
 | 
						|
      expect(hlsPlaylist.playlistUrl).to.contain(objectStorageBaseUrl)
 | 
						|
    }
 | 
						|
 | 
						|
    for (let i = 0; i < resolutions.length; i++) {
 | 
						|
      const segmentNum = 3
 | 
						|
      const segmentName = `${i}-00000${segmentNum}.ts`
 | 
						|
      await originServer.live.waitUntilSegmentGeneration({
 | 
						|
        server: originServer,
 | 
						|
        videoUUID: video.uuid,
 | 
						|
        playlistNumber: i,
 | 
						|
        segment: segmentNum,
 | 
						|
        objectStorage,
 | 
						|
        objectStorageBaseUrl
 | 
						|
      })
 | 
						|
 | 
						|
      const baseUrl = objectStorage
 | 
						|
        ? join(objectStorageBaseUrl, 'hls')
 | 
						|
        : originServer.url + '/static/streaming-playlists/hls'
 | 
						|
 | 
						|
      if (objectStorage) {
 | 
						|
        expect(hlsPlaylist.segmentsSha256Url).to.contain(objectStorageBaseUrl)
 | 
						|
      }
 | 
						|
 | 
						|
      const subPlaylist = await originServer.streamingPlaylists.get({
 | 
						|
        url: `${baseUrl}/${video.uuid}/${i}.m3u8`,
 | 
						|
        withRetry: objectStorage // With object storage, the request may fail because of inconsistent data in S3
 | 
						|
      })
 | 
						|
 | 
						|
      expect(subPlaylist).to.contain(segmentName)
 | 
						|
 | 
						|
      await checkLiveSegmentHash({
 | 
						|
        server,
 | 
						|
        baseUrlSegment: baseUrl,
 | 
						|
        videoUUID: video.uuid,
 | 
						|
        segmentName,
 | 
						|
        hlsPlaylist
 | 
						|
      })
 | 
						|
 | 
						|
      if (originServer.internalServerNumber === server.internalServerNumber) {
 | 
						|
        const infohash = sha1(`${2 + hlsPlaylist.playlistUrl}+V${i}`)
 | 
						|
        const dbInfohashes = await originServer.sql.getPlaylistInfohash(hlsPlaylist.id)
 | 
						|
 | 
						|
        expect(dbInfohashes).to.include(infohash)
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// ---------------------------------------------------------------------------
 | 
						|
 | 
						|
export {
 | 
						|
  checkLiveCleanup,
 | 
						|
  testVideoResolutions
 | 
						|
}
 | 
						|
 | 
						|
// ---------------------------------------------------------------------------
 | 
						|
 | 
						|
async function checkSavedLiveCleanup (hlsPath: string, savedResolutions: number[] = []) {
 | 
						|
  const files = await readdir(hlsPath)
 | 
						|
 | 
						|
  // fragmented file and playlist per resolution + master playlist + segments sha256 json file
 | 
						|
  expect(files).to.have.lengthOf(savedResolutions.length * 2 + 2)
 | 
						|
 | 
						|
  for (const resolution of savedResolutions) {
 | 
						|
    const fragmentedFile = files.find(f => f.endsWith(`-${resolution}-fragmented.mp4`))
 | 
						|
    expect(fragmentedFile).to.exist
 | 
						|
 | 
						|
    const playlistFile = files.find(f => f.endsWith(`${resolution}.m3u8`))
 | 
						|
    expect(playlistFile).to.exist
 | 
						|
  }
 | 
						|
 | 
						|
  const masterPlaylistFile = files.find(f => f.endsWith('-master.m3u8'))
 | 
						|
  expect(masterPlaylistFile).to.exist
 | 
						|
 | 
						|
  const shaFile = files.find(f => f.endsWith('-segments-sha256.json'))
 | 
						|
  expect(shaFile).to.exist
 | 
						|
}
 | 
						|
 | 
						|
async function checkUnsavedLiveCleanup (server: PeerTubeServer, videoUUID: string, hlsPath: string) {
 | 
						|
  let live: LiveVideo
 | 
						|
 | 
						|
  try {
 | 
						|
    live = await server.live.get({ videoId: videoUUID })
 | 
						|
  } catch {}
 | 
						|
 | 
						|
  if (live?.permanentLive) {
 | 
						|
    expect(await pathExists(hlsPath)).to.be.true
 | 
						|
 | 
						|
    const hlsFiles = await readdir(hlsPath)
 | 
						|
    expect(hlsFiles).to.have.lengthOf(1) // Only replays directory
 | 
						|
 | 
						|
    const replayDir = join(hlsPath, 'replay')
 | 
						|
    expect(await pathExists(replayDir)).to.be.true
 | 
						|
 | 
						|
    const replayFiles = await readdir(join(hlsPath, 'replay'))
 | 
						|
    expect(replayFiles).to.have.lengthOf(0)
 | 
						|
 | 
						|
    return
 | 
						|
  }
 | 
						|
 | 
						|
  expect(await pathExists(hlsPath)).to.be.false
 | 
						|
}
 |