diff --git a/.htaccess b/.htaccess
index b9e5b64..3d07569 100644
--- a/.htaccess
+++ b/.htaccess
@@ -3,58 +3,75 @@
 # Activer le moteur de réécriture
 RewriteEngine On
 
-# Protéger les fichiers sensibles
-<FilesMatch "^(config\.php|functions\.php)$">
-    Order Allow,Deny
-    Deny from all
-</FilesMatch>
+# Forcer HTTPS (à activer en production en supprimant le commentaire)
+# RewriteCond %{HTTPS} !=on
+# RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
 
-# Bloquer l'accès au répertoire includes
+# Protéger le répertoire includes
 <IfModule mod_rewrite.c>
     RewriteRule ^includes/ - [F,L]
 </IfModule>
 
-# Bloquer l'accès aux fichiers cachés
+# Bloquer l'accès aux fichiers sensibles
+<FilesMatch "^(config\.php|functions\.php|app_data\.php)$">
+    Order Allow,Deny
+    Deny from all
+</FilesMatch>
+
+# Protéger .htaccess et tout fichier commençant par un point
 <FilesMatch "^\.">
     Order Allow,Deny
     Deny from all
 </FilesMatch>
 
-# Forcer HTTPS (décommenter en production)
-# RewriteCond %{HTTPS} !=on
-# RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
-
-# Headers de sécurité
-<IfModule mod_headers.c>
-    # Empêcher le clickjacking
-    Header set X-Frame-Options "SAMEORIGIN"
-    
-    # Prévention XSS
-    Header set X-XSS-Protection "1; mode=block"
-    
-    # Empêcher le MIME sniffing
-    Header set X-Content-Type-Options "nosniff"
-    
-    # Référer policy
-    Header set Referrer-Policy "strict-origin-when-cross-origin"
-    
-    # Content Security Policy (CSP)
-    Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' https://cdn.jsdelivr.net; img-src 'self' data:; font-src 'self' https://cdn.jsdelivr.net; connect-src 'self';"
-</IfModule>
-
 # Désactiver l'affichage du contenu des répertoires
 Options -Indexes
 
-# Limiter les méthodes HTTP
+# Limiter les méthodes HTTP autorisées
 <LimitExcept GET POST HEAD>
     Order Allow,Deny
     Deny from all
 </LimitExcept>
 
-# PHP settings
+# Headers de sécurité
+<IfModule mod_headers.c>
+    # Protection contre le clickjacking
+    Header always set X-Frame-Options "SAMEORIGIN"
+    
+    # Protection XSS
+    Header always set X-XSS-Protection "1; mode=block"
+    
+    # Prévention MIME sniffing
+    Header always set X-Content-Type-Options "nosniff"
+    
+    # Referrer Policy
+    Header always set Referrer-Policy "strict-origin-when-cross-origin"
+    
+    # Content Security Policy - Ajusté pour les ressources externes utilisées
+    Header always set Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' https://cdn.jsdelivr.net; img-src 'self' data:; font-src 'self' https://cdn.jsdelivr.net; connect-src 'self'"
+    
+    # Désactiver la détection automatique du cache
+    Header unset ETag
+    FileETag None
+    
+    # Mise en cache des ressources statiques
+    <FilesMatch "\.(css|js)$">
+        Header set Cache-Control "max-age=604800, public"
+    </FilesMatch>
+    
+    # En production, activer HSTS (HTTP Strict Transport Security)
+    # Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" env=HTTPS
+</IfModule>
+
+# Configuration PHP
 <IfModule mod_php.c>
-    # Désactiver l'affichage des erreurs en production
+    # Masquer la version de PHP et autres informations
+    php_flag expose_php Off
+    
+    # Désactiver l'affichage des erreurs en production (à décommenter en production)
     # php_flag display_errors Off
+    # php_flag display_startup_errors Off
+    # php_value error_reporting 0
     
     # Limiter le temps d'exécution des scripts
     php_value max_execution_time 120
@@ -63,10 +80,22 @@ Options -Indexes
     php_value upload_max_filesize 10M
     php_value post_max_size 10M
     
-    # Sécuriser les cookies de session
+    # Sécurité des sessions
     php_value session.cookie_httponly 1
     php_value session.use_only_cookies 1
+    php_value session.cookie_samesite "Lax"
     
-    # Utiliser des cookies sécurisés en production
+    # Utiliser des cookies sécurisés en production (à décommenter en production)
     # php_value session.cookie_secure 1
-</IfModule>
\ No newline at end of file
+</IfModule>
+
+# Compresser les fichiers texte pour réduire la taille de transfert
+<IfModule mod_deflate.c>
+    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css application/javascript application/json
+</IfModule>
+
+# Protection contre les scans de vulnérabilités communes
+RedirectMatch 404 (?i)\.php\.suspected
+RedirectMatch 404 (?i)wp-login\.php
+RedirectMatch 404 (?i)wp-admin
+RedirectMatch 404 (?i)xmlrpc\.php
\ No newline at end of file
diff --git a/callback.php b/callback.php
index dbd47e6..e017dbd 100644
--- a/callback.php
+++ b/callback.php
@@ -40,83 +40,50 @@ if (!isset($_SESSION['misskey_instance']) || empty($_SESSION['misskey_instance']
     exit;
 }
 
-$instance = $_SESSION['misskey_instance'];
-$code = $_GET['code'];
-
-// En production, ici nous échangerions le code contre un token d'accès
-// Pour cette version initiale, nous simulons l'échange
-
-// Simulation : générer un token fictif
-// Dans une implémentation réelle, nous appellerions l'API Misskey pour obtenir un vrai token
-$_SESSION['misskey_token'] = 'DEMO_' . bin2hex(random_bytes(16));
-
-// Dans un cas réel, nous aurions un code similaire à celui-ci:
-/*
-// Construire l'URL pour l'échange du code
-$token_url = "https://{$instance}/oauth/token";
-
-// Paramètres de la requête
-$params = [
-    'client_id' => $config['client_id'],
-    'client_secret' => $config['client_secret'],
-    'grant_type' => 'authorization_code',
-    'code' => $code,
-    'redirect_uri' => $config['app_url'] . '/callback.php'
-];
-
-// Initialiser cURL
-$ch = curl_init();
-
-// Configurer la requête
-curl_setopt_array($ch, [
-    CURLOPT_URL => $token_url,
-    CURLOPT_RETURNTRANSFER => true,
-    CURLOPT_POST => true,
-    CURLOPT_POSTFIELDS => http_build_query($params),
-    CURLOPT_HTTPHEADER => [
-        'Content-Type: application/x-www-form-urlencoded'
-    ],
-    CURLOPT_TIMEOUT => 30,
-    CURLOPT_SSL_VERIFYPEER => true
-]);
-
-// Exécuter la requête
-$response = curl_exec($ch);
-$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
-$error = curl_error($ch);
-
-// Fermer la session cURL
-curl_close($ch);
-
-// Vérifier les erreurs
-if ($error || $http_code !== 200) {
+// Récupérer le client_secret depuis la session
+if (!isset($_SESSION['misskey_client_secret']) || empty($_SESSION['misskey_client_secret'])) {
     $_SESSION['messages'][] = [
         'type' => 'danger',
-        'text' => 'Erreur lors de l\'échange du code d\'autorisation: ' . ($error ?: 'HTTP ' . $http_code)
+        'text' => 'Informations d\'application manquantes. Veuillez recommencer.'
     ];
     header('Location: index.php');
     exit;
 }
 
-// Décoder la réponse
-$response_data = json_decode($response, true);
+$instance = $_SESSION['misskey_instance'];
+$code = $_GET['code'];
+$client_secret = $_SESSION['misskey_client_secret'];
 
-// Vérifier que le token est présent
-if (!isset($response_data['access_token']) || empty($response_data['access_token'])) {
+// Récupérer le client_id depuis les données d'application
+if (!isset($app_data['instances'][$instance]) || !isset($app_data['instances'][$instance]['client_id'])) {
     $_SESSION['messages'][] = [
         'type' => 'danger',
-        'text' => 'Aucun token d\'accès reçu. L\'authentification a échoué.'
+        'text' => 'Informations d\'application introuvables pour ' . $instance . '. Veuillez recommencer.'
+    ];
+    header('Location: index.php');
+    exit;
+}
+
+$client_id = $app_data['instances'][$instance]['client_id'];
+
+// Échanger le code contre un token d'accès
+$exchange_result = exchange_oauth_code($instance, $code, $client_id, $client_secret);
+
+if (!$exchange_result['success']) {
+    $_SESSION['messages'][] = [
+        'type' => 'danger',
+        'text' => 'Erreur lors de l\'échange du code d\'autorisation: ' . $exchange_result['message']
     ];
     header('Location: index.php');
     exit;
 }
 
 // Stocker le token dans la session
-$_SESSION['misskey_token'] = $response_data['access_token'];
-*/
+$_SESSION['misskey_token'] = $exchange_result['access_token'];
 
-// Nettoyer l'état OAuth
+// Nettoyer les données temporaires de la session
 unset($_SESSION['oauth_state']);
+unset($_SESSION['misskey_client_secret']);
 
 // Ajouter un message de succès
 $_SESSION['messages'][] = [
diff --git a/css/styles.css b/css/styles.css
index 9a49066..9bee0c0 100644
--- a/css/styles.css
+++ b/css/styles.css
@@ -1,61 +1,217 @@
-/* FavMasToKey - Styles personnalisés */
+/* FavMasToKey - Thème sombre */
+
+:root {
+    --bg-dark: #121212;
+    --bg-card: #1e1e1e;
+    --bg-input: #2a2a2a;
+    --text-primary: #e0e0e0;
+    --text-secondary: #b0b0b0;
+    --text-muted: #8a8a8a;
+    --primary-color: #7e57c2;
+    --primary-hover: #9575cd;
+    --success-color: #4caf50;
+    --info-color: #29b6f6;
+    --warning-color: #ffb74d;
+    --danger-color: #f44336;
+    --border-color: #333333;
+    --card-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
+}
 
 body {
-    background-color: #f8f9fa;
+    background-color: var(--bg-dark);
+    color: var(--text-primary);
+    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+    line-height: 1.6;
 }
 
 .container {
     max-width: 900px;
 }
 
-h1 {
-    color: #563d7c;
+h1, h2, h3, h4, h5, h6 {
+    color: var(--text-primary);
     margin-bottom: 0.5rem;
 }
 
+h1 {
+    color: var(--primary-color);
+}
+
+p {
+    color: var(--text-secondary);
+}
+
+/* Cards */
+.card {
+    background-color: var(--bg-card);
+    border: 1px solid var(--border-color);
+    border-radius: 10px;
+    box-shadow: var(--card-shadow);
+    overflow: hidden;
+}
+
+.card-title {
+    color: var(--primary-color);
+    border-bottom: 1px solid var(--border-color);
+    padding-bottom: 1rem;
+    margin-bottom: 1.5rem;
+}
+
+.card-body {
+    color: var(--text-primary);
+}
+
+/* Form elements */
+.form-control, .form-select {
+    background-color: var(--bg-input);
+    border: 1px solid var(--border-color);
+    color: var(--text-primary);
+}
+
+.form-control:focus, .form-select:focus {
+    background-color: var(--bg-input);
+    border-color: var(--primary-color);
+    box-shadow: 0 0 0 0.25rem rgba(126, 87, 194, 0.25);
+    color: var(--text-primary);
+}
+
+.form-control::placeholder {
+    color: var(--text-muted);
+}
+
+.form-text {
+    color: var(--text-muted);
+}
+
+/* Buttons */
+.btn-primary {
+    background-color: var(--primary-color);
+    border-color: var(--primary-color);
+}
+
+.btn-primary:hover, .btn-primary:focus {
+    background-color: var(--primary-hover);
+    border-color: var(--primary-hover);
+}
+
+.btn-outline-primary {
+    color: var(--primary-color);
+    border-color: var(--primary-color);
+}
+
+.btn-outline-primary:hover {
+    background-color: var(--primary-color);
+    border-color: var(--primary-color);
+    color: white;
+}
+
+.btn-link {
+    color: var(--primary-color);
+}
+
+.btn-warning {
+    background-color: var(--warning-color);
+    border-color: var(--warning-color);
+    color: #212529;
+}
+
+.btn-danger {
+    background-color: var(--danger-color);
+    border-color: var(--danger-color);
+}
+
+/* Alerts */
+.alert {
+    border-radius: 8px;
+    border: none;
+}
+
+.alert-info {
+    background-color: rgba(41, 182, 246, 0.2);
+    color: var(--info-color);
+    border-left: 4px solid var(--info-color);
+}
+
+.alert-success {
+    background-color: rgba(76, 175, 80, 0.2);
+    color: var(--success-color);
+    border-left: 4px solid var(--success-color);
+}
+
+.alert-warning {
+    background-color: rgba(255, 183, 77, 0.2);
+    color: var(--warning-color);
+    border-left: 4px solid var(--warning-color);
+}
+
+.alert-danger {
+    background-color: rgba(244, 67, 54, 0.2);
+    color: var(--danger-color);
+    border-left: 4px solid var(--danger-color);
+}
+
+/* Progress bars */
+.progress {
+    background-color: var(--border-color);
+    border-radius: 10px;
+    height: 15px;
+    overflow: hidden;
+}
+
+.progress-bar {
+    background-color: var(--primary-color);
+    border-radius: 10px;
+}
+
+.progress-bar.bg-info {
+    background-color: var(--info-color) !important;
+}
+
+/* Log container */
+#log-container {
+    background-color: #1a1a1a;
+    border: 1px solid var(--border-color);
+    border-radius: 5px;
+    font-family: 'Courier New', monospace;
+    font-size: 0.85rem;
+    padding: 10px;
+    max-height: 200px;
+    overflow-y: auto;
+}
+
+#operation-log .log-entry {
+    margin-bottom: 0.5rem;
+    padding: 2px 5px;
+    border-radius: 3px;
+}
+
+#operation-log .success {
+    color: var(--success-color);
+}
+
+#operation-log .error {
+    color: var(--danger-color);
+}
+
+#operation-log .info {
+    color: var(--info-color);
+}
+
+#operation-log .warning {
+    color: var(--warning-color);
+}
+
+/* Background for help sections */
+.bg-light {
+    background-color: #2a2a2a !important;
+    color: var(--text-secondary);
+}
+
 .step {
     transition: all 0.3s ease;
 }
 
-.card {
-    border-radius: 10px;
-    overflow: hidden;
-}
-
-.card-title {
-    color: #563d7c;
-    border-bottom: 1px solid #e9ecef;
-    padding-bottom: 1rem;
-    margin-bottom: 1.5rem;
-}
-
-#log-container {
-    font-family: 'Courier New', monospace;
-    font-size: 0.85rem;
-    border-radius: 5px;
-}
-
-#operation-log .log-entry {
-    margin-bottom: 0.5rem;
-}
-
-#operation-log .success {
-    color: #28a745;
-}
-
-#operation-log .error {
-    color: #dc3545;
-}
-
-#operation-log .info {
-    color: #17a2b8;
-}
-
-#operation-log .warning {
-    color: #ffc107;
-}
-
-/* Animation pour montrer le progrès */
+/* Animations */
 @keyframes progress-pulse {
     0% { opacity: 1; }
     50% { opacity: 0.7; }
@@ -66,18 +222,43 @@ h1 {
     animation: progress-pulse 2s infinite;
 }
 
-/* Styles pour les boutons */
-.btn-primary {
-    background-color: #563d7c;
-    border-color: #563d7c;
+/* Customizing Bootstrap components */
+.list-group-item {
+    background-color: var(--bg-card);
+    border-color: var(--border-color);
+    color: var(--text-primary);
 }
 
-.btn-primary:hover, .btn-primary:focus {
-    background-color: #452d6b;
-    border-color: #452d6b;
+.table {
+    color: var(--text-primary);
 }
 
-/* Responsive */
+.table-dark {
+    --bs-table-bg: var(--bg-card);
+    --bs-table-striped-bg: #2a2a2a;
+    --bs-table-border-color: var(--border-color);
+}
+
+/* Scrollbar styling */
+::-webkit-scrollbar {
+    width: 8px;
+    height: 8px;
+}
+
+::-webkit-scrollbar-track {
+    background: var(--bg-card);
+}
+
+::-webkit-scrollbar-thumb {
+    background: #555;
+    border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+    background: #777;
+}
+
+/* Responsive adjustments */
 @media (max-width: 768px) {
     .container {
         padding: 1rem;
@@ -86,4 +267,8 @@ h1 {
     .card-body {
         padding: 1.25rem;
     }
+    
+    h1 {
+        font-size: 2rem;
+    }
 }
\ No newline at end of file
diff --git a/deploy.sh b/deploy.sh
new file mode 100644
index 0000000..84b0647
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# Script de déploiement simplifié pour FavMasToKey
+
+# Créer les répertoires nécessaires
+mkdir -p includes css js images
+
+# Définir les permissions
+chmod 755 ./ ./includes ./css ./js ./images
+chmod 644 ./*.php ./css/*.css ./js/*.js ./.htaccess
+
+# Créer app_data.php
+touch includes/app_data.php
+chmod 666 includes/app_data.php
+
+echo "Déploiement terminé avec succès!"
\ No newline at end of file
diff --git a/doc.php b/doc.php
new file mode 100644
index 0000000..a054ccf
--- /dev/null
+++ b/doc.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * FavMasToKey - Documentation d'utilisation
+ */
+
+// Définir la constante pour inclure les fichiers
+define('FAVMASTOKEY', true);
+
+// Inclure les fichiers requis
+require_once 'includes/config.php';
+?>
+<!DOCTYPE html>
+<html lang="fr" data-bs-theme="dark">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="theme-color" content="#7e57c2">
+    <link rel="icon" href="images/favicon.svg" type="image/svg+xml">
+    <meta name="description" content="FavMasToKey - Documentation d'utilisation pour transférer vos favoris de Mastodon vers Misskey">
+    <title>Documentation - FavMasToKey</title>
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
+    <link rel="stylesheet" href="css/styles.css">
+</head>
+<body>
+    <div class="container py-5">
+        <header class="text-center mb-5">
+            <h1>FavMasToKey</h1>
+            <p class="lead">Documentation d'utilisation</p>
+            <p><a href="index.php" class="btn btn-primary">Retour à l'application</a></p>
+        </header>
+
+        <div class="row justify-content-center">
+            <div class="col-md-10">
+                <div class="card shadow-sm mb-4">
+                    <div class="card-body">
+                        <h2 class="card-title">Guide étape par étape</h2>
+                        
+                        <h3>1. Préparation</h3>
+                        <div class="mb-4">
+                            <h4>1.1 Obtenir vos favoris depuis Mastodon</h4>
+                            <ol>
+                                <li>Connectez-vous à votre compte Mastodon</li>
+                                <li>Allez dans <strong>Préférences</strong> > <strong>Exporter et importer</strong></li>
+                                <li>Dans la section <strong>Exporter</strong>, cliquez sur <strong>Demander vos favoris</strong></li>
+                                <li>Une fois le fichier prêt, téléchargez-le</li>
+                            </ol>
+                            
+                            <h4>1.2 Préparer votre compte Misskey</h4>
+                            <ol>
+                                <li>Assurez-vous d'être connecté à votre compte Misskey</li>
+                                <li>Vérifiez que vous avez suffisamment d'espace pour de nouveaux favoris</li>
+                            </ol>
+                        </div>
+                        
+                        <h3>2. Utilisation de FavMasToKey</h3>
+                        <div class="mb-4">
+                            <h4>2.1 Télécharger le fichier JSON</h4>
+                            <ol>
+                                <li>Sur la page d'accueil de FavMasToKey, cliquez sur "Parcourir" pour sélectionner votre fichier JSON de favoris</li>
+                                <li>Cliquez sur "Analyser le fichier" pour continuer</li>
+                            </ol>
+                            
+                            <h4>2.2 Connexion à votre compte Misskey</h4>
+                            <ol>
+                                <li>Entrez l'URL de votre instance Misskey (ex: misskey.io)</li>
+                                <li>Cliquez sur "Se connecter à Misskey"</li>
+                                <li>Vous serez redirigé vers votre instance Misskey pour autoriser l'application</li>
+                                <li>Suivez les instructions à l'écran pour autoriser FavMasToKey</li>
+                            </ol>
+                            
+                            <h4>2.3 Migration des favoris</h4>
+                            <ol>
+                                <li>Une fois l'autorisation accordée, vous serez redirigé vers l'écran de migration</li>
+                                <li>Cliquez sur "Démarrer la migration" pour commencer le processus</li>
+                                <li>Vous pouvez mettre en pause, reprendre ou annuler la migration à tout moment</li>
+                                <li>Le journal des opérations vous montre l'état de chaque favori traité</li>
+                            </ol>
+                        </div>
+                        
+                        <h3>3. Résolution des problèmes courants</h3>
+                        <div class="mb-4">
+                            <h4>3.1 Publications non trouvées</h4>
+                            <p>Certaines publications peuvent ne pas être trouvées sur le réseau fédéré pour diverses raisons :</p>
+                            <ul>
+                                <li>La publication a été supprimée</li>
+                                <li>L'instance d'origine est hors ligne</li>
+                                <li>L'utilisateur a changé ses paramètres de confidentialité</li>
+                                <li>Votre instance Misskey ne s'est jamais fédérée avec l'instance d'origine</li>
+                            </ul>
+                            <p>Solution : Malheureusement, il n'y a pas de solution simple pour ce problème, car il s'agit d'une limitation du réseau fédéré. Vous pouvez essayer de visiter manuellement les URLs qui ont échoué.</p>
+                            
+                            <h4>3.2 Erreurs d'authentification</h4>
+                            <p>Si vous rencontrez des problèmes lors de l'authentification avec Misskey :</p>
+                            <ul>
+                                <li>Vérifiez que vous êtes bien connecté à votre compte Misskey</li>
+                                <li>Assurez-vous que vous autorisez les cookies tiers dans votre navigateur</li>
+                                <li>Essayez de vous déconnecter puis de vous reconnecter à votre compte Misskey</li>
+                            </ul>
+                            
+                            <h4>3.3 Migration interrompue</h4>
+                            <p>Si votre migration est interrompue (par exemple, en fermant l'onglet ou en perdant la connexion Internet), FavMasToKey peut la reprendre :</p>
+                            <ul>
+                                <li>Retournez simplement sur la page de FavMasToKey</li>
+                                <li>L'application détectera automatiquement la migration en cours</li>
+                                <li>Confirmez que vous souhaitez reprendre là où vous vous êtes arrêté</li>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
+                
+                <div class="card shadow-sm">
+                    <div class="card-body">
+                        <h2 class="card-title">Informations techniques</h2>
+                        
+                        <h3>Comment ça marche ?</h3>
+                        <p>FavMasToKey fonctionne en suivant ces étapes :</p>
+                        <ol>
+                            <li><strong>Analyse du fichier JSON</strong> - L'application extrait les URLs des favoris depuis votre fichier Mastodon</li>
+                            <li><strong>Authentification OAuth</strong> - L'application s'enregistre auprès de votre instance Misskey et obtient votre autorisation</li>
+                            <li><strong>Recherche fédérée</strong> - Pour chaque favori, l'application recherche la publication équivalente sur le réseau fédéré</li>
+                            <li><strong>Ajout aux favoris</strong> - Si la publication est trouvée, elle est ajoutée à vos favoris Misskey</li>
+                        </ol>
+                        
+                        <h3>Confidentialité et sécurité</h3>
+                        <p>FavMasToKey a été conçu en mettant l'accent sur la confidentialité et la sécurité :</p>
+                        <ul>
+                            <li>Aucune donnée n'est stockée sur le serveur, tout est traité localement dans votre navigateur</li>
+                            <li>Les tokens d'authentification sont temporaires et ne sont stockés que pendant la durée de votre session</li>
+                            <li>Le code est open source et peut être audité</li>
+                            <li>L'application ne demande que les permissions minimales nécessaires (ajouter aux favoris)</li>
+                        </ul>
+                        
+                        <h3>Limitations connues</h3>
+                        <ul>
+                            <li>Les publications qui n'existent plus ou qui sont privées ne peuvent pas être retrouvées</li>
+                            <li>Les instances Misskey peuvent avoir des limites de taux (rate limits) qui ralentissent le processus</li>
+                            <li>Les grandes collections de favoris peuvent prendre du temps à migrer</li>
+                        </ul>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/images/favicon.svg b/images/favicon.svg
new file mode 100644
index 0000000..9316f56
--- /dev/null
+++ b/images/favicon.svg
@@ -0,0 +1,32 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
+ "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
+ width="500.000000pt" height="500.000000pt" viewBox="0 0 500.000000 500.000000"
+ preserveAspectRatio="xMidYMid meet">
+
+<g transform="translate(0.000000,500.000000) scale(0.100000,-0.100000)"
+fill="#000000" stroke="none">
+<path d="M1999 4846 c-2 -2 -85 -9 -184 -15 -208 -14 -278 -23 -430 -58 -318
+-74 -569 -230 -763 -476 -148 -187 -206 -333 -247 -621 -26 -186 -27 -196 -21
+-560 3 -204 10 -441 15 -526 5 -85 14 -267 20 -404 6 -149 19 -299 31 -375 52
+-318 62 -380 76 -436 77 -311 211 -524 464 -740 112 -95 199 -151 362 -230
+168 -83 346 -136 586 -175 259 -42 468 -42 738 0 270 42 549 133 648 211 46
+36 56 75 56 217 0 136 -10 186 -40 202 -28 15 -88 12 -203 -10 -58 -11 -147
+-24 -198 -30 -52 -5 -141 -16 -199 -25 -146 -23 -398 -16 -530 14 -161 37
+-291 110 -351 198 -38 55 -89 192 -89 238 0 26 6 39 25 51 23 15 32 15 113 1
+357 -65 299 -61 795 -60 408 1 475 4 620 23 383 50 577 95 722 167 325 162
+543 453 601 803 29 170 36 336 41 910 4 544 3 568 -17 670 -55 276 -151 455
+-345 646 -201 198 -479 322 -790 352 -306 31 -596 43 -1041 42 -253 0 -463 -2
+-465 -4z m804 -824 c87 -30 116 -58 157 -152 18 -40 24 -74 25 -125 0 -100
+-30 -155 -150 -277 -52 -54 -95 -99 -95 -101 0 -1 44 -3 98 -2 89 0 101 -2
+144 -28 133 -80 271 -286 330 -493 32 -112 32 -314 -1 -428 -108 -383 -414
+-651 -783 -687 -242 -24 -426 20 -619 147 -172 114 -321 320 -374 519 -23 84
+-23 385 0 470 21 81 86 214 142 294 54 76 807 832 852 856 48 24 133 44 166
+39 17 -3 65 -17 108 -32z"/>
+<path d="M2363 2992 c-106 -104 -190 -196 -204 -222 -20 -38 -24 -59 -24 -140
+0 -86 3 -100 29 -147 55 -99 158 -150 281 -141 120 10 206 71 243 171 45 119
+20 242 -69 344 -68 78 -97 170 -79 250 6 30 10 57 7 59 -2 3 -85 -76 -184
+-174z"/>
+</g>
+</svg>
diff --git a/includes/app_data.php b/includes/app_data.php
new file mode 100644
index 0000000..e69de29
diff --git a/includes/config.php b/includes/config.php
index 679b10c..2345abd 100644
--- a/includes/config.php
+++ b/includes/config.php
@@ -22,14 +22,10 @@ if (ENVIRONMENT === 'development') {
 
 // Configuration de l'application
 $config = [
-    // Informations de l'application (à remplir lors de la création de l'app sur Misskey)
+    // Informations de l'application 
     'app_name' => 'FavMasToKey',
     'app_description' => 'Outil de transfert des favoris de Mastodon vers Misskey',
-    'app_version' => '0.1.0',
-    
-    // Paramètres OAuth - À CONFIGURER
-    'client_id' => '',     // Obtenus lors de l'enregistrement de votre app sur Misskey
-    'client_secret' => '', // Obtenus lors de l'enregistrement de votre app sur Misskey
+    'app_version' => '0.2.0',
     
     // URLs de base
     'app_url' => (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']),
@@ -40,15 +36,43 @@ $config = [
     // Paramètres pour le traitement
     'batch_size' => 10,    // Nombre de favoris à traiter en une fois
     'timeout' => 30,       // Timeout des requêtes en secondes
-    'max_retries' => 3     // Nombre maximal de tentatives par favori
+    'max_retries' => 3,    // Nombre maximal de tentatives par favori
+    'delay_between_requests' => 500 // Délai entre les requêtes en millisecondes (pour éviter le rate limiting)
 ];
 
+// Fichier de stockage des informations d'application par instance
+$app_data_file = __DIR__ . '/app_data.php';
+
+// Charger ou créer le fichier de données d'application
+if (file_exists($app_data_file)) {
+    include $app_data_file;
+} else {
+    // Structure initiale pour les données d'application
+    $app_data = [
+        'instances' => []
+    ];
+    
+    // Créer le fichier avec une structure protégée
+    $app_data_content = "<?php\n// Généré automatiquement - Ne pas modifier manuellement\nif (!defined('FAVMASTOKEY')) { die('Accès direct interdit'); }\n\$app_data = " . var_export($app_data, true) . ";\n?>";
+    file_put_contents($app_data_file, $app_data_content);
+}
+
+/**
+ * Sauvegarde les données d'application
+ */
+function save_app_data() {
+    global $app_data, $app_data_file;
+    
+    $app_data_content = "<?php\n// Généré automatiquement - Ne pas modifier manuellement\nif (!defined('FAVMASTOKEY')) { die('Accès direct interdit'); }\n\$app_data = " . var_export($app_data, true) . ";\n?>";
+    file_put_contents($app_data_file, $app_data_content);
+}
+
 // Session
 session_start();
 
 // Fonctions utilitaires
 function debug($data) {
-    if (ENVIRONMENT === 'development') {
+    if (ENVIRONMENT === 'production') {
         echo '<pre>';
         print_r($data);
         echo '</pre>';
diff --git a/includes/functions.php b/includes/functions.php
index 7182c02..7a83633 100644
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -166,13 +166,221 @@ function misskey_api_request($instance, $endpoint, $data, $token) {
     }
 }
 
+/**
+ * Enregistre une application sur l'instance Misskey
+ * 
+ * @param string $instance Instance Misskey
+ * @return array Résultat de l'opération contenant client_id et client_secret
+ */
+function register_misskey_app($instance) {
+    global $config;
+    
+    // URL de l'API pour créer une application
+    $url = "https://{$instance}/api/app/create";
+    
+    // Construire l'URL de callback
+    $callback_url = $config['app_url'] . '/callback.php';
+    
+    // Données pour la création d'application
+    $data = [
+        'name' => $config['app_name'],
+        'description' => $config['app_description'],
+        'permission' => ['write:favorites'],
+        'callbackUrl' => $callback_url
+    ];
+    
+    // Initialiser cURL
+    $ch = curl_init();
+    
+    // Configurer la requête
+    curl_setopt_array($ch, [
+        CURLOPT_URL => $url,
+        CURLOPT_RETURNTRANSFER => true,
+        CURLOPT_POST => true,
+        CURLOPT_POSTFIELDS => json_encode($data),
+        CURLOPT_HTTPHEADER => [
+            'Content-Type: application/json',
+            'User-Agent: FavMasToKey/' . $config['app_version']
+        ],
+        CURLOPT_TIMEOUT => $config['timeout'],
+        CURLOPT_SSL_VERIFYPEER => true
+    ]);
+    
+    // Exécuter la requête
+    $response = curl_exec($ch);
+    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+    $error = curl_error($ch);
+    
+    // Fermer la session cURL
+    curl_close($ch);
+    
+    // Vérifier les erreurs
+    if ($error) {
+        return [
+            'success' => false,
+            'message' => 'Erreur cURL: ' . $error,
+            'http_code' => $http_code
+        ];
+    }
+    
+    // Décoder la réponse
+    $response_data = json_decode($response, true);
+    
+    // Vérifier si la requête a réussi
+    if ($http_code >= 200 && $http_code < 300 && isset($response_data['id'])) {
+        return [
+            'success' => true,
+            'client_id' => $response_data['id'],
+            'client_secret' => $response_data['secret'],
+            'http_code' => $http_code
+        ];
+    } else {
+        return [
+            'success' => false,
+            'message' => isset($response_data['error']) ? $response_data['error'] : 'Erreur API Misskey',
+            'http_code' => $http_code,
+            'data' => $response_data
+        ];
+    }
+}
+
+/**
+ * Échange un code d'autorisation contre un token d'accès
+ * 
+ * @param string $instance Instance Misskey
+ * @param string $code Code d'autorisation reçu du serveur OAuth
+ * @param string $client_id ID de l'application
+ * @param string $client_secret Secret de l'application
+ * @return array Résultat de l'opération contenant le token d'accès
+ */
+function exchange_oauth_code($instance, $code, $client_id, $client_secret) {
+    global $config;
+    
+    // URL pour l'échange du code
+    $url = "https://{$instance}/oauth/token";
+    
+    // Construire l'URL de callback (doit correspondre à celle utilisée pour l'autorisation)
+    $callback_url = $config['app_url'] . '/callback.php';
+    
+    // Données pour l'échange du code
+    $data = [
+        'client_id' => $client_id,
+        'client_secret' => $client_secret,
+        'grant_type' => 'authorization_code',
+        'code' => $code,
+        'redirect_uri' => $callback_url
+    ];
+    
+    // Initialiser cURL
+    $ch = curl_init();
+    
+    // Configurer la requête
+    curl_setopt_array($ch, [
+        CURLOPT_URL => $url,
+        CURLOPT_RETURNTRANSFER => true,
+        CURLOPT_POST => true,
+        CURLOPT_POSTFIELDS => http_build_query($data),
+        CURLOPT_HTTPHEADER => [
+            'Content-Type: application/x-www-form-urlencoded',
+            'User-Agent: FavMasToKey/' . $config['app_version']
+        ],
+        CURLOPT_TIMEOUT => $config['timeout'],
+        CURLOPT_SSL_VERIFYPEER => true
+    ]);
+    
+    // Exécuter la requête
+    $response = curl_exec($ch);
+    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+    $error = curl_error($ch);
+    
+    // Fermer la session cURL
+    curl_close($ch);
+    
+    // Vérifier les erreurs
+    if ($error) {
+        return [
+            'success' => false,
+            'message' => 'Erreur cURL: ' . $error,
+            'http_code' => $http_code
+        ];
+    }
+    
+    // Décoder la réponse
+    $response_data = json_decode($response, true);
+    
+    // Vérifier si la requête a réussi et si un token est présent
+    if ($http_code >= 200 && $http_code < 300 && isset($response_data['access_token'])) {
+        return [
+            'success' => true,
+            'access_token' => $response_data['access_token'],
+            'http_code' => $http_code
+        ];
+    } else {
+        return [
+            'success' => false,
+            'message' => isset($response_data['error']) ? $response_data['error'] : 'Erreur lors de l\'échange du code',
+            'http_code' => $http_code,
+            'data' => $response_data
+        ];
+    }
+}
+
+/**
+ * Recherche une note Mastodon sur le réseau fédéré de Misskey
+ * 
+ * @param string $instance Instance Misskey
+ * @param string $url URL de la publication Mastodon
+ * @param string $token Token d'accès
+ * @return array Résultat de la recherche
+ */
+function search_federated_note($instance, $url, $token) {
+    // Endpoint de recherche
+    $endpoint = '/api/notes/search-by-url';
+    
+    // Données pour la recherche
+    $data = [
+        'url' => $url
+    ];
+    
+    // Effectuer la requête
+    $result = misskey_api_request($instance, $endpoint, $data, $token);
+    
+    return $result;
+}
+
+/**
+ * Ajoute une note aux favoris sur Misskey
+ * 
+ * @param string $instance Instance Misskey
+ * @param string $note_id ID de la note à ajouter aux favoris
+ * @param string $token Token d'accès
+ * @return array Résultat de l'opération
+ */
+function add_to_favorites($instance, $note_id, $token) {
+    global $config;
+    
+    // Endpoint pour ajouter aux favoris
+    $endpoint = $config['misskey_api_endpoint'];
+    
+    // Données pour l'ajout aux favoris
+    $data = [
+        'noteId' => $note_id
+    ];
+    
+    // Effectuer la requête
+    $result = misskey_api_request($instance, $endpoint, $data, $token);
+    
+    return $result;
+}
+
 /**
  * Génère une URL d'autorisation OAuth pour Misskey
  * 
  * @param string $instance Instance Misskey
+ * @param string $client_id ID de l'application
  * @return string URL d'autorisation
  */
-function generate_oauth_url($instance) {
+function generate_oauth_url($instance, $client_id) {
     global $config;
     
     // Générer un état aléatoire pour la sécurité
@@ -185,7 +393,7 @@ function generate_oauth_url($instance) {
     
     // Paramètres de la requête d'autorisation
     $params = [
-        'client_id' => $config['client_id'],
+        'client_id' => $client_id,
         'response_type' => 'code',
         'redirect_uri' => $callback_url,
         'scope' => 'write:favorites',
diff --git a/index.php b/index.php
index a7123be..9c064aa 100644
--- a/index.php
+++ b/index.php
@@ -22,10 +22,13 @@ if (isset($_SESSION['messages'])) {
 }
 ?>
 <!DOCTYPE html>
-<html lang="fr">
+<html lang="fr" data-bs-theme="dark">
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <meta name="theme-color" content="#7e57c2">
+    <link rel="icon" href="images/favicon.svg" type="image/svg+xml">
+    <meta name="description" content="FavMasToKey - Transférez vos favoris de Mastodon vers Misskey en quelques clics">
     <title>FavMasToKey - Transférer vos favoris de Mastodon vers Misskey</title>
     <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">
     <link rel="stylesheet" href="css/styles.css">
@@ -35,6 +38,7 @@ if (isset($_SESSION['messages'])) {
         <header class="text-center mb-5">
             <h1>FavMasToKey</h1>
             <p class="lead">Transférez vos favoris Mastodon vers Misskey en quelques clics</p>
+            <p><a href="doc.php" class="btn btn-sm btn-outline-primary">Documentation</a></p>
         </header>
 
         <!-- Messages d'alerte -->
diff --git a/js/app.js b/js/app.js
index ad9f085..a9d1d1c 100644
--- a/js/app.js
+++ b/js/app.js
@@ -26,6 +26,24 @@ document.addEventListener('DOMContentLoaded', function() {
     let totalItems = 0;
     let isProcessing = false;
     let isPaused = false;
+    let successCount = 0;
+    let errorCount = 0;
+    let skippedCount = 0;
+    let migration = {
+        status: 'not_started', // not_started, in_progress, paused, completed, error
+        startTime: null,
+        lastUpdateTime: null,
+        progress: {
+            current: 0,
+            total: 0,
+            percentage: 0
+        },
+        stats: {
+            success: 0,
+            error: 0,
+            skipped: 0
+        }
+    };
     
     // Gérer le téléchargement et l'analyse du fichier JSON
     if (uploadForm) {
@@ -71,6 +89,26 @@ document.addEventListener('DOMContentLoaded', function() {
                     // 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
+                        }
+                    };
+                    
+                    // 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);
                 }
@@ -120,17 +158,109 @@ document.addEventListener('DOMContentLoaded', function() {
         });
     }
     
+    /**
+     * 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
+        };
+        
+        // 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;
+        
+        migration = {
+            status: 'not_started',
+            startTime: null,
+            lastUpdateTime: null,
+            progress: {
+                current: 0,
+                total: totalItems,
+                percentage: 0
+            },
+            stats: {
+                success: 0,
+                error: 0,
+                skipped: 0
+            }
+        };
+        
+        localStorage.setItem('favmastokey_migration', JSON.stringify(migration));
+        updateProgress(0);
+    }
+    
     // Gérer le processus de migration
     if (startMigration) {
         startMigration.addEventListener('click', function() {
             if (isProcessing) return;
             
-            // Récupérer les favoris depuis localStorage si nécessaire
+            // 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') {
+                    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;
+                        errorCount = migration.stats.error;
+                        skippedCount = migration.stats.skipped;
+                        
+                        // Afficher un résumé
+                        addLogEntry(`Reprise de la migration: ${successCount} réussis, ${errorCount} échecs, ${skippedCount} ignorés.`, '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;
@@ -142,6 +272,14 @@ document.addEventListener('DOMContentLoaded', function() {
             startMigration.classList.add('d-none');
             pauseMigration.classList.remove('d-none');
             
+            // 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');
+            
             addLogEntry('Démarrage de la migration...', 'info');
             
             // Lancer le processus de migration
@@ -160,10 +298,17 @@ document.addEventListener('DOMContentLoaded', function() {
                 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();
             }
         });
@@ -190,6 +335,9 @@ document.addEventListener('DOMContentLoaded', function() {
                 pauseMigration.classList.add('d-none');
                 pauseMigration.textContent = 'Pause';
                 currentProgress.classList.remove('active');
+                
+                // Réinitialiser les données de migration
+                resetMigration();
             }
         });
     }
@@ -202,12 +350,15 @@ document.addEventListener('DOMContentLoaded', function() {
             if (currentIndex >= totalItems) {
                 // Migration terminée
                 isProcessing = false;
-                addLogEntry('Migration terminée avec succès !', 'success');
+                const summary = `Migration terminée avec succès ! ${successCount} publications ajoutées aux favoris, ${errorCount} erreurs, ${skippedCount} déjà présentes.`;
+                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');
                 });
@@ -215,6 +366,9 @@ document.addEventListener('DOMContentLoaded', function() {
                 
                 // Mettre à jour la progression à 100%
                 updateProgress(100);
+                
+                // Mettre à jour le statut de la migration
+                updateMigrationData('completed');
             }
             return;
         }
@@ -252,6 +406,15 @@ document.addEventListener('DOMContentLoaded', function() {
                 if (data.results && data.results.length) {
                     data.results.forEach(result => {
                         addLogEntry(result.message, result.status);
+                        
+                        // Mettre à jour les compteurs
+                        if (result.status === 'success') {
+                            successCount++;
+                        } else if (result.status === 'error') {
+                            errorCount++;
+                        } else if (result.status === 'info') {
+                            skippedCount++;
+                        }
                     });
                 }
                 
@@ -259,7 +422,10 @@ document.addEventListener('DOMContentLoaded', function() {
                 currentIndex = endIndex;
                 
                 // Mettre à jour la progression
-                updateProgress();
+                updateProgress(data.progress.percentage);
+                
+                // Mettre à jour les données de migration
+                updateMigrationData('in_progress', data.progress);
                 
                 // Traiter le lot suivant après un court délai
                 setTimeout(processBatch, 1000);
@@ -271,6 +437,9 @@ document.addEventListener('DOMContentLoaded', function() {
                 isPaused = true;
                 pauseMigration.textContent = 'Reprendre';
                 currentProgress.classList.remove('active');
+                
+                // Mettre à jour le statut de la migration
+                updateMigrationData('error');
             }
         })
         .catch(error => {
diff --git a/oauth.php b/oauth.php
index da6d5d0..d6a28d0 100644
--- a/oauth.php
+++ b/oauth.php
@@ -40,33 +40,48 @@ if (!preg_match('/^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', $instance)) {
 // Stocker l'instance dans la session
 $_SESSION['misskey_instance'] = $instance;
 
-// En production, ici nous devrions vérifier si l'application est déjà enregistrée sur cette instance
-// Si ce n'est pas le cas, il faudrait l'enregistrer via l'API Misskey
-// Pour cette version initiale, nous utilisons des valeurs simulées
+// Vérifier si cette instance est déjà enregistrée
+$client_id = null;
+$client_secret = null;
+
+if (isset($app_data['instances'][$instance])) {
+    $client_id = $app_data['instances'][$instance]['client_id'];
+    $client_secret = $app_data['instances'][$instance]['client_secret'];
+} else {
+    // L'application n'est pas encore enregistrée, on l'enregistre
+    $registration = register_misskey_app($instance);
+    
+    if ($registration['success']) {
+        // Enregistrement réussi, stocker les informations
+        $client_id = $registration['client_id'];
+        $client_secret = $registration['client_secret'];
+        
+        // Sauvegarder dans le fichier de données
+        $app_data['instances'][$instance] = [
+            'client_id' => $client_id,
+            'client_secret' => $client_secret,
+            'registered_at' => time()
+        ];
+        
+        save_app_data();
+    } else {
+        // Échec de l'enregistrement
+        $_SESSION['messages'][] = [
+            'type' => 'danger',
+            'text' => 'Erreur lors de l\'enregistrement de l\'application sur ' . $instance . ': ' . $registration['message']
+        ];
+        header('Location: index.php');
+        exit;
+    }
+}
+
+// Stocker le client_secret dans la session pour l'utiliser plus tard
+$_SESSION['misskey_client_secret'] = $client_secret;
 
 // Générer l'URL d'autorisation
 try {
-    // REMARQUE: Pour une implémentation réelle, $config['client_id'] et $config['client_secret'] 
-    // devraient être stockés par instance car chaque instance Misskey nécessite une application distincte
-    
-    // Générer un état aléatoire pour la sécurité
-    $state = bin2hex(random_bytes(16));
-    $_SESSION['oauth_state'] = $state;
-    
-    // Construire l'URL de callback (doit correspondre à celle configurée dans l'application Misskey)
-    $callback_url = $config['app_url'] . '/callback.php';
-    
-    // Paramètres de la requête d'autorisation
-    $params = [
-        'client_id' => $config['client_id'] ?: 'DEMO_CLIENT_ID', // En production, utiliser une valeur réelle
-        'response_type' => 'code',
-        'redirect_uri' => $callback_url,
-        'scope' => 'write:favorites',
-        'state' => $state
-    ];
-    
-    // Construire l'URL d'autorisation
-    $auth_url = "https://{$instance}/oauth/authorize?" . http_build_query($params);
+    // Générer l'URL avec le client_id obtenu
+    $auth_url = generate_oauth_url($instance, $client_id);
     
     // Rediriger vers l'URL d'autorisation
     header('Location: ' . $auth_url);
diff --git a/process.php b/process.php
index c9f88ac..70386b8 100644
--- a/process.php
+++ b/process.php
@@ -65,61 +65,15 @@ foreach ($batch as $index => $url) {
         continue;
     }
     
-    // Extraire l'identifiant du toot
-    $pathParts = explode('/', trim($urlParts['path'], '/'));
-    $tootId = end($pathParts);
+    // Rechercher la note sur Misskey à partir de l'URL
+    $searchResult = search_federated_note($misskey_instance, $url, $token);
     
-    if (!is_numeric($tootId)) {
-        $results[] = [
-            'status' => 'error',
-            'message' => "Impossible d'extraire l'ID du toot: $url"
-        ];
-        continue;
-    }
-    
-    // Construire l'URL pour la recherche fédérée sur Misskey
-    $searchUrl = "https://" . $urlParts['host'] . "/@" . $pathParts[count($pathParts) - 3] . "/" . $tootId;
-    
-    // En production, ici nous ferions une recherche sur Misskey pour trouver l'équivalent du toot
-    // Pour cette version initiale, nous simulons la réussite/échec
-    
-    // Simulation : 90% de réussite, 10% d'échec
-    $success = (rand(1, 10) <= 9);
-    
-    if ($success) {
-        // Simulation de l'ajout aux favoris
-        $results[] = [
-            'status' => 'success',
-            'message' => "Ajouté aux favoris: $url"
-        ];
-    } else {
-        // Simulation d'erreur
-        $results[] = [
-            'status' => 'error',
-            'message' => "Impossible d'ajouter aux favoris: $url (publication introuvable ou inaccessible)"
-        ];
-    }
-    
-    // Dans un cas réel, nous aurions un code similaire à celui-ci:
-    /*
-    // Rechercher la note sur Misskey
-    $searchData = [
-        'query' => $searchUrl,
-        'limit' => 1
-    ];
-    
-    $searchResult = misskey_api_request($misskey_instance, '/api/notes/search', $searchData, $token);
-    
-    if ($searchResult['success'] && !empty($searchResult['data'])) {
-        // Récupérer l'ID de la note trouvée
-        $noteId = $searchResult['data'][0]['id'];
+    if ($searchResult['success'] && isset($searchResult['data']) && !empty($searchResult['data'])) {
+        // Note trouvée, récupérer son ID
+        $noteId = $searchResult['data']['id'];
         
-        // Ajouter aux favoris
-        $favoriteData = [
-            'noteId' => $noteId
-        ];
-        
-        $favoriteResult = misskey_api_request($misskey_instance, '/api/notes/favorites/create', $favoriteData, $token);
+        // Tenter d'ajouter la note aux favoris
+        $favoriteResult = add_to_favorites($misskey_instance, $noteId, $token);
         
         if ($favoriteResult['success']) {
             $results[] = [
@@ -127,21 +81,35 @@ foreach ($batch as $index => $url) {
                 'message' => "Ajouté aux favoris: $url"
             ];
         } else {
-            $results[] = [
-                'status' => 'error',
-                'message' => "Erreur lors de l'ajout aux favoris: " . $favoriteResult['message']
-            ];
+            // Vérifier si c'est une erreur de "déjà ajouté aux favoris"
+            $errorMessage = isset($favoriteResult['data']['error']['message']) 
+                ? $favoriteResult['data']['error']['message'] 
+                : $favoriteResult['message'];
+            
+            if (strpos($errorMessage, 'already') !== false) {
+                $results[] = [
+                    'status' => 'info',
+                    'message' => "Déjà dans vos favoris: $url"
+                ];
+            } else {
+                $results[] = [
+                    'status' => 'error',
+                    'message' => "Erreur lors de l'ajout aux favoris: $errorMessage"
+                ];
+            }
         }
     } else {
+        // Note non trouvée
+        $errorMessage = isset($searchResult['message']) ? $searchResult['message'] : "Publication introuvable";
+        
         $results[] = [
             'status' => 'error',
-            'message' => "Publication introuvable sur Misskey: $url"
+            'message' => "Publication non trouvée sur le réseau fédéré: $url ($errorMessage)"
         ];
     }
-    */
     
-    // Pause pour éviter de surcharger l'API (à utiliser en production)
-    // usleep(200000); // 200 ms
+    // Pause pour éviter le rate limiting
+    usleep($config['delay_between_requests'] * 1000);
 }
 
 // Renvoyer les résultats