feat: add animated image and audio player support
This commit is contained in:
+125
-6
@@ -1,6 +1,6 @@
|
||||
# @source/player Documentation
|
||||
|
||||
This document reflects the current codebase in this repository (`version 3.1.0`) and replaces older, drifted documentation.
|
||||
This document reflects the current codebase in this repository (`version 3.2.0`) and replaces older, drifted documentation.
|
||||
|
||||
## Table of contents
|
||||
|
||||
@@ -21,9 +21,11 @@ This document reflects the current codebase in this repository (`version 3.1.0`)
|
||||
|
||||
## 1. Overview
|
||||
|
||||
`@source/player` is a React video player library with:
|
||||
`@source/player` is a React media player library with:
|
||||
|
||||
- Protocol-aware playback (`native`, `hls`, `rtmp/flv`, `mpegts`)
|
||||
- Animated image playback (`.gif`, `.webp`, `.apng`, `.avif`) through a minimal render path
|
||||
- Dedicated audio playback component for music/podcast style use-cases
|
||||
- Built-in controls with settings menus (speed, quality, subtitles, subtitle styling, audio tracks)
|
||||
- Modular extension points for custom controls and overlays
|
||||
- Strong TypeScript API (props, handle types, context types, utility exports)
|
||||
@@ -36,9 +38,11 @@ The player is split into focused modules.
|
||||
### 2.1 High-level component graph
|
||||
|
||||
- `VideoPlayer`
|
||||
- `AudioPlayer`
|
||||
- `PlayerErrorBoundary`
|
||||
- `PlayerProvider` (context + state + actions + i18n)
|
||||
- `VideoElement` (media element, protocol setup, media events, subtitle rendering)
|
||||
- `mediaSource` utils (media kind detection for video/audio/animated image)
|
||||
- `ControlsLayer` (controls visibility, settings menu, keyboard/touch integration)
|
||||
- `SettingsMenu` (lazy-loaded, menu subviews)
|
||||
|
||||
@@ -50,6 +54,11 @@ The player is split into focused modules.
|
||||
- Wraps content with `PlayerErrorBoundary` and `PlayerProvider`
|
||||
- Exposes imperative API via ref (`VideoPlayerHandle`)
|
||||
|
||||
- `AudioPlayer`
|
||||
- Uses native `HTMLAudioElement` with a custom control surface
|
||||
- Supports keyboard shortcuts, playback rate cycling, and metadata/artwork UI
|
||||
- Exposes imperative API via ref (`AudioPlayerHandle`)
|
||||
|
||||
- `PlayerProvider`
|
||||
- Owns central `videoState`, `uiState`, and `settings`
|
||||
- Manages subtitle style draft/commit/persist lifecycle
|
||||
@@ -150,6 +159,29 @@ export function App() {
|
||||
</VideoPlayer>
|
||||
```
|
||||
|
||||
### 4.4 Animated image support
|
||||
|
||||
```tsx
|
||||
<VideoPlayer src="https://example.com/animations/loader.gif" />
|
||||
```
|
||||
|
||||
`VideoPlayer` auto-detects animated image sources and renders them with a lightweight `<img>` path.
|
||||
In this mode, control layer is hidden automatically for minimal runtime cost.
|
||||
|
||||
### 4.5 Dedicated audio player
|
||||
|
||||
```tsx
|
||||
import { AudioPlayer } from '@source/player'
|
||||
|
||||
<AudioPlayer
|
||||
src="https://example.com/audio/episode.mp3"
|
||||
title="Episode 12"
|
||||
subtitle="Engineering Notes"
|
||||
artwork="https://example.com/audio/cover.jpg"
|
||||
playbackRates={[0.75, 1, 1.25, 1.5, 2]}
|
||||
/>
|
||||
```
|
||||
|
||||
## 5. Streaming and protocol handling
|
||||
|
||||
### 5.1 Auto protocol detection
|
||||
@@ -215,6 +247,15 @@ Important note:
|
||||
- Progress bar and time display are hidden
|
||||
- Live badge is shown in controls
|
||||
|
||||
### 5.7 Animated image detection behavior
|
||||
|
||||
`VideoPlayer` can detect animated image sources by extension or data MIME:
|
||||
|
||||
- `.gif`, `.webp`, `.apng`, `.avif`
|
||||
- `data:image/gif`, `data:image/webp`, `data:image/apng`, `data:image/avif`
|
||||
|
||||
When detected, player uses image render path instead of protocol setup and stream engines.
|
||||
|
||||
## 6. Subtitles and subtitle style editor
|
||||
|
||||
### 6.1 Subtitle sources
|
||||
@@ -397,6 +438,7 @@ You can override any key with `translations?: Partial<Translations>`.
|
||||
| Prop | Type | Default | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| `src` | `string` | required | media URL |
|
||||
| `mediaType` | `'auto' \| 'video' \| 'animated-image'` | `'auto'` | force image/video mode |
|
||||
| `protocol` | `'auto' \| 'native' \| 'hls' \| 'rtmp' \| 'dash' \| 'mpegts'` | `'auto'` | force engine |
|
||||
| `poster` | `string` | - | poster image |
|
||||
| `autoplay` | `boolean` | `false` | autoplay attempt on load |
|
||||
@@ -498,7 +540,76 @@ interface VideoPlayerHandle {
|
||||
}
|
||||
```
|
||||
|
||||
### 10.3 `PlayerErrorBoundaryProps`
|
||||
### 10.3 `AudioPlayerProps`
|
||||
|
||||
#### Source and playback
|
||||
|
||||
| Prop | Type | Default | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| `src` | `string` | required | audio URL |
|
||||
| `autoplay` | `boolean` | `false` | autoplay attempt on load |
|
||||
| `loop` | `boolean` | `false` | loop playback |
|
||||
| `muted` | `boolean` | `false` | initial muted state |
|
||||
| `volume` | `number` | - | clamped to `0..1` |
|
||||
| `playbackRate` | `number` | - | initial/current rate |
|
||||
| `currentTime` | `number` | - | seeks when difference is significant |
|
||||
| `preload` | `'none' \| 'metadata' \| 'auto'` | `'metadata'` | media preload hint |
|
||||
| `crossOrigin` | `'' \| 'anonymous' \| 'use-credentials'` | - | CORS mode |
|
||||
|
||||
#### UI, metadata and composition
|
||||
|
||||
| Prop | Type | Default |
|
||||
| --- | --- | --- |
|
||||
| `controls` | `boolean` | `true` |
|
||||
| `keyboardShortcuts` | `boolean` | `true` |
|
||||
| `playbackRates` | `number[]` | `[0.5,0.75,1,1.25,1.5,2]` |
|
||||
| `title` | `string` | - |
|
||||
| `subtitle` | `string` | - |
|
||||
| `artwork` | `string` | - |
|
||||
| `theme` | `PlayerTheme` | - |
|
||||
| `className` | `string` | `''` |
|
||||
| `style` | `CSSProperties` | - |
|
||||
| `children` | `ReactNode` | - |
|
||||
| `controlsLeftExtra` | `ReactNode` | - |
|
||||
| `controlsRightExtra` | `ReactNode` | - |
|
||||
| `language` | `string` | browser language |
|
||||
| `translations` | `Partial<Translations>` | - |
|
||||
|
||||
#### Events
|
||||
|
||||
| Prop | Type |
|
||||
| --- | --- |
|
||||
| `onPlay` | `() => void` |
|
||||
| `onPause` | `() => void` |
|
||||
| `onEnded` | `() => void` |
|
||||
| `onTimeUpdate` | `(currentTime: number) => void` |
|
||||
| `onVolumeChange` | `(volume: number) => void` |
|
||||
| `onError` | `(error: Error) => void` |
|
||||
| `onLoadedMetadata` | `() => void` |
|
||||
| `onSeeking` | `() => void` |
|
||||
| `onSeeked` | `() => void` |
|
||||
| `onProgress` | `(buffered: number) => void` |
|
||||
| `onDurationChange` | `(duration: number) => void` |
|
||||
| `onRateChange` | `(playbackRate: number) => void` |
|
||||
| `onWaiting` | `() => void` |
|
||||
| `onCanPlay` | `() => void` |
|
||||
|
||||
### 10.4 `AudioPlayerHandle`
|
||||
|
||||
```ts
|
||||
interface AudioPlayerHandle {
|
||||
audio: HTMLAudioElement | null
|
||||
container: HTMLDivElement | null
|
||||
play(): void
|
||||
pause(): void
|
||||
seek(time: number): void
|
||||
setVolume(volume: number): void
|
||||
toggleMute(): void
|
||||
setPlaybackRate(rate: number): void
|
||||
}
|
||||
```
|
||||
|
||||
### 10.5 `PlayerErrorBoundaryProps`
|
||||
|
||||
```ts
|
||||
interface PlayerErrorBoundaryProps {
|
||||
@@ -510,7 +621,7 @@ interface PlayerErrorBoundaryProps {
|
||||
}
|
||||
```
|
||||
|
||||
### 10.4 Core types
|
||||
### 10.6 Core types
|
||||
|
||||
```ts
|
||||
type SubtitleTrack = {
|
||||
@@ -572,6 +683,7 @@ From `src/index.ts`:
|
||||
|
||||
- Components
|
||||
- `VideoPlayer`
|
||||
- `AudioPlayer`
|
||||
- `PlayerErrorBoundary`
|
||||
|
||||
- Context
|
||||
@@ -592,10 +704,13 @@ From `src/index.ts`:
|
||||
- `parseSRT`, `createSubtitleBlobURL`, `fetchSubtitle`
|
||||
- `validateVideoURL`, `getCORSErrorMessage`, `isCORSError`, `checkVideoCORS`
|
||||
- `initializePolyfills`, `features`
|
||||
- `detectPlayerMediaType`, `isAnimatedImageSource`, `isAudioSource`
|
||||
|
||||
- Types
|
||||
- `VideoPlayerProps`, `VideoPlayerHandle`, `SubtitleTrack`, `SubtitleStyle`, `SubtitleStyleEditorConfig`
|
||||
- `VideoPlayerProps`, `VideoPlayerHandle`, `AudioPlayerProps`, `AudioPlayerHandle`
|
||||
- `SubtitleTrack`, `SubtitleStyle`, `SubtitleStyleEditorConfig`
|
||||
- `SubtitlePosition`, `AudioTrack`, `VideoQuality`, `PlayerTheme`
|
||||
- `VideoMediaType`, `VideoMediaTypeInput`
|
||||
- `KeyboardShortcutConfig`, `TouchConfig`
|
||||
- `VideoState`, `UIState`, `PlayerSettings`, `PlayerContextValue`
|
||||
- `Translations`
|
||||
@@ -654,6 +769,8 @@ For HLS/FLV/MPEG-TS setups:
|
||||
- Direct RTMP URLs usually need HTTP-FLV proxying.
|
||||
- DASH detection exists but playback is not implemented yet.
|
||||
- MPEG-TS behavior depends on MSE support and stream/server conditions.
|
||||
- Animated image mode is display-only (no timeline controls by design).
|
||||
- Some browsers may restrict autoplay for audio unless muted or user-initiated.
|
||||
|
||||
Polyfill and feature utilities:
|
||||
|
||||
@@ -688,12 +805,14 @@ npm run validate:publish
|
||||
### 14.2 Test coverage areas in repository
|
||||
|
||||
- Core rendering and props (`VideoPlayer.test.tsx`)
|
||||
- Audio rendering and controls (`AudioPlayer.test.tsx`)
|
||||
- Settings interactions (`SettingsMenu.test.tsx`)
|
||||
- Error boundary reset and fallback behavior
|
||||
- Keyboard and touch hook behavior
|
||||
- Protocol detection and streaming setup utilities
|
||||
- Media type detection (`mediaSource.test.ts`)
|
||||
- CORS helper validation
|
||||
|
||||
---
|
||||
|
||||
If you maintain this document, update it together with changes to `src/types/index.ts`, `src/index.ts`, and `src/components/VideoPlayer.tsx` to avoid API drift.
|
||||
If you maintain this document, update it together with changes to `src/types/index.ts`, `src/index.ts`, `src/components/VideoPlayer.tsx`, and `src/components/AudioPlayer.tsx` to avoid API drift.
|
||||
|
||||
Reference in New Issue
Block a user