diff options
Diffstat (limited to 'shared/server-commands/videos/live-command.ts')
-rw-r--r-- | shared/server-commands/videos/live-command.ts | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/shared/server-commands/videos/live-command.ts b/shared/server-commands/videos/live-command.ts new file mode 100644 index 000000000..74f5d3089 --- /dev/null +++ b/shared/server-commands/videos/live-command.ts | |||
@@ -0,0 +1,155 @@ | |||
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 | copyCodecs?: boolean | ||
72 | }) { | ||
73 | const { videoId, fixtureName, copyCodecs } = options | ||
74 | const videoLive = await this.get({ videoId }) | ||
75 | |||
76 | return sendRTMPStream({ rtmpBaseUrl: videoLive.rtmpUrl, streamKey: videoLive.streamKey, fixtureName, copyCodecs }) | ||
77 | } | ||
78 | |||
79 | async runAndTestStreamError (options: OverrideCommandOptions & { | ||
80 | videoId: number | string | ||
81 | shouldHaveError: boolean | ||
82 | }) { | ||
83 | const command = await this.sendRTMPStreamInVideo(options) | ||
84 | |||
85 | return testFfmpegStreamError(command, options.shouldHaveError) | ||
86 | } | ||
87 | |||
88 | waitUntilPublished (options: OverrideCommandOptions & { | ||
89 | videoId: number | string | ||
90 | }) { | ||
91 | const { videoId } = options | ||
92 | return this.waitUntilState({ videoId, state: VideoState.PUBLISHED }) | ||
93 | } | ||
94 | |||
95 | waitUntilWaiting (options: OverrideCommandOptions & { | ||
96 | videoId: number | string | ||
97 | }) { | ||
98 | const { videoId } = options | ||
99 | return this.waitUntilState({ videoId, state: VideoState.WAITING_FOR_LIVE }) | ||
100 | } | ||
101 | |||
102 | waitUntilEnded (options: OverrideCommandOptions & { | ||
103 | videoId: number | string | ||
104 | }) { | ||
105 | const { videoId } = options | ||
106 | return this.waitUntilState({ videoId, state: VideoState.LIVE_ENDED }) | ||
107 | } | ||
108 | |||
109 | waitUntilSegmentGeneration (options: OverrideCommandOptions & { | ||
110 | videoUUID: string | ||
111 | resolution: number | ||
112 | segment: number | ||
113 | }) { | ||
114 | const { resolution, segment, videoUUID } = options | ||
115 | const segmentName = `${resolution}-00000${segment}.ts` | ||
116 | |||
117 | return this.server.servers.waitUntilLog(`${videoUUID}/${segmentName}`, 2, false) | ||
118 | } | ||
119 | |||
120 | async waitUntilSaved (options: OverrideCommandOptions & { | ||
121 | videoId: number | string | ||
122 | }) { | ||
123 | let video: VideoDetails | ||
124 | |||
125 | do { | ||
126 | video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId }) | ||
127 | |||
128 | await wait(500) | ||
129 | } while (video.isLive === true || video.state.id !== VideoState.PUBLISHED) | ||
130 | } | ||
131 | |||
132 | async countPlaylists (options: OverrideCommandOptions & { | ||
133 | videoUUID: string | ||
134 | }) { | ||
135 | const basePath = this.server.servers.buildDirectory('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 waitUntilState (options: OverrideCommandOptions & { | ||
144 | videoId: number | string | ||
145 | state: VideoState | ||
146 | }) { | ||
147 | let video: VideoDetails | ||
148 | |||
149 | do { | ||
150 | video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId }) | ||
151 | |||
152 | await wait(500) | ||
153 | } while (video.state.id !== options.state) | ||
154 | } | ||
155 | } | ||