diff options
author | Chocobozzz <me@florianbigard.com> | 2021-07-08 10:18:40 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2021-07-20 15:27:17 +0200 |
commit | 4f2199144e428c16460750305f737b890c1ac322 (patch) | |
tree | a19c5c0f254ab0b32d6c8838af33a1ba260e4877 /shared/extra-utils/videos/live-command.ts | |
parent | 2c27e70471120c92e0bc8c8114141fbb31ff98ac (diff) | |
download | PeerTube-4f2199144e428c16460750305f737b890c1ac322.tar.gz PeerTube-4f2199144e428c16460750305f737b890c1ac322.tar.zst PeerTube-4f2199144e428c16460750305f737b890c1ac322.zip |
Introduce live command
Diffstat (limited to 'shared/extra-utils/videos/live-command.ts')
-rw-r--r-- | shared/extra-utils/videos/live-command.ts | 156 |
1 files changed, 156 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..55811b8ba --- /dev/null +++ b/shared/extra-utils/videos/live-command.ts | |||
@@ -0,0 +1,156 @@ | |||
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 | } | ||