UNPKG

react-udux-player

Version:

A customizable music player for React applications

488 lines (404 loc) 14.4 kB
Here's the complete README.md file in one view for easy copying: ```markdown # React Udux Player A customizable music player for React applications with HLS streaming support, track preloading, style isolation, and a rich set of features. ## Features - 🎵 HLS streaming support - 🔄 Track preloading for smooth transitions - 🎨 Fully customizable theming - 🧩 Pluggable UI components - 📱 Responsive design - 🔊 Volume control with mute toggle - 🔀 Shuffle and repeat modes - ⏱️ Progress bar with seeking - 🎧 Event callbacks for player state changes - 🔒 Style isolation to prevent conflicts with your app's styles ## Installation ```bash npm install react-udux-player # or yarn add react-udux-player # or pnpm add react-udux-player ``` ## Quick Start ```javascriptreact import { UduxPlayerProvider, UduxPlayer, useUduxPlayer } from 'react-Udux-player'; // Define your tracks const tracks = [ { id: "1", title: "Song Title", artist: "Artist Name", duration: 180, streamUrl: "https://example.com/song.m3u8", coverUrl: "/images/cover.jpg" }, // More tracks... ]; // In your component function App() { return ( <UduxPlayerProvider> {/* Your app content */} <MusicLibrary /> <UduxPlayer /> </UduxPlayerProvider> ); } // Use the player in any component function MusicLibrary() { const { setQueue, togglePlay, currentTrack } = useUduxPlayer(); return ( <div> <h1>My Music</h1> <button onClick={() => { setQueue(tracks); togglePlay(); }}> Play All </button> {/* Display current track */} {currentTrack && ( <div> Now playing: {currentTrack.title} by {currentTrack.artist} </div> )} </div> ); } ``` ## Style Isolation The player uses a scoped CSS approach to prevent style conflicts with your application. All player styles are: - Dynamically generated and injected at runtime - Prefixed with unique class names (`udux-player-*`) - Contained within the player's DOM structure - Automatically cleaned up when the player is unmounted This ensures that the player's styles won't affect your application's styles and vice versa. ## Customization ### Theming You can customize the player's appearance by providing a theme object: ```javascriptreact import { UduxPlayerProvider, UduxPlayer } from 'react-Udux-player'; const myTheme = { colors: { primary: '#1DB954', // Main accent color background: '#121212', // Player background text: '#FFFFFF', // Primary text color textSecondary: '#B3B3B3', // Secondary text color progressBar: '#535353', // Progress bar background progressBarFilled: '#1DB954', // Filled part of progress bar controlBackground: '#282828', // Background for controls playerBorder: '#282828', // Border color }, sizes: { playerHeight: '90px', // Height of the player progressBarHeight: '4px', // Height of progress bar controlButtonSize: '32px', // Size of control buttons volumeSliderWidth: '100px', // Width of volume slider coverArtSize: '56px', // Size of cover art }, borderRadius: '4px', // Border radius for elements fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', // Font family }; function App() { return ( <UduxPlayerProvider theme={myTheme}> <UduxPlayer /> </UduxPlayerProvider> ); } ``` ### Custom Components You can replace any part of the player UI with your own components: ```javascriptreact import { UduxPlayerProvider, UduxPlayer } from 'react-Udux-player'; import MyCustomPlayButton from './MyCustomPlayButton'; import MyCustomVolumeControl from './MyCustomVolumeControl'; function App() { return ( <UduxPlayerProvider components={{ PlayButton: MyCustomPlayButton, VolumeControl: MyCustomVolumeControl, }} > <UduxPlayer /> </UduxPlayerProvider> ); } ``` Example of a custom play button component: ```javascriptreact // MyCustomPlayButton.jsx import React from 'react'; const MyCustomPlayButton = ({ isPlaying, onClick, isBuffering }) => { return ( <button onClick={onClick} style={{ backgroundColor: '#1DB954', color: 'white', border: 'none', borderRadius: '50%', width: '40px', height: '40px', display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', }} > {isBuffering ? ( <span>⋯</span> ) : isPlaying ? ( <span>❚❚</span> ) : ( <span>▶</span> )} </button> ); }; export default MyCustomPlayButton; ``` ## Custom Icons and Styling The player comes with a set of custom icons and styling out of the box: - Like/favorite button - Fullscreen toggle - Add to playlist button - Minimize player button - Now playing view toggle - Dynamic background colors based on album artwork ### Dominant Color Extraction The player automatically extracts dominant colors from the album artwork and applies them as a gradient background: ### Event Callbacks Listen to player events: ```javascriptreact import { UduxPlayerProvider, UduxPlayer } from 'react-Udux-player'; function App() { return ( <UduxPlayerProvider onTrackChange={(track) => console.log('Now playing:', track?.title)} onPlaybackStateChange={(isPlaying) => console.log('Playback:', isPlaying ? 'playing' : 'paused')} onError={(error) => console.error('Player error:', error)} onTimeUpdate={(time) => console.log('Current time:', time)} onQueueChange={(queue) => console.log('Queue updated:', queue.length, 'tracks')} onVolumeChange={(volume, isMuted) => console.log('Volume:', volume, isMuted ? '(muted)' : '')} onShuffleChange={(shuffleEnabled) => console.log('Shuffle:', shuffleEnabled ? 'on' : 'off')} onRepeatChange={(repeatMode) => console.log('Repeat mode:', repeatMode)} onSeek={(time) => console.log('Seeked to:', time)} onEnded={() => console.log('Track ended')} onLike={(track, isLiked) => console.log(`Track ${track?.title} ${isLiked ? 'liked' : 'unliked'}`)} onAddToPlaylist={(track) => console.log(`Add ${track?.title} to playlist`)} onFullscreenChange={(isFullscreen) => console.log(`Fullscreen: ${isFullscreen}`)} onViewChange={(isNowPlayingView) => console.log(`Now playing view: ${isNowPlayingView}`)} onMinimize={() => console.log('Player minimized')} > <UduxPlayer /> </UduxPlayerProvider> ); } ``` ## Creating a Custom Player You can create your own player UI while still using the player context: ```javascriptreact import React from 'react'; import { UduxPlayerProvider, useUduxPlayer } from 'react-Udux-player'; // Custom player component function CustomPlayer() { const { currentTrack, isPlaying, togglePlay, nextTrack, previousTrack, currentTime, duration, seek, } = useUduxPlayer(); // Format time as MM:SS const formatTime = (time) => { if (!time) return '0:00'; const minutes = Math.floor(time / 60); const seconds = Math.floor(time % 60); return `${minutes}:${seconds.toString().padStart(2, '0')}`; }; if (!currentTrack) { return <div>No track selected</div>; } return ( <div className="my-custom-player"> <div className="track-info"> <img src={currentTrack.coverUrl || '/default-cover.jpg'} alt={currentTrack.title} className="cover-art" /> <div> <h3>{currentTrack.title}</h3> <p>{currentTrack.artist}</p> </div> </div> <div className="controls"> <button onClick={previousTrack}>Previous</button> <button onClick={togglePlay}> {isPlaying ? 'Pause' : 'Play'} </button> <button onClick={nextTrack}>Next</button> </div> <div className="progress"> <span>{formatTime(currentTime)}</span> <input type="range" min={0} max={duration || 100} value={currentTime || 0} onChange={(e) => seek(Number(e.target.value))} /> <span>{formatTime(duration)}</span> </div> </div> ); } // Main app function App() { return ( <UduxPlayerProvider> <div> {/* Your app content */} <CustomPlayer /> </div> </UduxPlayerProvider> ); } ``` ## API Reference ### UduxPlayerProvider Props | Prop | Type | Description |-----|-----|----- | `children` | ReactNode | Child components | `theme` | Theme | Theme customization object | `components` | CustomComponents | Custom UI components | `initialVolume` | number | Initial volume level (0-100) | `initialShuffle` | boolean | Initial shuffle state | `initialRepeat` | "off" | "all" | "one" | Initial repeat mode | `onTrackChange` | (track: Track | null) => void | Called when the current track changes | `onPlaybackStateChange` | (isPlaying: boolean) => void | Called when playback starts or stops | `onError` | (error: Error) => void | Called when an error occurs | `onTimeUpdate` | (time: number) => void | Called when playback time updates | `onQueueChange` | (queue: Track[]) => void | Called when the queue changes | `onVolumeChange` | (volume: number, isMuted: boolean) => void | Called when volume changes | `onShuffleChange` | (shuffleEnabled: boolean) => void | Called when shuffle mode changes | `onRepeatChange` | (repeatMode: RepeatMode) => void | Called when repeat mode changes | `onSeek` | (time: number) => void | Called when seeking to a position | `onEnded` | () => void | Called when a track ends | `onLike` | (track: Track | null, isLiked: boolean) => void | Called when you like current track | `onAddToPlaylist` | (track: Track | null) => void | Called when you trigger add to playlist | `onFullscreenChange` | (isFullscreen: boolean) => void | Called when you trigger fullscreen mode | `onViewChange` | (isNowPlayingView: boolean) => void | Called when you toggle now playing | onMinimize | () => void | Called when you toggle minimize player ### useUduxPlayer Hook The `useUduxPlayer` hook provides access to the player state and controls: | Property/Method | Type | Description |-----|-----|----- | `currentTrack` | Track | null | Currently playing track | `queue` | Track[] | Current queue of tracks | `isPlaying` | boolean | Whether playback is active | `volume` | number | Current volume (0-100) | `isMuted` | boolean | Whether audio is muted | `currentTime` | number | Current playback position in seconds | `duration` | number | Duration of current track in seconds | `shuffle` | boolean | Whether shuffle mode is active | `repeat` | "off" | "all" | "one" | Current repeat mode | `isBuffering` | boolean | Whether the player is buffering | `togglePlay` | () => void | Toggle play/pause | `nextTrack` | () => void | Skip to next track | `previousTrack` | () => void | Go to previous track | `seek` | (time: number) => void | Seek to position in seconds | `setVolume` | (volume: number) => void | Set volume (0-100) | `toggleMute` | () => void | Toggle mute | `toggleShuffle` | () => void | Toggle shuffle mode | `toggleRepeat` | () => void | Cycle repeat modes | `setQueue` | (tracks: Track[]) => void | Set the queue of tracks | `theme` | Theme | Current theme object | `tracksToPreload` | number[] | Indices of tracks being preloaded ### Track Interface ```typescript interface Track { id: string; title: string; artist: string; album?: string; duration: number; coverUrl?: string; streamUrl: string; } ``` ### Theme Interface ```typescript interface Theme { colors: { primary: string; background: string; text: string; textSecondary: string; progressBar: string; progressBarFilled: string; controlBackground?: string; playerBorder?: string; }; sizes: { playerHeight: string; progressBarHeight: string; controlButtonSize: string; volumeSliderWidth?: string; coverArtSize?: string; }; borderRadius: string; fontFamily?: string; } ``` ### CustomComponents Interface ```typescript interface CustomComponents { PlayButton?: React.ComponentType<{ isPlaying: boolean; onClick: () => void; isBuffering: boolean }>; NextButton?: React.ComponentType<{ onClick: () => void; disabled: boolean }>; PreviousButton?: React.ComponentType<{ onClick: () => void; disabled: boolean }>; ShuffleButton?: React.ComponentType<{ active: boolean; onClick: () => void; disabled: boolean }>; RepeatButton?: React.ComponentType<{ mode: RepeatMode; onClick: () => void; disabled: boolean }>; VolumeControl?: React.ComponentType<{ volume: number; isMuted: boolean; onVolumeChange: (volume: number) => void; onMuteToggle: () => void; }>; ProgressBar?: React.ComponentType<{ currentTime: number; duration: number; onSeek: (time: number) => void; isBuffering: boolean; }>; TrackInfo?: React.ComponentType<{ track: Track | null }>; PlayerContainer?: React.ComponentType<{ children: React.ReactNode }>; } ``` ## Troubleshooting ### Controls not working If the player controls are not responding: 1. Make sure you've set up a queue of tracks using `setQueue` 2. Check that your stream URLs are valid and accessible 3. Verify that you're using the `useUduxPlayer` hook within a component that's a child of `UduxPlayerProvider` 4. Add console logs to your click handlers to verify they're being called ### Style conflicts If you're experiencing style conflicts: 1. The player uses scoped CSS with prefixed class names (`udux-player-*`) to prevent conflicts 2. If conflicts persist, you can create a custom player UI using the `useUduxPlayer` hook ### Audio not playing If audio doesn't play: 1. Check that your stream URLs are valid and accessible 2. Verify that the browser supports HLS playback (most modern browsers do) 3. Check browser console for any errors 4. Try using a public test stream like `https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8` ## Browser Support The player works in all modern browsers that support HTML5 audio and video. For HLS streaming support, it uses the `react-hls-player` package which provides cross-browser compatibility. ## License