mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-15 00:33:44 +08:00
Refactor video source identifiers and enhance player navigation with improved button functionality
This commit is contained in:
+89
-92
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useState, useRef } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { ThemeSwitcher } from '@/components/ThemeSwitcher';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
@@ -48,9 +49,9 @@ export default function Home() {
|
||||
|
||||
try {
|
||||
// Get all enabled source IDs
|
||||
const sourceIds = ['custom_0', 'custom_1', 'custom_2', 'custom_3', 'custom_4',
|
||||
'custom_5', 'custom_6', 'custom_7', 'custom_8', 'custom_9',
|
||||
'custom_10', 'custom_11', 'custom_12', 'custom_13', 'custom_14', 'custom_15'];
|
||||
const sourceIds = ['dytt', 'ruyi', 'baofeng', 'tianya', 'feifan',
|
||||
'sanliuling', 'wolong', 'jisu', 'mozhua', 'modu',
|
||||
'zuida', 'yinghua', 'baiduyun', 'wujin', 'wangwang', 'ikun'];
|
||||
|
||||
// Use streaming API
|
||||
const response = await fetch('/api/search-stream', {
|
||||
@@ -180,41 +181,26 @@ export default function Home() {
|
||||
|
||||
const getSourceName = (sourceId: string): string => {
|
||||
const sourceNames: Record<string, string> = {
|
||||
'custom_0': '电影天堂',
|
||||
'custom_1': '如意',
|
||||
'custom_2': '暴风',
|
||||
'custom_3': '天涯',
|
||||
'custom_4': '非凡影视',
|
||||
'custom_5': '360',
|
||||
'custom_6': '卧龙',
|
||||
'custom_7': '极速',
|
||||
'custom_8': '魔爪',
|
||||
'custom_9': '魔都',
|
||||
'custom_10': '海外看',
|
||||
'custom_11': '新浪',
|
||||
'custom_12': '光速',
|
||||
'custom_13': '红牛',
|
||||
'custom_14': '樱花',
|
||||
'custom_15': '飞速',
|
||||
'dytt': '电影天堂',
|
||||
'ruyi': '如意',
|
||||
'baofeng': '暴风',
|
||||
'tianya': '天涯',
|
||||
'feifan': '非凡影视',
|
||||
'sanliuling': '360',
|
||||
'wolong': '卧龙',
|
||||
'jisu': '极速',
|
||||
'mozhua': '魔爪',
|
||||
'modu': '魔都',
|
||||
'zuida': '最大',
|
||||
'yinghua': '樱花',
|
||||
'baiduyun': '百度云',
|
||||
'wujin': '无尽',
|
||||
'wangwang': '旺旺',
|
||||
'ikun': 'iKun',
|
||||
};
|
||||
return sourceNames[sourceId] || sourceId;
|
||||
};
|
||||
|
||||
const handleVideoClick = (video: any) => {
|
||||
// Abort ongoing search when user clicks a video
|
||||
if (abortControllerRef.current && loading) {
|
||||
abortControllerRef.current.abort();
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
id: video.vod_id,
|
||||
source: video.source,
|
||||
title: video.vod_name,
|
||||
});
|
||||
router.push(`/player?${params.toString()}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen">
|
||||
{/* Glass Navbar */}
|
||||
@@ -295,7 +281,7 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
{/* Results Section */}
|
||||
{(results.length >= 10 || (!loading && results.length > 0)) && (
|
||||
{(results.length >= 1 || (!loading && results.length > 0)) && (
|
||||
<div className="animate-fade-in">
|
||||
<div className="flex flex-col gap-4 mb-6">
|
||||
<div className="flex items-center justify-between flex-wrap gap-3">
|
||||
@@ -348,67 +334,78 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6 gap-4 md:gap-6">
|
||||
{results.map((video, index) => (
|
||||
<Card
|
||||
key={`${video.vod_id}-${index}`}
|
||||
onClick={() => handleVideoClick(video)}
|
||||
className={`p-0 overflow-hidden group cursor-pointer ${video.isNew ? 'animate-scale-in' : ''}`}
|
||||
>
|
||||
{/* Poster */}
|
||||
<div className="relative aspect-[2/3] bg-[color-mix(in_srgb,var(--glass-bg)_50%,transparent)] overflow-hidden" style={{ borderRadius: 'var(--radius-2xl) var(--radius-2xl) 0 0' }}>
|
||||
{video.vod_pic ? (
|
||||
<img
|
||||
src={video.vod_pic}
|
||||
alt={video.vod_name}
|
||||
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<Icons.Film size={64} className="text-[var(--text-color-secondary)]" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Source Badge - Top Left */}
|
||||
{video.sourceName && (
|
||||
<div className="absolute top-2 left-2 z-10">
|
||||
<Badge variant="primary" className="text-xs backdrop-blur-md bg-[var(--accent-color)]/90">
|
||||
{video.sourceName}
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Overlay */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||||
<div className="absolute bottom-0 left-0 right-0 p-3">
|
||||
{video.type_name && (
|
||||
<Badge variant="secondary" className="text-xs mb-2">
|
||||
{video.type_name}
|
||||
</Badge>
|
||||
)}
|
||||
{video.vod_year && (
|
||||
<div className="flex items-center gap-1 text-white/80 text-xs">
|
||||
<Icons.Calendar size={12} />
|
||||
<span>{video.vod_year}</span>
|
||||
{results.map((video, index) => {
|
||||
const videoUrl = `/player?${new URLSearchParams({
|
||||
id: video.vod_id,
|
||||
source: video.source,
|
||||
title: video.vod_name,
|
||||
}).toString()}`;
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={`${video.vod_id}-${index}`}
|
||||
href={videoUrl}
|
||||
>
|
||||
<Card
|
||||
className={`p-0 overflow-hidden group cursor-pointer ${video.isNew ? 'animate-scale-in' : ''}`}
|
||||
>
|
||||
{/* Poster */}
|
||||
<div className="relative aspect-[2/3] bg-[color-mix(in_srgb,var(--glass-bg)_50%,transparent)] overflow-hidden" style={{ borderRadius: 'var(--radius-2xl) var(--radius-2xl) 0 0' }}>
|
||||
{video.vod_pic ? (
|
||||
<img
|
||||
src={video.vod_pic}
|
||||
alt={video.vod_name}
|
||||
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
loading="lazy"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<Icons.Film size={64} className="text-[var(--text-color-secondary)]" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Source Badge - Top Left */}
|
||||
{video.sourceName && (
|
||||
<div className="absolute top-2 left-2 z-10">
|
||||
<Badge variant="primary" className="text-xs backdrop-blur-md bg-[var(--accent-color)]/90">
|
||||
{video.sourceName}
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Overlay */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300">
|
||||
<div className="absolute bottom-0 left-0 right-0 p-3">
|
||||
{video.type_name && (
|
||||
<Badge variant="secondary" className="text-xs mb-2">
|
||||
{video.type_name}
|
||||
</Badge>
|
||||
)}
|
||||
{video.vod_year && (
|
||||
<div className="flex items-center gap-1 text-white/80 text-xs">
|
||||
<Icons.Calendar size={12} />
|
||||
<span>{video.vod_year}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Info */}
|
||||
<div className="p-3">
|
||||
<h4 className="font-semibold text-sm text-[var(--text-color)] line-clamp-2 group-hover:text-[var(--accent-color)] transition-colors">
|
||||
{video.vod_name}
|
||||
</h4>
|
||||
{video.vod_remarks && (
|
||||
<p className="text-xs text-[var(--text-color-secondary)] mt-1 line-clamp-1">
|
||||
{video.vod_remarks}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
{/* Info */}
|
||||
<div className="p-3">
|
||||
<h4 className="font-semibold text-sm text-[var(--text-color)] line-clamp-2 group-hover:text-[var(--accent-color)] transition-colors">
|
||||
{video.vod_name}
|
||||
</h4>
|
||||
{video.vod_remarks && (
|
||||
<p className="text-xs text-[var(--text-color-secondary)] mt-1 line-clamp-1">
|
||||
{video.vod_remarks}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
+41
-24
@@ -7,6 +7,7 @@ import { Button } from '@/components/ui/Button';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { ThemeSwitcher } from '@/components/ThemeSwitcher';
|
||||
import { Icons } from '@/components/ui/Icon';
|
||||
import Image from 'next/image';
|
||||
|
||||
function PlayerContent() {
|
||||
const searchParams = useSearchParams();
|
||||
@@ -27,22 +28,22 @@ function PlayerContent() {
|
||||
const getSourceName = (sourceId: string | null): string => {
|
||||
if (!sourceId) return '';
|
||||
const sourceNames: Record<string, string> = {
|
||||
'custom_0': '电影天堂',
|
||||
'custom_1': '如意',
|
||||
'custom_2': '暴风',
|
||||
'custom_3': '天涯',
|
||||
'custom_4': '非凡影视',
|
||||
'custom_5': '360',
|
||||
'custom_6': '卧龙',
|
||||
'custom_7': '极速',
|
||||
'custom_8': '魔爪',
|
||||
'custom_9': '魔都',
|
||||
'custom_10': '海外看',
|
||||
'custom_11': '新浪',
|
||||
'custom_12': '光速',
|
||||
'custom_13': '红牛',
|
||||
'custom_14': '樱花',
|
||||
'custom_15': '飞速',
|
||||
'dytt': '电影天堂',
|
||||
'ruyi': '如意',
|
||||
'baofeng': '暴风',
|
||||
'tianya': '天涯',
|
||||
'feifan': '非凡影视',
|
||||
'sanliuling': '360',
|
||||
'wolong': '卧龙',
|
||||
'jisu': '极速',
|
||||
'mozhua': '魔爪',
|
||||
'modu': '魔都',
|
||||
'zuida': '最大',
|
||||
'yinghua': '樱花',
|
||||
'baiduyun': '百度云',
|
||||
'wujin': '无尽',
|
||||
'wangwang': '旺旺',
|
||||
'ikun': 'iKun',
|
||||
};
|
||||
return sourceNames[sourceId] || sourceId;
|
||||
};
|
||||
@@ -154,14 +155,30 @@ function PlayerContent() {
|
||||
<nav className="sticky top-4 z-50 mx-4 mt-4 mb-8">
|
||||
<div className="max-w-7xl mx-auto bg-[var(--glass-bg)] backdrop-blur-[25px] saturate-[180%] border border-[var(--glass-border)] rounded-[var(--radius-2xl)] shadow-[0_4px_12px_color-mix(in_srgb,var(--shadow-color)_40%,transparent)] px-6 py-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => router.back()}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Icons.ChevronLeft size={20} />
|
||||
<span>返回</span>
|
||||
</Button>
|
||||
<div className="flex items-center gap-3">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => router.push('/')}
|
||||
className="flex items-center gap-2 p-2 w-10 h-10"
|
||||
title="返回首页"
|
||||
>
|
||||
<Image
|
||||
src="/icon.png"
|
||||
alt="KVideo"
|
||||
width={24}
|
||||
height={24}
|
||||
className="object-contain"
|
||||
/>
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => router.back()}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Icons.ChevronLeft size={20} />
|
||||
<span>返回</span>
|
||||
</Button>
|
||||
</div>
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+16
-16
@@ -11,7 +11,7 @@ const HEALTH_CHECK_TIMEOUT = 5000;
|
||||
// Default predefined video sources - Real Chinese video APIs
|
||||
const DEFAULT_SOURCES: VideoSource[] = [
|
||||
{
|
||||
id: 'custom_0',
|
||||
id: 'dytt',
|
||||
name: '电影天堂',
|
||||
baseUrl: 'http://caiji.dyttzyapi.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -20,7 +20,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 1,
|
||||
},
|
||||
{
|
||||
id: 'custom_1',
|
||||
id: 'ruyi',
|
||||
name: '如意',
|
||||
baseUrl: 'https://cj.rycjapi.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -29,7 +29,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 2,
|
||||
},
|
||||
{
|
||||
id: 'custom_2',
|
||||
id: 'baofeng',
|
||||
name: '暴风',
|
||||
baseUrl: 'https://bfzyapi.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -38,7 +38,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 3,
|
||||
},
|
||||
{
|
||||
id: 'custom_3',
|
||||
id: 'tianya',
|
||||
name: '天涯',
|
||||
baseUrl: 'https://tyyszy.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -47,7 +47,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 4,
|
||||
},
|
||||
{
|
||||
id: 'custom_4',
|
||||
id: 'feifan',
|
||||
name: '非凡影视',
|
||||
baseUrl: 'http://ffzy5.tv/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -56,7 +56,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 5,
|
||||
},
|
||||
{
|
||||
id: 'custom_5',
|
||||
id: 'sanliuling',
|
||||
name: '360',
|
||||
baseUrl: 'https://360zy.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -65,7 +65,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 6,
|
||||
},
|
||||
{
|
||||
id: 'custom_6',
|
||||
id: 'wolong',
|
||||
name: '卧龙',
|
||||
baseUrl: 'https://wolongzyw.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -74,7 +74,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 7,
|
||||
},
|
||||
{
|
||||
id: 'custom_7',
|
||||
id: 'jisu',
|
||||
name: '极速',
|
||||
baseUrl: 'https://jszyapi.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -83,7 +83,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 8,
|
||||
},
|
||||
{
|
||||
id: 'custom_8',
|
||||
id: 'mozhua',
|
||||
name: '魔爪',
|
||||
baseUrl: 'https://mozhuazy.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -92,7 +92,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 9,
|
||||
},
|
||||
{
|
||||
id: 'custom_9',
|
||||
id: 'modu',
|
||||
name: '魔都',
|
||||
baseUrl: 'https://www.mdzyapi.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -101,7 +101,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 10,
|
||||
},
|
||||
{
|
||||
id: 'custom_10',
|
||||
id: 'zuida',
|
||||
name: '最大',
|
||||
baseUrl: 'https://api.zuidapi.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -110,7 +110,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 11,
|
||||
},
|
||||
{
|
||||
id: 'custom_11',
|
||||
id: 'yinghua',
|
||||
name: '樱花',
|
||||
baseUrl: 'https://m3u8.apiyhzy.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -119,7 +119,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 12,
|
||||
},
|
||||
{
|
||||
id: 'custom_12',
|
||||
id: 'baiduyun',
|
||||
name: '百度云',
|
||||
baseUrl: 'https://api.apibdzy.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -128,7 +128,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 13,
|
||||
},
|
||||
{
|
||||
id: 'custom_13',
|
||||
id: 'wujin',
|
||||
name: '无尽',
|
||||
baseUrl: 'https://api.wujinapi.me/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -137,7 +137,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 14,
|
||||
},
|
||||
{
|
||||
id: 'custom_14',
|
||||
id: 'wangwang',
|
||||
name: '旺旺',
|
||||
baseUrl: 'https://wwzy.tv/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
@@ -146,7 +146,7 @@ const DEFAULT_SOURCES: VideoSource[] = [
|
||||
priority: 15,
|
||||
},
|
||||
{
|
||||
id: 'custom_15',
|
||||
id: 'ikun',
|
||||
name: 'iKun',
|
||||
baseUrl: 'https://ikunzyapi.com/api.php/provide/vod',
|
||||
searchPath: '',
|
||||
|
||||
Reference in New Issue
Block a user