@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
JavaScript
;
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;