Sort gallery media by capture date instead of file name
All checks were successful
Docker / build (release) Successful in 9s
All checks were successful
Docker / build (release) Successful in 9s
This commit is contained in:
parent
2f0c426dc1
commit
f7415fb5f6
11 changed files with 314 additions and 33 deletions
77
photologue/management/commands/backfill_capture_dates.py
Normal file
77
photologue/management/commands/backfill_capture_dates.py
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue