X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=shared%2Fserver-commands%2Fvideos%2Flive-command.ts;h=dc3c5a86ef8f45862665448648bcf47644633f00;hb=fabe350bcac2d24c8524d4b6bb776dba7e374c8d;hp=f7816eca0d592292b5d2eb32ff6d672a378f9d3f;hpb=3318147300b4f998adf728eb0a5a14a4c1829c51;p=github%2FChocobozzz%2FPeerTube.git diff --git a/shared/server-commands/videos/live-command.ts b/shared/server-commands/videos/live-command.ts index f7816eca0..dc3c5a86e 100644 --- a/shared/server-commands/videos/live-command.ts +++ b/shared/server-commands/videos/live-command.ts @@ -1,11 +1,22 @@ /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */ import { readdir } from 'fs-extra' -import { omit } from 'lodash' import { join } from 'path' -import { wait } from '@shared/core-utils' -import { HttpStatusCode, LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models' +import { omit, wait } from '@shared/core-utils' +import { + HttpStatusCode, + LiveVideo, + LiveVideoCreate, + LiveVideoSession, + LiveVideoUpdate, + 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' @@ -25,6 +36,46 @@ export class LiveCommand extends AbstractCommand { }) } + // --------------------------------------------------------------------------- + + listSessions (options: OverrideCommandOptions & { + videoId: number | string + }) { + const path = `/api/v1/videos/live/${options.videoId}/sessions` + + return this.getRequestBody>({ + ...options, + + path, + implicitToken: true, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } + + async findLatestSession (options: OverrideCommandOptions & { + videoId: number | string + }) { + const { data: sessions } = await this.listSessions(options) + + return sessions[sessions.length - 1] + } + + getReplaySession (options: OverrideCommandOptions & { + videoId: number | string + }) { + const path = `/api/v1/videos/${options.videoId}/live-session` + + return this.getRequestBody({ + ...options, + + path, + implicitToken: true, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } + + // --------------------------------------------------------------------------- + update (options: OverrideCommandOptions & { videoId: number | string fields: LiveVideoUpdate @@ -57,7 +108,7 @@ export class LiveCommand extends AbstractCommand { path, attaches, - fields: omit(fields, 'thumbnailfile', 'previewfile'), + fields: omit(fields, [ 'thumbnailfile', 'previewfile' ]), implicitToken: true, defaultExpectedStatus: HttpStatusCode.OK_200 })) @@ -65,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 @@ -85,6 +164,8 @@ export class LiveCommand extends AbstractCommand { return testFfmpegStreamError(command, options.shouldHaveError) } + // --------------------------------------------------------------------------- + waitUntilPublished (options: OverrideCommandOptions & { videoId: number | string }) { @@ -106,18 +187,58 @@ export class LiveCommand extends AbstractCommand { return this.waitUntilState({ videoId, state: VideoState.LIVE_ENDED }) } - waitUntilSegmentGeneration (options: OverrideCommandOptions & { + async waitUntilSegmentGeneration (options: OverrideCommandOptions & { + server: PeerTubeServer videoUUID: string - resolution: number + playlistNumber: number segment: number + objectStorage: boolean + objectStorageBaseUrl?: string }) { - const { resolution, segment, videoUUID } = options - const segmentName = `${resolution}-00000${segment}.ts` + 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 { + 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] - return this.server.servers.waitUntilLog(`${videoUUID}/${segmentName}`, 2, false) + const shaBody = await server.streamingPlaylists.getSegmentSha256({ url: hlsPlaylist.segmentsSha256Url }) + + if (!shaBody[segmentName]) { + throw new Error('Segment SHA does not exist') + } + + error = false + } catch { + error = true + await wait(100) + } + } } - async waitUntilSaved (options: OverrideCommandOptions & { + async waitUntilReplacedByReplay (options: OverrideCommandOptions & { videoId: number | string }) { let video: VideoDetails @@ -129,6 +250,56 @@ export class LiveCommand extends AbstractCommand { } while (video.isLive === true || video.state.id !== VideoState.PUBLISHED) } + // --------------------------------------------------------------------------- + + getSegmentFile (options: OverrideCommandOptions & { + videoUUID: string + playlistNumber: number + segment: number + objectStorage?: boolean // default false + }) { + const { playlistNumber, segment, videoUUID, objectStorage = false } = options + + const segmentName = `${playlistNumber}-00000${segment}.ts` + const baseUrl = objectStorage + ? ObjectStorageCommand.getMockPlaylistBaseUrl() + : `${this.server.url}/static/streaming-playlists/hls` + + const url = `${baseUrl}/${videoUUID}/${segmentName}` + + return this.getRawRequest({ + ...options, + + url, + implicitToken: false, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } + + getPlaylistFile (options: OverrideCommandOptions & { + videoUUID: string + playlistName: string + objectStorage?: boolean // default false + }) { + const { playlistName, videoUUID, objectStorage = false } = options + + const baseUrl = objectStorage + ? ObjectStorageCommand.getMockPlaylistBaseUrl() + : `${this.server.url}/static/streaming-playlists/hls` + + const url = `${baseUrl}/${videoUUID}/${playlistName}` + + return this.getRawRequest({ + ...options, + + url, + implicitToken: false, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } + + // --------------------------------------------------------------------------- + async countPlaylists (options: OverrideCommandOptions & { videoUUID: string }) {