Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
310 changes: 218 additions & 92 deletions protogen/gen/opencloud/messages/search/v0/search.pb.go

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions protogen/gen/opencloud/messages/search/v0/search.pb.web.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions protogen/gen/opencloud/services/search/v0/search.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@
},
"motionPhoto": {
"$ref": "#/definitions/v0MotionPhoto"
},
"livePhoto": {
"$ref": "#/definitions/v0LivePhoto"
}
}
},
Expand Down Expand Up @@ -341,6 +344,29 @@
"v0IndexSpaceResponse": {
"type": "object"
},
"v0LivePhoto": {
"type": "object",
"properties": {
"contentId": {
"type": "string"
},
"stillImageTimeUs": {
"type": "string",
"format": "int64"
},
"auto": {
"type": "boolean"
},
"vitalityScore": {
"type": "number",
"format": "double"
},
"vitalityScoringVersion": {
"type": "string",
"format": "int64"
}
}
},
"v0Match": {
"type": "object",
"properties": {
Expand Down
9 changes: 9 additions & 0 deletions protogen/proto/opencloud/messages/search/v0/search.proto
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ message MotionPhoto {
optional int64 videoSize = 3;
}

message LivePhoto {
optional string contentId = 1;
optional int64 stillImageTimeUs = 2;
optional bool auto = 3;
optional double vitalityScore = 4;
optional int64 vitalityScoringVersion = 5;
}

message Entity {
Reference ref = 1;
ResourceID id = 2;
Expand All @@ -87,6 +95,7 @@ message Entity {
Photo photo = 19;
repeated string favorites = 20;
MotionPhoto motionPhoto = 21;
LivePhoto livePhoto = 23;
}

message Match {
Expand Down
1 change: 1 addition & 0 deletions services/graph/pkg/service/v0/driveitems.go
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ func cs3ResourceToDriveItem(logger *log.Logger, publicBaseURL *url.URL, res *sto
driveItem.Location = metadataToFacet[libregraph.GeoCoordinates](metadata, "location")
driveItem.Photo = metadataToFacet[libregraph.Photo](metadata, "photo")
driveItem.LibreGraphMotionPhoto = metadataToFacet[libregraph.MotionPhoto](metadata, "motionPhoto")
driveItem.LibreGraphLivePhoto = metadataToFacet[libregraph.LivePhoto](metadata, "livePhoto")
}

return driveItem, nil
Expand Down
1 change: 1 addition & 0 deletions services/search/pkg/bleve/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ func (b *Backend) Search(_ context.Context, sir *searchService.SearchIndexReques
Location: hitToFacet[searchMessage.GeoCoordinates](hit.Fields, "location"),
Photo: hitToFacet[searchMessage.Photo](hit.Fields, "photo"),
MotionPhoto: hitToFacet[searchMessage.MotionPhoto](hit.Fields, "motionPhoto"),
LivePhoto: hitToFacet[searchMessage.LivePhoto](hit.Fields, "livePhoto"),
},
}

Expand Down
1 change: 1 addition & 0 deletions services/search/pkg/content/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Document struct {
Location *libregraph.GeoCoordinates `json:"location,omitempty"`
Photo *libregraph.Photo `json:"photo,omitempty"`
MotionPhoto *libregraph.MotionPhoto `json:"motionPhoto,omitempty"`
LivePhoto *libregraph.LivePhoto `json:"livePhoto,omitempty"`
}

func CleanString(content, langCode string) string {
Expand Down
1 change: 1 addition & 0 deletions services/search/pkg/content/tika.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ func (t Tika) Extract(ctx context.Context, ri *provider.ResourceInfo) (Document,
doc.Photo = t.getPhoto(meta)
doc.Audio = t.getAudio(meta)
doc.MotionPhoto = t.getMotionPhoto(meta)
doc.LivePhoto = t.getLivePhoto(meta)
}

// verify against the file itself: a shared motion photo can keep the XMP but
Expand Down
49 changes: 49 additions & 0 deletions services/search/pkg/content/tika_live_photo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package content

import (
"math"
"strconv"

libregraph "github.com/opencloud-eu/libre-graph-api-go"
)

func (t Tika) getLivePhoto(meta map[string][]string) *libregraph.LivePhoto {
// ContentId pairs the two halves and is what makes it a live photo, so without
// it there is no facet. The video exposes it via the QuickTime item list; the
// still image carries it in the Apple maker note (tag 0x0011), which tika
// surfaces generically under "Content Identifier" (the HEIC maker note is
// parsed since metadata-extractor 2.21.0). A file is only ever one half, so
// reading both keys covers both.
contentID, err := getFirstValue(meta, "com.apple.quicktime.content.identifier", "Content Identifier")
if err != nil {
return nil
}
livePhoto := libregraph.NewLivePhoto(contentID)

// tika emits still-image-time already in microseconds
if v, err := getFirstValue(meta, "quicktime:still-image-time"); err == nil {
if f, err := strconv.ParseFloat(v, 64); err == nil {
livePhoto.SetStillImageTimeUs(int64(math.Round(f)))
}
}

if v, err := getFirstValue(meta, "com.apple.quicktime.live-photo.auto"); err == nil {
if b, err := strconv.ParseBool(v); err == nil {
livePhoto.SetAuto(b)
}
}

if v, err := getFirstValue(meta, "com.apple.quicktime.live-photo.vitality-score"); err == nil {
if f, err := strconv.ParseFloat(v, 64); err == nil {
livePhoto.SetVitalityScore(f)
}
}

if v, err := getFirstValue(meta, "com.apple.quicktime.live-photo.vitality-scoring-version"); err == nil {
if i, err := strconv.ParseInt(v, 0, 64); err == nil {
livePhoto.SetVitalityScoringVersion(i)
}
}

return livePhoto
}
45 changes: 45 additions & 0 deletions services/search/pkg/content/tika_live_photo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package content

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
libregraph "github.com/opencloud-eu/libre-graph-api-go"
)

var _ = Describe("getLivePhoto", func() {
It("maps the video half of a live photo", func() {
livePhoto := Tika{}.getLivePhoto(map[string][]string{
"Content-Type": {"video/quicktime"},
"com.apple.quicktime.content.identifier": {"6F1A2B3C-1234-4E5F-9A8B-0011223344CC"},
"quicktime:still-image-time": {"1500000"},
"com.apple.quicktime.live-photo.auto": {"1"},
"com.apple.quicktime.live-photo.vitality-score": {"0.75"},
"com.apple.quicktime.live-photo.vitality-scoring-version": {"4"},
})
Expect(livePhoto).ToNot(BeNil())

Expect(livePhoto.ContentId).To(Equal("6F1A2B3C-1234-4E5F-9A8B-0011223344CC"))
Expect(livePhoto.StillImageTimeUs).To(Equal(libregraph.PtrInt64(1500000)))
Expect(livePhoto.Auto).To(Equal(libregraph.PtrBool(true)))
Expect(livePhoto.VitalityScore).To(Equal(libregraph.PtrFloat64(0.75)))
Expect(livePhoto.VitalityScoringVersion).To(Equal(libregraph.PtrInt64(4)))
})

It("maps the still half via the Apple maker-note content identifier", func() {
// the still carries the pairing id in the Apple maker note, which tika
// surfaces under "Content Identifier" (HEIC maker note parsed since
// metadata-extractor 2.21.0, drewnoakes/metadata-extractor#739).
livePhoto := Tika{}.getLivePhoto(map[string][]string{
"Content-Type": {"image/heic"},
"Content Identifier": {"6F1A2B3C-1234-4E5F-9A8B-0011223344CC"},
})
Expect(livePhoto).ToNot(BeNil())
Expect(livePhoto.ContentId).To(Equal("6F1A2B3C-1234-4E5F-9A8B-0011223344CC"))
})

It("returns nil without a content identifier", func() {
Expect(Tika{}.getLivePhoto(map[string][]string{
"Content-Type": {"image/jpeg"},
})).To(BeNil())
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func OpenSearchHitToMatch(hit opensearchgoAPI.SearchHit) (*searchMessage.Match,
Location: copyFacet[searchMessage.GeoCoordinates](resource.Location),
Photo: copyFacet[searchMessage.Photo](resource.Photo),
MotionPhoto: copyFacet[searchMessage.MotionPhoto](resource.MotionPhoto),
LivePhoto: copyFacet[searchMessage.LivePhoto](resource.LivePhoto),
},
}

Expand Down
1 change: 1 addition & 0 deletions services/search/pkg/search/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ func (s *Service) doUpsertItem(ref *provider.Reference, batch BatchOperator) {
facetToMetadata(metadata, doc.Location, "libre.graph.location.")
facetToMetadata(metadata, doc.Photo, "libre.graph.photo.")
facetToMetadata(metadata, doc.MotionPhoto, "libre.graph.motionPhoto.")
facetToMetadata(metadata, doc.LivePhoto, "libre.graph.livePhoto.")
if len(metadata) == 0 {
return
}
Expand Down