From 227d1370da9bfce1dda52137f29a8995b491149d Mon Sep 17 00:00:00 2001 From: mtvpls Date: Wed, 7 Jan 2026 01:08:55 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A7=81=E4=BA=BA=E5=BD=B1?= =?UTF-8?q?=E5=BA=93=E6=8A=A5=E9=94=99=E5=AF=BC=E8=87=B4=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=97=A0=E7=BB=93=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/search/route.ts | 124 +++++++++++++++-------------- src/app/api/search/ws/route.ts | 140 ++++++++++++++++++--------------- 2 files changed, 142 insertions(+), 122 deletions(-) diff --git a/src/app/api/search/route.ts b/src/app/api/search/route.ts index 43812cc..37e5c8b 100644 --- a/src/app/api/search/route.ts +++ b/src/app/api/search/route.ts @@ -55,34 +55,39 @@ export async function GET(request: NextRequest) { const embyPromise = hasEmby ? Promise.race([ (async () => { - const { EmbyClient } = await import('@/lib/emby.client'); - const client = new EmbyClient(config.EmbyConfig!); - const searchResult = await client.getItems({ - searchTerm: query, - IncludeItemTypes: 'Movie,Series', - Recursive: true, - Fields: 'Overview,ProductionYear', - Limit: 50, - }); - return searchResult.Items.map((item) => ({ - id: item.Id, - source: 'emby', - source_name: 'Emby', - title: item.Name, - poster: client.getImageUrl(item.Id, 'Primary'), - episodes: [], - episodes_titles: [], - year: item.ProductionYear?.toString() || '', - desc: item.Overview || '', - type_name: item.Type === 'Movie' ? '电影' : '电视剧', - douban_id: 0, - })); + try { + const { EmbyClient } = await import('@/lib/emby.client'); + const client = new EmbyClient(config.EmbyConfig!); + const searchResult = await client.getItems({ + searchTerm: query, + IncludeItemTypes: 'Movie,Series', + Recursive: true, + Fields: 'Overview,ProductionYear', + Limit: 50, + }); + return searchResult.Items.map((item) => ({ + id: item.Id, + source: 'emby', + source_name: 'Emby', + title: item.Name, + poster: client.getImageUrl(item.Id, 'Primary'), + episodes: [], + episodes_titles: [], + year: item.ProductionYear?.toString() || '', + desc: item.Overview || '', + type_name: item.Type === 'Movie' ? '电影' : '电视剧', + douban_id: 0, + })); + } catch (error) { + console.error('[Search] 搜索 Emby 失败:', error); + return []; + } })(), new Promise((_, reject) => setTimeout(() => reject(new Error('Emby timeout')), 20000) ), ]).catch((error) => { - console.error('[Search] 搜索 Emby 失败:', error); + console.error('[Search] 搜索 Emby 超时:', error); return []; }) : Promise.resolve([]); @@ -91,51 +96,56 @@ export async function GET(request: NextRequest) { const openlistPromise = hasOpenList ? Promise.race([ (async () => { - const { getCachedMetaInfo, setCachedMetaInfo } = await import('@/lib/openlist-cache'); - const { getTMDBImageUrl } = await import('@/lib/tmdb.search'); - const { db } = await import('@/lib/db'); + try { + const { getCachedMetaInfo, setCachedMetaInfo } = await import('@/lib/openlist-cache'); + const { getTMDBImageUrl } = await import('@/lib/tmdb.search'); + const { db } = await import('@/lib/db'); - const rootPath = config.OpenListConfig!.RootPath || '/'; - let metaInfo = getCachedMetaInfo(rootPath); + const rootPath = config.OpenListConfig!.RootPath || '/'; + let metaInfo = getCachedMetaInfo(rootPath); - if (!metaInfo) { - const metainfoJson = await db.getGlobalValue('video.metainfo'); - if (metainfoJson) { - metaInfo = JSON.parse(metainfoJson); - if (metaInfo) { - setCachedMetaInfo(rootPath, metaInfo); + if (!metaInfo) { + const metainfoJson = await db.getGlobalValue('video.metainfo'); + if (metainfoJson) { + metaInfo = JSON.parse(metainfoJson); + if (metaInfo) { + setCachedMetaInfo(rootPath, metaInfo); + } } } - } - if (metaInfo && metaInfo.folders) { - return Object.entries(metaInfo.folders) - .filter(([folderName, info]: [string, any]) => { - const matchFolder = folderName.toLowerCase().includes(query.toLowerCase()); - const matchTitle = info.title.toLowerCase().includes(query.toLowerCase()); - return matchFolder || matchTitle; - }) - .map(([folderName, info]: [string, any]) => ({ - id: folderName, - source: 'openlist', - source_name: '私人影库', - title: info.title, - poster: getTMDBImageUrl(info.poster_path), - episodes: [], - episodes_titles: [], - year: info.release_date.split('-')[0] || '', - desc: info.overview, - type_name: info.media_type === 'movie' ? '电影' : '电视剧', - douban_id: 0, - })); + if (metaInfo && metaInfo.folders) { + return Object.entries(metaInfo.folders) + .filter(([folderName, info]: [string, any]) => { + const matchFolder = folderName.toLowerCase().includes(query.toLowerCase()); + const matchTitle = info.title.toLowerCase().includes(query.toLowerCase()); + return matchFolder || matchTitle; + }) + .map(([folderName, info]: [string, any]) => ({ + id: folderName, + source: 'openlist', + source_name: '私人影库', + title: info.title, + poster: getTMDBImageUrl(info.poster_path), + episodes: [], + episodes_titles: [], + year: info.release_date.split('-')[0] || '', + desc: info.overview, + type_name: info.media_type === 'movie' ? '电影' : '电视剧', + douban_id: 0, + })); + } + return []; + } catch (error) { + console.error('[Search] 搜索 OpenList 失败:', error); + return []; } - return []; })(), new Promise((_, reject) => setTimeout(() => reject(new Error('OpenList timeout')), 20000) ), ]).catch((error) => { - console.error('[Search] 搜索 OpenList 失败:', error); + console.error('[Search] 搜索 OpenList 超时:', error); return []; }) : Promise.resolve([]); diff --git a/src/app/api/search/ws/route.ts b/src/app/api/search/ws/route.ts index bd56ad9..4667595 100644 --- a/src/app/api/search/ws/route.ts +++ b/src/app/api/search/ws/route.ts @@ -93,28 +93,33 @@ export async function GET(request: NextRequest) { if (hasEmby) { Promise.race([ (async () => { - const { EmbyClient } = await import('@/lib/emby.client'); - const client = new EmbyClient(config.EmbyConfig!); - const searchResult = await client.getItems({ - searchTerm: query, - IncludeItemTypes: 'Movie,Series', - Recursive: true, - Fields: 'Overview,ProductionYear', - Limit: 50, - }); - return searchResult.Items.map((item) => ({ - id: item.Id, - source: 'emby', - source_name: 'Emby', - title: item.Name, - poster: client.getImageUrl(item.Id, 'Primary'), - episodes: [], - episodes_titles: [], - year: item.ProductionYear?.toString() || '', - desc: item.Overview || '', - type_name: item.Type === 'Movie' ? '电影' : '电视剧', - douban_id: 0, - })); + try { + const { EmbyClient } = await import('@/lib/emby.client'); + const client = new EmbyClient(config.EmbyConfig!); + const searchResult = await client.getItems({ + searchTerm: query, + IncludeItemTypes: 'Movie,Series', + Recursive: true, + Fields: 'Overview,ProductionYear', + Limit: 50, + }); + return searchResult.Items.map((item) => ({ + id: item.Id, + source: 'emby', + source_name: 'Emby', + title: item.Name, + poster: client.getImageUrl(item.Id, 'Primary'), + episodes: [], + episodes_titles: [], + year: item.ProductionYear?.toString() || '', + desc: item.Overview || '', + type_name: item.Type === 'Movie' ? '电影' : '电视剧', + douban_id: 0, + })); + } catch (error) { + console.error('[Search WS] 搜索 Emby 失败:', error); + return []; + } })(), new Promise((_, reject) => setTimeout(() => reject(new Error('Emby timeout')), 20000) @@ -140,17 +145,17 @@ export async function GET(request: NextRequest) { } }) .catch((error) => { - console.error('[Search WS] 搜索 Emby 失败:', error); + console.error('[Search WS] 搜索 Emby 超时:', error); completedSources++; if (!streamClosed) { - const errorEvent = `data: ${JSON.stringify({ - type: 'source_error', + const sourceEvent = `data: ${JSON.stringify({ + type: 'source_result', source: 'emby', sourceName: 'Emby', - error: error instanceof Error ? error.message : '搜索失败', + results: [], timestamp: Date.now() })}\n\n`; - safeEnqueue(encoder.encode(errorEvent)); + safeEnqueue(encoder.encode(sourceEvent)); } }); } @@ -159,45 +164,50 @@ export async function GET(request: NextRequest) { if (hasOpenList) { Promise.race([ (async () => { - const { getCachedMetaInfo, setCachedMetaInfo } = await import('@/lib/openlist-cache'); - const { getTMDBImageUrl } = await import('@/lib/tmdb.search'); - const { db } = await import('@/lib/db'); + try { + const { getCachedMetaInfo, setCachedMetaInfo } = await import('@/lib/openlist-cache'); + const { getTMDBImageUrl } = await import('@/lib/tmdb.search'); + const { db } = await import('@/lib/db'); - const rootPath = config.OpenListConfig!.RootPath || '/'; - let metaInfo = getCachedMetaInfo(rootPath); + const rootPath = config.OpenListConfig!.RootPath || '/'; + let metaInfo = getCachedMetaInfo(rootPath); - if (!metaInfo) { - const metainfoJson = await db.getGlobalValue('video.metainfo'); - if (metainfoJson) { - metaInfo = JSON.parse(metainfoJson); - if (metaInfo) { - setCachedMetaInfo(rootPath, metaInfo); + if (!metaInfo) { + const metainfoJson = await db.getGlobalValue('video.metainfo'); + if (metainfoJson) { + metaInfo = JSON.parse(metainfoJson); + if (metaInfo) { + setCachedMetaInfo(rootPath, metaInfo); + } } } - } - if (metaInfo && metaInfo.folders) { - return Object.entries(metaInfo.folders) - .filter(([key, info]: [string, any]) => { - const matchFolder = info.folderName.toLowerCase().includes(query.toLowerCase()); - const matchTitle = info.title.toLowerCase().includes(query.toLowerCase()); - return matchFolder || matchTitle; - }) - .map(([key, info]: [string, any]) => ({ - id: key, - source: 'openlist', - source_name: '私人影库', - title: info.title, - poster: getTMDBImageUrl(info.poster_path), - episodes: [], - episodes_titles: [], - year: info.release_date.split('-')[0] || '', - desc: info.overview, - type_name: info.media_type === 'movie' ? '电影' : '电视剧', - douban_id: 0, - })); + if (metaInfo && metaInfo.folders) { + return Object.entries(metaInfo.folders) + .filter(([key, info]: [string, any]) => { + const matchFolder = info.folderName.toLowerCase().includes(query.toLowerCase()); + const matchTitle = info.title.toLowerCase().includes(query.toLowerCase()); + return matchFolder || matchTitle; + }) + .map(([key, info]: [string, any]) => ({ + id: key, + source: 'openlist', + source_name: '私人影库', + title: info.title, + poster: getTMDBImageUrl(info.poster_path), + episodes: [], + episodes_titles: [], + year: info.release_date.split('-')[0] || '', + desc: info.overview, + type_name: info.media_type === 'movie' ? '电影' : '电视剧', + douban_id: 0, + })); + } + return []; + } catch (error) { + console.error('[Search WS] 搜索 OpenList 失败:', error); + return []; } - return []; })(), new Promise((_, reject) => setTimeout(() => reject(new Error('OpenList timeout')), 20000) @@ -223,17 +233,17 @@ export async function GET(request: NextRequest) { } }) .catch((error) => { - console.error('[Search WS] 搜索 OpenList 失败:', error); + console.error('[Search WS] 搜索 OpenList 超时:', error); completedSources++; if (!streamClosed) { - const errorEvent = `data: ${JSON.stringify({ - type: 'source_error', + const sourceEvent = `data: ${JSON.stringify({ + type: 'source_result', source: 'openlist', sourceName: '私人影库', - error: error instanceof Error ? error.message : '搜索失败', + results: [], timestamp: Date.now() })}\n\n`; - safeEnqueue(encoder.encode(errorEvent)); + safeEnqueue(encoder.encode(sourceEvent)); } }); }