Best Alternatives to ArtPlayer for Video Playback in LunaTV

LunaTV currently ships only with ArtPlayer, but you can replace it with Video.js, Plyr, or Shaka Player by modifying the initialization logic in src/app/play/page.tsx while preserving HLS streaming compatibility and custom UI controls like skip-intro and wake-lock.

LunaTV is an open-source streaming platform built around ArtPlayer for HTML5 video delivery. While ArtPlayer handles adaptive streaming through its tight integration with HLS.js, developers seeking different UI paradigms, DRM support, or enterprise-grade error handling often explore alternatives to ArtPlayer for video playback in LunaTV. This guide examines three production-ready video players and provides the exact integration steps needed to swap the default player without disrupting the application's stream management or feature set.

How ArtPlayer is Currently Integrated in LunaTV

LunaTV instantiates ArtPlayer in the main playback pages, specifically within src/app/play/page.tsx and src/app/live/page.tsx. The player lifecycle is managed through a cleanupPlayer function that destroys instances when components unmount or stream URLs change. The project relies on artplayer-plugin-hls for adaptive bitrate streaming, consuming URLs generated by src/lib/downstream.ts and metadata from src/lib/fetchVideoDetail.ts.

Because the repository contains no abstraction layer for player swapping, switching to an alternative requires replacing the ArtPlayer initialization block while maintaining the same videoUrl prop pipeline and HLS.js-compatible manifest sources.

Top 3 Alternatives to ArtPlayer for Video Playback in LunaTV

Video.js

Video.js offers the most mature ecosystem for HTML5 video, with native HLS support and extensive plugin architecture. It fits LunaTV’s React codebase through its robust API for programmatic control.

To integrate Video.js:

  1. Install the dependency:
pnpm add video.js
  1. Replace the ArtPlayer import and initialization in src/app/play/page.tsx:
import videojs from 'video.js';
import 'video.js/dist/video-js.css';

// Inside your component
const videoRef = useRef<HTMLVideoElement>(null);
const playerRef = useRef<any>(null);

useEffect(() => {
  if (!videoUrl || !videoRef.current) return;
  
  playerRef.current = videojs(videoRef.current, {
    autoplay: true,
    fluid: true,
    html5: {
      vhs: {
        overrideNative: true,
        limitRenditionByPlayerDimensions: true
      }
    }
  });
  
  playerRef.current.src({
    src: videoUrl,
    type: 'application/x-mpegURL'
  });
  
  // Replicate cleanupPlayer behavior
  return () => {
    if (playerRef.current) {
      playerRef.current.dispose();
      playerRef.current = null;
    }
  };
}, [videoUrl]);
  1. Map LunaTV’s custom controls (skip-intro, wake-lock) to Video.js’s event system using player.on('timeupdate', ...) and player.requestFullscreen().

Plyr

Plyr provides a lightweight, customizable interface with built-in HLS support via native HTML5 video elements. It is ideal if you want a cleaner default UI without sacrificing streaming capabilities.

To implement Plyr:

  1. Add the package:
pnpm add plyr
  1. Insert a standard video element and instantiate Plyr in src/app/play/page.tsx:
import Plyr from 'plyr';
import 'plyr/dist/plyr.css';

useEffect(() => {
  if (!videoRef.current || !videoUrl) return;
  
  const player = new Plyr(videoRef.current, {
    autoplay: true,
    muted: false,
    controls: ['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'settings', 'pip', 'airplay', 'fullscreen']
  });
  
  // Load HLS manifest directly
  if (Hls.isSupported()) {
    const hls = new Hls();
    hls.loadSource(videoUrl);
    hls.attachMedia(videoRef.current);
  } else {
    videoRef.current.src = videoUrl;
  }
  
  return () => player.destroy();
}, [videoUrl]);
  1. Wire LunaTV’s episode selection and skip-config features to Plyr’s API methods like player.currentTime and player.on('seeked', ...).

Shaka Player

Shaka Player is Google’s enterprise-grade streaming library, offering superior DRM support and robust error recovery for DASH and HLS manifests. Choose this alternative if LunaTV requires Widevine or FairPlay integration.

To configure Shaka Player:

  1. Install via package manager:
pnpm add shaka-player
  1. Initialize the player in the playback page:
import shaka from 'shaka-player';

useEffect(() => {
  if (!videoRef.current || !videoUrl) return;
  
  const player = new shaka.Player(videoRef.current);
  
  // Check browser support
  if (shaka.Player.isBrowserSupported()) {
    player.load(videoUrl).catch(error => {
      console.error('Error loading manifest:', error);
    });
  }
  
  // Replicate cleanupPlayer
  return () => {
    player.destroy();
  };
}, [videoUrl]);
  1. Implement skip-intro/outro logic using Shaka’s player.seekRange() to determine valid seek boundaries and player.getMediaElement().currentTime for precise positioning.

Step-by-Step Migration Guide

Migrating from ArtPlayer to any alternative requires modifying exactly one critical code block in src/app/play/page.tsx. Locate the function that currently calls new ArtPlayer({...}) and replace it with your chosen player’s initialization pattern.

Preserve these LunaTV dependencies during migration:

  • Stream sources: Continue consuming videoUrl values from src/lib/downstream.ts, as all three alternatives support the HLS manifests already generated by LunaTV’s backend.
  • Metadata: Retain imports from src/lib/fetchVideoDetail.ts to populate episode titles and thumbnails.
  • Cleanup logic: Ensure your new player implementation mirrors the cleanupPlayer pattern to prevent memory leaks when switching episodes or navigating away from src/app/live/page.tsx.

Update package.json to remove artplayer and artplayer-plugin-hls if eliminating ArtPlayer entirely, or keep them alongside new dependencies if supporting multiple players concurrently.

Preserving LunaTV Features During Player Migration

When implementing alternatives to ArtPlayer for video playback in LunaTV, you must manually reconnect three core features:

  • Skip Intro/Outro: Store timestamp offsets in your component state and call player.currentTime(skipEndTimestamp) when the playhead reaches the intro start time. Video.js uses player.currentTime(), Plyr uses player.forward(), and Shaka uses player.getMediaElement().currentTime = value.

  • Wake Lock: Maintain the existing WakeLock API calls in your page component, independent of the video player instance, since this feature relies on browser permissions rather than player APIs.

  • Episode Selection: Continue using the episode data from fetchVideoDetail.ts to populate your UI controls. When users select a new episode, update the videoUrl state; your player’s useEffect dependency array will trigger the cleanup and re-initialization cycle automatically.

Summary

  • LunaTV hardcodes ArtPlayer in src/app/play/page.tsx and src/app/live/page.tsx with no built-in abstraction for alternative players.
  • Video.js, Plyr, and Shaka Player all support LunaTV’s existing HLS stream URLs generated by src/lib/downstream.ts.
  • Replacement requires swapping the ArtPlayer initialization block and cleanupPlayer logic while preserving the videoUrl prop pipeline.
  • Custom features like skip-intro, wake-lock, and episode selection must be mapped to the new player’s specific API methods.
  • All three alternatives can coexist with LunaTV’s React architecture and streaming infrastructure without modifying backend endpoints.

Frequently Asked Questions

Does LunaTV support multiple video players out of the box?

No. According to the MoonTechLab/LunaTV source code, the repository ships exclusively with ArtPlayer and artplayer-plugin-hls. There is no configuration toggle or plugin system to enable alternative players without modifying the playback page source files.

Which video player is best for HLS streaming in LunaTV?

Video.js is generally the best drop-in replacement for HLS streaming due to its mature VHS engine and extensive documentation. However, if you require DRM support for protected content, Shaka Player provides superior Widevine and FairPlay integration compared to both ArtPlayer and Video.js.

How do I replace ArtPlayer without breaking existing features?

Focus on three integration points: maintain the videoUrl consumption pattern from src/lib/downstream.ts, replicate the cleanupPlayer disposal logic to prevent memory leaks, and remap LunaTV’s custom controls (skip-intro, airplay, wake-lock) to the new player’s event system. The underlying episode metadata from src/lib/fetchVideoDetail.ts remains compatible regardless of which player you choose.

Will switching players affect the HLS stream URLs?

No. The stream URLs generated by src/lib/downstream.ts are standard HLS manifests compatible with any HTML5 video player. Video.js, Plyr, and Shaka Player all support the same videoUrl format currently used by ArtPlayer, ensuring your backend configuration remains unchanged during migration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →