/** * Search Results Optimizer * * For each cloud type, keep only the top N most relevant results. * Order groups by priority: real cloud storage > other providers > magnet/others. * * Goal: give users a manageable, high-quality result set instead of overwhelming them * with hundreds of results dominated by magnet links. */ import { detectCloudType } from '../config/cloud-labels'; /** Minimal result shape the optimizer needs */ interface OptimizableResult { title?: string; url?: string; source?: string; score?: number; [key: string]: any; } /** Priority tiers for result ordering */ const CLOUD_PRIORITY: Record = { // Tier 1: Major cloud storage (most useful for save-to-cloud feature) baidu: 10, quark: 10, aliyun: 10, // Tier 2: Other cloud storage '115': 20, tianyi: 20, '123pan': 20, uc: 20, xunlei: 20, pikpak: 20, // Tier 3: Mobile/app links (not very useful) mobile: 50, // Tier 4: Direct links (lowest utility for cloud saving) magnet: 100, ed2k: 100, others: 100, }; const DEFAULT_PRIORITY = 50; /** Get cloud type for a result, with an extra check for tracker URLs */ function getCloudType(result: OptimizableResult): string { const url = result.url; // Check for tracker/private-site URLs not covered by shared detection if (url && /mteam|hdarea|hdsky/i.test(url)) return 'others'; return detectCloudType(url); } function getPriority(cloudType: string): number { return CLOUD_PRIORITY[cloudType] ?? DEFAULT_PRIORITY; } export interface OptimizationResult { results: OptimizableResult[]; /** Per-type stats for display */ perType: Array<{ type: string; count: number; total: number }>; /** How many items were kept vs filtered */ keptCount: number; filteredCount: number; } /** * Optimize search results: * 1. Group by cloud type * 2. Sort by score descending within each group * 3. Keep only top `maxPerType` results per type * 4. Order groups by priority (cloud storage first) */ export function optimizeSearchResults( items: OptimizableResult[], maxPerType: number = 20 ): OptimizationResult { // Step 1: Group by cloud type const grouped: Record = {}; const typeTotals: Record = {}; for (const item of items) { const ct = getCloudType(item); if (!grouped[ct]) { grouped[ct] = []; } grouped[ct].push(item); typeTotals[ct] = (typeTotals[ct] || 0) + 1; } // Step 2 & 3: Sort each group by score desc, take top N const kept: OptimizableResult[] = []; const perType: Array<{ type: string; count: number; total: number }> = []; for (const [ct, groupItems] of Object.entries(grouped)) { // Sort by score descending (higher score = more relevant) groupItems.sort((a, b) => (b.score || 0) - (a.score || 0)); const top = groupItems.slice(0, maxPerType); kept.push(...top); perType.push({ type: ct, count: top.length, total: typeTotals[ct], }); } // Step 4: Sort kept results by cloud priority, then by score within same priority kept.sort((a, b) => { const pa = getPriority(getCloudType(a)); const pb = getPriority(getCloudType(b)); if (pa !== pb) return pa - pb; return (b.score || 0) - (a.score || 0); }); const keptCount = kept.length; const filteredCount = items.length - keptCount; return { results: kept, perType, keptCount, filteredCount, }; }