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