]>
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 | ||
04aed767 | 17 | get (options: OverrideCommandOptions & { |
4f219914 C |
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, | |
a1637fa1 | 26 | implicitToken: true, |
4f219914 C |
27 | defaultExpectedStatus: HttpStatusCode.OK_200 |
28 | }) | |
29 | } | |
30 | ||
04aed767 | 31 | update (options: OverrideCommandOptions & { |
4f219914 C |
32 | videoId: number | string |
33 | fields: LiveVideoUpdate | |
34 | }) { | |
35 | const { videoId, fields } = options | |
36 | const path = '/api/v1/videos/live' | |
37 | ||
38 | return this.putBodyRequest({ | |
39 | ...options, | |
40 | ||
41 | path: path + '/' + videoId, | |
42 | fields, | |
a1637fa1 | 43 | implicitToken: true, |
4f219914 C |
44 | defaultExpectedStatus: HttpStatusCode.NO_CONTENT_204 |
45 | }) | |
46 | } | |
47 | ||
04aed767 | 48 | async create (options: OverrideCommandOptions & { |
4f219914 C |
49 | fields: LiveVideoCreate |
50 | }) { | |
51 | const { fields } = options | |
52 | const path = '/api/v1/videos/live' | |
53 | ||
54 | const attaches: any = {} | |
55 | if (fields.thumbnailfile) attaches.thumbnailfile = fields.thumbnailfile | |
56 | if (fields.previewfile) attaches.previewfile = fields.previewfile | |
57 | ||
58 | const body = await unwrapBody<{ video: VideoCreateResult }>(this.postUploadRequest({ | |
59 | ...options, | |
60 | ||
61 | path, | |
62 | attaches, | |
63 | fields: omit(fields, 'thumbnailfile', 'previewfile'), | |
a1637fa1 | 64 | implicitToken: true, |
4f219914 C |
65 | defaultExpectedStatus: HttpStatusCode.OK_200 |
66 | })) | |
67 | ||
68 | return body.video | |
69 | } | |
70 | ||
71 | async sendRTMPStreamInVideo (options: OverrideCommandOptions & { | |
72 | videoId: number | string | |
73 | fixtureName?: string | |
74 | }) { | |
75 | const { videoId, fixtureName } = options | |
04aed767 | 76 | const videoLive = await this.get({ videoId }) |
4f219914 C |
77 | |
78 | return sendRTMPStream(videoLive.rtmpUrl, videoLive.streamKey, fixtureName) | |
79 | } | |
80 | ||
04aed767 | 81 | async runAndTestStreamError (options: OverrideCommandOptions & { |
4f219914 C |
82 | videoId: number | string |
83 | shouldHaveError: boolean | |
84 | }) { | |
85 | const command = await this.sendRTMPStreamInVideo(options) | |
86 | ||
87 | return testFfmpegStreamError(command, options.shouldHaveError) | |
88 | } | |
89 | ||
04aed767 | 90 | waitUntilPublished (options: OverrideCommandOptions & { |
4f219914 C |
91 | videoId: number | string |
92 | }) { | |
93 | const { videoId } = options | |
04aed767 | 94 | return this.waitUntilState({ videoId, state: VideoState.PUBLISHED }) |
4f219914 C |
95 | } |
96 | ||
04aed767 | 97 | waitUntilWaiting (options: OverrideCommandOptions & { |
4f219914 C |
98 | videoId: number | string |
99 | }) { | |
100 | const { videoId } = options | |
04aed767 | 101 | return this.waitUntilState({ videoId, state: VideoState.WAITING_FOR_LIVE }) |
4f219914 C |
102 | } |
103 | ||
04aed767 | 104 | waitUntilEnded (options: OverrideCommandOptions & { |
4f219914 C |
105 | videoId: number | string |
106 | }) { | |
107 | const { videoId } = options | |
04aed767 | 108 | return this.waitUntilState({ videoId, state: VideoState.LIVE_ENDED }) |
4f219914 C |
109 | } |
110 | ||
04aed767 | 111 | waitUntilSegmentGeneration (options: OverrideCommandOptions & { |
4f219914 C |
112 | videoUUID: string |
113 | resolution: number | |
114 | segment: number | |
115 | }) { | |
116 | const { resolution, segment, videoUUID } = options | |
117 | const segmentName = `${resolution}-00000${segment}.ts` | |
118 | ||
119 | return waitUntilLog(this.server, `${videoUUID}/${segmentName}`, 2, false) | |
120 | } | |
121 | ||
04aed767 | 122 | async waitUntilSaved (options: OverrideCommandOptions & { |
4f219914 C |
123 | videoId: number | string |
124 | }) { | |
125 | let video: VideoDetails | |
126 | ||
127 | do { | |
128 | const res = await getVideoWithToken(this.server.url, options.token ?? this.server.accessToken, options.videoId) | |
129 | video = res.body | |
130 | ||
131 | await wait(500) | |
132 | } while (video.isLive === true && video.state.id !== VideoState.PUBLISHED) | |
133 | } | |
134 | ||
04aed767 | 135 | async countPlaylists (options: OverrideCommandOptions & { |
4f219914 C |
136 | videoUUID: string |
137 | }) { | |
138 | const basePath = buildServerDirectory(this.server, 'streaming-playlists') | |
139 | const hlsPath = join(basePath, 'hls', options.videoUUID) | |
140 | ||
141 | const files = await readdir(hlsPath) | |
142 | ||
143 | return files.filter(f => f.endsWith('.m3u8')).length | |
144 | } | |
145 | ||
04aed767 | 146 | private async waitUntilState (options: OverrideCommandOptions & { |
4f219914 C |
147 | videoId: number | string |
148 | state: VideoState | |
149 | }) { | |
150 | let video: VideoDetails | |
151 | ||
152 | do { | |
153 | const res = await getVideoWithToken(this.server.url, options.token ?? this.server.accessToken, options.videoId) | |
154 | video = res.body | |
155 | ||
156 | await wait(500) | |
157 | } while (video.state.id !== options.state) | |
158 | } | |
159 | } |