Files
scripts-bash/local/bin/trierPhotos.php
2025-08-17 13:02:48 +02:00

177 lines
6.8 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/php
<?php
$log_directory = getenv('HOME') . '/logs';
// Vérification et création du répertoire de journalisation
if (!is_dir($log_directory)) {
mkdir($log_directory, 0777, true);
}
if (!is_writable($log_directory)) {
die("Impossible d'accéder au répertoire de journalisation : $log_directory\n");
}
$timestamp = date('YmdHis');
$file_log = "$log_directory/trierPhotos_$timestamp.log";
$file_undo = "$log_directory/trierPhotos_$timestamp" . "_undo.log";
function logMessage($file, $message) {
file_put_contents($file, $message . "\n", FILE_APPEND);
echo $message . "\n";
}
/**
* Détermine le chemin de destination final selon les règles :
* - Si dest nexiste pas -> renvoie ce chemin (string).
* - Sil existe et SHA1 identique -> renvoie false (doublon déjà présent).
* - Sinon, tente filename_1.ext, filename_2.ext, ... :
* - si lun 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"));