UNPKG

@interactify-live/player-react-native

Version:

React Native library for Interactify player with media display, widgets, and MQTT integration

222 lines (221 loc) 9.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const react_native_1 = require("react-native"); // Use conditional import to avoid build-time errors for peer dependency let Video; try { Video = require("react-native-video").default; } catch (error) { // Fallback for when react-native-video is not installed Video = react_native_1.View; } const utils_1 = require("./utils"); const MediaDisplay = (0, react_1.forwardRef)((props, ref) => { const { media, width = 400, height = 300, autoplay = false, muted = false, loop = false, objectFit = "contain", backgroundColor = "#000000", onLoad, onError, onPlay, onPause, onEnded, onVideoReady, onLoadStart, onCanPlay, onBuffering, onProgress, } = props; const videoRef = (0, react_1.useRef)(null); const [currentMedia, setCurrentMedia] = (0, react_1.useState)(media || null); const [isPlaying, setIsPlaying] = (0, react_1.useState)(autoplay); const [isMuted, setIsMuted] = (0, react_1.useState)(muted); const [duration, setDuration] = (0, react_1.useState)(0); const [volume, setVolume] = (0, react_1.useState)(1); const [isBuffering, setIsBuffering] = (0, react_1.useState)(false); const [isLoaded, setIsLoaded] = (0, react_1.useState)(false); const containerStyle = { width: (0, utils_1.getDimension)(width), height: (0, utils_1.getDimension)(height), backgroundColor, overflow: "hidden", position: "relative", }; (0, react_1.useImperativeHandle)(ref, () => ({ load: (newMedia) => { console.log("MediaDisplay: Loading media", newMedia); setCurrentMedia(newMedia); setIsLoaded(false); setIsPlaying(false); setDuration(0); }, play: async () => { console.log("MediaDisplay: Play called"); if (videoRef.current && currentMedia?.type === "video") { try { videoRef.current.resume(); setIsPlaying(true); onPlay?.(currentMedia); return Promise.resolve(); } catch (error) { console.error("MediaDisplay: Play error", error); return Promise.reject(error); } } else { return Promise.reject(new Error("Cannot play: current media is not a video")); } }, pause: () => { console.log("MediaDisplay: Pause called"); if (videoRef.current && currentMedia?.type === "video") { videoRef.current.pause(); setIsPlaying(false); onPause?.(currentMedia); } }, seek: (time) => { console.log("MediaDisplay: Seek called", time); if (videoRef.current && currentMedia?.type === "video") { videoRef.current.getCurrentPosition().then((currentPos) => { videoRef.current?.seek(currentPos + time); }); } }, destroy: () => { console.log("MediaDisplay: Destroy called"); setCurrentMedia(null); setIsLoaded(false); setIsPlaying(false); setDuration(0); }, getCurrentTime: async () => { if (videoRef.current) { return videoRef.current .getCurrentPosition() .then((currentPos) => { return currentPos; }); } return 0; }, getDuration: () => { return duration; }, setCurrentTime: (time) => { if (videoRef.current) { videoRef.current.seek(time); } }, mute: () => { setIsMuted(true); }, unmute: () => { setIsMuted(false); }, isMuted: () => { return isMuted; }, isPlaying: () => { return isPlaying; }, })); // Handle media changes (0, react_1.useEffect)(() => { if (media) { setCurrentMedia(media); setIsLoaded(false); setIsPlaying(false); setDuration(0); } }, [media]); // Initialize playing state based on autoplay prop when media changes (0, react_1.useEffect)(() => { if (currentMedia?.type === "video") { // Set initial playing state based on autoplay prop setIsPlaying(autoplay); } }, [currentMedia, autoplay]); if (!currentMedia) { return (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, containerStyle] }); } if (currentMedia.type === "image") { return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, containerStyle], children: (0, jsx_runtime_1.jsx)(react_native_1.Image, { source: { uri: currentMedia.url }, style: [ styles.image, { resizeMode: (0, utils_1.getResizeMode)(objectFit), }, ], onLoad: () => { console.log("MediaDisplay: Image loaded", currentMedia); setIsLoaded(true); onLoad?.(currentMedia); }, onError: (error) => { console.error("MediaDisplay: Image error", error); onError?.(currentMedia, new Error("Failed to load image")); } }) })); } if (currentMedia.type === "video") { console.log("MediaDisplay: Rendering video with loop:", loop, "isPlaying:", isPlaying); return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, containerStyle], children: (0, jsx_runtime_1.jsx)(Video, { ref: videoRef, source: { uri: currentMedia.url }, style: [ styles.video, { resizeMode: (0, utils_1.getResizeMode)(objectFit), }, ], paused: !isPlaying, muted: isMuted, repeat: loop, volume: volume, onLoadStart: () => { console.log("MediaDisplay: Video load start", currentMedia); setIsBuffering(true); onLoadStart?.(currentMedia); }, onError: (error) => { console.error("MediaDisplay: Video error", currentMedia, error); setIsBuffering(false); onError?.(currentMedia, new Error(error.error.errorString || "Video error")); }, onLoad: (data) => { console.log("MediaDisplay: Video loaded", currentMedia, data); setIsLoaded(true); setDuration(data.duration); setIsBuffering(false); // Don't set playing state here - let the paused prop control it onLoad?.(currentMedia); }, onEnd: () => { console.log("MediaDisplay: Video end", currentMedia, "loop:", loop); // Only set playing to false if not looping if (!loop) { console.log("MediaDisplay: Setting isPlaying to false (not looping)"); setIsPlaying(false); } else { console.log("MediaDisplay: Keeping isPlaying true (looping enabled)"); } onEnded?.(currentMedia); }, onProgress: (data) => { if (data.playableDuration > 0) { onProgress?.(currentMedia, data.playableDuration, data.seekableDuration || duration); } }, onBuffer: (data) => { console.log("MediaDisplay: Video buffering", data); setIsBuffering(data.isBuffering); onBuffering?.(currentMedia, data.isBuffering); }, onReadyForDisplay: () => { console.log("MediaDisplay: Video ready for display", currentMedia); onCanPlay?.(currentMedia); onVideoReady?.(videoRef.current); }, onSeek: (data) => { console.log("MediaDisplay: Video seek", data); }, onTimedMetadata: (data) => { console.log("MediaDisplay: Video timed metadata", data); }, // iOS specific props ignoreSilentSwitch: "ignore", playInBackground: false, playWhenInactive: false, // Android specific props fullscreen: false, fullscreenOrientation: "landscape", fullscreenAutorotate: true, // Additional props for better HLS support automaticallyWaitsToMinimizeStalling: false, textTracks: [], selectedTextTrack: null, audioOnly: false, pictureInPicture: false, reportBandwidth: false }) })); } return (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, containerStyle] }); }); const styles = react_native_1.StyleSheet.create({ container: { justifyContent: "center", alignItems: "center", }, image: { width: "100%", height: "100%", }, video: { width: "100%", height: "100%", }, }); MediaDisplay.displayName = "MediaDisplay"; exports.default = MediaDisplay;