/* eslint-disable @typescript-eslint/no-explicit-any,no-console */ import { NextRequest, NextResponse } from 'next/server'; import { getAuthInfoFromCookie } from '@/lib/auth'; import { getConfig } from '@/lib/config'; import { API_CONFIG } from '@/lib/config'; export const runtime = 'nodejs'; export async function GET(request: NextRequest) { const authInfo = getAuthInfoFromCookie(request); if (!authInfo || !authInfo.username) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); } const { searchParams } = new URL(request.url); const searchKeyword = searchParams.get('q'); if (!searchKeyword) { return new Response( JSON.stringify({ error: '搜索关键词不能为空' }), { status: 400, headers: { 'Content-Type': 'application/json', }, } ); } const config = await getConfig(); const apiSites = config.SourceConfig; // 共享状态 let streamClosed = false; // 创建可读流 const stream = new ReadableStream({ async start(controller) { const encoder = new TextEncoder(); // 辅助函数:安全地向控制器写入数据 const safeEnqueue = (data: Uint8Array) => { try { if (streamClosed || (!controller.desiredSize && controller.desiredSize !== 0)) { return false; } controller.enqueue(data); return true; } catch (error) { console.warn('Failed to enqueue data:', error); streamClosed = true; return false; } }; // 发送开始事件 const startEvent = `data: ${JSON.stringify({ type: 'start', totalSources: apiSites.length })}\n\n`; if (!safeEnqueue(encoder.encode(startEvent))) { return; } // 记录已完成的源数量 let completedSources = 0; // 为每个源创建验证 Promise const validationPromises = apiSites.map(async (site) => { try { // 构建搜索URL,只获取第一页 const searchUrl = `${site.api}?ac=videolist&wd=${encodeURIComponent(searchKeyword)}`; // 设置超时控制 const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 10000); try { const response = await fetch(searchUrl, { headers: API_CONFIG.search.headers, signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } const data = await response.json() as any; // 检查结果是否有效 let status: 'valid' | 'no_results' | 'invalid'; if ( data && data.list && Array.isArray(data.list) && data.list.length > 0 ) { // 检查是否有标题包含搜索词的结果 const validResults = data.list.filter((item: any) => { const title = item.vod_name || ''; return title.toLowerCase().includes(searchKeyword.toLowerCase()); }); if (validResults.length > 0) { status = 'valid'; } else { status = 'no_results'; } } else { status = 'no_results'; } // 发送该源的验证结果 completedSources++; if (!streamClosed) { const sourceEvent = `data: ${JSON.stringify({ type: 'source_result', source: site.key, status })}\n\n`; if (!safeEnqueue(encoder.encode(sourceEvent))) { streamClosed = true; return; } } } finally { clearTimeout(timeoutId); } } catch (error) { console.warn(`验证失败 ${site.name}:`, error); // 发送源错误事件 completedSources++; if (!streamClosed) { const errorEvent = `data: ${JSON.stringify({ type: 'source_error', source: site.key, status: 'invalid' })}\n\n`; if (!safeEnqueue(encoder.encode(errorEvent))) { streamClosed = true; return; } } } // 检查是否所有源都已完成 if (completedSources === apiSites.length) { if (!streamClosed) { // 发送最终完成事件 const completeEvent = `data: ${JSON.stringify({ type: 'complete', completedSources })}\n\n`; if (safeEnqueue(encoder.encode(completeEvent))) { try { controller.close(); } catch (error) { console.warn('Failed to close controller:', error); } } } } }); // 等待所有验证完成 await Promise.allSettled(validationPromises); }, cancel() { streamClosed = true; console.log('Client disconnected, cancelling validation stream'); }, }); // 返回流式响应 return new Response(stream, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET', 'Access-Control-Allow-Headers': 'Content-Type', }, }); }