Sort gallery media by capture date instead of file name
All checks were successful
Docker / build (release) Successful in 9s

This commit is contained in:
krek0 2026-09-24 12:52:50 +02:00
parent 2f0c426dc1
commit f7415fb5f6
11 changed files with 314 additions and 33 deletions

View file

@ -0,0 +1,77 @@
# 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