import fetch from 'node-fetch'; interface InvidiousVideoThumbnail { quality: string; url: string; width: number; height: number; } interface InvidiousResult { type: string; title: string; videoId: string; playlistId: string; author: string; videoThumbnails?: InvidiousVideoThumbnail[]; } export interface SearchResult { type: string; title: string; author: string; mediaUrl: string; thumbnailUrl: string; } const USE_INVIDIOUS = process.env.USE_INVIDIOUS || true; const INVIDIOUS_BASE_URL = process.env.INVIDIOUS_BASE_URL || 'http://invidious.nor'; const INVIDIOUS_API_ENDPOINT = `${INVIDIOUS_BASE_URL}/api/v1`; export const getInvidiousSearchURL = (query: string): string => `${INVIDIOUS_API_ENDPOINT}/search?q=${encodeURIComponent(query)}`; export const getInvidiousThumbnailURL = (url: string): string => `${INVIDIOUS_BASE_URL}${url}`; const preferredThumbnailAPIURL = (thumbnails: InvidiousVideoThumbnail[] | undefined): string => { if (!thumbnails || thumbnails.length === 0) { return '/assets/placeholder.jpg'; } const mediumThumbnail = thumbnails.find(t => t.quality === 'medium'); const thumbnail = mediumThumbnail || thumbnails[0]; return `/api/thumbnail?url=${encodeURIComponent(thumbnail.url)}`; }; const getMediaURL = (result: InvidiousResult): string => { if (result.type === 'video') { return `https://www.youtube.com/watch?v=${result.videoId}`; } else if (result.type === 'playlist') { return `https://www.youtube.com/playlist?list=${result.playlistId}`; } throw new Error(`Unknown result type: ${result.type}`); }; export const searchInvidious = async (query: string): Promise => { try { const response = await fetch(getInvidiousSearchURL(query)); if (!response.ok) { throw new Error(`Invidious HTTP error: ${response.status}`); } const data = await response.json() as Array; return data.filter(item => { return item.type === 'video' || item.type === 'playlist'; }).map(item => ({ type: item.type, title: item.title, author: item.author, mediaUrl: getMediaURL(item), thumbnailUrl: preferredThumbnailAPIURL(item.videoThumbnails) })); } catch (error) { console.error('Failed to search Invidious:', error); throw error; } }