diff options
Diffstat (limited to 'shared/extra-utils/videos/live-command.ts')
-rw-r--r-- | shared/extra-utils/videos/live-command.ts | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/shared/extra-utils/videos/live-command.ts b/shared/extra-utils/videos/live-command.ts new file mode 100644 index 000000000..bf9486a05 --- /dev/null +++ b/shared/extra-utils/videos/live-command.ts | |||
@@ -0,0 +1,154 @@ | |||
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 { HttpStatusCode, LiveVideo, LiveVideoCreate, LiveVideoUpdate, VideoCreateResult, VideoDetails, VideoState } from '@shared/models' | ||
7 | import { wait } from '../miscs' | ||
8 | import { unwrapBody } from '../requests' | ||
9 | import { AbstractCommand, OverrideCommandOptions } from '../shared' | ||
10 | import { sendRTMPStream, testFfmpegStreamError } from './live' | ||
11 | |||
12 | export class LiveCommand extends AbstractCommand { | ||
13 | |||
14 | get (options: OverrideCommandOptions & { | ||
15 | videoId: number | string | ||
16 | }) { | ||
17 | const path = '/api/v1/videos/live' | ||
18 | |||
19 | return this.getRequestBody<LiveVideo>({ | ||
20 | ...options, | ||
21 | |||
22 | path: path + '/' + options.videoId, | ||
23 | implicitToken: true, | ||
24 | defaultExpectedStatus: HttpStatusCode.OK_200 | ||
25 | }) | ||
26 | } | ||
27 | |||
28 | update (options: OverrideCommandOptions & { | ||
29 | videoId: number | string | ||
30 | fields: LiveVideoUpdate | ||
31 | }) { | ||
32 | const { videoId, fields } = options | ||
33 | const path = '/api/v1/videos/live' | ||
34 | |||
35 | return this.putBodyRequest({ | ||
36 | ...options, | ||
37 | |||
38 | path: path + '/' + videoId, | ||
39 | fields, | ||
40 | implicitToken: true, | ||
41 | defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 | ||
42 | }) | ||
43 | } | ||
44 | |||
45 | async create (options: OverrideCommandOptions & { | ||
46 | fields: LiveVideoCreate | ||
47 | }) { | ||
48 | const { fields } = options | ||
49 | const path = '/api/v1/videos/live' | ||
50 | |||
51 | const attaches: any = {} | ||
52 | if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile | ||
53 | if (fields.previewfile) attaches.previewfile = fields.previewfile | ||
54 | |||
55 | const body = await unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({ | ||
56 | ...options, | ||
57 | |||
58 | path, | ||
59 | attaches, | ||
60 | fields: omit(fields, 'thumbnailfile', 'previewfile'), | ||
61 | implicitToken: true, | ||
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.get({ videoId }) | ||
74 | |||
75 | return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName) | ||
76 | } | ||
77 | |||
78 | async runAndTestStreamError (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 | waitUntilPublished (options: OverrideCommandOptions & { | ||
88 | videoId: number | string | ||
89 | }) { | ||
90 | const { videoId } = options | ||
91 | return this.waitUntilState({ videoId, state: VideoState.PUBLISHED }) | ||
92 | } | ||
93 | |||
94 | waitUntilWaiting (options: OverrideCommandOptions & { | ||
95 | videoId: number | string | ||
96 | }) { | ||
97 | const { videoId } = options | ||
98 | return this.waitUntilState({ videoId, state: VideoState.WAITING_FOR_LIVE }) | ||
99 | } | ||
100 | |||
101 | waitUntilEnded (options: OverrideCommandOptions & { | ||
102 | videoId: number | string | ||
103 | }) { | ||
104 | const { videoId } = options | ||
105 | return this.waitUntilState({ videoId, state: VideoState.LIVE_ENDED }) | ||
106 | } | ||
107 | |||
108 | waitUntilSegmentGeneration (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 this.server.servers.waitUntilLog(`${videoUUID}/${segmentName}`, 2, false) | ||
117 | } | ||
118 | |||
119 | async waitUntilSaved (options: OverrideCommandOptions & { | ||
120 | videoId: number | string | ||
121 | }) { | ||
122 | let video: VideoDetails | ||
123 | |||
124 | do { | ||
125 | video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId }) | ||
126 | |||
127 | await wait(500) | ||
128 | } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED) | ||
129 | } | ||
130 | |||
131 | async countPlaylists (options: OverrideCommandOptions & { | ||
132 | videoUUID: string | ||
133 | }) { | ||
134 | const basePath = this.server.servers.buildDirectory('streaming-playlists') | ||
135 | const hlsPath = join(basePath, 'hls', options.videoUUID) | ||
136 | |||
137 | const files = await readdir(hlsPath) | ||
138 | |||
139 | return files.filter(f => f.endsWith('.m3u8')).length | ||
140 | } | ||
141 | |||
142 | private async waitUntilState (options: OverrideCommandOptions & { | ||
143 | videoId: number | string | ||
144 | state: VideoState | ||
145 | }) { | ||
146 | let video: VideoDetails | ||
147 | |||
148 | do { | ||
149 | video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId }) | ||
150 | |||
151 | await wait(500) | ||
152 | } while (video.state.id !== options.state) | ||
153 | } | ||
154 | } | ||