From 4f2199144e428c16460750305f737b890c1ac322 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 8 Jul 2021 10:18:40 +0200 Subject: Introduce live command --- shared/extra-utils/index.ts | 16 +-- shared/extra-utils/requests/index.ts | 3 + shared/extra-utils/server/servers.ts | 3 + shared/extra-utils/shared/abstract-command.ts | 32 +++++- shared/extra-utils/users/index.ts | 1 - shared/extra-utils/videos/index.ts | 13 +++ shared/extra-utils/videos/live-command.ts | 156 ++++++++++++++++++++++++++ shared/extra-utils/videos/live.ts | 139 +---------------------- 8 files changed, 214 insertions(+), 149 deletions(-) create mode 100644 shared/extra-utils/requests/index.ts create mode 100644 shared/extra-utils/videos/index.ts create mode 100644 shared/extra-utils/videos/live-command.ts (limited to 'shared') diff --git a/shared/extra-utils/index.ts b/shared/extra-utils/index.ts index 68900af26..4b3636d06 100644 --- a/shared/extra-utils/index.ts +++ b/shared/extra-utils/index.ts @@ -7,21 +7,9 @@ export * from './miscs' export * from './mock-servers' export * from './moderation' export * from './overviews' +export * from './requests' export * from './search' export * from './server' export * from './socket' export * from './users' - -export * from './requests/check-api-params' -export * from './requests/requests' - -export * from './videos/live' -export * from './videos/services' -export * from './videos/video-blacklist' -export * from './videos/video-captions' -export * from './videos/video-change-ownership' -export * from './videos/video-channels' -export * from './videos/video-comments' -export * from './videos/video-playlists' -export * from './videos/video-streaming-playlists' -export * from './videos/videos' +export * from './videos' diff --git a/shared/extra-utils/requests/index.ts b/shared/extra-utils/requests/index.ts new file mode 100644 index 000000000..501163f92 --- /dev/null +++ b/shared/extra-utils/requests/index.ts @@ -0,0 +1,3 @@ +// Don't include activitypub that import stuff from server +export * from './check-api-params' +export * from './requests' diff --git a/shared/extra-utils/server/servers.ts b/shared/extra-utils/server/servers.ts index 57b37728a..eca0689aa 100644 --- a/shared/extra-utils/server/servers.ts +++ b/shared/extra-utils/server/servers.ts @@ -18,6 +18,7 @@ import { makeGetRequest } from '../requests/requests' import { SearchCommand } from '../search' import { SocketIOCommand } from '../socket' import { AccountsCommand, BlocklistCommand, SubscriptionsCommand } from '../users' +import { LiveCommand } from '../videos' import { ConfigCommand } from './config-command' import { ContactFormCommand } from './contact-form-command' import { DebugCommand } from './debug-command' @@ -99,6 +100,7 @@ interface ServerInfo { accountsCommand?: AccountsCommand blocklistCommand?: BlocklistCommand subscriptionsCommand?: SubscriptionsCommand + liveCommand?: LiveCommand } function parallelTests () { @@ -324,6 +326,7 @@ async function runServer (server: ServerInfo, configOverrideArg?: any, args = [] server.accountsCommand = new AccountsCommand(server) server.blocklistCommand = new BlocklistCommand(server) server.subscriptionsCommand = new SubscriptionsCommand(server) + server.liveCommand = new LiveCommand(server) res(server) }) diff --git a/shared/extra-utils/shared/abstract-command.ts b/shared/extra-utils/shared/abstract-command.ts index dd4598a91..38129d559 100644 --- a/shared/extra-utils/shared/abstract-command.ts +++ b/shared/extra-utils/shared/abstract-command.ts @@ -1,5 +1,5 @@ import { HttpStatusCode } from '@shared/core-utils' -import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest, unwrapBody, unwrapText } from '../requests/requests' +import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest, makeUploadRequest, unwrapBody, unwrapText } from '../requests/requests' import { ServerInfo } from '../server/servers' export interface OverrideCommandOptions { @@ -86,6 +86,36 @@ abstract class AbstractCommand { }) } + protected postUploadRequest (options: CommonCommandOptions & { + fields?: { [ fieldName: string ]: any } + attaches?: any + }) { + const { fields, attaches } = options + + return makeUploadRequest({ + ...this.buildCommonRequestOptions(options), + + method: 'POST', + fields, + attaches + }) + } + + protected putUploadRequest (options: CommonCommandOptions & { + fields?: { [ fieldName: string ]: any } + attaches?: any + }) { + const { fields, attaches } = options + + return makeUploadRequest({ + ...this.buildCommonRequestOptions(options), + + method: 'PUT', + fields, + attaches + }) + } + private buildCommonRequestOptions (options: CommonCommandOptions) { const { token, expectedStatus, defaultExpectedStatus, path } = options diff --git a/shared/extra-utils/users/index.ts b/shared/extra-utils/users/index.ts index 94ad37242..9f760d7fd 100644 --- a/shared/extra-utils/users/index.ts +++ b/shared/extra-utils/users/index.ts @@ -1,7 +1,6 @@ export * from './accounts-command' export * from './accounts' export * from './blocklist-command' - export * from './login' export * from './user-notifications' export * from './subscriptions-command' diff --git a/shared/extra-utils/videos/index.ts b/shared/extra-utils/videos/index.ts new file mode 100644 index 000000000..c9c884285 --- /dev/null +++ b/shared/extra-utils/videos/index.ts @@ -0,0 +1,13 @@ +export * from './live-command' +export * from './live' +export * from './services' +export * from './video-blacklist' +export * from './video-captions' +export * from './video-change-ownership' +export * from './video-channels' +export * from './video-comments' +export * from './video-history' +export * from './video-imports' +export * from './video-playlists' +export * from './video-streaming-playlists' +export * from './videos' diff --git a/shared/extra-utils/videos/live-command.ts b/shared/extra-utils/videos/live-command.ts new file mode 100644 index 000000000..55811b8ba --- /dev/null +++ b/shared/extra-utils/videos/live-command.ts @@ -0,0 +1,156 @@ +/* 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 { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models' +import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes' +import { buildServerDirectory, wait } from '../miscs/miscs' +import { unwrapBody } from '../requests' +import { waitUntilLog } from '../server/servers' +import { AbstractCommand, OverrideCommandOptions } from '../shared' +import { sendRTMPStream, testFfmpegStreamError } from './live' +import { getVideoWithToken } from './videos' + +export class LiveCommand extends AbstractCommand { + + getLive (options: OverrideCommandOptions & { + videoId: number | string + }) { + const path = '/api/v1/videos/live' + + return this.getRequestBody({ + ...options, + + path: path + '/' + options.videoId, + defaultExpectedStatus: HttpStatusCode.OK_200 + }) + } + + updateLive (options: OverrideCommandOptions & { + videoId: number | string + fields: LiveVideoUpdate + }) { + const { videoId, fields } = options + const path = '/api/v1/videos/live' + + return this.putBodyRequest({ + ...options, + + path: path + '/' + videoId, + fields, + defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 + }) + } + + async createLive (options: OverrideCommandOptions & { + fields: LiveVideoCreate + }) { + const { fields } = options + const path = '/api/v1/videos/live' + + const attaches: any = {} + if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile + if (fields.previewfile) attaches.previewfile = fields.previewfile + + const body = await unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({ + ...options, + + path, + attaches, + fields: omit(fields, 'thumbnailfile', 'previewfile'), + defaultExpectedStatus: HttpStatusCode.OK_200 + })) + + return body.video + } + + async sendRTMPStreamInVideo (options: OverrideCommandOptions & { + videoId: number | string + fixtureName?: string + }) { + const { videoId, fixtureName } = options + const videoLive = await this.getLive({ videoId }) + + return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName) + } + + async runAndTestFfmpegStreamError (options: OverrideCommandOptions & { + videoId: number | string + shouldHaveError: boolean + }) { + const command = await this.sendRTMPStreamInVideo(options) + + return testFfmpegStreamError(command, options.shouldHaveError) + } + + waitUntilLivePublished (options: OverrideCommandOptions & { + videoId: number | string + }) { + const { videoId } = options + return this.waitUntilLiveState({ videoId, state: VideoState.PUBLISHED }) + } + + waitUntilLiveWaiting (options: OverrideCommandOptions & { + videoId: number | string + }) { + const { videoId } = options + return this.waitUntilLiveState({ videoId, state: VideoState.WAITING_FOR_LIVE }) + } + + waitUntilLiveEnded (options: OverrideCommandOptions & { + videoId: number | string + }) { + const { videoId } = options + return this.waitUntilLiveState({ videoId, state: VideoState.LIVE_ENDED }) + } + + waitUntilLiveSegmentGeneration (options: OverrideCommandOptions & { + videoUUID: string + resolution: number + segment: number + }) { + const { resolution, segment, videoUUID } = options + const segmentName = `${resolution}-00000${segment}.ts` + + return waitUntilLog(this.server, `${videoUUID}/${segmentName}`, 2, false) + } + + async waitUntilLiveSaved (options: OverrideCommandOptions & { + videoId: number | string + }) { + let video: VideoDetails + + do { + const res = await getVideoWithToken(this.server.url, options.token ?? this.server.accessToken, options.videoId) + video = res.body + + await wait(500) + } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED) + } + + async getPlaylistsCount (options: OverrideCommandOptions & { + videoUUID: string + }) { + const basePath = buildServerDirectory(this.server, 'streaming-playlists') + const hlsPath = join(basePath, 'hls', options.videoUUID) + + const files = await readdir(hlsPath) + + return files.filter(f => f.endsWith('.m3u8')).length + } + + private async waitUntilLiveState (options: OverrideCommandOptions & { + videoId: number | string + state: VideoState + }) { + let video: VideoDetails + + do { + const res = await getVideoWithToken(this.server.url, options.token ?? this.server.accessToken, options.videoId) + video = res.body + + await wait(500) + } while (video.state.id !== options.state) + } +} diff --git a/shared/extra-utils/videos/live.ts b/shared/extra-utils/videos/live.ts index c0384769b..285a39c7e 100644 --- a/shared/extra-utils/videos/live.ts +++ b/shared/extra-utils/videos/live.ts @@ -3,69 +3,9 @@ import { expect } from 'chai' import * as ffmpeg from 'fluent-ffmpeg' import { pathExists, readdir } from 'fs-extra' -import { omit } from 'lodash' import { join } from 'path' -import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoDetails, VideoState } from '@shared/models' -import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes' import { buildAbsoluteFixturePath, buildServerDirectory, wait } from '../miscs/miscs' -import { makeGetRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests' -import { ServerInfo, waitUntilLog } from '../server/servers' -import { getVideoWithToken } from './videos' - -function getLive (url: string, token: string, videoId: number | string, statusCodeExpected = HttpStatusCode.OK_200) { - const path = '/api/v1/videos/live' - - return makeGetRequest({ - url, - token, - path: path + '/' + videoId, - statusCodeExpected - }) -} - -function updateLive ( - url: string, - token: string, - videoId: number | string, - fields: LiveVideoUpdate, - statusCodeExpected = HttpStatusCode.NO_CONTENT_204 -) { - const path = '/api/v1/videos/live' - - return makePutBodyRequest({ - url, - token, - path: path + '/' + videoId, - fields, - statusCodeExpected - }) -} - -function createLive (url: string, token: string, fields: LiveVideoCreate, statusCodeExpected = HttpStatusCode.OK_200) { - const path = '/api/v1/videos/live' - - const attaches: any = {} - if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile - if (fields.previewfile) attaches.previewfile = fields.previewfile - - const updatedFields = omit(fields, 'thumbnailfile', 'previewfile') - - return makeUploadRequest({ - url, - path, - token, - attaches, - fields: updatedFields, - statusCodeExpected - }) -} - -async function sendRTMPStreamInVideo (url: string, token: string, videoId: number | string, fixtureName?: string) { - const res = await getLive(url, token, videoId) - const videoLive = res.body as LiveVideo - - return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName) -} +import { ServerInfo } from '../server/servers' function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') { const fixture = buildAbsoluteFixturePath(fixtureName) @@ -109,12 +49,6 @@ function waitFfmpegUntilError (command: ffmpeg.FfmpegCommand, successAfterMS = 1 }) } -async function runAndTestFfmpegStreamError (url: string, token: string, videoId: number | string, shouldHaveError: boolean) { - const command = await sendRTMPStreamInVideo(url, token, videoId) - - return testFfmpegStreamError(command, shouldHaveError) -} - async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) { let error: Error @@ -136,48 +70,9 @@ async function stopFfmpeg (command: ffmpeg.FfmpegCommand) { await wait(500) } -function waitUntilLivePublished (url: string, token: string, videoId: number | string) { - return waitUntilLiveState(url, token, videoId, VideoState.PUBLISHED) -} - -function waitUntilLiveWaiting (url: string, token: string, videoId: number | string) { - return waitUntilLiveState(url, token, videoId, VideoState.WAITING_FOR_LIVE) -} - -function waitUntilLiveEnded (url: string, token: string, videoId: number | string) { - return waitUntilLiveState(url, token, videoId, VideoState.LIVE_ENDED) -} - -function waitUntilLiveSegmentGeneration (server: ServerInfo, videoUUID: string, resolutionNum: number, segmentNum: number) { - const segmentName = `${resolutionNum}-00000${segmentNum}.ts` - return waitUntilLog(server, `${videoUUID}/${segmentName}`, 2, false) -} - -async function waitUntilLiveState (url: string, token: string, videoId: number | string, state: VideoState) { - let video: VideoDetails - - do { - const res = await getVideoWithToken(url, token, videoId) - video = res.body - - await wait(500) - } while (video.state.id !== state) -} - -async function waitUntilLiveSaved (url: string, token: string, videoId: number | string) { - let video: VideoDetails - - do { - const res = await getVideoWithToken(url, token, videoId) - video = res.body - - await wait(500) - } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED) -} - async function waitUntilLivePublishedOnAllServers (servers: ServerInfo[], videoId: string) { for (const server of servers) { - await waitUntilLivePublished(server.url, server.accessToken, videoId) + await server.liveCommand.waitUntilLivePublished({ videoId }) } } @@ -206,33 +101,11 @@ async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resoluti expect(files).to.contain('segments-sha256.json') } -async function getPlaylistsCount (server: ServerInfo, videoUUID: string) { - const basePath = buildServerDirectory(server, 'streaming-playlists') - const hlsPath = join(basePath, 'hls', videoUUID) - - const files = await readdir(hlsPath) - - return files.filter(f => f.endsWith('.m3u8')).length -} - -// --------------------------------------------------------------------------- - export { - getLive, - getPlaylistsCount, - waitUntilLiveSaved, - waitUntilLivePublished, - updateLive, - createLive, - runAndTestFfmpegStreamError, - checkLiveCleanup, - waitUntilLiveSegmentGeneration, - stopFfmpeg, - waitUntilLiveWaiting, - sendRTMPStreamInVideo, - waitUntilLiveEnded, + sendRTMPStream, waitFfmpegUntilError, + testFfmpegStreamError, + stopFfmpeg, waitUntilLivePublishedOnAllServers, - sendRTMPStream, - testFfmpegStreamError + checkLiveCleanup } -- cgit v1.2.3