feat: add animated image and audio player support

This commit is contained in:
hibna
2026-02-14 12:54:08 +03:00
parent 39406822ae
commit ef39d20b43
16 changed files with 1505 additions and 54 deletions
+125 -6
View File
@@ -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.