diff --git a/src/app/api/image-proxy/route.ts b/src/app/api/image-proxy/route.ts new file mode 100644 index 0000000..73e54cc --- /dev/null +++ b/src/app/api/image-proxy/route.ts @@ -0,0 +1,59 @@ +import { NextResponse } from 'next/server'; + +export const runtime = 'edge'; + +// OrionTV 兼容接口 +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const imageUrl = searchParams.get('url'); + + if (!imageUrl) { + return NextResponse.json({ error: 'Missing image URL' }, { status: 400 }); + } + + try { + const imageResponse = await fetch(imageUrl, { + headers: { + Referer: 'https://movie.douban.com/', + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36', + }, + }); + + if (!imageResponse.ok) { + return NextResponse.json( + { error: imageResponse.statusText }, + { status: imageResponse.status } + ); + } + + const contentType = imageResponse.headers.get('content-type'); + + if (!imageResponse.body) { + return NextResponse.json( + { error: 'Image response has no body' }, + { status: 500 } + ); + } + + // 创建响应头 + const headers = new Headers(); + if (contentType) { + headers.set('Content-Type', contentType); + } + + // 设置缓存头(可选) + headers.set('Cache-Control', 'public, max-age=86400'); // 缓存24小时 + + // 直接返回图片流 + return new Response(imageResponse.body, { + status: 200, + headers, + }); + } catch (error) { + return NextResponse.json( + { error: 'Error fetching image' }, + { status: 500 } + ); + } +} diff --git a/src/app/api/search/one/route.ts b/src/app/api/search/one/route.ts new file mode 100644 index 0000000..4ad60c2 --- /dev/null +++ b/src/app/api/search/one/route.ts @@ -0,0 +1,72 @@ +import { NextResponse } from 'next/server'; + +import { getAvailableApiSites, getCacheTime } from '@/lib/config'; +import { searchFromApi } from '@/lib/downstream'; + +export const runtime = 'edge'; + +// OrionTV 兼容接口 +export async function GET(request: Request) { + const { searchParams } = new URL(request.url); + const query = searchParams.get('q'); + const resourceId = searchParams.get('resourceId'); + + if (!query || !resourceId) { + const cacheTime = getCacheTime(); + return NextResponse.json( + { result: null, error: '缺少必要参数: q 或 resourceId' }, + { + headers: { + 'Cache-Control': `public, max-age=${cacheTime}`, + }, + } + ); + } + + const apiSites = getAvailableApiSites(); + + try { + // 根据 resourceId 查找对应的 API 站点 + const targetSite = apiSites.find((site) => site.key === resourceId); + if (!targetSite) { + return NextResponse.json( + { + error: `未找到指定的视频源: ${resourceId}`, + result: null, + }, + { status: 404 } + ); + } + + const results = await searchFromApi(targetSite, query); + const result = results.filter((r) => r.title === query); + const cacheTime = getCacheTime(); + + if (result.length === 0) { + return NextResponse.json( + { + error: '未找到结果', + result: null, + }, + { status: 404 } + ); + } else { + return NextResponse.json( + { result: result }, + { + headers: { + 'Cache-Control': `public, max-age=${cacheTime}`, + }, + } + ); + } + } catch (error) { + return NextResponse.json( + { + error: '搜索失败', + result: null, + }, + { status: 500 } + ); + } +} diff --git a/src/app/api/search/resources/route.ts b/src/app/api/search/resources/route.ts new file mode 100644 index 0000000..15b1bed --- /dev/null +++ b/src/app/api/search/resources/route.ts @@ -0,0 +1,21 @@ +import { NextResponse } from 'next/server'; + +import { getAvailableApiSites, getCacheTime } from '@/lib/config'; + +export const runtime = 'edge'; + +// OrionTV 兼容接口 +export async function GET() { + try { + const apiSites = getAvailableApiSites(); + const cacheTime = getCacheTime(); + + return NextResponse.json(apiSites, { + headers: { + 'Cache-Control': `public, max-age=${cacheTime}`, + }, + }); + } catch (error) { + return NextResponse.json({ error: '获取资源失败' }, { status: 500 }); + } +} diff --git a/src/app/douban/page.tsx b/src/app/douban/page.tsx index ecf9688..362befa 100644 --- a/src/app/douban/page.tsx +++ b/src/app/douban/page.tsx @@ -203,7 +203,6 @@ function DoubanPageClient() { title={item.title} poster={item.poster} rate={item.rate} - type={type || 'movie'} /> ))} diff --git a/src/app/page.tsx b/src/app/page.tsx index b77515a..25c1c17 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -197,7 +197,6 @@ function HomeClient() { title={movie.title} poster={movie.poster} rate={movie.rate} - type='movie' /> ))} @@ -243,7 +242,6 @@ function HomeClient() { title={show.title} poster={show.poster} rate={show.rate} - type='tv' /> ))} diff --git a/src/components/DemoCard.tsx b/src/components/DemoCard.tsx index e062afc..237b901 100644 --- a/src/components/DemoCard.tsx +++ b/src/components/DemoCard.tsx @@ -10,7 +10,6 @@ interface DemoCardProps { title: string; poster: string; rate?: string; - type?: string; } function PlayCircleSolid({ @@ -42,7 +41,7 @@ function PlayCircleSolid({ ); } -const DemoCard = ({ id, title, poster, rate, type }: DemoCardProps) => { +const DemoCard = ({ id, title, poster, rate }: DemoCardProps) => { const [hover, setHover] = useState(false); const [isLoaded, setIsLoaded] = useState(false); const router = useRouter(); @@ -50,9 +49,7 @@ const DemoCard = ({ id, title, poster, rate, type }: DemoCardProps) => { const handleClick = () => { router.push( - `/play?title=${encodeURIComponent( - title.trim() - )}&douban_id=${id}&type=${type}` + `/play?title=${encodeURIComponent(title.trim())}&douban_id=${id}` ); };