/** * FavMasToKey - Script JavaScript principal */ // Attendre que le DOM soit chargé document.addEventListener('DOMContentLoaded', function() { // Éléments DOM const uploadForm = document.getElementById('upload-form'); const jsonFileInput = document.getElementById('json-file'); const step1 = document.getElementById('step1'); const step2 = document.getElementById('step2'); const step3 = document.getElementById('step3'); const fileSummary = document.getElementById('file-summary'); const backToStep1 = document.getElementById('back-to-step1'); const startMigration = document.getElementById('start-migration'); const pauseMigration = document.getElementById('pause-migration'); const cancelMigration = document.getElementById('cancel-migration'); const globalProgress = document.getElementById('global-progress'); const currentProgress = document.getElementById('current-progress'); const operationLog = document.getElementById('operation-log'); const slowModeCheckbox = document.getElementById('slow-mode'); const slowModeWarning = document.getElementById('slow-mode-warning'); const slowModeOptions = document.getElementById('slow-mode-options'); const slowModeDelay = document.getElementById('slow-mode-delay'); const delayValue = document.getElementById('delay-value'); // Éléments pour le mode tortue const tortoiseCheckbox = document.getElementById('tortoise-mode'); const tortoiseWarning = document.getElementById('tortoise-mode-warning'); const tortoiseOptions = document.getElementById('tortoise-mode-options'); const tortoiseDelay = document.getElementById('tortoise-mode-delay'); const tortoiseDelayValue = document.getElementById('tortoise-delay-value'); const autoPauseEnabled = document.getElementById('auto-pause-enabled'); // Variables globales let favoritesList = []; let currentIndex = 0; let totalItems = 0; let isProcessing = false; let isPaused = false; let successCount = 0; let errorCount = 0; let skippedCount = 0; let warningCount = 0; let consecutiveRateLimitErrors = 0; let adaptiveDelayIncreases = 0; let autoRestartTimer = null; let rateLimitPauseActive = false; let migration = { status: 'not_started', // not_started, in_progress, paused, completed, error, auto_paused startTime: null, lastUpdateTime: null, progress: { current: 0, total: 0, percentage: 0 }, stats: { success: 0, error: 0, skipped: 0, warning: 0 }, options: { slowMode: false, delaySeconds: 30, tortoiseMode: false, tortoiseDelaySeconds: 120, autoPauseEnabled: true } }; console.log('Éléments DOM initialisés:', { tortoiseCheckbox: !!tortoiseCheckbox, tortoiseWarning: !!tortoiseWarning, tortoiseOptions: !!tortoiseOptions, slowModeCheckbox: !!slowModeCheckbox }); // Gérer l'affichage du message d'avertissement pour le mode lent if (slowModeCheckbox) { slowModeCheckbox.addEventListener('change', function() { if (this.checked) { console.log('Mode lent activé'); slowModeWarning.classList.remove('d-none'); if (slowModeOptions) { slowModeOptions.classList.remove('d-none'); } // Désactiver le mode tortue s'il est activé if (tortoiseCheckbox && tortoiseCheckbox.checked) { console.log('Désactivation du mode tortue (conflit)'); tortoiseCheckbox.checked = false; if (tortoiseWarning) { tortoiseWarning.classList.add('d-none'); } if (tortoiseOptions) { tortoiseOptions.classList.add('d-none'); } } } else { console.log('Mode lent désactivé'); slowModeWarning.classList.add('d-none'); if (slowModeOptions) { slowModeOptions.classList.add('d-none'); } } // Mettre à jour les options de migration migration.options.slowMode = this.checked; // Sauvegarder dans localStorage if (localStorage.getItem('favmastokey_migration')) { const savedMigration = JSON.parse(localStorage.getItem('favmastokey_migration')); savedMigration.options.slowMode = this.checked; localStorage.setItem('favmastokey_migration', JSON.stringify(savedMigration)); } }); } // Gérer le curseur de délai pour le mode lent if (slowModeDelay && delayValue) { slowModeDelay.addEventListener('input', function() { delayValue.textContent = this.value; // Mettre à jour les options de migration migration.options.delaySeconds = parseInt(this.value); // Sauvegarder dans localStorage if (localStorage.getItem('favmastokey_migration')) { const savedMigration = JSON.parse(localStorage.getItem('favmastokey_migration')); if (savedMigration.options) { savedMigration.options.delaySeconds = parseInt(this.value); localStorage.setItem('favmastokey_migration', JSON.stringify(savedMigration)); } } }); } // Gérer l'affichage du message d'avertissement pour le mode tortue if (tortoiseCheckbox) { tortoiseCheckbox.addEventListener('change', function() { console.log('Changement du mode tortue:', this.checked); if (this.checked) { // Désactiver le mode lent s'il est activé if (slowModeCheckbox && slowModeCheckbox.checked) { console.log('Désactivation du mode lent (conflit)'); slowModeCheckbox.checked = false; if (slowModeWarning) { slowModeWarning.classList.add('d-none'); } if (slowModeOptions) { slowModeOptions.classList.add('d-none'); } } // Activer le mode tortue if (tortoiseWarning) { tortoiseWarning.classList.remove('d-none'); } if (tortoiseOptions) { tortoiseOptions.classList.remove('d-none'); } console.log('Mode tortue activé avec succès'); } else { // Désactiver le mode tortue if (tortoiseWarning) { tortoiseWarning.classList.add('d-none'); } if (tortoiseOptions) { tortoiseOptions.classList.add('d-none'); } console.log('Mode tortue désactivé avec succès'); } // Mettre à jour les options de migration migration.options.tortoiseMode = this.checked; // Sauvegarder dans localStorage if (localStorage.getItem('favmastokey_migration')) { const savedMigration = JSON.parse(localStorage.getItem('favmastokey_migration')); savedMigration.options.tortoiseMode = this.checked; localStorage.setItem('favmastokey_migration', JSON.stringify(savedMigration)); } }); } // Gérer le curseur de délai du mode tortue if (tortoiseDelay && tortoiseDelayValue) { tortoiseDelay.addEventListener('input', function() { tortoiseDelayValue.textContent = this.value; // Mettre à jour les options de migration migration.options.tortoiseDelaySeconds = parseInt(this.value); // Sauvegarder dans localStorage if (localStorage.getItem('favmastokey_migration')) { const savedMigration = JSON.parse(localStorage.getItem('favmastokey_migration')); if (savedMigration.options) { savedMigration.options.tortoiseDelaySeconds = parseInt(this.value); localStorage.setItem('favmastokey_migration', JSON.stringify(savedMigration)); } } }); } // Gérer l'option de pause automatique if (autoPauseEnabled) { autoPauseEnabled.addEventListener('change', function() { // Mettre à jour les options de migration migration.options.autoPauseEnabled = this.checked; // Sauvegarder dans localStorage if (localStorage.getItem('favmastokey_migration')) { const savedMigration = JSON.parse(localStorage.getItem('favmastokey_migration')); if (savedMigration.options) { savedMigration.options.autoPauseEnabled = this.checked; localStorage.setItem('favmastokey_migration', JSON.stringify(savedMigration)); } } }); } // Gérer le téléchargement et l'analyse du fichier JSON if (uploadForm) { uploadForm.addEventListener('submit', function(e) { e.preventDefault(); const file = jsonFileInput.files[0]; if (!file) { alert('Veuillez sélectionner un fichier JSON.'); return; } // Vérifier l'extension du fichier if (!file.name.endsWith('.json')) { alert('Le fichier doit être au format JSON.'); return; } // Lire le fichier const reader = new FileReader(); reader.onload = function(event) { try { const json = JSON.parse(event.target.result); // Vérifier la structure du fichier if (!json['@context'] || !json.type || !json.orderedItems) { alert('Le format du fichier JSON n\'est pas celui attendu pour un export de favoris Mastodon.'); return; } favoritesList = json.orderedItems; totalItems = favoritesList.length; // Afficher un résumé fileSummary.innerHTML = ` <strong>${totalItems}</strong> favoris trouvés dans votre fichier Mastodon. `; // Passer à l'étape 2 step1.classList.add('d-none'); step2.classList.remove('d-none'); // Stocker les données dans localStorage pour les conserver localStorage.setItem('favmastokey_favorites', JSON.stringify(favoritesList)); // Initialiser les données de migration migration = { status: 'not_started', startTime: null, lastUpdateTime: null, progress: { current: 0, total: totalItems, percentage: 0 }, stats: { success: 0, error: 0, skipped: 0, warning: 0 }, options: { slowMode: slowModeCheckbox ? slowModeCheckbox.checked : false, delaySeconds: slowModeDelay ? parseInt(slowModeDelay.value) : 30, tortoiseMode: tortoiseCheckbox ? tortoiseCheckbox.checked : false, tortoiseDelaySeconds: tortoiseDelay ? parseInt(tortoiseDelay.value) : 120, autoPauseEnabled: autoPauseEnabled ? autoPauseEnabled.checked : true } }; // Sauvegarder les données de migration localStorage.setItem('favmastokey_migration', JSON.stringify(migration)); } catch (error) { alert('Erreur lors de l\'analyse du fichier JSON: ' + error.message); } }; reader.onerror = function() { alert('Erreur lors de la lecture du fichier.'); }; reader.readAsText(file); }); } // Gestion du retour à l'étape 1 if (backToStep1) { backToStep1.addEventListener('click', function() { step2.classList.add('d-none'); step1.classList.remove('d-none'); }); } // Vérifier si nous sommes à l'étape 3 (basé sur l'ancre dans l'URL) if (window.location.hash === '#step3' && document.getElementById('step3')) { // Récupérer les favoris du localStorage if (localStorage.getItem('favmastokey_favorites')) { favoritesList = JSON.parse(localStorage.getItem('favmastokey_favorites')); totalItems = favoritesList.length; // Montrer l'étape 3 step1.classList.add('d-none'); step2.classList.add('d-none'); step3.classList.remove('d-none'); // Récupérer les données de migration du localStorage if (localStorage.getItem('favmastokey_migration')) { migration = JSON.parse(localStorage.getItem('favmastokey_migration')); currentIndex = migration.progress.current; updateProgress(migration.progress.percentage); // Restaurer les statistiques successCount = migration.stats.success || 0; errorCount = migration.stats.error || 0; skippedCount = migration.stats.skipped || 0; warningCount = migration.stats.warning || 0; // Restaurer l'état du mode lent si disponible if (migration.options) { // Restaurer l'état du mode lent if (migration.options.slowMode !== undefined && slowModeCheckbox) { slowModeCheckbox.checked = migration.options.slowMode; // Mettre à jour l'affichage de l'avertissement if (slowModeWarning) { if (migration.options.slowMode) { slowModeWarning.classList.remove('d-none'); if (slowModeOptions) { slowModeOptions.classList.remove('d-none'); } } else { slowModeWarning.classList.add('d-none'); if (slowModeOptions) { slowModeOptions.classList.add('d-none'); } } } } // Restaurer la valeur du délai if (migration.options.delaySeconds && slowModeDelay && delayValue) { slowModeDelay.value = migration.options.delaySeconds; delayValue.textContent = migration.options.delaySeconds; } // Restaurer l'état du mode tortue if (migration.options.tortoiseMode !== undefined && tortoiseCheckbox) { tortoiseCheckbox.checked = migration.options.tortoiseMode; console.log('État du mode tortue restauré:', migration.options.tortoiseMode); // Mettre à jour l'affichage de l'avertissement if (tortoiseWarning) { if (migration.options.tortoiseMode) { tortoiseWarning.classList.remove('d-none'); if (tortoiseOptions) { tortoiseOptions.classList.remove('d-none'); } } else { tortoiseWarning.classList.add('d-none'); if (tortoiseOptions) { tortoiseOptions.classList.add('d-none'); } } } } // Restaurer la valeur du délai tortue if (migration.options.tortoiseDelaySeconds && tortoiseDelay && tortoiseDelayValue) { tortoiseDelay.value = migration.options.tortoiseDelaySeconds; tortoiseDelayValue.textContent = migration.options.tortoiseDelaySeconds; } // Restaurer l'état de pause automatique if (migration.options.autoPauseEnabled !== undefined && autoPauseEnabled) { autoPauseEnabled.checked = migration.options.autoPauseEnabled; } } } } } /** * Met à jour les données de migration dans localStorage */ function updateMigrationData(status, progress = null) { migration.status = status; migration.lastUpdateTime = Date.now(); if (progress) { migration.progress = progress; } else { migration.progress = { current: currentIndex, total: totalItems, percentage: (currentIndex / totalItems) * 100 }; } migration.stats = { success: successCount, error: errorCount, skipped: skippedCount, warning: warningCount }; migration.options = { slowMode: slowModeCheckbox ? slowModeCheckbox.checked : false, delaySeconds: slowModeDelay ? parseInt(slowModeDelay.value) : 30, tortoiseMode: tortoiseCheckbox ? tortoiseCheckbox.checked : false, tortoiseDelaySeconds: tortoiseDelay ? parseInt(tortoiseDelay.value) : 120, autoPauseEnabled: autoPauseEnabled ? autoPauseEnabled.checked : true }; // Sauvegarder dans localStorage localStorage.setItem('favmastokey_migration', JSON.stringify(migration)); } /** * Réinitialise les données de migration */ function resetMigration() { currentIndex = 0; successCount = 0; errorCount = 0; skippedCount = 0; warningCount = 0; consecutiveRateLimitErrors = 0; adaptiveDelayIncreases = 0; migration = { status: 'not_started', startTime: null, lastUpdateTime: null, progress: { current: 0, total: totalItems, percentage: 0 }, stats: { success: 0, error: 0, skipped: 0, warning: 0 }, options: { slowMode: slowModeCheckbox ? slowModeCheckbox.checked : false, delaySeconds: slowModeDelay ? parseInt(slowModeDelay.value) : 30, tortoiseMode: tortoiseCheckbox ? tortoiseCheckbox.checked : false, tortoiseDelaySeconds: tortoiseDelay ? parseInt(tortoiseDelay.value) : 120, autoPauseEnabled: autoPauseEnabled ? autoPauseEnabled.checked : true } }; localStorage.setItem('favmastokey_migration', JSON.stringify(migration)); updateProgress(0); } /** * Augmente le délai en cas de détection de rate limit */ function increaseAdaptiveDelay() { if (adaptiveDelayIncreases < 3) { if (tortoiseCheckbox && tortoiseCheckbox.checked && tortoiseDelay && tortoiseDelayValue) { // Augmenter le délai du mode tortue const currentDelay = parseInt(tortoiseDelay.value); const newDelay = Math.min(300, currentDelay + 30); // Augmenter de 30 secondes jusqu'à 5 min max tortoiseDelay.value = newDelay; tortoiseDelayValue.textContent = newDelay; // Mettre à jour les options de migration migration.options.tortoiseDelaySeconds = newDelay; // Sauvegarder dans localStorage if (localStorage.getItem('favmastokey_migration')) { const savedMigration = JSON.parse(localStorage.getItem('favmastokey_migration')); savedMigration.options.tortoiseDelaySeconds = newDelay; localStorage.setItem('favmastokey_migration', JSON.stringify(savedMigration)); } adaptiveDelayIncreases++; addLogEntry(`Délai du mode tortue automatiquement augmenté à ${newDelay} secondes pour éviter les limitations d'API.`, 'warning'); return true; } else if (slowModeCheckbox && slowModeCheckbox.checked && slowModeDelay && delayValue) { // Augmenter le délai du mode lent const currentDelay = parseInt(slowModeDelay.value); const newDelay = Math.min(300, currentDelay + 30); // Augmenter de 30 secondes jusqu'à 5 min max slowModeDelay.value = newDelay; delayValue.textContent = newDelay; // Mettre à jour les options de migration migration.options.delaySeconds = newDelay; // Sauvegarder dans localStorage if (localStorage.getItem('favmastokey_migration')) { const savedMigration = JSON.parse(localStorage.getItem('favmastokey_migration')); savedMigration.options.delaySeconds = newDelay; localStorage.setItem('favmastokey_migration', JSON.stringify(savedMigration)); } adaptiveDelayIncreases++; addLogEntry(`Délai du mode lent automatiquement augmenté à ${newDelay} secondes pour éviter les limitations d'API.`, 'warning'); return true; } } return false; } /** * Déclenche une pause automatique due aux limites d'API */ function triggerRateLimitPause() { if (rateLimitPauseActive) return; // Éviter les pauses multiples // Vérifier si la pause automatique est activée if (autoPauseEnabled && !autoPauseEnabled.checked) { addLogEntry("Pause automatique désactivée. Considérez activer cette option ou augmenter le délai.", 'warning'); return; } rateLimitPauseActive = true; isPaused = true; // Durée de pause en minutes (15 minutes) const pauseDurationMinutes = 15; const pauseDurationMs = pauseDurationMinutes * 60 * 1000; addLogEntry(`Trop de limitations d'API détectées! Pause automatique de ${pauseDurationMinutes} minutes pour permettre la réinitialisation des limites.`, 'error'); if (pauseMigration) { pauseMigration.textContent = 'Reprise automatique en attente...'; pauseMigration.disabled = true; } // Mise à jour du statut updateMigrationData('auto_paused'); // Programmer la reprise automatique autoRestartTimer = setTimeout(() => { if (isProcessing && isPaused) { isPaused = false; rateLimitPauseActive = false; if (pauseMigration) { pauseMigration.textContent = 'Pause'; pauseMigration.disabled = false; } addLogEntry(`Reprise automatique après pause de ${pauseDurationMinutes} minutes.`, 'info'); updateMigrationData('in_progress'); // Redémarrer avec un délai réduit pour tester si les limites sont réinitialisées if (tortoiseCheckbox && tortoiseCheckbox.checked && tortoiseDelay && tortoiseDelayValue) { const currentDelay = parseInt(tortoiseDelay.value); // Réduire le délai mais pas en dessous de 60 secondes const newDelay = Math.max(60, currentDelay - 60); tortoiseDelay.value = newDelay; tortoiseDelayValue.textContent = newDelay; migration.options.tortoiseDelaySeconds = newDelay; } else if (slowModeCheckbox && slowModeCheckbox.checked && slowModeDelay && delayValue) { const currentDelay = parseInt(slowModeDelay.value); // Réduire le délai mais pas en dessous de 30 secondes const newDelay = Math.max(30, currentDelay - 10); slowModeDelay.value = newDelay; delayValue.textContent = newDelay; migration.options.delaySeconds = newDelay; } processBatch(); } }, pauseDurationMs); // Afficher un compte à rebours let remainingMinutes = pauseDurationMinutes; const countdownInterval = setInterval(() => { remainingMinutes--; if (remainingMinutes <= 0) { clearInterval(countdownInterval); return; } if (pauseMigration) { pauseMigration.textContent = `Reprise dans ${remainingMinutes}m...`; } }, 60000); // Mise à jour toutes les minutes } // Gérer le processus de migration if (startMigration) { startMigration.addEventListener('click', function() { if (isProcessing) return; // Récupérer les favoris et les données de migration depuis localStorage si nécessaire if (favoritesList.length === 0 && localStorage.getItem('favmastokey_favorites')) { favoritesList = JSON.parse(localStorage.getItem('favmastokey_favorites')); totalItems = favoritesList.length; } // Vérifier s'il y a une migration en cours à reprendre if (localStorage.getItem('favmastokey_migration')) { const savedMigration = JSON.parse(localStorage.getItem('favmastokey_migration')); // Si la migration était en cours ou en pause, proposer de la reprendre if (savedMigration.status === 'in_progress' || savedMigration.status === 'paused' || savedMigration.status === 'auto_paused') { const resumeConfirm = confirm(`Une migration précédente a été trouvée (${savedMigration.progress.percentage.toFixed(1)}% terminée). Voulez-vous la reprendre?`); if (resumeConfirm) { // Restaurer l'état de la migration migration = savedMigration; currentIndex = migration.progress.current; // Mettre à jour l'interface avec les données sauvegardées updateProgress(migration.progress.percentage); // Restaurer les statistiques successCount = migration.stats.success || 0; errorCount = migration.stats.error || 0; skippedCount = migration.stats.skipped || 0; warningCount = migration.stats.warning || 0; // Restaurer les options if (migration.options) { // Restaurer l'état du mode lent if (migration.options.slowMode !== undefined && slowModeCheckbox) { slowModeCheckbox.checked = migration.options.slowMode; // Mettre à jour l'affichage de l'avertissement if (slowModeWarning) { if (migration.options.slowMode) { slowModeWarning.classList.remove('d-none'); if (slowModeOptions) { slowModeOptions.classList.remove('d-none'); } } else { slowModeWarning.classList.add('d-none'); if (slowModeOptions) { slowModeOptions.classList.add('d-none'); } } } } // Restaurer la valeur du délai if (migration.options.delaySeconds && slowModeDelay && delayValue) { slowModeDelay.value = migration.options.delaySeconds; delayValue.textContent = migration.options.delaySeconds; } // Restaurer l'état du mode tortue if (migration.options.tortoiseMode !== undefined && tortoiseCheckbox) { tortoiseCheckbox.checked = migration.options.tortoiseMode; console.log('État du mode tortue restauré:', migration.options.tortoiseMode); // Mettre à jour l'affichage de l'avertissement if (tortoiseWarning) { if (migration.options.tortoiseMode) { tortoiseWarning.classList.remove('d-none'); if (tortoiseOptions) { tortoiseOptions.classList.remove('d-none'); } } else { tortoiseWarning.classList.add('d-none'); if (tortoiseOptions) { tortoiseOptions.classList.add('d-none'); } } } } // Restaurer la valeur du délai tortue if (migration.options.tortoiseDelaySeconds && tortoiseDelay && tortoiseDelayValue) { tortoiseDelay.value = migration.options.tortoiseDelaySeconds; tortoiseDelayValue.textContent = migration.options.tortoiseDelaySeconds; } // Restaurer l'état de pause automatique if (migration.options.autoPauseEnabled !== undefined && autoPauseEnabled) { autoPauseEnabled.checked = migration.options.autoPauseEnabled; } } // Afficher un résumé addLogEntry(`Reprise de la migration: ${successCount} réussis, ${errorCount} échecs, ${skippedCount} ignorés, ${warningCount} avertissements.`, 'info'); } else { // Réinitialiser la migration resetMigration(); } } else { // Réinitialiser la migration resetMigration(); } } else { // Initialiser une nouvelle migration resetMigration(); } if (favoritesList.length === 0) { addLogEntry('Aucun favori à migrer. Veuillez d\'abord télécharger votre fichier JSON.', 'error'); return; } // Démarrer la migration isProcessing = true; isPaused = false; rateLimitPauseActive = false; // Annuler le timer de reprise automatique si en cours if (autoRestartTimer) { clearTimeout(autoRestartTimer); autoRestartTimer = null; } startMigration.classList.add('d-none'); pauseMigration.classList.remove('d-none'); pauseMigration.textContent = 'Pause'; pauseMigration.disabled = false; // Initialiser le temps de démarrage si c'est une nouvelle migration if (migration.status === 'not_started' || migration.startTime === null) { migration.startTime = Date.now(); } // Mettre à jour le statut de la migration updateMigrationData('in_progress'); // Mettre à jour les options en fonction des dernières valeurs des cases à cocher let isTortoiseMode = tortoiseCheckbox && tortoiseCheckbox.checked; let isSlowMode = slowModeCheckbox && slowModeCheckbox.checked; // S'assurer que les deux modes ne sont pas activés en même temps if (isTortoiseMode && isSlowMode) { console.log("CONFLIT DE MODES: les deux modes sont activés, désactivation du mode lent"); if (slowModeCheckbox) { slowModeCheckbox.checked = false; isSlowMode = false; if (slowModeWarning) { slowModeWarning.classList.add('d-none'); } if (slowModeOptions) { slowModeOptions.classList.add('d-none'); } } } // Ajouter une entrée pour le mode utilisé let modeName = 'Mode normal'; let delayValue = 3; if (isTortoiseMode) { delayValue = tortoiseDelay ? parseInt(tortoiseDelay.value) : 120; modeName = `Mode tortue activé (${delayValue}s de délai)`; console.log('Mode tortue détecté pour la migration'); } else if (isSlowMode) { delayValue = slowModeDelay ? parseInt(slowModeDelay.value) : 30; modeName = `Mode ultra-lent activé (${delayValue}s de délai)`; console.log('Mode lent détecté pour la migration'); } else { console.log('Mode normal détecté pour la migration'); } addLogEntry(`Démarrage de la migration... (${modeName})`, 'info'); // Lancer le processus de migration processBatch(); }); } // Gérer la pause de la migration if (pauseMigration) { pauseMigration.addEventListener('click', function() { if (!isProcessing || pauseMigration.disabled) return; isPaused = !isPaused; if (isPaused) { pauseMigration.textContent = 'Reprendre'; addLogEntry('Migration en pause.', 'warning'); currentProgress.classList.remove('active'); // Mettre à jour le statut de la migration updateMigrationData('paused'); } else { pauseMigration.textContent = 'Pause'; addLogEntry('Reprise de la migration...', 'info'); currentProgress.classList.add('active'); // Mettre à jour le statut de la migration updateMigrationData('in_progress'); processBatch(); } }); } // Gérer l'annulation de la migration if (cancelMigration) { cancelMigration.addEventListener('click', function() { if (!isProcessing && currentIndex === 0) { // Retour à l'étape 1 si rien n'a commencé step3.classList.add('d-none'); step1.classList.remove('d-none'); return; } const confirmCancel = confirm('Êtes-vous sûr de vouloir annuler la migration en cours ?'); if (confirmCancel) { isProcessing = false; isPaused = false; rateLimitPauseActive = false; // Annuler le timer de reprise automatique si en cours if (autoRestartTimer) { clearTimeout(autoRestartTimer); autoRestartTimer = null; } addLogEntry('Migration annulée.', 'error'); // Réinitialiser l'interface startMigration.classList.remove('d-none'); pauseMigration.classList.add('d-none'); pauseMigration.textContent = 'Pause'; pauseMigration.disabled = false; currentProgress.classList.remove('active'); // Réinitialiser les données de migration resetMigration(); } }); } /** * Traite un lot de favoris */ function processBatch() { if (!isProcessing || isPaused || currentIndex >= totalItems) { if (currentIndex >= totalItems) { // Migration terminée isProcessing = false; const summary = `Migration terminée ! ${successCount} publications ajoutées aux favoris, ${errorCount} erreurs, ${skippedCount} déjà présentes, ${warningCount} avertissements.`; addLogEntry(summary, 'success'); startMigration.classList.remove('d-none'); startMigration.textContent = 'Terminer'; startMigration.addEventListener('click', function() { // Nettoyer localStorage et retourner à l'étape 1 localStorage.removeItem('favmastokey_favorites'); localStorage.removeItem('favmastokey_migration'); step3.classList.add('d-none'); step1.classList.remove('d-none'); }); pauseMigration.classList.add('d-none'); // Mettre à jour la progression à 100% updateProgress(100); // Mettre à jour le statut de la migration updateMigrationData('completed'); } return; } // Vérification des modes et débogage let isTortoiseMode = tortoiseCheckbox && tortoiseCheckbox.checked; let isSlowMode = slowModeCheckbox && slowModeCheckbox.checked; console.log('État des modes avant traitement du lot:', { isTortoiseMode, isSlowMode, tortoiseCheckboxExists: !!tortoiseCheckbox, tortoiseCheckboxState: tortoiseCheckbox ? tortoiseCheckbox.checked : 'N/A' }); // Nombre d'éléments à traiter dans ce lot const batchSize = (isTortoiseMode || isSlowMode) ? 1 : 2; // Toujours un seul élément en mode lent ou tortue const endIndex = Math.min(currentIndex + batchSize, totalItems); // Préparer les éléments du lot const batch = favoritesList.slice(currentIndex, endIndex); // Mettre à jour la progression actuelle currentProgress.classList.add('active'); updateProgress(); // Ajouter une entrée dans le journal addLogEntry(`Traitement du lot ${currentIndex + 1} à ${endIndex}...`, 'info'); // Compteur de tentatives pour cette requête let retryAttempt = 0; const maxRetries = 3; // Fonction pour envoyer la requête avec retry automatique function sendRequest() { // Déterminer le délai à utiliser - Assurez-vous que les valeurs sont à jour isTortoiseMode = tortoiseCheckbox && tortoiseCheckbox.checked; isSlowMode = slowModeCheckbox && slowModeCheckbox.checked; let delaySeconds = 3; if (isTortoiseMode && tortoiseDelay) { delaySeconds = parseInt(tortoiseDelay.value); } else if (isSlowMode && slowModeDelay) { delaySeconds = parseInt(slowModeDelay.value); } console.log('Envoi de la requête avec paramètres:', { tortoiseMode: isTortoiseMode, slowMode: isSlowMode, delaySeconds: delaySeconds }); fetch('process.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ batch: batch, currentIndex: currentIndex, totalItems: totalItems, slowMode: isSlowMode, tortoiseMode: isTortoiseMode, delaySeconds: delaySeconds }), // Augmenter le timeout pour éviter les erreurs de limite de temps timeout: 60000 }) .then(response => { // Vérifier si la réponse est au format JSON const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { // Si ce n'est pas du JSON, récupérer le texte pour déboguer return response.text().then(text => { throw new Error(`Réponse non-JSON reçue: ${text.substring(0, 100)}${text.length > 100 ? '...' : ''}`); }); } return response.json(); }) .then(data => { if (data.success) { // Traiter les résultats let hasRateLimitError = false; if (data.results && data.results.length) { data.results.forEach(result => { // Construction du message avec détails si disponibles let message = result.message; if (result.details) { message += ` (${result.details})`; } addLogEntry(message, result.status); // Mettre à jour les compteurs if (result.status === 'success') { successCount++; consecutiveRateLimitErrors = 0; // Réinitialiser le compteur en cas de succès } else if (result.status === 'error') { errorCount++; } else if (result.status === 'info') { skippedCount++; } else if (result.status === 'warning') { warningCount++; // Détecter les erreurs de rate limit if (result.error_type === 'rate_limit') { hasRateLimitError = true; } } }); } // Mettre à jour l'index currentIndex = endIndex; // Mettre à jour la progression updateProgress(data.progress.percentage); // Mettre à jour les données de migration updateMigrationData('in_progress', data.progress); // Si nous avons détecté une erreur de rate limit if (hasRateLimitError) { consecutiveRateLimitErrors++; if (consecutiveRateLimitErrors >= 3) { // Trop d'erreurs consécutives, déclencher une pause longue triggerRateLimitPause(); return; } else if (consecutiveRateLimitErrors >= 2) { // Si 2 erreurs consécutives, augmenter le délai automatiquement if (!isTortoiseMode && !isSlowMode) { // Recommander le mode lent addLogEntry("Activation automatique du mode ultra-lent suite à des limitations d'API répétées", "warning"); if (slowModeCheckbox) { slowModeCheckbox.checked = true; // Mettre à jour l'affichage if (slowModeWarning) { slowModeWarning.classList.remove('d-none'); } if (slowModeOptions) { slowModeOptions.classList.remove('d-none'); } // Mettre à jour les options de migration migration.options.slowMode = true; isSlowMode = true; // Sauvegarder dans localStorage updateMigrationData('in_progress', data.progress); } } else if (isSlowMode && !isTortoiseMode) { // Recommander le mode tortue addLogEntry("Activation automatique du mode tortue suite à des limitations d'API répétées en mode lent", "warning"); if (tortoiseCheckbox) { tortoiseCheckbox.checked = true; // Désactiver le mode lent if (slowModeCheckbox) { slowModeCheckbox.checked = false; if (slowModeWarning) { slowModeWarning.classList.add('d-none'); } if (slowModeOptions) { slowModeOptions.classList.add('d-none'); } } // Mettre à jour l'affichage if (tortoiseWarning) { tortoiseWarning.classList.remove('d-none'); } if (tortoiseOptions) { tortoiseOptions.classList.remove('d-none'); } // Mettre à jour les options de migration migration.options.slowMode = false; migration.options.tortoiseMode = true; isSlowMode = false; isTortoiseMode = true; // Sauvegarder dans localStorage updateMigrationData('in_progress', data.progress); } } else if (isTortoiseMode) { // Augmenter le délai si possible increaseAdaptiveDelay(); } // Attendre plus longtemps avant la prochaine requête const extraDelayInSec = 30; addLogEntry(`Ajout d'un délai supplémentaire de ${extraDelayInSec} secondes pour éviter les limitations d'API`, 'info'); // Traiter le lot suivant après un délai plus long setTimeout(processBatch, extraDelayInSec * 1000); return; } } else { // Réduire le compteur de rate limit si la requête s'est bien passée consecutiveRateLimitErrors = Math.max(0, consecutiveRateLimitErrors - 1); } // Si le serveur suggère d'utiliser un mode plus lent if (data.suggestions) { if (data.suggestions.use_tortoise_mode && !isTortoiseMode) { addLogEntry("L'API semble limiter les requêtes. Il est recommandé d'activer le 'Mode tortue'.", 'warning'); } else if (data.suggestions.use_slow_mode && !isSlowMode && !isTortoiseMode) { addLogEntry("L'API semble limiter les requêtes. Il est recommandé d'activer le 'Mode ultra-lent'.", 'warning'); } else if (data.suggestions.increase_delay) { if (isTortoiseMode) { addLogEntry("Considérez augmenter le délai du mode tortue pour réduire les limitations d'API.", 'warning'); } else if (isSlowMode) { addLogEntry("Considérez augmenter le délai du mode ultra-lent pour réduire les limitations d'API.", 'warning'); } } } // Traiter le lot suivant après un délai adapté au mode let nextBatchDelay = 3000; // Délai par défaut // Mettre à jour les états des modes isTortoiseMode = tortoiseCheckbox && tortoiseCheckbox.checked; isSlowMode = slowModeCheckbox && slowModeCheckbox.checked; if (isTortoiseMode) { // Utiliser le délai du mode tortue nextBatchDelay = delaySeconds * 1000; console.log('Utilisation du délai tortue:', nextBatchDelay); } else if (isSlowMode) { // Utiliser le délai du mode ultra-lent nextBatchDelay = delaySeconds * 1000; console.log('Utilisation du délai lent:', nextBatchDelay); } else { console.log('Utilisation du délai normal:', nextBatchDelay); } setTimeout(processBatch, nextBatchDelay); } else { // Gérer l'erreur addLogEntry('Erreur: ' + data.message, 'error'); // Pause en cas d'erreur isPaused = true; pauseMigration.textContent = 'Reprendre'; currentProgress.classList.remove('active'); // Mettre à jour le statut de la migration updateMigrationData('error'); } }) .catch(error => { // Afficher l'erreur détaillée dans la console pour le débogage console.error('Erreur complète:', error); // Ajouter l'erreur au journal addLogEntry('Erreur de connexion: ' + error.message, 'error'); // Retenter si nous n'avons pas atteint le nombre maximum de tentatives if (retryAttempt < maxRetries) { retryAttempt++; const waitTime = Math.pow(2, retryAttempt) * 1000; // Backoff exponentiel addLogEntry(`Nouvelle tentative (${retryAttempt}/${maxRetries}) dans ${waitTime/1000} secondes...`, 'warning'); setTimeout(sendRequest, waitTime); } else { // Pause après plusieurs échecs isPaused = true; pauseMigration.textContent = 'Reprendre'; currentProgress.classList.remove('active'); // Mettre à jour le statut de la migration updateMigrationData('error'); addLogEntry(`Échec après ${maxRetries} tentatives. Veuillez reprendre manuellement.`, 'error'); } }); } // Lancer la requête sendRequest(); } /** * Met à jour la barre de progression */ function updateProgress(forcedValue = null) { const progress = forcedValue !== null ? forcedValue : (currentIndex / totalItems) * 100; globalProgress.style.width = progress + '%'; globalProgress.textContent = Math.round(progress) + '%'; globalProgress.setAttribute('aria-valuenow', progress); if (forcedValue === null) { // Mettre à jour la progression actuelle const batchProgress = ((currentIndex % 5) / 5) * 100; currentProgress.style.width = batchProgress + '%'; currentProgress.setAttribute('aria-valuenow', batchProgress); } else { currentProgress.style.width = '100%'; currentProgress.setAttribute('aria-valuenow', 100); } } /** * Ajoute une entrée dans le journal des opérations */ function addLogEntry(message, status = 'info') { const entry = document.createElement('div'); entry.className = `log-entry ${status}`; const timestamp = new Date().toLocaleTimeString(); entry.textContent = `[${timestamp}] ${message}`; operationLog.appendChild(entry); // Scroll vers le bas pour voir la dernière entrée const logContainer = document.getElementById('log-container'); logContainer.scrollTop = logContainer.scrollHeight; } });