增加盘搜
This commit is contained in:
@@ -5105,6 +5105,9 @@ const SiteConfigComponent = ({
|
||||
DanmakuApiToken: '87654321',
|
||||
TMDBApiKey: '',
|
||||
TMDBProxy: '',
|
||||
PansouApiUrl: '',
|
||||
PansouUsername: '',
|
||||
PansouPassword: '',
|
||||
EnableComments: false,
|
||||
EnableRegistration: false,
|
||||
RegistrationRequireTurnstile: false,
|
||||
@@ -5189,6 +5192,9 @@ const SiteConfigComponent = ({
|
||||
DanmakuApiToken: config.SiteConfig.DanmakuApiToken || '87654321',
|
||||
TMDBApiKey: config.SiteConfig.TMDBApiKey || '',
|
||||
TMDBProxy: config.SiteConfig.TMDBProxy || '',
|
||||
PansouApiUrl: config.SiteConfig.PansouApiUrl || '',
|
||||
PansouUsername: config.SiteConfig.PansouUsername || '',
|
||||
PansouPassword: config.SiteConfig.PansouPassword || '',
|
||||
EnableComments: config.SiteConfig.EnableComments || false,
|
||||
});
|
||||
}
|
||||
@@ -5779,6 +5785,87 @@ const SiteConfigComponent = ({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pansou 配置 */}
|
||||
<div className='space-y-4 pt-4 border-t border-gray-200 dark:border-gray-700'>
|
||||
<h3 className='text-sm font-semibold text-gray-900 dark:text-gray-100'>
|
||||
Pansou 网盘搜索配置
|
||||
</h3>
|
||||
|
||||
{/* Pansou API 地址 */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
Pansou API 地址
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='请输入 Pansou API 地址,如:http://localhost:8888'
|
||||
value={siteSettings.PansouApiUrl}
|
||||
onChange={(e) =>
|
||||
setSiteSettings((prev) => ({
|
||||
...prev,
|
||||
PansouApiUrl: e.target.value,
|
||||
}))
|
||||
}
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent'
|
||||
/>
|
||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
||||
配置 Pansou 服务器地址,用于网盘资源搜索。项目地址:{' '}
|
||||
<a
|
||||
href='https://github.com/fish2018/pansou'
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300'
|
||||
>
|
||||
https://github.com/fish2018/pansou
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Pansou 账号 */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
Pansou 账号(可选)
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='如果 Pansou 启用了认证,请输入账号'
|
||||
value={siteSettings.PansouUsername}
|
||||
onChange={(e) =>
|
||||
setSiteSettings((prev) => ({
|
||||
...prev,
|
||||
PansouUsername: e.target.value,
|
||||
}))
|
||||
}
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent'
|
||||
/>
|
||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
||||
如果 Pansou 服务启用了认证功能,需要提供账号密码
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Pansou 密码 */}
|
||||
<div>
|
||||
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
|
||||
Pansou 密码(可选)
|
||||
</label>
|
||||
<input
|
||||
type='password'
|
||||
placeholder='如果 Pansou 启用了认证,请输入密码'
|
||||
value={siteSettings.PansouPassword}
|
||||
onChange={(e) =>
|
||||
setSiteSettings((prev) => ({
|
||||
...prev,
|
||||
PansouPassword: e.target.value,
|
||||
}))
|
||||
}
|
||||
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-green-500 focus:border-transparent'
|
||||
/>
|
||||
<p className='mt-1 text-xs text-gray-500 dark:text-gray-400'>
|
||||
配置账号密码后,系统会自动登录并缓存 Token
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 评论功能配置 */}
|
||||
<div className='space-y-4 pt-4 border-t border-gray-200 dark:border-gray-700'>
|
||||
<h3 className='text-sm font-semibold text-gray-900 dark:text-gray-100'>
|
||||
|
||||
@@ -43,6 +43,9 @@ export async function POST(request: NextRequest) {
|
||||
DanmakuApiToken,
|
||||
TMDBApiKey,
|
||||
TMDBProxy,
|
||||
PansouApiUrl,
|
||||
PansouUsername,
|
||||
PansouPassword,
|
||||
EnableComments,
|
||||
CustomAdFilterCode,
|
||||
CustomAdFilterVersion,
|
||||
@@ -76,6 +79,9 @@ export async function POST(request: NextRequest) {
|
||||
DanmakuApiToken: string;
|
||||
TMDBApiKey?: string;
|
||||
TMDBProxy?: string;
|
||||
PansouApiUrl?: string;
|
||||
PansouUsername?: string;
|
||||
PansouPassword?: string;
|
||||
EnableComments: boolean;
|
||||
CustomAdFilterCode?: string;
|
||||
CustomAdFilterVersion?: number;
|
||||
@@ -163,6 +169,9 @@ export async function POST(request: NextRequest) {
|
||||
DanmakuApiToken,
|
||||
TMDBApiKey,
|
||||
TMDBProxy,
|
||||
PansouApiUrl,
|
||||
PansouUsername,
|
||||
PansouPassword,
|
||||
EnableComments,
|
||||
CustomAdFilterCode,
|
||||
CustomAdFilterVersion,
|
||||
|
||||
61
src/app/api/pansou/search/route.ts
Normal file
61
src/app/api/pansou/search/route.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any,no-console */
|
||||
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
import { getConfig } from '@/lib/config';
|
||||
import { searchPansou } from '@/lib/pansou.client';
|
||||
|
||||
export const runtime = 'nodejs';
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
const { keyword } = body;
|
||||
|
||||
if (!keyword) {
|
||||
return NextResponse.json(
|
||||
{ error: '关键词不能为空' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 从系统配置中获取 Pansou 配置
|
||||
const config = await getConfig();
|
||||
const apiUrl = config.SiteConfig.PansouApiUrl;
|
||||
const username = config.SiteConfig.PansouUsername;
|
||||
const password = config.SiteConfig.PansouPassword;
|
||||
|
||||
console.log('Pansou 搜索请求:', {
|
||||
keyword,
|
||||
apiUrl: apiUrl ? '已配置' : '未配置',
|
||||
hasAuth: !!(username && password),
|
||||
});
|
||||
|
||||
if (!apiUrl) {
|
||||
return NextResponse.json(
|
||||
{ error: '未配置 Pansou API 地址,请在管理面板配置' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// 调用 Pansou 搜索
|
||||
const results = await searchPansou(apiUrl, keyword, {
|
||||
username,
|
||||
password,
|
||||
});
|
||||
|
||||
console.log('Pansou 搜索结果:', {
|
||||
total: results.total,
|
||||
hasData: !!results.merged_by_type,
|
||||
types: results.merged_by_type ? Object.keys(results.merged_by_type) : [],
|
||||
});
|
||||
|
||||
return NextResponse.json(results);
|
||||
} catch (error: any) {
|
||||
console.error('Pansou 搜索失败:', error);
|
||||
return NextResponse.json(
|
||||
{ error: error.message || '搜索失败' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,10 @@ export async function GET(request: NextRequest) {
|
||||
EnableOIDCLogin: config.SiteConfig.EnableOIDCLogin || false,
|
||||
EnableOIDCRegistration: config.SiteConfig.EnableOIDCRegistration || false,
|
||||
OIDCButtonText: config.SiteConfig.OIDCButtonText || '',
|
||||
SiteConfig: {
|
||||
PansouApiUrl: config.SiteConfig.PansouApiUrl || '',
|
||||
// 不暴露用户名和密码,认证在后端处理
|
||||
},
|
||||
};
|
||||
return NextResponse.json(result);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
'use client';
|
||||
|
||||
import { Heart } from 'lucide-react';
|
||||
import { Heart, Search, X } from 'lucide-react';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { Suspense, useEffect, useRef, useState } from 'react';
|
||||
|
||||
@@ -48,6 +48,7 @@ import DoubanComments from '@/components/DoubanComments';
|
||||
import DanmakuFilterSettings from '@/components/DanmakuFilterSettings';
|
||||
import Toast, { ToastProps } from '@/components/Toast';
|
||||
import { useEnableComments } from '@/hooks/useEnableComments';
|
||||
import PansouSearch from '@/components/PansouSearch';
|
||||
|
||||
// 扩展 HTMLVideoElement 类型以支持 hls 属性
|
||||
declare global {
|
||||
@@ -96,6 +97,9 @@ function PlayPageClient() {
|
||||
// 收藏状态
|
||||
const [favorited, setFavorited] = useState(false);
|
||||
|
||||
// 网盘搜索弹窗状态
|
||||
const [showPansouDialog, setShowPansouDialog] = useState(false);
|
||||
|
||||
// 跳过片头片尾配置
|
||||
const [skipConfig, setSkipConfig] = useState<{
|
||||
enable: boolean;
|
||||
@@ -4890,6 +4894,17 @@ function PlayPageClient() {
|
||||
>
|
||||
<FavoriteIcon filled={favorited} />
|
||||
</button>
|
||||
{/* 网盘搜索按钮 */}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setShowPansouDialog(true);
|
||||
}}
|
||||
className='flex-shrink-0 hover:opacity-80 transition-opacity'
|
||||
title='搜索网盘资源'
|
||||
>
|
||||
<Search className='h-6 w-6 text-gray-700 dark:text-gray-300' />
|
||||
</button>
|
||||
{/* 豆瓣评分显示 */}
|
||||
{doubanRating && doubanRating.value > 0 && (
|
||||
<div className='flex items-center gap-2 text-base font-normal'>
|
||||
@@ -5107,6 +5122,40 @@ function PlayPageClient() {
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 网盘搜索弹窗 */}
|
||||
{showPansouDialog && (
|
||||
<div
|
||||
className='fixed inset-0 z-[10000] flex items-center justify-center bg-black/50'
|
||||
onClick={() => setShowPansouDialog(false)}
|
||||
>
|
||||
<div
|
||||
className='relative w-full max-w-4xl max-h-[80vh] overflow-y-auto bg-white dark:bg-gray-900 rounded-lg shadow-xl m-4'
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* 弹窗头部 */}
|
||||
<div className='sticky top-0 z-10 flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900'>
|
||||
<h2 className='text-xl font-bold text-gray-900 dark:text-gray-100'>
|
||||
搜索网盘资源: {detail?.title || ''}
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => setShowPansouDialog(false)}
|
||||
className='p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors'
|
||||
>
|
||||
<X className='h-5 w-5 text-gray-600 dark:text-gray-400' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 弹窗内容 */}
|
||||
<div className='p-4'>
|
||||
<PansouSearch
|
||||
keyword={detail?.title || ''}
|
||||
triggerSearch={showPansouDialog}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,12 +18,17 @@ import PageLayout from '@/components/PageLayout';
|
||||
import SearchResultFilter, { SearchFilterCategory } from '@/components/SearchResultFilter';
|
||||
import SearchSuggestions from '@/components/SearchSuggestions';
|
||||
import VideoCard, { VideoCardHandle } from '@/components/VideoCard';
|
||||
import PansouSearch from '@/components/PansouSearch';
|
||||
|
||||
function SearchPageClient() {
|
||||
// 搜索历史
|
||||
const [searchHistory, setSearchHistory] = useState<string[]>([]);
|
||||
// 返回顶部按钮显示状态
|
||||
const [showBackToTop, setShowBackToTop] = useState(false);
|
||||
// 选项卡状态: 'video' 或 'pansou'
|
||||
const [activeTab, setActiveTab] = useState<'video' | 'pansou'>('video');
|
||||
// Pansou 搜索触发标志
|
||||
const [triggerPansouSearch, setTriggerPansouSearch] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
@@ -381,6 +386,14 @@ function SearchPageClient() {
|
||||
});
|
||||
}, [aggregatedResults, filterAgg, searchQuery]);
|
||||
|
||||
// 监听选项卡切换,自动执行搜索
|
||||
useEffect(() => {
|
||||
// 如果切换到网盘搜索选项卡,且有搜索关键词,且已显示结果,则触发搜索
|
||||
if (activeTab === 'pansou' && searchQuery.trim() && showResults) {
|
||||
setTriggerPansouSearch(prev => !prev);
|
||||
}
|
||||
}, [activeTab]);
|
||||
|
||||
useEffect(() => {
|
||||
// 无搜索参数时聚焦搜索框
|
||||
!searchParams.get('q') && document.getElementById('searchInput')?.focus();
|
||||
@@ -693,8 +706,15 @@ function SearchPageClient() {
|
||||
setShowResults(true);
|
||||
setShowSuggestions(false);
|
||||
|
||||
router.push(`/search?q=${encodeURIComponent(trimmed)}`);
|
||||
// 其余由 searchParams 变化的 effect 处理
|
||||
// 根据当前选项卡执行不同的搜索
|
||||
if (activeTab === 'video') {
|
||||
// 影视搜索
|
||||
router.push(`/search?q=${encodeURIComponent(trimmed)}`);
|
||||
// 其余由 searchParams 变化的 effect 处理
|
||||
} else if (activeTab === 'pansou') {
|
||||
// 网盘搜索 - 触发搜索
|
||||
setTriggerPansouSearch(prev => !prev); // 切换状态来触发搜索
|
||||
}
|
||||
};
|
||||
|
||||
const handleSuggestionSelect = (suggestion: string) => {
|
||||
@@ -778,14 +798,41 @@ function SearchPageClient() {
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{/* 选项卡 */}
|
||||
<div className='flex justify-center gap-2 mt-6'>
|
||||
<button
|
||||
onClick={() => setActiveTab('video')}
|
||||
className={`px-6 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
activeTab === 'video'
|
||||
? 'bg-green-600 text-white dark:bg-green-600'
|
||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
影视搜索
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('pansou')}
|
||||
className={`px-6 py-2 rounded-lg text-sm font-medium transition-colors ${
|
||||
activeTab === 'pansou'
|
||||
? 'bg-green-600 text-white dark:bg-green-600'
|
||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200 dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
网盘搜索
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 搜索结果或搜索历史 */}
|
||||
<div className='max-w-[95%] mx-auto mt-12 overflow-visible'>
|
||||
{showResults ? (
|
||||
<section className='mb-12'>
|
||||
{/* 标题 */}
|
||||
<div className='mb-4 flex items-center justify-between'>
|
||||
{activeTab === 'video' ? (
|
||||
<>
|
||||
{/* 影视搜索结果 */}
|
||||
{/* 标题 */}
|
||||
<div className='mb-4 flex items-center justify-between'>
|
||||
<h2 className='text-xl font-bold text-gray-800 dark:text-gray-200'>
|
||||
搜索结果
|
||||
{isFromCache ? (
|
||||
@@ -930,6 +977,21 @@ function SearchPageClient() {
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* 网盘搜索结果 */}
|
||||
<div className='mb-4'>
|
||||
<h2 className='text-xl font-bold text-gray-800 dark:text-gray-200'>
|
||||
网盘搜索结果
|
||||
</h2>
|
||||
</div>
|
||||
<PansouSearch
|
||||
keyword={searchQuery}
|
||||
triggerSearch={triggerPansouSearch}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
) : searchHistory.length > 0 ? (
|
||||
// 搜索历史
|
||||
|
||||
Reference in New Issue
Block a user