diff --git a/frontend/src/components/FloatingVideo.jsx b/frontend/src/components/FloatingVideo.jsx
index 9d8fb09e..cbb5e1a8 100644
--- a/frontend/src/components/FloatingVideo.jsx
+++ b/frontend/src/components/FloatingVideo.jsx
@@ -12,17 +12,55 @@ export default function FloatingVideo() {
const videoRef = useRef(null);
const playerRef = useRef(null);
const videoContainerRef = useRef(null);
+ const isLoadingRef = useRef(false);
+
+ // Safely destroy the player to prevent errors
+ const safeDestroyPlayer = () => {
+ try {
+ if (playerRef.current) {
+ // Set a flag to ignore abort errors
+ isLoadingRef.current = false;
+
+ // First unload the source to stop any in-progress fetches
+ if (videoRef.current) {
+ // Remove src attribute and force a load to clear any pending requests
+ videoRef.current.removeAttribute('src');
+ videoRef.current.load();
+ }
+
+ // Pause the player first
+ try {
+ playerRef.current.pause();
+ } catch (e) {
+ // Ignore pause errors
+ }
+
+ // Use a try-catch block specifically for the destroy call
+ try {
+ playerRef.current.destroy();
+ } catch (error) {
+ // Ignore expected abort errors
+ if (error.name !== 'AbortError' && !error.message?.includes('aborted')) {
+ console.log("Error during player destruction:", error.message);
+ }
+ } finally {
+ playerRef.current = null;
+ }
+ }
+ } catch (error) {
+ console.log("Error during player cleanup:", error);
+ playerRef.current = null;
+ }
+ };
useEffect(() => {
if (!isVisible || !streamUrl) {
+ safeDestroyPlayer();
return;
}
// Check if we have an existing player and clean it up
- if (playerRef.current) {
- playerRef.current.destroy();
- playerRef.current = null;
- }
+ safeDestroyPlayer();
// Debug log to help diagnose stream issues
console.log("Attempting to play stream:", streamUrl);
@@ -30,6 +68,9 @@ export default function FloatingVideo() {
try {
// If the browser supports MSE for live playback, initialize mpegts.js
if (mpegts.getFeatureList().mseLivePlayback) {
+ // Set loading flag
+ isLoadingRef.current = true;
+
const player = mpegts.createPlayer({
type: 'mpegts', // MPEG-TS format
url: streamUrl,
@@ -39,15 +80,35 @@ export default function FloatingVideo() {
liveBufferLatencyChasing: true,
liveSync: true,
cors: true, // Enable CORS for cross-domain requests
+ // Add error recovery options
+ autoCleanupSourceBuffer: true,
+ autoCleanupMaxBackwardDuration: 10,
+ autoCleanupMinBackwardDuration: 5,
+ reuseRedirectedURL: true,
});
player.attachMediaElement(videoRef.current);
+ // Add events to track loading state
+ player.on(mpegts.Events.LOADING_COMPLETE, () => {
+ isLoadingRef.current = false;
+ });
+
+ player.on(mpegts.Events.METADATA_ARRIVED, () => {
+ isLoadingRef.current = false;
+ });
+
// Add error event handler
player.on(mpegts.Events.ERROR, (errorType, errorDetail) => {
- console.error('Player error:', errorType, errorDetail);
+ isLoadingRef.current = false;
+
+ // Filter out aborted errors
+ if (errorType !== 'NetworkError' || !errorDetail?.includes('aborted')) {
+ console.error('Player error:', errorType, errorDetail);
+ }
+
// If it's a format issue, show a helpful message
- if (errorDetail.includes('Unsupported media type')) {
+ if (errorDetail?.includes('Unsupported media type')) {
const message = document.createElement('div');
message.textContent = "Unsupported stream format. Please try a different stream.";
message.style.position = 'absolute';
@@ -57,29 +118,48 @@ export default function FloatingVideo() {
message.style.color = 'white';
message.style.textAlign = 'center';
message.style.width = '100%';
- videoRef.current.parentNode.appendChild(message);
+ if (videoRef.current?.parentNode) {
+ videoRef.current.parentNode.appendChild(message);
+ }
}
});
player.load();
- player.play();
+
+ // Don't auto-play until we've loaded properly
+ player.on(mpegts.Events.MEDIA_INFO, () => {
+ try {
+ player.play().catch(e => {
+ console.log("Auto-play prevented:", e);
+ });
+ } catch (e) {
+ console.log("Error during play:", e);
+ }
+ });
// Store player instance so we can clean up later
playerRef.current = player;
}
} catch (error) {
+ isLoadingRef.current = false;
console.error("Error initializing player:", error);
}
// Cleanup when component unmounts or streamUrl changes
return () => {
- if (playerRef.current) {
- playerRef.current.destroy();
- playerRef.current = null;
- }
+ safeDestroyPlayer();
};
}, [isVisible, streamUrl]);
+ // Modified hideVideo handler to clean up player first
+ const handleClose = () => {
+ safeDestroyPlayer();
+ // Small delay before hiding the video component to ensure cleanup is complete
+ setTimeout(() => {
+ hideVideo();
+ }, 50);
+ };
+
// If the floating video is hidden or no URL is selected, do not render
if (!isVisible || !streamUrl) {
return null;
@@ -103,7 +183,7 @@ export default function FloatingVideo() {
>
{/* Simple header row with a close button */}
-
+
{/* The