import { ISource, SearchResult, MediaType, SelectionData, ContentLinks } from '../../src/types/source.js'; import { CONFIG } from '../../src/utils/config.js'; import { sourceRegistry } from '../../src/core/registry.js'; import { fetchSearch, fetchTrending, fetchPage } from './api.js'; import { parseSearchResults, parseTrendingResults, parseContentHTML, parseEpisodeLinks, parseOtherVersions } from './parser.js'; function isSeriesIdentifier(identifier: string): boolean { return /saison|pack-series|series-(vf|vostfr|terminee)/i.test(identifier); } export class FreeTeleAPI implements ISource { name = 'freetel'; displayName = 'Free-Télécharger'; get baseUrl() { return CONFIG.FT_URL?.replace(/\/$/, ''); } async healthCheck(): Promise { if (!this.baseUrl) { console.warn('[FreeTel] ⚠️ FT_URL non définie.'); return false; } try { const res = await fetch(this.baseUrl, { method: 'HEAD', headers: { 'User-Agent': 'Mozilla/5.0' }, signal: AbortSignal.timeout(5000), }); return res.ok; } catch { return true; // tolérant : le test réel se fait au premier scrape } } async search(query: string, mediaType: MediaType = 'movie'): Promise { if (!this.baseUrl) throw new Error('FT_URL non configurée.'); if (!query || query.length < 3) throw new Error('La recherche nécessite au moins 3 caractères.'); const html = await fetchSearch(this.baseUrl, query); let results = parseSearchResults(html, this.baseUrl); if (mediaType === 'movie') { results = results.filter(r => r.type === 'movie' || r.type === 'anime'); } else { results = results.filter(r => r.type === 'series' || r.type === 'anime'); } return results; } async getTrending(mediaType: MediaType): Promise { if (!this.baseUrl) return []; try { const html = await fetchTrending(this.baseUrl); let results = parseTrendingResults(html, this.baseUrl); if (mediaType === 'movie') { results = results.filter(r => r.type === 'movie' || r.type === 'anime'); } else { results = results.filter(r => r.type === 'series' || r.type === 'anime'); } return results.slice(0, 20); } catch (e: any) { console.error(`[FreeTel] Erreur trending ${mediaType}:`, e.message); return []; } } async getRecent(): Promise { if (!this.baseUrl) return []; try { const html = await fetchTrending(this.baseUrl); const results = parseTrendingResults(html, this.baseUrl).slice(0, 20); return results; } catch (e: any) { console.error(`[FreeTel] Erreur getRecent:`, e.message); return []; } } async getContentLinks(identifier: string): Promise { if (!this.baseUrl) throw new Error('FT_URL non configurée.'); const url = identifier.startsWith('http') ? identifier : `${this.baseUrl}/${identifier.replace(/^\//, '')}`; const html = await fetchPage(url); return parseContentHTML(html, isSeriesIdentifier(identifier)); } async getSelection(identifier: string, type?: string, seasonValue?: string | number): Promise { if (!this.baseUrl) throw new Error('FT_URL non configurée.'); // Si seasonValue est fournie (l'UI a cliqué sur une autre qualité), on switch de fiche const targetIdentifier = seasonValue ? String(seasonValue) : identifier; const url = targetIdentifier.startsWith('http') ? targetIdentifier : `${this.baseUrl}/${targetIdentifier.replace(/^\//, '')}`; const html = await fetchPage(url); const isSeries = isSeriesIdentifier(targetIdentifier); const content = parseContentHTML(html, isSeries); // Pour les films, exposer les autres qualités comme "seasons" (l'UI les affichera en dropdown) let seasons: { label: string; value: string }[] = []; if (!isSeries) { seasons = parseOtherVersions(html, this.baseUrl); // Ajouter la version courante comme première entrée (sélectionnée par défaut) const currentQuality = content.links[0]?.quality; if (currentQuality && currentQuality !== 'Inconnu') { seasons.unshift({ label: currentQuality, value: targetIdentifier }); } } return { links: content.links, seasons, isSeries, }; } async resolveLink(linkId: string): Promise { let hostUrl: string | null = null; // Cas série : page intermédiaire liens.free-telecharger.cam/SLUG-episode_N if (linkId.includes('liens.free-telecharger.')) { try { const html = await fetchPage(linkId); const hosts = parseEpisodeLinks(html); if (hosts.length === 0) { console.warn(`[FreeTel] Aucun hôte trouvé sur ${linkId}`); return null; } const preferred = hosts.find(h => /1fichier/i.test(h.host)) || hosts.find(h => /turbobit/i.test(h.host)) || hosts[0]; hostUrl = preferred ? preferred.url : null; } catch (e: any) { console.error(`[FreeTel] Erreur resolveLink:`, e.message); return null; } } else if (linkId.startsWith('http')) { // Cas film : linkId est déjà l'URL hôte (1fichier, Turbobit, …) hostUrl = linkId; } return hostUrl; } } sourceRegistry.register(new FreeTeleAPI());