# This file is part of photo21 # Copyright (C) 2022 Amicale des élèves de l'ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.core.management.base import BaseCommand from photologue.models import DATE_FROM_EXIF, DATE_FROM_VIDEO, DATE_MANUAL, Photo, Video from photologue.utils import capture_date_from_exif, capture_date_from_video class Command(BaseCommand): help = ( "Read the capture date of existing media from their metadata. Media whose " "date was set by hand are never touched. By default only media without a " "date, and media imported before dates were tracked, are read." ) def add_arguments(self, parser): parser.add_argument( "--force", action="store_true", help="Re-read every media, even those with a known date source.", ) parser.add_argument( "--dry-run", action="store_true", help="Report what would change without writing anything.", ) def handle(self, *args, **options): for model, reader in ((Photo, self.photo_date), (Video, self.video_date)): queryset = model.objects.exclude(date_source=DATE_MANUAL) if not options["force"]: # An empty source means the media predates this field, and its date # may have been stored in the wrong time zone. queryset = queryset.filter(date_taken__isnull=True) | queryset.filter(date_source="") self.process(model, queryset.distinct(), reader, options) def process(self, model, queryset, reader, options): name = model._meta.verbose_name_plural found = unchanged = missing = 0 for media in queryset.iterator(): taken, source = reader(media) if taken is None: missing += 1 self.stdout.write(f" no date in {media.title}") continue if taken == media.date_taken and source == media.date_source: unchanged += 1 continue found += 1 self.stdout.write(f" {media.title}: {media.date_taken} -> {taken} ({source})") if not options["dry_run"]: media.set_capture_date(taken, source) media.save(update_fields=["date_taken", "date_source"]) done = "would be updated" if options["dry_run"] else "updated" self.stdout.write( self.style.SUCCESS(f"{name}: {found} {done}, {unchanged} already correct, {missing} without metadata") ) @staticmethod def photo_date(photo): if not photo.image: return None, None try: exif_date = photo.exif(photo.image.file).get("EXIF DateTimeOriginal", None) except Exception: return None, None if exif_date is None: return None, None return capture_date_from_exif(exif_date.values), DATE_FROM_EXIF @staticmethod def video_date(video): if not video.file: return None, None return capture_date_from_video(video.file.path), DATE_FROM_VIDEO