Full Code of cookpete/react-player for AI

master 258fce939638 cached
44 files
198.6 KB
61.3k tokens
48 symbols
1 requests
Download .txt
Showing preview only (210K chars total). Download the full file or copy to clipboard to get everything.
Repository: cookpete/react-player
Branch: master
Commit: 258fce939638
Files: 44
Total size: 198.6 KB

Directory structure:
gitextract_acdd_gc9/

├── .github/
│   ├── FUNDING.yml
│   ├── issue_template.md
│   └── workflows/
│       ├── cd.yml
│       └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── MIGRATING.md
├── README.md
├── biome.json
├── codecov.yml
├── examples/
│   └── react/
│       ├── public/
│       │   ├── App.css
│       │   ├── defaults.css
│       │   ├── index.html
│       │   ├── range.css
│       │   └── reset.css
│       └── src/
│           ├── App.tsx
│           ├── Duration.tsx
│           └── index.tsx
├── package.json
├── scripts/
│   ├── builder/
│   │   ├── builder.js
│   │   └── package.json
│   └── tester/
│       ├── package.json
│       └── tester.js
├── src/
│   ├── HtmlPlayer.tsx
│   ├── Player.tsx
│   ├── Preview.tsx
│   ├── ReactPlayer.tsx
│   ├── index.ts
│   ├── patterns.ts
│   ├── players.ts
│   ├── props.ts
│   └── types.ts
├── test/
│   ├── Player.test.tsx
│   ├── Player.tsx
│   ├── ReactPlayer/
│   │   ├── instanceMethods.js
│   │   ├── props.tsx
│   │   ├── render.js
│   │   └── staticMethods.js
│   ├── helpers/
│   │   ├── helpers.tsx
│   │   └── server-safe-globals.js
│   └── react-player-tests.tsx
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: cookpete
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


================================================
FILE: .github/issue_template.md
================================================
Be sure to [search for your issue](https://github.com/CookPete/react-player/issues) before opening a new one.

#### Current Behavior
<!-- Describe what is happening -->

#### Expected Behavior
<!-- Describe what you are expecting to happen -->

#### Steps to Reproduce
<!-- Provide as much detail as possible -->
1.
1.
1.

#### Environment
- URL attempting to play: 
- Browser: 
- Operating system: 
- jsFiddle example: https://jsfiddle.net/sv5x3ug1

#### Other Information
<!-- Anything else to add -->


================================================
FILE: .github/workflows/cd.yml
================================================
name: CD

concurrency: production

on:
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:
    inputs:
      version:
        type: choice
        required: true
        description: Version
        options:
        - conventional
        - patch
        - minor
        - major
        - prerelease
        - from-package
        - from-git
      prerelease:
        type: choice
        description: Pre-release
        options:
        -
        - canary
        - beta
      dryrun:
        description: 'Dry-run'
        type: boolean

run-name: Deploy ${{ inputs.version }} ${{ inputs.dryrun && '--dry-run' || '' }} ${{ inputs.prerelease && format('--prerelease {0}', inputs.prerelease) || '' }}

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    permissions:
      contents: write
      id-token: write

    env:
      NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
      CONVENTIONAL_GITHUB_RELEASER_TOKEN: ${{ secrets.GITHUB_TOKEN }}

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Fetch all history for all tags and branches
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          # this line is required for the setup-node action to be able to run the npm publish below.
          registry-url: 'https://registry.npmjs.org'
      - uses: fregante/setup-git-user@v1
      - run: npm ci
      - run: npm run lint
      - run: npm run test:coverage
      - run: npm run build
      - run: npm run build:demo
      - run: npx --yes wet-run@1.0.1 release ${{ inputs.version }} ${{ inputs.dryrun && '--dry-run' || '' }} ${{ inputs.prerelease && format('--prerelease {0}', inputs.prerelease) || '' }} --provenance --github-release --verbose
      - name: Get NPM version
        id: npm-version
        uses: martinbeentjes/npm-get-version-action@v1.3.1
      - name: Released ${{ steps.npm-version.outputs.current-version}} ✨
        run: echo ${{ steps.npm-version.outputs.current-version}}


================================================
FILE: .github/workflows/ci.yml
================================================
name: Node.js CI

on: [push, pull_request]

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
  group: "pages"
  cancel-in-progress: false

jobs:
  lint:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x]

    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: npm
    - run: npm ci
    - run: npm run lint

  test:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [18.x]

    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: npm
    - run: npm ci
    - run: npm run test:coverage

  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [16.x, 18.x, 20.x]

    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
        cache: npm
    - run: npm ci
    - run: npm run build
    - run: npm run build:demo

  deploy-preview:
    # Grant GITHUB_TOKEN the permissions required to make a Pages deployment
    permissions:
      pages: write      # to deploy to Pages
      id-token: write   # to verify the deployment originates from an appropriate source

    environment:
      name: github-preview
      url: ${{ steps.deployment.outputs.page_url }}

    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4
      - uses: actions/configure-pages@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npm run build
      - run: npm run build:demo
      - uses: actions/upload-pages-artifact@v3
        with:
          path: './demo'
      - uses: actions/deploy-pages@v4
        id: deployment


================================================
FILE: .gitignore
================================================
node_modules
npm-debug.log
yarn-error.log
.DS_Store
/lib
/lazy
/demo
/coverage
/es6
.idea/
.vscode/
/disttest/
/dist/
tsconfig.tsbuildinfo


================================================
FILE: CHANGELOG.md
================================================
### Changelog

All notable changes to this project will be documented in this file. Dates are displayed in UTC.

Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).

#### [v3.4.0](https://github.com/cookpete/react-player/compare/v3.3.3...v3.4.0)

- fix: Improve accessibility and code quality [`#1985`](https://github.com/cookpete/react-player/pull/1985)
- feat: Add disableRemotePlayback prop [`#2004`](https://github.com/cookpete/react-player/pull/2004)
- fix: upgrade player deps [`a68f29d`](https://github.com/cookpete/react-player/commit/a68f29d7b0d5dbcbb544e989e89c73203451b933)

#### [v3.3.3](https://github.com/cookpete/react-player/compare/v3.3.2...v3.3.3)

> 19 September 2025

- fix: upgrade deps + fix hls config bug [`#1977`](https://github.com/cookpete/react-player/issues/1977)
- chore(release): 3.3.3 [`e09d35f`](https://github.com/cookpete/react-player/commit/e09d35f591551ca0eb7895d5b15dbb489d732cdd)

#### [v3.3.2](https://github.com/cookpete/react-player/compare/v3.3.1...v3.3.2)

> 25 August 2025

- fix: prevent React warnings for unknown event handler properties [`#1970`](https://github.com/cookpete/react-player/pull/1970)
- docs: the demo source in README.md [`#1979`](https://github.com/cookpete/react-player/pull/1979)
- Fix: Add share URLs to TikTok pattern matching [`#1973`](https://github.com/cookpete/react-player/pull/1973)
- MIGRATING.md: Fix broken link to demo app [`#1969`](https://github.com/cookpete/react-player/pull/1969)
- chore(release): 3.3.2 [`a9d43f1`](https://github.com/cookpete/react-player/commit/a9d43f1504ae37f0ee06c44edf913ea56faa638d)

#### [v3.3.1](https://github.com/cookpete/react-player/compare/v3.3.0...v3.3.1)

> 16 July 2025

- fix: merge circular dependency [`#1964`](https://github.com/cookpete/react-player/pull/1964)
- chore(release): 3.3.1 [`26001ec`](https://github.com/cookpete/react-player/commit/26001ec8c32d91cededfcaed026955fc4de2157b)

#### [v3.3.0](https://github.com/cookpete/react-player/compare/v3.2.1...v3.3.0)

> 16 July 2025

- feat: add tiktok support [`#1961`](https://github.com/cookpete/react-player/pull/1961)
- feat: add tiktok support (#1961) [`#1193`](https://github.com/cookpete/react-player/issues/1193)
- chore(release): 3.3.0 [`6c3a830`](https://github.com/cookpete/react-player/commit/6c3a83060ce7c91c42f1619822318a6b7d81ed27)

#### [v3.2.1](https://github.com/cookpete/react-player/compare/v3.2.0...v3.2.1)

> 15 July 2025

- fix: muted prop bug [`#1959`](https://github.com/cookpete/react-player/pull/1959)
- fix: muted prop bug (#1959) [`#1957`](https://github.com/cookpete/react-player/issues/1957)
- chore(release): 3.2.1 [`d72301f`](https://github.com/cookpete/react-player/commit/d72301f15fb77795c1e741fe1a5935dbf2d41be9)

#### [v3.2.0](https://github.com/cookpete/react-player/compare/v3.1.0...v3.2.0)

> 11 July 2025

- feat: add Spotify and Twitch support [`#1956`](https://github.com/cookpete/react-player/pull/1956)
- chore(release): 3.2.0 [`d650eaa`](https://github.com/cookpete/react-player/commit/d650eaac904c29ca84fd825a6b0e72c2fe6bb27e)

#### [v3.1.0](https://github.com/cookpete/react-player/compare/v3.0.0...v3.1.0)

> 2 July 2025

- docs: Add a Youtube playlist example [`#1946`](https://github.com/cookpete/react-player/pull/1946)
- feat: add YT playlist support [`#1947`](https://github.com/cookpete/react-player/pull/1947)
- chore(release): 3.1.0 [`a315b5f`](https://github.com/cookpete/react-player/commit/a315b5fb32f7364ed9af75d65314b15a1ec16133)

### [v3.0.0](https://github.com/cookpete/react-player/compare/v3.0.0-beta.4...v3.0.0)

> 27 June 2025

- docs: add custom player controls section & example [`#1942`](https://github.com/cookpete/react-player/pull/1942)
- chore(release): 3.0.0 [`ddd6303`](https://github.com/cookpete/react-player/commit/ddd6303f5be1dfa722e01e8a35ad932bcf8948ff)

#### [v3.0.0-beta.4](https://github.com/cookpete/react-player/compare/v3.0.0-beta.3...v3.0.0-beta.4)

> 26 June 2025

- fix: upgrade media element dependencies [`#1941`](https://github.com/cookpete/react-player/pull/1941)
- chore(release): 3.0.0-beta.4 [`6fa7049`](https://github.com/cookpete/react-player/commit/6fa70490006df3429d1a36aca42527182cb537aa)

#### [v3.0.0-beta.3](https://github.com/cookpete/react-player/compare/v3.0.0-beta.2...v3.0.0-beta.3)

> 19 May 2025

- fix: prevent infinite recursion in deepmerge by extracting children prop [`#1933`](https://github.com/cookpete/react-player/pull/1933)
- chore(release): 3.0.0-beta.3 [`d2f7578`](https://github.com/cookpete/react-player/commit/d2f75781a88bb8f9aef750f6b1e5860bc229426f)

#### [v3.0.0-beta.2](https://github.com/cookpete/react-player/compare/v3.0.0-beta.1...v3.0.0-beta.2)

> 8 May 2025

- fix: playing prop bug [`#1932`](https://github.com/cookpete/react-player/pull/1932)
- chore(release): 3.0.0-beta.2 [`aba61fa`](https://github.com/cookpete/react-player/commit/aba61fa57638dbcf961d5a77eee026004e5ce0fc)

#### [v3.0.0-beta.1](https://github.com/cookpete/react-player/compare/v3.0.0-beta.0...v3.0.0-beta.1)

> 8 May 2025

- fix: upgrade media elements [`#1929`](https://github.com/cookpete/react-player/pull/1929)
- fix: add `slot` and other media attrs [`#1931`](https://github.com/cookpete/react-player/pull/1931)
- chore(release): 3.0.0-beta.1 [`af90316`](https://github.com/cookpete/react-player/commit/af903164c80114d4072d61c4944cd2c3a3a8d188)
- fix: support React 19 [`a26d253`](https://github.com/cookpete/react-player/commit/a26d2538fd1284321ba2af6ded7d490506b24b8c)
- docs: fix formatting issues [`8545cb3`](https://github.com/cookpete/react-player/commit/8545cb3c85abd1d8e5af1ecb45b06ab2baced9e3)

#### [v3.0.0-beta.0](https://github.com/cookpete/react-player/compare/v2.16.1...v3.0.0-beta.0)

> 8 May 2025

- feat!: v3 refactor  [`#1886`](https://github.com/cookpete/react-player/pull/1886)
- chore: Small readme update [`#1884`](https://github.com/cookpete/react-player/pull/1884)
- chore(release): 3.0.0-beta.0 [`693caed`](https://github.com/cookpete/react-player/commit/693caed3ad1e1b32162bf0cad338bdb48625afd2)
- docs: fix `src` instead of `url` prop [`b0084f7`](https://github.com/cookpete/react-player/commit/b0084f78d6637df642ebf0f471967b59cbd58c8d)

#### [v2.16.1](https://github.com/cookpete/react-player/compare/v2.16.0...v2.16.1)

> 10 July 2025

- chore(release): 2.16.1 [`71efe92`](https://github.com/cookpete/react-player/commit/71efe9297ff8813d1f11790a92ae42e12152d394)
- fix: Mixcloud URL (#1953) [`47a8c49`](https://github.com/cookpete/react-player/commit/47a8c495ad80c0b629ab6254fc27c00ad5874f79)

#### [v2.16.0](https://github.com/cookpete/react-player/compare/v2.15.1...v2.16.0)

> 9 April 2024

- feat: add Mux + hls.js support back in [`#1769`](https://github.com/cookpete/react-player/pull/1769)
- chore(release): 2.16.0 [`795b196`](https://github.com/cookpete/react-player/commit/795b19614977fbe2b89f6fd14503d1bfb121a722)

#### [v2.15.1](https://github.com/cookpete/react-player/compare/v2.15.0...v2.15.1)

> 2 March 2024

- fix: dynamic Mux import [`#1758`](https://github.com/cookpete/react-player/pull/1758)
- docs: fix demo URL [`#1752`](https://github.com/cookpete/react-player/pull/1752)
- docs: readme updates, next-video callout [`#1751`](https://github.com/cookpete/react-player/pull/1751)
- fix: dynamic Mux import (#1758) [`#1755`](https://github.com/cookpete/react-player/issues/1755)
- chore(release): 2.15.1 [`4d7fcb5`](https://github.com/cookpete/react-player/commit/4d7fcb52ac5cc431347e4f4b0e08ac6d0b0825b5)

#### [v2.15.0](https://github.com/cookpete/react-player/compare/v2.14.1...v2.15.0)

> 28 February 2024

- chore(cd): remove --changelog b/c auto-changelog [`#1750`](https://github.com/cookpete/react-player/pull/1750)
- feat: add Mux player [`#1748`](https://github.com/cookpete/react-player/pull/1748)
- chore(github-pages): add demo preview [`#1747`](https://github.com/cookpete/react-player/pull/1747)
- chore: remove unneeded config files [`#1744`](https://github.com/cookpete/react-player/pull/1744)
- Add aria-label to Preview component [`#1705`](https://github.com/cookpete/react-player/pull/1705)
- Add Deno-specific browser check [`#1632`](https://github.com/cookpete/react-player/pull/1632)
- docs: fix badges [`#1735`](https://github.com/cookpete/react-player/pull/1735)
- chore(release): 2.15.0 [`6dfff1c`](https://github.com/cookpete/react-player/commit/6dfff1c8055388c9a0f8e59d58bf4ecd4cfdf013)

#### [v2.14.1](https://github.com/cookpete/react-player/compare/v2.14.0...v2.14.1)

> 19 December 2023

- fix: cjs interop require default pita [`#1722`](https://github.com/cookpete/react-player/pull/1722)
- docs(CHANGELOG): 2.14.1 [`921b0e0`](https://github.com/cookpete/react-player/commit/921b0e01148925f048716435333fcdc9ce2df24a)
- chore(release): 2.14.1 [`8ca9747`](https://github.com/cookpete/react-player/commit/8ca974794db1c7f8cacf4ae1580671548cdf5491)
- chore: npm ignore unneeded folders [`678f466`](https://github.com/cookpete/react-player/commit/678f466553d36dc02dfe2b926a31e8694b746842)

#### [v2.14.0](https://github.com/cookpete/react-player/compare/v2.13.0...v2.14.0)

> 14 December 2023

- fix: repo url [`#1709`](https://github.com/cookpete/react-player/pull/1709)
- fix: pkg repository.url [`#1708`](https://github.com/cookpete/react-player/pull/1708)
- chore: add CD workflow [`#1706`](https://github.com/cookpete/react-player/pull/1706)
- chore: use Node matrix with 16.x, 18.x, 20.x [`#1688`](https://github.com/cookpete/react-player/pull/1688)
- docs: update demo App.js link [`#1689`](https://github.com/cookpete/react-player/pull/1689)
- fix: modernize build using esbuild [`#1684`](https://github.com/cookpete/react-player/pull/1684)
- docs(CHANGELOG): 2.14.0 [`e018c42`](https://github.com/cookpete/react-player/commit/e018c4255dee89a1684f2fff8b1cb4db81ad2cce)
- chore(release): 2.14.0 [`1496f67`](https://github.com/cookpete/react-player/commit/1496f679fcefae26ecb5b1ea77c75b36f401dd90)

#### [v2.13.0](https://github.com/cookpete/react-player/compare/v2.12.0...v2.13.0)

> 5 September 2023

- Fix #1604 - FilePlayer does not work if I passed an array of urls [`#1612`](https://github.com/cookpete/react-player/pull/1612)
- fix: `src` sttribute become "undefinded" if `url` is an array [`#1648`](https://github.com/cookpete/react-player/pull/1648)
- Adding keepPlaying to other player types [`#1639`](https://github.com/cookpete/react-player/pull/1639)
- CI [`#1654`](https://github.com/cookpete/react-player/pull/1654)
- Swap out broken youtube URL [`#1659`](https://github.com/cookpete/react-player/pull/1659)
- Add keepPlaying to seekTo [`#1620`](https://github.com/cookpete/react-player/pull/1620)
- Added forceDisableHls option for FilePlayer [`#1625`](https://github.com/cookpete/react-player/pull/1625)
- added onPlaybackQualityChange prop [`#1636`](https://github.com/cookpete/react-player/pull/1636)
- Update the list of supported YouTube domains [`#1599`](https://github.com/cookpete/react-player/pull/1599)
- Fix #1604 - FilePlayer does not work if I passed an array of urls (#1612) [`#1604`](https://github.com/cookpete/react-player/issues/1604)
- Support Wisita URLs with query params [`#1591`](https://github.com/cookpete/react-player/issues/1591)
- Support vimeo manage links [`#1593`](https://github.com/cookpete/react-player/issues/1593)
- Update readme [`90237f5`](https://github.com/cookpete/react-player/commit/90237f51d43fc63870b0e6d0c86f4497f97ca586)

#### [v2.12.0](https://github.com/cookpete/react-player/compare/v2.11.2...v2.12.0)

> 7 March 2023

- Added Vimeo Muteability [`#1588`](https://github.com/cookpete/react-player/pull/1588)
- Add forceSafariHLS option for FilePlayer [`#1560`](https://github.com/cookpete/react-player/pull/1560)
- fix: standalone has side effects [`#1586`](https://github.com/cookpete/react-player/pull/1586)
- Update DASH example [`#1589`](https://github.com/cookpete/react-player/issues/1589)
- Support live youtube URLs [`#1580`](https://github.com/cookpete/react-player/issues/1580)

#### [v2.11.2](https://github.com/cookpete/react-player/compare/v2.11.1...v2.11.2)

> 10 February 2023

- Remove module property from package.json [`#1574`](https://github.com/cookpete/react-player/issues/1574)

#### [v2.11.1](https://github.com/cookpete/react-player/compare/v2.11.0...v2.11.1)

> 8 February 2023

- Update light prop usage re: new feature in PR#1405 [`#1510`](https://github.com/cookpete/react-player/pull/1510)
- Extending valid DailyMotion URL regex  Fix #1430 [`#1516`](https://github.com/cookpete/react-player/pull/1516)
- Add `"sideEffects": false` to `package.json` [`#1524`](https://github.com/cookpete/react-player/pull/1524)
- responsive CSS fix [`#1533`](https://github.com/cookpete/react-player/pull/1533)
- add unbundled es6 build with jsx file extensions [`#1537`](https://github.com/cookpete/react-player/pull/1537)
- Replace Vimeo default preview thumbnail [`#1553`](https://github.com/cookpete/react-player/pull/1553)
- fix typo [`#1558`](https://github.com/cookpete/react-player/pull/1558)
- fix: empty src attr in StrictMode [`#1538`](https://github.com/cookpete/react-player/pull/1538)
- Extending valid DailyMotion URL regex  Fix #1430 (#1516) [`#1430`](https://github.com/cookpete/react-player/issues/1430)

#### [v2.11.0](https://github.com/cookpete/react-player/compare/v2.10.1...v2.11.0)

> 17 September 2022

- Add event playbackratechange on vimeo player [`#1502`](https://github.com/cookpete/react-player/pull/1502)
- feat: allow auto play on mixcloud player [`#1467`](https://github.com/cookpete/react-player/pull/1467)
- Fix onProgress not firing in Strict Mode [`#1465`](https://github.com/cookpete/react-player/pull/1465)
- Generate a file `dist/ReactPlayer.standalone-module.js` ES6 module [`#1425`](https://github.com/cookpete/react-player/pull/1425)
- ability to pass a component in light prop [`#1405`](https://github.com/cookpete/react-player/pull/1405)
- (fix): clear video src to prevent old video from continue to load [`#1360`](https://github.com/cookpete/react-player/pull/1360)
- apply interface onProgressProps at base.d.ts [`#1342`](https://github.com/cookpete/react-player/pull/1342)
- Do not pass wrapper ref to custom wrappers [`#1476`](https://github.com/cookpete/react-player/issues/1476)
- Update readme [`e955a2c`](https://github.com/cookpete/react-player/commit/e955a2c9ca760196859926d08431cacdf585a214)
- Update browserslist [`dc68dba`](https://github.com/cookpete/react-player/commit/dc68dbad77d4f66a94f5f7c910a67e12672ae4e9)
- Remove patreon supporter [`4e5d334`](https://github.com/cookpete/react-player/commit/4e5d3341cc91b88666128b4e5c6c8925b273d342)

#### [v2.10.1](https://github.com/cookpete/react-player/compare/v2.10.0...v2.10.1)

> 7 May 2022

- don't stack event listeners [`#1186`](https://github.com/cookpete/react-player/pull/1186)
- Add flvjs error handling, pass it to props onError method. [`#1426`](https://github.com/cookpete/react-player/pull/1426)
- Allow parameters after entry_id on kaltura [`#1432`](https://github.com/cookpete/react-player/pull/1432)
- adds support for youtube short urls [`#1438`](https://github.com/cookpete/react-player/pull/1438)
- Prevent double load bug in strict mode [`#1439`](https://github.com/cookpete/react-player/issues/1439) [`#1450`](https://github.com/cookpete/react-player/pull/1450)
- Use allow rather than allowFullScreen [`#1444`](https://github.com/cookpete/react-player/issues/1444)
- yarn audit fix [`9ad5f38`](https://github.com/cookpete/react-player/commit/9ad5f387e78458ae4ebe776c20d1befa9760d023)
- Fix Player tests [`30314c7`](https://github.com/cookpete/react-player/commit/30314c71d6455d3249b2f472f62754941a92abf6)
- Remove email from package.json [`646e62e`](https://github.com/cookpete/react-player/commit/646e62eb0ef1c3b7e980b55f416f7d8845482af7)

#### [v2.10.0](https://github.com/cookpete/react-player/compare/v2.9.0...v2.10.0)

> 18 March 2022

- Added .m4b extension to AUDIO_EXTENSIONS [`#1415`](https://github.com/cookpete/react-player/pull/1415)
- Adjust listener for kaltura [`#1226`](https://github.com/cookpete/react-player/pull/1226)
- update default HLS version to latest release [`#1402`](https://github.com/cookpete/react-player/pull/1402)
- Make pre-publish.js ES5 compatible [`#1393`](https://github.com/cookpete/react-player/pull/1393)
- fix: Add exception handling when seekTo is called with amount value 0 [`#1372`](https://github.com/cookpete/react-player/pull/1372)
- Add onSeek support to Twitch player [`#1354`](https://github.com/cookpete/react-player/pull/1354)
- Add attributes in FacebookConfig type [`#1351`](https://github.com/cookpete/react-player/pull/1351)
- handle setPlaybackRate error [`#1223`](https://github.com/cookpete/react-player/pull/1223)
- Add onPlaybackRateChange callback prop [`#1224`](https://github.com/cookpete/react-player/pull/1224)
- Ability to pass null to previewTabIndex [`#1256`](https://github.com/cookpete/react-player/pull/1256)
- Add media attribute to source tag in file player [`#1269`](https://github.com/cookpete/react-player/pull/1269)
- fix: don't defer MediaStream [`#1230`](https://github.com/cookpete/react-player/pull/1230)
- add title attribute to vimeo iframe [`#1229`](https://github.com/cookpete/react-player/pull/1229)
- Add oEmbedUrl as changeable prop [`#1333`](https://github.com/cookpete/react-player/pull/1333)
- update video extenstions to accept media fragments for time [`#1213`](https://github.com/cookpete/react-player/pull/1213)
- Add type for wrapper in base.d.ts [`#1234`](https://github.com/cookpete/react-player/pull/1234)
- Make facebook config type properties optional [`#1314`](https://github.com/cookpete/react-player/pull/1314)
- Add disableDeferredLoading prop [`#1396`](https://github.com/cookpete/react-player/issues/1396)
- Support new vimeo external link format [`#1399`](https://github.com/cookpete/react-player/issues/1399)
- Tweak Vidyard pattern [`#1373`](https://github.com/cookpete/react-player/issues/1373)
- Adjust listener for kaltura (#1226) [`#1202`](https://github.com/cookpete/react-player/issues/1202)
- Update caniuse-lite [`9f6adcd`](https://github.com/cookpete/react-player/commit/9f6adcdc37304242d7c328a9df85b67bf8281614)
- Fix kaltura test [`0f512c2`](https://github.com/cookpete/react-player/commit/0f512c20472a2955882e324e7a17d0d970a75fa7)

#### [v2.9.0](https://github.com/cookpete/react-player/compare/v2.8.2...v2.9.0)

> 17 February 2021

- Adds previewTabIndex property to light mode [`#1169`](https://github.com/cookpete/react-player/pull/1169)
- Update YouTube URL pattern (#1170) [`#1171`](https://github.com/cookpete/react-player/pull/1171)
- Support Kaltura's HTML5 player [`#1082`](https://github.com/cookpete/react-player/pull/1082)
- Fix suspense check for nextjs [`#1165`](https://github.com/cookpete/react-player/issues/1165)
- Add kaltura player types [`b1e1053`](https://github.com/cookpete/react-player/commit/b1e105342f4eecf76927241f2ebddd456202bdae)

#### [v2.8.2](https://github.com/cookpete/react-player/compare/v2.8.1...v2.8.2)

> 26 January 2021

- fix: ensure lazy typedefs mirror defaults [`#1153`](https://github.com/cookpete/react-player/pull/1153)
- fix: nullify srcObject instead of calling removeAttribute [`#1145`](https://github.com/cookpete/react-player/pull/1145)
- updated basic types with fallback [`#1144`](https://github.com/cookpete/react-player/pull/1144)
- Update wistia url pattern [`#1149`](https://github.com/cookpete/react-player/issues/1149)
- fix: ensure lazy typedefs mirror defaults (#1153) [`#1146`](https://github.com/cookpete/react-player/issues/1146)

#### [v2.8.1](https://github.com/cookpete/react-player/compare/v2.8.0...v2.8.1)

> 20 January 2021

- fix: remove srcObject attribute if next uri is not a MediaStream [`#1121`](https://github.com/cookpete/react-player/pull/1121)
- Add onClickPreview [`#1131`](https://github.com/cookpete/react-player/pull/1131)
- Added types for single players [`#953`](https://github.com/cookpete/react-player/issues/953)
- yarn upgrade [`0c7dd4a`](https://github.com/cookpete/react-player/commit/0c7dd4a78728f54a53a15b2af1f0087d18ac99e4)
- Fix clean script [`9a448b1`](https://github.com/cookpete/react-player/commit/9a448b13151575e5ce18da8c57fc134a39f12d7d)

#### [v2.8.0](https://github.com/cookpete/react-player/compare/v2.7.2...v2.8.0)

> 20 January 2021

- added soundcloud playlist preview button for demo page so developers can know it also supports sc playlists [`#1113`](https://github.com/cookpete/react-player/pull/1113)
- Add missing FLV props to TS defs [`#1122`](https://github.com/cookpete/react-player/pull/1122)
- FEAT: customize fallback through props for lazy loading [`#1133`](https://github.com/cookpete/react-player/pull/1133)
- Add onClickPreview [`#1131`](https://github.com/cookpete/react-player/pull/1131)
- Added types for single players [`#953`](https://github.com/cookpete/react-player/issues/953)
- Add custom control support to Wistia [`#1125`](https://github.com/cookpete/react-player/issues/1125)
- Fix facebook muted logic [`#1127`](https://github.com/cookpete/react-player/issues/1127)
- yarn upgrade [`b21c908`](https://github.com/cookpete/react-player/commit/b21c908d70f06404528e1b74bfdfb155e0e5e617)

#### [v2.7.2](https://github.com/cookpete/react-player/compare/v2.7.1...v2.7.2)

> 16 December 2020

- fix: add playIcon prop to TS declaration [`#1084`](https://github.com/cookpete/react-player/pull/1084)

#### [v2.7.1](https://github.com/cookpete/react-player/compare/v2.7.0...v2.7.1)

> 16 December 2020

- Improve youtube playlist regex [`#1088`](https://github.com/cookpete/react-player/issues/1088)

#### [v2.7.0](https://github.com/cookpete/react-player/compare/v2.6.2...v2.7.0)

> 16 November 2020

- Parse start time from twitch url [`#1046`](https://github.com/cookpete/react-player/pull/1046)
- Add explicit comment to set muted to true for autoplay [`#1036`](https://github.com/cookpete/react-player/pull/1036)
- Updates the default version of dash.js, hls.js [`#1056`](https://github.com/cookpete/react-player/pull/1056)
- Fix canPlayFile to support blob uri, fix #1023 [`#1041`](https://github.com/cookpete/react-player/pull/1041)
- Update CONTRIBUTING.md [`#1017`](https://github.com/cookpete/react-player/pull/1017)
- Use hls.js manifest parsed event for onReady [`#1066`](https://github.com/cookpete/react-player/issues/1066)
- Add config.facebook.attributes [`#1071`](https://github.com/cookpete/react-player/issues/1071)
- Prevent errors when updating without a player [`#1052`](https://github.com/cookpete/react-player/issues/1052) [`#1074`](https://github.com/cookpete/react-player/issues/1074)
- Add support for youtube live channel urls [`#1033`](https://github.com/cookpete/react-player/issues/1033)
- Fix canPlayFile to support blob uri, fix #1023 (#1041) [`#1023`](https://github.com/cookpete/react-player/issues/1023)
- Add console warning when using youtube embedOptions.events [`#1016`](https://github.com/cookpete/react-player/issues/1016)
- Add support for fb.watch URLs [`#1061`](https://github.com/cookpete/react-player/issues/1061)
- Run yarn-audit-fix [`0c1bfd1`](https://github.com/cookpete/react-player/commit/0c1bfd1259a90df68bc9dd5e1f7558404c4a7345)
- Fix tests [`8e8c107`](https://github.com/cookpete/react-player/commit/8e8c107da5464082ba1bd9383b3711adb317f8b3)
- Adjust blob util [`f9c5429`](https://github.com/cookpete/react-player/commit/f9c54297344808e9da4291cbe5607344159f5342)

#### [v2.6.2](https://github.com/cookpete/react-player/compare/v2.6.1...v2.6.2)

> 8 September 2020

- Add twitch config interface [`#996`](https://github.com/cookpete/react-player/pull/996)
- Fix iPad Pro detection [`#1005`](https://github.com/CookPete/react-player/pull/1005) [`#1004`](https://github.com/CookPete/react-player/pull/1004)

#### [v2.6.1](https://github.com/cookpete/react-player/compare/v2.6.0...v2.6.1)

> 21 August 2020

- Fixes youtube player playsinline variable type [`#989`](https://github.com/cookpete/react-player/pull/989)
- Add descriptive text to Controls section [`#982`](https://github.com/cookpete/react-player/pull/982)
- added missing type for 'getSecondsLoaded' [`#977`](https://github.com/cookpete/react-player/pull/977)
- Fix single player import logic [`#993`](https://github.com/CookPete/react-player/issues/993)
- Remove gitads banner [`cea0c38`](https://github.com/cookpete/react-player/commit/cea0c3813455d686fea41a820e0e2a13b91b13d4)
- Tweak vimeo controls readme wording [`c05cf93`](https://github.com/cookpete/react-player/commit/c05cf932cbfd431196fed0c6dba9ce5d11c43840)
- Tweak gitads copy [`e252438`](https://github.com/cookpete/react-player/commit/e252438052e4ef1452a1e73c9b776e5a403fe691)

#### [v2.6.0](https://github.com/cookpete/react-player/compare/v2.5.0...v2.6.0)

> 23 July 2020

- Make force disabling PIP dependent on prop [`#964`](https://github.com/cookpete/react-player/pull/964)
- vimeo: listen for bufferring events and handle with matching callbacks [`#975`](https://github.com/cookpete/react-player/pull/975)
- Call onLoaded when file streaming SDKs have loaded [`#976`](https://github.com/CookPete/react-player/issues/976)
- yarn upgrade [`05ae217`](https://github.com/cookpete/react-player/commit/05ae21741f0963d6cb8be317b67879270de229ba)
- Add gitads banner to readme [`6840e35`](https://github.com/cookpete/react-player/commit/6840e3567339c2c5833b230919e1c639d0c6629c)

#### [v2.5.0](https://github.com/cookpete/react-player/compare/v2.4.0...v2.5.0)

> 3 July 2020

- Add support for FLV files [`#958`](https://github.com/CookPete/react-player/issues/958)
- Fix single player imports on IE11 [`#954`](https://github.com/CookPete/react-player/issues/954)

#### [v2.4.0](https://github.com/cookpete/react-player/compare/v2.3.1...v2.4.0)

> 28 June 2020

- Hide wistia controls with controls prop [`#937`](https://github.com/cookpete/react-player/pull/937)
- Add Cloudflare Stream support to file player [`#944`](https://github.com/CookPete/react-player/pull/944)
- Fix sample files [`#948`](https://github.com/CookPete/react-player/issues/948)

#### [v2.3.1](https://github.com/cookpete/react-player/compare/v2.3.0...v2.3.1)

> 13 June 2020

- dash deprecated method fix for different versions. [`#933`](https://github.com/cookpete/react-player/pull/933)
- wistia config types updated [`#931`](https://github.com/cookpete/react-player/pull/931)
- Tweak .npmignore [`2204c7e`](https://github.com/cookpete/react-player/commit/2204c7ef26345fc140a51d3b0670030cb3972d32)

#### [v2.3.0](https://github.com/cookpete/react-player/compare/v2.2.0...v2.3.0)

> 11 June 2020

- Add Wistia player id to support multiple players [`#905`](https://github.com/cookpete/react-player/pull/905)
- Copy typings into lazy dir before publish [`#918`](https://github.com/CookPete/react-player/issues/918)
- Add basic caching of preview thumbnails [`#927`](https://github.com/CookPete/react-player/issues/927)
- Fix SDK fetching when `exports` exists in global scope [`#921`](https://github.com/CookPete/react-player/issues/921)
- Bump packages [`b92031a`](https://github.com/cookpete/react-player/commit/b92031aa5bf485396713de1d38c60d08edc80131)
- Remove bower info from readme [`c66a1c7`](https://github.com/cookpete/react-player/commit/c66a1c753b00aa4c0709dabfa121515dab04359f)
- Fix wistia tests [`85af252`](https://github.com/cookpete/react-player/commit/85af252a3b6756d839da49413b85fbcc324b0b43)

#### [v2.2.0](https://github.com/cookpete/react-player/compare/v2.1.1...v2.2.0)

> 7 June 2020

- Move lazy loading players to react-player/lazy [`#912`](https://github.com/CookPete/react-player/issues/912) [`#907`](https://github.com/CookPete/react-player/issues/907) [`#865`](https://github.com/CookPete/react-player/issues/865) [`#910`](https://github.com/CookPete/react-player/issues/910) [`#902`](https://github.com/CookPete/react-player/issues/902)
- Support array of youtube urls [`#906`](https://github.com/CookPete/react-player/issues/906)
- The return of single player imports [`45635ef`](https://github.com/cookpete/react-player/commit/45635ef2d31c12c84f6d008e6420a9224fc5a0e3)
- Move canPlay logic into patterns.js [`45369bb`](https://github.com/cookpete/react-player/commit/45369bb21e54a51fd0fafc32dcd357726ae27af8)
- Add preview chunk name [`17b28ca`](https://github.com/cookpete/react-player/commit/17b28caa35fc361d18f7e3041c3be8eeaddbdfff)

#### [v2.1.1](https://github.com/cookpete/react-player/compare/v2.1.0...v2.1.1)

> 31 May 2020

- Correct typings for onReady [`#884`](https://github.com/CookPete/react-player/issues/884)
- Add webpackChunkName to dynamic imports [`#899`](https://github.com/CookPete/react-player/issues/899)
- Add patreon info to readme [`c4c597f`](https://github.com/cookpete/react-player/commit/c4c597f8e4c16ca3de2314fe9de45e363e3fd71b)

#### [v2.1.0](https://github.com/cookpete/react-player/compare/v2.0.1...v2.1.0)

> 23 May 2020

- Match YouTube User Uploads [`#877`](https://github.com/cookpete/react-player/pull/877)
- Destroy previous vidyard player before creating new one [`#894`](https://github.com/cookpete/react-player/pull/894)
- Improve light mode a11y [`#878`](https://github.com/CookPete/react-player/issues/878)
- Prevent Soundcloud onPause right before onEnded [`#879`](https://github.com/CookPete/react-player/issues/879)
- Prevent unwanted Vidyard autoplay [`#887`](https://github.com/CookPete/react-player/issues/887)
- Add onUnstarted youtube config option [`#888`](https://github.com/CookPete/react-player/issues/888)
- Reset hls and dash before loading new url [`#892`](https://github.com/CookPete/react-player/issues/892) [`#874`](https://github.com/CookPete/react-player/pull/874)
- Support youtube-nocookie.com [`#896`](https://github.com/CookPete/react-player/issues/896) [`#272`](https://github.com/CookPete/react-player/issues/272) [`#557`](https://github.com/CookPete/react-player/pull/557)
- Fix youtube fragment looping [`#897`](https://github.com/CookPete/react-player/issues/897)
- Prevent dynamic import transforms to fix lazy loading [`#886`](https://github.com/CookPete/react-player/issues/886)
- Fix youtube example playlist [`fcf4657`](https://github.com/cookpete/react-player/commit/fcf4657a5b767fa11f573d1f671350d65df3399a)

#### [v2.0.1](https://github.com/cookpete/react-player/compare/v2.0.0...v2.0.1)

> 28 April 2020

- Adds the missing default data for the selected player [`#864`](https://github.com/cookpete/react-player/pull/864)
- Only try and disablePIP on unmount if player is ready [`#849`](https://github.com/CookPete/react-player/issues/849)
- Prevent Suspense being rendering during SSR [`#738`](https://github.com/CookPete/react-player/issues/738) [`#865`](https://github.com/CookPete/react-player/issues/865)
- Match twitch channel pattern case-insensitive [`#860`](https://github.com/CookPete/react-player/issues/860)
- Rename refs to references [`#868`](https://github.com/CookPete/react-player/pull/868) [`#866`](https://github.com/CookPete/react-player/pull/866) [`#867`](https://github.com/CookPete/react-player/issues/867) [`#861`](https://github.com/CookPete/react-player/issues/861)
- Use file player for soundcloud hosted audio files [`#811`](https://github.com/CookPete/react-player/issues/811)

### [v2.0.0](https://github.com/cookpete/react-player/compare/v1.15.3...v2.0.0)

> 25 April 2020

- **Breaking change:** Use lazy players [`1752b8d`](https://github.com/cookpete/react-player/commit/1752b8dc5b034910530325c1a3068c044c196f84)
- **Breaking change:** Remove preload support [`1baa227`](https://github.com/cookpete/react-player/commit/1baa227843e4424998ef22735e8b969cfa817b5e)
- **Breaking change:** Remove legacy config props [`9339efa`](https://github.com/cookpete/react-player/commit/9339efa3c23434189763af4a4e6c052eda74a467)

#### [v1.15.3](https://github.com/cookpete/react-player/compare/v1.15.2...v1.15.3)

> 25 March 2020

- fix for soundcloud direct file link [`#803`](https://github.com/cookpete/react-player/pull/803)
- Update documented default hls.js version to 0.13.1 [`#806`](https://github.com/cookpete/react-player/pull/806)
- Fix hls sdk url [`#826`](https://github.com/CookPete/react-player/pull/826)
- Use proxy methods in wistia player [`#805`](https://github.com/CookPete/react-player/issues/805)
- Move hls destroy from stop to unmount [`#817`](https://github.com/CookPete/react-player/issues/817)
- Update vimeo readme links [`#819`](https://github.com/CookPete/react-player/issues/819)
- Bump packages [`013c9a8`](https://github.com/cookpete/react-player/commit/013c9a884dbe507ee381d593f5337142377a7e59)
- Remove dist test page [`763bb15`](https://github.com/cookpete/react-player/commit/763bb15935a11331cec4b794f7ecab7629836c52)
- Add FUNDING.yml [`e708e8e`](https://github.com/cookpete/react-player/commit/e708e8e8b233e25497f4a7ba6f8e2218d854ec01)

#### [v1.15.2](https://github.com/cookpete/react-player/compare/v1.15.1...v1.15.2)

> 2 February 2020

- Move @ava/babel to devDependencies [`#794`](https://github.com/cookpete/react-player/pull/794)
- Update yarn.lock [`da79e77`](https://github.com/cookpete/react-player/commit/da79e7740b9167ce66d0e3feb04ab6777df2fc2f)
- Update facebook regex to match story URLs [`94172f2`](https://github.com/cookpete/react-player/commit/94172f2ca63116cf57026dc24034040397bea600)

#### [v1.15.1](https://github.com/cookpete/react-player/compare/v1.15.0...v1.15.1)

> 2 February 2020

- Update facebook regex [`#793`](https://github.com/CookPete/react-player/issues/793)

#### [v1.15.0](https://github.com/cookpete/react-player/compare/v1.14.2...v1.15.0)

> 31 January 2020

- Bump handlebars from 4.2.0 to 4.5.3 [`#771`](https://github.com/cookpete/react-player/pull/771)
- Add showPreview() method to ReactPlayer typings [`#785`](https://github.com/cookpete/react-player/pull/785)
- Add playerId override to facebook and twitch players [`#788`](https://github.com/CookPete/react-player/issues/788)
- Relax facebook regex [`#778`](https://github.com/CookPete/react-player/issues/778)
- Update hls.js CDN and version [`#758`](https://github.com/CookPete/react-player/pull/758) [`#768`](https://github.com/CookPete/react-player/issues/768) [`#779`](https://github.com/CookPete/react-player/issues/779)
- Bump packages [`8c037ab`](https://github.com/cookpete/react-player/commit/8c037abaf4abecd1ca0793d62b6dafc9c257f57d)
- Bump packages [`9c59c13`](https://github.com/cookpete/react-player/commit/9c59c133a0b05a6f6e75b8bb3cae4498495ab0da)
- Migrate to terser-webpack-plugin [`83fd60d`](https://github.com/cookpete/react-player/commit/83fd60dba110a7f14f1b7003f194d67f16fba7c5)

#### [v1.14.2](https://github.com/cookpete/react-player/compare/v1.14.1...v1.14.2)

> 20 November 2019

- Support to set custom facebook version [`#745`](https://github.com/cookpete/react-player/pull/745)

#### [v1.14.1](https://github.com/cookpete/react-player/compare/v1.14.0...v1.14.1)

> 11 November 2019

- Ignore React nodes in isEqual [`#740`](https://github.com/CookPete/react-player/issues/740)

#### [v1.14.0](https://github.com/cookpete/react-player/compare/v1.13.0...v1.14.0)

> 6 November 2019

- fixes #733 [`#736`](https://github.com/cookpete/react-player/pull/736)
- Add PIP support for Safari [`#716`](https://github.com/cookpete/react-player/pull/716)
- Properly delete SDK requests [`#718`](https://github.com/cookpete/react-player/pull/718)
- Pass dash.js errors through to onError callback [`#732`](https://github.com/CookPete/react-player/issues/732)
- Use https SDK urls by default [`#728`](https://github.com/CookPete/react-player/issues/728)
- Add playIcon prop [`#730`](https://github.com/CookPete/react-player/issues/730)
- fixes #733 (#736) [`#733`](https://github.com/cookpete/react-player/issues/733)
- Tweak Twitch controls bug comment [`987a18b`](https://github.com/cookpete/react-player/commit/987a18b7ad18dc522ad39ae940c745ea8b756cbc)

#### [v1.13.0](https://github.com/cookpete/react-player/compare/v1.12.0...v1.13.0)

> 19 September 2019

- Delete SDK requests array after loading error [`#711`](https://github.com/cookpete/react-player/pull/711)
- Add typedef support for MediaStream url source [`#674`](https://github.com/cookpete/react-player/pull/674)
- Add readme note about SDK overrides [`#605`](https://github.com/CookPete/react-player/issues/605)
- Use proxy methods for FilePlayer listeners [`#616`](https://github.com/CookPete/react-player/issues/616)
- Add pip support info to readme [`#694`](https://github.com/CookPete/react-player/issues/694)
- Add controls support to Twitch and Vimeo [`#687`](https://github.com/CookPete/react-player/pull/687)
- Remove default black backgrounds [`#673`](https://github.com/CookPete/react-player/pull/673)
- Fix demo file paths [`#697`](https://github.com/CookPete/react-player/issues/697)
- Bump packages [`4c4e0b5`](https://github.com/cookpete/react-player/commit/4c4e0b5fb3321d1454017426d8adf2bb37ce1ac8)
- Bump packages [`9d39d92`](https://github.com/cookpete/react-player/commit/9d39d92b9883f6b27e268889e4d2f087dd47f225)

#### [v1.12.0](https://github.com/cookpete/react-player/compare/v1.11.2...v1.12.0)

> 18 August 2019

- Migrate to componentDidUpdate [`#651`](https://github.com/CookPete/react-player/issues/651) [`#696`](https://github.com/CookPete/react-player/issues/696) [`#692`](https://github.com/CookPete/react-player/pull/692) [`#693`](https://github.com/CookPete/react-player/pull/693)
- Overhaul development config [`#695`](https://github.com/CookPete/react-player/issues/695)
- Lint fixes [`ae0f230`](https://github.com/cookpete/react-player/commit/ae0f230dbe36cb95c3fe39e22687d69599185ad5)
- Do not always call setPlaybackRate on play [`2b59631`](https://github.com/cookpete/react-player/commit/2b59631fedc9acbb9ce685d28b04a8125b60071a)
- Remove old testing config from travis [`f91c737`](https://github.com/cookpete/react-player/commit/f91c7371b09013148409ada569280865bf9d3cc4)

#### [v1.11.2](https://github.com/cookpete/react-player/compare/v1.11.1...v1.11.2)

> 10 August 2019

- Fix preload display logic [`#675`](https://github.com/CookPete/react-player/issues/675)
- Add showPreview instance method [`#679`](https://github.com/CookPete/react-player/issues/679)
- Hide preview thumbnail when light changes to false [`#680`](https://github.com/CookPete/react-player/issues/680)
- Add playbackRate support to Vimeo [`#681`](https://github.com/CookPete/react-player/issues/681)
- Fix Vimeo fast switch bug [`#683`](https://github.com/CookPete/react-player/issues/683)

#### [v1.11.1](https://github.com/cookpete/react-player/compare/v1.11.0...v1.11.1)

> 13 June 2019

- Do not use vimeo API to play vimeo hosted video files. [`#597`](https://github.com/cookpete/react-player/pull/597)
- Catches error on Vimeo play() [`#645`](https://github.com/cookpete/react-player/pull/645)
- Update Meteor section in readme [`#647`](https://github.com/cookpete/react-player/pull/647)
- [d.ts] Fix typing of arguments of onError handler [`#655`](https://github.com/cookpete/react-player/pull/655)
- Fix twitch permanent loading state when stream is offline [`#657`](https://github.com/cookpete/react-player/pull/657)
- Fix an issue with setting state in the Preview component after being unmounted. [`#658`](https://github.com/cookpete/react-player/pull/658)
- Prevent double inline styles being applied [`#609`](https://github.com/CookPete/react-player/issues/609)
- Fix onProgress for infinite duration streams [`#503`](https://github.com/CookPete/react-player/issues/503)
- Fix light mode poster not updating [`#646`](https://github.com/CookPete/react-player/issues/646)
- Fix youtube looping [`#530`](https://github.com/CookPete/react-player/issues/530) [`#639`](https://github.com/CookPete/react-player/issues/639) [`#662`](https://github.com/CookPete/react-player/pull/662)
- Fix multiple onError params not being passed through [`#627`](https://github.com/CookPete/react-player/issues/627)

#### [v1.11.0](https://github.com/cookpete/react-player/compare/v1.10.0...v1.11.0)

> 10 April 2019

- Soundcloud - fix autoplay delegation [`#614`](https://github.com/cookpete/react-player/pull/614)
- Implemented onBufferEnd cb prop for FilePlayer, YouTube and Facebook [`#615`](https://github.com/cookpete/react-player/pull/615)
- Add on error callback to wistia player [`#617`](https://github.com/cookpete/react-player/pull/617)
- align typings for seekTo method with code [`#619`](https://github.com/cookpete/react-player/pull/619)
- Really really small typo in README.md [`#606`](https://github.com/cookpete/react-player/pull/606)
- Add issue template [`ba6c274`](https://github.com/cookpete/react-player/commit/ba6c2747f18c5d005866b88bca58a9f2342611a1)

#### [v1.10.0](https://github.com/cookpete/react-player/compare/v1.9.3...v1.10.0)

> 24 March 2019

- Add playsinline support to vimeo [`#574`](https://github.com/CookPete/react-player/issues/574)
- Use normal wrapper for light mode [`#584`](https://github.com/CookPete/react-player/issues/584)
- Pass controls prop through to preload players [`#587`](https://github.com/CookPete/react-player/issues/587)
- Add Soundcloud preload to props files [`#588`](https://github.com/CookPete/react-player/issues/588)
- Add type parameter to seekTo method [`#591`](https://github.com/CookPete/react-player/issues/591)
- Set isLoading to false after error [`#595`](https://github.com/CookPete/react-player/issues/595)
- Unbind wistia player events correctly [`#594`](https://github.com/CookPete/react-player/issues/594)

#### [v1.9.3](https://github.com/cookpete/react-player/compare/v1.9.2...v1.9.3)

> 27 January 2019

- Bump auto-changelog [`3486039`](https://github.com/cookpete/react-player/commit/3486039e99a7fb784df54830ffa136408477d55b)

#### [v1.9.2](https://github.com/cookpete/react-player/compare/v1.9.1...v1.9.2)

> 27 January 2019

- Support player setLoop methods [`#560`](https://github.com/CookPete/react-player/issues/560)
- Upgrade screenfull [`#560`](https://github.com/CookPete/react-player/issues/560)

#### [v1.9.1](https://github.com/cookpete/react-player/compare/v1.9.0...v1.9.1)

> 25 January 2019

- Use https for noembed fetching [`#558`](https://github.com/CookPete/react-player/issues/558) [`#448`](https://github.com/CookPete/react-player/issues/448)
- Add controls toggle and light mode to demo app [`0ba5b71`](https://github.com/cookpete/react-player/commit/0ba5b719d561021b6918e5bbd981278710e4ddec)
- Show preview when switching from non-light to light [`4a39dc4`](https://github.com/cookpete/react-player/commit/4a39dc4c52092f65b374651f54e02b8aca6eff76)

#### [v1.9.0](https://github.com/cookpete/react-player/compare/v1.8.0...v1.9.0)

> 24 January 2019

- Allows wrapper proptype to be a ForwardRef component [`#556`](https://github.com/cookpete/react-player/pull/556)
- playsinline for Android wechat [`#544`](https://github.com/cookpete/react-player/pull/544)
- Fix broken URL for standalone script [`#546`](https://github.com/cookpete/react-player/pull/546)
- Accept youtube.com/playlist?list= URLs [`#541`](https://github.com/cookpete/react-player/pull/541)
- Add youtube embedOptions config option [`#557`](https://github.com/CookPete/react-player/pull/557) [`#272`](https://github.com/CookPete/react-player/issues/272)
- Bump deepmerge [`7722679`](https://github.com/cookpete/react-player/commit/77226796f9542a59bde28be6bfbd74f24f994d16)
- Youtube playlist fixes [`04bf181`](https://github.com/cookpete/react-player/commit/04bf181a3d27856a86ef81b8c141b7bf68947198)

#### [v1.8.0](https://github.com/cookpete/react-player/compare/v1.7.1...v1.8.0)

> 19 December 2018

- Add light prop [`#448`](https://github.com/CookPete/react-player/issues/448)

#### [v1.7.1](https://github.com/cookpete/react-player/compare/v1.7.0...v1.7.1)

> 5 December 2018

- Parse youtube playlist from URL list param [`#318`](https://github.com/CookPete/react-player/issues/318)
- Loop youtube playlists correctly [`#530`](https://github.com/CookPete/react-player/issues/530)
- Fix dist build [`29fb971`](https://github.com/cookpete/react-player/commit/29fb971efae61ae90b66fca07f2bc36c55b7cc21)
- Update npmignore [`32e8286`](https://github.com/cookpete/react-player/commit/32e8286e81a03879e13dd0e0b3647ffd2bfd2248)

#### [v1.7.0](https://github.com/cookpete/react-player/compare/v1.6.6...v1.7.0)

> 17 November 2018

- Support Chrome Picture In Picture (PIP) mode [`#504`](https://github.com/cookpete/react-player/pull/504)
- Typings - label in TrackProps [`#489`](https://github.com/cookpete/react-player/pull/489)
- fix: fix onProgress callback not firing during iOS live streams [`#497`](https://github.com/cookpete/react-player/pull/497)
- Update vimeo preload URL [`#509`](https://github.com/cookpete/react-player/pull/509)
- add absent methods signature [`#507`](https://github.com/cookpete/react-player/pull/507)
- Add hlsVersion and dashVersion file config [`#513`](https://github.com/CookPete/react-player/pull/513)
- Export single player exports to main file [`#421`](https://github.com/CookPete/react-player/issues/421)
- Refresh vimeo duration on play [`#514`](https://github.com/CookPete/react-player/issues/514)
- Use PLAYING event instead of PLAY for Twitch player [`#498`](https://github.com/CookPete/react-player/issues/498)
- Do not call onEnded when looping [`#496`](https://github.com/CookPete/react-player/issues/496)
- Prevent singlePlayer from returning null when forcing via config [`#500`](https://github.com/CookPete/react-player/issues/500)

#### [v1.6.6](https://github.com/cookpete/react-player/compare/v1.6.5...v1.6.6)

> 20 September 2018

- Add coverage to npmignore [`580e21c`](https://github.com/cookpete/react-player/commit/580e21c60842492b325d039fa0361fd547cb79ed)

#### [v1.6.5](https://github.com/cookpete/react-player/compare/v1.6.4...v1.6.5)

> 20 September 2018

- Fix: signal multiple sources change to the browser [`#482`](https://github.com/cookpete/react-player/pull/482)
- Check null before accessing FilePlayer properties [`#465`](https://github.com/cookpete/react-player/pull/465)
-  Adds hlsOptions to FileConfig def [`#483`](https://github.com/cookpete/react-player/pull/483)
- Updated hls.js documentation link in README.md [`#440`](https://github.com/cookpete/react-player/pull/440)
- Prevent YouTube from playing after seekTo when paused [`#437`](https://github.com/CookPete/react-player/issues/437)
- Fix facebook iframe visibility bug [`#455`](https://github.com/CookPete/react-player/issues/455)
- Update dailymotion regex [`#438`](https://github.com/CookPete/react-player/issues/438)
- Fix issues with non-embeddable Facebook videos [`48401ab`](https://github.com/cookpete/react-player/commit/48401abdfc3f08675fddd8a130588cc53faf2e5b)
- Enable partial line coverage with codecov [`488f82d`](https://github.com/cookpete/react-player/commit/488f82da421ac9a27c2e52137767d19cc7f47a3e)
- Run tests before versioning [`4055469`](https://github.com/cookpete/react-player/commit/405546969a3fc6f26ee72b2b47a752c284b2835d)

#### [v1.6.4](https://github.com/cookpete/react-player/compare/v1.6.3...v1.6.4)

> 7 June 2018

- YouTube videos not forwarding `end` param [`#420`](https://github.com/cookpete/react-player/pull/420)

#### [v1.6.3](https://github.com/cookpete/react-player/compare/v1.6.2...v1.6.3)

> 6 June 2018

- Set FilePlayer autoPlay correctly [`#405`](https://github.com/CookPete/react-player/issues/405)
- Tidy up start and end timestamp logic [`#412`](https://github.com/CookPete/react-player/issues/412)
- Improve MediaStream guards [`#415`](https://github.com/CookPete/react-player/issues/415)
- Overhaul tests [`4bd78e0`](https://github.com/cookpete/react-player/commit/4bd78e0acbd4dd3399582564d0a0686b616361b0)
- Remove requirement for window global [`9618272`](https://github.com/cookpete/react-player/commit/9618272e6e4b518e493d3130b96dd2ebeb97b506)
- Player component tweaks [`1ac1480`](https://github.com/cookpete/react-player/commit/1ac148019e67ed75f31885a201755ec065c2f581)

#### [v1.6.2](https://github.com/cookpete/react-player/compare/v1.6.1...v1.6.2)

> 27 May 2018

- Pass instance through to onReady callback [`#407`](https://github.com/CookPete/react-player/issues/407)
- Mute preload players [`16f5fcb`](https://github.com/cookpete/react-player/commit/16f5fcb6bfdda69702d0741c7587218cf7f62d6f)

#### [v1.6.1](https://github.com/cookpete/react-player/compare/v1.6.0...v1.6.1)

> 20 May 2018

- Bump HLS.js [`#403`](https://github.com/cookpete/react-player/pull/403)

#### [v1.6.0](https://github.com/cookpete/react-player/compare/v1.5.1...v1.6.0)

> 17 May 2018

- Add support for MediaStream objects [`#398`](https://github.com/CookPete/react-player/issues/398)
- Prevent errors when switching URLs whilst loading [`#377`](https://github.com/CookPete/react-player/issues/377)
- Prevent loading SDKs multiple times [`#391`](https://github.com/CookPete/react-player/issues/391)

#### [v1.5.1](https://github.com/cookpete/react-player/compare/v1.5.0...v1.5.1)

> 15 May 2018

- Add proper mute support to fix autoplay [`#395`](https://github.com/CookPete/react-player/issues/395) [`#389`](https://github.com/CookPete/react-player/issues/389)
- Mute player for tests [`58c6a58`](https://github.com/cookpete/react-player/commit/58c6a58d45cfb09517e1023495036dcf66bf3f5f)
- Fix vimeo config options property [`a645eee`](https://github.com/cookpete/react-player/commit/a645eeead7793bd57ff64dbe3bec0c2077cb8a25)
- Skip soundcloud tests [`3958f8d`](https://github.com/cookpete/react-player/commit/3958f8dea68aba7d641b5292f7aacf001d18123f)

#### [v1.5.0](https://github.com/cookpete/react-player/compare/v1.4.0...v1.5.0)

> 23 April 2018

- Fix FilePlayer getSecondsLoaded [`#380`](https://github.com/cookpete/react-player/pull/380)
- Add support for dropbox files [`#379`](https://github.com/CookPete/react-player/issues/379)
- Tidy up getSecondsLoaded [`93351f9`](https://github.com/cookpete/react-player/commit/93351f92b7841cd6f99cf805bf3922d46fdec2cc)
- Update readme [`de639e4`](https://github.com/cookpete/react-player/commit/de639e41915ecc315f5a84a535d936c455d653e2)

#### [v1.4.0](https://github.com/cookpete/react-player/compare/v1.3.2...v1.4.0)

> 11 April 2018

- Add support for custom players [`#364`](https://github.com/cookpete/react-player/pull/364)
- Add instance methods to single player imports [`#376`](https://github.com/CookPete/react-player/issues/376)
- Fix tests [`597bf33`](https://github.com/cookpete/react-player/commit/597bf332b839c89b91aae4934b0e324bd43d635f)

#### [v1.3.2](https://github.com/cookpete/react-player/compare/v1.3.1...v1.3.2)

> 5 April 2018

- Render video element when poster is set [`#369`](https://github.com/CookPete/react-player/issues/369)
- Add https to Twitch SDK URL [`#367`](https://github.com/CookPete/react-player/issues/367)
- Add forceVideo option for FilePlayer [`a743396`](https://github.com/cookpete/react-player/commit/a74339606a6c7c773ea11d1eee1f43dde859a53b)
- Remove migration note [`308bca9`](https://github.com/cookpete/react-player/commit/308bca9af64f04382a1f3d141942860c85372fc2)

#### [v1.3.1](https://github.com/cookpete/react-player/compare/v1.3.0...v1.3.1)

> 22 March 2018

- Fix file player load sequence on iOS [`#340`](https://github.com/CookPete/react-player/issues/340)
- Do not use Vimeo player for vimeo.com file paths [`#358`](https://github.com/CookPete/react-player/issues/358)

#### [v1.3.0](https://github.com/cookpete/react-player/compare/v1.2.1...v1.3.0)

> 15 March 2018

- Include Mixcloud in README [`#342`](https://github.com/cookpete/react-player/pull/342)
- Add support for twitch player options [`#343`](https://github.com/CookPete/react-player/issues/343)
- Add single player wrapper div [`#346`](https://github.com/CookPete/react-player/issues/346)
- Loosen up the vimeo URL pattern [`#349`](https://github.com/CookPete/react-player/issues/349) [`#348`](https://github.com/CookPete/react-player/pull/348)
- Pass through HLS errors to onError [`#354`](https://github.com/CookPete/react-player/issues/354) [`#355`](https://github.com/CookPete/react-player/pull/355)
- Set volume to null by default [`#357`](https://github.com/CookPete/react-player/issues/357)
- Escape dots in URL patterns [`129a179`](https://github.com/cookpete/react-player/commit/129a179ba64e67554f79af44855450c203cab948)
- Tweak tests [`3b92c4a`](https://github.com/cookpete/react-player/commit/3b92c4af5a5d551f2598174d1da9377476c4ed87)
- Unmute facebook player when ready [`a672ee1`](https://github.com/cookpete/react-player/commit/a672ee1d4e1ea0e5a5b473bf76bc644caf5606d6)

#### [v1.2.1](https://github.com/cookpete/react-player/compare/v1.2.0...v1.2.1)

> 26 February 2018

- Add progressInterval logic back in to onProgress [`#339`](https://github.com/CookPete/react-player/issues/339)
- Skip mixcloud tests [`34b51a4`](https://github.com/cookpete/react-player/commit/34b51a4dad80cf2f0dc5cbef82cc5fe041397f7c)

#### [v1.2.0](https://github.com/cookpete/react-player/compare/v1.1.3...v1.2.0)

> 23 February 2018

- Fixes the onPause event propogation [`#336`](https://github.com/cookpete/react-player/pull/336)
- Support custom wrappers [`#334`](https://github.com/cookpete/react-player/pull/334)
- Add Mixcloud player [`#335`](https://github.com/CookPete/react-player/issues/335)
- Add support for importing single players [`#311`](https://github.com/CookPete/react-player/issues/311)
- Move onProgress logic into Player component [`42a030e`](https://github.com/cookpete/react-player/commit/42a030e6cfa721eb15c6af2b27c828160ebcae25)

#### [v1.1.3](https://github.com/cookpete/react-player/compare/v1.1.2...v1.1.3)

> 21 February 2018

- Match YouTube URL with extra parameters [`#332`](https://github.com/cookpete/react-player/pull/332)
- Update mp3 example [`#330`](https://github.com/CookPete/react-player/issues/330)
- Update stored config when props change [`#329`](https://github.com/CookPete/react-player/issues/329)
- Pass through all source props when using array of objects [`#333`](https://github.com/CookPete/react-player/issues/333)
- Skip wistia tests [`6e536d1`](https://github.com/cookpete/react-player/commit/6e536d11d339f4eb4e62cc6fc448c0597bc276b7)
- Fix soundcloud canPlay test [`1f158d1`](https://github.com/cookpete/react-player/commit/1f158d1b8bb353553ea5680ed9041db26055da53)

#### [v1.1.2](https://github.com/cookpete/react-player/compare/v1.1.1...v1.1.2)

> 8 February 2018

- Add config.file.hlsOptions [`#325`](https://github.com/CookPete/react-player/issues/325) [`#319`](https://github.com/CookPete/react-player/issues/319)
- Send any soundcloud URL to the widget [`#322`](https://github.com/CookPete/react-player/issues/322) [`#324`](https://github.com/CookPete/react-player/pull/324)
- Change progressFrequency to progressInterval [`#317`](https://github.com/CookPete/react-player/issues/317)
- Guard against navigator not being available [`#315`](https://github.com/CookPete/react-player/issues/315)
- Update hls, dash and streamable SDKs [`22dd274`](https://github.com/cookpete/react-player/commit/22dd274d20a15e664ac20f448289e529684e9444)

#### [v1.1.1](https://github.com/cookpete/react-player/compare/v1.1.0...v1.1.1)

> 18 January 2018

- Skip unreliable tests [`81c8a0b`](https://github.com/cookpete/react-player/commit/81c8a0bf389634604e245fe4211b07f44bbcfd9d)
- Update readme [`cf8a93f`](https://github.com/cookpete/react-player/commit/cf8a93fd39534cc2a72182675e3274a1cdad9ffb)

#### [v1.1.0](https://github.com/cookpete/react-player/compare/v1.0.0...v1.1.0)

> 18 January 2018

- Tidy up readme [`5befbfa`](https://github.com/cookpete/react-player/commit/5befbfab44313a48d7770cf00f3cda200ebc3bbb)
- Add standalone player [`4ffd201`](https://github.com/cookpete/react-player/commit/4ffd20112f18c2c7b3c25e39c283f8cfe9be88fc)
- Update description and keywords [`0194b43`](https://github.com/cookpete/react-player/commit/0194b43758fccbbda755d13d9eb0d2a4a80aba77)

### [v1.0.0](https://github.com/cookpete/react-player/compare/v1.0.0-beta.7...v1.0.0)

> 17 January 2018

- Do not load hls.js on iOS [`#293`](https://github.com/cookpete/react-player/pull/293)
- Better prop comparison for shouldComponentUpdate [`#302`](https://github.com/CookPete/react-player/issues/302)
- Less aggressive URL matching [`#297`](https://github.com/CookPete/react-player/issues/297)
- Remove vidme player [`#294`](https://github.com/CookPete/react-player/issues/294)
- Bump packages [`7050614`](https://github.com/cookpete/react-player/commit/7050614360bbdfb7f68c3e6c2b6fd5057c23015c)
- Bump packages [`c309053`](https://github.com/cookpete/react-player/commit/c30905356e59a3a71a8821df827cee72e3c5ec3f)
- Use postcss-loader in favour of sass-loader [`d691af4`](https://github.com/cookpete/react-player/commit/d691af41555871e0163fbc1a70702f784087a645)

#### [v1.0.0-beta.7](https://github.com/cookpete/react-player/compare/v1.0.0-beta.6...v1.0.0-beta.7)

> 14 December 2017

- Refactor player tests [`e2b6de6`](https://github.com/cookpete/react-player/commit/e2b6de61dad6802e59b438df5e0f19537857066b)
- Add more tests [`d8b79db`](https://github.com/cookpete/react-player/commit/d8b79db444494b6061583e6496fde5542ead69b8)
- Fix getInternalPlayer method [`bbed43f`](https://github.com/cookpete/react-player/commit/bbed43f6db3fd5de84cfeeae2172d9a64df1e852)

#### [v1.0.0-beta.6](https://github.com/cookpete/react-player/compare/v1.0.0-beta.5...v1.0.0-beta.6)

> 5 December 2017

- Vimeo On Demand URL Support [`#289`](https://github.com/cookpete/react-player/pull/289)
- Use index keys for FilePlayer source array [`#276`](https://github.com/CookPete/react-player/issues/276)
- Add more tests [`2e88e5e`](https://github.com/cookpete/react-player/commit/2e88e5e771fe4c6fc08ff681820e62e5b7d45fec)
- Add url switch tests [`78c483c`](https://github.com/cookpete/react-player/commit/78c483c09ee64ca27bffbddbd0583165082caf8d)
- Bump auto-changelog [`cd26b44`](https://github.com/cookpete/react-player/commit/cd26b44ea7a10b6430329f935c57fd017b5b3f64)

#### [v1.0.0-beta.5](https://github.com/cookpete/react-player/compare/v1.0.0-beta.4...v1.0.0-beta.5)

> 10 November 2017

- Set internal isPlaying flag to false on ended [`#271`](https://github.com/CookPete/react-player/issues/271)
- Bump auto-changelog [`e246e43`](https://github.com/cookpete/react-player/commit/e246e43ae227867a1b468bbfa7948bcb2e647c5b)

#### [v1.0.0-beta.4](https://github.com/cookpete/react-player/compare/v1.0.0-beta.3...v1.0.0-beta.4)

> 8 November 2017

- Update twitch URL pattern [`#267`](https://github.com/cookpete/react-player/issues/267)
- Retain player order to prevent weird iframe behaviour when switching players [`#264`](https://github.com/CookPete/react-player/issues/264) [`#265`](https://github.com/CookPete/react-player/issues/265)

#### [v1.0.0-beta.3](https://github.com/cookpete/react-player/compare/v1.0.0-beta.2...v1.0.0-beta.3)

> 30 October 2017

- Tweak version script [`00d88ea`](https://github.com/cookpete/react-player/commit/00d88ea5cd27f94c0b0f030f002ad221adb8cedf)

#### [v1.0.0-beta.2](https://github.com/cookpete/react-player/compare/v1.0.0-beta.1...v1.0.0-beta.2)

> 30 October 2017

- Add Soundcloud preload support [`#262`](https://github.com/CookPete/react-player/pull/262)
- Use MIT license [`dd5a0ac`](https://github.com/cookpete/react-player/commit/dd5a0ac64d997aac5e81252ed591b07f9e4835a2)
- Fix isPlaying bug [`193f3dd`](https://github.com/cookpete/react-player/commit/193f3dde5cec068e5ec1379d9e789e9c5aa2edfd)

#### [v1.0.0-beta.1](https://github.com/cookpete/react-player/compare/v1.0.0-beta.0...v1.0.0-beta.1)

> 28 October 2017

- Use latest auto-changelog [`d3401dc`](https://github.com/cookpete/react-player/commit/d3401dcb4d9b613bfb6c5413f808243061c9ee25)
- Bump packages [`c381cc2`](https://github.com/cookpete/react-player/commit/c381cc2f17fffc9ecd6c6ac664e419d2f67453ab)
- Prevent errors when unmounting before SDK loads [`979e639`](https://github.com/cookpete/react-player/commit/979e639b8f8faca50509c692d1688e7eb39fef50)

#### [v1.0.0-beta.0](https://github.com/cookpete/react-player/compare/v0.25.3...v1.0.0-beta.0)

> 26 October 2017

- Refactor player rendering [`91d1542`](https://github.com/cookpete/react-player/commit/91d15424065575d759885e9b0f8969c8b1f4a7b4)
- Update tests [`9b4d1c4`](https://github.com/cookpete/react-player/commit/9b4d1c4da466010e0d9f8a99f725b4e0c96db615)

#### [v0.25.3](https://github.com/cookpete/react-player/compare/v0.25.2...v0.25.3)

> 26 October 2017

- Loop when player is running [`#257`](https://github.com/cookpete/react-player/pull/257)
- properly destroy HLS.JS and / or DASH.JS instances [`#254`](https://github.com/cookpete/react-player/pull/254)
- Fix preloading [`6dd81ba`](https://github.com/cookpete/react-player/commit/6dd81ba4f8e9335e1bd2d15b47cd8d6403b35a3b)
- Update readme [`d5ba064`](https://github.com/cookpete/react-player/commit/d5ba064a42f3b0abfa4c0d78324305ef20120f54)

#### [v0.25.2](https://github.com/cookpete/react-player/compare/v0.25.1...v0.25.2)

> 17 October 2017

- tweak Vimeo duration logic [`#251`](https://github.com/cookpete/react-player/pull/251)
- Bump packages [`bf40e5f`](https://github.com/cookpete/react-player/commit/bf40e5fce5653df79f245588440f7b2f20f9a6cb)
- Fix dash autoplay logic [`4e3545e`](https://github.com/cookpete/react-player/commit/4e3545ef8f0e1b46b4a1dcab0c4881c4e507b7f8)

#### [v0.25.1](https://github.com/cookpete/react-player/compare/v0.25.0...v0.25.1)

> 17 October 2017

- Update typings for onProgress callback [`#246`](https://github.com/cookpete/react-player/pull/246)
- Fix parameter name for Vimeo player configuration [`#243`](https://github.com/cookpete/react-player/pull/243)
- Add passthrough prop support to typings [`#247`](https://github.com/CookPete/react-player/issues/247)
- Fix Vimeo duration logic [`#250`](https://github.com/CookPete/react-player/issues/250)
- fix parameter name for Vimeo player configuration [`f810221`](https://github.com/cookpete/react-player/commit/f8102218dd901e0e49a0d8301f5f38d73b92af92)

#### [v0.25.0](https://github.com/cookpete/react-player/compare/v0.24.6...v0.25.0)

> 4 October 2017

- Update polyfill info [`#239`](https://github.com/cookpete/react-player/issues/239)
- Use React 16 for demo [`#241`](https://github.com/cookpete/react-player/issues/241)
- Tweak vimeo player logic [`#240`](https://github.com/cookpete/react-player/issues/240)
- Bump packages [`a31ab1c`](https://github.com/cookpete/react-player/commit/a31ab1c4d7f2e783c129db938367fb7a44da4d8f)
- Update scripts and config file names [`920c602`](https://github.com/cookpete/react-player/commit/920c6024ec1a2ed703f557e2d71ccc406026053b)
- Remove promise and fetch polyfills from browser build [`a33238c`](https://github.com/cookpete/react-player/commit/a33238cb83493fefc83d01b38551ef5df9d33783)

#### [v0.24.6](https://github.com/cookpete/react-player/compare/v0.24.5...v0.24.6)

> 28 September 2017

- Make getInternalPlayer more flexible [`#238`](https://github.com/CookPete/react-player/issues/238)

#### [v0.24.5](https://github.com/cookpete/react-player/compare/v0.24.4...v0.24.5)

> 16 September 2017

- Update Streamable player to use player.js [`#237`](https://github.com/CookPete/react-player/issues/237)
- Bump auto-changelog [`84ad345`](https://github.com/cookpete/react-player/commit/84ad345c926bf7fc47b0c61e77aea4c517cbd028)
- Remove unnecessary stop logic from soundcloud [`f478f1a`](https://github.com/cookpete/react-player/commit/f478f1a3c29cfd8c95c4f49a17ddd05b1b042bc7)
- Skip Streamable tests [`f210f93`](https://github.com/cookpete/react-player/commit/f210f93f5670d605b95887d4f44a8a22c9e42eee)

#### [v0.24.4](https://github.com/cookpete/react-player/compare/v0.24.3...v0.24.4)

> 14 September 2017

- Updated TypeScript typings [`#235`](https://github.com/cookpete/react-player/pull/235)
- Prevent warnings when unmounting preloading players [`4c4d2ec`](https://github.com/cookpete/react-player/commit/4c4d2ecb2ba624bffe211cb041a97b1c42da9cc1)

#### [v0.24.3](https://github.com/cookpete/react-player/compare/v0.24.2...v0.24.3)

> 14 September 2017

- update of DOM listeners in FilePlayer when audio/video tags was switched [`#234`](https://github.com/cookpete/react-player/pull/234)
- Use callPlayer util for player methods [`c760655`](https://github.com/cookpete/react-player/commit/c760655b26fbf3c01a129665861dc25661d91f7d)
- Refactor progress logic [`f1b12e2`](https://github.com/cookpete/react-player/commit/f1b12e22262236b6c514c13de3534b8e4ede3f67)
- Clean up tests [`717397e`](https://github.com/cookpete/react-player/commit/717397e2010a7c2a9d2bbb543e3390ee9be94e9c)

#### [v0.24.2](https://github.com/cookpete/react-player/compare/v0.24.1...v0.24.2)

> 11 September 2017

- Add codecov support [`bc30340`](https://github.com/cookpete/react-player/commit/bc30340ce5da9299635bda0a1ad8560af9214516)
- Add getConfig tests [`f7eb2b5`](https://github.com/cookpete/react-player/commit/f7eb2b55929842523377bda20d80b62eb6c9a7d1)
- Player test tweaks [`c0ee179`](https://github.com/cookpete/react-player/commit/c0ee179cc61e316ca2f1f705bf53c65b304206a0)

#### [v0.24.1](https://github.com/cookpete/react-player/compare/v0.24.0...v0.24.1)

> 9 September 2017

- Refactor player tests [`4551f25`](https://github.com/cookpete/react-player/commit/4551f25041a24b9efdc7587ec2a3c98a2fc26094)
- Refactor player rendering logic [`2b62811`](https://github.com/cookpete/react-player/commit/2b62811bbb00333e52b93c4d1635cb78fbc67c51)
- Add util tests [`586f179`](https://github.com/cookpete/react-player/commit/586f1794d14fc2acb5760ed0f123447def4cf69c)

#### [v0.24.0](https://github.com/cookpete/react-player/compare/v0.23.0...v0.24.0)

> 9 September 2017

- Use single config prop [`#71`](https://github.com/cookpete/react-player/issues/71)
- Fix webpack production config [`#231`](https://github.com/CookPete/react-player/issues/231)
- Remove legacy Soundcloud player [`a55ef3c`](https://github.com/cookpete/react-player/commit/a55ef3c963d46a2cb1b1a7d71ba0d66ef3edcb20)
- Readme tweaks [`c0071f2`](https://github.com/cookpete/react-player/commit/c0071f2f987384ff4a8d188bf98a54f03eb42ea8)
- Bring back static canPlay util [`f4861d7`](https://github.com/cookpete/react-player/commit/f4861d714da0be1ec9f8c10c00342a6f9efdef41)

#### [v0.23.0](https://github.com/cookpete/react-player/compare/v0.22.0...v0.23.0)

> 8 September 2017

- Soundcloud: use Widget API instead of HTTP API [`#199`](https://github.com/cookpete/react-player/pull/199)
- Fixed broken TypeScript type definitions [`#230`](https://github.com/cookpete/react-player/pull/230)
- Refactor development setup [`81df2a8`](https://github.com/cookpete/react-player/commit/81df2a8c929e4a7d4c10d5c6606964d6d7bf5f1e)
- Use Widget instead of Soundcloud API [`4ea29f9`](https://github.com/cookpete/react-player/commit/4ea29f95c642b56b05464a5baab5cbe1fdb28e5a)
- Tidy up Soundcloud player [`d0947e2`](https://github.com/cookpete/react-player/commit/d0947e256a7998a76820239eca5fabc28783bc91)

#### [v0.22.0](https://github.com/cookpete/react-player/compare/v0.21.0...v0.22.0)

> 3 September 2017

- Move SDK loading logic into getSDK util [`318c9e9`](https://github.com/cookpete/react-player/commit/318c9e93cc55b00d939ab28ecaefce3467ef8440)
- Add Twitch player [`288c18d`](https://github.com/cookpete/react-player/commit/288c18ddd582a9cd8b629216041a0166d4c23987)
- Add sourcemap config to uglify plugin [`901dfbf`](https://github.com/cookpete/react-player/commit/901dfbf101e0d26f9c685088ad9a9b4d318d9950)

#### [v0.21.0](https://github.com/cookpete/react-player/compare/v0.20.0...v0.21.0)

> 2 September 2017

- Add muted prop [`#221`](https://github.com/CookPete/react-player/issues/221)
- Add onSeek prop [`#222`](https://github.com/CookPete/react-player/issues/222)
- Tweak Wistia player load logic [`b0e725c`](https://github.com/cookpete/react-player/commit/b0e725cc7a5c964ba17f3422ebf7406f409f0a60)
- Add missing typings [`8d1295e`](https://github.com/cookpete/react-player/commit/8d1295e6796ec31b66cfe5f6ef1694d8cafdad9e)
- Update demo videos [`26411b9`](https://github.com/cookpete/react-player/commit/26411b9a29ddcdbb8fec9f8e244860d59f23601b)

#### [v0.20.0](https://github.com/cookpete/react-player/compare/v0.19.1...v0.20.0)

> 27 July 2017

- Allow seekTo to accept number of seconds [`#75`](https://github.com/CookPete/react-player/issues/75)
- Pass errors when playing files through to onError callback [`#200`](https://github.com/CookPete/react-player/issues/200)
- Add support for subtitle tracks in FilePlayer [`#214`](https://github.com/CookPete/react-player/issues/214)
- Unmute facebook video when setting volume [`#215`](https://github.com/CookPete/react-player/issues/215)
- Bump packages [`8e9e57b`](https://github.com/cookpete/react-player/commit/8e9e57b37f18ec4358bc5bc940f5f7aed2f59a19)

#### [v0.19.1](https://github.com/cookpete/react-player/compare/v0.19.0...v0.19.1)

> 17 July 2017

- More accurate played fraction for Wistia [`#201`](https://github.com/cookpete/react-player/pull/201)

#### [v0.19.0](https://github.com/cookpete/react-player/compare/v0.18.0...v0.19.0)

> 13 July 2017

- Detatch HLS media when stopping [`#212`](https://github.com/CookPete/react-player/issues/212)
- Add wistia player options [`#198`](https://github.com/CookPete/react-player/pull/198)
- Pass through auto or falsey width and height to FilePlayer [`#211`](https://github.com/CookPete/react-player/issues/211)
- Only use add-module-exports plugin for bower build [`#193`](https://github.com/CookPete/react-player/issues/193) [`#196`](https://github.com/CookPete/react-player/pull/196)
- Bump packages [`7372bcb`](https://github.com/cookpete/react-player/commit/7372bcba6c737e195a3df1d772622f5324cff619)
- Remove need for findDOMNode in Vimeo player [`f6abc06`](https://github.com/cookpete/react-player/commit/f6abc065deb0d526706170c91436f344de8ddc88)
- Add streaming tools to readme [`adf1338`](https://github.com/cookpete/react-player/commit/adf1338f709bdb6eab5486b57f6bfbc2b9df3122)

#### [v0.18.0](https://github.com/cookpete/react-player/compare/v0.17.2...v0.18.0)

> 9 May 2017

- Add support for HLS and DASH streams [`c28c7ff`](https://github.com/cookpete/react-player/commit/c28c7ff637298e6d9ecee588741980b9e0481bfc)
- Bump packages [`6f8da1f`](https://github.com/cookpete/react-player/commit/6f8da1f1f6f643953d0e5c97635addf320c80da7)

#### [v0.17.2](https://github.com/cookpete/react-player/compare/v0.17.1...v0.17.2)

> 7 May 2017

- Add forceAudio to fileConfig [`#188`](https://github.com/CookPete/react-player/issues/188)

#### [v0.17.1](https://github.com/cookpete/react-player/compare/v0.17.0...v0.17.1)

> 5 May 2017

- Fix initial loading bug for Private Vimeo videos [`#187`](https://github.com/cookpete/react-player/pull/187)

#### [v0.17.0](https://github.com/cookpete/react-player/compare/v0.16.0...v0.17.0)

> 30 April 2017

- Add support for m.youtube.com [`#186`](https://github.com/CookPete/react-player/issues/186)
- Add support for multiple file sources [`#81`](https://github.com/CookPete/react-player/issues/81)
- Remove need for Vimeo player IDs [`214a7af`](https://github.com/cookpete/react-player/commit/214a7afebaf00cd3c5b0af06cc63efb0786bd439)

#### [v0.16.0](https://github.com/cookpete/react-player/compare/v0.15.0...v0.16.0)

> 27 April 2017

- Apply all non-valid props to the wrapper element [`#183`](https://github.com/cookpete/react-player/pull/183)
- Ensure YouTube player is in DOM when stopping [`#180`](https://github.com/CookPete/react-player/pull/180)
- Use new Vimeo player API [`#142`](https://github.com/CookPete/react-player/pull/142)
- Fix FilePlayer seekTo bug [`#149`](https://github.com/CookPete/react-player/pull/149)
- Add Facebook player [`#109`](https://github.com/CookPete/react-player/issues/109)
- Apply all non-valid props to the wrapper element [`#167`](https://github.com/CookPete/react-player/issues/167)
- Added DailyMotion support. [`71dcda6`](https://github.com/cookpete/react-player/commit/71dcda6c3d0d1705760f3196fd28810be5e86109)
- DailyMotion tweaks [`64f3cd3`](https://github.com/cookpete/react-player/commit/64f3cd3add0ec6dcea3a0980963676e3b8aa12f0)
- Bump babel-loader [`478c2c3`](https://github.com/cookpete/react-player/commit/478c2c3e987bb849d7e00ffc613f4cac5f3fd949)

#### [v0.15.0](https://github.com/cookpete/react-player/compare/v0.14.3...v0.15.0)

> 14 April 2017

- Add vidmeConfig with choice of video format [`#162`](https://github.com/CookPete/react-player/issues/162)
- Call player.load() when loading files [`#177`](https://github.com/CookPete/react-player/issues/177)
- Add getCurrentTime and getDuration methods [`#178`](https://github.com/CookPete/react-player/issues/178)
- Use prop-types library [`#179`](https://github.com/CookPete/react-player/issues/179)
- Add loadedSeconds and playedSeconds to onProgress [`#164`](https://github.com/CookPete/react-player/issues/164)
- Add playsinline prop [`#148`](https://github.com/CookPete/react-player/issues/148)
- Bump packages [`41aac30`](https://github.com/cookpete/react-player/commit/41aac30d82255e2a530c0e131c5a615f2ca6e394)
- Update to standard 10 [`1371cbc`](https://github.com/cookpete/react-player/commit/1371cbc344b7b932a4579de23c188dad02f2bdcb)
- Bump snazzy [`90c60b3`](https://github.com/cookpete/react-player/commit/90c60b3722b81f15bfab3cc926153b7bb66631af)

#### [v0.14.3](https://github.com/cookpete/react-player/compare/v0.14.2...v0.14.3)

> 21 February 2017

- Add assertion for canPlay with https://vimeo [`#141`](https://github.com/cookpete/react-player/pull/141)
- Use npm 4.x on travis [`#143`](https://github.com/cookpete/react-player/pull/143)
- Use ubuntu 14.04 for travis [`#144`](https://github.com/cookpete/react-player/pull/144)
- fixed width & height of video when it is in fullscreen mode [`#151`](https://github.com/cookpete/react-player/pull/151)
- update webpack to 2.2.1 [`#156`](https://github.com/cookpete/react-player/pull/156)
- Fix vidme url regex [`#158`](https://github.com/CookPete/react-player/issues/158)
- Bump packages [`92b9315`](https://github.com/cookpete/react-player/commit/92b9315f02f2b364b6cd59146e5c03587464cc5a)
- Minor code style fixes [`b39b1ec`](https://github.com/cookpete/react-player/commit/b39b1ec05690610a091bdf229de03d71782ea059)
- Add assertion for making sure canPlay is valid for https://vimeo [`0f55002`](https://github.com/cookpete/react-player/commit/0f550029c92754c193cb5b4adb835d960c2af1fe)

#### [v0.14.2](https://github.com/cookpete/react-player/compare/v0.14.1...v0.14.2)

> 14 February 2017

- Fix youtube player vars config usage [`#152`](https://github.com/CookPete/react-player/issues/152)
- Fix npm prepublish script on windows [`#139`](https://github.com/CookPete/react-player/issues/139)
- Use cross-env for build scripts [`a6191cf`](https://github.com/cookpete/react-player/commit/a6191cf1d0c14453c88deccfc81db63a60a3a1e0)

#### [v0.14.1](https://github.com/cookpete/react-player/compare/v0.14.0...v0.14.1)

> 1 January 2017

- Use video element for ogg files [`#137`](https://github.com/CookPete/react-player/issues/137)

#### [v0.14.0](https://github.com/cookpete/react-player/compare/v0.13.0...v0.14.0)

> 26 December 2016

- Wistia support [`#133`](https://github.com/cookpete/react-player/pull/133)
- Add yarn.lock [`690d031`](https://github.com/cookpete/react-player/commit/690d031a72a9057ee9313a05892f92343845593e)
- Tidy up Wistia player [`9c82bfd`](https://github.com/cookpete/react-player/commit/9c82bfd332076fabb5e549e6e2c97cc7d6666a03)
- Bump node-sass [`9d4689e`](https://github.com/cookpete/react-player/commit/9d4689ee8da2016d942b23e5a2b74d9df0d4e19c)

#### [v0.13.0](https://github.com/cookpete/react-player/compare/v0.12.1...v0.13.0)

> 12 December 2016

- PlaybackRate change feature [`#123`](https://github.com/cookpete/react-player/pull/123)
- Fix onProgress non-reporting if playing prop is not set. [`#127`](https://github.com/cookpete/react-player/pull/127)
- Add additional audio file extensions to regex [`#131`](https://github.com/cookpete/react-player/pull/131)
- Use node 6 on travis [`#130`](https://github.com/cookpete/react-player/pull/130)
- Add typescript definition file [`#134`](https://github.com/CookPete/react-player/issues/134)
- [feature] Change PlaybackRate [`d28c309`](https://github.com/cookpete/react-player/commit/d28c309529f59d961238d549c3391af7ea08a44f)
- Fix incorrect duration calculation in demo app. [`4114677`](https://github.com/cookpete/react-player/commit/411467726be66f7f5d7fdba826da76323ef6883f)
- Remove unnecessary escape characters [`ec078d1`](https://github.com/cookpete/react-player/commit/ec078d12a06730f5665b5e1786eb60751817a4f0)

#### [v0.12.1](https://github.com/cookpete/react-player/compare/v0.12.0...v0.12.1)

> 19 November 2016

- Add fullscreen example to demo [`563252b`](https://github.com/cookpete/react-player/commit/563252be94cafd5d973271f56c91fda4b1f87e4f)
- Fullscreen youtube player fix [`83e8e60`](https://github.com/cookpete/react-player/commit/83e8e60a018e85bb27aa907360142f0a63761d54)

#### [v0.12.0](https://github.com/cookpete/react-player/compare/v0.11.0...v0.12.0)

> 10 October 2016

- Pass DOM node to YouTube Player instead of ID [`#111`](https://github.com/cookpete/react-player/pull/111)
- update all dependencies [`#107`](https://github.com/cookpete/react-player/pull/107)
- Add showArtwork option to soundcloud config [`#108`](https://github.com/CookPete/react-player/issues/108)
- Tidy up code [`0f18e71`](https://github.com/cookpete/react-player/commit/0f18e712935dfe50dafb4f40b3794e7150c4b476)
- Add Vidme support [`609ee08`](https://github.com/cookpete/react-player/commit/609ee0845199879141dd89b20d3fbc996c20fdee)
- Remove anonymous ref callback functions [`26aec63`](https://github.com/cookpete/react-player/commit/26aec63f05bfeba28536645dba5f0cbf0c5891a8)

#### [v0.11.0](https://github.com/cookpete/react-player/compare/v0.10.0...v0.11.0)

> 26 September 2016

- Add onReady prop [`#106`](https://github.com/CookPete/react-player/issues/106)

#### [v0.10.0](https://github.com/cookpete/react-player/compare/v0.9.0...v0.10.0)

> 25 September 2016

- Add `hidden` prop to ReactPlayer [`#102`](https://github.com/cookpete/react-player/pull/102)
- Use mounted property to prevent setting state when unmounted [`#105`](https://github.com/CookPete/react-player/issues/105)
- Update onProgress logic to still report loaded amounts when paused [`#94`](https://github.com/CookPete/react-player/issues/94)
- Remove brackets from single param arrow functions [`ab24d08`](https://github.com/cookpete/react-player/commit/ab24d085b4ad72bb6e0c9ea43ca59e69be0d88f3)
- Add Streamable player [`ff6a300`](https://github.com/cookpete/react-player/commit/ff6a3006ec8960d51a884762ce2f767857ec01cf)
- Use ref callbacks instead of ref strings [`6c16ba0`](https://github.com/cookpete/react-player/commit/6c16ba0f44d0403ae3e089ca1dafed0077b095e7)

#### [v0.9.0](https://github.com/cookpete/react-player/compare/v0.8.0...v0.9.0)

> 9 August 2016

- Ensure YouTube SDK is loaded before resolving promise [`#88`](https://github.com/CookPete/react-player/issues/88)
- Fix looping behaviour [`#91`](https://github.com/CookPete/react-player/issues/91)
- Add style prop [`#92`](https://github.com/CookPete/react-player/issues/92)
- Skip tests before versioning [`ed6cab0`](https://github.com/cookpete/react-player/commit/ed6cab0cc983efaf0ef8813b77a0a3a407091726)

#### [v0.8.0](https://github.com/cookpete/react-player/compare/v0.7.5...v0.8.0)

> 29 July 2016

- Add support for non-es5 compatible browsers [`#87`](https://github.com/cookpete/react-player/pull/87)
- Render FilePlayer by default [`8d249ce`](https://github.com/cookpete/react-player/commit/8d249cedb234f63e59858840a5ea40b899632177)
- Add babel es3 plugins [`26abe2c`](https://github.com/cookpete/react-player/commit/26abe2ccb66e9677a20baa5f6a0da64262f53ef8)
- Bump packages [`2d43659`](https://github.com/cookpete/react-player/commit/2d436592fedbba4b92653e1f929d5a31ccf3b14d)

#### [v0.7.5](https://github.com/cookpete/react-player/compare/v0.7.4...v0.7.5)

> 27 July 2016

- Always send both played and loaded values in progress events [`#79`](https://github.com/CookPete/react-player/issues/79)
- Add RTSP stream support to FilePlayer [`#83`](https://github.com/CookPete/react-player/issues/83)
- Move initial setVolume to onStart [`#82`](https://github.com/CookPete/react-player/issues/82)
- Update player when width or height changes [`#84`](https://github.com/CookPete/react-player/issues/84)
- Stop progress loop on pause [`180039b`](https://github.com/cookpete/react-player/commit/180039b19b3975e7e0ecae1202ccb304d829fba7)

#### [v0.7.4](https://github.com/cookpete/react-player/compare/v0.7.3...v0.7.4)

> 23 June 2016

- Pause FilePlayer when stopping [`#78`](https://github.com/CookPete/react-player/issues/78)

#### [v0.7.3](https://github.com/cookpete/react-player/compare/v0.7.2...v0.7.3)

> 21 June 2016

- Pass fileConfig down correctly [`1dffcca`](https://github.com/cookpete/react-player/commit/1dffccafca1f3440068a66e661a1fced9cde1593)

#### [v0.7.2](https://github.com/cookpete/react-player/compare/v0.7.1...v0.7.2)

> 3 June 2016

- Add m4v and m4a extensions to filePlayer [`#72`](https://github.com/CookPete/react-player/issues/72)
- Fix onDuration being called with null [`#52`](https://github.com/CookPete/react-player/issues/52)
- Rename seekOnReady to seekOnPlay [`91f7344`](https://github.com/cookpete/react-player/commit/91f73449e835c5e7bda30889e909510118f8c84f)
- Add failing onDuration test [`564243e`](https://github.com/cookpete/react-player/commit/564243e19449326eab30c163f9ffb82678fcf502)
- Nest media switching test in a describe block [`2fe8806`](https://github.com/cookpete/react-player/commit/2fe8806996b915f59d5105b2f304018152c75a0b)

#### [v0.7.1](https://github.com/cookpete/react-player/compare/v0.7.0...v0.7.1)

> 1 June 2016

- Update supported file type regex [`#68`](https://github.com/cookpete/react-player/pull/68)
- Update README.md links [`#66`](https://github.com/cookpete/react-player/pull/66)
- Tweak example usage [`eff2623`](https://github.com/cookpete/react-player/commit/eff2623b302312116ff0a6bd2e60aca04f2473e6)
- make file extensions case insensitive [`6f0f346`](https://github.com/cookpete/react-player/commit/6f0f34682106bafa40b9a68de2d77d4f9425b1f5)
- Update supported media readme [`c37e87e`](https://github.com/cookpete/react-player/commit/c37e87ee92665facf96e0d9c08ca5dfc654b99ec)

#### [v0.7.0](https://github.com/cookpete/react-player/compare/v0.6.0...v0.7.0)

> 21 May 2016

- Use filePlayer for SoundCloud tracks [`#64`](https://github.com/CookPete/react-player/issues/64)
- Add controls prop [`9a2ae22`](https://github.com/cookpete/react-player/commit/9a2ae229283e0c41ceea62ba01cdac243cf2bb82)
- Tidy up filePlayer [`79c2d5a`](https://github.com/cookpete/react-player/commit/79c2d5a16f999628493c3ac7ce052960ae5be67a)
- Skip FilePlayer onError test [`c4ed490`](https://github.com/cookpete/react-player/commit/c4ed49093f307ea7c844987d1f9e398a4dea5596)

#### [v0.6.0](https://github.com/cookpete/react-player/compare/v0.5.8...v0.6.0)

> 19 May 2016

- Add filePlayer config prop [`#62`](https://github.com/CookPete/react-player/issues/62)
- Add loop prop [`#57`](https://github.com/CookPete/react-player/issues/57)
- Add onStart prop [`#61`](https://github.com/CookPete/react-player/issues/61)
- Use fetch-jsonp for SoundCloud resolve endpoint [`#24`](https://github.com/CookPete/react-player/issues/24) [`#47`](https://github.com/CookPete/react-player/issues/47)
- Remove class property semicolons [`0c8070d`](https://github.com/cookpete/react-player/commit/0c8070dbac394d40268b2df505672fbbd8befc2a)
- Remove arrow function brackets [`cb71f30`](https://github.com/cookpete/react-player/commit/cb71f30975407638445e1038a1ee45edce73b021)
- Add contribution guidelines [`16f515d`](https://github.com/cookpete/react-player/commit/16f515ddf693c18d5b5916b3f85ffb0cca551e3c)

#### [v0.5.8](https://github.com/cookpete/react-player/compare/v0.5.7...v0.5.8)

> 19 April 2016

- Bump packages [`#56`](https://github.com/CookPete/react-player/issues/56)
- Better canPlay test grouping [`f528ade`](https://github.com/cookpete/react-player/commit/f528ade2ef7a8403c3a517fa52818dce6f07f6f7)
- Move all tests to Karma [`bbefce0`](https://github.com/cookpete/react-player/commit/bbefce019a8620829c6f663d6536a15caa587ea1)
- Add footer to demo [`546678c`](https://github.com/cookpete/react-player/commit/546678c33c2ea1331ed7f63712f40687c83b73be)

#### [v0.5.7](https://github.com/cookpete/react-player/compare/v0.5.6...v0.5.7)

> 13 April 2016

- Pass on error event [`#48`](https://github.com/cookpete/react-player/pull/48)
- Add FilePlayer support for urls with query string [`#45`](https://github.com/cookpete/react-player/pull/45)
- Add progressFrequency prop [`#50`](https://github.com/CookPete/react-player/issues/50)
- Only include played amount in onProgress when playing [`#51`](https://github.com/CookPete/react-player/issues/51)
- Pass on event including error message and code onError [`#44`](https://github.com/cookpete/react-player/issues/44)
- Add default props to readme [`2abb25e`](https://github.com/cookpete/react-player/commit/2abb25e68b6e5caa7d68e3cbfeab63789add2728)
- Ignore dist when linting [`1740ca6`](https://github.com/cookpete/react-player/commit/1740ca6cb58d8b6f274074f364e385f2b107292b)
- Specify https in YouTube SDK location [`c05bd9d`](https://github.com/cookpete/react-player/commit/c05bd9dff3a8595c5bb2e53c33ddaf8db8c894de)

#### [v0.5.6](https://github.com/cookpete/react-player/compare/v0.5.5...v0.5.6)

> 8 March 2016

- Fix bower support [`#42`](https://github.com/CookPete/react-player/issues/42)
- Update readme with mobile considerations [`#41`](https://github.com/CookPete/react-player/issues/41)
- Remove comments from production builds [`03a4e22`](https://github.com/cookpete/react-player/commit/03a4e22a80f8c64fdc31acc33829b88ac15c85d5)
- Fix browser build [`78463b2`](https://github.com/cookpete/react-player/commit/78463b2592a12eac990673a991aada66f86b1434)
- Bump karma-mocha-reporter [`98bc28a`](https://github.com/cookpete/react-player/commit/98bc28a9d9200f6ce0d4545abc352481701839cd)

#### [v0.5.5](https://github.com/cookpete/react-player/compare/v0.5.4...v0.5.5)

> 29 February 2016

- Add browser build [`#35`](https://github.com/CookPete/react-player/issues/35) [`#38`](https://github.com/CookPete/react-player/issues/38)
- Add promise and fetch polyfills to build [`#39`](https://github.com/CookPete/react-player/issues/39)
- Update standard code style [`7231c45`](https://github.com/cookpete/react-player/commit/7231c458fb0af80370f308d3d77aab71c226aac6)
- Add bower.json [`e95ee66`](https://github.com/cookpete/react-player/commit/e95ee66ffeeeb50bcf2e7666c1b90fa2676d6688)
- Add react-component keyword [`fb50625`](https://github.com/cookpete/react-player/commit/fb506250f35efaa228f34d49ad67f2a0a1a36d44)

#### [v0.5.4](https://github.com/cookpete/react-player/compare/v0.5.3...v0.5.4)

> 3 February 2016

- Remove the need for array.find polyfill [`7aa4719`](https://github.com/cookpete/react-player/commit/7aa471943def19d9e9b0abab3cf1172cb1db1cb1)
- Add demo deployment to travis build [`60bcaf6`](https://github.com/cookpete/react-player/commit/60bcaf6aa7c0b100a514c495ff521a42cdfe1235)
- Tweak YouTube default playerVars [`b99e6d9`](https://github.com/cookpete/react-player/commit/b99e6d94e7534e7a8d8e434eaf4473b4ca480601)

#### [v0.5.3](https://github.com/cookpete/react-player/compare/v0.5.2...v0.5.3)

> 26 January 2016

- Support YouTube start time param [`7908463`](https://github.com/cookpete/react-player/commit/7908463d2dc1d3f59910432edd45460da152d426)
- Better karma test grouping [`8a3d4e8`](https://github.com/cookpete/react-player/commit/8a3d4e83944466945002634c84a1c1bfa5035fd9)
- Better SoundCloud error handling [`bce57af`](https://github.com/cookpete/react-player/commit/bce57af06f3296587f75dea7e5a87d4da2c8e6a9)

#### [v0.5.2](https://github.com/cookpete/react-player/compare/v0.5.1...v0.5.2)

> 25 January 2016

- Vimeo fullscreen support [`#33`](https://github.com/CookPete/react-player/issues/33)
- Tweak readme [`a741b2c`](https://github.com/cookpete/react-player/commit/a741b2c381dae449916895ce13f81e14dccf3afa)
- Add preload to propTypes [`ad05a91`](https://github.com/cookpete/react-player/commit/ad05a91622fd050b69f290720a7a7fe9b8881df7)

#### [v0.5.1](https://github.com/cookpete/react-player/compare/v0.5.0...v0.5.1)

> 21 January 2016

- Move onDuration callback from onReady to onPlay [`#31`](https://github.com/CookPete/react-player/issues/31)
- Destructure state in demo render method [`6ed327d`](https://github.com/cookpete/react-player/commit/6ed327db830a9b0ea98acf111b94122b6f4c8418)
- Add simple Duration component to improve demo [`d42c6d3`](https://github.com/cookpete/react-player/commit/d42c6d3c4c1e323e8d856b0a2b2c95e526d55e81)
- Change onProgress frequency to 1 second [`45746d9`](https://github.com/cookpete/react-player/commit/45746d96ebef9a290fca4a88deaeaa83843ca4ad)

#### [v0.5.0](https://github.com/cookpete/react-player/compare/v0.4.2...v0.5.0)

> 14 January 2016

- Add onDuration prop [`#28`](https://github.com/CookPete/react-player/issues/28)
- Ensure YouTube player calls onReady on subsequent loads [`3a997bb`](https://github.com/cookpete/react-player/commit/3a997bb63878ccff551a12e8e076f63b8cf208a3)
- Ensure vimeo player has duration before calling onReady [`8b31a8f`](https://github.com/cookpete/react-player/commit/8b31a8ff77aa57b611996670b9b09d2d9b9aeae8)
- Add live demo to readme [`4aa8a29`](https://github.com/cookpete/react-player/commit/4aa8a292f2e3ee20f79e4a815d09f353da6a553d)

#### [v0.4.2](https://github.com/cookpete/react-player/compare/v0.4.1...v0.4.2)

> 11 January 2016

- Add semicolons to class properties [`#27`](https://github.com/CookPete/react-player/issues/27)
- Limit Travis to just basic tests [`#26`](https://github.com/CookPete/react-player/issues/26)
- Bump packages [`21f03b2`](https://github.com/cookpete/react-player/commit/21f03b2309951456c86567c1ff254628814af224)

#### [v0.4.1](https://github.com/cookpete/react-player/compare/v0.4.0...v0.4.1)

> 11 January 2016

- Add more files to .npmignore [`1cd8052`](https://github.com/cookpete/react-player/commit/1cd80526d32a5b060208b3d1e7883adcfcd7cfe6)
- Update auto-changelog [`1cebd28`](https://github.com/cookpete/react-player/commit/1cebd28b25d66713204b5920052c8979d84c234d)
- Fix npm badge link [`f6a146b`](https://github.com/cookpete/react-player/commit/f6a146b3904934dcd10287100400e54aeeb50541)

#### [v0.4.0](https://github.com/cookpete/react-player/compare/v0.3.7...v0.4.0)

> 9 January 2016

- Use react-transform development setup [`#25`](https://github.com/cookpete/react-player/pull/25)
- Add basic karma testing [`#22`](https://github.com/CookPete/react-player/issues/22)
- Rebase on react-transform-boilerplate [`#8`](https://github.com/CookPete/react-player/issues/8)
- Add styles and various fixes to the demo app [`0b16606`](https://github.com/cookpete/react-player/commit/0b16606615252a2584f6380912208b11b1df81c4)
- Use a partial shrinkwrap to limit babel packages to 6.3 [`e9bcd72`](https://github.com/cookpete/react-player/commit/e9bcd72a16554231d0b9079944b3b99934011fbc)
- Separate demo files [`c7230e7`](https://github.com/cookpete/react-player/commit/c7230e71d69d4e06b4de266c692b457e7a7c421f)

#### [v0.3.7](https://github.com/cookpete/react-player/compare/v0.3.6...v0.3.7)

> 4 January 2016

- When seeking before player is ready, store value and seek later [`#19`](https://github.com/CookPete/react-player/issues/19)
- Remove redundant soundcloud player options [`647fa9e`](https://github.com/cookpete/react-player/commit/647fa9ef1eb0150dd322614ad74b339439830b50)
- Prevent IndexSizeError in FilePlayer [`ba31958`](https://github.com/cookpete/react-player/commit/ba31958aeaf11da9a0ef56915db311b3c5881181)
- Fix FilePlayer not loading on mount [`00f9671`](https://github.com/cookpete/react-player/commit/00f9671a12bf09d0160653391a680165d6098adf)

#### [v0.3.6](https://github.com/cookpete/react-player/compare/v0.3.5...v0.3.6)

> 4 January 2016

- Ensure volume on subsequent youtube loads [`5895663`](https://github.com/cookpete/react-player/commit/58956633754073e0c3d9316da9cadfd4a49a823c)

#### [v0.3.5](https://github.com/cookpete/react-player/compare/v0.3.4...v0.3.5)

> 4 January 2016

- Fix YouTube onReady bugs [`#21`](https://github.com/cookpete/react-player/pull/21)
- Set isReady before setVolume in onReady [`#23`](https://github.com/CookPete/react-player/issues/23)
- Handle YouTube URL change during SDK load [`#20`](https://github.com/CookPete/react-player/issues/20)
- Tidy up YouTube code [`64c952f`](https://github.com/cookpete/react-player/commit/64c952f31ca5397a13fbdbd35e3485dd2dbfc466)
- Lint and test before versioning [`fdf2fa3`](https://github.com/cookpete/react-player/commit/fdf2fa39136b1286d0859af9b86cc253b9354743)

#### [v0.3.4](https://github.com/cookpete/react-player/compare/v0.3.3...v0.3.4)

> 3 January 2016

- Update readme badges [`6d0503c`](https://github.com/cookpete/react-player/commit/6d0503c7d6706063394301af61db342caeff9272)
- Fix YouTube pause guard [`1f8d372`](https://github.com/cookpete/react-player/commit/1f8d3720239353c496db3076b306cc52e99607dd)

#### [v0.3.3](https://github.com/cookpete/react-player/compare/v0.3.2...v0.3.3)

> 2 January 2016

- Stronger guards for YouTube player methods [`f18792a`](https://github.com/cookpete/react-player/commit/f18792a2da3167f775d48481aa8eae150b747d4d)

#### [v0.3.2](https://github.com/cookpete/react-player/compare/v0.3.1...v0.3.2)

> 2 January 2016

- Use explicit isReady property to guard player methods [`#18`](https://github.com/CookPete/react-player/issues/18)
- Improve progress logic [`b0b3040`](https://github.com/cookpete/react-player/commit/b0b304049847d9568585bc11399fa6cfa4cab5dc)
- Remove propTypes and defaultProps from players [`796edd1`](https://github.com/cookpete/react-player/commit/796edd129a6aaea148dcd72ea897321e66cae0c1)
- Start using auto-changelog [`373b698`](https://github.com/cookpete/react-player/commit/373b698a49f2f35a37084fa3e59e4d0a7af971db)

#### [v0.3.1](https://github.com/cookpete/react-player/compare/v0.3.0...v0.3.1)

> 27 December 2015

- Use displayNames for players [`#17`](https://github.com/CookPete/react-player/issues/17)
- Fix changelog link [`f82b351`](https://github.com/cookpete/react-player/commit/f82b351aa6008d2cbb2e3bd95859d8a90a8e49d9)

#### [v0.3.0](https://github.com/cookpete/react-player/compare/v0.2.1...v0.3.0)

> 25 December 2015

- Prime players to enable autoplay when out of focus [`#13`](https://github.com/cookpete/react-player/pull/13)
- Enable multiple YouTube players [`#15`](https://github.com/CookPete/react-player/issues/15)
- Prime Youtube and Vimeo players with a blank video [`#7`](https://github.com/CookPete/react-player/issues/7)
- Move defaultProps to separate props file [`90ef334`](https://github.com/cookpete/react-player/commit/90ef33485369fc7892d11b2e4da04ffb64df1e99)
- Always render every player [`158fdde`](https://github.com/cookpete/react-player/commit/158fdde54b6188c9f9ca3034e9cb5bcc3fe3ff69)
- Move progress logic from players to top level component [`17fbef8`](https://github.com/cookpete/react-player/commit/17fbef87e82889f01d8257900f7edc55b05918a2)

#### [v0.2.1](https://github.com/cookpete/react-player/compare/v0.2.0...v0.2.1)

> 13 November 2015

- Remove autoplay and only play if props.playing is true when ready [`#10`](https://github.com/CookPete/react-player/issues/10)
- Be a bit more const-friendly [`a15700a`](https://github.com/cookpete/react-player/commit/a15700ac95349037ea6f4d9f52fddeff9530bbf5)
- Better npm publish cleanup scripts [`821be95`](https://github.com/cookpete/react-player/commit/821be95265fb774e904c24ff553997b94b7b2e42)
- Add onError support to FilePlayer [`63f1660`](https://github.com/cookpete/react-player/commit/63f1660890c8d4f6ce8912537167bc4fdf621020)

#### [v0.2.0](https://github.com/cookpete/react-player/compare/v0.1.0...v0.2.0)

> 6 November 2015

- Configuration [`#4`](https://github.com/cookpete/react-player/pull/4)
- Abort SoundCloud streaming if url changes during requests [`#6`](https://github.com/CookPete/react-player/issues/6)
- pass configuration options to players via props [`f1395d1`](https://github.com/cookpete/react-player/commit/f1395d134a05b36db1736179e0b58ac9bc6bd76c)
- Add change log [`28dfdad`](https://github.com/cookpete/react-player/commit/28dfdad7983f51a1c5271cbcfc6971d43fd78108)
- Update readme [`e99df95`](https://github.com/cookpete/react-player/commit/e99df95a0aea6cd0ad151fa063678244b10f05af)

#### [v0.1.0](https://github.com/cookpete/react-player/compare/v0.0.8...v0.1.0)

> 19 October 2015

- Add FilePlayer for HTML5 media files [`66482d5`](https://github.com/cookpete/react-player/commit/66482d51c38f3967c378160e31fcd29a28ec4616)
- Add very basic shallow rendering tests [`7beccbe`](https://github.com/cookpete/react-player/commit/7beccbec2746029ef068d3f990fcc349257092f6)
- Vimeo player improvements [`d308aa6`](https://github.com/cookpete/react-player/commit/d308aa62c70d902f95c1184d704f54ee1fcc98d3)

#### [v0.0.8](https://github.com/cookpete/react-player/compare/v0.0.7...v0.0.8)

> 20 September 2015

- Use findDOMNode [`#3`](https://github.com/CookPete/react-player/issues/3)
- Add basic unit tests for static canPlay method [`f1cde2c`](https://github.com/cookpete/react-player/commit/f1cde2c4fd6a939e944e82621908c26814a4b88b)
- Improve SoundCloud URL regex [`3c4c5fa`](https://github.com/cookpete/react-player/commit/3c4c5fabc4f7440cd9931bd984174baf0f62dcce)

#### [v0.0.7](https://github.com/cookpete/react-player/compare/v0.0.6...v0.0.7)

> 10 September 2015

- Guard against no iframe contentWindow in Vimeo player [`#2`](https://github.com/CookPete/react-player/pull/2)
- Set correct initial state in ReactPlayer [`#2`](https://github.com/CookPete/react-player/pull/2)
- Stop soundcloud track before playing another [`61bc61e`](https://github.com/cookpete/react-player/commit/61bc61e56fc5c7c2550d2a5e36bc072ae447329b)

#### [v0.0.6](https://github.com/cookpete/react-player/compare/v0.0.5...v0.0.6)

> 31 August 2015

- Use shouldComponentUpdate to prevent unnecessary rendering [`b7ca5a6`](https://github.com/cookpete/react-player/commit/b7ca5a66ff91578a49da76c5efeac5ddd45959db)
- Fix onProgress bug [`1bf018e`](https://github.com/cookpete/react-player/commit/1bf018ed5db4474ee9ba1f103e6df77983565875)

#### [v0.0.5](https://github.com/cookpete/react-player/compare/v0.0.4...v0.0.5)

> 27 August 2015

- Use external propTypes file [`225dfa0`](https://github.com/cookpete/react-player/commit/225dfa073e63206dca65202ce3cf4f23e56c84db)
- Add onError support [`df8dc61`](https://github.com/cookpete/react-player/commit/df8dc61582af5202e3eeb8d6141465be5ed1bf84)
- Add .npmignore [`81adedc`](https://github.com/cookpete/react-player/commit/81adedc585e4cf0b6380ccb08f3ff613e7eed6c6)

#### [v0.0.4](https://github.com/cookpete/react-player/compare/v0.0.3...v0.0.4)

> 27 August 2015

- Add Array.find() polyfill [`#1`](https://github.com/CookPete/react-player/issues/1)
- Add fetch polyfill libraries to package.json [`#1`](https://github.com/CookPete/react-player/issues/1)

#### [v0.0.3](https://github.com/cookpete/react-player/compare/v0.0.2...v0.0.3)

> 26 August 2015

- Update build script [`c9b627f`](https://github.com/cookpete/react-player/commit/c9b627ff9a2e146822d169c89b4ea265f2296f8e)
- Update readme [`2ec1b86`](https://github.com/cookpete/react-player/commit/2ec1b86aad52e8bf3080d607e140762e4e268216)

#### v0.0.2

> 24 August 2015

- First commit [`a4deecf`](https://github.com/cookpete/react-player/commit/a4deecfa421645e8e79ac9e33debe64d5b028dae)
- Move react package to dev and peer dependencies [`f42ea0c`](https://github.com/cookpete/react-player/commit/f42ea0c73683d4c6e486a89684b4d6bb633a6969)


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to ReactPlayer

Thanks for contributing to ReactPlayer!

Running the demo locally is relatively easy:

```bash
git clone https://github.com/CookPete/react-player.git
cd react-player
npm install # or yarn
npm start
open http://localhost:3000
```

## `dist` files

There is **no need** to build or commit files in `dist` after making changes. The `dist` files are only there for [bower](http://bower.io) support, and there is very little point in polluting every commit or pull request with the changes. The `dist` files will be automatically built and committed when new versions are released, so your changes will be included then.

## Linting

This project uses [standard](https://github.com/feross/standard) code style. Be sure to lint the code after making changes and fix any issues that come up.

```bash
npm run lint
```

## Testing

This project uses [karma](https://karma-runner.github.io) with [mocha](https://github.com/mochajs/mocha) and [chai](https://github.com/chaijs/chai) for testing in the browser. Be sure to test `ReactPlayer` after making changes and, if you’re feeling generous, add some tests of your own.

```bash
npm test
```


================================================
FILE: LICENSE.md
================================================
The MIT License

Copyright © Pete Cook http://cookpete.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: MIGRATING.md
================================================
## Migrating to `v3.0`

Breaking changes are in 🔥 __bold and on fire__.

### Some player providers are not supported yet

Since `v3.0` is a new architecture not all providers have been updated.
It is recommended to keep using `v2` and vote to add this provider to `v3` in [discussions](https://github.com/cookpete/react-player/discussions).
These include:

  - `Dailymotion`
  - `SoundCloud`
  - `Streamable`
  - `Twitch`
  - `Facebook`
  - `Mixcloud`
  - `Kaltura`

### Lazy players

As of `v3.0` all the players are lazy loaded by default. 
Due to the use of `lazy` and `Suspense`, 🔥 __React 16.6 or later is now required__.

### Player props

As of `v3.0` some player props are renamed to be closer to the native
[HTMLMediaElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement) naming.

- 🔥 __`url` => `src`__
- 🔥 __`playsinline` => `playsInline`__
- 🔥 __`progressInterval`__ deprecated
- 🔥 __`stopOnUnmount`__ deprecated
- 🔥 __`wrapper`__ is `undefined` by default. Set to `div` if you want a wrapper element.

### Player instance methods

As of `v3.0` use [`ref`](https://react.dev/learn/manipulating-the-dom-with-refs) to call instance methods on the player. See [the demo app](examples/react/src/App.tsx) for an example of this. Since `v3`, the instance methods aim to be 🔥 __compatible 
with the [HTMLMediaElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement) interface__.

### Player callback props

As of `v3.0` some player callback props are renamed to be closer to the native
[HTMLMediaElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement) event naming.

- 🔥 __`onProgress` => `onTimeUpdate` and `onProgress`__
- 🔥 __`onDuration` => `onDurationChange`__
- 🔥 __`onPlaybackRateChange` => `onRateChange`__
- 🔥 __`onSeek` => `onSeeking` and `onSeeked`__
- 🔥 __`onBuffer` => `onWaiting`__
- 🔥 __`onBufferEnd` => `onPlaying`__
- 🔥 __`onEnablePIP` => `onEnterPictureInPicture`__
- 🔥 __`onDisablePIP` => `onLeavePictureInPicture`__


## Migrating to `v2.0`

Breaking changes are in 🔥 __bold and on fire__.

### Lazy players

As of `v2.2`, if your build system supports `import()` statements, use `react-player/lazy` to [lazy load](https://reactjs.org/docs/code-splitting.html#reactlazy) the appropriate player for the `url` you pass in. This adds several `reactPlayer` chunks to your output, but reduces your main bundle size.

Due to the use of `lazy` and `Suspense`, 🔥 __React 16.6 or later is now required__.

```jsx
// Before
import ReactPlayer from 'react-player'

// After
import ReactPlayer from 'react-player/lazy'
```

Lazy players were the default import in `v2.1`, but moved to `react-player/lazy` in `v2.2` to avoid causing problems with common build systems.

### Single player imports

As of `v2.2`, the 🔥 __location of single player imports has changed__. Single players are not available in `v2.0` and `v2.1`.

```jsx
// Before
import ReactPlayer from 'react-player/lib/players/YouTube'

// After
import ReactPlayer from 'react-player/youtube'
```

### Preloading

The `preload` config option was originally added to solve a [very specific use case](https://github.com/CookPete/react-player/issues/7) a very long time ago. Modern browsers are trending towards disabling autoplay by default, which makes the preload behaviour quite useless. The implementation was also quite hacky, and added to the bundle size for a feature that seems to be very rarely used. For this reason, 🔥 __the `preload` option has been removed__.

### The `config` prop

🔥 __Deprecated config props have been removed.__ Previously these props still worked, but with a console warning.

```jsx
// Before
<ReactPlayer 
  youtubeConfig={{ playerVars: { showinfo: 1 } }} 
/>

// After
<ReactPlayer 
  config={{ youtube: { playerVars: { showinfo: 1 } }}} 
/>
```

It is also worth noting that you no longer need to use separate config keys for different players. For example, if you are only ever using one type of `url` you can put player-specific options directly inside `config`.

```jsx
// Before
<ReactPlayer 
  youtubeConfig={{ playerVars: { showinfo: 1 } }} 
/>

// After
<ReactPlayer 
  config={{ playerVars: { showinfo: 1 } }} 
/>
```

### `onReady` is invoked with the player instance

Previously, instance methods would be called using [refs](https://reactjs.org/docs/refs-and-the-dom.html). They still can, but in v2.0, `onReady` is called with the ReactPlayer instance, giving you the option of storing the instance and calling methods on it. This is especially useful when using `getInternalPlayer`.

```jsx
// Before
class Player extends Component {
  ref = player => {
    this.player = player            // Store a player that may not be ready for methods
    this.player.getInternalPlayer() // Returns null if player is not ready
  }
  handleReady = () => {
    this.player.getInternalPlayer() // Internal player now ready
  }
  render () {
    return (
      <ReactPlayer ref={this.ref} onReady={this.handleReady} />
    )
  }
}

// After
class Player extends Component {
  handleReady = player => {
    this.player = player            // Store a player that is ready for methods
    this.player.getInternalPlayer() // Internal player now ready
  }
  render () {
    return (
      <ReactPlayer onReady={this.handleReady} />
    )
  }
}


================================================
FILE: README.md
================================================
<h1 align='center'>
  ReactPlayer
</h1>

<p align='center'>
  <a href='https://www.npmjs.com/package/react-player'><img src='https://img.shields.io/npm/v/react-player.svg' alt='Latest npm version'></a>
  <a href='https://codecov.io/gh/CookPete/react-player'><img src='https://img.shields.io/codecov/c/github/cookpete/react-player.svg' alt='Test Coverage'></a>
  <a href='https://www.patreon.com/cookpete'><img src='https://img.shields.io/badge/sponsor-patreon-fa6854.svg' alt='Become a sponsor on Patreon'></a>
</p>

<p align='center'>
  A React component for playing a variety of URLs, including file paths, HLS, DASH, YouTube, Vimeo, Wistia and Mux.
</p>

---

> Version 3 of ReactPlayer is a major update with a new architecture and many new features. It is not backwards compatible with v2, so please see the [migration guide](MIGRATING.md) for details.


> Using Next.js and need to handle video upload/processing? Check out [next-video](https://github.com/muxinc/next-video).

### ✨ The future of ReactPlayer

Maintenance of ReactPlayer is being taken over by [Mux](https://www.mux.com). Mux is a [video api](https://www.mux.com/video-api) for developers. The team at Mux have worked on many highly respected projects and are committed to improving video tooling for developers.

ReactPlayer will remain open source, but with a higher rate of fixes and releases over time. Thanks to everyone in the community for your ongoing support.

### Usage

```bash
npm install react-player # or yarn add react-player
```

```jsx
import React from 'react'
import ReactPlayer from 'react-player'

// Render a YouTube video player
<ReactPlayer src='https://www.youtube.com/watch?v=LXb3EKWsInQ' />
```

If your build system supports `import()` statements and code splitting enable this to lazy load the appropriate player for the `src` you pass in. This adds several `reactPlayer` chunks to your output, but reduces your main bundle size.

Demo page: [`https://cookpete.github.io/react-player`](https://cookpete.github.io/react-player)

The component parses a URL and loads in the appropriate markup and external SDKs to play media from [various sources](#supported-media). [Props](#props) can be passed in to control playback and react to events such as buffering or media ending. See [the demo source](https://github.com/cookpete/react-player/blob/master/examples/react/src/App.tsx) for a full example.

For platforms without direct use of `npm` modules, a minified version of `ReactPlayer` is located in `dist` after installing. To generate this file yourself, checkout the repo and run `npm run build:dist`.

#### Autoplay

As of Chrome 66, [videos must be `muted` in order to play automatically](https://www.theverge.com/2018/3/22/17150870/google-chrome-autoplay-videos-sound-mute-update). Some players, like Facebook, cannot be unmuted until the user interacts with the video, so you may want to enable `controls` to allow users to unmute videos themselves. Please set `muted={true}`.

### Props

Prop | Description | Default
---- | ----------- | -------
`src` | The url of a video or song to play | `undefined`
`playing` | Set to `true` or `false` to play or pause the media | `undefined`
`preload` | Applies the `preload` attribute where supported | `undefined`
`playsInline` | Applies the `playsInline` attribute where supported | `false`
`disableRemotePlayback` | Applies the `disableRemotePlayback` attribute where supported | `false`
`crossOrigin` | Applies the `crossOrigin` attribute where supported | `undefined`
`loop` | Set to `true` or `false` to loop the media | `false`
`controls` | Set to `true` or `false` to display native player controls.<br/>&nbsp; ◦ &nbsp;For Vimeo videos, hiding controls must be enabled by the video owner. | `false`
`volume` | Set the volume of the player, between `0` and `1`<br/>&nbsp; ◦ &nbsp;`null` uses default volume on all players [`#357`](https://github.com/cookpete/react-player/issues/357) | `null`
`muted` | Mutes the player | `false`
`playbackRate` | Set the playback rate of the player<br />&nbsp; ◦ &nbsp;Only supported by YouTube, Wistia, and file paths | `1`
`pip` | Set to `true` or `false` to enable or disable [picture-in-picture mode](https://developers.google.com/web/updates/2018/10/watch-video-using-picture-in-picture)<br/>&nbsp; ◦ &nbsp;Only available when playing file URLs in [certain browsers](https://caniuse.com/#feat=picture-in-picture) | `false`
`width` | Set the width of the player | `320px`
`height` | Set the height of the player | `180px`
`style` | Add [inline styles](https://facebook.github.io/react/tips/inline-styles.html) to the root element | `{}`
`light` | Set to `true` to show just the video thumbnail, which loads the full player on click<br />&nbsp; ◦ &nbsp;Pass in an image URL to override the preview image | `false`
`fallback` | Element or component to use as a fallback if you are using lazy loading | `null`
`wrapper` | Element or component to use as the container element | `null`
`playIcon` | Element or component to use as the play icon in light mode
`previewTabIndex` | Set the tab index to be used on light mode | `0`

#### Callback props

Callback props take a function that gets fired on various player events:

Prop | Description
---- | -----------
`onClickPreview` | Called when user clicks the `light` mode preview
`onReady` | Called when media is loaded and ready to play. If `playing` is set to `true`, media will play immediately
`onStart` | Called when media starts playing
`onPlay` | Called when the `playing` prop is set to true
`onPlaying` | Called when media actually starts playing
`onProgress` | Called when media data is loaded
`onTimeUpdate` | Called when the media's current time changes
`onDurationChange` | Callback containing duration of the media, in seconds
`onPause` | Called when media is paused
`onWaiting` | Called when media is buffering and waiting for more data
`onSeeking` | Called when media is seeking
`onSeeked` | Called when media has finished seeking
`onRateChange` | Called when playback rate of the player changed<br />&nbsp; ◦ &nbsp;Only supported by YouTube, Vimeo ([if enabled](https://developer.vimeo.com/player/sdk/reference#playbackratechange)), Wistia, and file paths
`onEnded` | Called when media finishes playing<br />&nbsp; ◦ &nbsp;Does not fire when `loop` is set to `true`
`onError` | Called when an error occurs whilst attempting to play media
`onEnterPictureInPicture` | Called when entering picture-in-picture mode
`onLeavePictureInPicture` | Called when leaving picture-in-picture mode

#### Config prop

There is a single `config` prop to override settings for each type of player:

```jsx
<ReactPlayer
  src={src}
  config={{
    youtube: {
      color: 'white',
    },
  }}
/>
```

Settings for each player live under different keys:

Key | Options
--- | -------
`youtube` | https://developers.google.com/youtube/player_parameters#Parameters
`vimeo` | https://developer.vimeo.com/player/sdk/embed
`hls` | https://github.com/video-dev/hls.js/blob/master/docs/API.md#fine-tuning

### Methods

#### Static Methods

Method | Description
------ | -----------
`ReactPlayer.canPlay(src)` | Determine if a URL can be played. This does *not* detect media that is unplayable due to privacy settings, streaming permissions, etc. In that case, the `onError` prop will be invoked after attempting to play. Any URL that does not match any patterns will fall back to a native HTML5 media player.
`ReactPlayer.addCustomPlayer(CustomPlayer)` | Add a custom player. See [Adding custom players](#adding-custom-players)
`ReactPlayer.removeCustomPlayers()` | Remove any players that have been added using `addCustomPlayer()`

#### Instance Methods

Use [`ref`](https://react.dev/learn/manipulating-the-dom-with-refs) to call instance methods on the player. See [the demo app](examples/react/src/App.js) for an example of this. Since `v3`, the instance methods aim to be compatible 
with the [HTMLMediaElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement) interface.

### Advanced Usage

#### Custom player controls

By default ReactPlayer is a chromeless player. By setting the `controls` prop to `true`, you can enable the native controls for the player. However, the controls will look different for each player. The ones based on HTML5 media players will look like the native controls for that browser, while the ones based on third-party players will look like the native controls for that player.

```jsx
<ReactPlayer src='https://www.youtube.com/watch?v=LXb3EKWsInQ' controls />
```

If you like to add your own custom controls in a convenient way, you can use
[Media Chrome](https://github.com/muxinc/media-chrome). Media Chrome is a library that provides a set of UI components that can be used to quickly build custom media controls.

##### Simple example ([Codesandbox](https://codesandbox.io/p/sandbox/react-player-media-chrome-simple-nl3pg4))

```tsx
import ReactPlayer from "react-player";
import {
  MediaController,
  MediaControlBar,
  MediaTimeRange,
  MediaTimeDisplay,
  MediaVolumeRange,
  MediaPlaybackRateButton,
  MediaPlayButton,
  MediaSeekBackwardButton,
  MediaSeekForwardButton,
  MediaMuteButton,
  MediaFullscreenButton,
} from "media-chrome/react";

export default function Player() {
  return (
    <MediaController
      style={{
        width: "100%",
        aspectRatio: "16/9",
      }}
    >
      <ReactPlayer
        slot="media"
        src="https://stream.mux.com/maVbJv2GSYNRgS02kPXOOGdJMWGU1mkA019ZUjYE7VU7k"
        controls={false}
        style={{
          width: "100%",
          height: "100%",
          "--controls": "none",
        }}
      ></ReactPlayer>
      <MediaControlBar>
        <MediaPlayButton />
        <MediaSeekBackwardButton seekOffset={10} />
        <MediaSeekForwardButton seekOffset={10} />
        <MediaTimeRange />
        <MediaTimeDisplay showDuration />
        <MediaMuteButton />
        <MediaVolumeRange />
        <MediaPlaybackRateButton />
        <MediaFullscreenButton />
      </MediaControlBar>
    </MediaController>
  );
}
```

#### Light player

The `light` prop will render a video thumbnail with simple play icon, and only load the full player once a user has interacted with the image. [Noembed](https://noembed.com) is used to fetch thumbnails for a video URL. Note that automatic thumbnail fetching for Facebook, Wistia, Mixcloud and file URLs are not supported, and ongoing support for other URLs is not guaranteed.

If you want to pass in your own thumbnail to use, set `light` to the image URL rather than `true`.

You can also pass a component through the `light` prop:

```jsx
<ReactPlayer light={<img src='https://example.com/thumbnail.png' alt='Thumbnail' />} />
```

The styles for the preview image and play icon can be overridden by targeting the CSS classes `react-player__preview`, `react-player__shadow` and `react-player__play-icon`.

#### Responsive player

Set `width` to `100%`, `height` to `auto` and add an `aspectRatio` like `16 / 9` to get a responsive player:

```js
<ReactPlayer
  src="https://www.youtube.com/watch?v=LXb3EKWsInQ"
  style={{ width: '100%', height: 'auto', aspectRatio: '16/9' }}
/>
```

#### SDK Overrides

You can use your own version of any player SDK by using NPM resolutions. For example, to use a specific version of `hls.js`, add the following to your `package.json`:

```json
{
  "resolutions": {
    "hls.js": "1.6.2"
  }
}
```

#### Adding custom players

If you have your own player that is compatible with ReactPlayer’s internal architecture, you can add it using `addCustomPlayer`:

```javascript
import YourOwnPlayer from './somewhere';
ReactPlayer.addCustomPlayer(YourOwnPlayer);
```

Use `removeCustomPlayers` to clear all custom players:

```javascript
ReactPlayer.removeCustomPlayers();
```

It is your responsibility to ensure that custom players keep up with any internal changes to ReactPlayer in later versions.

#### Mobile considerations

Due to various restrictions, `ReactPlayer` is not guaranteed to function properly on mobile devices. The [YouTube player documentation](https://developers.google.com/youtube/iframe_api_reference), for example, explains that [certain mobile browsers require user interaction](https://developers.google.com/youtube/iframe_api_reference#Mobile_considerations) before playing:

> The HTML5 `<video>` element, in certain mobile browsers (such as Chrome and Safari), only allows playback to take place if it’s initiated by a user interaction (such as tapping on the player).

#### Multiple Sources and Tracks

Since `v3` if the player supports multiple sources and / or tracks, it works the same as the native 
`<source` and `<track>` elements in the HTML `<video>` or `<audio>` element. 

```jsx
<ReactPlayer controls>
  <source src="foo.webm" type="video/webm">
  <source src="foo.ogg" type="video/ogg">
  <track kind="subtitles" src="subs/subtitles.en.vtt" srclang="en" default>
  <track kind="subtitles" src="subs/subtitles.ja.vtt" srclang="ja">
  <track kind="subtitles" src="subs/subtitles.de.vtt" srclang="de">
</ReactPlayer>
```

### Migrating to `v3`

ReactPlayer `v3` is a major update with a new architecture and many new features. It is not backwards compatible with `v2`, so please see the [migration guide](MIGRATING.md) for details. 

Some providers have not been updated for `v3`, it is recommended to keep using `v2` and vote to add this provider to `v3` in [discussions](https://github.com/cookpete/react-player/discussions)

### Migrating to `v2`

ReactPlayer `v2` changes single player imports and adds lazy loading players. Support for `preload` has also been removed, plus some other changes. See [`MIGRATING.md`](/MIGRATING.md) for information.

### Supported media

* [Supported file types](https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats) are playing using [`<video>`](https://developer.mozilla.org/en/docs/Web/HTML/Element/video) or [`<audio>`](https://developer.mozilla.org/en/docs/Web/HTML/Element/audio) elements
* HLS streams are played using [`hls.js`](https://github.com/video-dev/hls.js)
* DASH streams are played using [`dash.js`](https://github.com/Dash-Industry-Forum/dash.js)
* Mux videos use the [`<mux-player>`](https://github.com/muxinc/elements/blob/main/packages/mux-player/README.md) element
* YouTube videos use the [YouTube iFrame Player API](https://developers.google.com/youtube/iframe_api_reference)
* Vimeo videos use the [Vimeo Player API](https://developer.vimeo.com/player/sdk)
* Wistia videos use the [Wistia Player API](https://wistia.com/doc/player-api)

### Contributing

See the [contribution guidelines](https://github.com/cookpete/react-player/blob/master/CONTRIBUTING.md) before creating a pull request.

### Thanks

- Thanks to anyone who has [contributed](https://github.com/cookpete/react-player/graphs/contributors).
- Big thanks to my [Patreon](https://patreon.com/cookpete) supporters!

<table>
  <tr>
    <td align='center'>
      <a href='https://the100.tv'><img src='https://the100.tv/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fthe100-tv-alone-2k.2e7c7877.png&w=384&q=75' width='120' /><br />Jackson Doherty</a>
    </td>
    <td align='center'>
      <a href='https://github.com/jaxomlotus'><img src='https://avatars.githubusercontent.com/u/485706?s=120&v=4' /><br />Joseph Fung</a>
    </td>
  </tr>
</table>


================================================
FILE: biome.json
================================================
{
  "files": {
    "include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"],
    "ignore": ["dist/**"]
  },
  "formatter": {
    "indentStyle": "space"
  }
}


================================================
FILE: codecov.yml
================================================
comment: false
coverage:
  parsers:
    javascript:
      enable_partials: yes
  status:
    project:
      default:
        enabled: false
    patch:
      default:
        enabled: false


================================================
FILE: examples/react/public/App.css
================================================
:root {
  --column-width: 480px;
  --gutter-width: 20px;
}

.app {
  margin: auto;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-weight: 300;
  text-align: center;
}

.section {
  display: inline-block;
  max-width: var(--column-width);
  margin: var(--gutter-width);
  text-align: left;
  vertical-align: top;
}

.player-wrapper {
  width: 480px;
  aspect-ratio: 16 / 9;
  background: rgba(0, 0, 0, .1);
  margin-bottom: 10px;
}

.faded {
  color: rgba(0, 0, 0,.6);
}

.footer {
  margin: var(--gutter-width);
}


================================================
FILE: examples/react/public/defaults.css
================================================
:root {
  /*
   * Variables with --color-base prefix define
   * the hue, and saturation values to be used for
   * hsla colors.
   *
   * ex:
   *
   * --color-base-{color}: {hue}, {saturation};
   *
   */

  --color-base-white: 0 0%;
  --color-base-black: 240 100%;
  --color-base-gray: 60 3%;

  /*
   * Color palettes are made using --color-base
   * variables, along with a lightness value to
   * define different variants.
   *
   */

  --color-gray-5: var(--color-base-gray) 5%;
  --color-gray-10: var(--color-base-gray) 10%;
  --color-gray-20: var(--color-base-gray) 20%;
  --color-gray-30: var(--color-base-gray) 30%;
  --color-gray-40: var(--color-base-gray) 40%;
  --color-gray-50: var(--color-base-gray) 50%;
  --color-gray-60: var(--color-base-gray) 60%;
  --color-gray-70: var(--color-base-gray) 70%;
  --color-gray-80: var(--color-base-gray) 80%;
  --color-gray-90: var(--color-base-gray) 90%;
  --color-gray-95: var(--color-base-gray) 95%;
}

body {
  margin-right: 10px;
  margin-left: 10px;
  font-size: 14px;
  line-height: 1.4;
}

em {
  font-style: italic;
}

body,
h1,
h2,
h3 {
  font-weight: 300;
  margin-bottom: 1em;
}

h1 { font-size: 20px; }
h2 { font-size: 16px; margin-top: 1em; }

table,
progress {
  width: 100%;
}

th,
td,
[type=text],
textarea {
  margin-right: 5px;
  padding: 3px 6px;
}

th {
  width: 10%;
  font-weight: 500;
  text-align: right;
  white-space: nowrap;
  vertical-align: middle;
}

[type=text],
textarea {
  width: 200px;
  padding: 5px;
  border: 1px solid hsl(var(--color-gray-70));
  border-radius: 3px;
  outline: 0;
}

[type=text]:focus,
textarea:focus {
  border-color: hsl(var(--color-gray-50));
  box-shadow: 0 0 5px hsl(var(--color-gray-90));
}

textarea {
  height: 100px;
  font-family: monospace;
  vertical-align: bottom;
}

button {
  cursor: pointer;
  margin: 3px;
  padding: 6px 12px;
  border: 0;
  border-radius: 3px;
  outline: 0;
  background-color: hsl(var(--color-gray-95));
}

button:focus {
  background-color: hsl(var(--color-gray-90));
}

button:hover {
  background-color: hsl(var(--color-gray-80));
}

button:active {
  background-color: hsl(var(--color-gray-70));
}


================================================
FILE: examples/react/public/index.html
================================================
<!doctype html>
<html lang='en'>
  <head>
    <meta charset='utf-8'>
    <meta name='viewport' content='width=device-width, initial-scale=1.0'>
    <title>ReactPlayer Demo</title>
    <meta name='description' content='A React component for playing a variety of URLs, including file paths, Mux, YouTube, Vimeo, and Wistia'>
    <link rel='stylesheet' href='./reset.css'>
    <link rel='stylesheet' href='./defaults.css'>
    <link rel='stylesheet' href='./range.css'>
    <link rel='stylesheet' href='./App.css'>
    <script type='module' src='./index.js'></script>
  </head>
  <body>
    <div id='app'></div>
  </body>
</html>


================================================
FILE: examples/react/public/range.css
================================================
/*
// Styling Cross-Browser Compatible Range Inputs with Sass
// Github: https://github.com/darlanrod/input-range-sass
// Author: Darlan Rod https://github.com/darlanrod
// Version 1.4.1
// MIT License
*/
/* Transformed to plain CSS and removed -ms selectors */

:root {
  --track-color: #eee;
  --thumb-color: #666;
  --thumb-radius: 12px;
  --thumb-height: 12px;
  --thumb-width: 12px;
  --thumb-shadow-size: 0;
  --thumb-shadow-blur: 0;
  --thumb-shadow-color: #111;
  --thumb-border-width: 0;
  --thumb-border-color: #fff;
  --track-width: 100%;
  --track-height: 10px;
  --track-shadow-size: 0;
  --track-shadow-blur: 0;
  --track-shadow-color: #222;
  --track-border-width: 0;
  --track-border-color: #000;
  --track-radius: 5px;
}

[type='range'] {
  -webkit-appearance: none;
  margin: var(--thumb-height) / 2 0;
  width: var(--track-width);
}

[type='range']:focus {
  outline: 0;
}

[type='range']::-webkit-slider-runnable-track {
  cursor: pointer;
  height: var(--track-height);
  transition: all 0.2s ease;
  width: var(--track-width);
  background: var(--track-color);
  border: var(--track-border-width) solid var(--track-border-color);
  border-radius: var(--track-radius);
  box-shadow: var(--track-shadow-size) var(--track-shadow-size) var(--track-shadow-blur) var(--track-shadow-color), 0 0 var(--track-shadow-size) lighten(var(--track-shadow-color), 5%);
}

[type='range']::-webkit-slider-thumb {
  -webkit-appearance: none;
  cursor: pointer;
  width: var(--thumb-width);
  height: var(--thumb-height);
  background: var(--thumb-color);
  border: var(--thumb-border-width) solid var(--thumb-border-color);
  border-radius: var(--thumb-radius);
  margin-top: calc((var(--track-border-width) * -2 + var(--track-height)) / 2 - (var(--thumb-height) / 2));
  box-shadow: var(--thumb-shadow-size) var(--thumb-shadow-size) var(--thumb-shadow-blur) var(--thumb-shadow-color);
}

[type='range']::-moz-range-track {
  cursor: pointer;
  height: var(--track-height);
  transition: all 0.2s ease;
  width: var(--track-width);
  background: var(--track-color);
  border: var(--track-border-width) solid var(--track-border-color);
  border-radius: var(--track-radius);
  box-shadow: var(--track-shadow-size) var(--track-shadow-size) var(--track-shadow-blur) var(--track-shadow-color);
}

[type='range']::-moz-range-thumb {
  cursor: pointer;
  width: var(--thumb-width);
  height: var(--thumb-height);
  background: var(--thumb-color);
  border: var(--thumb-border-width) solid var(--thumb-border-color);
  border-radius: var(--thumb-radius);
  box-shadow: var(--thumb-shadow-size) var(--thumb-shadow-size) var(--thumb-shadow-blur) var(--thumb-shadow-color);
}


================================================
FILE: examples/react/public/reset.css
================================================
/* http://meyerweb.com/eric/tools/css/reset/
 v2.0 | 20110126
 License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
  display: block;
}
body {
  line-height: 1;
}
ol, ul {
  list-style: none;
}
blockquote, q {
  quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
  content: '';
  content: none;
}
table {
  border-collapse: collapse;
  border-spacing: 0;
}


================================================
FILE: examples/react/src/App.tsx
================================================
// biome-ignore lint/style/useImportType:
import React, { useState, useRef, useCallback } from 'react';
import screenfull from 'screenfull';

import { version } from '../../../package.json';
import ReactPlayer from '../../../';
import Duration from './Duration';

const App = () => {
  const playerRef = useRef<HTMLVideoElement | null>(null);
  const urlInputRef = useRef<HTMLInputElement | null>(null);

  const initialState = {
    src: undefined,
    pip: false,
    playing: false,
    controls: false,
    light: false,
    volume: 1,
    muted: false,
    played: 0,
    loaded: 0,
    duration: 0,
    playbackRate: 1.0,
    loop: false,
    seeking: false,
    loadedSeconds: 0,
    playedSeconds: 0,
  };

  type PlayerState = Omit<typeof initialState, 'src'> & {
    src?: string;
  };

  const [state, setState] = useState<PlayerState>(initialState);

  const load = (src?: string) => {
    setState(prevState => ({
      ...prevState,
      src,
      played: 0,
      loaded: 0,
      pip: false,
    }));
  };

  const handlePlayPause = () => {
    setState(prevState => ({ ...prevState, playing: !prevState.playing }));
  };

  const handleStop = () => {
    setState(prevState => ({ ...prevState, src: undefined, playing: false }));
  };

  const handleToggleControls = () => {
    setState(prevState => ({ ...prevState, controls: !prevState.controls }));
  };

  const handleToggleLight = () => {
    setState(prevState => ({ ...prevState, light: !prevState.light }));
  };

  const handleToggleLoop = () => {
    setState(prevState => ({ ...prevState, loop: !prevState.loop }));
  };

  const handleVolumeChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
    const inputTarget = event.target as HTMLInputElement;
    setState(prevState => ({ ...prevState, volume: Number.parseFloat(inputTarget.value) }));
  };

  const handleToggleMuted = () => {
    setState(prevState => ({ ...prevState, muted: !prevState.muted }));
  };

  const handleSetPlaybackRate = (event: React.SyntheticEvent<HTMLButtonElement>) => {
    const buttonTarget = event.target as HTMLButtonElement;
    setState(prevState => ({
      ...prevState,
      playbackRate: Number.parseFloat(`${buttonTarget.dataset.value}`)
    }));
  };

  const handleRateChange = () => {
    const player = playerRef.current;
    if (!player) return;

    setState(prevState => ({ ...prevState, playbackRate: player.playbackRate }));
  };

  const handleTogglePIP = () => {
    setState(prevState => ({ ...prevState, pip: !prevState.pip }));
  };

  const handlePlay = () => {
    console.log('onPlay');
    setState(prevState => ({ ...prevState, playing: true }));
  };

  const handleEnterPictureInPicture = () => {
    console.log('onEnterPictureInPicture');
    setState(prevState => ({ ...prevState, pip: true }));
  };

  const handleLeavePictureInPicture = () => {
    console.log('onLeavePictureInPicture');
    setState(prevState => ({ ...prevState, pip: false }));
  };

  const handlePause = () => {
    console.log('onPause');
    setState(prevState => ({ ...prevState, playing: false }));
  };

  const handleSeekMouseDown = () => {
    setState(prevState => ({ ...prevState, seeking: true }));
  };

  const handleSeekChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
    const inputTarget = event.target as HTMLInputElement;
    setState(prevState => ({ ...prevState, played: Number.parseFloat(inputTarget.value) }));
  };

  const handleSeekMouseUp = (event: React.SyntheticEvent<HTMLInputElement>) => {
    const inputTarget = event.target as HTMLInputElement;
    setState(prevState => ({ ...prevState, seeking: false }));
    if (playerRef.current) {
      playerRef.current.currentTime = Number.parseFloat(inputTarget.value) * playerRef.current.duration;
    }
  };

  const handleProgress = () => {
    const player = playerRef.current;
    // We only want to update time slider if we are not currently seeking
    if (!player || state.seeking || !player.buffered?.length) return;

    console.log('onProgress');

    setState(prevState => ({
      ...prevState,
      loadedSeconds: player.buffered?.end(player.buffered?.length - 1),
      loaded: player.buffered?.end(player.buffered?.length - 1) / player.duration,
    }));
  };

  const handleTimeUpdate = () => {
    const player = playerRef.current;
    // We only want to update time slider if we are not currently seeking
    if (!player || state.seeking) return;

    console.log('onTimeUpdate', player.currentTime);

    if (!player.duration) return;

    setState(prevState => ({
      ...prevState,
      playedSeconds: player.currentTime,
      played: player.currentTime / player.duration,
    }));
  };

  const handleEnded = () => {
    console.log('onEnded');
    setState(prevState => ({ ...prevState, playing: prevState.loop }));
  };

  const handleDurationChange = () => {
    const player = playerRef.current;
    if (!player) return;

    console.log('onDurationChange', player.duration);
    setState(prevState => ({ ...prevState, duration: player.duration }));
  };

  const handleClickFullscreen = () => {
    const reactPlayer = document.querySelector('.react-player');
    if (reactPlayer) screenfull.request(reactPlayer);
  };

  const renderLoadButton = (src: string, label: string) => {
    return (
      <button type="button" onClick={() => load(src)}>
        {label}
      </button>
    );
  };

  const setPlayerRef = useCallback((player: HTMLVideoElement) => {
    if (!player) return;
    playerRef.current = player;
    console.log(player);
  }, []);

  const handleLoadCustomUrl = () => {
    if (urlInputRef.current?.value) {
      setState(prevState => ({ ...prevState, src: urlInputRef.current?.value }));
    }
  };

  const {
    src,
    playing,
    controls,
    light,
    volume,
    muted,
    loop,
    played,
    loaded,
    duration,
    playbackRate,
    pip,
  } = state;

  const SEPARATOR = ' · ';

  return (
    <div className="app">
      <section className="section">
        <h1>ReactPlayer Demo</h1>
        <div className="player-wrapper">
          <ReactPlayer
            ref={setPlayerRef}
            className="react-player"
            style={{ width: '100%', height: 'auto', aspectRatio: '16/9' }}
            src={src}
            pip={pip}
            playing={playing}
            controls={controls}
            light={light}
            loop={loop}
            playbackRate={playbackRate}
            volume={volume}
            muted={muted}
            config={{
              youtube: {
                color: 'white'
              },
              vimeo: {
                color: 'ffffff'
              },
              spotify: {
                preferVideo: true
              },
              tiktok: {
                fullscreen_button: true,
                progress_bar: true,
                play_button: true,
                volume_control: true,
                timestamp: false,
                music_info: false,
                description: false,
                rel: false,
                native_context_menu: true,
                closed_caption: false,
              }
            }}
            onLoadStart={() => console.log('onLoadStart')}
            onReady={() => console.log('onReady')}
            onStart={(e) => console.log('onStart', e)}
            onPlay={handlePlay}
            onEnterPictureInPicture={handleEnterPictureInPicture}
            onLeavePictureInPicture={handleLeavePictureInPicture}
            onPause={handlePause}
            onRateChange={handleRateChange}
            onSeeking={(e) => console.log('onSeeking', e)}
            onSeeked={(e) => console.log('onSeeked', e)}
            onEnded={handleEnded}
            onError={(e) => console.log('onError', e)}
            onTimeUpdate={handleTimeUpdate}
            onProgress={handleProgress}
            onDurationChange={handleDurationChange}
          />
        </div>

        <table>
          <tbody>
            <tr>
              <th>Controls</th>
              <td>
                <button type="button" onClick={handleStop}>
                  Stop
                </button>
                <button type="button" onClick={handlePlayPause}>
                  {playing ? 'Pause' : 'Play'}
                </button>
                <button type="button" onClick={handleClickFullscreen}>
                  Fullscreen
                </button>
                {src && ReactPlayer.canEnablePIP?.(src) && (
                  <button type="button" onClick={handleTogglePIP}>
                    {pip ? 'Disable PiP' : 'Enable PiP'}
                  </button>
                )}
              </td>
            </tr>
            <tr>
              <th>Speed</th>
              <td>
                <button type="button" onClick={handleSetPlaybackRate} data-value={1}>
                  1x
                </button>
                <button type="button" onClick={handleSetPlaybackRate} data-value={1.5}>
                  1.5x
                </button>
                <button type="button" onClick={handleSetPlaybackRate} data-value={2}>
                  2x
                </button>
              </td>
            </tr>
            <tr>
              <th><label htmlFor="seek">Seek</label></th>
              <td>
                <input
                  id="seek"
                  type="range"
                  min={0}
                  max={0.999999}
                  step="any"
                  value={played}
                  onMouseDown={handleSeekMouseDown}
                  onChange={handleSeekChange}
                  onMouseUp={handleSeekMouseUp}
                />
              </td>
            </tr>
            <tr>
              <th><label htmlFor="volume">Volume</label></th>
              <td>
                <input
                  id="volume"
                  type="range"
                  min={0}
                  max={1}
                  step="any"
                  value={volume}
                  onChange={handleVolumeChange}
                />
              </td>
            </tr>
            <tr>
              <th>
                <label htmlFor="controls">Controls</label>
              </th>
              <td>
                <input
                  id="controls"
                  type="checkbox"
                  checked={controls}
                  onChange={handleToggleControls}
                />
                <em>&nbsp; Requires player reload for some players</em>
              </td>
            </tr>
            <tr>
              <th>
                <label htmlFor="muted">Muted</label>
              </th>
              <td>
                <input
                  id="muted"
                  type="checkbox"
                  checked={muted}
                  onChange={handleToggleMuted}
                />
              </td>
            </tr>
            <tr>
              <th>
                <label htmlFor="loop">Loop</label>
              </th>
              <td>
                <input
                  id="loop"
                  type="checkbox"
                  checked={loop}
                  onChange={handleToggleLoop}
                />
              </td>
            </tr>
            <tr>
              <th>
                <label htmlFor="light">Light mode</label>
              </th>
              <td>
                <input
                  id="light"
                  type="checkbox"
                  checked={light}
                  onChange={handleToggleLight}
                />
              </td>
            </tr>
            <tr>
              <th>Played</th>
              <td>
                <progress max={1} value={played} />
              </td>
            </tr>
            <tr>
              <th>Loaded</th>
              <td>
                <progress max={1} value={loaded} />
              </td>
            </tr>
          </tbody>
        </table>
      </section>
      <section className="section">
        <table>
          <tbody>
            <tr>
              <th>HTML</th>
              <td>
                {renderLoadButton(
                  'https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/360/Big_Buck_Bunny_360_10s_1MB.mp4',
                  'mp4'
                )}
                {renderLoadButton(
                  'https://test-videos.co.uk/vids/bigbuckbunny/webm/vp8/360/Big_Buck_Bunny_360_10s_1MB.webm',
                  'webm'
                )}
                {renderLoadButton(
                  'https://filesamples.com/samples/video/ogv/sample_640x360.ogv',
                  'ogv'
                )}
                {renderLoadButton(
                  'https://storage.googleapis.com/media-session/elephants-dream/the-wires.mp3',
                  'mp3'
                )}
              </td>
            </tr>
            <tr>
              <th>HLS</th>
              <td>
                {renderLoadButton(
                  'https://stream.mux.com/VcmKA6aqzIzlg3MayLJDnbF55kX00mds028Z65QxvBYaA.m3u8',
                  'HLS (m3u8)'
                )}
              </td>
            </tr>
            <tr>
              <th>DASH</th>
              <td>
                {renderLoadButton(
                  'https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps_640x360_800k.mpd',
                  'DASH (mpd)'
                )}
              </td>
            </tr>
            <tr>
              <th>Mux</th>
              <td>
                {renderLoadButton(
                  'https://stream.mux.com/maVbJv2GSYNRgS02kPXOOGdJMWGU1mkA019ZUjYE7VU7k',
                  'Test A'
                )}
                {renderLoadButton(
                  'https://stream.mux.com/Sc89iWAyNkhJ3P1rQ02nrEdCFTnfT01CZ2KmaEcxXfB008',
                  'Test B'
                )}
              </td>
            </tr>
            <tr>
              <th>YouTube</th>
              <td>
                {renderLoadButton('https://www.youtube.com/watch?v=oUFJJNQGwhk', 'Test A')}
                {renderLoadButton('https://www.youtube.com/watch?v=jNgP6d9HraI', 'Test B')}
                {renderLoadButton('https://www.youtube.com/playlist?list=PLRfhDHeBTBJ7MU5DX4P_oBIRN457ah9lA', 'Playlist')}
              </td>
            </tr>
            <tr>
              <th>Vimeo</th>
              <td>
                {renderLoadButton('https://vimeo.com/90509568', 'Test A')}
                {renderLoadButton('https://vimeo.com/169599296', 'Test B')}
              </td>
            </tr>
            <tr>
              <th>Wistia</th>
              <td>
                {renderLoadButton('https://home.wistia.com/medias/e4a27b971d', 'Test A')}
                {renderLoadButton('https://home.wistia.com/medias/29b0fbf547', 'Test B')}
                {renderLoadButton('https://home.wistia.com/medias/bq6epni33s', 'Test C')}
              </td>
            </tr>
            <tr>
              <th>Spotify</th>
              <td>
                {renderLoadButton('https://open.spotify.com/episode/5Jo9ncrz2liWiKj8inZwD2', 'Test A')}
              </td>
            </tr>
            <tr>
              <th>Twitch</th>
              <td>
                {renderLoadButton('https://www.twitch.tv/videos/106400740', 'Test A')}
                {renderLoadButton('https://www.twitch.tv/kronovi', 'Test B')}
              </td>
            </tr>
            <tr>
              <th>TikTok</th>
              <td>
                {renderLoadButton('https://www.tiktok.com/@_luwes/video/7527476667770522893', 'Test A')}
                {renderLoadButton('https://www.tiktok.com/@scout2015/video/6718335390845095173', 'Test B')}
              </td>
            </tr>
            <tr>
              <th>Custom</th>
              <td>
                <input
                  ref={urlInputRef}
                  type="text"
                  placeholder="Enter URL"
                />
                <button
                  type="button"
                  onClick={handleLoadCustomUrl}
                >
                  Load
                </button>
              </td>
            </tr>
          </tbody>
        </table>

        <h2>State</h2>

        <table>
          <tbody>
            <tr>
              <th>src</th>
              <td className={!src ? 'faded' : ''}>{src || 'null'}</td>
            </tr>
            <tr>
              <th>playing</th>
              <td>{playing ? 'true' : 'false'}</td>
            </tr>
            <tr>
              <th>volume</th>
              <td>{volume.toFixed(3)}</td>
            </tr>
            <tr>
              <th>speed</th>
              <td>{playbackRate}</td>
            </tr>
            <tr>
              <th>played</th>
              <td>{played.toFixed(3)}</td>
            </tr>
            <tr>
              <th>loaded</th>
              <td>{loaded.toFixed(3)}</td>
            </tr>
            <tr>
              <th>duration</th>
              <td>
                <Duration seconds={duration} />
              </td>
            </tr>
            <tr>
              <th>elapsed</th>
              <td>
                <Duration seconds={duration * played} />
              </td>
            </tr>
            <tr>
              <th>remaining</th>
              <td>
                <Duration seconds={duration * (1 - played)} />
              </td>
            </tr>
          </tbody>
        </table>
      </section>
      <footer className="footer">
        Version <strong>{version}</strong>
        {SEPARATOR}
        <a href="https://github.com/CookPete/react-player">GitHub</a>
        {SEPARATOR}
        <a href="https://www.npmjs.com/package/react-player">npm</a>
      </footer>
    </div>
  );
};

export default App;


================================================
FILE: examples/react/src/Duration.tsx
================================================
import React from 'react';

export default function Duration({ className, seconds }: { className?: string; seconds: number }) {
  return (
    <time dateTime={`P${Math.round(seconds)}S`} className={className}>
      {format(seconds)}
    </time>
  );
}

function format(seconds: number) {
  const date = new Date(seconds * 1000);
  const hh = date.getUTCHours();
  const mm = date.getUTCMinutes();
  const ss = pad(date.getUTCSeconds());
  if (hh) {
    return `${hh}:${pad(mm)}:${ss}`;
  }
  return `${mm}:${ss}`;
}

function pad(string: string | number) {
  return (`0${string}`).slice(-2);
}


================================================
FILE: examples/react/src/index.tsx
================================================
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('app');
const root = createRoot(container as HTMLElement);
root.render(<App />);


================================================
FILE: package.json
================================================
{
  "name": "react-player",
  "version": "3.4.0",
  "description": "A React component for playing a variety of URLs, including file paths, Mux, YouTube, Vimeo, and Wistia",
  "author": "Pete Cook (https://github.com/cookpete)",
  "license": "MIT",
  "homepage": "https://github.com/cookpete/react-player",
  "bugs": {
    "url": "https://github.com/cookpete/react-player/issues"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/cookpete/react-player.git"
  },
  "files": [
    "dist"
  ],
  "main": "dist/index.js",
  "type": "module",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    },
    "./*": {
      "types": "./dist/*.d.ts",
      "default": "./dist/*.js"
    }
  },
  "scripts": {
    "clean": "rimraf dist demo coverage",
    "start": "run-p 'types -- -w' 'build:esm -- --watch=forever' 'build:demo -- --watch=forever --servedir=demo'",
    "lint": "biome lint src/*",
    "test": "tester test/*.tsx test/*/*.tsx test/*.js test/*/*.js --outdir=disttest --platform=node --format=esm --bundle --external:sinon --external:zora --target=esnext --sourcemap=inline",
    "test:coverage": "c8 --src src --exclude 'test/**' --exclude 'node_modules/**' --exclude 'scripts/**' --exclude-after-remap npm test",
    "test:codecov": "npm run test:coverage && c8 report --reporter json && codecov -f coverage/coverage-final.json",
    "types": "tsc",
    "build": "run-s types build:*",
    "build:esm": "builder \"src/**/*.*s*\" --outdir=dist --format=esm",
    "prebuild:demo": "rimraf demo && cp -r examples/react/public demo",
    "build:demo": "builder ./examples/react/src/index.js --format=esm --splitting --bundle --outdir=demo --minify",
    "preversion": "run-s lint test",
    "version": "auto-changelog -p && git add CHANGELOG.md",
    "prepublishOnly": "npm run build",
    "postpublish": "npm run clean"
  },
  "peerDependencies": {
    "@types/react": "^17.0.0 || ^18 || ^19",
    "react": "^17.0.2 || ^18 || ^19",
    "react-dom": "^17.0.2 || ^18 || ^19"
  },
  "dependencies": {
    "@mux/mux-player-react": "^3.8.0",
    "cloudflare-video-element": "^1.3.4",
    "dash-video-element": "^0.3.0",
    "hls-video-element": "^1.5.9",
    "spotify-audio-element": "^1.0.3",
    "tiktok-video-element": "^0.1.1",
    "twitch-video-element": "^0.1.5",
    "vimeo-video-element": "^1.6.1",
    "wistia-video-element": "^1.3.5",
    "youtube-video-element": "^1.8.0"
  },
  "devDependencies": {
    "@biomejs/biome": "1.8.2",
    "@types/node": "^20.14.6",
    "@types/react": "^18.3.3",
    "@types/react-dom": "^18.3.0",
    "@types/react-test-renderer": "^18.3.0",
    "auto-changelog": "^2.0.0",
    "builder": "file:./scripts/builder",
    "c8": "^8.0.1",
    "codecov": "^3.6.5",
    "cross-env": "^7.0.2",
    "esbuild": "^0.21.5",
    "npm-run-all": "^4.1.5",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-test-renderer": "^18.3.1",
    "rimraf": "^3.0.2",
    "screenfull": "^5.0.2",
    "sinon": "^16.0.0",
    "tester": "file:./scripts/tester",
    "typescript": "^5.4.5",
    "zora": "^5.2.0"
  },
  "auto-changelog": {
    "breakingPattern": "Breaking changes:"
  },
  "sideEffects": [
    "./test/**/*"
  ],
  "keywords": [
    "react",
    "media",
    "player",
    "video",
    "audio",
    "youtube",
    "vimeo",
    "wistia",
    "hls",
    "dash",
    "react-component"
  ]
}


================================================
FILE: scripts/builder/builder.js
================================================
#!/usr/bin/env node
import { parseArgs } from 'node:util'
import esbuild from 'esbuild'
import process from 'node:process'
import { realpath } from 'node:fs/promises'
import { fileURLToPath } from 'node:url'

const nodePath = await realpath(process.argv[1])
const modulePath = await realpath(fileURLToPath(import.meta.url))
const isCLI = nodePath === modulePath

if (isCLI) cliBuild()

export async function cliBuild () {
  const { values: args, positionals } = parseArgs({
    options: {},
    strict: false,
    allowPositionals: true
  })

  await build(positionals, args)
}

export async function build (positionals, args) {
  // https://esbuild.github.io/api/#live-reload
  const livereloadJs = 'new EventSource(\'/esbuild\').addEventListener(\'change\', () => location.reload());'

  // Assigns external modules to global variables.
  // https://github.com/evanw/esbuild/issues/337
  const plugins = {
    'global-externals': (arg) => {
      const options = JSON.parse(arg)
      const filter = new RegExp(`^${Object.keys(options)}$`)

      return {
        name: 'global-externals-plugin',
        setup (build) {
          build.onResolve({ filter }, (args) => ({
            path: args.path,
            namespace: 'global-externals-plugin'
          }))
          build.onLoad({ filter: /.*/, namespace: 'global-externals-plugin' }, (args) => {
            const contents = `module.exports = ${options[args.path]}`
            return { contents }
          })
        }
      }
    }
  }

  const options = {
    logLevel: 'info',
    entryPoints: positionals,
    outfile: args.outfile,
    outdir: args.outfile ? undefined : args.outdir ?? 'dist',
    target: args.target ?? 'es2019',
    bundle: args.bundle,
    minify: args.minify,
    format: args.format,
    platform: args.platform,
    sourcemap: args.sourcemap,
    splitting: args.splitting,
    globalName: args['global-name'],
    external: argsArray(args, 'external'),
    outExtension: argsObject(args, 'out-extension'),
    banner: argsObject(args, 'banner'),
    plugins: Object.entries(argsObject(args, 'plugin'))
      .map(([name, options]) => plugins[name](options)),
    define: {
      'globalThis.__TEST__': 'false',
      ...argsObject(args, 'define')
    },
    loader: {
      '.js': 'jsx',
      ...argsObject(args, 'loader')
    },
    footer: {
      ...argsObject(args, 'footer'),
      js: (args['footer:js'] ?? '') +
          (args.livereload ? `\n${livereloadJs}` : '')
    }
  }

  if (process.env.NODE_ENV) {
    options.define['process.env.NODE_ENV'] ||= `"${process.env.NODE_ENV}"`
  }

  // console.log(options)

  if (args.watch) {
    const ctx = await esbuild.context(options)

    await ctx.watch()

    if (args.servedir) {
      await ctx.serve({
        servedir: args.servedir
      })
    }

    return
  }

  await esbuild.build(options)
}

function argsArray (args, name) {
  return Object.keys(args)
    .filter(k => k.startsWith(`${name}:`))
    .map(k => k.slice(`${name}:`.length))
}

function argsObject (args, name) {
  return Object.keys(args)
    .filter(k => k.startsWith(`${name}:`))
    .reduce((acc, k) => {
      const key = k.slice(`${name}:`.length)
      acc[key] = args[k]
      return acc
    }, {})
}


================================================
FILE: scripts/builder/package.json
================================================
{
  "private": true,
  "name": "builder",
  "version": "0.0.0",
  "type": "module",
  "main": "./builder.js",
  "bin": {
    "builder": "./builder.js"
  }
}


================================================
FILE: scripts/tester/package.json
================================================
{
  "private": true,
  "name": "tester",
  "version": "0.0.0",
  "type": "module",
  "main": "./tester.js",
  "bin": {
    "tester": "./tester.js"
  }
}


================================================
FILE: scripts/tester/tester.js
================================================
#!/usr/bin/env node
import { parseArgs, promisify } from 'node:util';
import process from 'node:process';
import { realpath } from 'node:fs/promises';
import { exec } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { build } from 'builder';

const asyncExec = promisify(exec);
const nodePath = await realpath(process.argv[1]);
const modulePath = await realpath(fileURLToPath(import.meta.url));
const isCLI = nodePath === modulePath;

if (isCLI) cliTest();

export async function cliTest() {
  console.time('\n⚡ Tested in');

  const { values: args, positionals } = parseArgs({
    options: {},
    strict: false,
    allowPositionals: true,
  });

  await test(positionals, args);

  console.timeEnd('\n⚡ Tested in');
}

export async function test(positionals, args) {
  // Set flag for running tests in development mode, required for `act()`.
  args['define:process.env.NODE_ENV'] = '"development"';

  // Set flag for adding stubs used in tests.
  args['define:globalThis.__TEST__'] = 'true';

  await build(positionals, args);

  await cmd(`echo '{"type": "module"}' > ${args.outdir}/package.json`);

  // Ignore test/helpers/ folder for running tests.
  positionals = positionals.filter((p) => !p.startsWith('test/helpers/'));

  for (let file of positionals) {
    file = file.replace(/\.tsx?/, '.js');
    console.log(await cmd(`node --enable-source-maps dist${file}`));
  }
}

async function cmd(command, opts = {}) {
  command = command.trim().replace(/\s+/g, ' ');

  if (opts.verbose) console.log(`${command}`);

  const { stdout, stderr } = await asyncExec(command);

  if (stderr) {
    console.error(`\n${stderr}`);
  }

  return stdout.trim();
}


================================================
FILE: src/HtmlPlayer.tsx
================================================
import React from 'react';
import { AUDIO_EXTENSIONS } from './patterns.js';
import type { VideoElementProps } from './types.js';

const HtmlPlayer = React.forwardRef<HTMLVideoElement, VideoElementProps>((props, ref) => {
  const Media = AUDIO_EXTENSIONS.test(`${props.src}`) ? 'audio' : 'video';

  return (
    <Media {...props} ref={ref}>
      {props.children}
    </Media>
  );
});

export default HtmlPlayer;


================================================
FILE: src/Player.tsx
================================================
import React, { useCallback, useEffect, useRef } from 'react';
import type { SyntheticEvent } from 'react';
import type { PlayerEntry } from './players.js';
import type { ReactPlayerProps } from './types.js';

type Player = React.ForwardRefExoticComponent<
  ReactPlayerProps & {
    activePlayer: PlayerEntry['player'];
  }
>;

const Player: Player = React.forwardRef((props, ref) => {
  const { playing, pip } = props;

  const Player = props.activePlayer;
  const playerRef = useRef<HTMLVideoElement | null>(null);
  const startOnPlayRef = useRef(true);

  useEffect(() => {
    if (!playerRef.current) return;

    // Use strict equality for `playing`, if it's nullish, don't do anything.
    if (playerRef.current.paused && playing === true) {
      playerRef.current.play();
    }
    if (!playerRef.current.paused && playing === false) {
      playerRef.current.pause();
    }

    playerRef.current.playbackRate = props.playbackRate ?? 1;
    playerRef.current.volume = props.volume ?? 1;
  });

  useEffect(() => {
    if (!playerRef.current || !globalThis.document) return;

    if (pip && !document.pictureInPictureElement) {
      try {
        playerRef.current.requestPictureInPicture?.();
      } catch (err) {}
    }

    if (!pip && document.pictureInPictureElement) {
      try {
        // @ts-ignore
        playerRef.current.exitPictureInPicture?.();
        document.exitPictureInPicture?.();
      } catch (err) {}
    }
  }, [pip]);

  const handleLoadStart = (event: SyntheticEvent<HTMLVideoElement>) => {
    startOnPlayRef.current = true;
    props.onReady?.();
    props.onLoadStart?.(event);
  };

  const handlePlay = (event: SyntheticEvent<HTMLVideoElement>) => {
    if (startOnPlayRef.current) {
      startOnPlayRef.current = false;
      props.onStart?.(event);
    }
    props.onPlay?.(event);
  };

  if (!Player) {
    return null;
  }

  // Filter out ReactPlayer-specific event handlers to prevent them from being passed down
  // to the underlying HTML video element, which causes React warnings about unknown
  // event handler properties
  const eventProps: Record<string, EventListenerOrEventListenerObject> = {};
  const reactPlayerEventHandlers = ['onReady', 'onStart'];

  for (const key in props) {
    if (key.startsWith('on') && !reactPlayerEventHandlers.includes(key)) {
      eventProps[key] = props[key as keyof ReactPlayerProps];
    }
  }

  return (
    <Player
      {...eventProps}
      style={props.style}
      className={props.className}
      slot={props.slot}
      ref={useCallback(
        (node: HTMLVideoElement) => {
          playerRef.current = node;

          if (typeof ref === 'function') {
            ref(node);
          } else if (ref !== null) {
            ref.current = node;
          }
        },
        [ref]
      )}
      src={props.src}
      crossOrigin={props.crossOrigin}
      preload={props.preload}
      controls={props.controls}
      muted={props.muted}
      autoPlay={props.autoPlay}
      loop={props.loop}
      playsInline={props.playsInline}
      disableRemotePlayback={props.disableRemotePlayback}
      config={props.config}
      onLoadStart={handleLoadStart}
      onPlay={handlePlay}
    >
      {props.children}
    </Player>
  );
});

Player.displayName = 'Player';

export default Player;


================================================
FILE: src/Preview.tsx
================================================
import React, { useState, useEffect } from 'react';

import type { PreviewProps } from './types';

const ICON_SIZE = '64px';

const cache: Record<string, string> = {};

const Preview = ({
  src,
  light,
  oEmbedUrl,
  onClickPreview,
  playIcon,
  previewTabIndex,
  previewAriaLabel,
}: PreviewProps) => {
  const [image, setImage] = useState<string | null>(null);

  useEffect(() => {
    if (!src || !light || !oEmbedUrl) return;
    fetchImage({ src, light, oEmbedUrl });
  }, [src, light, oEmbedUrl]);

  const fetchImage = async ({
    src,
    light,
    oEmbedUrl,
  }: {
    src: string;
    light: boolean | string | React.ReactElement;
    oEmbedUrl: string;
  }) => {
    if (React.isValidElement(light)) {
      return;
    }
    if (typeof light === 'string') {
      setImage(light);
      return;
    }
    if (cache[src]) {
      setImage(cache[src]);
      return;
    }
    setImage(null);

    const response = await fetch(oEmbedUrl.replace('{url}', src));
    const data = await response.json();

    if (data.thumbnail_url) {
      const fetchedImage = data.thumbnail_url
        .replace('height=100', 'height=480')
        .replace('-d_295x166', '-d_640');
      setImage(fetchedImage);
      cache[src] = fetchedImage;
    }
  };

  const handleKeyPress = (e: React.KeyboardEvent<HTMLDivElement>) => {
    if (e.key === 'Enter' || e.key === ' ') {
      onClickPreview?.(e);
    }
  };

  const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
    onClickPreview?.(e);
  };

  const isElement = React.isValidElement(light);

  const flexCenter = {
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
  };

  const styles = {
    preview: {
      width: '100%',
      height: '100%',
      backgroundImage: image && !isElement ? `url(${image})` : undefined,
      backgroundSize: 'cover',
      backgroundPosition: 'center',
      cursor: 'pointer',
      ...flexCenter,
    },
    shadow: {
      background: 'radial-gradient(rgb(0, 0, 0, 0.3), rgba(0, 0, 0, 0) 60%)',
      borderRadius: ICON_SIZE,
      width: ICON_SIZE,
      height: ICON_SIZE,
      position: isElement ? 'absolute' as const : undefined,
      ...flexCenter,
    },
    playIcon: {
      borderStyle: 'solid',
      borderWidth: '16px 0 16px 26px',
      borderColor: 'transparent transparent transparent white',
      marginLeft: '7px',
    },
  };

  const defaultPlayIcon = (
    <div style={styles.shadow} className="react-player__shadow">
      <div style={styles.playIcon} className="react-player__play-icon" />
    </div>
  );

  return (
    <div
      style={styles.preview}
      className="react-player__preview"
      tabIndex={previewTabIndex}
      onClick={handleClick}
      onKeyDown={handleKeyPress}
      {...(previewAriaLabel ? { 'aria-label': previewAriaLabel } : {})}
    >
      {isElement ? light : null}
      {playIcon || defaultPlayIcon}
    </div>
  );
};

export default Preview;


================================================
FILE: src/ReactPlayer.tsx
================================================
import React, { lazy, Suspense, useEffect, useState } from 'react';

import { defaultProps } from './props.js';
import Player from './Player.js';

import type { ReactPlayerProps } from './types.js';
import type { PlayerEntry } from './players.js';

const Preview = lazy(() => import(/* webpackChunkName: 'reactPlayerPreview' */ './Preview.js'));
const customPlayers: PlayerEntry[] = [];

type ReactPlayer = React.ForwardRefExoticComponent<
  Omit<ReactPlayerProps, 'ref'> & React.RefAttributes<HTMLVideoElement>
> &
  Partial<{
    addCustomPlayer: (player: PlayerEntry) => void;
    removeCustomPlayers: () => void;
    canPlay: (src: string) => boolean;
    canEnablePIP: (src: string) => boolean;
  }>;

export const createReactPlayer = (players: PlayerEntry[], playerFallback: PlayerEntry) => {
  const getActivePlayer = (src?: string) => {
    for (const player of [...customPlayers, ...players]) {
      if (src && player.canPlay(src)) {
        return player;
      }
    }
    if (playerFallback) {
      return playerFallback;
    }
    return null;
  };

  const ReactPlayer: ReactPlayer = React.forwardRef((_props, ref) => {
    const props = { ...defaultProps, ..._props };

    const { src, slot, className, style, width, height, fallback, wrapper } = props;
    const [showPreview, setShowPreview] = useState(!!props.light);

    useEffect(() => {
      if (props.light) {
        setShowPreview(true);
      } else {
        setShowPreview(false);
      }
    }, [props.light]);

    const handleClickPreview = (e: React.SyntheticEvent) => {
      setShowPreview(false);
      props.onClickPreview?.(e);
    };

    const renderPreview = (src?: string) => {
      if (!src) return null;

      const { light, playIcon, previewTabIndex, oEmbedUrl, previewAriaLabel } = props;
      return (
        <Preview
          src={src}
          light={light}
          playIcon={playIcon}
          previewTabIndex={previewTabIndex}
          previewAriaLabel={previewAriaLabel}
          oEmbedUrl={oEmbedUrl}
          onClickPreview={handleClickPreview}
        />
      );
    };

    const renderActivePlayer = (src?: string) => {
      const player = getActivePlayer(src);
      if (!player) return null;

      const { style, width, height, wrapper } = props;
      const config = props.config?.[player.key as keyof ReactPlayerProps['config']];

      return (
        <Player
          {...props}
          ref={ref}
          activePlayer={player.player ?? (player as unknown as PlayerEntry['player'])}
          slot={wrapper ? undefined : slot}
          className={wrapper ? undefined : className}
          style={
            wrapper
              ? { display: 'block', width: '100%', height: '100%' }
              : { display: 'block', width, height, ...style }
          }
          config={config}
        />
      );
    };

    const Wrapper: ReactPlayerProps['wrapper'] = wrapper == null ? ForwardChildren : wrapper;

    const UniversalSuspense = fallback === false ? ForwardChildren : Suspense;

    return (
      <Wrapper slot={slot} className={className} style={{ width, height, ...style }}>
        <UniversalSuspense fallback={fallback}>
          {showPreview ? renderPreview(src) : renderActivePlayer(src)}
        </UniversalSuspense>
      </Wrapper>
    );
  });

  ReactPlayer.displayName = 'ReactPlayer';

  ReactPlayer.addCustomPlayer = (player: PlayerEntry) => {
    customPlayers.push(player);
  };

  ReactPlayer.removeCustomPlayers = () => {
    customPlayers.length = 0;
  };

  ReactPlayer.canPlay = (src?: string) => {
    if (src) {
      for (const Player of [...customPlayers, ...players]) {
        if (Player.canPlay(src)) {
          return true;
        }
      }
    }
    return false;
  };

  ReactPlayer.canEnablePIP = (src?: string) => {
    if (src) {
      for (const Player of [...customPlayers, ...players]) {
        if (Player.canPlay(src) && Player.canEnablePIP?.()) {
          return true;
        }
      }
    }
    return false;
  };

  return ReactPlayer;
};

const ForwardChildren = ({ children }: { children?: React.ReactNode }) => children;


================================================
FILE: src/index.ts
================================================
'use client';

import players from './players.js';
import { createReactPlayer } from './ReactPlayer.js';

// Fall back to HtmlPlayer if nothing else can play the URL
const fallback = players[players.length - 1];

export default createReactPlayer(players, fallback);


================================================
FILE: src/patterns.ts
================================================
export const AUDIO_EXTENSIONS =
  /\.(m4a|m4b|mp4a|mpga|mp2|mp2a|mp3|m2a|m3a|wav|weba|aac|oga|spx)($|\?)/i;
export const VIDEO_EXTENSIONS = /\.(mp4|og[gv]|webm|mov|m4v)(#t=[,\d+]+)?($|\?)/i;
export const HLS_EXTENSIONS = /\.(m3u8)($|\?)/i;
export const DASH_EXTENSIONS = /\.(mpd)($|\?)/i;
// Match Mux m3u8 URLs without the extension so users can use hls.js with Mux by adding the `.m3u8` extension. https://regexr.com/7um5f
export const MATCH_URL_MUX = /stream\.mux\.com\/(?!\w+\.m3u8)(\w+)/;
export const MATCH_URL_YOUTUBE =
  /(?:youtu\.be\/|youtube(?:-nocookie|education)?\.com\/(?:embed\/|v\/|watch\/|watch\?v=|watch\?.+&v=|shorts\/|live\/))((\w|-){11})|youtube\.com\/playlist\?list=|youtube\.com\/user\//;
export const MATCH_URL_VIMEO = /vimeo\.com\/(?!progressive_redirect).+/;
export const MATCH_URL_WISTIA =
  /(?:wistia\.(?:com|net)|wi\.st)\/(?:medias|embed)\/(?:iframe\/)?([^?]+)/;
export const MATCH_URL_SPOTIFY = /open\.spotify\.com\/(\w+)\/(\w+)/i;
export const MATCH_URL_TWITCH = /(?:www\.|go\.)?twitch\.tv\/([a-zA-Z0-9_]+|(videos?\/|\?video=)\d+)($|\?)/;
export const MATCH_URL_TIKTOK = /tiktok\.com\/(?:player\/v1\/|share\/video\/|@[^/]+\/video\/)([0-9]+)/;

const canPlayFile = (url: string, test: (u: string) => boolean) => {
  if (Array.isArray(url)) {
    for (const item of url) {
      if (typeof item === 'string' && canPlayFile(item, test)) {
        return true;
      }
      if (canPlayFile(item.src, test)) {
        return true;
      }
    }
    return false;
  }
  return test(url);
};

export const canPlay = {
  html: (url: string) =>
    canPlayFile(url, (u: string) => AUDIO_EXTENSIONS.test(u) || VIDEO_EXTENSIONS.test(u)),
  hls: (url: string) => canPlayFile(url, (u: string) => HLS_EXTENSIONS.test(u)),
  dash: (url: string) => canPlayFile(url, (u: string) => DASH_EXTENSIONS.test(u)),
  mux: (url: string) => MATCH_URL_MUX.test(url),
  youtube: (url: string) => MATCH_URL_YOUTUBE.test(url),
  vimeo: (url: string) =>
    MATCH_URL_VIMEO.test(url) && !VIDEO_EXTENSIONS.test(url) && !HLS_EXTENSIONS.test(url),
  wistia: (url: string) => MATCH_URL_WISTIA.test(url),
  spotify: (url: string) => MATCH_URL_SPOTIFY.test(url),
  twitch: (url: string) => MATCH_URL_TWITCH.test(url),
  tiktok: (url: string) => MATCH_URL_TIKTOK.test(url),
};


================================================
FILE: src/players.ts
================================================
import { lazy } from 'react';
import { canPlay } from './patterns.js';
import type { VideoElementProps } from './types.js';
import HtmlPlayer from './HtmlPlayer.js';

export type PlayerEntry = {
  key: string;
  name: string;
  canPlay: (src: string) => boolean;
  canEnablePIP?: () => boolean;
  player?:
    | React.ComponentType<VideoElementProps>
    | React.LazyExoticComponent<React.ComponentType<VideoElementProps>>;
};

const Players: PlayerEntry[] = [
  {
    key: 'hls',
    name: 'hls.js',
    canPlay: canPlay.hls,
    canEnablePIP: () => true,
    player: lazy(
      () => import(/* webpackChunkName: 'reactPlayerHls' */ 'hls-video-element/react')
    ) as React.LazyExoticComponent<React.ComponentType<VideoElementProps>>,
  },
  {
    key: 'dash',
    name: 'dash.js',
    canPlay: canPlay.dash,
    canEnablePIP: () => true,
    player: lazy(
      () => import(/* webpackChunkName: 'reactPlayerDash' */ 'dash-video-element/react')
    ) as React.LazyExoticComponent<React.ComponentType<VideoElementProps>>,
  },
  {
    key: 'mux',
    name: 'Mux',
    canPlay: canPlay.mux,
    canEnablePIP: () => true,
    player: lazy(
      () => import(/* webpackChunkName: 'reactPlayerMux' */ '@mux/mux-player-react')
    ) as React.LazyExoticComponent<React.ComponentType<VideoElementProps>>,
  },
  {
    key: 'youtube',
    name: 'YouTube',
    canPlay: canPlay.youtube,
    player: lazy(
      () => import(/* webpackChunkName: 'reactPlayerYouTube' */ 'youtube-video-element/react')
    ) as React.LazyExoticComponent<React.ComponentType<VideoElementProps>>,
  },
  {
    key: 'vimeo',
    name: 'Vimeo',
    canPlay: canPlay.vimeo,
    player: lazy(
      () => import(/* webpackChunkName: 'reactPlayerVimeo' */ 'vimeo-video-element/react')
    ) as React.LazyExoticComponent<React.ComponentType<VideoElementProps>>,
  },
  {
    key: 'wistia',
    name: 'Wistia',
    canPlay: canPlay.wistia,
    canEnablePIP: () => true,
    player: lazy(
      () => import(/* webpackChunkName: 'reactPlayerWistia' */ 'wistia-video-element/react')
    ) as React.LazyExoticComponent<React.ComponentType<VideoElementProps>>,
  },
  {
    key: 'spotify',
    name: 'Spotify',
    canPlay: canPlay.spotify,
    canEnablePIP: () => false,
    player: lazy(
      () => import(/* webpackChunkName: 'reactPlayerSpotify' */ 'spotify-audio-element/react')
    ) as React.LazyExoticComponent<React.ComponentType<VideoElementProps>>,
  },
  {
    key: 'twitch',
    name: 'Twitch',
    canPlay: canPlay.twitch,
    canEnablePIP: () => false,
    player: lazy(
      () => import(/* webpackChunkName: 'reactPlayerTwitch' */ 'twitch-video-element/react')
    ) as React.LazyExoticComponent<React.ComponentType<VideoElementProps>>,
  },
  {
    key: 'tiktok',
    name: 'TikTok',
    canPlay: canPlay.tiktok,
    canEnablePIP: () => false,
    player: lazy(
      () => import(/* webpackChunkName: 'reactPlayerTiktok' */ 'tiktok-video-element/react')
    ) as React.LazyExoticComponent<React.ComponentType<VideoElementProps>>,
  },
  {
    key: 'html',
    name: 'html',
    canPlay: canPlay.html,
    canEnablePIP: () => true,
    player: HtmlPlayer,
  },
];

export default Players;


================================================
FILE: src/props.ts
================================================
import type { ReactPlayerProps } from './types.js';

export const defaultProps: ReactPlayerProps = {
  // Falsy values don't need to be defined
  //
  // native video attrs
  // src: undefined,
  // preload: undefined,
  // crossOrigin: undefined,
  // autoPlay: false,
  // muted: false,
  // loop: false,
  // controls: false,
  // playsInline: false,
  // disableRemotePlayback: false,
  width: '320px',
  height: '180px',

  // native video props
  volume: 1,
  playbackRate: 1,

  // custom props
  // playing: undefined,
  // pip: false,
  // light: false,
  // fallback: null,
  previewTabIndex: 0,
  previewAriaLabel: '',
  oEmbedUrl: 'https://noembed.com/embed?url={url}',
};


================================================
FILE: src/types.ts
================================================
import type { MediaHTMLAttributes, SyntheticEvent } from 'react';
import type HlsVideoElement from 'hls-video-element';
import type SpotifyAudioElement from 'spotify-audio-element';
import type YouTubeVideoElement from 'youtube-video-element';
import type VimeoVideoElement from 'vimeo-video-element';
import type TwitchVideoElement from 'twitch-video-element';
import type TikTokVideoElement from 'tiktok-video-element';

interface VideoHTMLAttributes<T> extends MediaHTMLAttributes<T> {
  height?: number | string | undefined;
  playsInline?: boolean | undefined;
  poster?: string | undefined;
  width?: number | string | undefined;
  disablePictureInPicture?: boolean | undefined;
  disableRemotePlayback?: boolean | undefined;
  onEnterPictureInPicture?: ((this: HTMLVideoElement, ev: Event) => void) | undefined;
  onLeavePictureInPicture?: ((this: HTMLVideoElement, ev: Event) => void) | undefined;
}

export interface VideoElementProps
  extends React.DetailedHTMLProps<VideoHTMLAttributes<HTMLVideoElement>, HTMLVideoElement> {
  playbackRate?: number;
  volume?: number;
  config?: Config;
}

export interface ReactPlayerProps extends PreviewProps, VideoElementProps {
  config?: Config;
  fallback?: React.ReactNode;
  onReady?: () => void;
  onStart?: (event: SyntheticEvent<HTMLVideoElement>) => void;
  pip?: boolean;
  playing?: boolean;
  wrapper?: string | React.ComponentType<React.HTMLAttributes<HTMLDivElement>>;
}

export interface PreviewProps {
  src?: string;
  light?: boolean | string | React.ReactElement;
  oEmbedUrl?: string;
  onClickPreview?: (event: React.SyntheticEvent) => void;
  playIcon?: React.ReactNode;
  previewAriaLabel?: string;
  previewTabIndex?: number;
}

export interface Config {
  dash?: Record<string, unknown>;
  hls?: HlsVideoElement['config'];
  html?: Record<string, unknown>;
  mux?: Record<string, unknown>;
  spotify?: SpotifyAudioElement['config'];
  tiktok?: TikTokVideoElement['config'];
  twitch?: TwitchVideoElement['config'];
  vimeo?: VimeoVideoElement['config'];
  wistia?: Record<string, unknown>;
  youtube?: YouTubeVideoElement['config'];
}


================================================
FILE: test/Player.test.tsx
================================================
import './helpers/server-safe-globals.js';
import { test } from 'zora';
import sinon from 'sinon';
import React from 'react';
import Player from '../src/Player';

// Mock the activePlayer component
const MockActivePlayer = React.forwardRef<HTMLVideoElement, any>((props, ref) => {
  return React.createElement('video', { ...props, ref });
});

test('filters out ReactPlayer-specific event handlers to prevent React warnings', async (t) => {
  // Mock console.warn to capture warnings
  const originalWarn = console.warn;
  const warnings: string[] = [];
  console.warn = sinon.fake((...args) => {
    warnings.push(args.join(' '));
  });

  const props = {
    activePlayer: MockActivePlayer,
    onReady: sinon.fake(),
    onStart: sinon.fake(),
    onPlay: sinon.fake(),
    onPause: sinon.fake(),
    onEnded: sinon.fake(),
    onLoadStart: sinon.fake(),
    // These should be passed through to the underlying video element
    onLoadedMetadata: sinon.fake(),
    onCanPlay: sinon.fake(),
    onError: sinon.fake(),
  };

  // Just verify that the component can be created without errors
  // The actual filtering logic is tested by the fact that no warnings are generated
  t.ok(React.createElement(Player, props));

  // Check that no warnings about unknown event handlers were logged
  const unknownEventHandlerWarnings = warnings.filter(warning => 
    warning.includes('Unknown event handler property')
  );
  t.equal(unknownEventHandlerWarnings.length, 0);

  // Restore console.warn
  console.warn = originalWarn;
});


================================================
FILE: test/Player.tsx
================================================
import './helpers/server-safe-globals.js';
import { test } from 'zora';
import sinon from 'sinon';
import { act } from 'react-test-renderer';
import React from 'react';
import Player from '../src/Player';
import HtmlPlayer from '../src/HtmlPlayer';
import { render } from './helpers/helpers';

test('video.load()', async (t) => {
  const videoRef: React.Ref<HTMLVideoElement> = React.createRef();
  render(<Player ref={videoRef} src="file.mp4" activePlayer={HtmlPlayer} />);

  const loadstart = sinon.fake();
  videoRef.current?.addEventListener('loadstart', loadstart);

  await Promise.resolve();
  t.ok(loadstart.calledOnce);
});

test('video.play()', async (t) => {
  const videoRef: React.Ref<HTMLVideoElement> = React.createRef();
  const wrapper = render(<Player ref={videoRef} src="file.mp4" playing={false} activePlayer={HtmlPlayer} />);

  const play = sinon.fake();
  videoRef.current?.addEventListener('play', play);

  act(() => {
    wrapper.update(<Player ref={videoRef} src="file.mp4" playing={true} activePlayer={HtmlPlayer} />);
  });
  await Promise.resolve();

  t.ok(play.calledOnce);
  t.equal(videoRef.current?.paused, false);
});

test('video.pause()', async (t) => {
  const videoRef: React.Ref<HTMLVideoElement> = React.createRef();
  const wrapper = render(<Player ref={videoRef} src="file.mp4" playing={true} activePlayer={HtmlPlayer} />);

  const pause = sinon.fake();
  videoRef.current?.addEventListener('pause', pause);

  act(() => {
    wrapper.update(<Player ref={videoRef} src="file.mp4" playing={false} activePlayer={HtmlPlayer} />);
  });
  await Promise.resolve();

  t.ok(pause.calledOnce);
  t.equal(videoRef.current?.paused, true);
});

test('video.volume = 0.5', async (t) => {
  const videoRef: React.Ref<HTMLVideoElement> = React.createRef();
  const wrapper = render(<Player ref={videoRef} src="file.mp4" activePlayer={HtmlPlayer} />);

  act(() => {
    wrapper.update(<Player ref={videoRef} src="file.mp4" volume={0.5} activePlayer={HtmlPlayer} />);
  });
  await Promise.resolve();

  t.equal(videoRef.current?.volume, 0.5);
});

test('video.muted = true', async (t) => {
  let videoRef: React.Ref<HTMLVideoElement> = React.createRef();
  const wrapper = render(<Player ref={videoRef} src="file.mp4" activePlayer={HtmlPlayer} />);
  t.equal(videoRef.current?.muted, false);

  act(() => {
    videoRef = React.createRef();
    wrapper.update(<Player ref={videoRef} src="file.mp4" muted activePlayer={HtmlPlayer} />);
  });
  await Promise.resolve();

  t.equal(videoRef.current?.muted, true);
});

test('video.muted = false', async (t) => {
  let videoRef: React.Ref<HTMLVideoElement> = React.createRef();
  const wrapper = render(<Player ref={videoRef} src="file.mp4" muted activePlayer={HtmlPlayer} />);
  t.equal(videoRef.current?.muted, true);

  act(() => {
    videoRef = React.createRef();
    wrapper.update(<Player ref={videoRef} src="file.mp4" activePlayer={HtmlPlayer} />);
  });
  await Promise.resolve();

  t.equal(videoRef.current?.muted, false);
});

test('video.playbackRate = 0.5', async (t) => {
  const videoRef: React.Ref<HTMLVideoElement> = React.createRef();
  const wrapper = render(<Player ref={videoRef} src="file.mp4" activePlayer={HtmlPlayer} />);

  act(() => {
    wrapper.update(<Player ref={videoRef} src="file.mp4" playbackRate={0.5} activePlayer={HtmlPlayer} />);
  });
  await Promise.resolve();

  t.equal(videoRef.current?.playbackRate, 0.5);
});

await test('video.duration', async (t) => {
  const videoRef: React.Ref<HTMLVideoElement> = React.createRef();
  render(<Player ref={videoRef} src="https://stream.mux.com/a4nOgmxGWg6gULfcBbAa00gXyfcwPnAFldF8RdsNyk8M/low.mp4" activePlayer={HtmlPlayer} />);

  await new Promise((resolve) => {
    videoRef.current?.addEventListener('durationchange', resolve);
  });

  t.equal(videoRef.current?.duration, 10);
});


================================================
FILE: test/ReactPlayer/instanceMethods.js
================================================
import { test } from 'zora';
import sinon from 'sinon';
import React from 'react';
import { create } from 'react-test-renderer';
import ReactPlayer from '../../src/index';

const COMMON_METHODS = ['getDuration', 'getCurrentTime', 'getSecondsLoaded', 'getInternalPlayer'];

// for (const method of COMMON_METHODS) {
//   test(`${method}()`, t => {
//     const instance = create(<ReactPlayer />).getInstance()
//     instance.references.player({ [method]: () => 123 })
//     t.ok(instance[method]() === 123)
//   })

//   test(`${method}() - null`, t => {
//     const instance = create(<ReactPlayer />).getInstance()
//     t.ok(instance[method]() === null)
//   })
// }

// test('getInternalPlayer() - default', t => {
//   const instance = create(<ReactPlayer />).getInstance()
//   const getInternalPlayer = sinon.fake.returns('abc')
//   instance.references.player({ getInternalPlayer })
//   t.ok(instance.getInternalPlayer() === 'abc')
//   t.ok(getInternalPlayer.calledOnceWith('player'))
// })

// test('seekTo()', t => {
//   const instance = create(<ReactPlayer />).getInstance()
//   instance.references.player({ seekTo: sinon.fake() })
//   instance.seekTo(5)
//   t.ok(instance.player.seekTo.calledOnce)
//   t.ok(instance.player.seekTo.calledWith(5))
// })

// test('seekTo() - null', t => {
//   const instance = create(<ReactPlayer />).getInstance()
//   t.ok(instance.seekTo() === null)
// })

// test('onReady()', t => {
//   const onReady = sinon.fake()
//   const instance = create(<ReactPlayer onReady={onReady} />).getInstance()
//   instance.handleReady()
//   t.ok(onReady.calledWith(instance))
// })

// test('refs', t => {
//   const instance = create(<ReactPlayer />).getInstance()
//   instance.references.player('abc')
//   instance.references.wrapper('def')
//   t.ok(instance.player === 'abc')
//   t.ok(instance.wrapper === 'def')
// })


================================================
FILE: test/ReactPlayer/props.tsx
================================================
import '../helpers/server-safe-globals.js';
import { test } from 'zora';
import React from 'react';
import { create } from 'react-test-renderer';
import ReactPlayer from '../../src/index';
import { render } from '../helpers/helpers';

test('className', async (t) => {
  const wrapper = render(<ReactPlayer className="react-player" />);
  t.equal(wrapper.root.findByType('video').props.className, 'react-player');
});

test('style', (t) => {
  const wrapper = render(<ReactPlayer style={{ marginTop: '1rem' }} />);
  t.equal(wrapper.root.findByType('video').props.style.marginTop, '1rem');
});

test('wrapper - string', (t) => {
  const wrapper = create(<ReactPlayer wrapper="span" />);
  t.equal(wrapper.toJSON().type, 'span');
});

test('wrapper - element', (t) => {
  const Element = () => null;
  const wrapper = create(<ReactPlayer wrapper={Element} />);
  t.ok(wrapper.root.findByType(Element));
});


================================================
FILE: test/ReactPlayer/render.js
================================================
import React from 'react';
import { test } from 'zora';
import { create } from 'react-test-renderer';
import ReactPlayer from '../../src/index';
import Player from '../../src/Player';
import HtmlPlayer from '../../src/HtmlPlayer';

globalThis.window = { MediaStream: Object };

test.skip('render', (t) => {
  const wrapper = create(<ReactPlayer />);
  t.ok(wrapper.equals(<div style={{ width: '640px', height: '360px' }}>{null}</div>));
});

test.skip('fallback player', (t) => {
  const wrapper = create(<ReactPlayer src="http://example.com/random/path" />);
  t.ok(
    wrapper
      .childAt(0)
      .matchesElement(<Player activePlayer={HtmlPlayer} onReady={wrapper.instance().handleReady} />)
  );
});


================================================
FILE: test/ReactPlayer/staticMethods.js
================================================
import { test } from 'zora';
import React from 'react';
import { create } from 'react-test-renderer';
import '../helpers/server-safe-globals';
import ReactPlayer from '../../src/index';
import Player from '../../src/Player';

test('canPlay()', (t) => {
  t.ok(ReactPlayer.canPlay('https://www.youtube.com/watch?v=oUFJJNQGwhk'));
  t.ok(ReactPlayer.canPlay('https://youtube.com/shorts/370kwJ-x5TY?feature=share'));
  t.ok(ReactPlayer.canPlay('https://vimeo.com/90509568'));
  t.ok(ReactPlayer.canPlay('https://home.wistia.com/medias/e4a27b971d'));
  t.ok(ReactPlayer.canPlay('http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4'));
  t.ok(ReactPlayer.canPlay('http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4#t=1'));
  t.notOk(ReactPlayer.canPlay('http://example.com/random/path'));
});

test('addCustomPlayer()', (t) => {
  const CustomPlayer = React.forwardRef(() => <video />);
  CustomPlayer.displayName = 'CustomPlayer';
  CustomPlayer.canPlay = (src) => /example\.com/.test(src);

  ReactPlayer.addCustomPlayer(CustomPlayer);
  const wrapper = create(<ReactPlayer src="http://example.com/random/path" />);
  t.ok(ReactPlayer.canPlay('http://example.com/random/path'));
  t.ok(wrapper.root.findByType(Player));
  t.equal(wrapper.root.findByType(Player).props.activePlayer, CustomPlayer);
  ReactPlayer.removeCustomPlayers();
  t.notOk(ReactPlayer.canPlay('http://example.com/random/path'));
});


================================================
FILE: test/helpers/helpers.tsx
================================================
import { test } from 'zora'
import sinon from 'sinon'
import React from 'react'
import { ReactTestRenderer, act, create } from 'react-test-renderer'

export function render(comp: React.ReactElement): ReactTestRenderer {
  let result;
  act(() => {
    result = create(comp, {
      createNodeMock: (element) => {
        if (element.type === 'video') {
          const video = document.createElement('video');
          video.src = element.props.src;
          video.muted = element.props.muted;
          return video;
        }
      },
    });
  });
  return result;
}

export function containsMatchingElement (wrapper, comp) {
  return isObjectContained(create(comp).toJSON(), wrapper.toJSON())
}

export function isObjectContained (subObject, jsonObject) {
  if (typeof subObject !== 'object' || typeof jsonObject !== 'object') {
    return false
  }

  for (const key in subObject) {
    if (!(key in jsonObject)) {
      return false
    }

    if (typeof subObject[key] === 'object' && typeof jsonObject[key] === 'object') {
      if (!isObjectContained(subObject[key], jsonObject[key])) {
        return false
      }
    } else if (subObject[key] !== jsonObject[key]) {
      return false
    }
  }
  return true
}


================================================
FILE: test/helpers/server-safe-globals.js
================================================
// Prevent from Node.Timeout to hang the process
const oldSetTimeout = globalThis.setTimeout;
// @ts-ignore
globalThis.setTimeout = (callback, delay) => {
  const timeout = oldSetTimeout(callback, delay);
  oldSetTimeout(() => {
    timeout.unref();
  }, 100);
};

class Element extends EventTarget {
  style = {};
  querySelector = () => new Element();
  contains = () => true;
}

class HTMLVideoElement extends Element {
  #attrs = {
    muted: false,
    src: '',
  };

  constructor() {
    super();
    this.paused = true;
    this.volume = 1;
    this.currentTime = 0;
    this.duration = NaN;
    this.playbackRate = 1;
  }

  setAttribute(name, value) {
    if (name === 'src') {
      this.#attrs.src = value;
      this.load();
    } else {
      this.#attrs[name] = value;
    }
  }

  getAttribute(name) {
    return this.#attrs[name];
  }

  get src() {
    return this.getAttribute('src');
  }

  set src(value) {
    this.setAttribute('src', value);
  }

  get muted() {
    return this.getAttribute('muted');
  }

  set muted(value) {
    this.setAttribute('muted', !!value);
  }

  async load() {
    await Promise.resolve();
    this.dispatchEvent(new Event('loadstart'));

    await Promise.resolve();
    this.duration = 10;
    this.dispatchEvent(new Event('durationchange'));
Download .txt
gitextract_acdd_gc9/

├── .github/
│   ├── FUNDING.yml
│   ├── issue_template.md
│   └── workflows/
│       ├── cd.yml
│       └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.md
├── MIGRATING.md
├── README.md
├── biome.json
├── codecov.yml
├── examples/
│   └── react/
│       ├── public/
│       │   ├── App.css
│       │   ├── defaults.css
│       │   ├── index.html
│       │   ├── range.css
│       │   └── reset.css
│       └── src/
│           ├── App.tsx
│           ├── Duration.tsx
│           └── index.tsx
├── package.json
├── scripts/
│   ├── builder/
│   │   ├── builder.js
│   │   └── package.json
│   └── tester/
│       ├── package.json
│       └── tester.js
├── src/
│   ├── HtmlPlayer.tsx
│   ├── Player.tsx
│   ├── Preview.tsx
│   ├── ReactPlayer.tsx
│   ├── index.ts
│   ├── patterns.ts
│   ├── players.ts
│   ├── props.ts
│   └── types.ts
├── test/
│   ├── Player.test.tsx
│   ├── Player.tsx
│   ├── ReactPlayer/
│   │   ├── instanceMethods.js
│   │   ├── props.tsx
│   │   ├── render.js
│   │   └── staticMethods.js
│   ├── helpers/
│   │   ├── helpers.tsx
│   │   └── server-safe-globals.js
│   └── react-player-tests.tsx
└── tsconfig.json
Download .txt
SYMBOL INDEX (48 symbols across 13 files)

FILE: examples/react/src/App.tsx
  type PlayerState (line 31) | type PlayerState = Omit<typeof initialState, 'src'> & {

FILE: examples/react/src/Duration.tsx
  function Duration (line 3) | function Duration({ className, seconds }: { className?: string; seconds:...
  function format (line 11) | function format(seconds: number) {
  function pad (line 22) | function pad(string: string | number) {

FILE: scripts/builder/builder.js
  function cliBuild (line 14) | async function cliBuild () {
  function build (line 24) | async function build (positionals, args) {
  function argsArray (line 107) | function argsArray (args, name) {
  function argsObject (line 113) | function argsObject (args, name) {

FILE: scripts/tester/tester.js
  function cliTest (line 16) | async function cliTest() {
  function test (line 30) | async function test(positionals, args) {
  function cmd (line 50) | async function cmd(command, opts = {}) {

FILE: src/Player.tsx
  type Player (line 6) | type Player = React.ForwardRefExoticComponent<

FILE: src/Preview.tsx
  constant ICON_SIZE (line 5) | const ICON_SIZE = '64px';

FILE: src/ReactPlayer.tsx
  type ReactPlayer (line 12) | type ReactPlayer = React.ForwardRefExoticComponent<

FILE: src/patterns.ts
  constant AUDIO_EXTENSIONS (line 1) | const AUDIO_EXTENSIONS =
  constant VIDEO_EXTENSIONS (line 3) | const VIDEO_EXTENSIONS = /\.(mp4|og[gv]|webm|mov|m4v)(#t=[,\d+]+)?($|\?)/i;
  constant HLS_EXTENSIONS (line 4) | const HLS_EXTENSIONS = /\.(m3u8)($|\?)/i;
  constant DASH_EXTENSIONS (line 5) | const DASH_EXTENSIONS = /\.(mpd)($|\?)/i;
  constant MATCH_URL_MUX (line 7) | const MATCH_URL_MUX = /stream\.mux\.com\/(?!\w+\.m3u8)(\w+)/;
  constant MATCH_URL_YOUTUBE (line 8) | const MATCH_URL_YOUTUBE =
  constant MATCH_URL_VIMEO (line 10) | const MATCH_URL_VIMEO = /vimeo\.com\/(?!progressive_redirect).+/;
  constant MATCH_URL_WISTIA (line 11) | const MATCH_URL_WISTIA =
  constant MATCH_URL_SPOTIFY (line 13) | const MATCH_URL_SPOTIFY = /open\.spotify\.com\/(\w+)\/(\w+)/i;
  constant MATCH_URL_TWITCH (line 14) | const MATCH_URL_TWITCH = /(?:www\.|go\.)?twitch\.tv\/([a-zA-Z0-9_]+|(vid...
  constant MATCH_URL_TIKTOK (line 15) | const MATCH_URL_TIKTOK = /tiktok\.com\/(?:player\/v1\/|share\/video\/|@[...

FILE: src/players.ts
  type PlayerEntry (line 6) | type PlayerEntry = {

FILE: src/types.ts
  type VideoHTMLAttributes (line 9) | interface VideoHTMLAttributes<T> extends MediaHTMLAttributes<T> {
  type VideoElementProps (line 20) | interface VideoElementProps
  type ReactPlayerProps (line 27) | interface ReactPlayerProps extends PreviewProps, VideoElementProps {
  type PreviewProps (line 37) | interface PreviewProps {
  type Config (line 47) | interface Config {

FILE: test/ReactPlayer/instanceMethods.js
  constant COMMON_METHODS (line 7) | const COMMON_METHODS = ['getDuration', 'getCurrentTime', 'getSecondsLoad...

FILE: test/helpers/helpers.tsx
  function render (line 6) | function render(comp: React.ReactElement): ReactTestRenderer {
  function containsMatchingElement (line 23) | function containsMatchingElement (wrapper, comp) {
  function isObjectContained (line 27) | function isObjectContained (subObject, jsonObject) {

FILE: test/helpers/server-safe-globals.js
  class Element (line 11) | class Element extends EventTarget {
  class HTMLVideoElement (line 17) | class HTMLVideoElement extends Element {
    method constructor (line 23) | constructor() {
    method setAttribute (line 32) | setAttribute(name, value) {
    method getAttribute (line 41) | getAttribute(name) {
    method src (line 45) | get src() {
    method src (line 49) | set src(value) {
    method muted (line 53) | get muted() {
    method muted (line 57) | set muted(value) {
    method load (line 61) | async load() {
    method pause (line 73) | pause() {
    method play (line 78) | async play() {
  class MediaStream (line 86) | class MediaStream {}
Condensed preview — 44 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (211K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 679,
    "preview": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [u"
  },
  {
    "path": ".github/issue_template.md",
    "chars": 504,
    "preview": "Be sure to [search for your issue](https://github.com/CookPete/react-player/issues) before opening a new one.\n\n#### Curr"
  },
  {
    "path": ".github/workflows/cd.yml",
    "chars": 2022,
    "preview": "name: CD\n\nconcurrency: production\n\non:\n  # Allows you to run this workflow manually from the Actions tab\n  workflow_disp"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 2170,
    "preview": "name: Node.js CI\n\non: [push, pull_request]\n\n# Allow only one concurrent deployment, skipping runs queued between the run"
  },
  {
    "path": ".gitignore",
    "chars": 139,
    "preview": "node_modules\nnpm-debug.log\nyarn-error.log\n.DS_Store\n/lib\n/lazy\n/demo\n/coverage\n/es6\n.idea/\n.vscode/\n/disttest/\n/dist/\nts"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 99528,
    "preview": "### Changelog\n\nAll notable changes to this project will be documented in this file. Dates are displayed in UTC.\n\nGenerat"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1165,
    "preview": "# Contributing to ReactPlayer\n\nThanks for contributing to ReactPlayer!\n\nRunning the demo locally is relatively easy:\n\n``"
  },
  {
    "path": "LICENSE.md",
    "chars": 1083,
    "preview": "The MIT License\n\nCopyright © Pete Cook http://cookpete.com\n\nPermission is hereby granted, free of charge, to any person "
  },
  {
    "path": "MIGRATING.md",
    "chars": 5310,
    "preview": "## Migrating to `v3.0`\n\nBreaking changes are in 🔥 __bold and on fire__.\n\n### Some player providers are not supported yet"
  },
  {
    "path": "README.md",
    "chars": 15357,
    "preview": "<h1 align='center'>\n  ReactPlayer\n</h1>\n\n<p align='center'>\n  <a href='https://www.npmjs.com/package/react-player'><img "
  },
  {
    "path": "biome.json",
    "chars": 175,
    "preview": "{\n  \"files\": {\n    \"include\": [\"src/**/*.js\", \"src/**/*.jsx\", \"src/**/*.ts\", \"src/**/*.tsx\"],\n    \"ignore\": [\"dist/**\"]\n"
  },
  {
    "path": "codecov.yml",
    "chars": 189,
    "preview": "comment: false\ncoverage:\n  parsers:\n    javascript:\n      enable_partials: yes\n  status:\n    project:\n      default:\n   "
  },
  {
    "path": "examples/react/public/App.css",
    "chars": 537,
    "preview": ":root {\n  --column-width: 480px;\n  --gutter-width: 20px;\n}\n\n.app {\n  margin: auto;\n  font-family: 'Helvetica Neue', Helv"
  },
  {
    "path": "examples/react/public/defaults.css",
    "chars": 2150,
    "preview": ":root {\n  /*\n   * Variables with --color-base prefix define\n   * the hue, and saturation values to be used for\n   * hsla"
  },
  {
    "path": "examples/react/public/index.html",
    "chars": 627,
    "preview": "<!doctype html>\n<html lang='en'>\n  <head>\n    <meta charset='utf-8'>\n    <meta name='viewport' content='width=device-wid"
  },
  {
    "path": "examples/react/public/range.css",
    "chars": 2668,
    "preview": "/*\n// Styling Cross-Browser Compatible Range Inputs with Sass\n// Github: https://github.com/darlanrod/input-range-sass\n/"
  },
  {
    "path": "examples/react/public/reset.css",
    "chars": 1099,
    "preview": "/* http://meyerweb.com/eric/tools/css/reset/\n v2.0 | 20110126\n License: none (public domain)\n*/\n\nhtml, body, div, span, "
  },
  {
    "path": "examples/react/src/App.tsx",
    "chars": 17710,
    "preview": "// biome-ignore lint/style/useImportType:\nimport React, { useState, useRef, useCallback } from 'react';\nimport screenful"
  },
  {
    "path": "examples/react/src/Duration.tsx",
    "chars": 595,
    "preview": "import React from 'react';\n\nexport default function Duration({ className, seconds }: { className?: string; seconds: numb"
  },
  {
    "path": "examples/react/src/index.tsx",
    "chars": 223,
    "preview": "import React from 'react';\nimport { createRoot } from 'react-dom/client';\nimport App from './App';\n\nconst container = do"
  },
  {
    "path": "package.json",
    "chars": 3399,
    "preview": "{\n  \"name\": \"react-player\",\n  \"version\": \"3.4.0\",\n  \"description\": \"A React component for playing a variety of URLs, inc"
  },
  {
    "path": "scripts/builder/builder.js",
    "chars": 3233,
    "preview": "#!/usr/bin/env node\nimport { parseArgs } from 'node:util'\nimport esbuild from 'esbuild'\nimport process from 'node:proces"
  },
  {
    "path": "scripts/builder/package.json",
    "chars": 157,
    "preview": "{\n  \"private\": true,\n  \"name\": \"builder\",\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"main\": \"./builder.js\",\n  \"bin\": {"
  },
  {
    "path": "scripts/tester/package.json",
    "chars": 153,
    "preview": "{\n  \"private\": true,\n  \"name\": \"tester\",\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"main\": \"./tester.js\",\n  \"bin\": {\n "
  },
  {
    "path": "scripts/tester/tester.js",
    "chars": 1688,
    "preview": "#!/usr/bin/env node\nimport { parseArgs, promisify } from 'node:util';\nimport process from 'node:process';\nimport { realp"
  },
  {
    "path": "src/HtmlPlayer.tsx",
    "chars": 415,
    "preview": "import React from 'react';\nimport { AUDIO_EXTENSIONS } from './patterns.js';\nimport type { VideoElementProps } from './t"
  },
  {
    "path": "src/Player.tsx",
    "chars": 3302,
    "preview": "import React, { useCallback, useEffect, useRef } from 'react';\nimport type { SyntheticEvent } from 'react';\nimport type "
  },
  {
    "path": "src/Preview.tsx",
    "chars": 2940,
    "preview": "import React, { useState, useEffect } from 'react';\n\nimport type { PreviewProps } from './types';\n\nconst ICON_SIZE = '64"
  },
  {
    "path": "src/ReactPlayer.tsx",
    "chars": 4119,
    "preview": "import React, { lazy, Suspense, useEffect, useState } from 'react';\n\nimport { defaultProps } from './props.js';\nimport P"
  },
  {
    "path": "src/index.ts",
    "chars": 266,
    "preview": "'use client';\n\nimport players from './players.js';\nimport { createReactPlayer } from './ReactPlayer.js';\n\n// Fall back t"
  },
  {
    "path": "src/patterns.ts",
    "chars": 2272,
    "preview": "export const AUDIO_EXTENSIONS =\n  /\\.(m4a|m4b|mp4a|mpga|mp2|mp2a|mp3|m2a|m3a|wav|weba|aac|oga|spx)($|\\?)/i;\nexport const"
  },
  {
    "path": "src/players.ts",
    "chars": 3172,
    "preview": "import { lazy } from 'react';\nimport { canPlay } from './patterns.js';\nimport type { VideoElementProps } from './types.j"
  },
  {
    "path": "src/props.ts",
    "chars": 685,
    "preview": "import type { ReactPlayerProps } from './types.js';\n\nexport const defaultProps: ReactPlayerProps = {\n  // Falsy values d"
  },
  {
    "path": "src/types.ts",
    "chars": 2110,
    "preview": "import type { MediaHTMLAttributes, SyntheticEvent } from 'react';\nimport type HlsVideoElement from 'hls-video-element';\n"
  },
  {
    "path": "test/Player.test.tsx",
    "chars": 1529,
    "preview": "import './helpers/server-safe-globals.js';\nimport { test } from 'zora';\nimport sinon from 'sinon';\nimport React from 're"
  },
  {
    "path": "test/Player.tsx",
    "chars": 3849,
    "preview": "import './helpers/server-safe-globals.js';\nimport { test } from 'zora';\nimport sinon from 'sinon';\nimport { act } from '"
  },
  {
    "path": "test/ReactPlayer/instanceMethods.js",
    "chars": 1870,
    "preview": "import { test } from 'zora';\nimport sinon from 'sinon';\nimport React from 'react';\nimport { create } from 'react-test-re"
  },
  {
    "path": "test/ReactPlayer/props.tsx",
    "chars": 905,
    "preview": "import '../helpers/server-safe-globals.js';\nimport { test } from 'zora';\nimport React from 'react';\nimport { create } fr"
  },
  {
    "path": "test/ReactPlayer/render.js",
    "chars": 708,
    "preview": "import React from 'react';\nimport { test } from 'zora';\nimport { create } from 'react-test-renderer';\nimport ReactPlayer"
  },
  {
    "path": "test/ReactPlayer/staticMethods.js",
    "chars": 1398,
    "preview": "import { test } from 'zora';\nimport React from 'react';\nimport { create } from 'react-test-renderer';\nimport '../helpers"
  },
  {
    "path": "test/helpers/helpers.tsx",
    "chars": 1225,
    "preview": "import { test } from 'zora'\nimport sinon from 'sinon'\nimport React from 'react'\nimport { ReactTestRenderer, act, create "
  },
  {
    "path": "test/helpers/server-safe-globals.js",
    "chars": 2233,
    "preview": "// Prevent from Node.Timeout to hang the process\nconst oldSetTimeout = globalThis.setTimeout;\n// @ts-ignore\nglobalThis.s"
  },
  {
    "path": "test/react-player-tests.tsx",
    "chars": 7283,
    "preview": "// const COMMON_METHODS = ['getDuration', 'getCurrentTime', 'getSecondsLoaded']\n\n// for (const method of COMMON_METHODS)"
  },
  {
    "path": "tsconfig.json",
    "chars": 554,
    "preview": "{\n  \"compilerOptions\": {\n    \"preserveWatchOutput\": true,\n    \"target\": \"es2022\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"es"
  }
]

About this extraction

This page contains the full source code of the cookpete/react-player GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 44 files (198.6 KB), approximately 61.3k tokens, and a symbol index with 48 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!