UNPKG

tiny-server-essentials

Version:

A good utility toolkit to unify Express v5 and Socket.IO v4 into a seamless development experience with modular helpers, server wrappers, and WebSocket tools.

662 lines 28.7 kB
/** @typedef {'mic'|'cam'|'screen'} StreamTypes */ /** @typedef {'Mic'|'Cam'|'Screen'} StreamEventTypes */ /** @typedef {import('./TinyMediaReceiver.mjs').ReceiverTags} ReceiverTags */ /** * @typedef {Object} StreamConfig * @property {string} mimeType - A valid MIME type for MediaRecorder. * @property {number} timeslice - Interval in milliseconds for emitting data chunks. * @property {string|null} audioCodec - Audio codec that can be used * @property {string|null} videoCodec - Video codec that can be used */ /** * @typedef {Object} MandatoryConstraints * @property {'screen'|'window'|'application'|'desktop'} [chromeMediaSource] * Capture source for Chrome/Electron. * @property {string} [chromeMediaSourceId] * Specific ID of the capture source (usually obtained via desktopCapturer). * @property {number} [maxWidth] * Maximum width of the capture. * @property {number} [maxHeight] * Maximum height of the capture. * @property {number} [maxFrameRate] * Maximum frame rate. * @property {number} [minWidth] * Minimum width of the capture. * @property {number} [minHeight] * Minimum height of the capture. * @property {number} [minFrameRate] * Minimum frame rate. * @property {boolean} [googLeakyBucket] * Experimental setting used in some Chromium versions. * @property {boolean} [googTemporalLayeredScreencast] * Experimental setting for temporal layers in screen capture. */ /** * @typedef {Object} AdvancedScreenVideoConstraints * @property {'screen'|'window'|'application'|'browser'|'monitor'} [mediaSource] * Capture source, useful in Electron and Firefox. * @property {string} [chromeMediaSource] * Used in older versions of Chrome/Electron. Usually 'desktop'. * @property {string} [chromeMediaSourceId] * Used in Electron to select a specific screen or window. * @property {number} [frameRate] * Desired capture frame rate (e.g., 30 or 60). * @property {number} [width] * Ideal capture width. * @property {number} [height] * Ideal capture height. * @property {MandatoryConstraints} [mandatory] * Allows configuring advanced fields in Electron/Chrome (like `chromeMediaSource`). */ /** * @typedef {Object} ScreenShareConstraints * @property {boolean|MediaTrackConstraints} [audio] * @property {boolean|AdvancedScreenVideoConstraints|MediaTrackConstraints} video * Constraints for screen capture, can be simple (true) or detailed. */ /** * Manages media streams (microphone, camera, screen) with flexible device constraints, * socket emission support, and audio volume metering. * * This class: * - Allows starting and managing media input streams (mic, cam, screen). * - Supports custom constraints and device targeting via deviceId or full constraint objects. * - Emits media streams to a socket or handler using predefined labels (mic, cam, screen, etc.). * - Provides audio volume metering for microphone and screen when audio is available. * - Supports Electron-specific constraints when applicable. * * Events emitted: * - `"Mic"`: Audio stream from microphone. * - `"MicMeter"`: Volume level from microphone. * - `"Cam"`: Video stream from webcam. * - `"Screen"`: Video stream from screen capture. * - `"ScreenMeter"`: Volume level from screen audio. * * Internally uses: * - `navigator.mediaDevices.getUserMedia` for mic/cam. * - `navigator.mediaDevices.getDisplayMedia` for screen sharing. * - Optional support for `chromeMediaSource` and related properties in Electron environments. * * @class * @beta */ export class TinyStreamManager { /** * Event labels used internally and externally for stream control and monitoring. * These events are emitted or listened to over socket or internal dispatch. * @readonly */ readonly Events: { /** * Event name emitted when the instance is destroyed. * This constant can be used to subscribe to the destruction event of the instance. * @type {'Destroyed'} */ Destroyed: "Destroyed"; /** * Emitted when a media data receiver (e.g., WebSocket, PeerConnection, etc.) has been removed. * This may happen when a connection is closed or explicitly terminated. * @type {'ReceiverDeleted'} */ ReceiverDeleted: "ReceiverDeleted"; /** * Emitted when a new media data receiver has been added to the stream. * Useful for dynamic systems where receivers can join at runtime. * @type {'ReceiverAdded'} */ ReceiverAdded: "ReceiverAdded"; /** * Event emitted to request starting the webcam stream. * @type {'StartCam'} */ StartCam: "StartCam"; /** * Event emitted to request starting the microphone stream. * @type {'StartMic'} */ StartMic: "StartMic"; /** * Event emitted to request starting the screen sharing stream. * @type {'StartScreen'} */ StartScreen: "StartScreen"; /** * Event emitted to request stopping the webcam stream. * @type {'StopCam'} */ StopCam: "StopCam"; /** * Event emitted to request stopping the microphone stream. * @type {'StopMic'} */ StopMic: "StopMic"; /** * Event emitted to request stopping the screen sharing stream. * @type {'StopScreen'} */ StopScreen: "StopScreen"; /** * Event emitted when the webcam stream is transmitted. * @type {'Cam'} */ Cam: "Cam"; /** * Event emitted when the microphone stream is transmitted. * @type {'Mic'} */ Mic: "Mic"; /** * Event emitted when the screen sharing stream is transmitted. * @type {'Screen'} */ Screen: "Screen"; /** * Event emitted periodically with screen audio volume data. * @type {'ScreenMeter'} */ ScreenMeter: "ScreenMeter"; /** * Event emitted periodically with microphone audio volume data. * @type {'MicMeter'} */ MicMeter: "MicMeter"; }; /** * Checks whether a given event name is defined in the Events map. * * This method verifies if the provided string matches one of the predefined * event labels (e.g., "Mic", "Cam", "Screen", "MicMeter", "ScreenMeter"). * * @param {string} name - The name of the event to check. * @returns {boolean} Returns `true` if the event exists in the Events map, otherwise `false`. */ existsEvent(name: string): boolean; /** * Provides access to a secure internal EventEmitter for subclass use only. * * This method exposes a dedicated EventEmitter instance intended specifically for subclasses * that extend the main class. It prevents subclasses from accidentally or intentionally using * the primary class's public event system (`emit`), which could lead to unpredictable behavior * or interference in the base class's event flow. * * For security and consistency, this method is designed to be accessed only once. * Multiple accesses are blocked to avoid leaks or misuse of the internal event bus. * * @returns {EventEmitter} A special internal EventEmitter instance for subclass use. * @throws {Error} If the method is called more than once. */ getSysEvents(): EventEmitter; /** * @typedef {(...args: any[]) => void} ListenerCallback * A generic callback function used for event listeners. */ /** * Sets the maximum number of listeners for the internal event emitter. * * @param {number} max - The maximum number of listeners allowed. */ setMaxListeners(max: number): void; /** * Emits an event with optional arguments. * @param {string | symbol} event - The name of the event to emit. * @param {...any} args - Arguments passed to event listeners. * @returns {boolean} `true` if the event had listeners, `false` otherwise. */ emit(event: string | symbol, ...args: any[]): boolean; /** * Registers a listener for the specified event. * @param {string | symbol} event - The name of the event to listen for. * @param {ListenerCallback} listener - The callback function to invoke. * @returns {this} The current class instance (for chaining). */ on(event: string | symbol, listener: (...args: any[]) => void): this; /** * Registers a one-time listener for the specified event. * @param {string | symbol} event - The name of the event to listen for once. * @param {ListenerCallback} listener - The callback function to invoke. * @returns {this} The current class instance (for chaining). */ once(event: string | symbol, listener: (...args: any[]) => void): this; /** * Removes a listener from the specified event. * @param {string | symbol} event - The name of the event. * @param {ListenerCallback} listener - The listener to remove. * @returns {this} The current class instance (for chaining). */ off(event: string | symbol, listener: (...args: any[]) => void): this; /** * Alias for `on`. * @param {string | symbol} event - The name of the event. * @param {ListenerCallback} listener - The callback to register. * @returns {this} The current class instance (for chaining). */ addListener(event: string | symbol, listener: (...args: any[]) => void): this; /** * Alias for `off`. * @param {string | symbol} event - The name of the event. * @param {ListenerCallback} listener - The listener to remove. * @returns {this} The current class instance (for chaining). */ removeListener(event: string | symbol, listener: (...args: any[]) => void): this; /** * Removes all listeners for a specific event, or all events if no event is specified. * @param {string | symbol} [event] - The name of the event. If omitted, all listeners from all events will be removed. * @returns {this} The current class instance (for chaining). */ removeAllListeners(event?: string | symbol): this; /** * Returns the number of times the given `listener` is registered for the specified `event`. * If no `listener` is passed, returns how many listeners are registered for the `event`. * @param {string | symbol} eventName - The name of the event. * @param {Function} [listener] - Optional listener function to count. * @returns {number} Number of matching listeners. */ listenerCount(eventName: string | symbol, listener?: Function): number; /** * Adds a listener function to the **beginning** of the listeners array for the specified event. * The listener is called every time the event is emitted. * @param {string | symbol} eventName - The event name. * @param {ListenerCallback} listener - The callback function. * @returns {this} The current class instance (for chaining). */ prependListener(eventName: string | symbol, listener: (...args: any[]) => void): this; /** * Adds a **one-time** listener function to the **beginning** of the listeners array. * The next time the event is triggered, this listener is removed and then invoked. * @param {string | symbol} eventName - The event name. * @param {ListenerCallback} listener - The callback function. * @returns {this} The current class instance (for chaining). */ prependOnceListener(eventName: string | symbol, listener: (...args: any[]) => void): this; /** * Returns an array of event names for which listeners are currently registered. * @returns {(string | symbol)[]} Array of event names. */ eventNames(): (string | symbol)[]; /** * Gets the current maximum number of listeners allowed for any single event. * @returns {number} The max listener count. */ getMaxListeners(): number; /** * Returns a copy of the listeners array for the specified event. * @param {string | symbol} eventName - The event name. * @returns {Function[]} An array of listener functions. */ listeners(eventName: string | symbol): Function[]; /** * Returns a copy of the internal listeners array for the specified event, * including wrapper functions like those used by `.once()`. * @param {string | symbol} eventName - The event name. * @returns {Function[]} An array of raw listener functions. */ rawListeners(eventName: string | symbol): Function[]; /** @type {VolumeMeter|null} */ micMeter: VolumeMeter | null; /** @type {VolumeMeter|null} */ screenMeter: VolumeMeter | null; /** @type {MediaStream|null} */ micStream: MediaStream | null; /** @type {MediaStream|null} */ camStream: MediaStream | null; /** @type {MediaStream|null} */ screenStream: MediaStream | null; /** * Updates the configuration for a specific media source. * * @param {StreamTypes} target - The config to update. * @param {{ mimeType?: string, timeslice?: number }} updates - The new configuration values. * * @throws {Error} If the target is invalid. * @throws {Error} If the mimeType is invalid or unsupported. * @throws {Error} If the timeslice is not a positive finite number. */ updateMediaConfig(target: StreamTypes, updates?: { mimeType?: string; timeslice?: number; }): void; /** * Returns the current microphone stream if it is a valid MediaStream. * * @returns {MediaStream} The active microphone stream. * @throws {Error} If the microphone stream is not a valid MediaStream. */ getMicStream(): MediaStream; /** * Returns the current webcam stream if it is a valid MediaStream. * * @returns {MediaStream} The active webcam stream. * @throws {Error} If the webcam stream is not a valid MediaStream. */ getCamStream(): MediaStream; /** * Returns the current screen sharing stream if it is a valid MediaStream. * * @returns {MediaStream} The active screen sharing stream. * @throws {Error} If the screen stream is not a valid MediaStream. */ getScreenStream(): MediaStream; /** * Returns the current microphone volume meter if it is a valid VolumeMeter instance. * * @returns {VolumeMeter} The active microphone volume meter. * @throws {Error} If the microphone meter is not a valid VolumeMeter instance. */ getMicMeter(): VolumeMeter; /** * Returns the current screen volume meter if it is a valid VolumeMeter instance. * * @returns {VolumeMeter} The active screen volume meter. * @throws {Error} If the screen meter is not a valid VolumeMeter instance. */ getScreenMeter(): VolumeMeter; /** * Checks if the microphone volume meter exists and is a valid VolumeMeter instance. * * @returns {boolean} True if micMeter exists and is valid, false otherwise. */ hasMicMeter(): boolean; /** * Checks if the screen volume meter exists and is a valid VolumeMeter instance. * * @returns {boolean} True if screenMeter exists and is valid, false otherwise. */ hasScreenMeter(): boolean; /** * Updates the internal list of available media devices. * * This method queries the user's system for media input and output devices using * `navigator.mediaDevices.enumerateDevices()` and categorizes them into three groups: * video inputs, audio inputs, and audio outputs (speakers). The result is stored in the * `this.devices` object and also returned for immediate use. * * If the device list cannot be retrieved, it falls back to setting all categories as empty arrays. * * @returns {Promise<{ video: MediaDeviceInfo[], audio: MediaDeviceInfo[], speaker: MediaDeviceInfo[] }>} * A promise resolving to an object containing categorized media devices. */ updateDeviceList(): Promise<{ video: MediaDeviceInfo[]; audio: MediaDeviceInfo[]; speaker: MediaDeviceInfo[]; }>; /** * Retrieves a list of media devices filtered by the specified kind. * * This method strictly enforces the kind to be one of: 'audio', 'video', or 'speaker'. * It throws an error if the input is not a string, not one of the allowed kinds, * or if no devices are available for the given kind. * * @param {'audio' | 'video' | 'speaker'} kind - The type of device to retrieve. * @returns {MediaDeviceInfo[]} An array of media devices matching the specified kind. * @throws {Error} If the input is not a string. * @throws {RangeError} If the input is not one of the accepted device kinds. * @throws {Error} If no devices are found for the given kind. */ getDevicesByKind(kind: "audio" | "video" | "speaker"): MediaDeviceInfo[]; /** * Returns an array containing all available audio, video, and speaker devices. * * This method throws an error if the manager is not yet initialized. * * @returns {MediaDeviceInfo[]} An array of all available media devices. * @throws {Error} If the manager is not yet initialized. */ getAllDevices(): MediaDeviceInfo[]; /** * Starts capturing audio from the microphone with flexible constraints. * * If a deviceId is provided, it targets a specific microphone. You can also pass in * a full MediaTrackConstraints object to fine-tune behavior (e.g., noise suppression, echo cancellation). * * This method: * - Emits the audio stream over the socket under the label `"mic"`. * - Starts volume monitoring and emits microphone volume under the label `"micMeter"`. * * @param {string|MediaTrackConstraints|null} options - Either a deviceId string, a full constraints object, or null for defaults. * @param {boolean} hearVoice - `true` = Hear your voice. * @returns {Promise<MediaStream>} A promise resolving to the active audio stream. * @throws {Error} If the deviceId is invalid or if no audio track is found in the stream. */ startMic(options?: string | MediaTrackConstraints | null, hearVoice?: boolean): Promise<MediaStream>; /** * Starts capturing video from the webcam with flexible constraints. * * Accepts a deviceId string for a specific webcam, or a full MediaTrackConstraints * object to customize video input (resolution, frameRate, etc.). * * This method: * - Emits the video stream over the socket under the label `"cam"`. * * @param {string|MediaTrackConstraints|null} options - Either a deviceId string, a full constraints object, or null for defaults. * @returns {Promise<MediaStream>} A promise resolving to the active video stream. * @throws {Error} If the deviceId is invalid. */ startCam(options?: string | MediaTrackConstraints | null): Promise<MediaStream>; /** * Starts screen sharing with customizable audio and video constraints. * * You can pass a boolean to enable or disable audio, * or an object to define custom `audio` and `video` constraints. * * This method: * - Emits the screen stream over the socket under the label `"screen"`. * - If audio is present, starts volume monitoring and emits under the label `"screenMeter"`. * * @param {boolean|ScreenShareConstraints} options - `true` = enable audio, `false` = no audio, or an object with audio/video constraints. * @param {boolean} hearScreen - `true` = Hear your screen audio (**Your ear will be destroyed!!!**). * @returns {Promise<MediaStream>} A promise resolving to the active screen capture stream. * @throws {Error} If the options are invalid. */ startScreen(options?: boolean | ScreenShareConstraints, hearScreen?: boolean): Promise<MediaStream>; /** * Stops the microphone stream if active. * * Stops all tracks from the current microphone stream and clears its reference. * Throws an error if the microphone stream is not set or invalid. * * @throws {Error} If the microphone stream is not active or invalid. */ stopMic(): void; /** * Stops the webcam stream if active. * * Stops all tracks from the current webcam stream and clears its reference. * Throws an error if the webcam stream is not set or invalid. * * @throws {Error} If the webcam stream is not active or invalid. */ stopCam(): void; /** * Stops the screen sharing stream if active. * * Stops all tracks from the current screen stream and clears its reference. * Throws an error if the screen stream is not set or invalid. * * @throws {Error} If the screen sharing stream is not active or invalid. */ stopScreen(): void; /** * Stops all active media streams (microphone, webcam, and screen share). * * This method calls the individual stop methods to ensure each stream is safely terminated. * Use this when you want to stop all media input/output at once. */ stopAll(): void; /** * Generates a unique media identifier string based on the given parameters. * * @param {string} userId - The ID of the user. * @param {string} type - The type/category of the media. * @param {string} mime - The MIME type of the media. * @param {ReceiverTags} element - The name of the media element. * @returns {string} A concatenated string uniquely identifying the media. */ getMediaId(userId: string, type: string, mime: string, element: ReceiverTags): string; /** * Checks if a media receiver exists for the given stream parameters. * * @param {string} userId - The user id to attach the stream. * @param {string} type - The stream type, e.g., 'mic'. * @param {string} mime - The mime type, e.g., 'audio/webm;codecs=opus'. * @param {ReceiverTags} element - The tag name needs to be `audio` or `video` to attach the stream. * @returns {boolean} */ hasReceiver(userId: string, type: string, mime: string, element: ReceiverTags): boolean; /** * Deletes a media receiver. * * @param {string} userId - The user id to attach the stream. * @param {string} type - The stream type, e.g., 'mic'. * @param {string} mime - The mime type, e.g., 'audio/webm;codecs=opus'. * @param {ReceiverTags} element - The tag name needs to be `audio` or `video` to attach the stream. */ deleteReceiver(userId: string, type: string, mime: string, element: ReceiverTags): void; /** * Gets a media player for continuous streaming from received chunks. * * @param {string} userId - The user id to attach the stream. * @param {string} type - The stream type, e.g., 'mic'. * @param {string} mime - The mime type, e.g., 'audio/webm;codecs=opus'. * @param {ReceiverTags} element - The tag name needs to be `audio` or `video` to attach the stream. * @returns {TinyMediaReceiver} - If the instance has not yet been created, it will be created automatically. * @throws {Error} If no media receiver exists for the given parameters. */ getReceiver(userId: string, type: string, mime: string, element: ReceiverTags): TinyMediaReceiver; /** * Initializes a media player for continuous streaming from received chunks. * * @param {string} userId - The user id to attach the stream. * @param {StreamTypes} type - The stream type, e.g., 'mic'. * @param {string} mimeType - The mime type, e.g., 'audio/webm;codecs=opus'. * @param {ReceiverTags} element - The tag name needs to be `audio` or `video` to attach the stream. * @param {Object} [options={}] * @param {number} [options.maxBufferBack=10] - Maximum buffer (in seconds) back to keep in the buffer behind the current time. * @param {number} [options.cleanupTime] - Interval time in milliseconds to perform buffer cleanup. Must be a positive number. * @param {number} [options.bufferTolerance] - Tolerance value (in seconds) used when comparing buffer ranges. Must be a positive number. * @returns {TinyMediaReceiver} - If the instance has not yet been created, it will be created automatically. */ initReceiver(userId: string, type: StreamTypes, mimeType: string, element: ReceiverTags, { maxBufferBack, cleanupTime, bufferTolerance }?: { maxBufferBack?: number | undefined; cleanupTime?: number | undefined; bufferTolerance?: number | undefined; }): TinyMediaReceiver; /** * Destroys the instance by terminating all active streams, stopping processes, and removing all event listeners. * * This method performs a full cleanup of the instance. It first iterates through all entries in `#streams` and calls * their `destroy()` methods to properly dispose of any underlying resources (e.g., sockets, file handles, etc.). After that, * it clears the `#streams` map entirely. It also calls `stopAll()` to terminate any ongoing operations or processes, * and finally removes all listeners from both `#events` and `#sysEvents` to avoid memory leaks or unintended side effects. * * Call this method when the instance is no longer needed or before disposing of it. * * @returns {void} */ destroy(): void; #private; } export type StreamTypes = "mic" | "cam" | "screen"; export type StreamEventTypes = "Mic" | "Cam" | "Screen"; export type ReceiverTags = import("./TinyMediaReceiver.mjs").ReceiverTags; export type StreamConfig = { /** * - A valid MIME type for MediaRecorder. */ mimeType: string; /** * - Interval in milliseconds for emitting data chunks. */ timeslice: number; /** * - Audio codec that can be used */ audioCodec: string | null; /** * - Video codec that can be used */ videoCodec: string | null; }; export type MandatoryConstraints = { /** * Capture source for Chrome/Electron. */ chromeMediaSource?: "screen" | "window" | "application" | "desktop" | undefined; /** * Specific ID of the capture source (usually obtained via desktopCapturer). */ chromeMediaSourceId?: string | undefined; /** * Maximum width of the capture. */ maxWidth?: number | undefined; /** * Maximum height of the capture. */ maxHeight?: number | undefined; /** * Maximum frame rate. */ maxFrameRate?: number | undefined; /** * Minimum width of the capture. */ minWidth?: number | undefined; /** * Minimum height of the capture. */ minHeight?: number | undefined; /** * Minimum frame rate. */ minFrameRate?: number | undefined; /** * Experimental setting used in some Chromium versions. */ googLeakyBucket?: boolean | undefined; /** * Experimental setting for temporal layers in screen capture. */ googTemporalLayeredScreencast?: boolean | undefined; }; export type AdvancedScreenVideoConstraints = { /** * Capture source, useful in Electron and Firefox. */ mediaSource?: "screen" | "window" | "application" | "browser" | "monitor" | undefined; /** * Used in older versions of Chrome/Electron. Usually 'desktop'. */ chromeMediaSource?: string | undefined; /** * Used in Electron to select a specific screen or window. */ chromeMediaSourceId?: string | undefined; /** * Desired capture frame rate (e.g., 30 or 60). */ frameRate?: number | undefined; /** * Ideal capture width. */ width?: number | undefined; /** * Ideal capture height. */ height?: number | undefined; /** * Allows configuring advanced fields in Electron/Chrome (like `chromeMediaSource`). */ mandatory?: MandatoryConstraints | undefined; }; export type ScreenShareConstraints = { audio?: boolean | MediaTrackConstraints | undefined; /** * Constraints for screen capture, can be simple (true) or detailed. */ video: boolean | AdvancedScreenVideoConstraints | MediaTrackConstraints; }; import { EventEmitter } from 'events'; import VolumeMeter from './VolumeMeter.mjs'; import TinyMediaReceiver from './TinyMediaReceiver.mjs'; //# sourceMappingURL=TinyStreamManager.d.mts.map