légère amélioration du "mode lent"

This commit is contained in:
Esenjin 2025-03-21 13:35:57 +01:00
parent 261f7e0f99
commit 5a1d788b18
5 changed files with 244 additions and 52 deletions

@ -102,6 +102,27 @@ p {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");
}
/* Range slider pour le délai */
.form-range {
background-color: transparent;
}
.form-range::-webkit-slider-thumb {
background: var(--primary-color);
}
.form-range::-moz-range-thumb {
background: var(--primary-color);
}
.form-range::-webkit-slider-runnable-track {
background-color: var(--border-color);
}
.form-range::-moz-range-track {
background-color: var(--border-color);
}
/* Buttons */
.btn-primary {
background-color: var(--primary-color);

@ -29,7 +29,7 @@ $config = [
// Informations de l'application
'app_name' => 'FavMasToKey',
'app_description' => 'Outil de transfert des favoris de Mastodon vers Misskey',
'app_version' => '0.4.0', // Mise à jour de la version pour les nouvelles fonctionnalités
'app_version' => '0.4.1', // Mise à jour de la version pour le mode ultra-lent
// URL de base - Utilisée pour les liens dans l'application
'app_url' => 'https://concepts.esenjin.xyz/favmastokey', // Remplacez par l'URL exacte de votre application
@ -41,8 +41,12 @@ $config = [
'batch_size' => 2,
'timeout' => 90,
'max_retries' => 3,
'delay_between_requests' => 3000, // Délai normal entre les requêtes (en millisecondes)
'slow_mode_delay' => 7000 // Délai en mode lent entre les requêtes (en millisecondes)
'delay_between_requests' => 3000, // Délai normal entre les requêtes (en millisecondes)
'slow_mode_delay' => 30000, // Délai par défaut en mode lent (30 secondes)
'slow_mode_min' => 10000, // Délai minimum pour le mode lent (10 secondes)
'slow_mode_max' => 60000, // Délai maximum pour le mode lent (60 secondes)
'adaptive_delay_step' => 5000, // Pas d'augmentation du délai en cas de rate limiting (5 secondes)
'adaptive_delay_max_increases' => 3 // Nombre maximum d'augmentations automatiques du délai
];
// Session

@ -237,14 +237,23 @@ if (isset($_SESSION['messages'])) {
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" id="slow-mode">
<label class="form-check-label" for="slow-mode">Mode lent</label>
<label class="form-check-label" for="slow-mode">Mode ultra-lent</label>
<div class="form-text">Activez cette option pour les instances Misskey avec des limitations d'API strictes</div>
</div>
<div id="slow-mode-options" class="mb-4 d-none">
<label for="slow-mode-delay" class="form-label">Délai entre les requêtes : <span id="delay-value">30</span> secondes</label>
<input type="range" class="form-range" id="slow-mode-delay" min="10" max="60" step="5" value="30">
<div class="d-flex justify-content-between small text-muted">
<span>10s (plus rapide)</span>
<span>60s (plus lent)</span>
</div>
</div>
<div id="slow-mode-warning" class="alert alert-warning mb-4 d-none">
<strong>Mode lent activé</strong>
<p>Le mode lent ajoute des délais supplémentaires entre les requêtes et réduit le nombre de favoris traités simultanément. Cela rend l'importation beaucoup plus lente mais réduit considérablement le risque de blocage par l'API.</p>
<p class="mb-0">Recommandé si vous rencontrez des erreurs de limite de taux (rate limit) ou si votre instance Misskey est très stricte sur le nombre de requêtes autorisées.</p>
<strong>Mode ultra-lent activé</strong>
<p>Ce mode ajoute des délais très importants entre les requêtes (30 secondes par défaut) et ne traite qu'un seul favori à la fois. Cela rend l'importation beaucoup plus lente mais évite presque complètement le risque de blocage par l'API.</p>
<p class="mb-0">Recommandé si vous rencontrez systématiquement des erreurs de limite de taux (rate limit) ou si votre instance Misskey est extrêmement stricte sur le nombre de requêtes autorisées.</p>
</div>
<div class="mb-4">

226
js/app.js

@ -20,6 +20,9 @@ document.addEventListener('DOMContentLoaded', function() {
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');
// Variables globales
let favoritesList = [];
@ -31,6 +34,8 @@ document.addEventListener('DOMContentLoaded', function() {
let errorCount = 0;
let skippedCount = 0;
let warningCount = 0;
let consecutiveRateLimitErrors = 0;
let adaptiveDelayIncreases = 0;
let migration = {
status: 'not_started', // not_started, in_progress, paused, completed, error
startTime: null,
@ -47,7 +52,8 @@ document.addEventListener('DOMContentLoaded', function() {
warning: 0
},
options: {
slowMode: false
slowMode: false,
delaySeconds: 30
}
};
@ -57,8 +63,14 @@ document.addEventListener('DOMContentLoaded', function() {
if (slowModeWarning) {
if (this.checked) {
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');
}
}
}
@ -68,12 +80,34 @@ document.addEventListener('DOMContentLoaded', function() {
// Sauvegarder dans localStorage
if (localStorage.getItem('favmastokey_migration')) {
const savedMigration = JSON.parse(localStorage.getItem('favmastokey_migration'));
savedMigration.options = { slowMode: this.checked };
savedMigration.options = {
slowMode: this.checked,
delaySeconds: slowModeDelay ? parseInt(slowModeDelay.value) : 30
};
localStorage.setItem('favmastokey_migration', JSON.stringify(savedMigration));
}
});
}
// Gérer le curseur de délai
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 le téléchargement et l'analyse du fichier JSON
if (uploadForm) {
uploadForm.addEventListener('submit', function(e) {
@ -135,7 +169,8 @@ document.addEventListener('DOMContentLoaded', function() {
warning: 0
},
options: {
slowMode: slowModeCheckbox ? slowModeCheckbox.checked : false
slowMode: slowModeCheckbox ? slowModeCheckbox.checked : false,
delaySeconds: slowModeDelay ? parseInt(slowModeDelay.value) : 30
}
};
@ -182,17 +217,32 @@ document.addEventListener('DOMContentLoaded', function() {
updateProgress(migration.progress.percentage);
// Restaurer l'état du mode lent si disponible
if (migration.options && 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');
} else {
slowModeWarning.classList.add('d-none');
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;
}
}
}
}
@ -223,7 +273,8 @@ document.addEventListener('DOMContentLoaded', function() {
};
migration.options = {
slowMode: slowModeCheckbox ? slowModeCheckbox.checked : false
slowMode: slowModeCheckbox ? slowModeCheckbox.checked : false,
delaySeconds: slowModeDelay ? parseInt(slowModeDelay.value) : 30
};
// Sauvegarder dans localStorage
@ -239,6 +290,8 @@ document.addEventListener('DOMContentLoaded', function() {
errorCount = 0;
skippedCount = 0;
warningCount = 0;
consecutiveRateLimitErrors = 0;
adaptiveDelayIncreases = 0;
migration = {
status: 'not_started',
@ -256,7 +309,8 @@ document.addEventListener('DOMContentLoaded', function() {
warning: 0
},
options: {
slowMode: slowModeCheckbox ? slowModeCheckbox.checked : false
slowMode: slowModeCheckbox ? slowModeCheckbox.checked : false,
delaySeconds: slowModeDelay ? parseInt(slowModeDelay.value) : 30
}
};
@ -264,6 +318,35 @@ document.addEventListener('DOMContentLoaded', function() {
updateProgress(0);
}
/**
* Augmente le délai en cas de détection de rate limit
*/
function increaseAdaptiveDelay() {
if (adaptiveDelayIncreases < 3 && slowModeDelay && delayValue) {
const currentDelay = parseInt(slowModeDelay.value);
const newDelay = Math.min(60, currentDelay + 5); // Augmenter de 5 secondes jusqu'à 60 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 automatiquement augmenté à ${newDelay} secondes pour éviter les limitations d'API.`, 'warning');
return true;
}
return false;
}
// Gérer le processus de migration
if (startMigration) {
startMigration.addEventListener('click', function() {
@ -297,18 +380,33 @@ document.addEventListener('DOMContentLoaded', function() {
skippedCount = migration.stats.skipped || 0;
warningCount = migration.stats.warning || 0;
// Restaurer l'état du mode lent si disponible
if (migration.options && 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');
} else {
slowModeWarning.classList.add('d-none');
// 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;
}
}
// Afficher un résumé
@ -346,7 +444,9 @@ document.addEventListener('DOMContentLoaded', function() {
updateMigrationData('in_progress');
// Ajouter une entrée pour le mode utilisé
const modeName = slowModeCheckbox && slowModeCheckbox.checked ? 'Mode lent activé' : 'Mode normal';
const isSlowMode = slowModeCheckbox && slowModeCheckbox.checked;
const delaySeconds = slowModeDelay ? parseInt(slowModeDelay.value) : 30;
const modeName = isSlowMode ? `Mode ultra-lent activé (${delaySeconds}s de délai)` : 'Mode normal';
addLogEntry(`Démarrage de la migration... (${modeName})`, 'info');
// Lancer le processus de migration
@ -440,9 +540,9 @@ document.addEventListener('DOMContentLoaded', function() {
return;
}
// Nombre d'éléments à traiter dans ce lot (réduit en mode lent)
// Nombre d'éléments à traiter dans ce lot (toujours 1 en mode ultra-lent)
const isSlowMode = slowModeCheckbox && slowModeCheckbox.checked;
const batchSize = isSlowMode ? 1 : 2; // Un seul élément à la fois en mode lent
const batchSize = isSlowMode ? 1 : 2; // Toujours un seul élément à la fois en mode ultra-lent
const endIndex = Math.min(currentIndex + batchSize, totalItems);
// Préparer les éléments du lot
@ -461,6 +561,8 @@ document.addEventListener('DOMContentLoaded', function() {
// Fonction pour envoyer la requête avec retry automatique
function sendRequest() {
const delaySeconds = migration.options.delaySeconds || 30;
fetch('process.php', {
method: 'POST',
headers: {
@ -470,7 +572,8 @@ document.addEventListener('DOMContentLoaded', function() {
batch: batch,
currentIndex: currentIndex,
totalItems: totalItems,
slowMode: isSlowMode
slowMode: isSlowMode,
delaySeconds: delaySeconds
}),
// Augmenter le timeout pour éviter les erreurs de limite de temps
timeout: 60000
@ -489,6 +592,8 @@ document.addEventListener('DOMContentLoaded', function() {
.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
@ -502,27 +607,22 @@ document.addEventListener('DOMContentLoaded', function() {
// 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++;
}
// Afficher une suggestion pour activer le mode lent si nécessaire
if (result.error_type === 'rate_limit' && !isSlowMode) {
addLogEntry("Astuce : Activez le 'Mode lent' pour éviter les limitations de l'API.", 'info');
// Détecter les erreurs de rate limit
if (result.error_type === 'rate_limit') {
hasRateLimitError = true;
}
}
});
}
// Si le serveur suggère d'utiliser le mode lent
if (data.suggestions && data.suggestions.use_slow_mode && !isSlowMode && slowModeCheckbox) {
addLogEntry("L'API semble limiter les requêtes. Il est recommandé d'activer le 'Mode lent'.", 'warning');
// On pourrait aussi cocher automatiquement la case, mais c'est mieux de laisser l'utilisateur décider
}
// Mettre à jour l'index
currentIndex = endIndex;
@ -532,8 +632,54 @@ document.addEventListener('DOMContentLoaded', function() {
// 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 >= 2) {
// Si 2 erreurs consécutives, augmenter le délai automatiquement
if (!isSlowMode && slowModeCheckbox) {
addLogEntry("Activation automatique du mode ultra-lent suite à des limitations d'API répétées", "warning");
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;
// Sauvegarder dans localStorage
updateMigrationData('in_progress', data.progress);
} else if (isSlowMode) {
// Augmenter le délai si possible
increaseAdaptiveDelay();
}
// Attendre plus longtemps avant la prochaine requête
const extraDelayInSec = 10;
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 le mode lent
if (data.suggestions && data.suggestions.use_slow_mode && !isSlowMode && slowModeCheckbox) {
addLogEntry("L'API semble limiter les requêtes. Il est recommandé d'activer le 'Mode ultra-lent'.", 'warning');
}
// Traiter le lot suivant après un délai adapté au mode
const nextBatchDelay = isSlowMode ? 3000 : 1000; // Attendre plus longtemps en mode lent
const nextBatchDelay = isSlowMode ? (delaySeconds * 1000) : 3000; // Utiliser le délai personnalisé en mode lent
setTimeout(processBatch, nextBatchDelay);
} else {
// Gérer l'erreur

@ -69,9 +69,14 @@ try {
$currentIndex = isset($input['currentIndex']) ? (int)$input['currentIndex'] : 0;
$totalItems = isset($input['totalItems']) ? (int)$input['totalItems'] : count($batch);
$slowMode = isset($input['slowMode']) ? (bool)$input['slowMode'] : false;
$delaySeconds = isset($input['delaySeconds']) ? (int)$input['delaySeconds'] : 30;
// Ajuster le délai entre les requêtes selon le mode
$delayMs = $slowMode ? $config['slow_mode_delay'] : $config['delay_between_requests'];
// Ajuster le délai entre les requêtes selon le mode et la valeur personnalisée
if ($slowMode) {
$delayMs = $delaySeconds * 1000; // Convertir en millisecondes
} else {
$delayMs = $config['delay_between_requests'];
}
// Résultats du traitement
$results = [];
@ -141,7 +146,7 @@ try {
} else if ($errorType === 'rate_limit') {
$results[] = [
'status' => 'warning',
'message' => "Limite d'API atteinte, veuillez activer le mode lent: $url",
'message' => "Limite d'API atteinte pour: $url. " . ($slowMode ? "Essayez d'augmenter le délai." : "Activez le mode ultra-lent."),
'error_type' => $errorType,
'details' => $errorMessage
];
@ -179,7 +184,9 @@ try {
(isset($searchResult['data']['error']) &&
(isset($searchResult['data']['error']['code']) && $searchResult['data']['error']['code'] == 'RATE_LIMIT_EXCEEDED'))) {
$errorType = 'rate_limit';
$errorMessage = "Limite d'API atteinte. Essayez d'activer le mode lent ou d'attendre quelques minutes.";
$errorMessage = $slowMode
? "Limite d'API atteinte malgré le mode ultra-lent. Essayez d'augmenter le délai ou attendez quelques minutes."
: "Limite d'API atteinte. Activez le mode ultra-lent ou attendez quelques minutes.";
} elseif ($httpCode >= 500) {
$errorType = 'server_error';
$errorMessage = "L'instance Misskey rencontre des problèmes. Statut HTTP: $httpCode";
@ -227,7 +234,12 @@ try {
'percentage' => round((($currentIndex + count($batch)) / $totalItems) * 100, 2)
],
'suggestions' => [
'use_slow_mode' => $hasRateLimitErrors && !$slowMode
'use_slow_mode' => $hasRateLimitErrors && !$slowMode,
'increase_delay' => $hasRateLimitErrors && $slowMode && $delaySeconds < 60
],
'config' => [
'slow_mode' => $slowMode,
'delay_seconds' => $delaySeconds
]
]);