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