ipfs-hls-player
Version:
Video.js-based HLS player optimized for IPFS content-addressed storage
597 lines (456 loc) • 21.6 kB
Markdown
# IPFS HLS Player
**Version 1.2.3**
A complete video player for IPFS-hosted content. This all-in-one solution handles HLS streams, MP4, MOV, WebM, and other formats with automatic enhancement, seamless loading, and intelligent fallback to native players when optimal.
**Universal Format Support** - HLS, MP4, MOV, WebM, and more
**Automatic Type Detection** - Works with extensionless IPFS CIDs
**MOV File Compatibility** - Full Chrome/Brave support via intelligent MIME normalization
**Seamless Loading** - No flash or layout shift
**Cross-Browser** - Works everywhere, including Safari
**Zero Configuration** - Just add to your page and it works
**Production Ready** - Used in real IPFS applications
## What This Does
This player solves the fundamental challenges of serving video from IPFS:
1. **HLS Compatibility**: Handles IPFS URLs in HLS playlists
2. **Type Detection**: Identifies formats without file extensions
3. **Safari Support**: Automatically uses native player when optimal
4. **Professional UX**: Smooth loading with no visual glitches
## The IPFS HLS Challenge
Standard HLS uses relative paths in playlists:
```m3u8
#EXTINF:10.0,
segment001.ts
```
IPFS-compatible HLS uses absolute URLs with CIDs (each segment is a separate IPFS object):
```m3u8
#EXTINF:10.0,
https://gateway.ipfs.io/ipfs/QmSegmentHash123
```
**How HLS works on SPK Network:** During the transcoding/upload process:
1. Each video segment (.ts file) gets its own CID
2. The M3U8 playlist is rewritten to replace relative paths with absolute IPFS gateway URLs
3. The modified playlist gets its own CID
4. All segments and playlists are uploaded to IPFS
5. You play the video using the playlist's CID: `https://gateway.ipfs.io/ipfs/QmPlaylistHash`
This player is optimized to play these IPFS-hosted HLS streams.
## What This Player Provides
- **Pre-configured Video.js**: Optimized settings for IPFS gateway playback
- **VHS Override**: Forces JavaScript HLS implementation for better compatibility
- **Quality Selection UI**: Built-in adaptive bitrate controls
- **Automatic Enhancement**: Finds and upgrades video elements on the page
- **IPFS Gateway Optimization**: Tuned for distributed gateway performance
- **Intelligent Type Detection**: Handles IPFS CIDs and various video formats without assumptions
## Features
- **IPFS-Optimized**: Pre-configured for IPFS-hosted HLS streams
- **Quality Selector**: Manual quality selection UI for HLS streams (Chrome/Firefox)
- **Auto-Enhancement**: Automatically upgrades video elements
- **Seamless Loading**: No flash or layout shift during initialization
- **Universal Support**: Works with HLS, MP4, MOV, WebM across all browsers
- **Enhanced MOV Support**: Chrome/Brave compatibility via MIME type normalization (v1.2.3)
- **Intelligent Detection**: Automatic format detection via magic bytes and filename parameters
- **Native Fallback**: Seamlessly uses native players when optimal
- **Responsive**: Mobile-friendly controls and layouts
- **Customizable**: Clean styling with CSS variables
- **Vue Integration**: Optional Vue.js mixin included
## Installation
### NPM
```bash
npm install ipfs-hls-player
```
### CDN
```html
<!-- CSS -->
<link rel="stylesheet" href="https://unpkg.com/ipfs-hls-player/css/ipfs-hls-player.css">
<!-- JavaScript (minified for production) -->
<script src="https://unpkg.com/ipfs-hls-player/dist/ipfs-hls-player.min.js"></script>
```
## Usage
### Basic Setup
```html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="path/to/ipfs-hls-player.css">
</head>
<body>
<!-- Apply width constraints to container, not video element -->
<div style="max-width: 800px; margin: 0 auto;">
<video id="my-video" style="width: 100%;"></video>
</div>
<script src="path/to/ipfs-hls-player.js"></script>
<script>
// Initialize player with IPFS HLS stream
const video = document.getElementById('my-video');
IPFSHLSPlayer.initializePlayer(video, {
src: 'https://ipfs.io/ipfs/QmYourPlaylistCID'
});
</script>
</body>
</html>
```
**Important:** Apply width constraints (max-width, width) to the container element, not the video element itself. This ensures proper sizing in both normal and fullscreen modes.
### Automatic Enhancement
The player can automatically enhance all video elements on a page:
```javascript
// Enhance all videos on page load
document.addEventListener('DOMContentLoaded', async () => {
await IPFSHLSPlayer.enhanceStaticVideos();
});
```
### Vue.js Integration
```javascript
import IPFSHLSPlayerMixin from 'ipfs-hls-player/src/vue-integration';
export default {
mixins: [IPFSHLSPlayerMixin],
// Your component code...
}
```
### CSS Class Configuration
The player allows customization of which CSS classes are applied to video elements:
```javascript
// Customize CSS classes for specific styling needs
IPFSHLSPlayer.initializePlayer(video, {
src: 'https://ipfs.io/ipfs/QmYourPlaylistCID',
cssClasses: {
// Required classes (always applied)
required: ['video-js', 'vjs-default-skin'],
// Optional classes (applied by default, can be overridden)
optional: ['vjs-big-play-centered', 'vjs-fluid'],
// Custom classes for your application
custom: ['my-custom-class', 'theme-dark']
}
});
```
This is useful when:
- Integrating with existing styling systems
- Preventing conflicts with other video frameworks
- Applying custom themes or layouts
### Intelligent MIME Type Detection
The player includes advanced type detection specifically designed for IPFS content where file extensions are not available:
#### Magic Byte Detection
For IPFS CIDs without file extensions, the player uses **magic byte detection** - the industry standard method for identifying file types:
1. **Fetches first 200 bytes** of the content using a Range request (efficient)
2. **Checks magic bytes** to identify the format:
- `#EXTM3U` → HLS playlist (`application/x-mpegURL`)
- `ftyp` box → MP4/MOV/QuickTime (`video/mp4`)
- EBML header → WebM/Matroska (`video/webm`)
- `OggS` → Ogg/Ogv (`video/ogg`)
- And more formats...
3. **Falls back to Content-Type** header if magic bytes don't match
4. **Normalizes MIME types** (e.g., `video/quicktime` → `video/mp4`)
#### Video.js Middleware Integration
The player uses Video.js middleware to transparently handle IPFS URLs:
```javascript
// Automatically detects type for IPFS URLs
IPFSHLSPlayer.initializePlayer(video, {
src: 'https://ipfs.io/ipfs/QmYourCID' // No type needed!
});
```
The middleware:
- Only processes IPFS URLs without an explicit type
- Runs magic byte detection asynchronously
- Provides the detected type to Video.js
- Falls back to `video/mp4` if detection fails
#### Manual Type Override
You can still manually specify types for faster initialization:
```javascript
IPFSHLSPlayer.initializePlayer(video, {
src: 'https://ipfs.io/ipfs/QmYourHLSPlaylist',
type: 'application/x-mpegURL' // Skip detection for known HLS
});
```
This intelligent detection ensures all video formats work correctly with IPFS, not just MP4.
## API Reference
### `IPFSHLSPlayer.initializePlayer(element, options)`
Initialize a player on a video element.
**Parameters:**
- `element` (HTMLVideoElement): Video element to enhance
- `options` (Object): Player configuration
- `src` (String): Video source URL
- `type` (String): MIME type (optional - intelligently auto-detected based on URL)
- `poster` (String): Poster image URL
- `autoplay` (Boolean): Auto-start playback
- `loop` (Boolean): Loop playback
- `muted` (Boolean): Start muted
- `cssClasses` (Object): CSS class configuration
- `required` (Array): Classes always applied
- `optional` (Array): Default classes (can be overridden)
- `custom` (Array): Additional custom classes
**Returns:** Video.js player instance
### `IPFSHLSPlayer.enhanceVideoElement(video, options)`
Enhance an existing video element with IPFS HLS Player.
**Parameters:**
- `video` (HTMLVideoElement): Video element to enhance
- `options` (Object): Enhancement options
**Returns:** Promise<Player>
### `IPFSHLSPlayer.destroyPlayer(element)`
Destroy a player instance and clean up.
**Parameters:**
- `element` (HTMLVideoElement): Video element with player
### `IPFSHLSPlayer.enhanceStaticVideos(container)`
Enhance all unenhanced videos in a container.
**Parameters:**
- `container` (HTMLElement): Container to search within (default: document)
**Returns:** Promise<Array> of player instances
## Seamless Loading Experience
The player provides a completely seamless initialization with no visual jumps or layout shifts:
### How It Works
1. **Preemptive Sizing**: CSS immediately establishes 16:9 aspect ratio for all videos
2. **Hidden Initial State**: Videos are hidden while enhancement occurs
3. **Loading Indicator**: Spinner shows during player initialization
4. **Smooth Reveal**: Videos fade in when ready
### Loading States
- **`ipfs-video-loading`**: Shows spinner, hides video
- **`ipfs-video-ready`**: Video enhanced and ready, fades in smoothly
- **`ipfs-video-error`**: Fallback state if enhancement fails
### CSS Requirements
**Important**: Don't apply generic `video` styles in your CSS. The player handles all sizing:
```css
/* DON'T do this */
video {
max-width: 100%;
height: auto;
}
/* DO this - containers need explicit dimensions */
.my-video-container {
max-width: 800px;
width: 100%; /* Important for Safari */
aspect-ratio: 16/9; /* Prevents fullscreen issues */
margin: 0 auto;
}
```
**Safari Note**: Containers must have explicit `width` and `aspect-ratio` to prevent fullscreen behavior when creating videos dynamically.
### Benefits
- **Zero layout shift** - Space reserved from first paint
- **No flash of unstyled content** - Videos hidden until ready
- **Professional appearance** - Smooth transitions and loading feedback
- **Consistent sizing** - 16:9 aspect ratio maintained throughout
## Philosophy: Capability-Based with Browser-Specific Workarounds
This player primarily uses capability detection but includes specific browser detection where necessary to work around known limitations. This pragmatic approach ensures reliable playback across all browsers.
### How It Works
Player selection is determined by:
1. **Native HLS Support**: Can the browser play HLS natively?
2. **MediaSource Extensions (MSE)**: Does the browser support MSE for JavaScript-based playback?
3. **Browser-Specific Limitations**: Are there known issues that require browser-specific handling?
- Safari/WebKit browsers are explicitly detected due to Video.js 8.x limitations
- The detection is scalable to add other browsers if similar issues arise
### Self-Contained Type Detection
The player is fully self-contained and can intelligently detect video types without requiring type attributes. While type attributes are respected if provided (for performance), the player can determine the correct type through multiple methods:
#### Detection Hierarchy (in order of priority)
1. **Explicit Type** (fastest)
```html
<video src="video.m3u8" type="application/x-mpegURL">
```
If provided, the type is used immediately - no detection needed.
2. **URL Query Parameters**
```
https://example.com/video?type=video/mp4
https://ipfs.gateway.com/ipfs/QmCID?filename=video.mov
```
Type hints embedded in the URL are extracted and used. Supports both `type` and `filename` parameters (v1.2.3+).
3. **File Extensions**
```
.m3u8 → application/x-mpegURL
.mp4 → video/mp4
.webm → video/webm
.mov → video/mp4 (normalized for Chrome/Brave compatibility)
```
Common extensions are mapped to MIME types. MOV files are normalized to `video/mp4` to ensure Chrome/Brave compatibility (v1.2.3+).
4. **Magic Byte Detection** (universal fallback)
```
#EXTM3U → HLS Playlist
ftyp box → MP4/MOV/QuickTime
EBML header → WebM/Matroska
OggS → Ogg Video
```
For extensionless URLs (like IPFS CIDs), the player fetches the first 200 bytes and identifies the format by its binary signature.
#### Key Points
- **Type is optional**: The player works perfectly without type attributes
- **Type is respected**: If you provide a type, it's used (no detection overhead)
- **Universal compatibility**: Works with any URL format - extensions, extensionless, or query params
- **Performance optimized**: Detection only runs when needed
- **Native player support**: Both native and Video.js players benefit from type detection
### Player Strategy
```javascript
// Decision tree (no browser names, just capabilities):
if (content is HLS) {
if (has native HLS && cannot override) → Use native player
else if (has MSE) → Use Video.js with HLS
else if (has native HLS) → Use native player
else → Try Video.js anyway
} else {
→ Use Video.js (handles all other formats)
}
```
### Browser Compatibility Notes
#### Safari/WebKit Special Handling
Safari on macOS/iOS has native HLS support that cannot be reliably overridden by Video.js 8.x. The player explicitly detects Safari browsers using WebKit-specific properties and automatically uses the native player for HLS content.
**Detection Method:**
- Checks for WebKit-specific APIs (`webkitEnterFullscreen`, `webkitSupportsFullscreen`)
- Verifies Safari-specific properties (`window.safari` object)
- Excludes Chrome (which also has some WebKit properties but works fine with Video.js)
**Why Special Handling?**
This is a known limitation of Video.js 8.x with Safari. Rather than attempting to override Safari's native HLS (which causes playback issues), the player intelligently uses Safari's native capabilities.
#### Why Native Players Are Sometimes Used
When Video.js 8.x cannot properly override a browser's native HLS implementation (a technical limitation of Video.js), the player automatically uses native playback. This ensures the best possible user experience regardless of browser.
## Configuration
### Global Configuration
```javascript
window.ipfsHLSPlayerConfig = {
debug: true, // Enable debug logging
enableStaticEnhancement: false // Disable automatic enhancement
};
```
### Player Options
```javascript
IPFSHLSPlayer.initializePlayer(video, {
html5: {
vhs: {
overrideNative: true, // Critical for IPFS
smoothQualityChange: true,
fastQualityChange: true
}
},
playbackRates: [0.5, 1, 1.5, 2],
fluid: true,
responsive: true
});
```
## IPFS Gateway Support
The player works with any IPFS gateway:
```javascript
// IPFS.io gateway
src: 'https://ipfs.io/ipfs/QmPlaylistCID'
// Cloudflare gateway
src: 'https://cloudflare-ipfs.com/ipfs/QmPlaylistCID'
// Local IPFS node
src: 'http://localhost:8080/ipfs/QmPlaylistCID'
// Custom gateway (like SPK Network)
src: 'https://ipfs.dlux.io/ipfs/QmPlaylistCID'
```
## HLS Quality Selector
The player includes an optimized quality selector for HLS streams with proper timing to ensure all quality levels are detected:
### How It Works
1. **For .m3u8 URLs**: Quality selector initializes immediately
2. **For IPFS CIDs**:
- Waits for the `loadstart` event (after type detection)
- Checks if content is HLS
- Initializes selector before manifest loads
- Ensures all quality levels are tracked
### Quality Levels Display
- Shows all available resolutions (1080p, 720p, 480p, etc.)
- Allows manual quality selection
- Displays current quality
- Only appears for HLS content (hidden for MP4s)
### Timing Optimization
The player uses the `loadstart` event for perfect timing:
- Fires after middleware detects content type
- Occurs before HLS manifest loads
- Ensures quality selector catches all levels as they're added
- Prevents empty quality dropdowns
## Browser Support
- Chrome 60+ (MOV files fully supported via MIME normalization)
- Firefox 55+
- Safari 11+ (HLS via native player)
- Edge 79+
- Brave (MOV files fully supported via MIME normalization)
- iOS Safari 11+ (HLS via native player)
- Chrome for Android
### Safari & Native HLS Support
The player automatically detects browser capabilities and selects the best playback method:
#### Safari Native Player Approach
- ✅ **All video formats** - Safari uses native player for everything (HLS, MP4, MOV, WebM)
- ✅ **Consistent user experience** - Same controls for all video types
- ✅ **Better performance** - Native player is more efficient
- ✅ **Safari/iOS features** - Full support for AirPlay, Picture-in-Picture, media keys
- ⚠️ **Limited customization** - No quality selector or custom controls
#### Why Safari Uses Native for Everything
1. **Consistency**: Users see the same familiar Safari controls for all videos
2. **Performance**: Native player provides better battery life and smoother playback
3. **Integration**: Seamless support for Safari-specific features like AirPlay
4. **Simplicity**: Cleaner code path with fewer compatibility issues
#### Feature Comparison
| Feature | Chrome/Firefox (Video.js) | Safari (Native) |
|---------|--------------------------|-----------------|
| HLS Playback | ✅ | ✅ |
| MP4/MOV/WebM | ✅ | ✅ |
| Quality Selector | ✅ | ❌ (HLS auto) |
| Custom Controls | ✅ | ❌ |
| AirPlay Support | ❌ | ✅ |
| Picture-in-Picture | ✅ | ✅ |
| Adaptive Bitrate | ✅ | ✅ (automatic) |
| Type Detection | ✅ | ✅ |
#### Technical Background
Safari's native video player provides the best experience for Safari users. Rather than forcing Video.js on Safari (which has compatibility issues with HLS), the player uses Safari's native capabilities for all content types. This design choice prioritizes user experience, performance, and reliability over feature parity across browsers.
## Troubleshooting
### CORS Errors
Ensure your IPFS gateway supports CORS headers for cross-origin playback. Most public gateways handle this correctly, but local nodes may need configuration.
### Mixed Content Warnings
If your site uses HTTPS, ensure all IPFS gateway URLs also use HTTPS. Browsers block HTTP content on HTTPS pages.
### Player Not Initializing
- Check browser console for errors
- Verify Video.js CSS is loading (the player includes automatic fallback CDN loading)
- Ensure the video element exists in DOM before calling `initializePlayer`
### HLS Playback Issues
- Verify the M3U8 playlist uses absolute IPFS URLs (not relative paths)
- Check that all segment files are accessible via the gateway
- Test the playlist URL directly in the browser
### Type Detection Issues
If videos fail to play with IPFS CIDs:
- Check the Content-Type headers from your IPFS gateway using browser dev tools
- Try manually specifying the type parameter
- Ensure your gateway properly sets Content-Type headers for video files
- For HLS streams without .m3u8 extension, always specify `type: 'application/x-mpegURL'`
### Safari HLS Playback
HLS streams work automatically in Safari using the native player.
If you encounter issues with HLS in Safari:
- Verify the HLS playlist uses absolute IPFS URLs
- Check that segments are accessible via the gateway
- Native player controls will be used (quality selector unavailable)
- Test in Chrome or Firefox to compare Video.js features
### "Player Already Initialized" Warning
When reusing video elements:
```javascript
// Clean up existing player first
if (video._ipfsHLSPlayer) {
IPFSHLSPlayer.destroyPlayer(video);
}
// Now safe to initialize new player
const player = IPFSHLSPlayer.initializePlayer(video, options);
```
## Development
```bash
# Install dependencies
npm install
# Development build with watch
npm run dev
# Production build
npm run build
```
**Note:** Browsers handle local file access differently. For testing the examples, it's recommended to serve them from a local web server (e.g., `python -m http.server` or `npx http-server`).
## Why Use This Player
While Video.js can play HLS streams out of the box, IPFS-hosted content benefits from specific optimizations:
1. **Gateway Performance**: Tuned buffering and timeout settings for distributed gateways
2. **JavaScript HLS**: Uses VHS (Video.js HTTP Streaming) which handles IPFS URLs more reliably than native HLS
3. **Quality Selection**: Clear UI for manual quality switching when adaptive bitrate needs help
4. **Simplified Setup**: Pre-configured Video.js with all the right settings for IPFS content
This player packages these optimizations into a drop-in solution for IPFS video playback.
## Changelog
### Version 1.2.3 (Latest)
- **Fixed**: MOV files now play correctly in Chrome/Brave browsers
- **Added**: Filename parameter detection for SPK Drive compatibility
- **Improved**: MIME type normalization (MOV → MP4) for broader browser support
- **Enhanced**: Type detection now checks `?filename=` URL parameters
### Version 1.2.2
- Safari native player strategy for all video types
- Improved poster frame support for Safari
### Version 1.2.0
- Initial stable release with full IPFS HLS support
- Magic byte detection for extensionless URLs
- Video.js middleware integration
## Credits
Built on top of:
- [Video.js](https://videojs.com/) - The open source HTML5 video player
- [videojs-hls-quality-selector](https://github.com/chrisboustead/videojs-hls-quality-selector) - Quality selection UI
## License
MIT © Mark E. Giles
See [LICENSE](LICENSE) file for details.