utils.ts
1 import type { SearchType } from './types'; 2 import { external } from './config'; 3 4 /** 5 * Determina el tipo de contenido basado en la URL y metadatos del ítem 6 * @param link URL del recurso 7 * @param defaultType Tipo por defecto a usar si no se determina otro 8 * @returns El tipo de contenido determinado 9 */ 10 export function determineContentType(link: string, defaultType: SearchType = 'web'): SearchType { 11 if (!link) return defaultType; 12 13 // Determinar tipo por extensión del archivo 14 if (/\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff|ico)$/i.test(link)) { 15 return 'images'; 16 } 17 18 if (/\.(mp4|webm|avi|mov|wmv|flv|mkv)$/i.test(link)) { 19 return 'videos'; 20 } 21 22 if (/\.(mp3|wav|ogg|flac|aac)$/i.test(link)) { 23 return 'audio'; 24 } 25 26 if (/\.(pdf|doc|docx|xls|xlsx|ppt|pptx|odt|ods|odp|txt|rtf)$/i.test(link)) { 27 return 'document'; 28 } 29 30 if (/\.(exe|msi|apk|app|dmg|deb|rpm|zip|rar|tar|gz)$/i.test(link)) { 31 return 'app'; 32 } 33 34 return defaultType; 35 } 36 37 /** 38 * Extrae el dominio de una URL 39 * @param url URL a procesar 40 * @returns Dominio extraído o cadena vacía si no es válido 41 */ 42 export function extractDomain(url: string): string { 43 if (!url) return ''; 44 45 try { 46 const urlObj = new URL(url); 47 return urlObj.hostname; 48 } catch (error) { 49 // Fallback con regex si la URL no es válida 50 const domainMatch = url.match(/^(?:https?:\/\/)?([^\/]+)/i); 51 return domainMatch ? domainMatch[1] : ''; 52 } 53 } 54 55 /** 56 * Formatea una URL para visualización 57 * @param url URL a formatear 58 * @param maxLength Longitud máxima permitida 59 * @returns URL formateada 60 */ 61 export function formatUrl(url: string, maxLength: number = 50): string { 62 if (!url) return ''; 63 64 try { 65 const urlObj = new URL(url); 66 const pathname = urlObj.pathname === '/' ? '' : urlObj.pathname; 67 let formatted = urlObj.hostname + pathname; 68 69 // Truncar si es muy largo 70 if (formatted.length > maxLength) { 71 formatted = formatted.substring(0, maxLength - 3) + '...'; 72 } 73 return formatted; 74 } catch (error) { 75 return url.length > maxLength ? url.substring(0, maxLength - 3) + '...' : url; 76 } 77 } 78 79 /** 80 * Decodifica entidades HTML en un texto 81 * @param html Texto con posibles entidades HTML 82 * @returns Texto decodificado 83 */ 84 export function decodeHtml(html: string): string { 85 if (!html) return ''; 86 87 try { 88 const txt = document.createElement('textarea'); 89 txt.innerHTML = html; 90 return txt.value; 91 } catch (error) { 92 return html; 93 } 94 } 95 96 /** 97 * Trunca un texto a una longitud máxima 98 * @param text Texto a truncar 99 * @param maxLength Longitud máxima 100 * @returns Texto truncado 101 */ 102 export function truncateText(text: string, maxLength: number): string { 103 if (!text) return ''; 104 105 return text.length > maxLength ? text.substring(0, maxLength) + '...' : text; 106 } 107 108 /** 109 * Formatea un tamaño en bytes a formato legible 110 * @param bytes Tamaño en bytes 111 * @returns Tamaño formateado 112 */ 113 export function formatSize(bytes: number): string { 114 if (isNaN(bytes) || bytes < 0) return ''; 115 116 if (bytes < 1024) return bytes + ' B'; 117 if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'; 118 if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(1) + ' MB'; 119 120 return (bytes / (1024 * 1024 * 1024)).toFixed(1) + ' GB'; 121 } 122 123 /** 124 * Obtiene la URL del favicon para un dominio 125 * @param domain Dominio del sitio 126 * @param size Tamaño del favicon (16, 32, 48, etc.) 127 * @returns URL del favicon 128 */ 129 export function getFaviconUrl(domain: string, size: number = 32): string { 130 if (!domain) return ''; 131 return `${external.faviconApi}?domain=${encodeURIComponent(domain)}&sz=${size}`; 132 } 133 134 /** 135 * Genera una URL de imagen placeholder aleatoria 136 * @param width Ancho de la imagen 137 * @param height Alto de la imagen 138 * @param seed Semilla para la aleatorización (opcional) 139 * @returns URL de imagen placeholder 140 */ 141 export function getPlaceholderImage(width: number = 800, height: number = 600, seed?: number): string { 142 const randomSeed = seed || Math.floor(Math.random() * 10000); 143 return `${external.placeholderImage}/${width}/${height}?random=${randomSeed}`; 144 } 145 146 /** 147 * Obtiene una miniatura para un elemento basado en su tipo 148 * @param item Elemento del que obtener la miniatura 149 * @param type Tipo de contenido 150 * @returns URL de miniatura 151 */ 152 export function getThumbnailUrl(item: any, type: SearchType): string { 153 // Para búsqueda de imágenes 154 if (type === 'images') { 155 // Verificar propiedades comunes para imágenes 156 if (item.image) return item.image; 157 if (item.thumbnail) return item.thumbnail; 158 if (item.imageurl) return item.imageurl; 159 if (item.iconlink) return item.iconlink; 160 161 // Verificar propiedades en atributos 162 if (item.attr) { 163 if (item.attr.image) return item.attr.image; 164 if (item.attr.thumbnail) return item.attr.thumbnail; 165 if (item.attr.src) return item.attr.src; 166 } 167 168 // Verificar si el link es una imagen 169 if (item.link && determineContentType(item.link) === 'images') { 170 return item.link; 171 } 172 173 // Placeholder para imágenes 174 return getPlaceholderImage(); 175 } 176 177 // Para otros tipos de contenido 178 if (item.thumbnail) return item.thumbnail; 179 if (item.image) return item.image; 180 if (item.favicon) return item.favicon; 181 182 // Usar favicon para contenido web 183 if (item.link) { 184 const domain = extractDomain(item.link); 185 if (domain) { 186 return getFaviconUrl(domain); 187 } 188 } 189 190 return ''; 191 } 192 193 /** 194 * Debounce una función para evitar llamadas repetidas 195 * @param func Función a ejecutar 196 * @param wait Tiempo de espera en ms 197 * @returns Función con debounce 198 */ 199 export function debounce<T extends (...args: any[]) => any>( 200 func: T, 201 wait: number 202 ): (...args: Parameters<T>) => void { 203 let timeout: ReturnType<typeof setTimeout> | null = null; 204 205 return function(...args: Parameters<T>): void { 206 const later = () => { 207 timeout = null; 208 func(...args); 209 }; 210 211 if (timeout) clearTimeout(timeout); 212 timeout = setTimeout(later, wait); 213 }; 214 } 215 216 /** 217 * Throttle una función para limitar la frecuencia de ejecución 218 * @param func Función a ejecutar 219 * @param limit Límite de tiempo en ms 220 * @returns Función con throttle 221 */ 222 export function throttle<T extends (...args: any[]) => any>( 223 func: T, 224 limit: number 225 ): (...args: Parameters<T>) => void { 226 let inThrottle = false; 227 228 return function(...args: Parameters<T>): void { 229 if (!inThrottle) { 230 func(...args); 231 inThrottle = true; 232 setTimeout(() => (inThrottle = false), limit); 233 } 234 }; 235 }