feat: add video resolution detection and expose callback in player components

This commit is contained in:
kuekhaoyang
2026-03-12 23:12:55 +08:00
parent 0e87c94a64
commit 49fd5b70f4
5 changed files with 47 additions and 0 deletions
+6
View File
@@ -9,6 +9,7 @@ import { EpisodeList } from '@/components/player/EpisodeList';
import { PlayerError } from '@/components/player/PlayerError';
import { SourceInfo } from '@/components/player/EpisodeList';
import type { VideoSource } from '@/lib/types';
import type { VideoResolutionInfo } from '@/components/player/hooks/useVideoResolution';
import { useVideoPlayer } from '@/lib/hooks/useVideoPlayer';
import { useHistory } from '@/lib/store/history-store';
import { FavoritesSidebar } from '@/components/favorites/FavoritesSidebar';
@@ -265,6 +266,9 @@ function PlayerContent() {
const [currentSourceId, setCurrentSourceId] = useState(source);
const playerTimeRef = useRef(0);
// Track detected video resolution from the player
const [detectedResolution, setDetectedResolution] = useState<VideoResolutionInfo | null>(null);
// Add initial history entry when video data is loaded
useEffect(() => {
if (videoData && playUrl && videoId) {
@@ -363,6 +367,7 @@ function PlayerContent() {
videoTitle={videoData?.vod_name || title || ''}
episodeName={videoData?.episodes?.[currentEpisode]?.name || ''}
externalTimeRef={playerTimeRef}
onResolutionDetected={setDetectedResolution}
/>
<div className="hidden lg:block">
<VideoMetadata
@@ -425,6 +430,7 @@ function PlayerContent() {
onToggleReverse={handleToggleReverse}
sources={groupedSources.length > 0 ? groupedSources : undefined}
currentSource={currentSourceId || source || ''}
currentResolution={detectedResolution}
onSourceChange={(newSource) => {
const params = new URLSearchParams();
params.set('id', String(newSource.id));
+2
View File
@@ -18,6 +18,8 @@ interface CustomVideoPlayerProps {
// Danmaku props
videoTitle?: string;
episodeName?: string;
// Resolution callback
onResolutionDetected?: (info: import('./hooks/useVideoResolution').VideoResolutionInfo) => void;
}
/**
+10
View File
@@ -31,6 +31,8 @@ interface DesktopVideoPlayerProps {
// Danmaku props
videoTitle?: string;
episodeName?: string;
// Resolution callback
onResolutionDetected?: (info: import('./hooks/useVideoResolution').VideoResolutionInfo) => void;
}
export function DesktopVideoPlayer({
@@ -46,6 +48,7 @@ export function DesktopVideoPlayer({
isReversed = false,
videoTitle = '',
episodeName = '',
onResolutionDetected,
}: DesktopVideoPlayerProps) {
const { refs, data, actions } = useDesktopPlayerState();
const { fullscreenType: settingsFullscreenType } = usePlayerSettings();
@@ -55,6 +58,13 @@ export function DesktopVideoPlayer({
// Detect actual video resolution
const videoResolution = useVideoResolution(refs.videoRef);
// Notify parent when resolution is detected
React.useEffect(() => {
if (videoResolution && onResolutionDetected) {
onResolutionDetected(videoResolution);
}
}, [videoResolution, onResolutionDetected]);
// Danmaku
const { danmakuEnabled, setDanmakuEnabled, comments: danmakuComments } = useDanmaku({
videoTitle,
+25
View File
@@ -10,6 +10,7 @@ import { Button } from '@/components/ui/Button';
import { useKeyboardNavigation } from '@/lib/hooks/useKeyboardNavigation';
import { settingsStore } from '@/lib/store/settings-store';
import { extractQualityLabel } from '@/lib/utils/video';
import type { VideoResolutionInfo } from './hooks/useVideoResolution';
interface Episode {
name?: string;
@@ -36,6 +37,8 @@ interface EpisodeListProps {
sources?: SourceInfo[];
currentSource?: string;
onSourceChange?: (source: SourceInfo) => void;
// Actual detected resolution for the current source
currentResolution?: VideoResolutionInfo | null;
}
export function EpisodeList({
@@ -47,6 +50,7 @@ export function EpisodeList({
sources,
currentSource,
onSourceChange,
currentResolution,
}: EpisodeListProps) {
const listRef = useRef<HTMLDivElement>(null);
const buttonRefs = useRef<(HTMLButtonElement | null)[]>([]);
@@ -228,6 +232,11 @@ export function EpisodeList({
<span className="text-sm font-medium text-[var(--text-color)] truncate">
{currentSourceInfo?.sourceName || currentSourceInfo?.source || '当前来源'}
</span>
{currentResolution && (
<span className={`inline-flex items-center px-1 py-0 rounded text-[9px] font-bold text-white ${currentResolution.color} flex-shrink-0`}>
{currentResolution.label}
</span>
)}
<Badge variant="primary" className="flex-shrink-0">{sources!.length}</Badge>
</div>
<Icons.ChevronDown
@@ -322,6 +331,14 @@ export function EpisodeList({
<div className="font-medium text-sm truncate flex items-center gap-1.5">
{source.sourceName || source.source}
{(() => {
// For the current source, prefer actual detected resolution
if (isCurrent && currentResolution) {
return (
<span className={`inline-flex items-center px-1 py-0 rounded text-[9px] font-bold text-white ${currentResolution.color}`}>
{currentResolution.label}
</span>
);
}
const qb = extractQualityLabel(source.remarks);
return qb ? (
<span className={`inline-flex items-center px-1 py-0 rounded text-[9px] font-bold text-white ${qb.color}`}>
@@ -402,6 +419,14 @@ export function EpisodeList({
<div className="font-medium text-sm truncate flex items-center gap-1.5">
{source.sourceName || source.source}
{(() => {
// For the current source, prefer actual detected resolution
if (isCurrent && currentResolution) {
return (
<span className={`inline-flex items-center px-1 py-0 rounded text-[9px] font-bold text-white ${currentResolution.color}`}>
{currentResolution.label}
</span>
);
}
const qb = extractQualityLabel(source.remarks);
return qb ? (
<span className={`inline-flex items-center px-1 py-0 rounded text-[9px] font-bold text-white ${qb.color}`}>
+4
View File
@@ -25,6 +25,8 @@ interface VideoPlayerProps {
episodeName?: string;
// Expose current time to parent
externalTimeRef?: React.MutableRefObject<number>;
// Resolution callback
onResolutionDetected?: (info: import('./hooks/useVideoResolution').VideoResolutionInfo) => void;
}
export function VideoPlayer({
@@ -39,6 +41,7 @@ export function VideoPlayer({
videoTitle,
episodeName,
externalTimeRef,
onResolutionDetected,
}: VideoPlayerProps) {
const [videoError, setVideoError] = useState<string>('');
const [useProxy, setUseProxy] = useState(false);
@@ -244,6 +247,7 @@ export function VideoPlayer({
isReversed={isReversed}
videoTitle={videoTitle}
episodeName={episodeName}
onResolutionDetected={onResolutionDetected}
/>
)}
</Card>