]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - shared/server-commands/videos/live-command.ts
Correctly wait for live segment generation
[github/Chocobozzz/PeerTube.git] / shared / server-commands / videos / live-command.ts
index d804fd883b53287b2656b1fe5e369525e4e60215..73f4eefd331a94086c625b55b41c1898d0f6aeff 100644 (file)
@@ -12,9 +12,11 @@ import {
   ResultList,
   VideoCreateResult,
   VideoDetails,
+  VideoPrivacy,
   VideoState
 } from '@shared/models'
 import { unwrapBody } from '../requests'
+import { ObjectStorageCommand, PeerTubeServer } from '../server'
 import { AbstractCommand, OverrideCommandOptions } from '../shared'
 import { sendRTMPStream, testFfmpegStreamError } from './live'
 
@@ -34,6 +36,8 @@ export class LiveCommand extends AbstractCommand {
     })
   }
 
+  // ---------------------------------------------------------------------------
+
   listSessions (options: OverrideCommandOptions & {
     videoId: number | string
   }) {
@@ -70,6 +74,8 @@ export class LiveCommand extends AbstractCommand {
     })
   }
 
+  // ---------------------------------------------------------------------------
+
   update (options: OverrideCommandOptions & {
     videoId: number | string
     fields: LiveVideoUpdate
@@ -110,6 +116,34 @@ export class LiveCommand extends AbstractCommand {
     return body.video
   }
 
+  async quickCreate (options: OverrideCommandOptions & {
+    saveReplay: boolean
+    permanentLive: boolean
+    privacy?: VideoPrivacy
+  }) {
+    const { saveReplay, permanentLive, privacy = VideoPrivacy.PUBLIC } = options
+
+    const { uuid } = await this.create({
+      ...options,
+
+      fields: {
+        name: 'live',
+        permanentLive,
+        saveReplay,
+        replaySettings: { privacy },
+        channelId: this.server.store.channel.id,
+        privacy
+      }
+    })
+
+    const video = await this.server.videos.getWithToken({ id: uuid })
+    const live = await this.get({ videoId: uuid })
+
+    return { video, live }
+  }
+
+  // ---------------------------------------------------------------------------
+
   async sendRTMPStreamInVideo (options: OverrideCommandOptions & {
     videoId: number | string
     fixtureName?: string
@@ -130,6 +164,8 @@ export class LiveCommand extends AbstractCommand {
     return testFfmpegStreamError(command, options.shouldHaveError)
   }
 
+  // ---------------------------------------------------------------------------
+
   waitUntilPublished (options: OverrideCommandOptions & {
     videoId: number | string
   }) {
@@ -151,27 +187,90 @@ export class LiveCommand extends AbstractCommand {
     return this.waitUntilState({ videoId, state: VideoState.LIVE_ENDED })
   }
 
-  waitUntilSegmentGeneration (options: OverrideCommandOptions & {
+  async waitUntilSegmentGeneration (options: OverrideCommandOptions & {
+    server: PeerTubeServer
     videoUUID: string
     playlistNumber: number
     segment: number
-    totalSessions?: number
+    objectStorage: boolean
+    objectStorageBaseUrl?: string
   }) {
-    const { playlistNumber, segment, videoUUID, totalSessions = 1 } = options
+    const {
+      server,
+      objectStorage,
+      playlistNumber,
+      segment,
+      videoUUID,
+      objectStorageBaseUrl = ObjectStorageCommand.getMockPlaylistBaseUrl()
+    } = options
+
     const segmentName = `${playlistNumber}-00000${segment}.ts`
+    const baseUrl = objectStorage
+      ? join(objectStorageBaseUrl, 'hls')
+      : server.url + '/static/streaming-playlists/hls'
+
+    let error = true
+
+    while (error) {
+      try {
+        // Check fragment exists
+        await this.getRawRequest({
+          ...options,
+
+          url: `${baseUrl}/${videoUUID}/${segmentName}`,
+          implicitToken: false,
+          defaultExpectedStatus: HttpStatusCode.OK_200
+        })
+
+        const video = await server.videos.get({ id: videoUUID })
+        const hlsPlaylist = video.streamingPlaylists[0]
+
+        // Check SHA generation
+        const shaBody = await server.streamingPlaylists.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url, withRetry: objectStorage })
+        if (!shaBody[segmentName]) {
+          throw new Error('Segment SHA does not exist')
+        }
+
+        // Check fragment is in m3u8 playlist
+        const subPlaylist = await server.streamingPlaylists.get({ url: `${baseUrl}/${video.uuid}/${playlistNumber}.m3u8` })
+        if (!subPlaylist.includes(segmentName)) throw new Error('Fragment does not exist in playlist')
+
+        error = false
+      } catch {
+        error = true
+        await wait(100)
+      }
+    }
+  }
+
+  async waitUntilReplacedByReplay (options: OverrideCommandOptions & {
+    videoId: number | string
+  }) {
+    let video: VideoDetails
+
+    do {
+      video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId })
 
-    return this.server.servers.waitUntilLog(`${videoUUID}/${segmentName}`, totalSessions * 2, false)
+      await wait(500)
+    } while (video.isLive === true || video.state.id !== VideoState.PUBLISHED)
   }
 
-  getSegment (options: OverrideCommandOptions & {
+  // ---------------------------------------------------------------------------
+
+  getSegmentFile (options: OverrideCommandOptions & {
     videoUUID: string
     playlistNumber: number
     segment: number
+    objectStorage?: boolean // default false
   }) {
-    const { playlistNumber, segment, videoUUID } = options
+    const { playlistNumber, segment, videoUUID, objectStorage = false } = options
 
     const segmentName = `${playlistNumber}-00000${segment}.ts`
-    const url = `${this.server.url}/static/streaming-playlists/hls/${videoUUID}/${segmentName}`
+    const baseUrl = objectStorage
+      ? ObjectStorageCommand.getMockPlaylistBaseUrl()
+      : `${this.server.url}/static/streaming-playlists/hls`
+
+    const url = `${baseUrl}/${videoUUID}/${segmentName}`
 
     return this.getRawRequest({
       ...options,
@@ -182,18 +281,30 @@ export class LiveCommand extends AbstractCommand {
     })
   }
 
-  async waitUntilReplacedByReplay (options: OverrideCommandOptions & {
-    videoId: number | string
+  getPlaylistFile (options: OverrideCommandOptions & {
+    videoUUID: string
+    playlistName: string
+    objectStorage?: boolean // default false
   }) {
-    let video: VideoDetails
+    const { playlistName, videoUUID, objectStorage = false } = options
 
-    do {
-      video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId })
+    const baseUrl = objectStorage
+      ? ObjectStorageCommand.getMockPlaylistBaseUrl()
+      : `${this.server.url}/static/streaming-playlists/hls`
 
-      await wait(500)
-    } while (video.isLive === true || video.state.id !== VideoState.PUBLISHED)
+    const url = `${baseUrl}/${videoUUID}/${playlistName}`
+
+    return this.getRawRequest({
+      ...options,
+
+      url,
+      implicitToken: false,
+      defaultExpectedStatus: HttpStatusCode.OK_200
+    })
   }
 
+  // ---------------------------------------------------------------------------
+
   async countPlaylists (options: OverrideCommandOptions & {
     videoUUID: string
   }) {