import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:image_picker/image_picker.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; import '../../core/constants/env_config.dart'; /// Configuration de compression d'image. class CompressionConfig { const CompressionConfig({ this.quality = 85, this.maxWidth = 1920, this.maxHeight = 1920, this.format = CompressFormat.jpeg, }); final int quality; // 0-100 final int maxWidth; final int maxHeight; final CompressFormat format; /// Configuration pour les posts (équilibre qualité/taille) static const CompressionConfig post = CompressionConfig( quality: 85, maxWidth: 1920, maxHeight: 1920, ); /// Configuration pour les thumbnails (petite taille) static const CompressionConfig thumbnail = CompressionConfig( quality: 70, maxWidth: 400, maxHeight: 400, ); /// Configuration pour les stories (vertical, haute qualité) static const CompressionConfig story = CompressionConfig( quality: 90, maxWidth: 1080, maxHeight: 1920, ); /// Configuration pour les avatars (petit, carré) static const CompressionConfig avatar = CompressionConfig( quality: 80, maxWidth: 500, maxHeight: 500, ); } /// Service de compression d'images. /// /// Compresse les images avant l'upload pour réduire la bande passante /// et améliorer les performances. class ImageCompressionService { /// Compresse une image selon la configuration donnée. Future compressImage( XFile file, { CompressionConfig config = CompressionConfig.post, }) async { try { final filePath = file.path; final lastIndex = filePath.lastIndexOf('.'); final splitted = filePath.substring(0, lastIndex); final outPath = '${splitted}_compressed${path.extension(filePath)}'; if (EnvConfig.enableDetailedLogs) { final originalSize = await File(filePath).length(); debugPrint('[ImageCompression] Compression de: ${path.basename(filePath)}'); debugPrint('[ImageCompression] Taille originale: ${_formatBytes(originalSize)}'); } final result = await FlutterImageCompress.compressAndGetFile( filePath, outPath, quality: config.quality, minWidth: config.maxWidth, minHeight: config.maxHeight, format: config.format, ); if (result != null) { if (EnvConfig.enableDetailedLogs) { final compressedSize = await File(result.path).length(); final originalSize = await File(filePath).length(); final reduction = ((1 - compressedSize / originalSize) * 100).toStringAsFixed(1); debugPrint('[ImageCompression] Taille compressée: ${_formatBytes(compressedSize)}'); debugPrint('[ImageCompression] Réduction: $reduction%'); } return result; } return null; } catch (e) { debugPrint('[ImageCompression] Erreur: $e'); // En cas d'erreur, on retourne le fichier original return file; } } /// Compresse plusieurs images en parallèle. Future> compressMultipleImages( List files, { CompressionConfig config = CompressionConfig.post, void Function(int processed, int total)? onProgress, }) async { final results = []; int processed = 0; for (final file in files) { // Ne compresser que les images, pas les vidéos if (_isImageFile(file.path)) { final compressed = await compressImage(file, config: config); results.add(compressed ?? file); } else { results.add(file); } processed++; if (onProgress != null) { onProgress(processed, files.length); } } return results; } /// Crée un thumbnail à partir d'une image. Future createThumbnail(XFile file) async { return compressImage(file, config: CompressionConfig.thumbnail); } /// Vérifie si le fichier est une image. bool _isImageFile(String filePath) { const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp']; final extension = path.extension(filePath).toLowerCase(); return imageExtensions.contains(extension); } /// Formate la taille en bytes de manière lisible. String _formatBytes(int bytes) { if (bytes < 1024) return '$bytes B'; if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB'; if (bytes < 1024 * 1024 * 1024) { return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; } return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB'; } /// Nettoie les fichiers temporaires compressés. Future cleanupTempFiles() async { try { final tempDir = await getTemporaryDirectory(); final files = tempDir.listSync(); for (final file in files) { if (file.path.contains('_compressed')) { await file.delete(); } } if (EnvConfig.enableDetailedLogs) { debugPrint('[ImageCompression] Fichiers temporaires nettoyés'); } } catch (e) { debugPrint('[ImageCompression] Erreur nettoyage: $e'); } } }