w

Examples

This section provides practical examples of using the M3U8 Online Player for various scenarios and use cases.

Basic Usage Examples

Example 1: Simple Stream Loading

// Load a basic M3U8 stream
const loadBasicStream = async () => {
  const streamUrl =
    'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8';

  try {
    await loadStream(streamUrl);
    console.log('Stream loaded successfully');
  } catch (error) {
    console.error('Failed to load stream:', error);
  }
};

Example 2: Stream with Custom Controls

// Load stream with custom playback controls
const loadStreamWithControls = async (url: string) => {
  await loadStream(url);

  // Set custom volume
  updateVolume(75);

  // Auto-play if desired
  if (videoPlayer.value) {
    videoPlayer.value.play();
  }
};

Example 3: Error Handling

// Comprehensive error handling example
const loadStreamWithErrorHandling = async (url: string) => {
  try {
    // Validate URL first
    if (!isValidUrl(url)) {
      throw new Error('Invalid URL format');
    }

    await loadStream(url);

    // Set up error listeners
    if (videoPlayer.value) {
      videoPlayer.value.addEventListener('error', (event) => {
        const error = event.target?.error;
        console.error('Video error:', error);

        // Handle specific error codes
        switch (error?.code) {
          case 1: // MEDIA_ERR_ABORTED
            console.log('Stream loading was aborted');
            break;
          case 2: // MEDIA_ERR_NETWORK
            console.log('Network error occurred');
            break;
          case 3: // MEDIA_ERR_DECODE
            console.log('Stream decoding error');
            break;
          case 4: // MEDIA_ERR_SRC_NOT_SUPPORTED
            console.log('Stream format not supported');
            break;
        }
      });
    }
  } catch (error) {
    console.error('Stream loading failed:', error);
    // Show user-friendly error message
    toast.error('Failed to load stream. Please check the URL and try again.');
  }
};

Advanced Examples

Example 4: Stream Quality Detection

// Detect and display stream quality information
const analyzeStreamQuality = async (url: string) => {
  await loadStream(url);

  if (videoPlayer.value) {
    videoPlayer.value.addEventListener('loadedmetadata', () => {
      const video = videoPlayer.value!;
      const quality = {
        width: video.videoWidth,
        height: video.videoHeight,
        duration: video.duration,
        bitrate: estimateBitrate(video),
      };

      console.log('Stream quality:', quality);

      // Update UI with quality information
      streamInfo.value = {
        resolution: `${quality.width}x${quality.height}`,
        duration: formatTime(quality.duration),
        bitrate: `${quality.bitrate} kbps`,
      };
    });
  }
};

const estimateBitrate = (video: HTMLVideoElement): number => {
  // Simple bitrate estimation based on file size and duration
  // This is a basic example - real implementation would be more complex
  return Math.round((video.duration * 1000) / 8); // Rough estimate
};

Example 5: History Management

// Advanced history management with filtering
const manageStreamHistory = () => {
  // Get history records
  const history = historyRecords.value;

  // Filter by date range
  const recentHistory = history.filter((record) => {
    const recordDate = new Date(record.timestamp);
    const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
    return recordDate > weekAgo;
  });

  // Group by domain
  const historyByDomain = history.reduce(
    (groups, record) => {
      try {
        const domain = new URL(record.url).hostname;
        if (!groups[domain]) {
          groups[domain] = [];
        }
        groups[domain].push(record);
      } catch (error) {
        // Handle invalid URLs
        console.warn('Invalid URL in history:', record.url);
      }
      return groups;
    },
    {} as Record<string, HistoryRecord[]>,
  );

  console.log('Recent history:', recentHistory);
  console.log('History by domain:', historyByDomain);
};

Example 6: Custom Event Handling

// Custom event system for stream management
class StreamEventManager {
  private listeners: Map<string, Function[]> = new Map();

  on(event: string, callback: Function) {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    this.listeners.get(event)!.push(callback);
  }

  emit(event: string, data?: any) {
    const callbacks = this.listeners.get(event) || [];
    callbacks.forEach((callback) => callback(data));
  }

  off(event: string, callback: Function) {
    const callbacks = this.listeners.get(event) || [];
    const index = callbacks.indexOf(callback);
    if (index > -1) {
      callbacks.splice(index, 1);
    }
  }
}

// Usage
const eventManager = new StreamEventManager();

eventManager.on('streamLoaded', (url) => {
  console.log('Stream loaded:', url);
  // Update UI, analytics, etc.
});

eventManager.on('streamError', (error) => {
  console.error('Stream error:', error);
  // Handle error, show notification, etc.
});

// Emit events when appropriate
const loadStreamWithEvents = async (url: string) => {
  try {
    await loadStream(url);
    eventManager.emit('streamLoaded', url);
  } catch (error) {
    eventManager.emit('streamError', error);
  }
};

Integration Examples

Example 7: React Component Integration

import React, { useEffect, useRef, useState } from 'react';

interface M3U8PlayerProps {
  url?: string;
  autoplay?: boolean;
  onLoad?: (url: string) => void;
  onError?: (error: string) => void;
}

const M3U8Player: React.FC<M3U8PlayerProps> = ({ url, autoplay = false, onLoad, onError }) => {
  const videoRef = useRef<HTMLVideoElement>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState('');
  const [isPlaying, setIsPlaying] = useState(false);

  useEffect(() => {
    if (url && videoRef.current) {
      loadStream(url);
    }
  }, [url]);

  const loadStream = async (streamUrl: string) => {
    setIsLoading(true);
    setError('');

    try {
      if (videoRef.current) {
        videoRef.current.src = streamUrl;
        onLoad?.(streamUrl);

        if (autoplay) {
          await videoRef.current.play();
        }
      }
    } catch (err: any) {
      const errorMessage = err.message || 'Failed to load stream';
      setError(errorMessage);
      onError?.(errorMessage);
    } finally {
      setIsLoading(false);
    }
  };

  const togglePlayPause = () => {
    if (videoRef.current) {
      if (isPlaying) {
        videoRef.current.pause();
      } else {
        videoRef.current.play();
      }
    }
  };

  return (
    <div className="m3u8-player">
      <div className="player-container">
        <video
          ref={videoRef}
          controls
          onPlay={() => setIsPlaying(true)}
          onPause={() => setIsPlaying(false)}
          onError={(e) => {
            const error = e.currentTarget.error;
            const errorMessage = error?.message || 'Playback error';
            setError(errorMessage);
            onError?.(errorMessage);
          }}
        />

        {isLoading && (
          <div className="loading-overlay">
            <div className="spinner">Loading...</div>
          </div>
        )}

        {error && (
          <div className="error-overlay">
            <div className="error-message">{error}</div>
          </div>
        )}
      </div>

      <div className="controls">
        <button onClick={togglePlayPause}>{isPlaying ? 'Pause' : 'Play'}</button>
      </div>
    </div>
  );
};

export default M3U8Player;

Example 8: Vue Composable

// composables/useM3U8Player.ts
import { ref, computed, onMounted, onUnmounted } from 'vue';

export const useM3U8Player = () => {
  const videoPlayer = ref<HTMLVideoElement>();
  const currentUrl = ref('');
  const isLoading = ref(false);
  const error = ref('');
  const isPlaying = ref(false);
  const volume = ref(100);
  const currentTime = ref(0);
  const duration = ref(0);

  const loadStream = async (url: string) => {
    if (!isValidUrl(url)) {
      throw new Error('Invalid URL format');
    }

    isLoading.value = true;
    error.value = '';

    try {
      currentUrl.value = url;
      if (videoPlayer.value) {
        videoPlayer.value.src = url;
      }
    } catch (err: any) {
      error.value = err.message || 'Failed to load stream';
      throw err;
    } finally {
      isLoading.value = false;
    }
  };

  const togglePlayPause = () => {
    if (!videoPlayer.value) return;

    if (isPlaying.value) {
      videoPlayer.value.pause();
    } else {
      videoPlayer.value.play();
    }
  };

  const updateVolume = (newVolume: number) => {
    if (!videoPlayer.value) return;

    videoPlayer.value.volume = newVolume / 100;
    volume.value = newVolume;
  };

  const setupEventListeners = () => {
    if (!videoPlayer.value) return;

    const video = videoPlayer.value;

    video.addEventListener('play', () => {
      isPlaying.value = true;
    });

    video.addEventListener('pause', () => {
      isPlaying.value = false;
    });

    video.addEventListener('loadedmetadata', () => {
      duration.value = video.duration;
      volume.value = Math.round(video.volume * 100);
    });

    video.addEventListener('timeupdate', () => {
      currentTime.value = video.currentTime;
    });

    video.addEventListener('error', (event) => {
      const target = event.target as HTMLVideoElement;
      error.value = target.error?.message || 'Playback error';
    });
  };

  onMounted(() => {
    setupEventListeners();
  });

  return {
    videoPlayer,
    currentUrl,
    isLoading,
    error,
    isPlaying,
    volume,
    currentTime,
    duration,
    loadStream,
    togglePlayPause,
    updateVolume,
  };
};

Example 9: URL Parameter Integration

// Load stream from URL parameters
const loadFromUrlParams = () => {
  const urlParams = new URLSearchParams(window.location.search);

  // Get stream URL
  const streamUrl = urlParams.get('stream');
  if (streamUrl && isValidUrl(streamUrl)) {
    loadStream(streamUrl);
  }

  // Get configuration
  const config = {
    autoplay: urlParams.get('autoplay') === 'true',
    volume: parseInt(urlParams.get('volume') || '100'),
    muted: urlParams.get('muted') === 'true',
  };

  // Apply configuration
  if (videoPlayer.value) {
    videoPlayer.value.volume = config.volume / 100;
    videoPlayer.value.muted = config.muted;

    if (config.autoplay) {
      videoPlayer.value.autoplay = true;
    }
  }

  return config;
};

// Usage: /m3u8-player?stream=https://example.com/stream.m3u8&autoplay=true&volume=75

Testing Examples

Example 10: Unit Testing

// tests/m3u8Player.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { loadStream, isValidUrl, formatTime } from '../m3u8Player';

describe('M3U8 Player', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('URL Validation', () => {
    it('should validate correct M3U8 URLs', () => {
      const validUrls = [
        'https://example.com/stream.m3u8',
        'https://cdn.example.com/playlist.m3u8',
        'http://localhost:8080/stream.m3u8',
      ];

      validUrls.forEach((url) => {
        expect(isValidUrl(url)).toBe(true);
      });
    });

    it('should reject invalid URLs', () => {
      const invalidUrls = [
        'not-a-url',
        'ftp://example.com/stream.m3u8',
        'https://example.com/stream.mp4',
        '',
      ];

      invalidUrls.forEach((url) => {
        expect(isValidUrl(url)).toBe(false);
      });
    });
  });

  describe('Time Formatting', () => {
    it('should format time correctly', () => {
      expect(formatTime(0)).toBe('00:00');
      expect(formatTime(65)).toBe('01:05');
      expect(formatTime(3661)).toBe('01:01:01');
      expect(formatTime(NaN)).toBe('00:00');
    });
  });

  describe('Stream Loading', () => {
    it('should load valid streams', async () => {
      const mockUrl = 'https://example.com/stream.m3u8';

      // Mock fetch for testing
      global.fetch = vi.fn().mockResolvedValue({
        ok: true,
        text: () => Promise.resolve('#EXTM3U\n#EXTINF:10,segment1.ts'),
      });

      await expect(loadStream(mockUrl)).resolves.not.toThrow();
    });

    it('should handle loading errors', async () => {
      const invalidUrl = 'invalid-url';

      await expect(loadStream(invalidUrl)).rejects.toThrow('Invalid URL format');
    });
  });
});

Example 11: Integration Testing

// tests/integration/m3u8Player.integration.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createApp } from 'vue';
import M3U8Player from '../components/M3U8Player.vue';

describe('M3U8 Player Integration', () => {
  let app: any;
  let container: HTMLElement;

  beforeAll(() => {
    container = document.createElement('div');
    document.body.appendChild(container);

    app = createApp(M3U8Player);
    app.mount(container);
  });

  afterAll(() => {
    app.unmount();
    document.body.removeChild(container);
  });

  it('should render player component', () => {
    const videoElement = container.querySelector('video');
    expect(videoElement).toBeTruthy();
  });

  it('should handle stream loading', async () => {
    const testUrl =
      'https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8';

    // Simulate loading a stream
    const loadButton = container.querySelector('[data-testid="load-button"]');
    const urlInput = container.querySelector('[data-testid="url-input"]') as HTMLInputElement;

    urlInput.value = testUrl;
    loadButton?.click();

    // Wait for loading to complete
    await new Promise((resolve) => setTimeout(resolve, 1000));

    const videoElement = container.querySelector('video') as HTMLVideoElement;
    expect(videoElement.src).toBe(testUrl);
  });
});

These examples demonstrate various ways to use and integrate the M3U8 Online Player, from basic usage to advanced integration scenarios.

Was this page helpful?