aboutsummaryrefslogtreecommitdiffhomepage
path: root/shared/extra-utils/videos
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2021-07-08 10:18:40 +0200
committerChocobozzz <me@florianbigard.com>2021-07-20 15:27:17 +0200
commit4f2199144e428c16460750305f737b890c1ac322 (patch)
treea19c5c0f254ab0b32d6c8838af33a1ba260e4877 /shared/extra-utils/videos
parent2c27e70471120c92e0bc8c8114141fbb31ff98ac (diff)
downloadPeerTube-4f2199144e428c16460750305f737b890c1ac322.tar.gz
PeerTube-4f2199144e428c16460750305f737b890c1ac322.tar.zst
PeerTube-4f2199144e428c16460750305f737b890c1ac322.zip
Introduce live command
Diffstat (limited to 'shared/extra-utils/videos')
-rw-r--r--shared/extra-utils/videos/index.ts13
-rw-r--r--shared/extra-utils/videos/live-command.ts156
-rw-r--r--shared/extra-utils/videos/live.ts139
3 files changed, 175 insertions, 133 deletions
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 @@
1export * from './live-command'
2export * from './live'
3export * from './services'
4export * from './video-blacklist'
5export * from './video-captions'
6export * from './video-change-ownership'
7export * from './video-channels'
8export * from './video-comments'
9export * from './video-history'
10export * from './video-imports'
11export * from './video-playlists'
12export * from './video-streaming-playlists'
13export * 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 @@
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { readdir } from 'fs-extra'
4import { omit } from 'lodash'
5import { join } from 'path'
6import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models'
7import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes'
8import { buildServerDirectory, wait } from '../miscs/miscs'
9import { unwrapBody } from '../requests'
10import { waitUntilLog } from '../server/servers'
11import { AbstractCommand, OverrideCommandOptions } from '../shared'
12import { sendRTMPStream, testFfmpegStreamError } from './live'
13import { getVideoWithToken } from './videos'
14
15export class LiveCommand extends AbstractCommand {
16
17 getLive (options: OverrideCommandOptions & {
18 videoId: number | string
19 }) {
20 const path = '/api/v1/videos/live'
21
22 return this.getRequestBody<LiveVideo>({
23 ...options,
24
25 path: path + '/' + options.videoId,
26 defaultExpectedStatus: HttpStatusCode.OK_200
27 })
28 }
29
30 updateLive (options: OverrideCommandOptions & {
31 videoId: number | string
32 fields: LiveVideoUpdate
33 }) {
34 const { videoId, fields } = options
35 const path = '/api/v1/videos/live'
36
37 return this.putBodyRequest({
38 ...options,
39
40 path: path + '/' + videoId,
41 fields,
42 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
43 })
44 }
45
46 async createLive (options: OverrideCommandOptions & {
47 fields: LiveVideoCreate
48 }) {
49 const { fields } = options
50 const path = '/api/v1/videos/live'
51
52 const attaches: any = {}
53 if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile
54 if (fields.previewfile) attaches.previewfile = fields.previewfile
55
56 const body = await unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({
57 ...options,
58
59 path,
60 attaches,
61 fields: omit(fields, 'thumbnailfile', 'previewfile'),
62 defaultExpectedStatus: HttpStatusCode.OK_200
63 }))
64
65 return body.video
66 }
67
68 async sendRTMPStreamInVideo (options: OverrideCommandOptions & {
69 videoId: number | string
70 fixtureName?: string
71 }) {
72 const { videoId, fixtureName } = options
73 const videoLive = await this.getLive({ videoId })
74
75 return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName)
76 }
77
78 async runAndTestFfmpegStreamError (options: OverrideCommandOptions & {
79 videoId: number | string
80 shouldHaveError: boolean
81 }) {
82 const command = await this.sendRTMPStreamInVideo(options)
83
84 return testFfmpegStreamError(command, options.shouldHaveError)
85 }
86
87 waitUntilLivePublished (options: OverrideCommandOptions & {
88 videoId: number | string
89 }) {
90 const { videoId } = options
91 return this.waitUntilLiveState({ videoId, state: VideoState.PUBLISHED })
92 }
93
94 waitUntilLiveWaiting (options: OverrideCommandOptions & {
95 videoId: number | string
96 }) {
97 const { videoId } = options
98 return this.waitUntilLiveState({ videoId, state: VideoState.WAITING_FOR_LIVE })
99 }
100
101 waitUntilLiveEnded (options: OverrideCommandOptions & {
102 videoId: number | string
103 }) {
104 const { videoId } = options
105 return this.waitUntilLiveState({ videoId, state: VideoState.LIVE_ENDED })
106 }
107
108 waitUntilLiveSegmentGeneration (options: OverrideCommandOptions & {
109 videoUUID: string
110 resolution: number
111 segment: number
112 }) {
113 const { resolution, segment, videoUUID } = options
114 const segmentName = `${resolution}-00000${segment}.ts`
115
116 return waitUntilLog(this.server, `${videoUUID}/${segmentName}`, 2, false)
117 }
118
119 async waitUntilLiveSaved (options: OverrideCommandOptions & {
120 videoId: number | string
121 }) {
122 let video: VideoDetails
123
124 do {
125 const res = await getVideoWithToken(this.server.url, options.token ?? this.server.accessToken, options.videoId)
126 video = res.body
127
128 await wait(500)
129 } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED)
130 }
131
132 async getPlaylistsCount (options: OverrideCommandOptions & {
133 videoUUID: string
134 }) {
135 const basePath = buildServerDirectory(this.server, 'streaming-playlists')
136 const hlsPath = join(basePath, 'hls', options.videoUUID)
137
138 const files = await readdir(hlsPath)
139
140 return files.filter(f => f.endsWith('.m3u8')).length
141 }
142
143 private async waitUntilLiveState (options: OverrideCommandOptions & {
144 videoId: number | string
145 state: VideoState
146 }) {
147 let video: VideoDetails
148
149 do {
150 const res = await getVideoWithToken(this.server.url, options.token ?? this.server.accessToken, options.videoId)
151 video = res.body
152
153 await wait(500)
154 } while (video.state.id !== options.state)
155 }
156}
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 @@
3import { expect } from 'chai' 3import { expect } from 'chai'
4import * as ffmpeg from 'fluent-ffmpeg' 4import * as ffmpeg from 'fluent-ffmpeg'
5import { pathExists, readdir } from 'fs-extra' 5import { pathExists, readdir } from 'fs-extra'
6import { omit } from 'lodash'
7import { join } from 'path' 6import { join } from 'path'
8import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoDetails, VideoState } from '@shared/models'
9import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
10import { buildAbsoluteFixturePath, buildServerDirectory, wait } from '../miscs/miscs' 7import { buildAbsoluteFixturePath, buildServerDirectory, wait } from '../miscs/miscs'
11import { makeGetRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests' 8import { ServerInfo } from '../server/servers'
12import { ServerInfo, waitUntilLog } from '../server/servers'
13import { getVideoWithToken } from './videos'
14
15function getLive (url: string, token: string, videoId: number | string, statusCodeExpected = HttpStatusCode.OK_200) {
16 const path = '/api/v1/videos/live'
17
18 return makeGetRequest({
19 url,
20 token,
21 path: path + '/' + videoId,
22 statusCodeExpected
23 })
24}
25
26function updateLive (
27 url: string,
28 token: string,
29 videoId: number | string,
30 fields: LiveVideoUpdate,
31 statusCodeExpected = HttpStatusCode.NO_CONTENT_204
32) {
33 const path = '/api/v1/videos/live'
34
35 return makePutBodyRequest({
36 url,
37 token,
38 path: path + '/' + videoId,
39 fields,
40 statusCodeExpected
41 })
42}
43
44function createLive (url: string, token: string, fields: LiveVideoCreate, statusCodeExpected = HttpStatusCode.OK_200) {
45 const path = '/api/v1/videos/live'
46
47 const attaches: any = {}
48 if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile
49 if (fields.previewfile) attaches.previewfile = fields.previewfile
50
51 const updatedFields = omit(fields, 'thumbnailfile', 'previewfile')
52
53 return makeUploadRequest({
54 url,
55 path,
56 token,
57 attaches,
58 fields: updatedFields,
59 statusCodeExpected
60 })
61}
62
63async function sendRTMPStreamInVideo (url: string, token: string, videoId: number | string, fixtureName?: string) {
64 const res = await getLive(url, token, videoId)
65 const videoLive = res.body as LiveVideo
66
67 return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName)
68}
69 9
70function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') { 10function sendRTMPStream (rtmpBaseUrl: string, streamKey: string, fixtureName = 'video_short.mp4') {
71 const fixture = buildAbsoluteFixturePath(fixtureName) 11 const fixture = buildAbsoluteFixturePath(fixtureName)
@@ -109,12 +49,6 @@ function waitFfmpegUntilError (command: ffmpeg.FfmpegCommand, successAfterMS = 1
109 }) 49 })
110} 50}
111 51
112async function runAndTestFfmpegStreamError (url: string, token: string, videoId: number | string, shouldHaveError: boolean) {
113 const command = await sendRTMPStreamInVideo(url, token, videoId)
114
115 return testFfmpegStreamError(command, shouldHaveError)
116}
117
118async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) { 52async function testFfmpegStreamError (command: ffmpeg.FfmpegCommand, shouldHaveError: boolean) {
119 let error: Error 53 let error: Error
120 54
@@ -136,48 +70,9 @@ async function stopFfmpeg (command: ffmpeg.FfmpegCommand) {
136 await wait(500) 70 await wait(500)
137} 71}
138 72
139function waitUntilLivePublished (url: string, token: string, videoId: number | string) {
140 return waitUntilLiveState(url, token, videoId, VideoState.PUBLISHED)
141}
142
143function waitUntilLiveWaiting (url: string, token: string, videoId: number | string) {
144 return waitUntilLiveState(url, token, videoId, VideoState.WAITING_FOR_LIVE)
145}
146
147function waitUntilLiveEnded (url: string, token: string, videoId: number | string) {
148 return waitUntilLiveState(url, token, videoId, VideoState.LIVE_ENDED)
149}
150
151function waitUntilLiveSegmentGeneration (server: ServerInfo, videoUUID: string, resolutionNum: number, segmentNum: number) {
152 const segmentName = `${resolutionNum}-00000${segmentNum}.ts`
153 return waitUntilLog(server, `${videoUUID}/${segmentName}`, 2, false)
154}
155
156async function waitUntilLiveState (url: string, token: string, videoId: number | string, state: VideoState) {
157 let video: VideoDetails
158
159 do {
160 const res = await getVideoWithToken(url, token, videoId)
161 video = res.body
162
163 await wait(500)
164 } while (video.state.id !== state)
165}
166
167async function waitUntilLiveSaved (url: string, token: string, videoId: number | string) {
168 let video: VideoDetails
169
170 do {
171 const res = await getVideoWithToken(url, token, videoId)
172 video = res.body
173
174 await wait(500)
175 } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED)
176}
177
178async function waitUntilLivePublishedOnAllServers (servers: ServerInfo[], videoId: string) { 73async function waitUntilLivePublishedOnAllServers (servers: ServerInfo[], videoId: string) {
179 for (const server of servers) { 74 for (const server of servers) {
180 await waitUntilLivePublished(server.url, server.accessToken, videoId) 75 await server.liveCommand.waitUntilLivePublished({ videoId })
181 } 76 }
182} 77}
183 78
@@ -206,33 +101,11 @@ async function checkLiveCleanup (server: ServerInfo, videoUUID: string, resoluti
206 expect(files).to.contain('segments-sha256.json') 101 expect(files).to.contain('segments-sha256.json')
207} 102}
208 103
209async function getPlaylistsCount (server: ServerInfo, videoUUID: string) {
210 const basePath = buildServerDirectory(server, 'streaming-playlists')
211 const hlsPath = join(basePath, 'hls', videoUUID)
212
213 const files = await readdir(hlsPath)
214
215 return files.filter(f => f.endsWith('.m3u8')).length
216}
217
218// ---------------------------------------------------------------------------
219
220export { 104export {
221 getLive, 105 sendRTMPStream,
222 getPlaylistsCount,
223 waitUntilLiveSaved,
224 waitUntilLivePublished,
225 updateLive,
226 createLive,
227 runAndTestFfmpegStreamError,
228 checkLiveCleanup,
229 waitUntilLiveSegmentGeneration,
230 stopFfmpeg,
231 waitUntilLiveWaiting,
232 sendRTMPStreamInVideo,
233 waitUntilLiveEnded,
234 waitFfmpegUntilError, 106 waitFfmpegUntilError,
107 testFfmpegStreamError,
108 stopFfmpeg,
235 waitUntilLivePublishedOnAllServers, 109 waitUntilLivePublishedOnAllServers,
236 sendRTMPStream, 110 checkLiveCleanup
237 testFfmpegStreamError
238} 111}