| name | shelby-media-player |
| description | Use when integrating Shelby Protocol media player for React video streaming. Helps with installation, SimpleShakaVideoPlayer setup, custom layouts, TailwindCSS 4 configuration, and building adaptive HLS/DASH video players. Invoke for video player components, decentralized video streaming, or Shaka Player integration with Shelby. |
| allowed-tools | Read, Write, Edit, Grep, Glob |
| model | sonnet |
Shelby Media Player
Purpose
This skill assists developers integrating the Shelby Protocol media player SDK into React applications for video streaming. The player is a lightweight React wrapper around Shaka Player, designed for adaptive HLS/DASH streaming from Shelby's decentralized storage network.
When to Use
This skill should be invoked when:
- User mentions "@shelby-protocol/player", "SimpleShakaVideoPlayer", or "Shelby video player"
- User wants to build video streaming applications with React
- User needs help with HLS or DASH adaptive streaming
- User asks about Shaka Player integration with Shelby
- User needs custom video player layouts or controls
- User is troubleshooting TailwindCSS 4 integration with the player
- User wants to stream videos from Shelby decentralized storage
- User asks about video player components, controls, or customization
Process
1. Installation and Setup
Install the package:
npm install @shelby-protocol/player
# or
pnpm add @shelby-protocol/player
# or
yarn add @shelby-protocol/player
# or
bun add @shelby-protocol/player
TailwindCSS 4 Requirement:
The player requires TailwindCSS 4 for default styles:
npm install tailwindcss@4
Configure styles in globals.css:
Option 1 - Source directive (recommended):
@source "@shelby-protocol/player";
Option 2 - Direct import:
@import "@shelby-protocol/player/shadcn.css";
2. Basic Implementation
Simple player setup:
import { SimpleShakaVideoPlayer } from '@shelby-protocol/player';
function MyVideoPlayer() {
return (
<SimpleShakaVideoPlayer
src="https://example.com/video.m3u8"
poster="https://example.com/thumbnail.jpg"
title="My Video Title"
/>
);
}
Props:
- src (required): Video source URL (HLS
.m3u8or DASH.mpdformat) - poster (optional): Poster image URL displayed before playback
- title (optional): Video title for accessibility and display
3. Integration with Shelby Storage
Stream videos from Shelby blobs:
import { SimpleShakaVideoPlayer } from '@shelby-protocol/player';
interface ShelbyVideoPlayerProps {
blobName: string;
poster?: string;
title: string;
}
function ShelbyVideoPlayer({ blobName, poster, title }: ShelbyVideoPlayerProps) {
// Construct Shelby API URL
const videoUrl = `https://api.shelbynet.shelby.xyz/shelby/blobs/${blobName}`;
return (
<SimpleShakaVideoPlayer
src={videoUrl}
poster={poster}
title={title}
/>
);
}
// Usage
<ShelbyVideoPlayer
blobName="videos/intro.m3u8"
poster="/thumbnails/intro.jpg"
title="Introduction Video"
/>
4. Custom Player Layouts
Build custom layouts using primitive components:
import {
VideoOutlet,
Controls,
PlayButton,
VolumeButton,
TimeSlider,
FullscreenButton,
usePlayer
} from '@shelby-protocol/player';
function CustomVideoPlayer({ src, poster, title }: VideoPlayerProps) {
const { containerRef } = usePlayer();
return (
<div ref={containerRef} className="relative">
<VideoOutlet />
{/* Custom controls layout */}
<Controls>
<div className="flex items-center gap-4 px-4 py-2">
<PlayButton />
<TimeSlider />
<VolumeButton />
<FullscreenButton />
</div>
</Controls>
</div>
);
}
Available primitive components:
VideoOutlet: Main video containerPoster: Poster image displayControls: Player control containerPlayButton: Play/pause buttonVolumeButton: Volume control with sliderTimeSlider: Seek bar/timelineFullscreenButton: Fullscreen toggleDefaultLayout: Pre-configured layout
5. Advanced Customization with usePlayer Hook
Access Shaka Player instance for advanced controls:
import { usePlayer } from '@shelby-protocol/player';
import { useEffect } from 'react';
function AdvancedVideoPlayer({ src }: { src: string }) {
const { containerRef, player } = usePlayer();
useEffect(() => {
if (player) {
// Access Shaka Player instance for advanced controls
player.configure({
streaming: {
bufferingGoal: 30,
rebufferingGoal: 10,
}
});
// Add event listeners
player.addEventListener('error', (event) => {
console.error('Player error:', event);
});
}
}, [player]);
return (
<div ref={containerRef}>
{/* Custom player UI */}
</div>
);
}
6. Complete Example Components
Video gallery component:
import { SimpleShakaVideoPlayer } from '@shelby-protocol/player';
interface Video {
id: string;
blobName: string;
title: string;
poster: string;
duration: string;
}
function VideoGallery({ videos }: { videos: Video[] }) {
const [selectedVideo, setSelectedVideo] = useState<Video | null>(null);
return (
<div>
{/* Video grid */}
<div className="grid grid-cols-3 gap-4">
{videos.map((video) => (
<div
key={video.id}
onClick={() => setSelectedVideo(video)}
className="cursor-pointer"
>
<img src={video.poster} alt={video.title} />
<h3>{video.title}</h3>
<p>{video.duration}</p>
</div>
))}
</div>
{/* Video player modal */}
{selectedVideo && (
<div className="fixed inset-0 bg-black/80 flex items-center justify-center">
<div className="w-full max-w-4xl">
<SimpleShakaVideoPlayer
src={`https://api.shelbynet.shelby.xyz/shelby/blobs/${selectedVideo.blobName}`}
poster={selectedVideo.poster}
title={selectedVideo.title}
/>
<button onClick={() => setSelectedVideo(null)}>Close</button>
</div>
</div>
)}
</div>
);
}
Output Format
When helping users integrate Shelby media player:
- Check dependencies - Verify React and TailwindCSS 4 are installed
- Provide complete examples - Include imports and full component code
- Explain styling setup - Show TailwindCSS configuration
- Demonstrate integration - Show Shelby storage URL construction
- Handle edge cases - Show error handling and loading states
Best Practices
Video Format and Encoding
- Use adaptive streaming formats: HLS (
.m3u8) or DASH (.mpd) - Provide multiple quality levels for adaptive bitrate streaming
- Optimize encoding settings for target devices and bandwidth
- Use proper video codecs: H.264 for compatibility, H.265 for efficiency
Performance
- Lazy load video components when possible to improve page load
- Implement intersection observer to load videos only when visible
- Optimize poster images - compress and use appropriate sizes
- Consider CDN or edge caching for frequently accessed videos
- Use appropriate buffering settings based on use case
User Experience
- Always provide poster images for better visual experience
- Include meaningful video titles for accessibility
- Implement loading states while video initializes
- Handle errors gracefully with user-friendly messages
- Provide playback controls appropriate for your use case
- Consider autoplay policies - most browsers restrict autoplay
Accessibility
- Use semantic HTML with proper ARIA labels
- Provide keyboard navigation for all controls
- Include captions/subtitles when available
- Ensure adequate color contrast for controls
- Test with screen readers
Styling
- Override CSS variables for custom theming
- Use Tailwind utility classes on wrapper elements
- Create reusable styled components for consistency
- Maintain responsive design across device sizes
- Test on different screen sizes and orientations
Examples
Example 1: Simple Video Player Page
User Request: "Create a page to play a video from Shelby storage"
Implementation:
// app/video/[blobName]/page.tsx
import { SimpleShakaVideoPlayer } from '@shelby-protocol/player';
interface VideoPageProps {
params: {
blobName: string;
};
}
export default function VideoPage({ params }: VideoPageProps) {
const { blobName } = params;
// Decode blob name from URL
const decodedBlobName = decodeURIComponent(blobName);
// Construct video URL
const videoUrl = `https://api.shelbynet.shelby.xyz/shelby/blobs/${decodedBlobName}`;
return (
<div className="container mx-auto py-8">
<SimpleShakaVideoPlayer
src={videoUrl}
poster="/default-poster.jpg"
title="Video from Shelby Storage"
/>
</div>
);
}
Example 2: Custom Player with Playlist
User Request: "Build a video player with a playlist sidebar"
Implementation:
// components/PlaylistPlayer.tsx
import { useState } from 'react';
import { SimpleShakaVideoPlayer } from '@shelby-protocol/player';
interface PlaylistItem {
id: string;
blobName: string;
title: string;
poster: string;
duration: string;
}
interface PlaylistPlayerProps {
playlist: PlaylistItem[];
}
export function PlaylistPlayer({ playlist }: PlaylistPlayerProps) {
const [currentIndex, setCurrentIndex] = useState(0);
const currentVideo = playlist[currentIndex];
const videoUrl = `https://api.shelbynet.shelby.xyz/shelby/blobs/${currentVideo.blobName}`;
return (
<div className="flex gap-4">
{/* Main player */}
<div className="flex-1">
<SimpleShakaVideoPlayer
key={currentVideo.id} // Force remount on video change
src={videoUrl}
poster={currentVideo.poster}
title={currentVideo.title}
/>
<div className="mt-4">
<h2 className="text-2xl font-bold">{currentVideo.title}</h2>
</div>
</div>
{/* Playlist sidebar */}
<div className="w-80 space-y-2">
<h3 className="font-semibold text-lg mb-4">Playlist</h3>
{playlist.map((video, index) => (
<div
key={video.id}
onClick={() => setCurrentIndex(index)}
className={`
p-3 rounded cursor-pointer transition-colors
${index === currentIndex ? 'bg-blue-500 text-white' : 'bg-gray-100 hover:bg-gray-200'}
`}
>
<div className="flex gap-3">
<img
src={video.poster}
alt={video.title}
className="w-24 h-14 object-cover rounded"
/>
<div className="flex-1">
<p className="font-medium text-sm line-clamp-2">{video.title}</p>
<p className="text-xs opacity-70">{video.duration}</p>
</div>
</div>
</div>
))}
</div>
</div>
);
}
Example 3: Advanced Player with Analytics
User Request: "Create a video player that tracks viewing analytics"
Implementation:
// components/AnalyticsPlayer.tsx
import { usePlayer } from '@shelby-protocol/player';
import { useEffect, useRef } from 'react';
interface AnalyticsPlayerProps {
src: string;
poster?: string;
title: string;
videoId: string;
onAnalytics?: (event: string, data: any) => void;
}
export function AnalyticsPlayer({
src,
poster,
title,
videoId,
onAnalytics
}: AnalyticsPlayerProps) {
const { containerRef, player } = usePlayer();
const watchTimeRef = useRef(0);
const lastTimeRef = useRef(0);
useEffect(() => {
if (!player) return;
// Track play events
const handlePlay = () => {
onAnalytics?.('video_play', { videoId, title });
lastTimeRef.current = Date.now();
};
// Track pause events
const handlePause = () => {
const watchTime = (Date.now() - lastTimeRef.current) / 1000;
watchTimeRef.current += watchTime;
onAnalytics?.('video_pause', {
videoId,
title,
watchTime: watchTimeRef.current
});
};
// Track completion
const handleEnded = () => {
onAnalytics?.('video_complete', {
videoId,
title,
totalWatchTime: watchTimeRef.current
});
};
// Track errors
const handleError = (event: any) => {
onAnalytics?.('video_error', {
videoId,
title,
error: event.detail
});
};
// Add listeners
player.addEventListener('play', handlePlay);
player.addEventListener('pause', handlePause);
player.addEventListener('ended', handleEnded);
player.addEventListener('error', handleError);
// Track initial load
onAnalytics?.('video_load', { videoId, title });
// Cleanup
return () => {
player.removeEventListener('play', handlePlay);
player.removeEventListener('pause', handlePause);
player.removeEventListener('ended', handleEnded);
player.removeEventListener('error', handleError);
};
}, [player, videoId, title, onAnalytics]);
return (
<div ref={containerRef}>
{/* Use built-in components */}
<SimpleShakaVideoPlayer
src={src}
poster={poster}
title={title}
/>
</div>
);
}
// Usage
<AnalyticsPlayer
src="https://api.shelbynet.shelby.xyz/shelby/blobs/videos/intro.m3u8"
poster="/posters/intro.jpg"
title="Introduction Video"
videoId="intro-2024"
onAnalytics={(event, data) => {
// Send to analytics service
console.log('Analytics:', event, data);
// analytics.track(event, data);
}}
/>
Error Handling
Common issues and solutions:
- Styles not working: Install TailwindCSS 4 and add source directive to
globals.css - Video not playing: Check URL accessibility, format (HLS/DASH), and CORS headers
- Player not rendering: Verify React is installed and component is properly imported
- CORS errors: Ensure Shelby API or video CDN has proper CORS headers
- Buffering issues: Adjust Shaka Player buffering configuration
- Fullscreen not working: Check browser permissions and Fullscreen API support
- Black screen: Verify video format compatibility with browser
- Controls not responding: Check event listeners and state management
Troubleshooting
Styles Not Rendering
Problem: Player appears unstyled or broken.
Solutions:
Verify TailwindCSS 4 is installed:
npm install tailwindcss@4Add source directive to
globals.css:@source "@shelby-protocol/player";Restart development server
Video Not Loading
Problem: Video doesn't load or play.
Solutions:
- Check video URL is accessible (test in browser)
- Verify video format is HLS (
.m3u8) or DASH (.mpd) - Check browser console for errors
- Ensure CORS headers are set correctly
- Test with a known working HLS/DASH stream
Performance Issues
Problem: Video stutters or buffers excessively.
Solutions:
- Optimize video encoding settings
- Use appropriate bitrate for target bandwidth
- Implement proper chunking for large videos
- Configure Shaka Player buffering settings
- Consider CDN or edge caching
Notes
- Based on Shaka Player: Uses Google's adaptive streaming library
- Streaming formats: Supports HLS and DASH adaptive streaming
- TailwindCSS 4 required: For default styling (shadcn-style components)
- Browser support: Modern browsers with HLS (Safari native) or DASH support
- Fullscreen API: Uses browser's native fullscreen capabilities
- Adaptive bitrate: Automatically adjusts quality based on network conditions
- Custom controls: Build completely custom UIs with primitive components
- Shelby integration: Works seamlessly with Shelby decentralized storage
- TypeScript support: Fully typed for better DX
Related Resources
- Shelby SDK: Upload videos programmatically (
@shelby-protocol/sdk) - Shelby CLI: Upload videos via command line
- Shaka Player Docs: https://shaka-player-demo.appspot.com/docs/api/
- HLS Spec: https://datatracker.ietf.org/doc/html/rfc8216
- DASH Spec: https://www.iso.org/standard/79329.html
- TailwindCSS: https://tailwindcss.com/