207 lines
5.9 KiB
TypeScript
207 lines
5.9 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { detectVideoFormat, VideoFile } from '@/lib/video-format-detector';
|
|
import ArtPlayerWrapper from '@/components/artplayer-wrapper';
|
|
|
|
interface UnifiedVideoPlayerProps {
|
|
video: VideoFile;
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
playerType?: 'modal' | 'inline';
|
|
useArtPlayer?: boolean;
|
|
onProgress?: (time: number) => void;
|
|
onBookmark?: (videoId: number) => void;
|
|
onUnbookmark?: (videoId: number) => void;
|
|
onRate?: (videoId: number, rating: number) => void;
|
|
showBookmarks?: boolean;
|
|
showRatings?: boolean;
|
|
scrollPosition?: number;
|
|
formatFileSize?: (bytes: number) => string;
|
|
autoplay?: boolean;
|
|
}
|
|
|
|
export default function UnifiedVideoPlayer({
|
|
video,
|
|
isOpen,
|
|
onClose,
|
|
playerType = 'modal',
|
|
useArtPlayer: forceArtPlayer = true, // Always use ArtPlayer now
|
|
onProgress,
|
|
onBookmark,
|
|
onUnbookmark,
|
|
onRate,
|
|
showBookmarks = false,
|
|
showRatings = false,
|
|
scrollPosition,
|
|
formatFileSize,
|
|
autoplay = true
|
|
}: UnifiedVideoPlayerProps) {
|
|
const [format, setFormat] = useState<ReturnType<typeof detectVideoFormat> | null>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isBookmarked, setIsBookmarked] = useState(false);
|
|
const [bookmarkCheckLoading, setBookmarkCheckLoading] = useState(true);
|
|
const [currentRating, setCurrentRating] = useState(0);
|
|
const [ratingCheckLoading, setRatingCheckLoading] = useState(true);
|
|
|
|
// Check current bookmark status and rating when video opens
|
|
useEffect(() => {
|
|
if (isOpen && video.id) {
|
|
checkBookmarkStatus();
|
|
checkRatingStatus();
|
|
}
|
|
}, [isOpen, video.id]);
|
|
|
|
const checkBookmarkStatus = async () => {
|
|
if (!video.id) return;
|
|
|
|
setBookmarkCheckLoading(true);
|
|
try {
|
|
const response = await fetch(`/api/bookmarks/${video.id}`);
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setIsBookmarked(data.isBookmarked || false);
|
|
} else {
|
|
setIsBookmarked(false);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error checking bookmark status:', error);
|
|
setIsBookmarked(false);
|
|
} finally {
|
|
setBookmarkCheckLoading(false);
|
|
}
|
|
};
|
|
|
|
const checkRatingStatus = async () => {
|
|
if (!video.id) return;
|
|
|
|
setRatingCheckLoading(true);
|
|
try {
|
|
const response = await fetch(`/api/stars/${video.id}`);
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setCurrentRating(data.rating || 0);
|
|
} else {
|
|
setCurrentRating(0);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error checking rating status:', error);
|
|
setCurrentRating(0);
|
|
} finally {
|
|
setRatingCheckLoading(false);
|
|
}
|
|
};
|
|
|
|
// Detect format on mount
|
|
useEffect(() => {
|
|
if (video) {
|
|
const detectedFormat = detectVideoFormat(video);
|
|
setFormat(detectedFormat);
|
|
setIsLoading(false);
|
|
}
|
|
}, [video]);
|
|
|
|
// Handle ArtPlayer errors with recovery
|
|
const handleArtPlayerError = useCallback((error: string) => {
|
|
console.log('ArtPlayer encountered error:', error);
|
|
|
|
// Try to recover by using direct streaming if HLS failed
|
|
if (format?.type === 'hls') {
|
|
console.log('HLS failed, trying direct streaming fallback...');
|
|
const directFormat = {
|
|
...format,
|
|
type: 'direct' as const,
|
|
url: `/api/stream/direct/${video.id}`,
|
|
supportLevel: 'native' as const
|
|
};
|
|
setFormat(directFormat);
|
|
} else {
|
|
console.log('ArtPlayer error with direct streaming, logging only');
|
|
// Just log the error, no more fallbacks needed
|
|
}
|
|
}, [format, video.id]);
|
|
|
|
// Handle progress updates
|
|
const handleProgressUpdate = useCallback((time: number) => {
|
|
if (onProgress) {
|
|
onProgress(time);
|
|
}
|
|
}, [onProgress]);
|
|
|
|
// Handle bookmark toggle
|
|
const handleBookmarkToggle = useCallback(async (videoId: number) => {
|
|
if (onBookmark) {
|
|
await onBookmark(videoId);
|
|
setIsBookmarked(true); // Update local state
|
|
}
|
|
}, [onBookmark]);
|
|
|
|
// Handle unbookmark
|
|
const handleUnbookmark = useCallback(async (videoId: number) => {
|
|
if (onUnbookmark) {
|
|
await onUnbookmark(videoId);
|
|
setIsBookmarked(false); // Update local state
|
|
}
|
|
}, [onUnbookmark]);
|
|
|
|
// Handle rating
|
|
const handleRatingUpdate = useCallback(async (videoId: number, rating: number) => {
|
|
if (onRate) {
|
|
await onRate(videoId, rating);
|
|
setCurrentRating(rating); // Update local state
|
|
}
|
|
}, [onRate]);
|
|
|
|
// Always render ArtPlayer (no more fallbacks)
|
|
const renderPlayer = () => {
|
|
// Always use ArtPlayer for both modal and inline modes
|
|
return (
|
|
<ArtPlayerWrapper
|
|
video={video}
|
|
isOpen={isOpen}
|
|
onClose={onClose}
|
|
onProgress={handleProgressUpdate}
|
|
onBookmark={handleBookmarkToggle}
|
|
onUnbookmark={handleUnbookmark}
|
|
onRate={handleRatingUpdate}
|
|
onError={handleArtPlayerError}
|
|
useArtPlayer={true}
|
|
isBookmarked={isBookmarked}
|
|
bookmarkCount={video.bookmark_count || 0}
|
|
avgRating={currentRating}
|
|
showBookmarks={showBookmarks}
|
|
showRatings={showRatings}
|
|
autoplay={autoplay}
|
|
/>
|
|
);
|
|
};
|
|
|
|
if (isLoading || bookmarkCheckLoading || ratingCheckLoading) {
|
|
return (
|
|
<div className="fixed inset-0 bg-black/90 z-50 flex items-center justify-center">
|
|
<div className="text-white text-center">
|
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-white mx-auto mb-4"></div>
|
|
<p>Loading ArtPlayer...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="unified-video-player">
|
|
{/* ArtPlayer indicator (for debugging) */}
|
|
{process.env.NODE_ENV === 'development' && format && (
|
|
<div className="fixed top-4 left-4 z-50 bg-blue-500/20 text-blue-400 rounded-full px-3 py-1.5 text-xs">
|
|
ArtPlayer - {format.supportLevel}
|
|
</div>
|
|
)}
|
|
|
|
{renderPlayer()}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Re-export for external use
|
|
export { detectVideoFormat }; |