Skip to content

Web Player Examples

Browser-based HLS video player implementations.

Video.js Player

Basic Setup

html
<!DOCTYPE html>
<html>
<head>
  <title>Lunar Stream Player</title>
  <link href="https://vjs.zencdn.net/8.0.4/video-js.css" rel="stylesheet">
  <style>
    body { margin: 0; background: #000; }
    .container { max-width: 1280px; margin: 0 auto; padding: 20px; }
    .video-js { width: 100%; aspect-ratio: 16/9; }
    .status { color: #fff; padding: 10px; text-align: center; }
  </style>
</head>
<body>
  <div class="container">
    <div id="status" class="status">Loading...</div>
    <video id="player" class="video-js vjs-default-skin" controls></video>
  </div>

  <script src="https://vjs.zencdn.net/8.0.4/video.min.js"></script>
  <script>
    const streamKey = new URLSearchParams(window.location.search).get('key');
    const statusEl = document.getElementById('status');

    async function loadStream() {
      if (!streamKey) {
        statusEl.textContent = 'No stream key provided. Add ?key=YOUR_STREAM_KEY to URL';
        return;
      }

      try {
        const response = await fetch(
          `https://api.lunarstream.kozow.com/api/v1/livestreams/stream-info/${streamKey}`
        );
        const { data } = await response.json();

        if (data.status === 'STARTED') {
          statusEl.textContent = `Now Playing: ${data.name}`;
          initPlayer(data.hlsSources);
        } else {
          statusEl.textContent = `Stream "${data.name}" is ${data.status}`;
        }
      } catch (error) {
        statusEl.textContent = 'Stream not found';
      }
    }

    function initPlayer(hlsSources) {
      const player = videojs('player', {
        liveui: true,
        html5: { vhs: { overrideNative: true } }
      });

      let currentIndex = 0;
      player.src({ src: hlsSources[currentIndex].url, type: 'application/x-mpegURL' });

      player.on('error', () => {
        currentIndex++;
        if (currentIndex < hlsSources.length) {
          player.src({ src: hlsSources[currentIndex].url, type: 'application/x-mpegURL' });
          player.play();
        }
      });

      player.play();
    }

    loadStream();
  </script>
</body>
</html>

HLS.js Player

html
<!DOCTYPE html>
<html>
<head>
  <title>HLS.js Player</title>
  <style>
    body { margin: 0; background: #000; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
    video { max-width: 100%; max-height: 100vh; }
  </style>
</head>
<body>
  <video id="video" controls></video>

  <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
  <script>
    const video = document.getElementById('video');
    const hlsUrl = 'https://stream.lunarstream.kozow.com/live/YOUR_STREAM_KEY/hls.m3u8';

    if (Hls.isSupported()) {
      const hls = new Hls({
        enableWorker: true,
        lowLatencyMode: true
      });

      hls.loadSource(hlsUrl);
      hls.attachMedia(video);

      hls.on(Hls.Events.MANIFEST_PARSED, () => {
        video.play();
      });

      hls.on(Hls.Events.ERROR, (event, data) => {
        if (data.fatal) {
          switch (data.type) {
            case Hls.ErrorTypes.NETWORK_ERROR:
              console.log('Network error, trying to recover...');
              hls.startLoad();
              break;
            case Hls.ErrorTypes.MEDIA_ERROR:
              console.log('Media error, trying to recover...');
              hls.recoverMediaError();
              break;
            default:
              hls.destroy();
              break;
          }
        }
      });
    } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
      video.src = hlsUrl;
      video.addEventListener('loadedmetadata', () => video.play());
    }
  </script>
</body>
</html>

React Component

jsx
import React, { useEffect, useRef, useState } from 'react';
import videojs from 'video.js';
import 'video.js/dist/video-js.css';

const StreamPlayer = ({ streamKey }) => {
  const videoRef = useRef(null);
  const playerRef = useRef(null);
  const [status, setStatus] = useState('loading');

  useEffect(() => {
    const loadStream = async () => {
      const response = await fetch(
        `https://api.lunarstream.kozow.com/api/v1/livestreams/stream-info/${streamKey}`
      );
      const { data } = await response.json();

      if (data.status === 'STARTED' && data.hlsSources?.length > 0) {
        setStatus('playing');
        playerRef.current = videojs(videoRef.current, { liveui: true });
        playerRef.current.src({ src: data.hlsSources[0].url, type: 'application/x-mpegURL' });
        playerRef.current.play();
      } else {
        setStatus(data.status);
      }
    };

    loadStream();
    return () => playerRef.current?.dispose();
  }, [streamKey]);

  if (status === 'loading') return <div>Loading...</div>;
  if (status !== 'playing') return <div>Stream is {status}</div>;

  return <video ref={videoRef} className="video-js vjs-default-skin" />;
};

export default StreamPlayer;

Released under the MIT License.