DAISY Player React
A modern, accessible audio player for DAISY (Digital Accessible Information System) format books, built with React and TypeScript.
Installation
npm install a11y-player
# or
yarn add a11y-player
Quick Start
import { DaisyPlayer } from 'a11y-player';
// No need to import CSS separately - styles are included in the bundle
function App() {
return (
<DaisyPlayer
dirUrl="https://example.com/daisy-books/book1"
appUrl="https://myapp.com"
/>
);
}
Features
- Accessible Audio Playback: Fully keyboard-navigable player with screen reader support
- Section Navigation: Browse and jump to different sections of the audiobook
- Playback Controls: Play/pause, skip forward/backward, adjust playback speed
- Bookmark Sharing: Generate and share links to specific positions in the audiobook
- Real-time Callbacks: Time updates, bookmark changes, and playback state monitoring
- Programmatic Control: Full API access via ref interface
- Event Handling: Play, pause, and seek event callbacks
- Responsive Design: Works on various screen sizes and devices
- React Router Integration: Built-in component for URL bookmark parameters
Components
DaisyPlayer
The core player component.
Props
Prop | Type | Description | Required |
---|---|---|---|
dirUrl |
string | Base URL for the DAISY book files | Yes |
appUrl |
string | Application URL for sharing links | Yes |
initialBookmark |
string | Initial bookmark position (format: "smilFile:timeInSeconds") | No |
className |
string | Custom class name for the container | No |
language |
string | UI language code (default: 'en') | No |
pathPrefix |
string | Path prefix for bookmark URLs | No |
Callback Props
Prop | Type | Description | Required |
---|---|---|---|
onTimeUpdate |
(currentTime: number, bookmark: string, isPlaying: boolean) => void |
Called at regular intervals during playback | No |
timeUpdateInterval |
number | Time update interval in milliseconds (default: 1000) | No |
onBookmarkChange |
(bookmark: string) => void |
Called when bookmark position changes | No |
onPlaybackStateChange |
(state: PlaybackState) => void |
Called when playback state changes | No |
onPlay |
() => void |
Called when playback starts | No |
onPause |
() => void |
Called when playback pauses | No |
onSeek |
(bookmark: string) => void |
Called when seeking to a new position | No |
PlaybackState Interface
interface PlaybackState {
currentTime: number;
duration: number;
isPlaying: boolean;
currentBookmark: string;
playbackRate: number;
}
DaisyPlayerWithRouter
A wrapper around DaisyPlayer that automatically reads the initial bookmark from URL parameters.
Props
Inherits all props from DaisyPlayer
plus:
Prop | Type | Description | Required |
---|---|---|---|
bookmarkParam |
string | URL parameter name to use for the bookmark (default: "bookmark") | No |
DaisyPlayerRef Interface
For programmatic control, use a ref to access these methods:
interface DaisyPlayerRef {
getCurrentTime(): number;
getDuration(): number;
isPlaying(): boolean;
getCurrentBookmark(): string;
getPlaybackRate(): number;
togglePlayPause(): void;
seek(bookmark: string): void;
}
Usage Examples
Basic Usage
import { DaisyPlayer } from 'a11y-player';
function App() {
return (
<DaisyPlayer
dirUrl="https://example.com/daisy-books/book1"
appUrl="https://myapp.com"
/>
);
}
With React Router Integration
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { DaisyPlayerWithRouter } from 'a11y-player';
function App() {
return (
<BrowserRouter>
<Routes>
<Route
path="/:bookmark?"
element={
<DaisyPlayerWithRouter
dirUrl="https://example.com/daisy-books/book1"
appUrl="https://myapp.com"
bookmarkParam="bookmark"
/>
}
/>
</Routes>
</BrowserRouter>
);
}
With Custom Bookmark Handling
import { useState } from 'react';
import { DaisyPlayer } from 'a11y-player';
function MyPlayer() {
const [bookmark, setBookmark] = useState(null);
return (
<>
<DaisyPlayer
dirUrl="https://example.com/daisy-books/book1"
appUrl="https://myapp.com"
initialBookmark={bookmark}
onBookmarkChange={(newBookmark) => setBookmark(newBookmark)}
/>
<div>
Current bookmark: {bookmark}
<button onClick={() => localStorage.setItem('savedBookmark', bookmark)}>
Save Position
</button>
<button onClick={() => setBookmark(localStorage.getItem('savedBookmark'))}>
Restore Position
</button>
</div>
</>
);
}
With Time Updates and Auto-Save
import { useState, useCallback } from 'react';
import { DaisyPlayer } from 'a11y-player';
function AutoSavePlayer() {
const [lastSaved, setLastSaved] = useState(null);
const handleTimeUpdate = useCallback((currentTime, bookmark, isPlaying) => {
// Auto-save every 30 seconds during playback
if (isPlaying && currentTime % 30 === 0) {
localStorage.setItem('autoSaveBookmark', bookmark);
setLastSaved(new Date().toLocaleTimeString());
}
}, []);
return (
<>
<DaisyPlayer
dirUrl="https://example.com/daisy-books/book1"
appUrl="https://myapp.com"
onTimeUpdate={handleTimeUpdate}
timeUpdateInterval={1000} // Check every second
onPlaybackStateChange={(state) => {
console.log('Playback state:', state);
}}
/>
{lastSaved && <p>Last auto-saved: {lastSaved}</p>}
</>
);
}
With Programmatic Control
import { useRef } from 'react';
import { DaisyPlayer, DaisyPlayerRef } from 'a11y-player';
function ControlledPlayer() {
const playerRef = useRef<DaisyPlayerRef>(null);
const handleJumpToChapter = (chapterBookmark) => {
playerRef.current?.seek(chapterBookmark);
};
const handleTogglePlayback = () => {
playerRef.current?.togglePlayPause();
};
const getCurrentInfo = () => {
const currentTime = playerRef.current?.getCurrentTime();
const bookmark = playerRef.current?.getCurrentBookmark();
console.log(`Current time: ${currentTime}, Bookmark: ${bookmark}`);
};
return (
<>
<DaisyPlayer
ref={playerRef}
dirUrl="https://example.com/daisy-books/book1"
appUrl="https://myapp.com"
/>
<div>
<button onClick={handleTogglePlayback}>Toggle Play/Pause</button>
<button onClick={getCurrentInfo}>Get Current Info</button>
<button onClick={() => handleJumpToChapter('chapter1.smil:0')}>Jump to Chapter 1</button>
</div>
</>
);
}
With Event Callbacks
import { DaisyPlayer } from 'a11y-player';
function EventAwarePlayer() {
return (
<DaisyPlayer
dirUrl="https://example.com/daisy-books/book1"
appUrl="https://myapp.com"
onPlay={() => console.log('Playback started')}
onPause={() => console.log('Playback paused')}
onSeek={(bookmark) => console.log('Seeked to:', bookmark)}
onBookmarkChange={(bookmark) => {
// Send to analytics or save to database
fetch('/api/progress', {
method: 'POST',
body: JSON.stringify({ bookmark }),
headers: { 'Content-Type': 'application/json' }
});
}}
/>
);
}
DAISY Format Requirements
The DAISY Player requires a backend server that hosts DAISY format books with the following specifications:
Required Files:
ncc.html
: The Navigation Control Center file containing the book's structure and metadata- SMIL files (
.smil
): Files describing the synchronization between text and audio - Audio files (typically MP3): The actual audio content referenced by the SMIL files
File Access: The application directly accesses files using these URL patterns:
${dirUrl}/ncc.html
- For book structure and metadata${dirUrl}/{smilFile}
- For specific SMIL files${dirUrl}/{audioFile}
- For audio files
CORS Configuration: The backend must allow cross-origin requests from the frontend application domain.
Keyboard Shortcuts
- Space or K: Play/pause
- J** or **Left Arrow: Rewind 10 seconds
- L** or **Right Arrow: Forward 10 seconds
- Up Arrow: Increase playback speed
- Down Arrow: Decrease playback speed
- I: Toggle section list view
- Escape: Close section list view (when open)
Keyboard shortcuts are only active when the player container (or any of its controls) has keyboard focus. Use the Tab key or click on the player to focus it before using these shortcuts.
Accessibility
This player is designed with accessibility as a primary concern:
- Fully keyboard navigable
- ARIA attributes for screen reader compatibility
- High contrast visuals
- Keyboard shortcuts for common actions
- Focus management between different views
Development
Prerequisites
- Node.js (v14 or higher)
- npm or yarn
Setup
- Clone the repository
- Install dependencies:
npm install
- Start the development server:
npm run dev
Building for Production
npm run build
API Reference
Types
// Main component props
export interface DaisyPlayerProps {
dirUrl: string;
appUrl: string;
pathPrefix?: string;
initialBookmark?: string;
className?: string;
language?: string;
onTimeUpdate?: (currentTime: number, bookmark: string, isPlaying: boolean) => void;
timeUpdateInterval?: number;
onBookmarkChange?: (bookmark: string) => void;
onPlaybackStateChange?: (state: PlaybackState) => void;
onPlay?: () => void;
onPause?: () => void;
onSeek?: (bookmark: string) => void;
}
// Router component props
export interface DaisyPlayerWithRouterProps extends DaisyPlayerProps {
bookmarkParam?: string;
}
// Ref interface for programmatic control
export interface DaisyPlayerRef {
getCurrentTime(): number;
getDuration(): number;
isPlaying(): boolean;
getCurrentBookmark(): string;
getPlaybackRate(): number;
togglePlayPause(): void;
seek(bookmark: string): void;
}
// Playback state structure
interface PlaybackState {
currentTime: number;
duration: number;
isPlaying: boolean;
currentBookmark: string; // Format: "smilFile:timeInSeconds"
playbackRate: number;
}
Changelog
v0.2.0
- Added
onTimeUpdate
callback with configurable interval - Added
onPlaybackStateChange
callback for comprehensive state monitoring - Added
DaisyPlayerRef
interface for programmatic control - Added individual event callbacks:
onPlay
,onPause
,onSeek
- Enhanced
onBookmarkChange
callback (now properly typed) - Added
ref
support to bothDaisyPlayer
andDaisyPlayerWithRouter
- Improved TypeScript definitions and exports