/** * Utility functions */ /** * Format bytes to human readable format */ function formatBytes(bytes) { if (!bytes) return '0 B'; const k = 1024; const sizes = ['B', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } /** * Format bytes per second to speed */ function formatSpeed(bytesPerSecond) { return formatBytes(bytesPerSecond) + '/s'; } /** * Escape HTML to prevent XSS */ function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * Translate download status to French */ function translateStatus(status) { const translations = { 'pending': 'En attente', 'downloading': 'Téléchargement', 'paused': 'En pause', 'completed': 'Terminé', 'failed': 'Échoué', 'cancelled': 'Annulé' }; return translations[status] || status; } /** * Extract series name from filename (for grouping) */ function extractSeriesName(filename) { let name = filename; // Remove file extension name = name.replace(/\.[^/.]+$/, ''); // Remove episode numbers and patterns name = name .replace(/[-_ ]?(E(?:p)?|Episode|Épisode|Saison|Season)[-_: ]?\d+/gi, '') .replace(/[-_ ]?S\d{2}E\d{2}/gi, '') .replace(/\[.*?\]/g, '') .replace(/\(.*\)/g, '') .replace(/[-_ ]?\d{3,4}p/gi, '') .replace(/[-_ ]?(VOSTFR|VF|MULTI)/gi, '') .replace(/\s+/g, ' ') // Replace multiple spaces with single space .replace(/[-_]+$/, '') // Remove trailing dashes/underscores .trim(); // If nothing left or too short, use original filename without extension if (!name || name.length < 3) { return filename.replace(/\.[^/.]+$/, ''); } return name; } /** * Get day string for grouping */ function getDayString(dateString) { const date = new Date(dateString); const today = new Date(); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); if (date.toDateString() === today.toDateString()) { return "Aujourd'hui"; } else if (date.toDateString() === yesterday.toDateString()) { return "Hier"; } else { return date.toLocaleDateString('fr-FR', { weekday: 'long', day: 'numeric', month: 'short' }); } }