feat(exif): extract capture date and surface as sortable taken_at field
Adds EXIF date extraction during indexing so photos can be sorted by when they were actually taken, not just when the file was modified on disk. - Add `taken_at` (nullable TEXT, ISO 8601) column to the images table via ensure_column migration and a new idx_images_taken_at index - Populate taken_at in build_record via extract_exif_date, which tries DateTimeOriginal → DateTimeDigitized → DateTime tags and rejects all-zero sentinel dates written by uninitialised cameras - upsert_image uses taken_at = excluded.taken_at so a re-indexed file gets fresh EXIF; stale dates from replaced files are not preserved - Add taken_asc / taken_desc sort using COALESCE(taken_at, created_at) so images without EXIF gracefully fall back to their creation date - Surface as "Taken: newest / oldest" in the Toolbar sort dropdown - Shift map_image_row column indices after the new taken_at (index 10)
This commit is contained in:
@@ -5,6 +5,8 @@ import { tileSizeForZoom, useGalleryStore, SortOrder, MediaFilter, SearchCommand
|
||||
const BASE_SORT_OPTIONS: { value: SortOrder; label: string }[] = [
|
||||
{ value: "date_desc", label: "Newest first" },
|
||||
{ value: "date_asc", label: "Oldest first" },
|
||||
{ value: "taken_desc", label: "Taken: newest" },
|
||||
{ value: "taken_asc", label: "Taken: oldest" },
|
||||
{ value: "name_asc", label: "Name A–Z" },
|
||||
{ value: "name_desc", label: "Name Z–A" },
|
||||
{ value: "rating_desc", label: "Highest rated" },
|
||||
|
||||
+8
-1
@@ -37,6 +37,7 @@ export interface ImageRecord {
|
||||
file_size: number;
|
||||
created_at: string | null;
|
||||
modified_at: string | null;
|
||||
taken_at: string | null;
|
||||
mime_type: string;
|
||||
media_kind: MediaKind;
|
||||
duration_ms: number | null;
|
||||
@@ -214,7 +215,9 @@ export type SortOrder =
|
||||
| "rating_desc"
|
||||
| "rating_asc"
|
||||
| "duration_desc"
|
||||
| "duration_asc";
|
||||
| "duration_asc"
|
||||
| "taken_desc"
|
||||
| "taken_asc";
|
||||
|
||||
interface GalleryState {
|
||||
folders: Folder[];
|
||||
@@ -494,6 +497,10 @@ function compareImages(a: ImageRecord, b: ImageRecord, sort: SortOrder): number
|
||||
return compareNullableNumber(a.duration_ms, b.duration_ms);
|
||||
case "duration_desc":
|
||||
return compareNullableNumber(b.duration_ms, a.duration_ms);
|
||||
case "taken_asc":
|
||||
return compareNullableDate(a.taken_at ?? a.created_at, b.taken_at ?? b.created_at);
|
||||
case "taken_desc":
|
||||
return compareNullableDate(b.taken_at ?? b.created_at, a.taken_at ?? a.created_at);
|
||||
default:
|
||||
return compareNullableDate(b.modified_at, a.modified_at);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user