{"id":47836493,"url":"https://github.com/alessiofrittoli/react-media-player","last_synced_at":"2026-04-03T20:33:02.789Z","repository":{"id":342012277,"uuid":"1171917737","full_name":"alessiofrittoli/react-media-player","owner":"alessiofrittoli","description":"Handle media players with ease","archived":false,"fork":false,"pushed_at":"2026-03-30T15:47:40.000Z","size":204,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-03-30T17:38:11.678Z","etag":null,"topics":["audio-player","mediaplayer","mediaplayer-api","mediasession","mediasession-api","react-media-player","video-player"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/@alessiofrittoli/react-media-player","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alessiofrittoli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"license.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["alessiofrittoli"]}},"created_at":"2026-03-03T18:49:10.000Z","updated_at":"2026-03-30T15:47:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/alessiofrittoli/react-media-player","commit_stats":null,"previous_names":["alessiofrittoli/react-media-player"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/alessiofrittoli/react-media-player","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Freact-media-player","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Freact-media-player/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Freact-media-player/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Freact-media-player/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alessiofrittoli","download_url":"https://codeload.github.com/alessiofrittoli/react-media-player/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alessiofrittoli%2Freact-media-player/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31375769,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T17:53:18.093Z","status":"ssl_error","status_checked_at":"2026-04-03T17:53:17.617Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio-player","mediaplayer","mediaplayer-api","mediasession","mediasession-api","react-media-player","video-player"],"created_at":"2026-04-03T20:33:01.991Z","updated_at":"2026-04-03T20:33:02.775Z","avatar_url":"https://github.com/alessiofrittoli.png","language":"TypeScript","funding_links":["https://github.com/sponsors/alessiofrittoli"],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eReact Media Player 🎥\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n  Handle media players with ease\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://npmjs.org/package/@alessiofrittoli/react-media-player\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/@alessiofrittoli/react-media-player\" alt=\"Latest version\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://coveralls.io/github/alessiofrittoli/react-media-player\"\u003e\n    \u003cimg src=\"https://coveralls.io/repos/github/alessiofrittoli/react-media-player/badge.svg\" alt=\"Test coverage\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://socket.dev/npm/package/@alessiofrittoli/react-media-player/overview\"\u003e\n    \u003cimg src=\"https://socket.dev/api/badge/npm/package/@alessiofrittoli/react-media-player\" alt=\"Socket Security score\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://npmjs.org/package/@alessiofrittoli/react-media-player\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dm/@alessiofrittoli/react-media-player.svg\" alt=\"npm downloads\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://bundlephobia.com/package/@alessiofrittoli/react-media-player\"\u003e\n    \u003cimg src=\"https://badgen.net/bundlephobia/dependency-count/@alessiofrittoli/react-media-player\" alt=\"Dependencies\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://libraries.io/npm/%40alessiofrittoli%2Freact-media-player\"\u003e\n    \u003cimg src=\"https://img.shields.io/librariesio/release/npm/@alessiofrittoli/react-media-player\" alt=\"Dependencies status\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://bundlephobia.com/package/@alessiofrittoli/react-media-player\"\u003e\n    \u003cimg src=\"https://badgen.net/bundlephobia/min/@alessiofrittoli/react-media-player\" alt=\"minified\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://bundlephobia.com/package/@alessiofrittoli/react-media-player\"\u003e\n    \u003cimg src=\"https://badgen.net/bundlephobia/minzip/@alessiofrittoli/react-media-player\" alt=\"minizipped\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://bundlephobia.com/package/@alessiofrittoli/react-media-player\"\u003e\n    \u003cimg src=\"https://badgen.net/bundlephobia/tree-shaking/@alessiofrittoli/react-media-player\" alt=\"Tree shakable\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/sponsors/alessiofrittoli\"\u003e\n    \u003cimg src=\"https://img.shields.io/static/v1?label=Fund%20this%20package\u0026message=%E2%9D%A4\u0026logo=GitHub\u0026color=%23DB61A2\" alt=\"Fund this package\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n[sponsor-badge]: https://img.shields.io/static/v1?label=Fund%20this%20package\u0026message=%E2%9D%A4\u0026logo=GitHub\u0026color=%23DB61A2\n[sponsor-url]: https://github.com/sponsors/alessiofrittoli\n\n### Table of Contents\n\n- [Getting started](#getting-started)\n- [API Reference](#api-reference)\n  - [React Hooks](#react-hooks)\n    - [`useAudioPlayer`](#useaudioplayer)\n    - [`useAudioPlayerStore`](#useaudioplayerstore)\n    - [`useVideoPlayer`](#usevideoplayer)\n    - [`useVideoPlayerStore`](#usevideoplayerstore)\n    - [`useMediaPlayer`](#usemediaplayer)\n    - [`useVolume`](#usevolume)\n    - [`useVolumeStore`](#usevolumestore)\n    - [`useMediaPlayerController`](#usemediaplayercontroller)\n    - [`useMediaPlayerLoading`](#usemediaplayerloading)\n    - [`useMediaPreload`](#usemediapreload)\n    - [`useMediaSession`](#usemediasession)\n    - [`useMediaSessionPiP`](#usemediasessionpip)\n  - [React Components](#react-components)\n    - [`\u003cAudioPlayer /\u003e`](#audioplayer-)\n    - [`\u003cVideoPlayer /\u003e`](#videoplayer-)\n    - [`\u003cAudioPlayerProvider /\u003e`](#audioplayerprovider-)\n    - [`\u003cVideoPlayerProvider /\u003e`](#videoplayerprovider-)\n    - [`\u003cVolumeProvider /\u003e`](#volumeprovider-)\n  - [Utils](#utils)\n- [Development](#development)\n  - [Install dependencies](#install-dependencies)\n  - [Build the source code](#build-the-source-code)\n  - [ESLint](#eslint)\n  - [Jest](#jest)\n- [Contributing](#contributing)\n- [Security](#security)\n- [Credits](#made-with-)\n\n---\n\n### Getting started\n\nRun the following command to start using `react-media-player` in your projects:\n\n```bash\nnpm i @alessiofrittoli/react-media-player\n```\n\nor using `pnpm`\n\n```bash\npnpm i @alessiofrittoli/react-media-player\n```\n\n---\n\n### API Reference\n\n#### Defining the queue\n\n```ts\nimport { addItemsUUID } from \"@alessiofrittoli/react-media-player/utils\";\nimport type { Media, Queue } from \"@alessiofrittoli/react-media-player\";\n\ninterface PlaylistMedia extends Media {\n  customProp: boolean;\n}\n\ninterface Playlist extends Queue\u003cPlaylistMedia\u003e {\n  name?: string;\n}\n\nconst queue: Playlist = {\n  name: \"Playlist name\",\n  items: addItemsUUID\u003cPlaylistMedia\u003e([\n    {\n      src: \"/song.mp3\",\n      type: \"audio\",\n      title: \"Song title\",\n      album: \"Album name\",\n      artist: \"Artist name\",\n      customProp: true,\n      fade: { in: 1000, out: 1000 },\n      artwork: [\n        { src: \"/artwork-96.png\", sizes: 96, type: \"image/png\" },\n        { src: \"/artwork-128.png\", sizes: 128, type: \"image/png\" },\n        { src: \"/artwork-192.png\", sizes: 192, type: \"image/png\" },\n        { src: \"/artwork-256.png\", sizes: 256, type: \"image/png\" },\n        { src: \"/artwork-384.png\", sizes: 384, type: \"image/png\" },\n        { src: \"/artwork-512.png\", sizes: 512, type: \"image/png\" },\n      ],\n      videoArtwork: [{ src: \"/video-artwork.mp4\", type: \"video/mp4\" }],\n    },\n    {\n      src: \"/song-2.mp3\",\n      type: \"audio\",\n      title: \"Song title 2\",\n      album: \"Album name\",\n      artist: \"Artist name\",\n      customProp: true,\n      fade: { in: 1000, out: 1000 },\n      artwork: [\n        { src: \"/artwork-96.png\", sizes: 96, type: \"image/png\" },\n        { src: \"/artwork-128.png\", sizes: 128, type: \"image/png\" },\n        { src: \"/artwork-192.png\", sizes: 192, type: \"image/png\" },\n        { src: \"/artwork-256.png\", sizes: 256, type: \"image/png\" },\n        { src: \"/artwork-384.png\", sizes: 384, type: \"image/png\" },\n        { src: \"/artwork-512.png\", sizes: 512, type: \"image/png\" },\n      ],\n      videoArtwork: [{ src: \"/video-artwork.mp4\", type: \"video/mp4\" }],\n    },\n  ]),\n};\n```\n\n---\n\n#### React Hooks\n\n##### `useAudioPlayer`\n\nEasily handle React audio players.\n\nThis hook acts as a wrapper around [`useMediaPlayer`](#usemediaplayer) and it automatically creates `Audio` resource for you.\n\nPlease refer to [`useMediaPlayer`](#usemediaplayer) doc section for API reference.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { useAudioPlayer } from \"@alessiofrittoli/react-media-player\";\nimport type {\n  MediaChangeHandler,\n  PlaybackErrorHandler,\n} from \"@alessiofrittoli/react-media-player\";\n\nuseAudioPlayer({\n  queue,\n  initialMedia: queue.items.at(2),\n  normalizeVolume: true,\n  playPauseFadeDuration: 500,\n  preload: true,\n  repeat: true,\n  restartThreshold: 6000,\n  volume: 1,\n  onMediaChange: useCallback\u003cMediaChangeHandler\u003cT\u003e\u003e((media) =\u003e {}, []),\n  onPlaybackError: useCallback\u003cPlaybackErrorHandler\u003e((error) =\u003e {}, []),\n});\n```\n\n- See [Defining the queue](#defining-the-queue) for more info.\n\n\u003c/details\u003e\n\n---\n\n##### `useAudioPlayerStore`\n\nAccess [`useAudioPlayer`](#useaudioplayer) API exposed by [`\u003cAudioPlayerProvider /\u003e`](#audioplayerprovider-) Component.\n\n---\n\n##### `useVideoPlayer`\n\nEasily handle React video players.\n\nThis hook acts as a wrapper around [`useMediaPlayer`](#usemediaplayer) and it automatically creates a `React.RefObject` that\nneeds to be attached to a `\u003cvideo /\u003e` JSX node.\n\nPlease refer to [`useMediaPlayer`](#usemediaplayer) doc section for API reference.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```tsx\n\"use client\";\n\nimport { useVideoPlayer } from \"@alessiofrittoli/react-media-player\";\nimport type {\n  MediaChangeHandler,\n  PlaybackErrorHandler,\n} from \"@alessiofrittoli/react-media-player\";\n\nexport const VideoPlayer: React.FC = () =\u003e {\n  const { videoRef } = useVideoPlayer({\n    queue,\n    initialMedia: queue.items.at(2),\n    normalizeVolume: true,\n    playPauseFadeDuration: 500,\n    preload: true,\n    repeat: true,\n    restartThreshold: 6000,\n    volume: 1,\n    onMediaChange: useCallback\u003cMediaChangeHandler\u003cT\u003e\u003e((media) =\u003e {}, []),\n    onPlaybackError: useCallback\u003cPlaybackErrorHandler\u003e((error) =\u003e {}, []),\n  });\n\n  return \u003cvideo ref={videoRef} /\u003e;\n};\n```\n\n- See [Defining the queue](#defining-the-queue) for more info.\n\n\u003c/details\u003e\n\n---\n\n##### `useVideoPlayerStore`\n\nAccess [`useVideoPlayer`](#usevideoplayer) API exposed by [`\u003cVideoPlayerProvider /\u003e`](#videoplayerprovider-) Component.\n\n---\n\n##### `useMediaPlayer`\n\nEasily handle React media players.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Type                      | Description            |\n| --------- | ------------------------- | ---------------------- |\n| `T`       | `T extends Queue = Queue` | The type of the queue. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter         | Type                       | Default | Description                                                                                |\n| ----------------- | -------------------------- | ------- | ------------------------------------------------------------------------------------------ |\n| `options`         | `UseMediaPlayerOptions\u003cT\u003e` | -       | An object defining media player options.                                                   |\n|                   |                            |         | - extends [`UseVolumeOptions`](#usevolumeoptions) interface.                               |\n|                   |                            |         | - extends [`UseMediaPlayerControllerOptions`](#usemediaplayercontrolleroptions) interface. |\n| `options.preload` | `boolean`                  | `true`  | Indicates whether to preload next media when current media is about to end.                |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `UseMediaPlayer\u003cT\u003e`\n\nAn object defining media player state and utilities.\n\n- extends [`UseVolume`](#usevolume-interface) interface.\n- extends [`UseMediaPlayerController\u003cT\u003e`](#usemediaplayercontroller-interface) interface.\n- extends [`UseMediaPreload`](#usemediapreload-interface) interface.\n- extends [`UseMediaPlayerLoading`](#usemediaplayerloading-interface) interface.\n\n| Property | Type               | Description                   |\n| -------- | ------------------ | ----------------------------- |\n| `media`  | `HTMLMediaElement` | The given `HTMLMediaElement`. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { useRef } from \"react\";\nimport { useMediaPlayer } from \"@alessiofrittoli/react-media-player\";\nimport type {\n  MediaChangeHandler,\n  PlaybackErrorHandler,\n} from \"@alessiofrittoli/react-media-player\";\n\nconst media = useRef(typeof window !== \"undefined\" ? new Audio() : undefined);\n\nuseMediaPlayer({\n  media: media.current,\n  queue,\n  initialMedia: queue.items.at(2),\n  normalizeVolume: true,\n  playPauseFadeDuration: 500,\n  preload: true,\n  repeat: true,\n  restartThreshold: 6000,\n  volume: 1,\n  onMediaChange: useCallback\u003cMediaChangeHandler\u003cT\u003e\u003e((media) =\u003e {}, []),\n  onPlaybackError: useCallback\u003cPlaybackErrorHandler\u003e((error) =\u003e {}, []),\n});\n```\n\n- See [Defining the queue](#defining-the-queue) for more info.\n\n\u003c/details\u003e\n\n---\n\n##### `useVolume`\n\nManage audio volume control.\n\nPlease note that this hook doesn't update states to avoid useless overloads. This hook only handles `media` volume updates and relative normalizations.\n\nUI state updates can be managed using [`useVolumeStore`](#usevolumestore) accessible inside [`\u003cVolumeProvider /\u003e`](#volumeprovider-) Component children.\n\n###### `UseVolumeOptions`\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter                 | Type               | Default | Description                                             |\n| ------------------------- | ------------------ | ------- | ------------------------------------------------------- |\n| `options`                 | `UseVolumeOptions` | -       | Configuration options for the volume hook.              |\n| `options.media`           | `HTMLMediaElement` | -       | The `HTMLMediaElement`.                                 |\n| `options.volume`          | `number`           | `1`     | The master volume [0-1].                                |\n| `options.normalizeVolume` | `boolean`          | `true`  | Normalize master volume.                                |\n| `options.fade`            | `number`           | `200`   | Volume fade in milliseconds applied when toggling mute. |\n\n\u003c/details\u003e\n\n---\n\n###### `UseVolume` interface\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `UseVolume`\n\nAn object providing volume control functionality including volume management, mute toggling, and volume normalization for media players.\n\n| Property          | Type                      | Description                                                                             |\n| ----------------- | ------------------------- | --------------------------------------------------------------------------------------- |\n| `volumeRef`       | `React.RefObject\u003cnumber\u003e` | A React RefObject that stores the master volume value [0-1].                            |\n|                   |                           | This value may stores the normalized value if `normalizeVolume` has been set to `true`. |\n| `initialVolume`   | `number`                  | The initial master volume [0-1].                                                        |\n| `normalizeVolume` | `boolean`                 | Indicates whether volume normalization is applied.                                      |\n| `setVolume`       | `ChangeHandler`           | Set volume.                                                                             |\n| `toggleMute`      | `ToggleMuteHandler`       | Toggle mute.                                                                            |\n|                   |                           | Returns `0` if muting, otherwise the volume value before the mute was activated.        |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```tsx\nimport { useVolume } from \"@alessiofrittoli/react-media-player\";\n\nconst { setVolume, toggleMute, volumeRef } = useVolume({\n  media: HTMLAudioElement | HTMLVideoElement,\n  volume: 0.8,\n  normalizeVolume: true,\n  fade: 300,\n});\n```\n\n\u003c/details\u003e\n\n---\n\n##### `useVolumeStore`\n\nAccess [`useVolume`](#usevolume) API exposed by [`\u003cVolumeProvider /\u003e`](#volumeprovider-) Component.\n\n---\n\n##### `useMediaPlayerController`\n\nReact media player controller state.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Type                      | Description            |\n| --------- | ------------------------- | ---------------------- |\n| `T`       | `T extends Queue = Queue` | The type of the queue. |\n\n\u003c/details\u003e\n\n---\n\n###### `UseMediaPlayerControllerOptions`\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter                       | Type                                 | Default | Description                                                                                  |\n| ------------------------------- | ------------------------------------ | ------- | -------------------------------------------------------------------------------------------- |\n| `options`                       | `UseMediaPlayerControllerOptions\u003cT\u003e` | -       | An object defining media player controller options.                                          |\n| `options.volumeRef`             | `React.RefObject\u003cnumber\u003e`            | -       | A React RefObject that stores the master volume value [0-1].                                 |\n|                                 |                                      | -       | Compatible with `volumeRef` returned by [`useVolume`](#usevolume) hook.                      |\n| `options.repeat`                | `boolean`                            | `true`  | Indicates whether repeatition of the given queue is initially active.                        |\n| `options.media`                 | `HTMLMediaElement`                   | -       | The `HTMLMediaElement`.                                                                      |\n| `options.queue`                 | `T`                                  | -       | An object describing the queue. See [Defining the queue](#defining-the-queue) for more info. |\n| `options.initialMedia`          | `InitialMedia\u003cQueuedItemType\u003cT\u003e\u003e`    | -       | Defines the initial queue media to load.                                                     |\n| `options.restartThreshold`      | `number\\|false`                      | `5000`  | Indicates time in milliseconds after that the media restart to `0`                           |\n|                                 |                                      |         | rather than playing the previous one.                                                        |\n|                                 |                                      |         | This only take effect when `previous()` method is called.                                    |\n|                                 |                                      |         | You can opt-out by this functionality by setting `restartThreshold` to `false` or `0`.       |\n| `options.playPauseFadeDuration` | `number`                             | `200`   | Volume fade in milliseconds applied when soundtrack start playing/get paused.                |\n| `options.onMediaChange`         | `MediaChangeHandler\u003cT\u003e`              | -       | A callback executed when media player is playing and transitioning to another media.         |\n| `options.onPlaybackError`       | `PlaybackErrorHandler`               | -       | A callback executed when an error occurs when playing a media.                               |\n\n\u003c/details\u003e\n\n---\n\n###### `UseMediaPlayerController` interface\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `UseMediaPlayerController\u003cT\u003e`\n\nAn object defining media player state and utilities.\n\n- extends and exposes [`useQueue`](https://npmjs.com/package/@alessiofrittoli/react-hooks#usequeue) APIs.\n  it may be worthy to take a look at undocumented returned properties in the [`useQueue`](https://npmjs.com/package/@alessiofrittoli/react-hooks#usequeue) documentation.\n\n| Properties        | Type                         | Description                                                                          |\n| ----------------- | ---------------------------- | ------------------------------------------------------------------------------------ |\n| `state`           | `PlayerState`                | Defines the current media player state.                                              |\n|                   |                              | It could be one of the following:                                                    |\n|                   |                              | - `playing` \\| The media player is currently playing.                                |\n|                   |                              | - `paused` \\| The media player is currently paused.                                  |\n|                   |                              | - `stopped` \\| The media player hasn't been started yet or has been stopped.         |\n| `isPlaying`       | `boolean`                    | Defines whether the media player is currently playing.                               |\n| `playPause`       | `PlayPauseHandler\u003cT\u003e`        | Play/pause/stop the media player or start another media.                             |\n|                   |                              | - Returns: The queued item being played.                                             |\n| `togglePlayPause` | `UtilityPlayPauseHandler\u003cT\u003e` | Toggle play/pause.                                                                   |\n|                   |                              | - Returns: The queued item being played/paused.                                      |\n| `stop`            | `UtilityPlayPauseHandler\u003cT\u003e` | Stop media player.                                                                   |\n|                   |                              | - Returns: The queued item that was playing before stopping the media player if any. |\n| `previous`        | `UtilityPlayPauseHandler\u003cT\u003e` | Play previous queued media.                                                          |\n|                   |                              | - Returns: The queued item being played if any.                                      |\n| `next`            | `UtilityPlayPauseHandler\u003cT\u003e` | Play next queued media.                                                              |\n|                   |                              | - Returns: The queued item being played if any.                                      |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Toggle play/pause\n\n```tsx\n\"use client\";\n\nimport { useRef } from \"react\";\nimport { useMediaPlayerController } from \"@alessiofrittoli/react-media-player\";\n\nexport const MyComponent: React.FC = () =\u003e {\n  const media = useRef(typeof window !== \"undefined\" ? new Audio() : undefined);\n  const { isPlaying, togglePlayPause } = useMediaPlayerController({\n    queue,\n    media,\n  });\n\n  return (\n    \u003cbutton onClick={togglePlayPause}\u003e{!isPlaying ? \"Play\" : \"Pause\"}\u003c/button\u003e\n  );\n};\n```\n\n---\n\n###### Play previous media\n\n```tsx\n\"use client\";\n\nimport { useRef } from \"react\";\nimport { useMediaPlayerController } from \"@alessiofrittoli/react-media-player\";\n\nexport const MyComponent: React.FC = () =\u003e {\n  const media = useRef(typeof window !== \"undefined\" ? new Audio() : undefined);\n  const { hasPrevious, previous } = useMediaPlayerController({\n    queue,\n    media,\n    repeat: false,\n  });\n\n  return hasPrevious \u0026\u0026 \u003cbutton onClick={previous}\u003ePrevious song\u003c/button\u003e;\n};\n```\n\n---\n\n###### Play next media\n\n```tsx\n\"use client\";\n\nimport { useRef } from \"react\";\nimport { useMediaPlayerController } from \"@alessiofrittoli/react-media-player\";\n\nexport const MyComponent: React.FC = () =\u003e {\n  const media = useRef(typeof window !== \"undefined\" ? new Audio() : undefined);\n  const { hasNext, next } = useMediaPlayerController({\n    queue,\n    media,\n    repeat: false,\n  });\n\n  return hasNext \u0026\u0026 \u003cbutton onClick={next}\u003eNext song\u003c/button\u003e;\n};\n```\n\n---\n\n###### Play a queued media matching given UUID\n\n```tsx\n\"use client\";\n\nimport { useRef } from \"react\";\nimport { useMediaPlayerController } from \"@alessiofrittoli/react-media-player\";\n\nexport const MyComponent: React.FC = () =\u003e {\n  const media = useRef(typeof window !== \"undefined\" ? new Audio() : undefined);\n  const { playPause } = useMediaPlayerController({ queue, media });\n\n  return (\n    \u003cbutton\n      onClick={() =\u003e {\n        playPause({ uuid: queue.items.at(1)?.uuid });\n      }}\n    \u003e\n      Play {queue.items.at(1)?.title}\n    \u003c/button\u003e\n  );\n};\n```\n\n---\n\n###### Update queue and play a queued media matching given UUID\n\n```tsx\n\"use client\";\n\nimport { useRef } from \"react\";\nimport { useMediaPlayerController } from \"@alessiofrittoli/react-media-player\";\n\nexport const MyComponent: React.FC = () =\u003e {\n  const media = useRef(typeof window !== \"undefined\" ? new Audio() : undefined);\n  const { playPause } = useMediaPlayerController({ queue, media });\n\n  return (\n    \u003cbutton\n      onClick={() =\u003e {\n        playPause({ queue: queue2, uuid: queue2.items.at(1)?.uuid });\n      }}\n    \u003e\n      Play {queue2.items.at(1)?.title}\n    \u003c/button\u003e\n  );\n};\n```\n\n\u003c/details\u003e\n\n---\n\n##### `useMediaPlayerLoading`\n\nHandle media loading and error states.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter       | Type                           | Description                                      |\n| --------------- | ------------------------------ | ------------------------------------------------ |\n| `options`       | `UseMediaPlayerLoadingOptions` | An object defining media player loading options. |\n| `options.media` | `HTMLMediaElement`             | The `HTMLMediaElement`.                          |\n\n\u003c/details\u003e\n\n---\n\n###### `UseMediaPlayerLoading` interface\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `UseMediaPlayerLoading`\n\nAn object defining loading and error states.\n\n| Parameter   | Type         | Description                                                                                                                                                                                 |\n| ----------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `isLoading` | `boolean`    | Indicates whether the current media is loading.                                                                                                                                             |\n| `error`     | `MediaError` | The `MediaError` interface represents an error which occurred while handling                                                                                                                |\n|             |              | media in an HTML media element based on [`HTMLMediaElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement),                                                            |\n|             |              | such as [`\u003caudio\u003e`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/audio) or [`\u003cvideo\u003e`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/video). |\n|             |              | - see [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/MediaError).                                                                                                         |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```tsx\n\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { useMediaPlayerLoading } from \"@alessiofrittoli/react-media-player\";\n\nexport const MyComponent: React.FC = () =\u003e {\n  const [media, setMedia] = useState\u003cHTMLAudioElement\u003e();\n\n  const { isLoading, error } = useMediaPlayerLoading({ media });\n\n  useEffect(() =\u003e {\n    setMedia(new Audio(\"/song.mp3\"));\n  }, []);\n\n  return (\n    \u003c\u003e\n      {isLoading \u0026\u0026 \u003cspan\u003eLoading...\u003c/span\u003e}\n      {!isLoading \u0026\u0026 !error \u0026\u0026 \u003cspan\u003eLoaded\u003c/span\u003e}\n      {error?.code === MediaError?.MEDIA_ERR_ABORTED \u0026\u0026 (\n        \u003cspan\u003e\n          The fetching of the associated resource was aborted by the user's\n          request.\n        \u003c/span\u003e\n      )}\n      {error?.code === MediaError?.MEDIA_ERR_NETWORK \u0026\u0026 (\n        \u003cspan\u003e\n          Some kind of network error occurred which prevented the media from\n          being successfully fetched, despite having previously been available.\n        \u003c/span\u003e\n      )}\n      {error?.code === MediaError?.MEDIA_ERR_DECODE \u0026\u0026 (\n        \u003cspan\u003e\n          Despite having previously been determined to be usable, an error\n          occurred while trying to decode the media resource, resulting in an\n          error.\n        \u003c/span\u003e\n      )}\n      {error?.code === MediaError?.MEDIA_ERR_SRC_NOT_SUPPORTED \u0026\u0026 (\n        \u003cspan\u003e\n          The associated resource or media provider object (such as a\n          MediaStream) has been found to be unsuitable.\n        \u003c/span\u003e\n      )}\n    \u003c/\u003e\n  );\n};\n```\n\n\u003c/details\u003e\n\n---\n\n##### `useMediaPreload`\n\nHandle media preload.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Type                      | Description            |\n| --------- | ------------------------- | ---------------------- |\n| `T`       | `T extends Queue = Queue` | The type of the queue. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter                 | Type                          | Default | Description                                                          |\n| ------------------------- | ----------------------------- | ------- | -------------------------------------------------------------------- |\n| `options`                 | `UseVolumeOptions`            | -       | Configuration options for the volume hook.                           |\n| `options.controller`      | `UseMediaPlayerController\u003cT\u003e` | -       | The media player controller.                                         |\n| `options.cacheEntries`    | `number`                      | `3`     | Defines the maximum cache entries.                                   |\n| `options.checkConnection` | `boolean`                     | `true`  | Defines whether preload is enabled based on user connection quality. |\n\n\u003c/details\u003e\n\n---\n\n###### `UseMediaPreload` interface\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `UseMediaPreload`\n\nAn object containing preload functions.\n\n| Property               | Type                           | Description             |\n| ---------------------- | ------------------------------ | ----------------------- |\n| `preloadMedia`         | `PreloadMediaHandler`          | Preload media.          |\n| `preloadPreviousMedia` | `PreloadPreviousOrNextHandler` | Preload previous media. |\n| `preloadNextMedia`     | `PreloadPreviousOrNextHandler` | Preload next media.     |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Basic usage\n\n```tsx\n\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport { addItemsUUID } from \"@alessiofrittoli/react-media-player/utils\";\nimport {\n  useMediaPlayerController,\n  useMediaPreload,\n  type Media,\n  type Queue,\n} from \"@alessiofrittoli/react-media-player\";\n\ninterface Playlist extends Queue\u003cMedia\u003e {\n  name?: string;\n}\n\nconst queue: Playlist = {\n  name: \"Playlist name\",\n  items: addItemsUUID\u003cMedia\u003e([\n    {\n      src: \"/song-1.mp3\",\n      type: \"audio\",\n    },\n    {\n      src: \"/song-2.mp3\",\n      type: \"audio\",\n    },\n  ]),\n};\n\nexport const MyComponent: React.FC = () =\u003e {\n  const [media, setMedia] = useState\u003cHTMLAudioElement\u003e();\n\n  const controller = useMediaPlayerController({ queue, media });\n  const { preloadPreviousMedia, preloadNextMedia } = useMediaPreload({\n    controller,\n  });\n  const { isPlaying, hasPrevious, hasNext, previous, next, togglePlayPause } =\n    controller;\n\n  useEffect(() =\u003e {\n    setMedia(new Audio());\n  }, []);\n\n  return (\n    \u003c\u003e\n      \u003cbutton\n        onMouseEnter={() =\u003e {\n          if (!hasPrevious) return;\n          preloadPreviousMedia();\n        }}\n        onClick={() =\u003e {\n          if (!hasPrevious) return;\n          previous();\n        }}\n      \u003e\n        Previous\n      \u003c/button\u003e\n      \u003cbutton onClick={togglePlayPause}\u003e{!isPlaying ? \"Play\" : \"Pause\"}\u003c/button\u003e\n      \u003cbutton\n        onMouseEnter={() =\u003e {\n          if (!hasNext) return;\n          preloadNextMedia();\n        }}\n        onClick={() =\u003e {\n          if (!hasNext) return;\n          next();\n        }}\n      \u003e\n        Next\n      \u003c/button\u003e\n    \u003c/\u003e\n  );\n};\n```\n\n---\n\n###### Override `checkConnection` option\n\nReturns 'metadata' for slow-2g or 2g connections\n\n```ts\nimport { useMediaPreload } from \"@alessiofrittoli/react-media-player\";\n\nconst { preloadNextMedia } = useMediaPreload({\n  controller,\n  checkConnection = true, // preload strategy may use `metadata` if connection is `slow-2g` or `2g`\n});\n\npreloadNextMedia(false); // preload strategy will be `auto` ignoring previously passed `checkConnection` option\n```\n\n\u003c/details\u003e\n\n---\n\n##### `useMediaSession`\n\nHook into MediaSession API for controlling media playback through system controls.\n\nManages MediaSession state and action handlers for play, pause, stop, seek, previous, and next operations.\nSynchronizes the native media element's playback state with the MediaSession API and handles user interactions\nthrough system media controls (e.g., keyboard shortcuts, media control buttons).\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter                | Type                        | Description                                                                                                |\n| ------------------------ | --------------------------- | ---------------------------------------------------------------------------------------------------------- |\n| `options`                | `UseMediaSessionOptions`    | An object defining options and callbacks.                                                                  |\n| `options.media`          | `HTMLMediaElement`          | The `HTMLMediaElement`.                                                                                    |\n| `options.register`       | `boolean`                   | Indicates whether to register the action handlers.                                                         |\n|                          |                             | ⚠️ It is better to set `register` to `true` once and only after `media.play()` has been called.            |\n| `options.onPlay`         | `MediaSessionActionHandler` | A custom callback executed once user requested to play the media through browser/device controls.          |\n| `options.onPause`        | `MediaSessionActionHandler` | A custom callback executed once user requested to pause the media through browser/device controls.         |\n| `options.onStop`         | `MediaSessionActionHandler` | A custom callback executed once user requested to stop the media through browser/device controls.          |\n|                          |                             | ⚠️ Stop requests always depend on browser support.                                                         |\n| `options.onPrev`         | `MediaSessionActionHandler` | A custom callback executed once user requested to play the previous media through browser/device controls. |\n|                          |                             | ⚠️ Please note that if no `onPrev` function is given, the MediaSession functionality will not be enabled.  |\n| `options.onNext`         | `MediaSessionActionHandler` | A custom callback executed once user requested to play the next media through browser/device controls.     |\n|                          |                             | ⚠️ Please note that if no `onNext` function is given, the MediaSession functionality will not be enabled.  |\n| `options.onSeekBackward` | `MediaSessionActionHandler` | A custom callback executed once user requested to seek backward through browser/device controls.           |\n| `options.onSeekForward`  | `MediaSessionActionHandler` | A custom callback executed once user requested to seek forward through browser/device controls.            |\n| `options.onSeekTo`       | `MediaSessionActionHandler` | A custom callback executed once user requested to seek to a specific time through browser/device controls. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport {\n  PlayerState,\n  useMediaSession,\n  useMediaPlayerController,\n} from \"@alessiofrittoli/react-media-player\";\n\nconst { state, hasNext, hasPrevious, togglePlayPause, stop, previous, next } =\n  useMediaPlayerController({ queue, media });\n\nuseMediaSession({\n  media,\n  register: state !== PlayerState.STOPPED,\n  onPlay: togglePlayPause,\n  onPause: togglePlayPause,\n  onStop: stop,\n  onPrev: hasPrevious ? previous : undefined,\n  onNext: hasNext ? next : undefined,\n});\n```\n\n\u003c/details\u003e\n\n---\n\n##### `useMediaSessionPiP`\n\nHook into MediaSession Picture-in-Picture requests.\n\n_Useful resources_\n\n- [Document Picture-in-Picture API](https://npmjs.com/package/@alessiofrittoli/web-utils#document-picture-in-picture)\n- [Media Artwork Picture-in-Picture API](https://www.npmjs.com/package/@alessiofrittoli/media-utils#openartworkpictureinpicture)\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter            | Type                        | Description                                                      |\n| -------------------- | --------------------------- | ---------------------------------------------------------------- |\n| `options`            | `UseMediaSessionPiPOptions` | An object defining options and callbacks.                        |\n| `options.register`   | `boolean`                   | Indicates whether to register the action handler.                |\n|                      |                             | ⚠️ Enter PiP requests always depends on browser support.         |\n| `options.onEnterPiP` | `MediaSessionActionHandler` | A custom callback executed once the user requested to enter PiP. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```tsx\n\"use client\";\n\nimport { useCallback, useState, type ReactPortal } from \"react\";\nimport {\n  PlayerState,\n  useMediaSessionPiP,\n  useMediaPlayerController,\n} from \"@alessiofrittoli/react-media-player\";\nimport {\n  openDocumentPictureInPicture,\n  isDocumentPictureInPictureSupported,\n} from \"@alessiofrittoli/web-utils\";\nimport { openArtworkPictureInPicture } from \"@alessiofrittoli/media-utils/picture-in-picture\";\n\nexport const MyComponent: React.FC = () =\u003e {\n  const [portal, setPortal] = useState\u003cReactPortal\u003e();\n  const { state } = useMediaPlayerController({ queue, media });\n\n  const open = useCallback(async () =\u003e {\n    if (isDocumentPictureInPictureSupported()) {\n      const { window } = await openDocumentPictureInPicture();\n\n      const reactNode = (\n        \u003cPictureInPictureWindowProvider window={window}\u003e\n          \u003cPictureInPictureComponent /\u003e\n        \u003c/PictureInPictureWindowProvider\u003e\n      );\n\n      const portal = createPortal(reactNode, window.document.body);\n\n      return;\n    }\n\n    await openArtworkPictureInPicture( ... );\n  }, []);\n\n  useMediaSessionPiP({\n    register: state !== PlayerState.STOPPED,\n    onEnterPiP: open,\n  });\n\n  return portal;\n};\n```\n\n\u003c/details\u003e\n\n---\n\n#### React Components\n\n##### `\u003cAudioPlayer /\u003e`\n\nCreates a React Audio Player and exposes [`useAudioPlayer`](#useaudioplayer) API through React Context\nwith [`\u003cAudioPlayerProvider /\u003e`](#audioplayerprovider-) and [`\u003cVolumeProvider /\u003e`](#volumeprovider-).\n\nThis allows you to easily mix-up client and server components passed to the Component children.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eComponent Props\u003c/summary\u003e\n\n- extends [`useAudioPlayer`](#useaudioplayer) options\n\n| Property   | Type        | Description                                                                                                                                                                                                       |\n| ---------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `children` | `ReactNode` | Any `ReactNode` which will get access to [`useAudioPlayer`](#useaudioplayer) API and volume UI states through [`useAudioPlayerStore`](#useaudioplayerstore) and [`useVolumeStore`](#usevolumestore) respectively. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Basic usage\n\n```tsx\nimport { AudioPlayer } from \"@alessiofrittoli/react-media-player\";\n\nexport const AppAudioPlayer: React.FC = () =\u003e (\n  \u003cAudioPlayer queue={queue}\u003e\n    \u003cAudioPlayerControls /\u003e\n  \u003c/AudioPlayer\u003e\n);\n```\n\n---\n\n###### Accessing APIs in custom UI controls\n\n```tsx\nimport { useCallback } from \"react\";\nimport { useUpdateEffect } from \"@alessiofrittoli/react-hooks\";\nimport { useAudioPlayerStore } from \"@alessiofrittoli/react-media-player\";\n\nexport const AudioPlayerControls: React.FC = () =\u003e {\n  const { isPlaying, togglePlayPause } = useAudioPlayerStore();\n\n  return (\n    \u003c\u003e\n      \u003cbutton onClick={togglePlayPause}\u003e{!isPlaying ? \"Play\" : \"Pause\"}\u003c/button\u003e\n    \u003c/\u003e\n  );\n};\n```\n\n\u003c/details\u003e\n\n---\n\n##### `\u003cVideoPlayer /\u003e`\n\nCreates a React Video Player and exposes [`useVideoPlayer`](#usevideoplayer) API through React Context\nwith [`\u003cVideoPlayerProvider /\u003e`](#videoplayerprovider-) and [`\u003cVolumeProvider /\u003e`](#volumeprovider-).\n\nThis allows you to easily mix-up client and server components passed to the Component children.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eComponent Props\u003c/summary\u003e\n\n- extends [`useVideoPlayer`](#usevideoplayer) options\n\n| Property    | Type                            | Description                                                                                                                                                                                                       |\n| ----------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `children`  | `ReactNode`                     | Any `ReactNode` which will get access to [`useVideoPlayer`](#usevideoplayer) API and volume UI states through [`useVideoPlayerStore`](#usevideoplayerstore) and [`useVolumeStore`](#usevolumestore) respectively. |\n| `htmlProps` | `React.ComponentProps\u003c'video'\u003e` | Props passed to the rendered `HTMLVideoElement`.                                                                                                                                                                  |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n###### Basic usage\n\n```tsx\nimport { VideoPlayer } from \"@alessiofrittoli/react-media-player\";\n\nexport const AppVideoPlayer: React.FC = () =\u003e (\n  \u003cVideoPlayer queue={queue}\u003e\n    \u003cVideoPlayerControls /\u003e\n  \u003c/VideoPlayer\u003e\n);\n```\n\n---\n\n###### Accessing APIs in custom UI controls\n\n```tsx\n\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useUpdateEffect } from \"@alessiofrittoli/react-hooks\";\nimport { useVideoPlayerStore } from \"@alessiofrittoli/react-media-player\";\n\nexport const VideoPlayerControls: React.FC = () =\u003e {\n  const { isPlaying, togglePlayPause } = useVideoPlayerStore();\n\n  return (\n    \u003c\u003e\n      \u003cbutton onClick={togglePlayPause}\u003e{!isPlaying ? \"Play\" : \"Pause\"}\u003c/button\u003e\n    \u003c/\u003e\n  );\n};\n```\n\n\u003c/details\u003e\n\n---\n\n##### `\u003cAudioPlayerProvider /\u003e`\n\nExposes [`useAudioPlayer`](#useaudioplayer) API.\n\n---\n\n##### `\u003cVideoPlayerProvider /\u003e`\n\nExposes [`useVideoPlayer`](#usevideoplayer) API.\n\n---\n\n##### `\u003cVolumeProvider /\u003e`\n\nExposes UI state updates utilities.\n\nThis may come pretty handy when volume is controlled by multiple UI controllers and saves [`useVolume`](#usevolume) hook\nfrom dispatching state updates whenever a 0.1 volume value has been changed by the user.\n\nThis Component is already mounted when using the [`\u003cAudioPlayer /\u003e`](#audioplayer-) or [`\u003cVideoPlayer /\u003e`](#videoplayer-) Component,\nso no extra action is required by you.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```tsx\n\"use client\";\n\nimport { useCallback } from \"react\";\nimport { useUpdateEffect } from \"@alessiofrittoli/react-hooks\";\nimport {\n  AudioPlayer,\n  useVolumeStore,\n  useAudioPlayerStore,\n} from \"@alessiofrittoli/react-media-player\";\n\nexport const AppAudioPlayer: React.FC = () =\u003e (\n  \u003cAudioPlayer queue={queue}\u003e\n    \u003cAudioPlayerVolumeControl /\u003e\n  \u003c/AudioPlayer\u003e\n);\n\nexport const AudioPlayerVolumeControl: React.FC = () =\u003e {\n  const { volume, setVolume: commitVolume } = useVolumeStore();\n\n  const { initialVolume, setVolume, toggleMute } = useAudioPlayerStore();\n\n  const isMute = volume \u003c= 0;\n\n  const updateVolume = useCallback(\n    (volume: number) =\u003e {\n      setVolume(volume / 100);\n      commitVolume(volume / 100);\n    },\n    [setVolume, commitVolume],\n  );\n\n  const toggleMuteHandler = useCallback(() =\u003e {\n    commitVolume(toggleMute());\n  }, [toggleMute, commitVolume]);\n\n  useUpdateEffect(() =\u003e {\n    updateVolume(initialVolume * 100);\n  }, [initialVolume, updateVolume]);\n\n  const onChangeHandler = useCallback\u003cReact.ChangeEventHandler\u003e(\n    (event) =\u003e {\n      const input = event.target as HTMLInputElement;\n      const value = Number(input.value);\n\n      if (isNaN(value)) return;\n\n      const percent = (value * 100) / 100;\n\n      updateVolume(percent);\n    },\n    [updateVolume],\n  );\n\n  return (\n    \u003c\u003e\n      \u003cbutton onClick={toggleMuteHandler}\u003e{!isMute ? \"Mute\" : \"Unmute\"}\u003c/button\u003e\n      \u003cinput\n        type=\"range\"\n        value={volume * 100}\n        max={100}\n        step={1}\n        onChange={onChangeHandler}\n        aria-valuetext={`${volume * 100}%`}\n      /\u003e\n    \u003c/\u003e\n  );\n};\n```\n\n\u003c/details\u003e\n\n---\n\n#### Utils\n\n##### Queue Utils\n\nThis library exposes queue utility functions exported by [`@alessiofrittoli/react-hooks`](https://npmjs.com/package/@alessiofrittoli/react-hooks)\nand defines others documented below.\n\n- See [Queue Utils](https://npmjs.com/package/@alessiofrittoli/react-hooks#queue-utils).\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport {\n  addItemUUID,\n  addItemsUUID,\n  maybeAddItemUUID,\n  maybeAddItemsUUID,\n  findIndexByUUID,\n} from \"@alessiofrittoli/react-media-player/utils\";\n\n...\n```\n\n\u003c/details\u003e\n\n---\n\n##### `inheritDataFromQueue`\n\nInherit metadata fields from a queue into a queued item payload.\n\nPlease note that the given item fields takes precedence over queue fields.\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eInherited properties from the given queue\u003c/summary\u003e\n\n| Property       | Description                  |\n| -------------- | ---------------------------- |\n| `album`        | The album name of the media. |\n| `artist`       | The artist of the media.     |\n| `artwork`      | The media artwork.           |\n| `videoArtwork` | The media video artwork.     |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eType parameters\u003c/summary\u003e\n\n| Parameter | Type                      | Description            |\n| --------- | ------------------------- | ---------------------- |\n| `T`       | `T extends Queue = Queue` | The type of the queue. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eParameters\u003c/summary\u003e\n\n| Parameter | Type                | Default | Description                                    |\n| --------- | ------------------- | ------- | ---------------------------------------------- |\n| `item`    | `QueuedItemType\u003cT\u003e` | -       | The queued item.                               |\n| `queue`   | `T\\| NewQueue\u003cT\u003e`   | -       | The queue from which the fields are inherited. |\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eReturns\u003c/summary\u003e\n\nType: `QueuedItemType\u003cT\u003e`\n\nThe queued item with intherited fileds from the given `queue`.\n\n\u003c/details\u003e\n\n---\n\n\u003cdetails\u003e\n\n\u003csummary style=\"cursor:pointer\"\u003eUsage\u003c/summary\u003e\n\n```ts\nimport { inheritDataFromQueue } from \"@alessiofrittoli/react-media-player/utils\";\n\nconst item = {\n  uuid: \"random-uuid\",\n  title: \"Track 1\",\n} as unknown as QueuedItemType\u003cQueue\u003e;\n\nconst queue = {\n  album: \"Album A\",\n  artist: \"Artist A\",\n  artwork: \"artwork-a.jpg\",\n  videoArtwork: \"video-a.jpg\",\n} as unknown as Queue;\n\ninheritDataFromQueue(item, queue);\n\n/*\nReturns:\n  {\n    album         : 'Album A',\n    artist        : 'Artist A',\n    artwork       : 'artwork-a.jpg',\n    videoArtwork  : 'video-a.jpg',\n    uuid          : 'random-uuid',\n    title         : 'Track 1',\n  }\n*/\n```\n\n\u003c/details\u003e\n\n---\n\n### Development\n\n#### Install dependencies\n\n```bash\nnpm install\n```\n\nor using `pnpm`\n\n```bash\npnpm i\n```\n\n#### Build the source code\n\nRun the following command to test and build code for distribution.\n\n```bash\npnpm build\n```\n\n#### [ESLint](https://www.npmjs.com/package/eslint)\n\nRun warnings and errors checks.\n\n```bash\npnpm lint\n```\n\n#### [Jest](https://npmjs.com/package/jest)\n\nRun all the defined test suites by running the following:\n\n```bash\n# Run tests and watch file changes.\npnpm test:watch\n\n# Run tests in a CI environment.\npnpm test:ci\n```\n\n- See [`package.json`](./package.json) file scripts for more info.\n\nRun tests with coverage.\n\nAn HTTP server is then started to serve coverage files from `./coverage` folder.\n\n⚠️ You may see a blank page the first time you run this command. Simply refresh the browser to see the updates.\n\n```bash\npnpm test:coverage:serve\n```\n\n---\n\n### Contributing\n\nContributions are truly welcome!\n\nPlease refer to the [Contributing Doc](./CONTRIBUTING.md) for more information on how to start contributing to this project.\n\nHelp keep this project up to date with [GitHub Sponsor][sponsor-url].\n\n[![GitHub Sponsor][sponsor-badge]][sponsor-url]\n\n---\n\n### Security\n\nIf you believe you have found a security vulnerability, we encourage you to **_responsibly disclose this and NOT open a public issue_**. We will investigate all legitimate reports. Email `security@alessiofrittoli.it` to disclose any security vulnerabilities.\n\n### Made with ☕\n\n\u003ctable style='display:flex;gap:20px;'\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\n        \u003cimg alt=\"avatar\" src='https://avatars.githubusercontent.com/u/35973186' style='width:60px;border-radius:50%;object-fit:contain;'\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n        \u003ctable style='display:flex;gap:2px;flex-direction:column;'\u003e\n          \u003ctbody\u003e\n              \u003ctr\u003e\n                \u003ctd\u003e\n                  \u003ca href='https://github.com/alessiofrittoli' target='_blank' rel='noopener'\u003eAlessio Frittoli\u003c/a\u003e\n                \u003c/td\u003e\n              \u003c/tr\u003e\n              \u003ctr\u003e\n                \u003ctd\u003e\n                  \u003csmall\u003e\n                    \u003ca href='https://alessiofrittoli.it' target='_blank' rel='noopener'\u003ehttps://alessiofrittoli.it\u003c/a\u003e |\n                    \u003ca href='mailto:info@alessiofrittoli.it' target='_blank' rel='noopener'\u003einfo@alessiofrittoli.it\u003c/a\u003e\n                  \u003c/small\u003e\n                \u003c/td\u003e\n              \u003c/tr\u003e\n          \u003c/tbody\u003e\n        \u003c/table\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falessiofrittoli%2Freact-media-player","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falessiofrittoli%2Freact-media-player","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falessiofrittoli%2Freact-media-player/lists"}