Spaces:
Sleeping
Sleeping
| import { getLocalMovies } from '../../../lib/localDb.js'; | |
| // Cấu hình timeout cho fetch | |
| async function fetchWithTimeout(resource, options = {}) { | |
| const { timeout = 8000 } = options; | |
| const controller = new AbortController(); | |
| const id = setTimeout(() => controller.abort(), timeout); | |
| try { | |
| const response = await fetch(resource, { | |
| ...options, | |
| signal: controller.signal | |
| }); | |
| return response; | |
| } finally { | |
| clearTimeout(id); | |
| } | |
| } | |
| export async function GET({ request }) { | |
| // Check quyền admin từ cookie auth_token | |
| const cookies = request.headers.get('cookie') || ''; | |
| const hasToken = cookies.includes('auth_token='); | |
| if (!hasToken) { | |
| return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401 }); | |
| } | |
| const url = new URL(request.url); | |
| const keyword = url.searchParams.get('keyword') || ''; | |
| if (!keyword.trim()) { | |
| return new Response(JSON.stringify({ items: [], APP_DOMAIN_CDN_IMAGE: '' }), { | |
| headers: { 'Content-Type': 'application/json' } | |
| }); | |
| } | |
| try { | |
| // 1. Gọi API tìm kiếm trực tuyến của KKPhim | |
| const kkRes = await fetchWithTimeout( | |
| `https://phimapi.com/v1/api/tim-kiem?keyword=${encodeURIComponent(keyword)}&limit=40` | |
| ); | |
| if (!kkRes.ok) { | |
| throw new Error(`API KKPhim trả về HTTP ${kkRes.status}`); | |
| } | |
| const searchData = await kkRes.json(); | |
| if (searchData.status !== 'success') { | |
| throw new Error(searchData.message || 'Lỗi API KKPhim'); | |
| } | |
| const items = searchData.data?.items || []; | |
| const imageDomain = searchData.data?.APP_DOMAIN_CDN_IMAGE || 'https://phimimg.com'; | |
| // 2. Load phim cục bộ để đối chiếu | |
| const localMovies = await getLocalMovies(); | |
| // 3. Map kết quả đối chiếu | |
| const mappedItems = items.map(item => { | |
| const existing = localMovies[item.slug]; | |
| let localEpCount = 0; | |
| if (existing && Array.isArray(existing.episodes)) { | |
| localEpCount = Math.max(...existing.episodes.map(s => s.server_data?.length || 0), 0); | |
| } | |
| return { | |
| name: item.name, | |
| origin_name: item.origin_name, | |
| slug: item.slug, | |
| type: item.type, | |
| thumb_url: item.thumb_url.startsWith('http') ? item.thumb_url : `${imageDomain}/${item.thumb_url}`, | |
| poster_url: item.poster_url.startsWith('http') ? item.poster_url : `${imageDomain}/${item.poster_url}`, | |
| year: item.year || 'N/A', | |
| episode_current: item.episode_current || 'N/A', | |
| exists: !!existing, | |
| local_episode_count: localEpCount, | |
| existing_status: existing ? (existing.movie.status || 'N/A') : null, | |
| existing_episodes: existing && Array.isArray(existing.episodes) | |
| ? existing.episodes.flatMap(s => s.server_data?.map(ep => ep.name) || []) | |
| : [] | |
| }; | |
| }); | |
| return new Response(JSON.stringify({ | |
| items: mappedItems, | |
| APP_DOMAIN_CDN_IMAGE: imageDomain | |
| }), { | |
| headers: { 'Content-Type': 'application/json' } | |
| }); | |
| } catch (err) { | |
| return new Response(JSON.stringify({ error: err.message }), { | |
| status: 500, | |
| headers: { 'Content-Type': 'application/json' } | |
| }); | |
| } | |
| } | |