]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - shared/server-commands/videos/live-command.ts
Try to fix tests
[github/Chocobozzz/PeerTube.git] / shared / server-commands / videos / live-command.ts
... / ...
CommitLineData
1/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
2
3import { readdir } from 'fs-extra'
4import { join } from 'path'
5import { omit, wait } from '@shared/core-utils'
6import {
7 HttpStatusCode,
8 LiveVideo,
9 LiveVideoCreate,
10 LiveVideoSession,
11 LiveVideoUpdate,
12 ResultList,
13 VideoCreateResult,
14 VideoDetails,
15 VideoState
16} from '@shared/models'
17import { unwrapBody } from '../requests'
18import { ObjectStorageCommand } from '../server'
19import { AbstractCommand, OverrideCommandOptions } from '../shared'
20import { sendRTMPStream, testFfmpegStreamError } from './live'
21
22export class LiveCommand extends AbstractCommand {
23
24 get (options: OverrideCommandOptions & {
25 videoId: number | string
26 }) {
27 const path = '/api/v1/videos/live'
28
29 return this.getRequestBody<LiveVideo>({
30 ...options,
31
32 path: path + '/' + options.videoId,
33 implicitToken: true,
34 defaultExpectedStatus: HttpStatusCode.OK_200
35 })
36 }
37
38 // ---------------------------------------------------------------------------
39
40 listSessions (options: OverrideCommandOptions & {
41 videoId: number | string
42 }) {
43 const path = `/api/v1/videos/live/${options.videoId}/sessions`
44
45 return this.getRequestBody<ResultList<LiveVideoSession>>({
46 ...options,
47
48 path,
49 implicitToken: true,
50 defaultExpectedStatus: HttpStatusCode.OK_200
51 })
52 }
53
54 async findLatestSession (options: OverrideCommandOptions & {
55 videoId: number | string
56 }) {
57 const { data: sessions } = await this.listSessions(options)
58
59 return sessions[sessions.length - 1]
60 }
61
62 getReplaySession (options: OverrideCommandOptions & {
63 videoId: number | string
64 }) {
65 const path = `/api/v1/videos/${options.videoId}/live-session`
66
67 return this.getRequestBody<LiveVideoSession>({
68 ...options,
69
70 path,
71 implicitToken: true,
72 defaultExpectedStatus: HttpStatusCode.OK_200
73 })
74 }
75
76 // ---------------------------------------------------------------------------
77
78 update (options: OverrideCommandOptions & {
79 videoId: number | string
80 fields: LiveVideoUpdate
81 }) {
82 const { videoId, fields } = options
83 const path = '/api/v1/videos/live'
84
85 return this.putBodyRequest({
86 ...options,
87
88 path: path + '/' + videoId,
89 fields,
90 implicitToken: true,
91 defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204
92 })
93 }
94
95 async create (options: OverrideCommandOptions & {
96 fields: LiveVideoCreate
97 }) {
98 const { fields } = options
99 const path = '/api/v1/videos/live'
100
101 const attaches: any = {}
102 if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile
103 if (fields.previewfile) attaches.previewfile = fields.previewfile
104
105 const body = await unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({
106 ...options,
107
108 path,
109 attaches,
110 fields: omit(fields, [ 'thumbnailfile', 'previewfile' ]),
111 implicitToken: true,
112 defaultExpectedStatus: HttpStatusCode.OK_200
113 }))
114
115 return body.video
116 }
117
118 // ---------------------------------------------------------------------------
119
120 async sendRTMPStreamInVideo (options: OverrideCommandOptions & {
121 videoId: number | string
122 fixtureName?: string
123 copyCodecs?: boolean
124 }) {
125 const { videoId, fixtureName, copyCodecs } = options
126 const videoLive = await this.get({ videoId })
127
128 return sendRTMPStream({ rtmpBaseUrl: videoLive.rtmpUrl, streamKey: videoLive.streamKey, fixtureName, copyCodecs })
129 }
130
131 async runAndTestStreamError (options: OverrideCommandOptions & {
132 videoId: number | string
133 shouldHaveError: boolean
134 }) {
135 const command = await this.sendRTMPStreamInVideo(options)
136
137 return testFfmpegStreamError(command, options.shouldHaveError)
138 }
139
140 // ---------------------------------------------------------------------------
141
142 waitUntilPublished (options: OverrideCommandOptions & {
143 videoId: number | string
144 }) {
145 const { videoId } = options
146 return this.waitUntilState({ videoId, state: VideoState.PUBLISHED })
147 }
148
149 waitUntilWaiting (options: OverrideCommandOptions & {
150 videoId: number | string
151 }) {
152 const { videoId } = options
153 return this.waitUntilState({ videoId, state: VideoState.WAITING_FOR_LIVE })
154 }
155
156 waitUntilEnded (options: OverrideCommandOptions & {
157 videoId: number | string
158 }) {
159 const { videoId } = options
160 return this.waitUntilState({ videoId, state: VideoState.LIVE_ENDED })
161 }
162
163 waitUntilSegmentGeneration (options: OverrideCommandOptions & {
164 videoUUID: string
165 playlistNumber: number
166 segment: number
167 totalSessions?: number
168 }) {
169 const { playlistNumber, segment, videoUUID, totalSessions = 1 } = options
170 const segmentName = `${playlistNumber}-00000${segment}.ts`
171
172 return this.server.servers.waitUntilLog(`${videoUUID}/${segmentName}`, totalSessions * 2, false)
173 }
174
175 waitUntilSegmentUpload (options: OverrideCommandOptions & {
176 playlistNumber: number
177 segment: number
178 totalSessions?: number
179 }) {
180 const { playlistNumber, segment, totalSessions = 1 } = options
181 const segmentName = `${playlistNumber}-00000${segment}.ts`
182
183 return this.server.servers.waitUntilLog(`${segmentName} in bucket `, totalSessions * 2, false)
184 }
185
186 async waitUntilReplacedByReplay (options: OverrideCommandOptions & {
187 videoId: number | string
188 }) {
189 let video: VideoDetails
190
191 do {
192 video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId })
193
194 await wait(500)
195 } while (video.isLive === true || video.state.id !== VideoState.PUBLISHED)
196 }
197
198 // ---------------------------------------------------------------------------
199
200 getSegmentFile (options: OverrideCommandOptions & {
201 videoUUID: string
202 playlistNumber: number
203 segment: number
204 objectStorage?: boolean // default false
205 }) {
206 const { playlistNumber, segment, videoUUID, objectStorage = false } = options
207
208 const segmentName = `${playlistNumber}-00000${segment}.ts`
209 const baseUrl = objectStorage
210 ? ObjectStorageCommand.getPlaylistBaseUrl()
211 : `${this.server.url}/static/streaming-playlists/hls`
212
213 const url = `${baseUrl}/${videoUUID}/${segmentName}`
214
215 return this.getRawRequest({
216 ...options,
217
218 url,
219 implicitToken: false,
220 defaultExpectedStatus: HttpStatusCode.OK_200
221 })
222 }
223
224 getPlaylistFile (options: OverrideCommandOptions & {
225 videoUUID: string
226 playlistName: string
227 objectStorage?: boolean // default false
228 }) {
229 const { playlistName, videoUUID, objectStorage = false } = options
230
231 const baseUrl = objectStorage
232 ? ObjectStorageCommand.getPlaylistBaseUrl()
233 : `${this.server.url}/static/streaming-playlists/hls`
234
235 const url = `${baseUrl}/${videoUUID}/${playlistName}`
236
237 return this.getRawRequest({
238 ...options,
239
240 url,
241 implicitToken: false,
242 defaultExpectedStatus: HttpStatusCode.OK_200
243 })
244 }
245
246 // ---------------------------------------------------------------------------
247
248 async countPlaylists (options: OverrideCommandOptions & {
249 videoUUID: string
250 }) {
251 const basePath = this.server.servers.buildDirectory('streaming-playlists')
252 const hlsPath = join(basePath, 'hls', options.videoUUID)
253
254 const files = await readdir(hlsPath)
255
256 return files.filter(f => f.endsWith('.m3u8')).length
257 }
258
259 private async waitUntilState (options: OverrideCommandOptions & {
260 videoId: number | string
261 state: VideoState
262 }) {
263 let video: VideoDetails
264
265 do {
266 video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId })
267
268 await wait(500)
269 } while (video.state.id !== options.state)
270 }
271}