]>
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' | |
6c5065a0 | 8 | import { wait } from '../miscs' |
4f219914 | 9 | import { unwrapBody } from '../requests' |
4f219914 C |
10 | import { AbstractCommand, OverrideCommandOptions } from '../shared' |
11 | import { sendRTMPStream, testFfmpegStreamError } from './live' | |
4f219914 C |
12 | |
13 | export class LiveCommand extends AbstractCommand { | |
14 | ||
04aed767 | 15 | get (options: OverrideCommandOptions & { |
4f219914 C |
16 | videoId: number | string |
17 | }) { | |
18 | const path = '/api/v1/videos/live' | |
19 | ||
20 | return this.getRequestBody<LiveVideo>({ | |
21 | ...options, | |
22 | ||
23 | path: path + '/' + options.videoId, | |
a1637fa1 | 24 | implicitToken: true, |
4f219914 C |
25 | defaultExpectedStatus: HttpStatusCode.OK_200 |
26 | }) | |
27 | } | |
28 | ||
04aed767 | 29 | update (options: OverrideCommandOptions & { |
4f219914 C |
30 | videoId: number | string |
31 | fields: LiveVideoUpdate | |
32 | }) { | |
33 | const { videoId, fields } = options | |
34 | const path = '/api/v1/videos/live' | |
35 | ||
36 | return this.putBodyRequest({ | |
37 | ...options, | |
38 | ||
39 | path: path + '/' + videoId, | |
40 | fields, | |
a1637fa1 | 41 | implicitToken: true, |
4f219914 C |
42 | defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 |
43 | }) | |
44 | } | |
45 | ||
04aed767 | 46 | async create (options: OverrideCommandOptions & { |
4f219914 C |
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'), | |
a1637fa1 | 62 | implicitToken: true, |
4f219914 C |
63 | defaultExpectedStatus: HttpStatusCode.OK_200 |
64 | })) | |
65 | ||
66 | return body.video | |
67 | } | |
68 | ||
69 | async sendRTMPStreamInVideo (options: OverrideCommandOptions & { | |
70 | videoId: number | string | |
71 | fixtureName?: string | |
72 | }) { | |
73 | const { videoId, fixtureName } = options | |
04aed767 | 74 | const videoLive = await this.get({ videoId }) |
4f219914 C |
75 | |
76 | return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName) | |
77 | } | |
78 | ||
04aed767 | 79 | async runAndTestStreamError (options: OverrideCommandOptions & { |
4f219914 C |
80 | videoId: number | string |
81 | shouldHaveError: boolean | |
82 | }) { | |
83 | const command = await this.sendRTMPStreamInVideo(options) | |
84 | ||
85 | return testFfmpegStreamError(command, options.shouldHaveError) | |
86 | } | |
87 | ||
04aed767 | 88 | waitUntilPublished (options: OverrideCommandOptions & { |
4f219914 C |
89 | videoId: number | string |
90 | }) { | |
91 | const { videoId } = options | |
04aed767 | 92 | return this.waitUntilState({ videoId, state: VideoState.PUBLISHED }) |
4f219914 C |
93 | } |
94 | ||
04aed767 | 95 | waitUntilWaiting (options: OverrideCommandOptions & { |
4f219914 C |
96 | videoId: number | string |
97 | }) { | |
98 | const { videoId } = options | |
04aed767 | 99 | return this.waitUntilState({ videoId, state: VideoState.WAITING_FOR_LIVE }) |
4f219914 C |
100 | } |
101 | ||
04aed767 | 102 | waitUntilEnded (options: OverrideCommandOptions & { |
4f219914 C |
103 | videoId: number | string |
104 | }) { | |
105 | const { videoId } = options | |
04aed767 | 106 | return this.waitUntilState({ videoId, state: VideoState.LIVE_ENDED }) |
4f219914 C |
107 | } |
108 | ||
04aed767 | 109 | waitUntilSegmentGeneration (options: OverrideCommandOptions & { |
4f219914 C |
110 | videoUUID: string |
111 | resolution: number | |
112 | segment: number | |
113 | }) { | |
114 | const { resolution, segment, videoUUID } = options | |
115 | const segmentName = `${resolution}-00000${segment}.ts` | |
116 | ||
89d241a7 | 117 | return this.server.servers.waitUntilLog(`${videoUUID}/${segmentName}`, 2, false) |
4f219914 C |
118 | } |
119 | ||
04aed767 | 120 | async waitUntilSaved (options: OverrideCommandOptions & { |
4f219914 C |
121 | videoId: number | string |
122 | }) { | |
123 | let video: VideoDetails | |
124 | ||
125 | do { | |
89d241a7 | 126 | video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId }) |
4f219914 C |
127 | |
128 | await wait(500) | |
129 | } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED) | |
130 | } | |
131 | ||
04aed767 | 132 | async countPlaylists (options: OverrideCommandOptions & { |
4f219914 C |
133 | videoUUID: string |
134 | }) { | |
89d241a7 | 135 | const basePath = this.server.servers.buildDirectory('streaming-playlists') |
4f219914 C |
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 | ||
04aed767 | 143 | private async waitUntilState (options: OverrideCommandOptions & { |
4f219914 C |
144 | videoId: number | string |
145 | state: VideoState | |
146 | }) { | |
147 | let video: VideoDetails | |
148 | ||
149 | do { | |
89d241a7 | 150 | video = await this.server.videos.getWithToken({ token: options.token, id: options.videoId }) |
4f219914 C |
151 | |
152 | await wait(500) | |
153 | } while (video.state.id !== options.state) | |
154 | } | |
155 | } |