diff --git a/pkg/conversions/strings.go b/pkg/conversions/strings.go index 63cc68f2a7..a03b7d963b 100644 --- a/pkg/conversions/strings.go +++ b/pkg/conversions/strings.go @@ -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) +} diff --git a/protogen/gen/opencloud/messages/search/v0/search.pb.go b/protogen/gen/opencloud/messages/search/v0/search.pb.go index 0a8bf9c1c2..88290ad957 100644 --- a/protogen/gen/opencloud/messages/search/v0/search.pb.go +++ b/protogen/gen/opencloud/messages/search/v0/search.pb.go @@ -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, }, diff --git a/protogen/gen/opencloud/services/search/v0/search.swagger.json b/protogen/gen/opencloud/services/search/v0/search.swagger.json index 43ff1e2c79..86d46d57d1 100644 --- a/protogen/gen/opencloud/services/search/v0/search.swagger.json +++ b/protogen/gen/opencloud/services/search/v0/search.swagger.json @@ -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": { diff --git a/protogen/proto/opencloud/messages/search/v0/search.proto b/protogen/proto/opencloud/messages/search/v0/search.proto index a2a25a8d5e..41bcb5136b 100644 --- a/protogen/proto/opencloud/messages/search/v0/search.proto +++ b/protogen/proto/opencloud/messages/search/v0/search.proto @@ -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 { diff --git a/services/graph/pkg/service/v0/thumbnails.go b/services/graph/pkg/service/v0/thumbnails.go index 75d28bd3a7..357647fcf1 100644 --- a/services/graph/pkg/service/v0/thumbnails.go +++ b/services/graph/pkg/service/v0/thumbnails.go @@ -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} diff --git a/services/search/pkg/bleve/backend.go b/services/search/pkg/bleve/backend.go index 7fc6fe5bfa..6da56dd018 100644 --- a/services/search/pkg/bleve/backend.go +++ b/services/search/pkg/bleve/backend.go @@ -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"), diff --git a/services/search/pkg/bleve/testdata/mapping.golden.json b/services/search/pkg/bleve/testdata/mapping.golden.json index 61ea6474f1..5f12e2b398 100644 --- a/services/search/pkg/bleve/testdata/mapping.golden.json +++ b/services/search/pkg/bleve/testdata/mapping.golden.json @@ -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, diff --git a/services/search/pkg/content/content.go b/services/search/pkg/content/content.go index 6a945b0fd3..d645a90bb2 100644 --- a/services/search/pkg/content/content.go +++ b/services/search/pkg/content/content.go @@ -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)) } diff --git a/services/search/pkg/content/tika.go b/services/search/pkg/content/tika.go index 7b918e9ab9..34e68a6a47 100644 --- a/services/search/pkg/content/tika.go +++ b/services/search/pkg/content/tika.go @@ -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) } diff --git a/services/search/pkg/content/tika_preview.go b/services/search/pkg/content/tika_preview.go new file mode 100644 index 0000000000..76a238e562 --- /dev/null +++ b/services/search/pkg/content/tika_preview.go @@ -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 +} diff --git a/services/search/pkg/content/tika_preview_test.go b/services/search/pkg/content/tika_preview_test.go new file mode 100644 index 0000000000..f57055d768 --- /dev/null +++ b/services/search/pkg/content/tika_preview_test.go @@ -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()) + }) +}) diff --git a/services/search/pkg/content/tika_test.go b/services/search/pkg/content/tika_test.go index 5a224a9e33..fc0c3e41ec 100644 --- a/services/search/pkg/content/tika_test.go +++ b/services/search/pkg/content/tika_test.go @@ -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() { diff --git a/services/search/pkg/opensearch/internal/convert/opensearch.go b/services/search/pkg/opensearch/internal/convert/opensearch.go index 81d1b347e2..747f1dd856 100644 --- a/services/search/pkg/opensearch/internal/convert/opensearch.go +++ b/services/search/pkg/opensearch/internal/convert/opensearch.go @@ -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), diff --git a/services/search/pkg/opensearch/testdata/resource.golden.json b/services/search/pkg/opensearch/testdata/resource.golden.json index ab343eef11..584ca6ea6d 100644 --- a/services/search/pkg/opensearch/testdata/resource.golden.json +++ b/services/search/pkg/opensearch/testdata/resource.golden.json @@ -283,6 +283,16 @@ } } }, + "preview": { + "properties": { + "height": { + "type": "integer" + }, + "width": { + "type": "integer" + } + } + }, "video": { "properties": { "audioBitsPerSample": { diff --git a/services/search/pkg/search/service.go b/services/search/pkg/search/service.go index 47caee4070..b39d64ce52 100644 --- a/services/search/pkg/search/service.go +++ b/services/search/pkg/search/service.go @@ -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 } diff --git a/services/thumbnails/pkg/thumbnail/has_preview.go b/services/thumbnails/pkg/thumbnail/has_preview.go new file mode 100644 index 0000000000..063f53e2bc --- /dev/null +++ b/services/thumbnails/pkg/thumbnail/has_preview.go @@ -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 +} diff --git a/services/thumbnails/pkg/thumbnail/has_preview_test.go b/services/thumbnails/pkg/thumbnail/has_preview_test.go new file mode 100644 index 0000000000..271e091cca --- /dev/null +++ b/services/thumbnails/pkg/thumbnail/has_preview_test.go @@ -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)) + } + }) +}) diff --git a/services/thumbnails/pkg/thumbnail/mimetypes.go b/services/thumbnails/pkg/thumbnail/mimetypes.go index 02a91e22f5..390cec01c7 100644 --- a/services/thumbnails/pkg/thumbnail/mimetypes.go +++ b/services/thumbnails/pkg/thumbnail/mimetypes.go @@ -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": {}, +} diff --git a/services/thumbnails/pkg/thumbnail/mimetypes_common.go b/services/thumbnails/pkg/thumbnail/mimetypes_common.go new file mode 100644 index 0000000000..96f047c713 --- /dev/null +++ b/services/thumbnails/pkg/thumbnail/mimetypes_common.go @@ -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 +}() diff --git a/services/thumbnails/pkg/thumbnail/mimetypes_vips.go b/services/thumbnails/pkg/thumbnail/mimetypes_vips.go index b94fafafaf..929718e7ad 100644 --- a/services/thumbnails/pkg/thumbnail/mimetypes_vips.go +++ b/services/thumbnails/pkg/thumbnail/mimetypes_vips.go @@ -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": {}, +} diff --git a/services/thumbnails/pkg/thumbnail/thumbnail_suite_test.go b/services/thumbnails/pkg/thumbnail/thumbnail_suite_test.go new file mode 100644 index 0000000000..7217b6a146 --- /dev/null +++ b/services/thumbnails/pkg/thumbnail/thumbnail_suite_test.go @@ -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") +} diff --git a/services/webdav/pkg/service/v0/search.go b/services/webdav/pkg/service/v0/search.go index e2f6a3f4a4..8da71806d9 100644 --- a/services/webdav/pkg/service/v0/search.go +++ b/services/webdav/pkg/service/v0/search.go @@ -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"))