]>
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' | |
6 | import { LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models' | |
7 | import { HttpStatusCode } from '../../core-utils/miscs/http-error-codes' | |
8 | import { buildServerDirectory, wait } from '../miscs/miscs' | |
9 | import { unwrapBody } from '../requests' | |
10 | import { waitUntilLog } from '../server/servers' | |
11 | import { AbstractCommand, OverrideCommandOptions } from '../shared' | |
12 | import { sendRTMPStream, testFfmpegStreamError } from './live' | |
13 | import { getVideoWithToken } from './videos' | |
14 | ||
15 | export 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 | } |