#!/usr/bin/php renvoie ce chemin (string). * - S’il existe et SHA1 identique -> renvoie false (doublon déjà présent). * - Sinon, tente filename_1.ext, filename_2.ext, ... : * - si l’un existe et a le même SHA1 -> false (déjà présent sous variante). * - sinon renvoie le premier nom libre. */ function computeDestinationPath(string $src, string $destDir, string $baseName): string|false { $srcSha1 = @sha1_file($src); if ($srcSha1 === false) return false; $destPath = $destDir . '/' . $baseName; if (!file_exists($destPath)) { return $destPath; } $dstSha1 = @sha1_file($destPath); if ($dstSha1 !== false && hash_equals($srcSha1, $dstSha1)) { return false; // doublon exact déjà en place } $pi = pathinfo($baseName); $name = $pi['filename'] ?? $baseName; $ext = isset($pi['extension']) ? ('.' . $pi['extension']) : ''; for ($i = 1; $i <= 10000; $i++) { $candidate = $destDir . '/' . $name . '_' . $i . $ext; if (!file_exists($candidate)) { return $candidate; // premier nom libre } // si existe, comparer le SHA1 $candSha1 = @sha1_file($candidate); if ($candSha1 !== false && hash_equals($srcSha1, $candSha1)) { return false; // déjà présent sous une variante } } return false; // garde-fou } /** Déplace un fichier (rename sinon copy+unlink) */ function moveFile(string $src, string $dst): bool { // Essai direct if (@rename($src, $dst)) return true; // Fallback inter-filesystems if (@copy($src, $dst)) { if (@unlink($src)) return true; // rollback si on n'arrive pas à supprimer la source @unlink($dst); } return false; } logMessage($file_log, "Début du script : " . date("c")); logMessage($file_log, "Lieu d'exécution : " . getcwd()); if ($handle = opendir('.')) { while (($entry = readdir($handle)) !== false) { if ($entry === '.' || $entry === '..') continue; if (!is_file($entry)) continue; // ignore dossiers et liens $mime = @mime_content_type($entry) ?: ''; $mime_type = explode("/", $mime . '/'); logMessage($file_log, "\nFichier détecté : $entry"); logMessage($file_log, "MIME type : {$mime_type[0]}"); $traitement_ok = in_array($mime_type[0], ['image', 'video'], true); $dateTimeOriginal = null; // EXIF (JPEG uniquement) if ($traitement_ok && $mime_type[0] === 'image' && ($mime_type[1] ?? '') === 'jpeg') { $file_exif = @exif_read_data($entry, 'EXIF'); if (!empty($file_exif['DateTimeOriginal'])) { $date_exif = explode(':', str_replace(' ', ':', $file_exif['DateTimeOriginal'])); if (count($date_exif) >= 3) { $dateTimeOriginal = [ 'y' => (int)$date_exif[0], 'm' => (int)$date_exif[1], 'd' => (int)$date_exif[2] ]; logMessage($file_log, "EXIF DateTimeOriginal : {$file_exif['DateTimeOriginal']}"); } } } // Nom du fichier if (!$dateTimeOriginal) { if (preg_match('/^(IMG-|VID_|IMG_|VID-)(\d{4})(\d{2})(\d{2})/i', $entry, $m)) { $dateTimeOriginal = ['y' => (int)$m[2], 'm' => (int)$m[3], 'd' => (int)$m[4]]; logMessage($file_log, "Date extraite du nom de fichier."); } elseif (preg_match('/^Screenshot_(\d{4})(\d{2})(\d{2})/i', $entry, $m)) { $dateTimeOriginal = ['y' => (int)$m[1], 'm' => (int)$m[2], 'd' => (int)$m[3]]; logMessage($file_log, "Date extraite du nom de fichier (Screenshot_...)."); } } // Sanity check des dates if ($dateTimeOriginal) { if ($dateTimeOriginal['m'] < 1 || $dateTimeOriginal['m'] > 12 || $dateTimeOriginal['d'] < 1 || $dateTimeOriginal['d'] > 31 || $dateTimeOriginal['y'] < 1900 || $dateTimeOriginal['y'] > (int)date('Y')) { $dateTimeOriginal = null; } } // À défaut, mtime if (!$dateTimeOriginal) { $date_modif = explode(':', date('Y:m:d', @filemtime($entry) ?: time())); $dateTimeOriginal = [ 'y' => (int)$date_modif[0], 'm' => (int)$date_modif[1], 'd' => (int)$date_modif[2] ]; logMessage($file_log, "Date issue du mtime : {$date_modif[0]}-{$date_modif[1]}-{$date_modif[2]}"); } // Dossier + déplacement if ($dateTimeOriginal) { $rep_dest = sprintf('%04d/%02d/%02d', $dateTimeOriginal['y'], $dateTimeOriginal['m'], $dateTimeOriginal['d']); if (!is_dir($rep_dest)) { if (!mkdir($rep_dest, 0777, true) && !is_dir($rep_dest)) { logMessage($file_log, "[ERREUR] Impossible de créer le dossier : $rep_dest"); continue; } logMessage($file_log, "Dossier créé : $rep_dest"); } $finalPath = computeDestinationPath($entry, $rep_dest, basename($entry)); if ($finalPath === false) { // Un doublon identique existe déjà quelque part dans $rep_dest if (@unlink($entry)) { logMessage($file_log, "[INFO] Doublon exact détecté -> source supprimée : $entry"); // Undo = rien à faire (le fichier existe déjà en dest) } else { logMessage($file_log, "[ERREUR] Impossible de supprimer la source doublon : $entry"); } continue; } // Déplacer (rename, sinon copy+unlink) if (moveFile($entry, $finalPath)) { logMessage($file_log, "[OK] Déplacé : $entry → $finalPath"); file_put_contents($file_undo, "mv \"$finalPath\" \"$entry\"\n", FILE_APPEND); } else { logMessage($file_log, "[ERREUR] Échec du déplacement : $entry → $finalPath"); } } } closedir($handle); } logMessage($file_log, "Fin du script : " . date("c"));