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