From 3f7727ec57d6304698e88a6528d314aeb84823c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9drix?= Date: Sun, 17 Aug 2025 13:02:48 +0200 Subject: [PATCH] sdf --- .config/files_local-bin | 2 +- local/bin/trierPhotos.php | 167 +++++++++++++++++++++----------- local/bin/updateall.sh | 194 +++++++++++++++++++++++++++++--------- 3 files changed, 261 insertions(+), 102 deletions(-) diff --git a/.config/files_local-bin b/.config/files_local-bin index 2a01877..f4aed50 100644 --- a/.config/files_local-bin +++ b/.config/files_local-bin @@ -1,3 +1,4 @@ +trierPhotos.php updateall.sh castopod_update.sh check_domain_cert.sh @@ -13,6 +14,5 @@ podcast_convertImage.sh random_music_player.sh ssh-add-config.sh update_bullseye2buster.sh -trierPhotos.php generate_playlist_fp.php ytdll diff --git a/local/bin/trierPhotos.php b/local/bin/trierPhotos.php index c93e125..ae990fe 100755 --- a/local/bin/trierPhotos.php +++ b/local/bin/trierPhotos.php @@ -13,7 +13,7 @@ if (!is_writable($log_directory)) { } $timestamp = date('YmdHis'); -$file_log = "$log_directory/trierPhotos_$timestamp.log"; +$file_log = "$log_directory/trierPhotos_$timestamp.log"; $file_undo = "$log_directory/trierPhotos_$timestamp" . "_undo.log"; function logMessage($file, $message) { @@ -21,99 +21,156 @@ function logMessage($file, $message) { echo $message . "\n"; } +/** + * Détermine le chemin de destination final selon les règles : + * - Si dest n’existe pas -> 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_type = explode("/", mime_content_type($entry)); + $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']); + $traitement_ok = in_array($mime_type[0], ['image', 'video'], true); $dateTimeOriginal = null; - - // Lecture des métadonnées EXIF - if ($traitement_ok && $mime_type[0] === 'image' && $mime_type[1] === 'jpeg') { + + // 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'])); - $dateTimeOriginal = [ - 'y' => $date_exif[0], - 'm' => $date_exif[1], - 'd' => $date_exif[2] - ]; - logMessage($file_log, "EXIF DateTimeOriginal : {$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']}"); + } } } - - // Détection via le nom du fichier + + // Nom du fichier if (!$dateTimeOriginal) { - if (preg_match('/^(IMG-|VID_|IMG_|VID-)(\d{4})(\d{2})(\d{2})/', $entry, $matches)) { - $dateTimeOriginal = [ - 'y' => $matches[2], - 'm' => $matches[3], - 'd' => $matches[4] - ]; + 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})/', $entry, $matches)) { - $dateTimeOriginal = [ - 'y' => $matches[1], - 'm' => $matches[2], - 'd' => $matches[3] - ]; + } 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_...)."); } } - - // Vérification et correction des dates + + // Sanity check des dates if ($dateTimeOriginal) { if ($dateTimeOriginal['m'] < 1 || $dateTimeOriginal['m'] > 12 || $dateTimeOriginal['d'] < 1 || $dateTimeOriginal['d'] > 31 || - $dateTimeOriginal['y'] < 1900 || $dateTimeOriginal['y'] > date('Y')) { + $dateTimeOriginal['y'] < 1900 || $dateTimeOriginal['y'] > (int)date('Y')) { $dateTimeOriginal = null; } } - - // Utilisation de la date de modification si aucune autre date trouvée + + // À défaut, mtime if (!$dateTimeOriginal) { - $date_modif = explode(':', date('Y:m:d', filemtime($entry))); + $date_modif = explode(':', date('Y:m:d', @filemtime($entry) ?: time())); $dateTimeOriginal = [ - 'y' => $date_modif[0], - 'm' => $date_modif[1], - 'd' => $date_modif[2] + '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]}"); } - // Création du dossier de destination et déplacement + // Dossier + déplacement if ($dateTimeOriginal) { - $rep_dest = "{$dateTimeOriginal['y']}/{$dateTimeOriginal['m']}/{$dateTimeOriginal['d']}"; + $rep_dest = sprintf('%04d/%02d/%02d', $dateTimeOriginal['y'], $dateTimeOriginal['m'], $dateTimeOriginal['d']); if (!is_dir($rep_dest)) { - mkdir($rep_dest, 0777, true); + 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"); } - - $file_dest = "$rep_dest/$entry"; - if (file_exists($file_dest)) { - $md5_src = md5_file($entry); - $md5_dst = md5_file($file_dest); - - if ($md5_src === $md5_dst) { - unlink($entry); - logMessage($file_log, "[INFO] Doublon exact détecté et supprimé : $file_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 { - // Générer un nom unique en ajoutant un suffixe MD5 ou un timestamp - $file_info = pathinfo($entry); - $new_filename = $file_info['filename'] . "_" . substr($md5_src, 0, 8) . "." . $file_info['extension']; - $file_dest_unique = "$rep_dest/$new_filename"; - - rename($entry, $file_dest_unique); - logMessage($file_log, "[INFO] Conflit évité, fichier renommé et déplacé : $file_dest_unique"); - file_put_contents($file_undo, "mv \"$file_dest_unique\" \"$entry\"\n", FILE_APPEND); + 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")); + diff --git a/local/bin/updateall.sh b/local/bin/updateall.sh index 8d230d9..2ebe774 100755 --- a/local/bin/updateall.sh +++ b/local/bin/updateall.sh @@ -18,10 +18,42 @@ SSH_TIMEOUT="5" run_ssh() { local machine="$1" - local cmd="$2" - timeout "$SSH_TIMEOUT" ssh $SSH_OPTS "$machine" "$cmd" + local start_time=$(date +%s) + shift + + if [ $# -eq 0 ]; then + ssh "$machine" 'bash -s' < /dev/stdin + else + ssh "$machine" "$@" + fi + + local end_time=$(date +%s) + local duration=$(( end_time - start_time )) + echo -e " ⏱️ Durée : ${duration}s" } + +extract_stats_block() { + local label="$1" + grep -A2 "^>>> $label" "$logfile" | grep -E '^[0-9]+ mis à jour' | tail -n1 +} + + +parse_summary_line() { + awk ' + { + updated += $1 + installed += $4 + removed += $7 + last_notup = $11 + } + END { + print updated, installed, removed, (last_notup ? last_notup : 0) + }' +} + + + run_scp() { local src="$1" local dest="$2" @@ -30,46 +62,107 @@ run_scp() { } -# Fonction pour mettre à jour avec apt update_with_apt() { local machine="$1" - local tmpfile="/tmp/update_${machine}_$(date +%s).log" + local uniqkey="$2" + + local start_time=$(date +%s) echo -e " - Mise à jour avec apt-get sur $machine \n" - # Exécution distante avec apt-get uniquement, log récupéré localement - run_ssh "$machine" ' - set -e - export DEBIAN_FRONTEND=noninteractive - LOGFILE=$(mktemp) + stats_tmp=$(mktemp) - sudo apt-get -y -q clean - sudo apt-get -y -q update - sudo apt-get -y -q --with-new-pkgs full-upgrade >> "$LOGFILE" 2>&1 - sudo apt-get -y -q autoremove >> "$LOGFILE" 2>&1 +run_ssh "$machine" <<'EOF' | tee "$stats_tmp" +export DEBIAN_FRONTEND=noninteractive - cat "$LOGFILE" - rm -f "$LOGFILE" - ' > "$tmpfile" +echo ">>> clean" +sudo apt-get -y clean - # Analyse locale du log pour extraire les lignes intéressantes - updates=$(grep "^Les paquets suivants seront mis à jour" "$tmpfile" | wc -w) - installs=$(grep "^Les NOUVEAUX paquets suivants seront installés" "$tmpfile" | wc -w) - removes=$(grep "^Les paquets suivants seront ENLEVÉS" "$tmpfile" | wc -w) +echo ">>> update" +sudo apt-get update - # Retirer le nombre de mots fixes d’en-tête pour chaque ligne - updates=$(( updates > 6 ? updates - 6 : 0 )) - installs=$(( installs > 8 ? installs - 8 : 0 )) - removes=$(( removes > 6 ? removes - 6 : 0 )) +echo ">>> check-upgrade" +UPGRADABLE=$(apt-get -s upgrade | grep "^Inst" | wc -l) - if [ "$updates" -gt 0 ] || [ "$installs" -gt 0 ] || [ "$removes" -gt 0 ]; then - echo -e "📦 ${GREEN}$machine${NC} : $updates mis à jour, $installs installés, $removes supprimés" +if [ "${UPGRADABLE:-0}" -gt 0 ] 2>/dev/null; then + echo ">>> upgrade" + sudo apt-get -y upgrade + + echo ">>> full-upgrade" + sudo apt-get -y full-upgrade + + echo ">>> autoremove" + sudo apt-get -y autoremove +else + echo ">>> Aucun paquet à mettre à jour" +fi + + +if [ -f /var/run/reboot-required ]; then + echo ">>> REDÉMARRAGE NÉCESSAIRE" +fi +EOF + + + local end_time=$(date +%s) + local duration=$(( end_time - start_time )) + + log_capture=$(cat "$stats_tmp") + + local ssh_status=$? + + if [ "$ssh_status" -ne 0 ]; then + echo -e "❌ Échec SSH ou erreur distante sur $machine (code $ssh_status)" + else + echo -e "✅ $machine : Terminé avec succès" fi - rm -f "$tmpfile" echo + + # Récupération stats à partir des logs visibles en live + # On extrait les chiffres comme dans le message de apt + # Initialisation des compteurs + updated=0 + installed=0 + removed=0 + unchanged=0 + + # Extraire les lignes après chaque bloc + line_upgrade=$(echo "$log_capture" | grep -A2 '^>>> upgrade' | grep -E '^[0-9]+ mis à jour' | tail -n1) + line_fullup=$(echo "$log_capture" | grep -A2 '^>>> full-upgrade' | grep -E '^[0-9]+ mis à jour' | tail -n1) + line_autorm=$(echo "$log_capture" | grep -A2 '^>>> autoremove' | grep -E '^[0-9]+ mis à jour' | tail -n1) + + # Ajouter les lignes ensemble + line_all="$line_upgrade"$'\n'"$line_fullup"$'\n'"$line_autorm" + + read updated installed removed unchanged <<< $(echo "$line_all" | parse_summary_line) + + # Date/heure actuelle + now=$(date '+%Y-%m-%d %H:%M:%S') + + # Fichier de stats + stats_file="$HOME/.config/updateall-stats" + mkdir -p "$(dirname "$stats_file")" + + # Ajouter l’en-tête si le fichier est vide + if [ ! -s "$stats_file" ]; then + echo "machine,date,duree,updated,installed,removed,unchanged,uniqkey" > "$stats_file" + fi + + # Ajout ligne CSV + echo "$machine,$now,$duration,$updated,$installed,$removed,$unchanged,$uniqkey" >> "$stats_file" + + } + + + + + + + + # Fonction pour mettre à jour avec dnf update_with_dnf() { echo -e " - Mise à jour avec dnf sur $1 \n" @@ -151,7 +244,7 @@ update_machine () { if timeout 5 ssh "$machine" which apt > /dev/null 2>&1; then echo -e "${GREEN}apt${NC}" - update_with_apt "$machine" + update_with_apt "$machine" "$uniqkey" elif timeout 5 ssh "$machine" which dnf > /dev/null 2>&1; then echo -e "${GREEN}dnf${NC}" update_with_dnf "$machine" @@ -241,17 +334,21 @@ for machine in "${machines[@]}"; do ((current++)) echo -ne ">> $machine ($current/$total)\n" - if grep -q "^$machine " ~/.config/updateall-hosts; then - status=$(grep "^$machine " ~/.config/updateall-hosts | awk '{print $2}') + while read host status; do + # ignorer commentaires et lignes vides + [[ -z "$host" || "$host" =~ ^# ]] && continue + + if [ "$machine" = "$host" ]; then if [ "$status" = "1" ]; then echo -e "${GREEN}connue${NC}" check_host "$machine" machine_online="$?" - + if [ "$machine_online" -eq 1 ]; then check_keyinstall "$machine" "$uniqkey" keyinstall_present="$?" + if [ "$keyinstall_present" -eq 0 ]; then update_machine "$machine" create_installkey "$machine" "$uniqkey" @@ -264,22 +361,27 @@ for machine in "${machines[@]}"; do echo -e "${RED}ignorée${NC}" ignored_machines+=("$machine") fi - else - check_host "$machine" - machine_online="$?" - - if [ "$machine_online" -eq 1 ]; then - echo -e "${RED}vue pour la 1re fois${NC}" - confirm_update "$machine" - update_machine "$machine" - run_custom_script "$machine" - create_installkey "$machine" "$uniqkey" - ok_machines+=("$machine") - else - echo -e "${RED}non accessible${NC}" - error_machines+=("$machine") - fi + matched=1 + break fi +done < ~/.config/updateall-hosts + +if [ "$matched" != "1" ]; then + check_host "$machine" + machine_online="$?" + + if [ "$machine_online" -eq 1 ]; then + echo -e "${RED}vue pour la 1re fois${NC}" + confirm_update "$machine" + update_machine "$machine" + run_custom_script "$machine" + create_installkey "$machine" "$uniqkey" + ok_machines+=("$machine") + else + echo -e "${RED}non accessible${NC}" + error_machines+=("$machine") + fi +fi done