Compare commits

...
Author SHA1 Message Date
Dominik Schmidt 6a6530252c feat(graph): base the thumbnails on the preview check
The driveItem listings pick their thumbnails by mime type, which claims a
preview for anything the thumbnailer could render in principle and misses
the embedded ones. Use thumbnail.HasPreview instead, which also answers for
audio with cover art, and add the source thumbnail with the dimensions that
are actually known.

The share listings keep matching on the mime type, they carry driveItems
without a resource info.
2026-09-07 12:50:57 +02:00
Dominik Schmidt ab5c9615b9 test(search): pin container-only facets and the cover preview in extract 2026-09-07 12:50:57 +02:00
Dominik Schmidt c1a9cc59a0 refactor(search): set the preview apart from the 1:1 facets 2026-09-07 12:50:57 +02:00
Dominik Schmidt 60f93c0591 docs: drop fork mention in comment 2026-09-07 12:50:57 +02:00
Dominik Schmidt 7fc112b07f refactor: split PreviewDimensions onto multiple lines 2026-09-07 12:50:57 +02:00
Dominik Schmidt daae6d28f7 refactor: StringToInt32 takes an explicit fallback 2026-09-07 12:50:57 +02:00
Dominik Schmidt 6a93188167 refactor: drop named returns in PreviewDimensions 2026-09-07 12:50:57 +02:00
Dominik Schmidt efb89c1bf8 refactor: StringToInt32 returns the parse error 2026-09-07 12:50:57 +02:00
Dominik Schmidt 3b92809285 test: use ginkgo/gomega for the preview tests 2026-09-07 12:50:57 +02:00
Dominik Schmidt 9ab5c8282d refactor: move getPreview to tika_preview.go, rename has_preview.go 2026-09-07 12:50:57 +02:00
Dominik Schmidt 93ab534580 docs: tighten comments 2026-09-07 12:50:57 +02:00
Dominik Schmidt c3c6aaea9b docs: shorten comments 2026-09-07 12:50:57 +02:00
Dominik Schmidt b68b272e4b feat(search): honest has-preview for audio in search results
Add a preview facet to the search message entity carrying the embedded preview
dimensions, mapped from the index in both engines. The webdav search report now
derives oc:has-preview via thumbnail.HasPreviewForMimeType: unconditional types
stay true, embedded-preview types (audio) are true only when a cover was
indexed, instead of the blanket mimetype match that reported a preview for every
audio file.
2026-09-07 12:50:57 +02:00
Dominik Schmidt 8c7e4b4257 feat(graph): thumbnails relationship on driveItem listings
Add a shared helper that builds the thumbnails relationship from a resource's
info, gated on thumbnail.HasPreview so audio without embedded cover art no
longer advertises a preview that fails to render. Reported dimensions are
aspect-correct (fit into the box) rather than square, with a source thumbnail
carrying the native dimensions (audio cover from oc.preview, images from the
image facet). Wired into the drive children listings on $expand=thumbnails.
2026-09-07 12:50:57 +02:00
Dominik Schmidt 9c48782d79 feat(search): prefer the front cover for the preview dimensions
getPreview now prefers the embedded image tagged as the front cover
(dc:description "Cover (front)") and falls back to the first embedded image,
matching the thumbnailer's cover selection so the reported oc.preview
dimensions belong to the picture that actually gets rendered.
2026-09-07 12:50:57 +02:00
Dominik Schmidt d64fd2c01d fix(search): derive facets from container metadata only
With embedded resources now surfaced by Tika (cover art via TIKA-4801), the
per-meta facet loop let an embedded image overwrite the container's facets:
audio files ended up with a bogus image facet (the cover's dimensions) and a
wiped audio facet. Take audio/image/photo/location facets from the container
entry only; embedded cover dimensions are captured as the preview instead.
2026-09-07 12:50:57 +02:00
Dominik Schmidt 9717fad2c3 feat(search): index embedded preview dimensions as oc.preview
For embedded-preview mime types (audio cover art), extract the embedded
cover's dimensions from Tika's recursive metadata (requires TIKA-4801) and
store them as oc.preview.width/height in the search index and arbitrary
metadata, routed through the shared facet-to-metadata flattening. Presence of
the dimensions is the preview-presence signal consumed by thumbnail.HasPreview.
2026-09-07 12:50:57 +02:00
Dominik Schmidt 935ce7c073 feat(thumbnails): split preview mime types + add HasPreview
Split SupportedMimeTypes into UnconditionalPreviewMimeTypes (preview follows
from the mimetype) and EmbeddedPreviewMimeTypes (audio cover art, may be
absent); SupportedMimeTypes stays as their union for the generator. Add
HasPreview(md) as the single source of truth: unconditional types are always
true, embedded types depend on stored oc.preview dimensions.
2026-09-07 12:50:57 +02:00
22 changed files with 752 additions and 334 deletions

No files matched your search

+10
View File
@@ -1,6 +1,7 @@
package conversions
import (
"strconv"
"strings"
)
@@ -14,3 +15,12 @@ func StringToSliceString(src string, sep string) []string {
return parts
}
// StringToInt32 parses s as a base-10 int32, returning fallback on any error.
func StringToInt32(s string, fallback int32) int32 {
v, err := strconv.ParseInt(s, 10, 32)
if err != nil {
return fallback
}
return int32(v)
}
@@ -361,6 +361,63 @@ func (x *Image) GetHeight() int32 {
return 0
}
// Preview carries the dimensions of an embedded preview (e.g. audio cover art).
// Its presence signals that a preview is available for the resource.
type Preview struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Width *int32 `protobuf:"varint,1,opt,name=width,proto3,oneof" json:"width,omitempty"`
Height *int32 `protobuf:"varint,2,opt,name=height,proto3,oneof" json:"height,omitempty"`
}
func (x *Preview) Reset() {
*x = Preview{}
if protoimpl.UnsafeEnabled {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Preview) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Preview) ProtoMessage() {}
func (x *Preview) ProtoReflect() protoreflect.Message {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Preview.ProtoReflect.Descriptor instead.
func (*Preview) Descriptor() ([]byte, []int) {
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{4}
}
func (x *Preview) GetWidth() int32 {
if x != nil && x.Width != nil {
return *x.Width
}
return 0
}
func (x *Preview) GetHeight() int32 {
if x != nil && x.Height != nil {
return *x.Height
}
return 0
}
type GeoCoordinates struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -374,7 +431,7 @@ type GeoCoordinates struct {
func (x *GeoCoordinates) Reset() {
*x = GeoCoordinates{}
if protoimpl.UnsafeEnabled {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[4]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -387,7 +444,7 @@ func (x *GeoCoordinates) String() string {
func (*GeoCoordinates) ProtoMessage() {}
func (x *GeoCoordinates) ProtoReflect() protoreflect.Message {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[4]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -400,7 +457,7 @@ func (x *GeoCoordinates) ProtoReflect() protoreflect.Message {
// Deprecated: Use GeoCoordinates.ProtoReflect.Descriptor instead.
func (*GeoCoordinates) Descriptor() ([]byte, []int) {
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{4}
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{5}
}
func (x *GeoCoordinates) GetAltitude() float64 {
@@ -443,7 +500,7 @@ type Photo struct {
func (x *Photo) Reset() {
*x = Photo{}
if protoimpl.UnsafeEnabled {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[5]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -456,7 +513,7 @@ func (x *Photo) String() string {
func (*Photo) ProtoMessage() {}
func (x *Photo) ProtoReflect() protoreflect.Message {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[5]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -469,7 +526,7 @@ func (x *Photo) ProtoReflect() protoreflect.Message {
// Deprecated: Use Photo.ProtoReflect.Descriptor instead.
func (*Photo) Descriptor() ([]byte, []int) {
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{5}
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{6}
}
func (x *Photo) GetCameraMake() string {
@@ -555,7 +612,7 @@ type Video struct {
func (x *Video) Reset() {
*x = Video{}
if protoimpl.UnsafeEnabled {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[6]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -568,7 +625,7 @@ func (x *Video) String() string {
func (*Video) ProtoMessage() {}
func (x *Video) ProtoReflect() protoreflect.Message {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[6]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -581,7 +638,7 @@ func (x *Video) ProtoReflect() protoreflect.Message {
// Deprecated: Use Video.ProtoReflect.Descriptor instead.
func (*Video) Descriptor() ([]byte, []int) {
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{6}
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{7}
}
func (x *Video) GetAudioBitsPerSample() int32 {
@@ -667,7 +724,7 @@ type MotionPhoto struct {
func (x *MotionPhoto) Reset() {
*x = MotionPhoto{}
if protoimpl.UnsafeEnabled {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[7]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -680,7 +737,7 @@ func (x *MotionPhoto) String() string {
func (*MotionPhoto) ProtoMessage() {}
func (x *MotionPhoto) ProtoReflect() protoreflect.Message {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[7]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -693,7 +750,7 @@ func (x *MotionPhoto) ProtoReflect() protoreflect.Message {
// Deprecated: Use MotionPhoto.ProtoReflect.Descriptor instead.
func (*MotionPhoto) Descriptor() ([]byte, []int) {
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{7}
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{8}
}
func (x *MotionPhoto) GetVersion() int32 {
@@ -732,7 +789,7 @@ type LivePhoto struct {
func (x *LivePhoto) Reset() {
*x = LivePhoto{}
if protoimpl.UnsafeEnabled {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[8]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -745,7 +802,7 @@ func (x *LivePhoto) String() string {
func (*LivePhoto) ProtoMessage() {}
func (x *LivePhoto) ProtoReflect() protoreflect.Message {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[8]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -758,7 +815,7 @@ func (x *LivePhoto) ProtoReflect() protoreflect.Message {
// Deprecated: Use LivePhoto.ProtoReflect.Descriptor instead.
func (*LivePhoto) Descriptor() ([]byte, []int) {
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{8}
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{9}
}
func (x *LivePhoto) GetContentId() string {
@@ -824,12 +881,13 @@ type Entity struct {
MotionPhoto *MotionPhoto `protobuf:"bytes,21,opt,name=motionPhoto,proto3" json:"motionPhoto,omitempty"`
Video *Video `protobuf:"bytes,22,opt,name=video,proto3" json:"video,omitempty"`
LivePhoto *LivePhoto `protobuf:"bytes,23,opt,name=livePhoto,proto3" json:"livePhoto,omitempty"`
Preview *Preview `protobuf:"bytes,24,opt,name=preview,proto3" json:"preview,omitempty"`
}
func (x *Entity) Reset() {
*x = Entity{}
if protoimpl.UnsafeEnabled {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[9]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -842,7 +900,7 @@ func (x *Entity) String() string {
func (*Entity) ProtoMessage() {}
func (x *Entity) ProtoReflect() protoreflect.Message {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[9]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[10]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -855,7 +913,7 @@ func (x *Entity) ProtoReflect() protoreflect.Message {
// Deprecated: Use Entity.ProtoReflect.Descriptor instead.
func (*Entity) Descriptor() ([]byte, []int) {
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{9}
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{10}
}
func (x *Entity) GetRef() *Reference {
@@ -1019,6 +1077,13 @@ func (x *Entity) GetLivePhoto() *LivePhoto {
return nil
}
func (x *Entity) GetPreview() *Preview {
if x != nil {
return x.Preview
}
return nil
}
type Match struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -1033,7 +1098,7 @@ type Match struct {
func (x *Match) Reset() {
*x = Match{}
if protoimpl.UnsafeEnabled {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[10]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -1046,7 +1111,7 @@ func (x *Match) String() string {
func (*Match) ProtoMessage() {}
func (x *Match) ProtoReflect() protoreflect.Message {
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[10]
mi := &file_opencloud_messages_search_v0_search_proto_msgTypes[11]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -1059,7 +1124,7 @@ func (x *Match) ProtoReflect() protoreflect.Message {
// Deprecated: Use Match.ProtoReflect.Descriptor instead.
func (*Match) Descriptor() ([]byte, []int) {
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{10}
return file_opencloud_messages_search_v0_search_proto_rawDescGZIP(), []int{11}
}
func (x *Match) GetEntity() *Entity {
@@ -1149,197 +1214,206 @@ var file_opencloud_messages_search_v0_search_proto_rawDesc = []byte{
0x01, 0x12, 0x1b, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
0x05, 0x48, 0x01, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x42, 0x08,
0x0a, 0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69,
0x67, 0x68, 0x74, 0x22, 0x9d, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64,
0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x08, 0x61, 0x6c, 0x74, 0x69, 0x74, 0x75,
0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x08, 0x61, 0x6c, 0x74, 0x69,
0x74, 0x75, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74,
0x75, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x48, 0x01, 0x52, 0x08, 0x6c, 0x61, 0x74,
0x69, 0x74, 0x75, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67,
0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x48, 0x02, 0x52, 0x09, 0x6c,
0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f,
0x61, 0x6c, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6c, 0x61, 0x74,
0x69, 0x74, 0x75, 0x64, 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74,
0x75, 0x64, 0x65, 0x22, 0x9b, 0x04, 0x0a, 0x05, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x0a,
0x0a, 0x63, 0x61, 0x6d, 0x65, 0x72, 0x61, 0x4d, 0x61, 0x6b, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x61, 0x6d, 0x65, 0x72, 0x61, 0x4d, 0x61, 0x6b, 0x65, 0x88,
0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x63, 0x61, 0x6d, 0x65, 0x72, 0x61, 0x4d, 0x6f, 0x64, 0x65,
0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x63, 0x61, 0x6d, 0x65, 0x72,
0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x13, 0x65, 0x78, 0x70,
0x6f, 0x73, 0x75, 0x72, 0x65, 0x44, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72,
0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x48, 0x02, 0x52, 0x13, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x75,
0x72, 0x65, 0x44, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01,
0x12, 0x31, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x4e, 0x75, 0x6d, 0x65,
0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x02, 0x48, 0x03, 0x52, 0x11, 0x65,
0x78, 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72,
0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x66, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x05,
0x20, 0x01, 0x28, 0x02, 0x48, 0x04, 0x52, 0x07, 0x66, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x88,
0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x66, 0x6f, 0x63, 0x61, 0x6c, 0x4c, 0x65, 0x6e, 0x67, 0x74,
0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x48, 0x05, 0x52, 0x0b, 0x66, 0x6f, 0x63, 0x61, 0x6c,
0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x15, 0x0a, 0x03, 0x69, 0x73, 0x6f,
0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x48, 0x06, 0x52, 0x03, 0x69, 0x73, 0x6f, 0x88, 0x01, 0x01,
0x12, 0x25, 0x0a, 0x0b, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18,
0x08, 0x20, 0x01, 0x28, 0x05, 0x48, 0x07, 0x52, 0x0b, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x45, 0x0a, 0x0d, 0x74, 0x61, 0x6b, 0x65, 0x6e,
0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48, 0x08, 0x52, 0x0d, 0x74, 0x61,
0x6b, 0x65, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d,
0x0a, 0x0b, 0x5f, 0x63, 0x61, 0x6d, 0x65, 0x72, 0x61, 0x4d, 0x61, 0x6b, 0x65, 0x42, 0x0e, 0x0a,
0x0c, 0x5f, 0x63, 0x61, 0x6d, 0x65, 0x72, 0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x42, 0x16, 0x0a,
0x14, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x44, 0x65, 0x6e, 0x6f, 0x6d, 0x69,
0x6e, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x75,
0x72, 0x65, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f,
0x66, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x66, 0x6f, 0x63, 0x61,
0x6c, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x42, 0x06, 0x0a, 0x04, 0x5f, 0x69, 0x73, 0x6f, 0x42,
0x0e, 0x0a, 0x0c, 0x5f, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42,
0x10, 0x0a, 0x0e, 0x5f, 0x74, 0x61, 0x6b, 0x65, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
0x65, 0x22, 0x9b, 0x04, 0x0a, 0x05, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x12, 0x33, 0x0a, 0x12, 0x61,
0x75, 0x64, 0x69, 0x6f, 0x42, 0x69, 0x74, 0x73, 0x50, 0x65, 0x72, 0x53, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x12, 0x61, 0x75, 0x64, 0x69, 0x6f,
0x42, 0x69, 0x74, 0x73, 0x50, 0x65, 0x72, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x88, 0x01, 0x01,
0x12, 0x29, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x6f,
0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x61,
0x75, 0x64, 0x69, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x48, 0x02, 0x52, 0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x88,
0x01, 0x01, 0x12, 0x39, 0x0a, 0x15, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x53, 0x61, 0x6d, 0x70, 0x6c,
0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28,
0x05, 0x48, 0x03, 0x52, 0x15, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a,
0x07, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x48, 0x04,
0x52, 0x07, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08,
0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x48, 0x05,
0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a,
0x06, 0x66, 0x6f, 0x75, 0x72, 0x43, 0x43, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x06, 0x52,
0x06, 0x66, 0x6f, 0x75, 0x72, 0x43, 0x43, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x66, 0x72,
0x61, 0x6d, 0x65, 0x52, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x01, 0x48, 0x07, 0x52,
0x09, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x61, 0x74, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a,
0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x48, 0x08, 0x52,
0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x77, 0x69,
0x64, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x05, 0x48, 0x09, 0x52, 0x05, 0x77, 0x69, 0x64,
0x74, 0x68, 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x42,
0x69, 0x74, 0x73, 0x50, 0x65, 0x72, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x42, 0x10, 0x0a, 0x0e,
0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x42, 0x0e,
0x0a, 0x0c, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x42, 0x18,
0x0a, 0x16, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x50,
0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74,
0x72, 0x61, 0x74, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x66, 0x6f, 0x75, 0x72, 0x43, 0x43, 0x42, 0x0c, 0x0a, 0x0a,
0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x61, 0x74, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68,
0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22,
0xc4, 0x01, 0x0a, 0x0b, 0x4d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x12,
0x1d, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
0x48, 0x00, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3d,
0x0a, 0x17, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x55, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x48,
0x01, 0x52, 0x17, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x55, 0x73, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a,
0x09, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03,
0x48, 0x02, 0x52, 0x09, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01,
0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x1a, 0x0a, 0x18,
0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x55, 0x73, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x76, 0x69, 0x64,
0x65, 0x6f, 0x53, 0x69, 0x7a, 0x65, 0x22, 0xb9, 0x02, 0x0a, 0x09, 0x4c, 0x69, 0x76, 0x65, 0x50,
0x68, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65,
0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x10, 0x73, 0x74, 0x69, 0x6c, 0x6c,
0x49, 0x6d, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28,
0x03, 0x48, 0x01, 0x52, 0x10, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x54,
0x69, 0x6d, 0x65, 0x55, 0x73, 0x88, 0x01, 0x01, 0x12, 0x17, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x6f,
0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x88, 0x01,
0x01, 0x12, 0x29, 0x0a, 0x0d, 0x76, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f,
0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x48, 0x03, 0x52, 0x0d, 0x76, 0x69, 0x74, 0x61,
0x6c, 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x88, 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x16,
0x76, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x48, 0x04, 0x52, 0x16,
0x76, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x6f,
0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x73, 0x74, 0x69, 0x6c,
0x6c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x73, 0x42, 0x07, 0x0a, 0x05,
0x5f, 0x61, 0x75, 0x74, 0x6f, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x76, 0x69, 0x74, 0x61, 0x6c, 0x69,
0x74, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x19, 0x0a, 0x17, 0x5f, 0x76, 0x69, 0x74, 0x61,
0x6c, 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x22, 0xc9, 0x08, 0x0a, 0x06, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x39, 0x0a,
0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65,
0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e,
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65,
0x6e, 0x63, 0x65, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12, 0x38, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x52, 0x02,
0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x65, 0x74, 0x61, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69,
0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x48,
0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f,
0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69,
0x66, 0x69, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65,
0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d,
0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d,
0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18,
0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64,
0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65,
0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x24, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x65, 0x52, 0x6f,
0x6f, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x68,
0x61, 0x72, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x09, 0x70,
0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28,
0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74,
0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09,
0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69,
0x67, 0x68, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x68, 0x69, 0x67, 0x68,
0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x39, 0x0a, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x18,
0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75,
0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63,
0x68, 0x2e, 0x76, 0x30, 0x2e, 0x41, 0x75, 0x64, 0x69, 0x6f, 0x52, 0x05, 0x61, 0x75, 0x64, 0x69,
0x6f, 0x12, 0x48, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e,
0x76, 0x30, 0x2e, 0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65,
0x73, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x0e, 0x72,
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x11, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e,
0x76, 0x30, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x72,
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x05, 0x69,
0x6d, 0x61, 0x67, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x70, 0x65,
0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e,
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52,
0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x39, 0x0a, 0x05, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x18,
0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75,
0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63,
0x68, 0x2e, 0x76, 0x30, 0x2e, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x05, 0x70, 0x68, 0x6f, 0x74,
0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x73, 0x18, 0x14,
0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x66, 0x61, 0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x73, 0x12,
0x4b, 0x0a, 0x0b, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x18, 0x15,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
0x2e, 0x76, 0x30, 0x2e, 0x4d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52,
0x0b, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x12, 0x39, 0x0a, 0x05,
0x76, 0x69, 0x64, 0x65, 0x6f, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x70,
0x67, 0x68, 0x74, 0x22, 0x56, 0x0a, 0x07, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x12, 0x19,
0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52,
0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x68, 0x65, 0x69,
0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x06, 0x68, 0x65, 0x69,
0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68,
0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x9d, 0x01, 0x0a, 0x0e,
0x47, 0x65, 0x6f, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x12, 0x1f,
0x0a, 0x08, 0x61, 0x6c, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01,
0x48, 0x00, 0x52, 0x08, 0x61, 0x6c, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12,
0x1f, 0x0a, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x01, 0x48, 0x01, 0x52, 0x08, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x88, 0x01, 0x01,
0x12, 0x21, 0x0a, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x18, 0x03, 0x20,
0x01, 0x28, 0x01, 0x48, 0x02, 0x52, 0x09, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65,
0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x61, 0x6c, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65,
0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6c, 0x61, 0x74, 0x69, 0x74, 0x75, 0x64, 0x65, 0x42, 0x0c, 0x0a,
0x0a, 0x5f, 0x6c, 0x6f, 0x6e, 0x67, 0x69, 0x74, 0x75, 0x64, 0x65, 0x22, 0x9b, 0x04, 0x0a, 0x05,
0x50, 0x68, 0x6f, 0x74, 0x6f, 0x12, 0x23, 0x0a, 0x0a, 0x63, 0x61, 0x6d, 0x65, 0x72, 0x61, 0x4d,
0x61, 0x6b, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x61, 0x6d,
0x65, 0x72, 0x61, 0x4d, 0x61, 0x6b, 0x65, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x63, 0x61,
0x6d, 0x65, 0x72, 0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48,
0x01, 0x52, 0x0b, 0x63, 0x61, 0x6d, 0x65, 0x72, 0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x88, 0x01,
0x01, 0x12, 0x35, 0x0a, 0x13, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x44, 0x65, 0x6e,
0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x48, 0x02,
0x52, 0x13, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x44, 0x65, 0x6e, 0x6f, 0x6d, 0x69,
0x6e, 0x61, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x31, 0x0a, 0x11, 0x65, 0x78, 0x70, 0x6f,
0x73, 0x75, 0x72, 0x65, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20,
0x01, 0x28, 0x02, 0x48, 0x03, 0x52, 0x11, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x4e,
0x75, 0x6d, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x66,
0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x02, 0x48, 0x04, 0x52, 0x07,
0x66, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x66, 0x6f,
0x63, 0x61, 0x6c, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x48,
0x05, 0x52, 0x0b, 0x66, 0x6f, 0x63, 0x61, 0x6c, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x88, 0x01,
0x01, 0x12, 0x15, 0x0a, 0x03, 0x69, 0x73, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x48, 0x06,
0x52, 0x03, 0x69, 0x73, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x6f, 0x72, 0x69, 0x65,
0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x48, 0x07, 0x52,
0x0b, 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12,
0x45, 0x0a, 0x0d, 0x74, 0x61, 0x6b, 0x65, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65,
0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
0x6d, 0x70, 0x48, 0x08, 0x52, 0x0d, 0x74, 0x61, 0x6b, 0x65, 0x6e, 0x44, 0x61, 0x74, 0x65, 0x54,
0x69, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x61, 0x6d, 0x65, 0x72,
0x61, 0x4d, 0x61, 0x6b, 0x65, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x61, 0x6d, 0x65, 0x72, 0x61,
0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x42, 0x16, 0x0a, 0x14, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x75,
0x72, 0x65, 0x44, 0x65, 0x6e, 0x6f, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x14, 0x0a,
0x12, 0x5f, 0x65, 0x78, 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x4e, 0x75, 0x6d, 0x65, 0x72, 0x61,
0x74, 0x6f, 0x72, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x66, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x42,
0x0e, 0x0a, 0x0c, 0x5f, 0x66, 0x6f, 0x63, 0x61, 0x6c, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x42,
0x06, 0x0a, 0x04, 0x5f, 0x69, 0x73, 0x6f, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x6f, 0x72, 0x69, 0x65,
0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x74, 0x61, 0x6b, 0x65,
0x6e, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x9b, 0x04, 0x0a, 0x05, 0x56, 0x69,
0x64, 0x65, 0x6f, 0x12, 0x33, 0x0a, 0x12, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x42, 0x69, 0x74, 0x73,
0x50, 0x65, 0x72, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x48,
0x00, 0x52, 0x12, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x42, 0x69, 0x74, 0x73, 0x50, 0x65, 0x72, 0x53,
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0d, 0x61, 0x75, 0x64, 0x69,
0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48,
0x01, 0x52, 0x0d, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73,
0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x46, 0x6f, 0x72, 0x6d,
0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x0b, 0x61, 0x75, 0x64, 0x69,
0x6f, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x15, 0x61, 0x75,
0x64, 0x69, 0x6f, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63,
0x6f, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x48, 0x03, 0x52, 0x15, 0x61, 0x75, 0x64,
0x69, 0x6f, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f,
0x6e, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65,
0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x48, 0x04, 0x52, 0x07, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74,
0x65, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x48, 0x05, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x66, 0x6f, 0x75, 0x72, 0x43, 0x43, 0x18,
0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x06, 0x52, 0x06, 0x66, 0x6f, 0x75, 0x72, 0x43, 0x43, 0x88,
0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x61, 0x74, 0x65, 0x18,
0x08, 0x20, 0x01, 0x28, 0x01, 0x48, 0x07, 0x52, 0x09, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x61,
0x74, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18,
0x09, 0x20, 0x01, 0x28, 0x05, 0x48, 0x08, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88,
0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28,
0x05, 0x48, 0x09, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x88, 0x01, 0x01, 0x42, 0x15, 0x0a,
0x13, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x42, 0x69, 0x74, 0x73, 0x50, 0x65, 0x72, 0x53, 0x61,
0x6d, 0x70, 0x6c, 0x65, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x68,
0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f,
0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x61, 0x75, 0x64, 0x69, 0x6f,
0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64,
0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x62, 0x69, 0x74, 0x72, 0x61, 0x74, 0x65, 0x42, 0x0b, 0x0a, 0x09,
0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x66, 0x6f,
0x75, 0x72, 0x43, 0x43, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x61,
0x74, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x08, 0x0a,
0x06, 0x5f, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0xc4, 0x01, 0x0a, 0x0b, 0x4d, 0x6f, 0x74, 0x69,
0x6f, 0x6e, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x12, 0x1d, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69,
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x3d, 0x0a, 0x17, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x55,
0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x17, 0x70, 0x72, 0x65, 0x73, 0x65,
0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x55, 0x73, 0x88, 0x01, 0x01, 0x12, 0x21, 0x0a, 0x09, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x53, 0x69,
0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x02, 0x52, 0x09, 0x76, 0x69, 0x64, 0x65,
0x6f, 0x53, 0x69, 0x7a, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x42, 0x1a, 0x0a, 0x18, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x55, 0x73,
0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x53, 0x69, 0x7a, 0x65, 0x22, 0xb9,
0x02, 0x0a, 0x09, 0x4c, 0x69, 0x76, 0x65, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x12, 0x21, 0x0a, 0x09,
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48,
0x00, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12,
0x2f, 0x0a, 0x10, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6d,
0x65, 0x55, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x10, 0x73, 0x74, 0x69,
0x6c, 0x6c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x73, 0x88, 0x01, 0x01,
0x12, 0x17, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x48, 0x02,
0x52, 0x04, 0x61, 0x75, 0x74, 0x6f, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0d, 0x76, 0x69, 0x74,
0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01,
0x48, 0x03, 0x52, 0x0d, 0x76, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f, 0x72,
0x65, 0x88, 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x16, 0x76, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x74, 0x79,
0x53, 0x63, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05,
0x20, 0x01, 0x28, 0x03, 0x48, 0x04, 0x52, 0x16, 0x76, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x74, 0x79,
0x53, 0x63, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01,
0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x42,
0x13, 0x0a, 0x11, 0x5f, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x54, 0x69,
0x6d, 0x65, 0x55, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x42, 0x10, 0x0a,
0x0e, 0x5f, 0x76, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x42,
0x19, 0x0a, 0x17, 0x5f, 0x76, 0x69, 0x74, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x53, 0x63, 0x6f, 0x72,
0x69, 0x6e, 0x67, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x8a, 0x09, 0x0a, 0x06, 0x45,
0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x39, 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76,
0x30, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x03, 0x72, 0x65, 0x66,
0x12, 0x38, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f,
0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x49, 0x44, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12,
0x0a, 0x04, 0x65, 0x74, 0x61, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x65, 0x74,
0x61, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04,
0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6d,
0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10,
0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65,
0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a,
0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12,
0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74,
0x79, 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x24, 0x0a,
0x0d, 0x73, 0x68, 0x61, 0x72, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x0b,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x4e,
0x61, 0x6d, 0x65, 0x12, 0x45, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64,
0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f,
0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72,
0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x44,
0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61,
0x67, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x1e,
0x0a, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0a, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x12, 0x39,
0x0a, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e,
0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x41, 0x75, 0x64,
0x69, 0x6f, 0x52, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x12, 0x48, 0x0a, 0x08, 0x6c, 0x6f, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x6f, 0x70,
0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73,
0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f,
0x52, 0x05, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x12, 0x45, 0x0a, 0x09, 0x6c, 0x69, 0x76, 0x65, 0x50,
0x68, 0x6f, 0x74, 0x6f, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65,
0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e,
0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x4c, 0x69, 0x76, 0x65, 0x50, 0x68,
0x6f, 0x74, 0x6f, 0x52, 0x09, 0x6c, 0x69, 0x76, 0x65, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x22, 0x5b,
0x0a, 0x05, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74,
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c,
0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, 0x6f, 0x43, 0x6f,
0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x0e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x69, 0x74,
0x65, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x6f, 0x70,
0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73,
0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x49, 0x74, 0x65,
0x6d, 0x49, 0x64, 0x12, 0x39, 0x0a, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x12, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76,
0x30, 0x2e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x12, 0x39,
0x0a, 0x05, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e,
0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x50, 0x68, 0x6f,
0x74, 0x6f, 0x52, 0x05, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x61, 0x76,
0x6f, 0x72, 0x69, 0x74, 0x65, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x66, 0x61,
0x76, 0x6f, 0x72, 0x69, 0x74, 0x65, 0x73, 0x12, 0x4b, 0x0a, 0x0b, 0x6d, 0x6f, 0x74, 0x69, 0x6f,
0x6e, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6f,
0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x4d, 0x6f, 0x74, 0x69,
0x6f, 0x6e, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x0b, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50,
0x68, 0x6f, 0x74, 0x6f, 0x12, 0x39, 0x0a, 0x05, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x18, 0x16, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e,
0x76, 0x30, 0x2e, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x52, 0x05, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x12,
0x45, 0x0a, 0x09, 0x6c, 0x69, 0x76, 0x65, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x18, 0x17, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x27, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76,
0x30, 0x2e, 0x4c, 0x69, 0x76, 0x65, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x52, 0x09, 0x6c, 0x69, 0x76,
0x65, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x12, 0x3f, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x76, 0x69, 0x65,
0x77, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c,
0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61,
0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x06, 0x65,
0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x42, 0x4d, 0x5a, 0x4b, 0x67,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c,
0x6f, 0x75, 0x64, 0x2d, 0x65, 0x75, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x70,
0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73,
0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2f, 0x76, 0x30, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x07,
0x70, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x22, 0x5b, 0x0a, 0x05, 0x4d, 0x61, 0x74, 0x63, 0x68,
0x12, 0x3c, 0x0a, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x24, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x6d, 0x65, 0x73,
0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2e, 0x76, 0x30, 0x2e,
0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x06, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x14,
0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x02, 0x52, 0x05, 0x73,
0x63, 0x6f, 0x72, 0x65, 0x42, 0x4d, 0x5a, 0x4b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x65, 0x75, 0x2f,
0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x67,
0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2f, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68,
0x2f, 0x76, 0x30, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1354,42 +1428,44 @@ func file_opencloud_messages_search_v0_search_proto_rawDescGZIP() []byte {
return file_opencloud_messages_search_v0_search_proto_rawDescData
}
var file_opencloud_messages_search_v0_search_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
var file_opencloud_messages_search_v0_search_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
var file_opencloud_messages_search_v0_search_proto_goTypes = []interface{}{
(*ResourceID)(nil), // 0: opencloud.messages.search.v0.ResourceID
(*Reference)(nil), // 1: opencloud.messages.search.v0.Reference
(*Audio)(nil), // 2: opencloud.messages.search.v0.Audio
(*Image)(nil), // 3: opencloud.messages.search.v0.Image
(*GeoCoordinates)(nil), // 4: opencloud.messages.search.v0.GeoCoordinates
(*Photo)(nil), // 5: opencloud.messages.search.v0.Photo
(*Video)(nil), // 6: opencloud.messages.search.v0.Video
(*MotionPhoto)(nil), // 7: opencloud.messages.search.v0.MotionPhoto
(*LivePhoto)(nil), // 8: opencloud.messages.search.v0.LivePhoto
(*Entity)(nil), // 9: opencloud.messages.search.v0.Entity
(*Match)(nil), // 10: opencloud.messages.search.v0.Match
(*timestamppb.Timestamp)(nil), // 11: google.protobuf.Timestamp
(*Preview)(nil), // 4: opencloud.messages.search.v0.Preview
(*GeoCoordinates)(nil), // 5: opencloud.messages.search.v0.GeoCoordinates
(*Photo)(nil), // 6: opencloud.messages.search.v0.Photo
(*Video)(nil), // 7: opencloud.messages.search.v0.Video
(*MotionPhoto)(nil), // 8: opencloud.messages.search.v0.MotionPhoto
(*LivePhoto)(nil), // 9: opencloud.messages.search.v0.LivePhoto
(*Entity)(nil), // 10: opencloud.messages.search.v0.Entity
(*Match)(nil), // 11: opencloud.messages.search.v0.Match
(*timestamppb.Timestamp)(nil), // 12: google.protobuf.Timestamp
}
var file_opencloud_messages_search_v0_search_proto_depIdxs = []int32{
0, // 0: opencloud.messages.search.v0.Reference.resource_id:type_name -> opencloud.messages.search.v0.ResourceID
11, // 1: opencloud.messages.search.v0.Photo.takenDateTime:type_name -> google.protobuf.Timestamp
12, // 1: opencloud.messages.search.v0.Photo.takenDateTime:type_name -> google.protobuf.Timestamp
1, // 2: opencloud.messages.search.v0.Entity.ref:type_name -> opencloud.messages.search.v0.Reference
0, // 3: opencloud.messages.search.v0.Entity.id:type_name -> opencloud.messages.search.v0.ResourceID
11, // 4: opencloud.messages.search.v0.Entity.last_modified_time:type_name -> google.protobuf.Timestamp
12, // 4: opencloud.messages.search.v0.Entity.last_modified_time:type_name -> google.protobuf.Timestamp
0, // 5: opencloud.messages.search.v0.Entity.parent_id:type_name -> opencloud.messages.search.v0.ResourceID
2, // 6: opencloud.messages.search.v0.Entity.audio:type_name -> opencloud.messages.search.v0.Audio
4, // 7: opencloud.messages.search.v0.Entity.location:type_name -> opencloud.messages.search.v0.GeoCoordinates
5, // 7: opencloud.messages.search.v0.Entity.location:type_name -> opencloud.messages.search.v0.GeoCoordinates
0, // 8: opencloud.messages.search.v0.Entity.remote_item_id:type_name -> opencloud.messages.search.v0.ResourceID
3, // 9: opencloud.messages.search.v0.Entity.image:type_name -> opencloud.messages.search.v0.Image
5, // 10: opencloud.messages.search.v0.Entity.photo:type_name -> opencloud.messages.search.v0.Photo
7, // 11: opencloud.messages.search.v0.Entity.motionPhoto:type_name -> opencloud.messages.search.v0.MotionPhoto
6, // 12: opencloud.messages.search.v0.Entity.video:type_name -> opencloud.messages.search.v0.Video
8, // 13: opencloud.messages.search.v0.Entity.livePhoto:type_name -> opencloud.messages.search.v0.LivePhoto
9, // 14: opencloud.messages.search.v0.Match.entity:type_name -> opencloud.messages.search.v0.Entity
15, // [15:15] is the sub-list for method output_type
15, // [15:15] is the sub-list for method input_type
15, // [15:15] is the sub-list for extension type_name
15, // [15:15] is the sub-list for extension extendee
0, // [0:15] is the sub-list for field type_name
6, // 10: opencloud.messages.search.v0.Entity.photo:type_name -> opencloud.messages.search.v0.Photo
8, // 11: opencloud.messages.search.v0.Entity.motionPhoto:type_name -> opencloud.messages.search.v0.MotionPhoto
7, // 12: opencloud.messages.search.v0.Entity.video:type_name -> opencloud.messages.search.v0.Video
9, // 13: opencloud.messages.search.v0.Entity.livePhoto:type_name -> opencloud.messages.search.v0.LivePhoto
4, // 14: opencloud.messages.search.v0.Entity.preview:type_name -> opencloud.messages.search.v0.Preview
10, // 15: opencloud.messages.search.v0.Match.entity:type_name -> opencloud.messages.search.v0.Entity
16, // [16:16] is the sub-list for method output_type
16, // [16:16] is the sub-list for method input_type
16, // [16:16] is the sub-list for extension type_name
16, // [16:16] is the sub-list for extension extendee
0, // [0:16] is the sub-list for field type_name
}
func init() { file_opencloud_messages_search_v0_search_proto_init() }
@@ -1447,7 +1523,7 @@ func file_opencloud_messages_search_v0_search_proto_init() {
}
}
file_opencloud_messages_search_v0_search_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GeoCoordinates); i {
switch v := v.(*Preview); i {
case 0:
return &v.state
case 1:
@@ -1459,7 +1535,7 @@ func file_opencloud_messages_search_v0_search_proto_init() {
}
}
file_opencloud_messages_search_v0_search_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Photo); i {
switch v := v.(*GeoCoordinates); i {
case 0:
return &v.state
case 1:
@@ -1471,7 +1547,7 @@ func file_opencloud_messages_search_v0_search_proto_init() {
}
}
file_opencloud_messages_search_v0_search_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Video); i {
switch v := v.(*Photo); i {
case 0:
return &v.state
case 1:
@@ -1483,7 +1559,7 @@ func file_opencloud_messages_search_v0_search_proto_init() {
}
}
file_opencloud_messages_search_v0_search_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MotionPhoto); i {
switch v := v.(*Video); i {
case 0:
return &v.state
case 1:
@@ -1495,7 +1571,7 @@ func file_opencloud_messages_search_v0_search_proto_init() {
}
}
file_opencloud_messages_search_v0_search_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*LivePhoto); i {
switch v := v.(*MotionPhoto); i {
case 0:
return &v.state
case 1:
@@ -1507,7 +1583,7 @@ func file_opencloud_messages_search_v0_search_proto_init() {
}
}
file_opencloud_messages_search_v0_search_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Entity); i {
switch v := v.(*LivePhoto); i {
case 0:
return &v.state
case 1:
@@ -1519,6 +1595,18 @@ func file_opencloud_messages_search_v0_search_proto_init() {
}
}
file_opencloud_messages_search_v0_search_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Entity); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_opencloud_messages_search_v0_search_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Match); i {
case 0:
return &v.state
@@ -1538,13 +1626,14 @@ func file_opencloud_messages_search_v0_search_proto_init() {
file_opencloud_messages_search_v0_search_proto_msgTypes[6].OneofWrappers = []interface{}{}
file_opencloud_messages_search_v0_search_proto_msgTypes[7].OneofWrappers = []interface{}{}
file_opencloud_messages_search_v0_search_proto_msgTypes[8].OneofWrappers = []interface{}{}
file_opencloud_messages_search_v0_search_proto_msgTypes[9].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_opencloud_messages_search_v0_search_proto_rawDesc,
NumEnums: 0,
NumMessages: 11,
NumMessages: 12,
NumExtensions: 0,
NumServices: 0,
},
@@ -307,6 +307,9 @@
},
"livePhoto": {
"$ref": "#/definitions/v0LivePhoto"
},
"preview": {
"$ref": "#/definitions/v0Preview"
}
}
},
@@ -478,6 +481,20 @@
}
}
},
"v0Preview": {
"type": "object",
"properties": {
"width": {
"type": "integer",
"format": "int32"
},
"height": {
"type": "integer",
"format": "int32"
}
},
"description": "Preview carries the dimensions of an embedded preview (e.g. audio cover art).\nIts presence signals that a preview is available for the resource."
},
"v0Reference": {
"type": "object",
"properties": {
@@ -41,6 +41,13 @@ message Image {
optional int32 height = 2;
}
// Preview carries the dimensions of an embedded preview (e.g. audio cover art).
// Its presence signals that a preview is available for the resource.
message Preview {
optional int32 width = 1;
optional int32 height = 2;
}
message GeoCoordinates {
optional double altitude = 1;
optional double latitude = 2;
@@ -110,6 +117,7 @@ message Entity {
MotionPhoto motionPhoto = 21;
Video video = 22;
LivePhoto livePhoto = 23;
Preview preview = 24;
}
message Match {
+28 -4
View File
@@ -8,6 +8,7 @@ import (
libregraph "github.com/opencloud-eu/libre-graph-api-go"
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
"github.com/opencloud-eu/opencloud/pkg/conversions"
"github.com/opencloud-eu/opencloud/services/thumbnails/pkg/thumbnail"
)
@@ -34,17 +35,36 @@ func setDriveItemThumbnails(item *libregraph.DriveItem, res *provider.ResourceIn
}
}
// previewThumbnailSet returns nil when the thumbnailer cannot render the resource.
// previewThumbnailSet returns nil when no preview can be produced for the resource.
func previewThumbnailSet(res *provider.ResourceInfo, baseURL string) *libregraph.ThumbnailSet {
if !thumbnail.IsMimeTypeSupported(res.GetMimeType()) {
if !thumbnail.HasPreview(res) {
return nil
}
return thumbnailSetFor(baseURL, storagespace.FormatResourceID(res.GetId()))
itemID := storagespace.FormatResourceID(res.GetId())
set := thumbnailSetFor(baseURL, itemID)
// only the source carries exact dimensions, the boxes above are requests
if w, h := previewSourceDimensions(res); w > 0 && h > 0 {
url := fmt.Sprintf("%s&x=%d&y=%d", previewBaseURL(baseURL, itemID), w, h)
set.Source = &libregraph.Thumbnail{Url: &url, Width: &w, Height: &h}
}
return set
}
// previewSourceDimensions: audio cover from oc.preview, images from the image facet.
func previewSourceDimensions(res *provider.ResourceInfo) (int32, int32) {
if w, h := thumbnail.PreviewDimensions(res); w > 0 && h > 0 {
return w, h
}
meta := res.GetArbitraryMetadata().GetMetadata()
w := conversions.StringToInt32(meta["libre.graph.image.width"], 0)
h := conversions.StringToInt32(meta["libre.graph.image.height"], 0)
return w, h
}
// thumbnailSetFor builds the urls of the WebDAV preview endpoint.
func thumbnailSetFor(baseURL, itemID string) *libregraph.ThumbnailSet {
base := fmt.Sprintf("%s/dav/spaces/%s?scalingup=0&preview=1&processor=thumbnail", baseURL, itemID)
base := previewBaseURL(baseURL, itemID)
return &libregraph.ThumbnailSet{
Small: previewThumbnail(base, thumbnailBoxSmall),
Medium: previewThumbnail(base, thumbnailBoxMedium),
@@ -52,6 +72,10 @@ func thumbnailSetFor(baseURL, itemID string) *libregraph.ThumbnailSet {
}
}
func previewBaseURL(baseURL, itemID string) string {
return fmt.Sprintf("%s/dav/spaces/%s?scalingup=0&preview=1&processor=thumbnail", baseURL, itemID)
}
func previewThumbnail(base string, box int32) *libregraph.Thumbnail {
url := fmt.Sprintf("%s&x=%d&y=%d", base, box, box)
return &libregraph.Thumbnail{Url: &url}
+12 -10
View File
@@ -125,16 +125,18 @@ func (b *Backend) Search(_ context.Context, sir *searchService.SearchIndexReques
ResourceId: resourceIDtoSearchID(rootID),
Path: getFieldValue[string](hit.Fields, "Path"),
},
Id: resourceIDtoSearchID(rID),
Name: getFieldValue[string](hit.Fields, "Name"),
ParentId: resourceIDtoSearchID(pID),
Size: uint64(getFieldValue[float64](hit.Fields, "Size")),
Type: uint64(getFieldValue[float64](hit.Fields, "Type")),
MimeType: getFieldValue[string](hit.Fields, "MimeType"),
Deleted: getFieldValue[bool](hit.Fields, "Deleted"),
Tags: getFieldSliceValue[string](hit.Fields, "Tags"),
Favorites: getFieldSliceValue[string](hit.Fields, "Favorites"),
Highlights: getFragmentValue(hit.Fragments, "Content", 0),
Id: resourceIDtoSearchID(rID),
Name: getFieldValue[string](hit.Fields, "Name"),
ParentId: resourceIDtoSearchID(pID),
Size: uint64(getFieldValue[float64](hit.Fields, "Size")),
Type: uint64(getFieldValue[float64](hit.Fields, "Type")),
MimeType: getFieldValue[string](hit.Fields, "MimeType"),
Deleted: getFieldValue[bool](hit.Fields, "Deleted"),
Tags: getFieldSliceValue[string](hit.Fields, "Tags"),
Favorites: getFieldSliceValue[string](hit.Fields, "Favorites"),
Highlights: getFragmentValue(hit.Fragments, "Content", 0),
Preview: hitToFacet[searchMessage.Preview](hit.Fields, "preview"),
Audio: hitToFacet[searchMessage.Audio](hit.Fields, "audio"),
Image: hitToFacet[searchMessage.Image](hit.Fields, "image"),
Location: hitToFacet[searchMessage.GeoCoordinates](hit.Fields, "location"),
+32
View File
@@ -1053,6 +1053,38 @@
}
}
},
"preview": {
"enabled": true,
"dynamic": true,
"properties": {
"height": {
"enabled": true,
"dynamic": true,
"fields": [
{
"type": "number",
"store": true,
"index": true,
"include_in_all": true,
"docvalues": true
}
]
},
"width": {
"enabled": true,
"dynamic": true,
"fields": [
{
"type": "number",
"store": true,
"index": true,
"include_in_all": true,
"docvalues": true
}
]
}
}
},
"video": {
"enabled": true,
"dynamic": true,
+25 -8
View File
@@ -15,14 +15,16 @@ func init() {
// Document wraps all resource meta fields,
// it is used as a content extraction result.
type Document struct {
Title string `json:"Title"`
Name string `json:"Name"`
Content string `json:"Content"`
Size uint64 `json:"Size"`
Mtime *time.Time `json:"Mtime,omitempty"`
MimeType string `json:"MimeType"`
Tags []string `json:"Tags"`
Favorites []string `json:"Favorites"`
Title string `json:"Title"`
Name string `json:"Name"`
Content string `json:"Content"`
Size uint64 `json:"Size"`
Mtime *time.Time `json:"Mtime,omitempty"`
MimeType string `json:"MimeType"`
Tags []string `json:"Tags"`
Favorites []string `json:"Favorites"`
Preview *Preview `json:"preview,omitempty"`
Audio *libregraph.Audio `json:"audio,omitempty"`
Image *libregraph.Image `json:"image,omitempty"`
Location *libregraph.GeoCoordinates `json:"location,omitempty"`
@@ -32,6 +34,21 @@ type Document struct {
LivePhoto *libregraph.LivePhoto `json:"livePhoto,omitempty"`
}
// Preview holds the dimensions of an embedded preview (e.g. audio cover art).
// Internal signal, not a Graph facet; its presence marks that a preview exists.
type Preview struct {
Width int32 `json:"width"`
Height int32 `json:"height"`
}
// ToMap flows Preview through the shared facet flattening, under oc.preview.
func (p Preview) ToMap() (map[string]interface{}, error) {
return map[string]interface{}{
"width": p.Width,
"height": p.Height,
}, nil
}
func CleanString(content, langCode string) string {
return strings.TrimSpace(stopwords.CleanString(content, langCode, true))
}
+18 -29
View File
@@ -82,7 +82,11 @@ func (t Tika) Extract(ctx context.Context, ri *provider.ResourceInfo) (Document,
if err != nil {
return doc, err
}
if len(metas) == 0 {
return doc, nil
}
// Title and content aggregate across the container and embedded resources.
for _, meta := range metas {
title, err := getFirstValue(meta, "dc:title")
if err != nil {
@@ -98,42 +102,27 @@ func (t Tika) Extract(ctx context.Context, ri *provider.ResourceInfo) (Document,
} else if content, err := getFirstValue(meta, "X-TIKA:content"); err == nil {
doc.Content = strings.TrimSpace(fmt.Sprintf("%s %s", doc.Content, content))
}
// keep facets from earlier entries, an embedded resource's meta
// (e.g. cover art) must not reset them
if v := t.getLocation(meta); v != nil {
doc.Location = v
}
if v := t.getImage(meta); v != nil {
doc.Image = v
}
if v := t.getPhoto(meta); v != nil {
doc.Photo = v
}
if v := t.getAudio(meta); v != nil {
doc.Audio = v
}
if v := t.getLivePhoto(meta); v != nil {
doc.LivePhoto = v
}
}
if len(metas) > 0 {
// the video facet says the file is a video, so it comes from the file
// itself: the clip tika extracts from a motion photo must not make its
// image look like one
doc.Video = t.getVideo(metas[0])
}
// a motion photo is the xmp on the file itself plus the video tika extracted
// from it. The xmp alone proves nothing: a share can keep it and strip the
// appended video.
if len(metas) > 0 {
if i := slices.IndexFunc(metas[1:], isVideo); i >= 0 {
doc.MotionPhoto = t.getMotionPhoto(metas[0], metas[i+1])
}
if i := slices.IndexFunc(metas[1:], isVideo); i >= 0 {
doc.MotionPhoto = t.getMotionPhoto(metas[0], metas[i+1])
}
doc.Preview = getPreview(ri.GetMimeType(), metas)
// Facets come from the container (first entry) only; embedded resources like
// audio cover art must not leak in (the cover becomes the preview instead).
container := metas[0]
doc.Location = t.getLocation(container)
doc.Image = t.getImage(container)
doc.Photo = t.getPhoto(container)
doc.Audio = t.getAudio(container)
doc.Video = t.getVideo(container)
doc.LivePhoto = t.getLivePhoto(container)
if langCode := t.detectLanguage(ctx, doc.Content); langCode != "" && t.CleanStopWords {
doc.Content = CleanString(doc.Content, langCode)
}
@@ -0,0 +1,47 @@
package content
import (
"strconv"
"strings"
"github.com/opencloud-eu/opencloud/services/thumbnails/pkg/thumbnail"
)
// frontCoverDescription is the picture type Tika reports (as dc:description) for
// the front cover, matching the thumbnailer's cover selection.
const frontCoverDescription = "Cover (front)"
// getPreview returns the dimensions of an audio file's embedded cover art from
// Tika's recursive metadata, preferring the front cover (dc:description) and
// falling back to the first image, matching the thumbnailer's selection. It only
// runs for EmbeddedPreviewMimeTypes.
func getPreview(mimeType string, metas []map[string][]string) *Preview {
if _, ok := thumbnail.EmbeddedPreviewMimeTypes[mimeType]; !ok {
return nil
}
var first *Preview
for _, meta := range metas {
ct, err := getFirstValue(meta, "Content-Type")
if err != nil || !strings.HasPrefix(ct, "image/") {
continue
}
w, wErr := getFirstValue(meta, "tiff:ImageWidth")
h, hErr := getFirstValue(meta, "tiff:ImageLength")
if wErr != nil || hErr != nil {
continue
}
width, wErr := strconv.ParseInt(w, 10, 32)
height, hErr := strconv.ParseInt(h, 10, 32)
if wErr != nil || hErr != nil || width <= 0 || height <= 0 {
continue
}
preview := &Preview{Width: int32(width), Height: int32(height)}
if desc, _ := getFirstValue(meta, "dc:description"); desc == frontCoverDescription {
return preview
}
if first == nil {
first = preview
}
}
return first
}
@@ -0,0 +1,49 @@
package content
import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("getPreview", func() {
audio := map[string][]string{"Content-Type": {"audio/mpeg"}}
cover := map[string][]string{
"Content-Type": {"image/jpeg"},
"tiff:ImageWidth": {"500"},
"tiff:ImageLength": {"400"},
}
coverNoDims := map[string][]string{"Content-Type": {"image/jpeg"}}
It("returns the embedded cover dimensions for audio", func() {
p := getPreview("audio/mpeg", []map[string][]string{audio, cover})
Expect(p).ToNot(BeNil())
Expect(*p).To(Equal(Preview{Width: 500, Height: 400}))
})
It("returns nil when the audio has no cover", func() {
Expect(getPreview("audio/mpeg", []map[string][]string{audio})).To(BeNil())
})
It("returns nil when the cover lacks dimensions", func() {
Expect(getPreview("audio/mpeg", []map[string][]string{audio, coverNoDims})).To(BeNil())
})
It("prefers the front cover over an earlier back cover", func() {
back := map[string][]string{
"Content-Type": {"image/jpeg"}, "dc:description": {"Cover (back)"},
"tiff:ImageWidth": {"30"}, "tiff:ImageLength": {"30"},
}
front := map[string][]string{
"Content-Type": {"image/jpeg"}, "dc:description": {"Cover (front)"},
"tiff:ImageWidth": {"64"}, "tiff:ImageLength": {"40"},
}
p := getPreview("audio/mpeg", []map[string][]string{audio, back, front})
Expect(p).ToNot(BeNil())
Expect(*p).To(Equal(Preview{Width: 64, Height: 40}))
})
It("is gated to embedded-preview types", func() {
// an image is unconditional; its preview is not driven by oc.preview.
Expect(getPreview("image/png", []map[string][]string{cover})).To(BeNil())
})
})
+10 -5
View File
@@ -170,17 +170,22 @@ var _ = Describe("Tika", func() {
Expect(doc.Content).To(Equal("body test stop words!!!"))
})
It("keeps the audio facet when an embedded resource follows", func() {
fullResponse = `[{"Content-Type": "audio/mpeg", "dc:title": "Sucker", "tk:content": "lyrics"}, {"Content-Type": "image/jpeg", "tiff:ImageWidth": "500"}]`
It("keeps the audio facet and captures the cover as preview", func() {
fullResponse = `[{"Content-Type": "audio/mpeg", "dc:title": "Sucker", "tk:content": "lyrics"}, {"Content-Type": "image/jpeg", "tiff:ImageWidth": "500", "tiff:ImageLength": "400"}]`
doc, err := tika.Extract(context.TODO(), &provider.ResourceInfo{
Type: provider.ResourceType_RESOURCE_TYPE_FILE,
Size: 1,
Type: provider.ResourceType_RESOURCE_TYPE_FILE,
Size: 1,
MimeType: "audio/mpeg",
})
Expect(err).ToNot(HaveOccurred())
Expect(doc.Audio).ToNot(BeNil())
Expect(doc.Audio.Title).To(Equal(libregraph.PtrString("Sucker")))
Expect(doc.Image).ToNot(BeNil())
// facets describe the container only, the cover surfaces as preview
Expect(doc.Image).To(BeNil())
Expect(doc.Preview).ToNot(BeNil())
Expect(doc.Preview.Width).To(Equal(int32(500)))
Expect(doc.Preview.Height).To(Equal(int32(400)))
})
It("adds no audio facet to non-audio documents", func() {
@@ -79,6 +79,9 @@ func OpenSearchHitToMatch(hit opensearchgoAPI.SearchHit) (*searchMessage.Match,
return strings.Join(contentHighlights[:], "; ")
}(),
Preview: copyFacet[searchMessage.Preview](resource.Preview),
Audio: copyFacet[searchMessage.Audio](resource.Audio),
Image: copyFacet[searchMessage.Image](resource.Image),
Location: copyFacet[searchMessage.GeoCoordinates](resource.Location),
@@ -283,6 +283,16 @@
}
}
},
"preview": {
"properties": {
"height": {
"type": "integer"
},
"width": {
"type": "integer"
}
}
},
"video": {
"properties": {
"audioBitsPerSample": {
+1
View File
@@ -688,6 +688,7 @@ func (s *Service) doUpsertItem(ref *provider.Reference, batch BatchOperator) {
facetToMetadata(metadata, doc.Video, "libre.graph.video.")
facetToMetadata(metadata, doc.MotionPhoto, "libre.graph.motionPhoto.")
facetToMetadata(metadata, doc.LivePhoto, "libre.graph.livePhoto.")
facetToMetadata(metadata, doc.Preview, "oc.preview.")
if len(metadata) == 0 {
return
}
@@ -0,0 +1,45 @@
package thumbnail
import (
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/opencloud-eu/opencloud/pkg/conversions"
)
// Arbitrary-metadata keys holding an embedded preview's dimensions (e.g. audio
// cover art), written at index time. Their presence signals a preview exists.
const (
PreviewWidthKey = "oc.preview.width"
PreviewHeightKey = "oc.preview.height"
)
// HasPreview reports whether a preview can be produced for the resource:
// unconditional types by mimetype, embedded-preview types by stored dimensions.
func HasPreview(md *provider.ResourceInfo) bool {
if md == nil {
return false
}
w, h := PreviewDimensions(md)
return HasPreviewForMimeType(md.GetMimeType(), w > 0 && h > 0)
}
// HasPreviewForMimeType is HasPreview for callers that only have the mimetype
// and a presence signal (e.g. search results) rather than a full ResourceInfo.
func HasPreviewForMimeType(mimeType string, hasEmbeddedPreview bool) bool {
if _, ok := UnconditionalPreviewMimeTypes[mimeType]; ok {
return true
}
if _, ok := EmbeddedPreviewMimeTypes[mimeType]; ok {
return hasEmbeddedPreview
}
return false
}
// PreviewDimensions returns the stored embedded-preview dimensions, or (0, 0).
// Only meaningful for EmbeddedPreviewMimeTypes.
func PreviewDimensions(md *provider.ResourceInfo) (int32, int32) {
meta := md.GetArbitraryMetadata().GetMetadata()
w := conversions.StringToInt32(meta[PreviewWidthKey], 0)
h := conversions.StringToInt32(meta[PreviewHeightKey], 0)
return w, h
}
@@ -0,0 +1,45 @@
package thumbnail_test
import (
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/opencloud-eu/opencloud/services/thumbnails/pkg/thumbnail"
)
func resourceInfo(mime string, meta map[string]string) *provider.ResourceInfo {
ri := &provider.ResourceInfo{MimeType: mime}
if meta != nil {
ri.ArbitraryMetadata = &provider.ArbitraryMetadata{Metadata: meta}
}
return ri
}
var _ = Describe("HasPreview", func() {
DescribeTable("preview availability",
func(md *provider.ResourceInfo, want bool) {
Expect(thumbnail.HasPreview(md)).To(Equal(want))
},
Entry("nil", nil, false),
Entry("unconditional image", resourceInfo("image/png", nil), true),
Entry("unconditional text", resourceInfo("text/plain", nil), true),
Entry("unsupported type", resourceInfo("application/pdf", nil), false),
Entry("audio without preview dims", resourceInfo("audio/mpeg", nil), false),
Entry("audio with zero dims", resourceInfo("audio/mpeg", map[string]string{
thumbnail.PreviewWidthKey: "0", thumbnail.PreviewHeightKey: "0",
}), false),
Entry("audio with preview dims", resourceInfo("audio/mpeg", map[string]string{
thumbnail.PreviewWidthKey: "500", thumbnail.PreviewHeightKey: "500",
}), true),
)
It("SupportedMimeTypes is the union of both preview sets", func() {
for k := range thumbnail.UnconditionalPreviewMimeTypes {
Expect(thumbnail.SupportedMimeTypes).To(HaveKey(k))
}
for k := range thumbnail.EmbeddedPreviewMimeTypes {
Expect(thumbnail.SupportedMimeTypes).To(HaveKey(k))
}
})
})
+14 -18
View File
@@ -2,21 +2,17 @@
package thumbnail
var (
// SupportedMimeTypes contains an all mimetypes which are supported by the thumbnailer.
SupportedMimeTypes = map[string]struct{}{
"image/png": {},
"image/jpg": {},
"image/jpeg": {},
"image/gif": {},
"image/bmp": {},
"image/x-ms-bmp": {},
"image/tiff": {},
"text/plain": {},
"audio/flac": {},
"audio/mpeg": {},
"audio/ogg": {},
"application/vnd.geogebra.slides": {},
"application/vnd.geogebra.pinboard": {},
}
)
// UnconditionalPreviewMimeTypes always have a preview: the thumbnailer renders
// one from the content.
var UnconditionalPreviewMimeTypes = map[string]struct{}{
"image/png": {},
"image/jpg": {},
"image/jpeg": {},
"image/gif": {},
"image/bmp": {},
"image/x-ms-bmp": {},
"image/tiff": {},
"text/plain": {},
"application/vnd.geogebra.slides": {},
"application/vnd.geogebra.pinboard": {},
}
@@ -0,0 +1,21 @@
package thumbnail
// EmbeddedPreviewMimeTypes have an embedded preview (e.g. audio cover art) that
// may be absent; availability is decided per file (see HasPreview).
var EmbeddedPreviewMimeTypes = map[string]struct{}{
"audio/flac": {},
"audio/mpeg": {},
"audio/ogg": {},
}
// SupportedMimeTypes is the union of both preview sets; the generator gates on it.
var SupportedMimeTypes = func() map[string]struct{} {
m := make(map[string]struct{}, len(UnconditionalPreviewMimeTypes)+len(EmbeddedPreviewMimeTypes))
for k := range UnconditionalPreviewMimeTypes {
m[k] = struct{}{}
}
for k := range EmbeddedPreviewMimeTypes {
m[k] = struct{}{}
}
return m
}()
@@ -2,22 +2,18 @@
package thumbnail
var (
// SupportedMimeTypes contains an all mimetypes which are supported by the thumbnailer.
SupportedMimeTypes = map[string]struct{}{
"image/png": {},
"image/jpg": {},
"image/jpeg": {},
"image/gif": {},
"image/bmp": {},
"image/x-ms-bmp": {},
"image/tiff": {},
"text/plain": {},
"audio/flac": {},
"audio/mpeg": {},
"audio/ogg": {},
"application/vnd.geogebra.slides": {},
"application/vnd.geogebra.pinboard": {},
"image/webp": {},
}
)
// UnconditionalPreviewMimeTypes always have a preview: the thumbnailer renders
// one from the content.
var UnconditionalPreviewMimeTypes = map[string]struct{}{
"image/png": {},
"image/jpg": {},
"image/jpeg": {},
"image/gif": {},
"image/bmp": {},
"image/x-ms-bmp": {},
"image/tiff": {},
"image/webp": {},
"text/plain": {},
"application/vnd.geogebra.slides": {},
"application/vnd.geogebra.pinboard": {},
}
@@ -0,0 +1,13 @@
package thumbnail_test
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestThumbnail(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Thumbnail Suite")
}
+1 -2
View File
@@ -222,8 +222,7 @@ func matchToPropResponse(ctx context.Context, davPrefix, publicURL string, match
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:permissions", match.Entity.Permissions))
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:highlights", match.Entity.Highlights))
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("d:getcontenttype", match.Entity.MimeType))
_, isSupportedMimeType := thumbnail.SupportedMimeTypes[match.Entity.MimeType]
if isSupportedMimeType {
if thumbnail.HasPreviewForMimeType(match.Entity.MimeType, match.Entity.GetPreview() != nil) {
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:has-preview", "1"))
} else {
propstatOK.Prop = append(propstatOK.Prop, prop.Escaped("oc:has-preview", "0"))