126 lines
3.4 KiB
TypeScript
Executable File
126 lines
3.4 KiB
TypeScript
Executable File
/**
|
|
* 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<string, number> = {
|
|
// 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<string, OptimizableResult[]> = {};
|
|
const typeTotals: Record<string, number> = {};
|
|
|
|
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,
|
|
};
|
|
}
|