mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-15 00:33:44 +08:00
- Added EpisodeList component for displaying a list of episodes with selection functionality. - Created PlayerError component to handle and display video playback errors. - Developed VideoMetadata component to show detailed information about the video. - Implemented VideoPlayer component for video playback with error handling and loading states. - Introduced EmptyState and NoResults components for search results handling. - Created ResultsHeader component to display search results summary. - Developed SearchForm component for user input and search initiation. - Implemented SourceBadges component to show available video sources. - Created VideoGrid component to display search results in a grid format. - Added useSearchCache and useSearchStream hooks for managing search state and caching results. - Implemented useVideoPlayer hook for fetching and managing video details. - Added utility function to map source IDs to their respective names.
113 lines
3.4 KiB
TypeScript
113 lines
3.4 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
|
|
export interface VideoData {
|
|
vod_id: string;
|
|
vod_name: string;
|
|
vod_pic?: string;
|
|
vod_content?: string;
|
|
vod_actor?: string;
|
|
vod_director?: string;
|
|
vod_year?: string;
|
|
vod_area?: string;
|
|
type_name?: string;
|
|
episodes?: Array<{ name?: string; url: string }>;
|
|
}
|
|
|
|
export interface UseVideoPlayerReturn {
|
|
videoData: VideoData | null;
|
|
loading: boolean;
|
|
videoError: string;
|
|
currentEpisode: number;
|
|
playUrl: string;
|
|
setCurrentEpisode: (index: number) => void;
|
|
setPlayUrl: (url: string) => void;
|
|
setVideoError: (error: string) => void;
|
|
fetchVideoDetails: () => Promise<void>;
|
|
}
|
|
|
|
export function useVideoPlayer(
|
|
videoId: string | null,
|
|
source: string | null,
|
|
episodeParam: string | null
|
|
): UseVideoPlayerReturn {
|
|
const [videoData, setVideoData] = useState<VideoData | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [currentEpisode, setCurrentEpisode] = useState(0);
|
|
const [playUrl, setPlayUrl] = useState('');
|
|
const [videoError, setVideoError] = useState<string>('');
|
|
|
|
const fetchVideoDetails = useCallback(async () => {
|
|
if (!videoId || !source) return;
|
|
|
|
try {
|
|
setVideoError('');
|
|
setLoading(true);
|
|
|
|
const response = await fetch(`/api/detail?id=${videoId}&source=${source}`);
|
|
const data = await response.json();
|
|
|
|
console.log('Video detail API response:', data);
|
|
|
|
if (!response.ok) {
|
|
if (response.status === 404) {
|
|
setVideoError(data.error || 'This video source is not available. Please go back and try another source.');
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
throw new Error(data.error || `HTTP ${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
if (data.success && data.data) {
|
|
console.log('Video data received:', {
|
|
id: data.data.vod_id,
|
|
name: data.data.vod_name,
|
|
episodeCount: data.data.episodes?.length || 0,
|
|
firstEpisodeUrl: data.data.episodes?.[0]?.url
|
|
});
|
|
|
|
setVideoData(data.data);
|
|
setLoading(false);
|
|
|
|
if (data.data.episodes && data.data.episodes.length > 0) {
|
|
const episodeIndex = episodeParam ? parseInt(episodeParam, 10) : 0;
|
|
const validIndex = (episodeIndex >= 0 && episodeIndex < data.data.episodes.length) ? episodeIndex : 0;
|
|
|
|
const episodeUrl = data.data.episodes[validIndex].url;
|
|
console.log('Setting play URL for episode', validIndex, ':', episodeUrl);
|
|
setCurrentEpisode(validIndex);
|
|
setPlayUrl(episodeUrl);
|
|
} else {
|
|
console.warn('No episodes found in video data');
|
|
setVideoError('No playable episodes available for this video from this source');
|
|
}
|
|
} else {
|
|
throw new Error(data.error || 'Invalid response from API');
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to fetch video details:', error);
|
|
setVideoError(error instanceof Error ? error.message : 'Failed to load video details. Please try another source.');
|
|
setLoading(false);
|
|
}
|
|
}, [videoId, source, episodeParam]);
|
|
|
|
useEffect(() => {
|
|
if (videoId && source) {
|
|
fetchVideoDetails();
|
|
}
|
|
}, [videoId, source, fetchVideoDetails]);
|
|
|
|
return {
|
|
videoData,
|
|
loading,
|
|
videoError,
|
|
currentEpisode,
|
|
playUrl,
|
|
setCurrentEpisode,
|
|
setPlayUrl,
|
|
setVideoError,
|
|
fetchVideoDetails,
|
|
};
|
|
}
|