Esenjin 87eeeafdde ajout du "mode tortue"
pour les instances vraiment trop stricts avec leur api ...
2025-03-21 20:20:12 +01:00

1210 lines
55 KiB
JavaScript

/**
* 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;
}
});