/
vshmidt
/
streamlit
Обзор
Документация
Войти
/
vshmidt
/
streamlit
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
frontend/app/src/util/ScreenCastRecorder.ts
173 строки
5 KB
Ken McGrady
Update copyright header to 2026 (#13491)
02 янв 2026, 16:21
Не верифицирован
02 янв 2026, 16:21
374540a
Код
Авторство
О чём код?
/** * Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { getLogger } from "loglevel" import { notNullOrUndefined } from "@streamlit/utils" const BLOB_TYPE = "video/webm" const LOG = getLogger("ScreenCastRecorder") interface ScreenCastRecorderOptions { recordAudio: boolean onErrorOrStop: () => void } class ScreenCastRecorder { private readonly recordAudio: boolean private inputStream: MediaStream | null private recordedChunks: Blob[] private mediaRecorder: MediaRecorder | null private readonly onErrorOrStopCallback: () => void /** True if the current browser likely supports screencasts. */ public static isSupportedBrowser(): boolean { try { return ( notNullOrUndefined(navigator.mediaDevices) && notNullOrUndefined(navigator.mediaDevices.getUserMedia) && notNullOrUndefined(navigator.mediaDevices.getDisplayMedia) && MediaRecorder.isTypeSupported(BLOB_TYPE) ) // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { // In the event of an error, assume it won't support screencasts return false } } constructor({ recordAudio, onErrorOrStop }: ScreenCastRecorderOptions) { this.recordAudio = recordAudio this.onErrorOrStopCallback = onErrorOrStop this.inputStream = null this.recordedChunks = [] this.mediaRecorder = null } /** * This asynchronous method will initialize the screen recording object asking * for permissions to the user which are needed to start recording. */ public async initialize(): Promise<void> { const desktopStream = await navigator.mediaDevices.getDisplayMedia({ video: true, }) let tracks = desktopStream.getTracks() if (this.recordAudio) { const voiceStream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true, }) tracks = tracks.concat(voiceStream.getAudioTracks()) } this.recordedChunks = [] this.inputStream = new MediaStream(tracks) this.mediaRecorder = new MediaRecorder(this.inputStream, { mimeType: BLOB_TYPE, }) this.mediaRecorder.ondataavailable = e => this.recordedChunks.push(e.data) } public getState(): string { if (this.mediaRecorder) { return this.mediaRecorder.state } return "inactive" } /** * This method will start the screen recording if the user has granted permissions * and the mediaRecorder has been initialized * * @returns {boolean} */ public start(): boolean { if (!this.mediaRecorder) { LOG.warn(`ScreenCastRecorder.start: mediaRecorder is null`) return false } // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: Replace 'any' with a more specific type. const logRecorderError = (e: any): void => { LOG.warn(`mediaRecorder.start threw an error: ${e}`) } // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: Replace 'any' with a more specific type. this.mediaRecorder.onerror = (e: any): void => { logRecorderError(e) this.onErrorOrStopCallback() } this.mediaRecorder.onstop = (): void => this.onErrorOrStopCallback() try { this.mediaRecorder.start() } catch (e) { logRecorderError(e) return false } return true } /** * This method will stop recording and then return the generated Blob * * @returns {(Promise|undefined)} * A Promise which will return the generated Blob * Undefined if the MediaRecorder could not initialize */ public stop(): Promise<Blob> | undefined { if (!this.mediaRecorder) { return undefined } let resolver: (value?: unknown) => void const promise = new Promise(r => { resolver = r }) this.mediaRecorder.onstop = () => resolver() this.mediaRecorder.stop() if (this.inputStream) { this.inputStream.getTracks().forEach(s => s.stop()) this.inputStream = null } return promise.then(() => this.buildOutputBlob()) } private buildOutputBlob(): Blob { return new Blob(this.recordedChunks, { type: BLOB_TYPE }) } } export default ScreenCastRecorder