UNPKG

@rs1/media-player

Version:

A smart media player for React, for audio and video content.

1,399 lines 90.3 kB
/// <reference types="react" /> import React from "react"; declare const _default: React.ForwardRefExoticComponent<{ style?: React.CSSProperties | undefined; className?: string | undefined; } & React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>; /** * This component is responsible for rendering the media element * composing the media components together. * We do so to separate the rendering loop between `MediaWrapper`, * `MediaElement` and `MediaSource` to avoid unnecessary re-renders. */ declare function Media(): JSX.Element; /* ┐ │ File: types.ts [/src/bits/controls/types.ts] │ Package: @rs1/media-player | RS1 Project │ Author: Andrea Corsini │ Created: April 28th, 2023 - 17:47:45 │ Modified: May 9th, 2023 - 12:40:01 │ │ Copyright (c) 2023 Andrea Corsini T/A RS1 Project. │ This work is licensed under the terms of the MIT License. │ For a copy, see https://opensource.org/licenses/MIT │ or the LICENSE file in the root of this project. └ */ type ControlKey = /* Buttons */ "play" | "pause" | "playpause" | "backward10" | "forward10" | "previous" | "next" | "mute" | "fullscreen" | "pictureinpicture" | "shuffle" | "repeat" | "airplay" | "cast" | "playlist" | "seekbar" | "time" | "duration" | "remaining" | "title" | "artist" | "album" | "metadata" | "position" | "prefix" | "suffix" | "index" | "loading" | "stalled" | "error" | "spacer" | "empty"; /** * The props the custom control receives. */ type CustomControlProps = { className?: string; style?: React.CSSProperties; [key: string]: unknown; }; /** * A control is either a key of the ControlKey type or a React component that accepts CustomControlProps. */ type GenericControlType = ControlKey | React.ComponentType<CustomControlProps>; /** * A controls-group is an array of controls (either keys or components). */ type ControlsGroupType = GenericControlType[]; /** * A controls-row is an array of controls (either keys or components) or control-groups. * This allows nesting controls in a grid-like structure. */ type ControlsRowType = (GenericControlType | ControlsGroupType)[]; /** * A controls-grid is an array of controls-rows or a single controls-row. * Again, this allows nesting controls in a grid-like structure. */ type ControlsGridType = ControlsRowType[] | ControlsRowType; /** * Map of textually defined button sizes to their numeric values (in `rem` for TailwindCSS) */ declare const buttonSizes: { readonly xs: 2; readonly sm: 4; readonly md: 6; readonly lg: 8; readonly xl: 10; }; type WithLabel = { /** * The label to display on the button */ label: string; /** * If we have a label, we can't have an icon */ icon?: never; }; type WithIcon = { /** * The icon to display on the button. * If it's a string, it's assumed to be the content of an SVG file; * this will be parsed to extract paths and viewbox and then rendered as a React component. * If it's a React node, it's assumed to be a React component that will be rendered as-is. */ icon: string | React.ReactNode; /** * If we have an icon, we can't have a label */ label?: never; }; type BaseButtonProps = (WithLabel | WithIcon) & { /** * The key of the control, used to determine if there's a theme color to use for this particular button. */ controlKey?: ControlKey; /** * The size of the icon. Defaults to `md`. */ size?: keyof typeof buttonSizes; /** * Whether the button is currently loading. If `true`, a spinner will be displayed instead of the button content. * Defaults to `false`. */ loading?: boolean; /** * Whether the button is currently in error state. If `true`, an error icon will be displayed instead of the button. * Defaults to `false`. */ error?: boolean; /** * Whether the button is currently active. If `false`, the button will be rendered with a 50% opacity. * It's useful for `toggle` buttons to show if the action the button performs is currently active or not. * Defaults to `true`. */ active?: boolean; } & React.ButtonHTMLAttributes<HTMLButtonElement>; declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<BaseButtonProps & React.RefAttributes<HTMLButtonElement>>>; type BaseTagProps = { /** * The key of the control, used to determine if there's * a theme color to use for this particular element. */ controlKey?: ControlKey; /** * The text to display. */ text?: string; } & React.HTMLAttributes<HTMLSpanElement>; declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<{ controlKey?: ControlKey | undefined; text?: string | undefined; } & React.HTMLAttributes<HTMLSpanElement> & React.RefAttributes<HTMLSpanElement>>>; /* ┐ │ File: aspect-ratio.ts [/src/media/utils/aspect-ratio.ts] │ Package: @rs1/media-player | RS1 Project │ Author: Andrea Corsini │ Created: April 25th, 2023 - 11:40:05 │ Modified: April 27th, 2023 - 10:35:00 │ │ Copyright (c) 2023 Andrea Corsini T/A RS1 Project. │ This work is licensed under the terms of the MIT License. │ For a copy, see https://opensource.org/licenses/MIT │ or the LICENSE file in the root of this project. └ */ declare const aspectRatios: readonly [ "1:1", "2:1", "1:2", "3:1", "1:3", "2:3", "3:2", "4:3", "3:4", "16:9", "9:16", "21:9", "9:21" ]; type AspectRatio = (typeof aspectRatios)[number] | "auto" | "stretch"; type MediaType = "audio" | "video"; /** * Defines a new type that depends on media types. * Given a type `T`, it returns a type that allows either `T` directly * or an array of tuples of the form `[MediaType, T]`. */ type WithMediaType<T> = T | [ MediaType, T ][]; /** * Defines a new type that depends on breakpoints. * Given a type `T`, it returns a type that allows either `T` directly * or a map of the form `{ [Breakpoint]: T }`. */ type WithBreakpoint<T> = T | { [K in Breakpoint]?: T; }; /** * The configuration object for the media player. * This object needs to be passed to the `MediaProvider` component via the `config` prop. * It will then be available to all the children components via the `useMediaConfig` hook. */ interface MediaConfig { /** * The aspect ratio of the player.\ * It can be specified for each media type by using an array of tuples. (Format: `[[mediaType1, ratio1], [mediaType2, ratio2]]`)\ * It can be specified for each breakpoint by using a map. (Format: `{ [breakpoint1]: ratio1, [breakpoint2]: ratio2 }` or `{ [breakpoint1]: [[mediaType1, ratio1], [mediaType2, ratio2]], [breakpoint2]: [[mediaType1, ratio1], [mediaType2, ratio2]] }`)\ * * **Note:** Only `auto` and `stretch` are valid aspect ratios if the player mode is not `video`. * ```js * // It can be a specific ratio: * const squareRatio = '1:1' * const doubleSquareRatio = '2:1' * const halfSquareRatio = '1:2' * const tripleSquareRatio = '3:1' * const thirdSquareRatio = '1:3' * const doubleThirdRatio = '2:3' * const tripleDoubleRatio = '3:2' * const fourThirdRatio = '4:3' * const thirdFourRatio = '3:4' * const wideRatio = '16:9' * const tallRatio = '9:16' * const wideCinemaRatio = '21:9' * const tallCinemaRatio = '9:21' * * // It can be `auto` (the intrinsic aspect ratio of the video): * const autoRatio = 'auto' * * // It can be `stretch` (the player will stretch to fill the container): * const stretchRatio = 'stretch' * ``` * @default 'auto' */ aspectRatio: WithBreakpoint<WithMediaType<AspectRatio>>; /** * The display mode of the player.\ * It can be specified for each media type by using an array of tuples. (Format: `[[mediaType1, mode1], [mediaType2, mode2]]`) * It can be specified for each breakpoint by using a map. (Format: `{ [breakpoint1]: mode1, [breakpoint2]: mode2 }` or `{ [breakpoint1]: [[mediaType1, mode1], [mediaType2, mode2]] }`) * * Supported modes: * - `auto`: the player will choose the best mode based on the media type and the device (`video` for video, `artwork` for audio) (default) * - `video`: the player will display as a video player * - `artwork`: the player will display as an artwork player (similar to Spotify, Apple Music, etc. - shows the artwork and the controls) * - `vinyl`: the player will display as a vinyl player (shows the artwork as a spinning vinyl and the controls) * - `controls`: the player will display as a controls-only player (shows only the controls) * - `artwork-mini`: the player will display as a mini artwork player (similar to Spotify, Apple Music, etc. - shows the artwork and the controls) * - `vinyl-mini`: the player will display as a mini vinyl player (shows the artwork as a spinning vinyl and the controls) * ```js * // It can be a string (the same mode for all media types and breakpoints). * // Format: `mode` * // This will use the `artwork` mode for all media types and breakpoints. * const playerMode = 'artwork' * * // It can be an array of tuples (a different mode for each media type). * // Format: `[[mediaType1, mode1], [mediaType2, mode2]]` * // This will use the `artwork` mode for audio and the `video` mode for video (for all breakpoints). * const playerMode = [['audio', 'artwork'], ['video', 'video']] * * // It can be a map of strings (a different mode for each breakpoint). * // Format: `{ [breakpointName]: mode }` * // This will use the `artwork-mini` mode for the `xs` breakpoint and the `artwork` mode for the `md` breakpoint (for all media types). * const playerMode = { xs: 'artwork-mini', md: 'artwork' } * * // It can be a map of arrays of tuples (a different mode for each media type and breakpoint). * // Format: `{ [breakpointName]: [[mediaType1, mode1], [mediaType2, mode2]] }` * // This will use the `artwork-mini` mode for audio and the `video` mode for video for the `xs` breakpoint and the `artwork` mode for audio and the `video` mode for video for the `md` breakpoint. * const playerMode = { xs: [['audio', 'artwork-mini'], ['video', 'video']], md: [['audio', 'artwork'], ['video', 'video']] } * ``` * @default 'auto' */ playerMode: WithBreakpoint<WithMediaType<"auto" | "video" | "artwork" | "vinyl" | "controls" | "artwork-mini" | "vinyl-mini">>; /** * The background of the player.\ * It can be specified for each media type by using an array of tuples. (Format: `[[mediaType1, bg1], [mediaType2, bg2]]`)\ * It can be specified for each breakpoint by using a map. (Format: `{ [breakpoint1]: bg1, [breakpoint2]: bg1 }` or `{ [breakpoint1]: [[mediaType1, bg1], [mediaType2, bg2]], [breakpoint2]: [[mediaType1, bg1], [mediaType2, bg2]] }`)\ * * Supported backgrounds: * - `none`: no background (default) * - `flat`: a flat background * - `blur`: a blurred background (the player will use the artwork blurred as background) * * **Note:** this has no effect if the player mode is `video`. * ``` * // A flat background for all breakpoints: * const playerBackground = 'flat' * * // A flat background for the `xs` breakpoint and a blurred background for the `md` breakpoint: * const playerBackground = { xs: 'flat', md: 'blur' } * ``` * @default 'none' */ playerBackground: WithBreakpoint<WithMediaType<"none" | "flat" | "blur">>; /** * A map of breakpoints to use for the player.\ * The map is in the form of `{ [breakpointName]: minWindowWith }`.\ * The breakpoints will be used to determine the player mode and the controls grid type * based on the width of the window. * ```js * // The default breakpoints: * const breakpoints = { * xs: 0, * sm: 576, * md: 768, * lg: 992, * xl: 1200, * } * ``` */ breakpoints: CustomBreakpoints; /** * Wether to automatically hide the controls after a certain amount of time. * * **Note:** this has no effect if the player mode is not `video`. * ``` * // The controls will always be visible: * const autoHideControls = false * * // The controls will be hidden after 3 seconds: * const autoHideControls = true * * // The controls will be hidden after `n` milliseconds: * const autoHideControls = 3000 // `n` milliseconds * ``` * @default 3000 */ autoHideControls: number | boolean; /** * The list of controls to show. The controls will be organized in rows and groups based on the provided array.\ * Controls can be represented either as a string (the name of the control) or as a React component.\ * Nesting arrays will create groups and rows.\ * It can be specified for each breakpoint by using a map. (Format: `{ [breakpoint1]: controls1, [breakpoint2]: controls2 }`) * ```js * // 3 rows of controls * const controls = [ * 'playpause', * ['time', 'seekbar', 'remaining'], * [ * ['mute', 'airplay'], * ['pictureinpicture', 'fullscreen'] * ] * ] * // The first row will contain the `playpause` control (same as `['playpause']`). * // A single control will be centered: * |----------[playpause]---------| * * // The second row will contain the `time`, `seekbar`, and `remaining` controls. * // Multiple controls in rows are spaced evenly: * |[time]--[seekbar]--[remaining]| * * // The third row will contain 2 groups: the first group will contain the `mute` and `airplay` controls, * // the second group will contain the `pictureinpicture` and `fullscreen` controls. * // Multiple groups in rows are spaced evenly between them, and the controls inside the groups * // are aligned left for the first group, right for the last group, and centered for the others: * ||[mute]--[airplay]----||----[pictureinpicture]--[fullscreen]|| * * // The final grid will look like this: * |----------------------------------[ playpause ]----------------------------------| * |[ time ]----------------[ seekbar ]----------------[ remaining ]| * ||[ mute ]--[ airplay ]-------||----[pictureinpicture]--[ fullscreen ]|| * ``` * * For the list of available controls, see the `ControlKey` type or `src/bits/controls`.\ * All predefined controls can be included importing the `controls` map. * ```js * import { controls } from '@rs1/media-player' * ``` * For the default controls grid, see `src/media/factories/media-config.ts`. * * @default * ```js * const controls = [ * [ * ['playlist', 'shuffle', 'error'], * ['previous', 'backward10', 'playpause', 'forward10', 'next'], * ['stalled', 'repeat'] * ], * ['time', 'seekbar', 'remaining'], * [ * ['mute', 'airplay'], * 'metadata', * ['pictureinpicture', 'fullscreen'] * ], * ] * ``` */ controls: WithBreakpoint<ControlsGridType | false>; /** * The separator to use between the metadata items in the metadata control. * ``` * // The metadata items will be separated by a dot: * const controlsMetadataSeparator = ' . ' * * // The metadata items will be separated by a dash: * const controlsMetadataSeparator = ' - ' * ``` * @default ' — ' */ controlsMetadataSeparator: string; /** * Whether the media should start playing automatically. * * **Note:** on some browsers, autoplay is disabled by default and can only be enabled if the media is also muted. * ``` * // The media will start playing automatically: * const autoPlay = true * * // The media will not start playing automatically: * const autoPlay = false * ``` * @default false */ autoPlay: boolean; /** * Whether the media should restart automatically the same video/track when it ends. * ``` * // The video/track will restart automatically when it ends: * const autoRestart = true * * // The video/track will not restart automatically when it ends: * // (It will go to the next video/track in the playlist if there is one) * const autoRestart = false * ``` * @default false */ loop: boolean; /** * Whether the media should start playing as muted. * * **Notes:** * - use in combination with `autoPlay` to make sure the media will start playing automatically; * - use in combination with `canMute` if you don't want the user to be able to unmute the media. * ``` * // The media will start playing as muted: * const muted = true * * // The media will not start playing as muted: * const muted = false * ``` * @default false */ muted: boolean; /** * Whether the player should respond to keyboard events. * * The single key events can be disabled individually via the `can*` props. * * **Supported keyboard events:** * - ` ` (Space): toggle play/pause; * - (can be disabled via `canPause`) * - `←` (ArrowLeft): seek backward 10 seconds; * - (can be disabled via `canSeek`) * - `→` (ArrowRight): seek forward 10 seconds; * - (can be disabled via `canSeek`) * - `↑` (ArrowUp): increase volume by 5%; * - (can be disabled via `canChangeVolume`) * - `↓` (ArrowDown): decrease volume by 5%; * - (can be disabled via `canChangeVolume`) * - `m` (KeyM): toggle mute/unmute; * - (can be disabled via `canMute`) * - `f` (KeyF): toggle fullscreen; * - (can be disabled via `canFullscreen`) * - `p` (KeyP): toggle picture-in-picture; * - (can be disabled via `canPictureInPicture`) * - `r` (KeyR): toggle repeat-all/repeat-one; * - (can be disabled via `canLoop`) * - `l` (KeyL): toggle repeat-all/repeat-one; * - (can be disabled via `canLoop`) * - `s` (KeyS): toggle shuffle; * - (can be disabled via `canShuffle`) * ``` * // The player will respond to keyboard events: * const keyboardControls = true * * // The player will not respond to keyboard events: * const keyboardControls = false * ``` * @default true */ canControlWithKeyboard: boolean; /** * Whether the playlist can be shuffled via the UI/keyboard. * @default true */ canShuffle: boolean; /** * Whether the current video/track can be looped via the UI/keyboard. * (Loop means that the current video/track will be played again when it ends.) * @default true */ canLoop: boolean; /** * Whether the player can be paused via the UI/keyboard. * @default true */ canPause: boolean; /** * Whether the player can seek to a specific time via the UI/keyboard. * @default true */ canSeek: boolean; /** * Whether the player can be muted via the UI/keyboard. * @default true */ canMute: boolean; /** * Whether the player volume can be changed via the UI/keyboard. * @default true */ canChangeVolume: boolean; /** * Whether the player playback rate can be changed via the UI/keyboard. * @default true */ canChangePlaybackRate: boolean; /** * Whether the player can enter/exit fullscreen via the UI/keyboard. * @default true */ canFullscreen: boolean; /** * Whether the player can enter/exit picture-in-picture via the UI/keyboard. * @default true */ canPictureInPicture: boolean; /** * Whether the player can stream to AirPlay via the UI/keyboard. * @default true */ canAirPlay: boolean; /** * Whether the player can stream to remote devices via the UI/keyboard. * @default true */ canRemotePlay: boolean; /** * Whether the user should be able to tap/click on the player to toggle play/pause. * - For `vinyl`/`artwork` mode this means tapping on the artwork. * - For `video` mode this means tapping on the video. * This has no effect if `canPause` is `false`, or if the player mode is `controls`. * @default true */ canTapToPause: boolean; /** * Whether the user should be able to double tap/click on the player to seek. * (Double tap on the left side of the player to seek backward 10 seconds, double tap on the right side to seek forward 10 seconds.) * - For `vinyl`/`artwork` mode this means double tapping on the artwork. * - For `video` mode this means double tapping on the video. * This has no effect if `canSeek` is `false`, or if the player mode is `controls`, `artwork-mini`, or `vinyl-mini`. * @default true */ canDoubleTapToSeek: boolean; } /** * A collection of functions to control the media. * These functions are available to all the `MediaProvider` children via the `useMediaControls` hook. */ interface MediaControls { /** * Starts playing the media. */ play: () => void; /** * Pauses the media. */ pause: () => void; /** * Toggles play/pause. * (If the media is playing it will be paused, if the media is paused it will be played.) */ togglePlay: () => void; /** * Seeks to a specific time in seconds. * @param {number} time - The time to seek to. * Time must be a number between `0` and `duration`. * @example * ``` * const { seek } = useMediaControls() * * // Seek to time 1:23 * seek(83) * ``` */ seek: (time: number) => void; /** * Skips `offset` seconds forward/backward from the current time. * @param {number} offset - The offset to skip. * @example * ``` * const { skip } = useMediaControls() * * // Skip 10 seconds forward * skip(10) * * // Skip 10 seconds backward * skip(-10) * ``` */ skip: (offset: number) => void; /** * Skips to the next video/track in the playlist. * If the current video/track is the last one in the playlist, it will skip to the first video/track. * @example * ``` * const { next } = useMediaControls() * next() * ``` */ next: () => void; /** * Skips to the previous video/track in the playlist. * If the current video/track is the first one in the playlist, it will skip to the last video/track. * @example * ``` * const { previous } = useMediaControls() * previous() * ``` */ previous: () => void; /** * Mutes the media. * @example * ``` * const { mute } = useMediaControls() * mute() * ``` */ mute: () => void; /** * Unmutes the media. * @example * ``` * const { unmute } = useMediaControls() * unmute() * ``` */ unmute: () => void; /** * Toggles the media mute state. * (If the media is muted it will be unmuted, if the media is unmuted it will be muted.) * @example * ``` * const { toggleMute } = useMediaControls() * toggleMute() * ``` */ toggleMute: () => void; /** * Pushes the player into fullscreen mode. * @example * ``` * const { enterFullscreen } = useMediaControls() * enterFullscreen() * ``` */ enterFullscreen: () => void; /** * Exits fullscreen mode. * @example * ``` * const { exitFullscreen } = useMediaControls() * exitFullscreen() * ``` */ exitFullscreen: () => void; /** * Toggles fullscreen mode. * (If the player is in fullscreen mode it will exit fullscreen mode, if the player is not in fullscreen mode it will enter fullscreen mode.) * @example * ``` * const { toggleFullscreen } = useMediaControls() * toggleFullscreen() * ``` */ toggleFullscreen: () => void; /** * Pushes the player into picture-in-picture mode. * It works only if the browser supports picture-in-picture and a video is playing. * @example * ``` * const { enterPiP } = useMediaControls() * enterPiP() * ``` */ enterPiP: () => void; /** * Exits picture-in-picture mode. * @example * ``` * const { exitPiP } = useMediaControls() * exitPiP() * ``` */ exitPiP: () => void; /** * Toggles picture-in-picture mode. * It works only if the browser supports picture-in-picture and a video is playing. * (If the player is in picture-in-picture mode it will exit picture-in-picture mode, if the player is not in picture-in-picture mode it will enter picture-in-picture mode.) * @example * ``` * const { togglePiP } = useMediaControls() * togglePiP() * ``` */ togglePiP: () => void; /** * Seeks to a specific time expressed in percentage [0-1]. (`progress = time / duration`) * @param {number | ((progress: number) => number)} progress - The progress to seek to. * It can either be: * - a number between `0` and `1`, * - a function that receives the current progress and returns a new progress. * @example * ``` * const { setProgress } = useMediaControls() * * // Seek halfway through the video/track * setProgress(0.5) * * // Seek forward of 10% * setProgress(progress => progress + 0.1) * ``` */ setProgress: (progress: number | ((progress: number) => number)) => void; /** * Sets the volume of the media player. * @param {number | ((volume: number) => number)} volume - The volume to set. * It can either be: * - a number between `0` and `1` (where `0` is muted and `1` is max volume), * - a function that receives the current volume and returns a new volume. * @example * ``` * const { setVolume } = useMediaControls() * * // Set the volume to 50% * setVolume(0.5) * * // Increase the volume by 10% * setVolume(volume => volume + 0.1) * ``` */ setVolume: (volume: number | ((volume: number) => number)) => void; /** * Sets the playback rate of the media player. * @param {MediaPlaybackRate | ((playbackRate: MediaPlaybackRate) => MediaPlaybackRate)} playbackRate - The playback rate to set. * It can either be: * - a number between 0.25 and 2 (where 1 is the normal playback rate) * - - *[this should be one of 0.25, 0.5, 0.75, **1**, 1.25, 1.5, 2]* * - a function that receives the current playback rate and returns a new playback rate. * @example * ``` * const { setPlaybackRate } = useMediaControls() * * // Slow down the playback rate to 0.5x * setPlaybackRate(0.5) * * // Increase the playback rate by 0.25x * setPlaybackRate(playbackRate => playbackRate + 0.25) * ``` * @see MediaPlaybackRate * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/playbackRate */ setPlaybackRate: (playbackRate: MediaPlaybackRate | ((playbackRate: MediaPlaybackRate) => MediaPlaybackRate)) => void; } /** * A TypeScript representation of a CSS color. * Supports: * - hex colors (`#000000`, `#000`, `#00000000`, `#0000`), * - rgb colors (`rgb(0, 0, 0)`), * - rgba colors (`rgba(0, 0, 0, 0)`), * - hsl colors (`hsl(0, 0%, 0%)`), * - hsla colors (`hsla(0, 0%, 0%, 0)`), * - keywords (`transparent`, `currentcolor`, `inherit`). */ type Color = `#${string}` | `rgb(${number}, ${number}, ${number})` | `rgba(${number}, ${number}, ${number}, ${number})` | `hsl(${number}, ${number}%, ${number}%)` | `hsla(${number}, ${number}%, ${number}%, ${number})` | "transparent" | "currentcolor" | "inherit"; /** * The theme object for the media player. * This object needs to be passed to the `MediaProvider` component via the `theme` prop. * It will then be available to all the children components via the `useMediaTheme` hook. * * See `./src/media/factories/media-theme.ts` for the default themes. */ interface MediaTheme extends Partial<Record<ControlKey | keyof MediaTrack, Color>> { /** * The foreground color of the controls. * This means for example the color of the play/pause button, the seek bar, etc. * * **Example:** `#EEEEEE` */ controlsColor: Color; /** * The background color of the controls. * When the player is in `video` mode, this (faded and blurred) will be the color of the controls bar. * When the player is in `artwork`, `vinyl`, or `controls` mode: * - if the `playerBackground` is set to `none`, this will have no effect, * - if the `playerBackground` is set to `flat` or `blur`, this will be the background of the player. * * **Example:** `#000000` */ controlsBg: Color; /** * The color of the controls when hovered. * This means for example the color of the play/pause button when hovered. * * **Example:** `#FFFFFF` */ accentColor: Color; /** * The color to use for the error message and the error icon. * (When the media player fails to load a media track) * * **Example:** `#FF0000` */ errorColor: Color; /** * The background color of the playing media. * This will have different effects depending on the mode of the player: * - `video`: this will be the background of the player. * - `artwork`: this will be the background of the artwork (only visible if the artwork has transparent parts). * - all others: this will have no effect. * * **Example:** `#000000` */ mediaBg: Color; /** * The font family to use for the media player. * * **Example:** `"Arial", sans-serif` */ textFont: string; /** * The font family to use for the mono-spaced text in the media player. * (For example the time in the seek bar) * * **Example:** `"Courier New", monospace` */ monoFont: string; } /** * @typedef {Object} MediaTrack - A representation of a media track. */ interface MediaTrack { /** * The source(s) of the media track.\ * If multiple sources are provided, the first one that is supported by the browser will be used. */ src: string[]; /** * The type of the media track, either `audio` or `video`. */ type: MediaType; /** * The MIME type of the media track.\ * If multiple sources are provided, this should be an array of MIME types.\ * If not provided, the MIME type will be inferred from the source(s) extension(s). */ mimeType: string[]; /** * The unique ID of the media track.\ * Useful for identifying the track in the playlist or setting the active track later on. */ id: string | number; /** * The index of the media track in the playlist. */ index: number; /** * The title of the media track. */ title?: string; /** * The artist of the media track. */ artist?: string; /** * The album of the media track. */ album?: string; /** * An URL to the cover image of the media track.\ * It will be used as the cover of the track in the `artwork`/`vinyl` player mode. */ cover?: string; /** * An URL to the artwork image of the media track.\ * It will be used in the MediaSession API (Native media controls).\ * It will serve as a fallback cover image for the `artwork`/`vinyl` player mode. */ artwork?: string; /** * An URL to the thumbnail image of the media track.\ * It will be used as the preview thumbnail in the `video` player mode.\ * It will serve as a fallback artwork image for the MediaSession API.\ * It will serve as a fallback cover image for the `artwork`/`vinyl` player mode. */ thumbnail?: string; /** * Optionally, a string to prefix the track position with.\ * Useful for dual-sided vinyls (A/B) or compilations (CD1/CD2). */ prefix?: string; /** * Optionally, a string to suffix the track position with.\ * Useful for dual-sided vinyls (A/B) or compilations (CD1/CD2). */ suffix?: string; /** * Optionally, a string or number representing the track position in the playlist/album.\ * Useful for tracklists and albums. (1, 2, 3, ...) */ position?: string | number; /** * If the track request should include CORS headers, set this to `anonymous` or `use-credentials`.\ * If not provided, the track will be requested without CORS headers. */ crossOrigin?: "anonymous" | "use-credentials"; } type RawMediaTrack = Omit<MediaTrack, "type" | "src" | "mimeType" | "id" | "index" | "crossOrigin"> & { src: string | string[]; mimeType?: string | string[]; id?: string | number; type?: MediaType; crossOrigin?: "anonymous" | "use-credentials" | "" | boolean; }; /** * @typedef {Object} MediaPlaylist - A representation of a media playlist. */ interface MediaPlaylist { /** * The unique ID of the playlist. */ id: string; /** * The title of the whole playlist.\ * If no album is provided for a track, this will be used as the album name. */ title?: string; /** * The artist of the whole playlist.\ * If no artist is provided for a track, this will be used as the artist name. */ artist?: string; /** * The album name of the playlist. * If no album is provided for a track and no title is provided for the playlist, * this will be used as the album name. */ album?: string; /** * An URL to the cover image to be used for the whole playlist.\ * If no cover is provided for a track, this will be used as the cover of the track. */ cover?: string; /** * An URL to the artwork image to be used for the whole playlist.\ * If no artwork is provided for a track, this will be used as the artwork of the track. */ artwork?: string; /** * An URL to the thumbnail image to be used for the whole playlist.\ * If no poster is provided for a track, this will be used as the poster of the track. */ thumbnail?: string; /** * Optionally, a string to prefix all track positions with.\ * Useful for dual-sided vinyls (A/B) or compilations (CD1/CD2). */ prefix?: string; /** * Optionally, a string to suffix all track positions with.\ * Useful for dual-sided vinyls (A/B) or compilations (CD1/CD2). */ suffix?: string; /** * An array of media tracks in the playlist. */ tracks: MediaTrack[]; } type RawMediaPlaylist = Omit<MediaPlaylist, "id" | "tracks"> & { id?: string; tracks: RawMediaTrack[]; }; /** * A collection of functions to control the playlist. * These functions are available to all the `MediaProvider` children via the `usePlaylistControls` hook. */ interface PlaylistControls { /** * Activates the loop state for the current track. * @example * ```js * const { loop } = usePlaylistControls() * loop() * ``` */ loop: () => void; /** * Deactivates the loop state for the current track. * @example * ```js * const { unloop } = usePlaylistControls() * unloop() * ``` */ unloop: () => void; /** * Toggles the loop state of the current track. * If the track is looped, it will be unlooped. * If the track is unlooped, it will be looped. * @example * ```js * const { toggleLoop } = usePlaylistControls() * toggleLoop() * ``` */ toggleLoop: () => void; /** * Shuffles the current playlist. * @example * ```js * const { shuffle } = usePlaylistControls() * shuffle() * ``` */ shuffle: () => void; /** * Unshuffles the current playlist. * @example * ```js * const { unshuffle } = usePlaylistControls() * unshuffle() * ``` */ unshuffle: () => void; /** * Toggles the shuffle state of the current playlist. * If the playlist is shuffled, it will be unshuffled. * If the playlist is unshuffled, it will be shuffled. * @example * ``` * const { toggleShuffle } = usePlaylistControls() * toggleShuffle() * ``` */ toggleShuffle: () => void; /** * Sets the active track. * @param {RawMediaTrack | string | number | null} track - The track to set as active. * `track` can be one of the following: * - a `MediaTrack` object, it will be used as the active track. * - - `MediaTrack` objects are compared based on the `id` property. * - - The track will be added to the playlist if it's not already in it. * - a `string`, it will be used as the ID of the track in the playlist. * - a `number`, it will be used as the index of the track in the playlist. * - `null`, the active track will be unset. * @example * ```js * const { setTrack } = usePlaylistControls() * * // Set the first track in the playlist as active * setTrack(0) * * // Set the track with the ID "my-track" as active * setTrack("my-track") * * const track = { * src: "https://example.com/my-audio.mp3", * id: "my-audio-track" * } * // Set `track` as active * setTrack(track) * * // Unset the active track * setTrack(null) * ``` */ setTrack: (track: RawMediaTrack | string | number | null) => void; /** * Skips the active track by the specified offset. (Negative values are allowed) * @param {number} offset - The offset to skip the track by. * If the offset is greater than the playlist length, the track will be skipped to the end. * If the offset is lower than the negative playlist length, the track will be skipped to the start. * @example * ```js * const { skipTrackBy } = usePlaylistControls() * * // Go to the next track * skipTrackBy(1) * * // Go to the previous track * skipTrackBy(-1) * * // Considering a playlist with 10 tracks * // and the active track being the 2nd track * // ... * * // to reach the 5th track * skipTrackBy(3) * * // to reach the 1st track * skipTrackBy(-1) * * // this would still go to the 1st track * // because the offset is lower than the negative playlist length * skipTrackBy(-100) * * // this would still go to the 10th track * skipTrackBy(100) * ``` */ skipTrackBy: (offset: number) => void; /** * Skip to the previous track in the playlist. * If the active track is the first track in the playlist, the last track will be selected. * @example * ```js * const { previousTrack } = usePlaylistControls() * previousTrack() * * // This is equivalent to * const { skipTrackBy } = usePlaylistControls() * skipTrackBy(-1) * ``` */ previousTrack: () => void; /** * Skip to the next track in the playlist. * If the active track is the last track in the playlist, the first track will be selected. * @example * ```js * const { nextTrack } = usePlaylistControls() * nextTrack() * * // This is equivalent to * const { skipTrackBy } = usePlaylistControls() * skipTrackBy(1) * ``` */ nextTrack: () => void; } /** * @typedef {Object} PlaylistState - holds the state of the playlist */ interface PlaylistState { /** * Whether the playlist is currently shuffled. */ shuffle: boolean; /** * The repeat mode of the playlist. * - `repeat` - The playlist will be repeated. * (1 -> 2 -> 3 -> 1 -> 2 -> 3 -> ...) — *default* * - `repeat-one` - The active track will be repeated. * (1 -> 1 -> 1 -> 1 -> ...) */ mode: "repeat" | "repeat-one"; /** * The current media-tracks queue. */ queue: MediaTrack[]; } /** * Media intrinsic size in pixels. */ type MediaSize = readonly [ number, number ]; /** * Media playback rate as float between 0.25 and 2 in steps of 0.25. */ type MediaPlaybackRate = 0.25 | 0.5 | 0.75 | 1 | 1.25 | 1.5 | 1.75 | 2; /** * @typedef {Object} MediaState - holds the state of the media player */ interface MediaState { /** * Whether the media was playing before the last pause. * This is used to restore the playback state after an interruption-caused pause. * E.g. when the `MediaTrack` changes or the user exits fullscreen/PiP. * @private * @default false */ wasPlaying: boolean; /** * Whether the media is currently playing. * @default false */ isPlaying: boolean; /** * Whether the media is currently waiting for data to be loaded. * @default false */ isWaiting: boolean; /** * Whether the media is currently stalled. (is waiting for data but the data is not coming) * @default false */ isStalled: boolean; /** * Whether the media is currently seeking, and if so, in which direction. * @default false */ isSeeking: "forward" | "backward" | false; /** * Whether the media is currently loaded and ready to play. * @default false */ isLoaded: boolean; /** * Whether the media is currently muted. * @default false */ isMuted: boolean; /** * Whether the media is currently in fullscreen mode. * @default false */ isFullscreen: boolean; /** * Whether the media is currently in Picture-in-Picture mode. * @default false */ isPictureInPicture: boolean; /** * Whether the media is currently in AirPlay mode. * @default false */ isAirPlay: boolean; /** * Whether the media player is currently in immersive mode * (playing a media, with last user interaction more than 5 seconds ago). * @default false */ isImmersive: boolean; /** * Whether the media has encountered an error, and if so, the error message. * @default false */ hasError: string | false; /** * Whether the media has loaded data. * @default false */ hasLoadedData: boolean; /** * Whether the media has loaded at least the metadata. * Metadata includes the duration, the intrinsic size, etc. * @default false */ hasLoadedMetadata: boolean; /** * The current volume as a float between 0 and 1. * @default 1 */ volume: number; /** * The current playback rate as a float between 0.25 and 2 in steps of 0.25. * @see MediaPlaybackRate * @default 1 */ playbackRate: MediaPlaybackRate; /** * The intrinsic size of the media in pixels. * @see MediaSize * @default [0, 0] */ intrinsicSize: MediaSize; } /** * @typedef {Object} MediaTimeState - Holds the current time, duration, progress and buffer of the media. */ interface MediaTimeState { /** * The current playback time in seconds. * @default 0 */ time: number; /** * The total duration of the media in seconds. * @default 0 */ duration: number; /** * The current playback progress as a float between 0 and 1. * This is calculated as `time / duration`. * @default 0 */ progress: number; /** * The current buffered time as a float between 0 and 1. * This is calculated as `buffered / duration`. * @default 0 */ buffered: number; } type BaseTextProps = { /** * The text to display. */ text?: string; /** * A map of text to display for each property of the track. * @example { artist: 'Artist', title: 'Title' } * Knowing what property corresponds to what text allows * the user to customize the styling of each property, via the theme or CSS. * Each property will be wrapped in a span with the id `${id}-${property}`, * and will be separated from the next property by a span with the id `${id}-${property}-separator` * with a content of either the `separator` prop or the `controlsMetadataSeparator` from the config. */ textMap?: Partial<Record<keyof MediaTrack, string>>; /** * The separator to use between properties. * If not specified, the `controlsMetadataSeparator` from the config will be used. * If that is not specified either, a dash will be used. */ separator?: string; } & React.HTMLAttributes<HTMLSpanElement>; declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<{ text?: string | undefined; textMap?: Partial<Record<keyof MediaTrack, string>> | undefined; separator?: string | undefined; } & React.HTMLAttributes<HTMLSpanElement> & React.RefAttributes<HTMLSpanElement>>>; type BaseTimeProps = { type?: "time" | "duration" | "remaining"; } & React.HTMLAttributes<HTMLSpanElement>; declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<{ type?: "time" | "duration" | "remaining" | undefined; } & React.HTMLAttributes<HTMLSpanElement> & React.RefAttributes<HTMLSpanElement>>>; declare namespace controls { /* ┐ │ File: types.ts [/src/bits/controls/types.ts] │ Package: @rs1/media-player | RS1 Project │ Author: Andrea Corsini │ Created: April 28th, 2023 - 17:47:45 │ Modified: May 9th, 2023 - 12:40:01 │ │ Copyright (c) 2023 Andrea Corsini T/A RS1 Project. │ This work is licensed under the terms of the MIT License. │ For a copy, see https://opensource.org/licenses/MIT │ or the LICENSE file in the root of this project. └ */ type ControlKey = /* Buttons */ "play" | "pause" | "playpause" | "backward10" | "forward10" | "previous" | "next" | "mute" | "fullscreen" | "pictureinpicture" | "shuffle" | "repeat" | "airplay" | "cast" | "playlist" | "seekbar" | "time" | "duration" | "remaining" | "title" | "artist" | "album" | "metadata" | "position" | "prefix" | "suffix" | "index" | "loading" | "stalled" | "error" | "spacer" | "empty"; /** * The props the custom control receives. */ type CustomControlProps = { className?: string; style?: React.CSSProperties; [key: string]: unknown; }; /** * A control is either a key of the ControlKey type or a React component that accepts CustomControlProps. */ type GenericControlType = ControlKey | React.ComponentType<CustomControlProps>; /** * A controls-group is an array of controls (either keys or components). */ type ControlsGroupType = GenericControlType[]; /** * A controls-row is an array of controls (either keys or components) or control-groups. * This allows nesting controls in a grid-like structure. */ type ControlsRowType = (GenericControlType | ControlsGroupType)[]; /** * A controls-grid is an array of controls-rows or a single controls-row. * Again, this allows nesting controls in a grid-like structure. */ type ControlsGridType = ControlsRowType[] | ControlsRowType; /** * A button that shows the AirPlay picker.\ * Note: this button works only on Safari. It won't be rendered on other browsers. */ function AirPlay(props: CustomControlProps): JSX.Element | null; /** * A text label showing the current track's album.\ * Won't be rendered if the track has no album. */ function Album(props: CustomControlProps): JSX.Element; /** * A text label showing the current track's artist.\ * Won't be rendered if the track has no artist. */ function Artist(props: CustomControlProps): JSX.Element; /** * A button that skips backward 10 seconds.\ * Won't be rendered if the media can't be seeked. */ function Backward10(props: CustomControlProps): JSX.Element | null; /** * A text label showing the current track's duration.\ * Will be rendered as `--:--` if the track is not loaded. */ function Duration(props: CustomControlProps): JSX.Element; /** * An icon that shows when an error occurs while loading the media.\ * Will render an empty div if there's no error. */ function Error(props: CustomControlProps): JSX.Element; /** * A button that skips forward 10 seconds.\ * Won't be rendered if the media can't be seeked. */ function Forward10(props: CustomControlProps): JSX.Element | null; /** * A button that toggles fullscreen mode.\ * Won't be rendered if fullscreen is not supported/allowed. */ function Fullscreen(props: CustomControlProps): JSX.Element | null; /** * An icon that shows when the media is loading.\ * Will render an empty div if the media is already loaded.\ * **Note:** `loading` is different from `buffering`, `seeking` and `stalled`. * It means that the player is waiting for the media-source to load, and * this happens only once per playlist item. */ function Loading(props: CustomControlProps): JSX.Element; /** * A text label showing the current track's metadata (title, artist, album).\ * The metadata will be separated with the `controlsMetadataSeparator` config value (default: ` — `).\ * Won't be rendered if the track has no metadata. *