Ajout option --set-file-date pour modifier le mtime des fichiers

Permet de modifier la date de modification des fichiers images selon
la date de prise de vue du frame, utile pour le tri par date dans
les gestionnaires de fichiers.

Utilise os.utime() pour modifier atime et mtime.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-10 16:30:06 +01:00
parent 954d644e79
commit 456717f639
2 changed files with 58 additions and 6 deletions

View File

@@ -39,9 +39,10 @@ source venv/bin/activate
python -m json_to_metadata <fichier.json> -d <dossier_images> python -m json_to_metadata <fichier.json> -d <dossier_images>
# Options CLI # Options CLI
# -v, --verbose Mode verbeux (DEBUG) # -v, --verbose Mode verbeux (DEBUG)
# --dry-run Affiche sans exécuter # --dry-run Affiche sans exécuter
# --force-xmp Force création XMP même pour TIFF/JPEG # --force-xmp Force création XMP même pour TIFF/JPEG
# --set-file-date Modifie le mtime des fichiers selon la date de prise de vue
# Tests # Tests
pytest tests/ -v pytest tests/ -v

View File

@@ -7,7 +7,9 @@ des métadonnées EXIF.
import argparse import argparse
import logging import logging
import os
import sys import sys
from datetime import datetime
from pathlib import Path from pathlib import Path
from . import __version__ from . import __version__
@@ -18,7 +20,7 @@ from .exif_writer import (
write_exif_with_fallback, write_exif_with_fallback,
) )
from .json_parser import ValidationError, load_json, validate_roll from .json_parser import ValidationError, load_json, validate_roll
from .metadata_mapper import map_frame_to_exif from .metadata_mapper import map_frame_to_exif, parse_date
from .xmp_writer import write_xmp_sidecar from .xmp_writer import write_xmp_sidecar
# Configuration du logging # Configuration du logging
@@ -45,6 +47,35 @@ def setup_logging(verbose: bool = False) -> None:
) )
def set_file_mtime(filepath: Path, dt: datetime, dry_run: bool = False) -> bool:
"""
Modifie la date de modification (mtime) d'un fichier.
Args:
filepath: Chemin vers le fichier.
dt: Date/heure à appliquer.
dry_run: Si True, affiche sans modifier.
Returns:
True si la modification a réussi.
"""
try:
timestamp = dt.timestamp()
if dry_run:
logger.info(f"[DRY-RUN] Modification date fichier : {filepath.name}{dt}")
return True
# Modifie atime et mtime
os.utime(filepath, (timestamp, timestamp))
logger.debug(f"Date fichier modifiée : {filepath.name}{dt}")
return True
except (OSError, ValueError) as e:
logger.warning(f"Impossible de modifier la date du fichier {filepath.name} : {e}")
return False
def find_image_for_frame( def find_image_for_frame(
frame: dict, frame: dict,
directory: Path, directory: Path,
@@ -95,7 +126,8 @@ def process_roll(
roll: dict, roll: dict,
image_dir: Path, image_dir: Path,
dry_run: bool = False, dry_run: bool = False,
force_xmp: bool = False force_xmp: bool = False,
set_file_date: bool = False
) -> tuple[int, int, int]: ) -> tuple[int, int, int]:
""" """
Traite un roll complet : valide et écrit les métadonnées pour chaque frame. Traite un roll complet : valide et écrit les métadonnées pour chaque frame.
@@ -105,6 +137,7 @@ def process_roll(
image_dir: Répertoire contenant les images. image_dir: Répertoire contenant les images.
dry_run: Mode simulation. dry_run: Mode simulation.
force_xmp: Force la création de fichiers XMP. force_xmp: Force la création de fichiers XMP.
set_file_date: Modifie la date de modification du fichier.
Returns: Returns:
Tuple (succès, échecs, images non trouvées). Tuple (succès, échecs, images non trouvées).
@@ -144,10 +177,12 @@ def process_roll(
continue continue
# Écrire les métadonnées # Écrire les métadonnées
write_success = False
try: try:
if force_xmp: if force_xmp:
xmp_path = write_xmp_sidecar(image_path, tags, dry_run) xmp_path = write_xmp_sidecar(image_path, tags, dry_run)
if xmp_path: if xmp_path:
write_success = True
success_count += 1 success_count += 1
logger.info(f"Frame {frame_id} : XMP créé ({image_path.name})") logger.info(f"Frame {frame_id} : XMP créé ({image_path.name})")
else: else:
@@ -158,6 +193,7 @@ def process_roll(
dry_run dry_run
) )
if success: if success:
write_success = True
success_count += 1 success_count += 1
logger.info(f"Frame {frame_id} : métadonnées écrites via {method}") logger.info(f"Frame {frame_id} : métadonnées écrites via {method}")
else: else:
@@ -171,6 +207,14 @@ def process_roll(
logger.error(f"Erreur I/O pour le frame {frame_id} : {e}") logger.error(f"Erreur I/O pour le frame {frame_id} : {e}")
failure_count += 1 failure_count += 1
# Modifier la date du fichier si demandé
if write_success and set_file_date and frame.get('date'):
try:
frame_date = parse_date(frame['date'])
set_file_mtime(image_path, frame_date, dry_run)
except ValueError as e:
logger.warning(f"Date invalide pour le frame {frame_id} : {e}")
return success_count, failure_count, not_found_count return success_count, failure_count, not_found_count
@@ -221,6 +265,12 @@ def main(args: list[str] | None = None) -> int:
help='Force la création de fichiers XMP même pour les formats supportant EXIF' help='Force la création de fichiers XMP même pour les formats supportant EXIF'
) )
parser.add_argument(
'--set-file-date',
action='store_true',
help='Modifie la date de modification des fichiers selon la date de prise de vue'
)
parser.add_argument( parser.add_argument(
'--version', '--version',
action='version', action='version',
@@ -302,7 +352,8 @@ def main(args: list[str] | None = None) -> int:
roll, roll,
image_dir, image_dir,
dry_run=parsed_args.dry_run, dry_run=parsed_args.dry_run,
force_xmp=parsed_args.force_xmp force_xmp=parsed_args.force_xmp,
set_file_date=parsed_args.set_file_date
) )
total_success += success total_success += success
total_failure += failure total_failure += failure