feat(versions): link at ingest, hide in listings, version endpoints
XMP (PNG iTXt DerivedFrom, native chunk reader) then file-name convention links at ingest; unlinked name candidates are retried each tick. Versions are hidden from /photos (SQL for the recursive listing, hidden-row set for the walk and tag branches), /memories, reels, /photos/exif, the GPS summary, unified search and the media gauges while their original is reachable in the view; include_versions=true lists them. /photos gains the parallel arrays photo_display_paths / photo_version_counts, memories / search hits / /photos/exif rows gain display_path / version_count, all skipped when empty. GET /image/versions and PUT /image/versions/display (library optional). Tags and favorites on a version path bind to the original; faces, CLIP and duplicate grouping skip version hashes; insight generation redirects to the original. Orphan GC and the back-ref refresh cover the new tables. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
044240037a
commit
080a75c336
24 files changed
+1614
-47
No files matched your search
@@ -108,6 +108,19 @@ pub struct PhotosResponse {
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub photo_libraries: Vec<i32>,
|
||||
|
||||
/// Photo versions (#191), parallel to `photos`. `photos[i]` is always
|
||||
/// the *original's* path (it anchors tags, sort order and dates).
|
||||
/// `photo_display_paths[i]` is the file to show instead when the global
|
||||
/// display choice isn't the original, else `null`; skipped when no photo
|
||||
/// on the page has one.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub photo_display_paths: Vec<Option<String>>,
|
||||
|
||||
/// How many versions stand behind `photos[i]`; skipped when the page has
|
||||
/// none, so responses without versions are unchanged.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub photo_version_counts: Vec<u32>,
|
||||
|
||||
// Pagination metadata (only present when limit is set)
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub total_count: Option<i64>,
|
||||
@@ -180,6 +193,13 @@ pub struct FilesRequest {
|
||||
/// inside a group.
|
||||
#[serde(default)]
|
||||
pub include_duplicates: Option<bool>,
|
||||
|
||||
/// When true, list photo versions (#191) as photos of their own instead
|
||||
/// of hiding them behind their original. Default false. Counterpart of
|
||||
/// `include_duplicates`; a version whose original is not reachable in
|
||||
/// the request's view is listed either way.
|
||||
#[serde(default)]
|
||||
pub include_versions: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Deserialize, PartialEq, Debug)]
|
||||
@@ -495,6 +515,11 @@ pub struct ExifSummary {
|
||||
/// Capture wall clock to display (naive ISO-8601; see
|
||||
/// `MetadataResponse::wall_clock`).
|
||||
pub wall_clock: Option<String>,
|
||||
/// `display_path` / `version_count` (#191); absent for a photo without
|
||||
/// versions. `file_path` is always the original's (versions are hidden
|
||||
/// from this listing while their original is reachable).
|
||||
#[serde(flatten)]
|
||||
pub versions: crate::versions::VersionFields,
|
||||
}
|
||||
|
||||
impl ExifSummary {
|
||||
@@ -525,6 +550,7 @@ impl ExifSummary {
|
||||
date_utc: r.date_utc,
|
||||
camera_offset_minutes: r.camera_offset_minutes,
|
||||
date_taken_tz_source: r.date_taken_tz_source,
|
||||
versions: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user