diff --git a/changelog/unreleased/thumbnail-processors.md b/changelog/unreleased/thumbnail-processors.md new file mode 100644 index 00000000000..fcd49ab21a1 --- /dev/null +++ b/changelog/unreleased/thumbnail-processors.md @@ -0,0 +1,22 @@ +Enhancement: Thumbnail generation with image processors + +Thumbnails can now be changed during creation, previously the images were always scaled to fit the given frame, +but it could happen that the images were cut off because they could not be placed better due to the aspect ratio. + +This pr introduces the possibility of specifying how the behavior should be, following processors are available + +* resize +* fit +* fill +* thumbnail + +the processor can be applied by adding the processor query param to the request, e.g. `processor=fit`, `processor=fill`, ... + +to find out more how the individual processors work please read https://github.com/disintegration/imaging + +if no processor is provided it behaves the same as before (resize for gif's and thumbnail for all other) + +https://github.com/owncloud/ocis/pull/7409 +https://github.com/owncloud/enterprise/issues/6057 +https://github.com/owncloud/ocis/issues/5179 +https://github.com/owncloud/web/issues/7728 diff --git a/protogen/gen/ocis/services/thumbnails/v0/thumbnails.pb.go b/protogen/gen/ocis/services/thumbnails/v0/thumbnails.pb.go index 25633f26e52..1b0aa92c6d3 100644 --- a/protogen/gen/ocis/services/thumbnails/v0/thumbnails.pb.go +++ b/protogen/gen/ocis/services/thumbnails/v0/thumbnails.pb.go @@ -36,6 +36,8 @@ type GetThumbnailRequest struct { Width int32 `protobuf:"varint,3,opt,name=width,proto3" json:"width,omitempty"` // The height of the thumbnail Height int32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` + // Indicates which processor should be used + Processor string `protobuf:"bytes,5,opt,name=processor,proto3" json:"processor,omitempty"` // Types that are assignable to Source: // // *GetThumbnailRequest_WebdavSource @@ -103,6 +105,13 @@ func (x *GetThumbnailRequest) GetHeight() int32 { return 0 } +func (x *GetThumbnailRequest) GetProcessor() string { + if x != nil { + return x.Processor + } + return "" +} + func (m *GetThumbnailRequest) GetSource() isGetThumbnailRequest_Source { if m != nil { return m.Source @@ -129,11 +138,11 @@ type isGetThumbnailRequest_Source interface { } type GetThumbnailRequest_WebdavSource struct { - WebdavSource *v0.WebdavSource `protobuf:"bytes,5,opt,name=webdav_source,json=webdavSource,proto3,oneof"` + WebdavSource *v0.WebdavSource `protobuf:"bytes,6,opt,name=webdav_source,json=webdavSource,proto3,oneof"` } type GetThumbnailRequest_Cs3Source struct { - Cs3Source *v0.CS3Source `protobuf:"bytes,6,opt,name=cs3_source,json=cs3Source,proto3,oneof"` + Cs3Source *v0.CS3Source `protobuf:"bytes,7,opt,name=cs3_source,json=cs3Source,proto3,oneof"` } func (*GetThumbnailRequest_WebdavSource) isGetThumbnailRequest_Source() {} @@ -220,7 +229,7 @@ var file_ocis_services_thumbnails_v0_thumbnails_proto_rawDesc = []byte{ 0x69, 0x6c, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd7, 0x02, 0x0a, 0x13, 0x47, 0x65, + 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf5, 0x02, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x12, 0x51, 0x0a, @@ -231,58 +240,59 @@ var file_ocis_services_thumbnails_v0_thumbnails_proto_rawDesc = []byte{ 0x65, 0x52, 0x0d, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x77, 0x69, 0x64, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x50, - 0x0a, 0x0d, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, - 0x2e, 0x76, 0x30, 0x2e, 0x57, 0x65, 0x62, 0x64, 0x61, 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x48, 0x00, 0x52, 0x0c, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x12, 0x47, 0x0a, 0x0a, 0x63, 0x73, 0x33, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, - 0x76, 0x30, 0x2e, 0x43, 0x53, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, - 0x63, 0x73, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x22, 0x7e, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, - 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, - 0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, - 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, - 0x79, 0x70, 0x65, 0x32, 0x87, 0x01, 0x0a, 0x10, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, - 0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, - 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x30, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c, + 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x50, 0x0a, 0x0d, + 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, + 0x30, 0x2e, 0x57, 0x65, 0x62, 0x64, 0x61, 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, + 0x52, 0x0c, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x47, + 0x0a, 0x0a, 0x63, 0x73, 0x33, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, + 0x2e, 0x43, 0x53, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x63, 0x73, + 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x22, 0x7e, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, + 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x61, 0x74, + 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x25, + 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, 0x79, 0x70, + 0x65, 0x32, 0x87, 0x01, 0x0a, 0x10, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, + 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x30, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, + 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, - 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6f, 0x63, 0x69, - 0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, - 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, - 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xe9, 0x02, - 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, - 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, - 0x2f, 0x76, 0x30, 0x92, 0x41, 0xa2, 0x02, 0x12, 0xb8, 0x01, 0x0a, 0x22, 0x6f, 0x77, 0x6e, 0x43, - 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x53, 0x63, - 0x61, 0x6c, 0x65, 0x20, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x47, - 0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12, - 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, - 0x73, 0x1a, 0x14, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x77, 0x6e, 0x63, 0x6c, - 0x6f, 0x75, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x2a, 0x42, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, - 0x65, 0x2d, 0x32, 0x2e, 0x30, 0x12, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, - 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, - 0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x30, - 0x2e, 0x30, 0x2a, 0x02, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3d, 0x0a, 0x10, 0x44, 0x65, - 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x29, - 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, - 0x2e, 0x64, 0x65, 0x76, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x68, - 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xe9, 0x02, 0x5a, 0x41, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, + 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x67, 0x65, + 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x2f, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2f, 0x76, + 0x30, 0x92, 0x41, 0xa2, 0x02, 0x12, 0xb8, 0x01, 0x0a, 0x22, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, + 0x75, 0x64, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x53, 0x63, 0x61, 0x6c, + 0x65, 0x20, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x47, 0x0a, 0x0d, + 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12, 0x20, 0x68, + 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x1a, + 0x14, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, + 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x2a, 0x42, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2d, + 0x32, 0x2e, 0x30, 0x12, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, + 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x30, 0x2e, 0x30, + 0x2a, 0x02, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3d, 0x0a, 0x10, 0x44, 0x65, 0x76, 0x65, + 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x29, 0x68, 0x74, + 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x64, + 0x65, 0x76, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x68, 0x75, 0x6d, + 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/protogen/proto/ocis/services/thumbnails/v0/thumbnails.proto b/protogen/proto/ocis/services/thumbnails/v0/thumbnails.proto index 03c1dba60c0..f3499e9e91f 100644 --- a/protogen/proto/ocis/services/thumbnails/v0/thumbnails.proto +++ b/protogen/proto/ocis/services/thumbnails/v0/thumbnails.proto @@ -47,9 +47,11 @@ message GetThumbnailRequest { int32 width = 3; // The height of the thumbnail int32 height = 4; + // Indicates which image processor to use + string processor = 5; oneof source { - ocis.messages.thumbnails.v0.WebdavSource webdav_source = 5; - ocis.messages.thumbnails.v0.CS3Source cs3_source = 6; + ocis.messages.thumbnails.v0.WebdavSource webdav_source = 6; + ocis.messages.thumbnails.v0.CS3Source cs3_source = 7; } } diff --git a/services/thumbnails/README.md b/services/thumbnails/README.md index 43dbc463d08..2d39563ee14 100644 --- a/services/thumbnails/README.md +++ b/services/thumbnails/README.md @@ -10,9 +10,9 @@ The relevant environment variables defining file locations are: - (2) `STORAGE_USERS_OCIS_ROOT` - (3) `THUMBNAILS_FILESYSTEMSTORAGE_ROOT` -(1) ... Having a default set by the Infinite Scale code, but if defined, used as base path for other services. -(2) ... Source files, defaults to (1) plus path component, but can be freely defined if required. -(3) ... Target files, defaults to (1) plus path component, but can be freely defined if required. +(1) ... Having a default set by the Infinite Scale code, but if defined, used as base path for other services. +(2) ... Source files, defaults to (1) plus path component, but can be freely defined if required. +(3) ... Target files, defaults to (1) plus path component, but can be freely defined if required. For details and defaults for these environment variables see the ocis admin documentation. @@ -45,9 +45,20 @@ Various resolutions can be defined via `THUMBNAILS_RESOLUTIONS`. A requestor can Example: -Requested: 18x12 -Available: 30x20, 15x10, 9x6 -Returned: 15x10 +Requested: 18x12 +Available: 30x20, 15x10, 9x6 +Returned: 15x10 + +## Thumbnail Processors + +Image generation can be configured by defining different processors, following processors are available: + +* `resize` +* `fit` +* `fill` +* `thumbnail` + +To apply one of those, a query parameter has to be added to the request, e.g. `?processor=fit` ## Deleting Thumbnails diff --git a/services/thumbnails/pkg/service/grpc/v0/service.go b/services/thumbnails/pkg/service/grpc/v0/service.go index 070613b0c03..fd97bfd897e 100644 --- a/services/thumbnails/pkg/service/grpc/v0/service.go +++ b/services/thumbnails/pkg/service/grpc/v0/service.go @@ -16,6 +16,10 @@ import ( "github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/utils" "github.com/golang-jwt/jwt/v4" + "github.com/pkg/errors" + merrors "go-micro.dev/v4/errors" + "google.golang.org/grpc/metadata" + "github.com/owncloud/ocis/v2/ocis-pkg/log" thumbnailssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/thumbnails/v0" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/preprocessor" @@ -23,9 +27,6 @@ import ( tjwt "github.com/owncloud/ocis/v2/services/thumbnails/pkg/service/jwt" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail/imgsource" - "github.com/pkg/errors" - merrors "go-micro.dev/v4/errors" - "google.golang.org/grpc/metadata" ) // NewService returns a service implementation for Service. @@ -124,7 +125,7 @@ func (g Thumbnail) handleCS3Source(ctx context.Context, req *thumbnailssvc.GetTh if tType == "" { tType = req.GetThumbnailType().String() } - tr, err := thumbnail.PrepareRequest(int(req.Width), int(req.Height), tType, sRes.GetInfo().GetChecksum().GetSum()) + tr, err := thumbnail.PrepareRequest(int(req.Width), int(req.Height), tType, sRes.GetInfo().GetChecksum().GetSum(), req.Processor) if err != nil { return "", merrors.BadRequest(g.serviceID, err.Error()) } @@ -207,7 +208,7 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *thumbnailssvc.Ge if tType == "" { tType = req.GetThumbnailType().String() } - tr, err := thumbnail.PrepareRequest(int(req.Width), int(req.Height), tType, sRes.GetInfo().GetChecksum().GetSum()) + tr, err := thumbnail.PrepareRequest(int(req.Width), int(req.Height), tType, sRes.GetInfo().GetChecksum().GetSum(), req.Processor) if err != nil { return "", merrors.BadRequest(g.serviceID, err.Error()) } diff --git a/services/thumbnails/pkg/thumbnail/encoding.go b/services/thumbnails/pkg/thumbnail/encoding.go index 5126fd6af9b..96f71074439 100644 --- a/services/thumbnails/pkg/thumbnail/encoding.go +++ b/services/thumbnails/pkg/thumbnail/encoding.go @@ -114,8 +114,8 @@ func EncoderForType(fileType string) (Encoder, error) { } // GetExtForMime return the supported extension by mime -func GetExtForMime(mime string) string { - ext := strings.TrimPrefix(strings.TrimSpace(strings.ToLower(mime)), "image/") +func GetExtForMime(fileType string) string { + ext := strings.TrimPrefix(strings.TrimSpace(strings.ToLower(fileType)), "image/") switch ext { case typeJpg, typeJpeg, typePng, typeGif: return ext diff --git a/services/thumbnails/pkg/thumbnail/generator.go b/services/thumbnails/pkg/thumbnail/generator.go index b22c15da458..e8d967c4eb9 100644 --- a/services/thumbnails/pkg/thumbnail/generator.go +++ b/services/thumbnails/pkg/thumbnail/generator.go @@ -1,7 +1,6 @@ package thumbnail import ( - "errors" "image" "image/color" "image/draw" @@ -11,36 +10,34 @@ import ( "github.com/disintegration/imaging" ) -var ( - // ErrInvalidType represents the error when a type can't be encoded. - ErrInvalidType2 = errors.New("can't encode this type") - // ErrNoGeneratorForType represents the error when no generator could be found for a type. - ErrNoGeneratorForType = errors.New("no generator for this type found") -) - +// Generator generates a web friendly file version. type Generator interface { - GenerateThumbnail(image.Rectangle, interface{}) (interface{}, error) + Generate(image.Rectangle, interface{}, Processor) (interface{}, error) } +// SimpleGenerator is the default image generator and is used for all image types expect gif. type SimpleGenerator struct{} -func (g SimpleGenerator) GenerateThumbnail(size image.Rectangle, img interface{}) (interface{}, error) { +// Generate generates a alternative image version. +func (g SimpleGenerator) Generate(size image.Rectangle, img interface{}, processor Processor) (interface{}, error) { m, ok := img.(image.Image) if !ok { - return nil, ErrInvalidType2 + return nil, ErrInvalidType } - return imaging.Thumbnail(m, size.Dx(), size.Dy(), imaging.Lanczos), nil + return processor.Process(m, size.Dx(), size.Dy(), imaging.Lanczos), nil } +// GifGenerator is used to create a web friendly version of the provided gif image. type GifGenerator struct{} -func (g GifGenerator) GenerateThumbnail(size image.Rectangle, img interface{}) (interface{}, error) { +// Generate generates a alternative gif version. +func (g GifGenerator) Generate(size image.Rectangle, img interface{}, processor Processor) (interface{}, error) { // Code inspired by https://github.com/willnorris/gifresize/blob/db93a7e1dcb1c279f7eeb99cc6d90b9e2e23e871/gifresize.go m, ok := img.(*gif.GIF) if !ok { - return nil, ErrInvalidType2 + return nil, ErrInvalidType } // Create a new RGBA image to hold the incremental frames. srcX, srcY := m.Config.Width, m.Config.Height @@ -51,8 +48,8 @@ func (g GifGenerator) GenerateThumbnail(size image.Rectangle, img interface{}) ( bounds := frame.Bounds() prev := tmp draw.Draw(tmp, bounds, frame, bounds.Min, draw.Over) - scaled := imaging.Resize(tmp, size.Dx(), size.Dy(), imaging.Lanczos) - m.Image[i] = g.imageToPaletted(scaled, frame.Palette) + processed := processor.Process(tmp, size.Dx(), size.Dy(), imaging.Lanczos) + m.Image[i] = g.imageToPaletted(processed, frame.Palette) switch m.Disposal[i] { case gif.DisposalBackground: diff --git a/services/thumbnails/pkg/thumbnail/processor.go b/services/thumbnails/pkg/thumbnail/processor.go new file mode 100644 index 00000000000..3782fac4fce --- /dev/null +++ b/services/thumbnails/pkg/thumbnail/processor.go @@ -0,0 +1,51 @@ +package thumbnail + +import ( + "image" + "strings" + + "github.com/disintegration/imaging" +) + +// Processor processes the thumbnail by applying different transformations to it. +type Processor interface { + ID() string + Process(img image.Image, width, height int, filter imaging.ResampleFilter) *image.NRGBA +} + +// DefinableProcessor is the most simple processor, it holds a replaceable image converter function. +type DefinableProcessor struct { + Slug string + Converter func(img image.Image, width, height int, filter imaging.ResampleFilter) *image.NRGBA +} + +// ID returns the processor identification. +func (p DefinableProcessor) ID() string { return p.Slug } + +// Process transforms the given image. +func (p DefinableProcessor) Process(img image.Image, width, height int, filter imaging.ResampleFilter) *image.NRGBA { + return p.Converter(img, width, height, filter) +} + +// ProcessorFor returns a matching Processor +func ProcessorFor(id, fileType string) (DefinableProcessor, error) { + switch strings.ToLower(id) { + case "fit": + return DefinableProcessor{Slug: strings.ToLower(id), Converter: imaging.Fit}, nil + case "resize": + return DefinableProcessor{Slug: strings.ToLower(id), Converter: imaging.Resize}, nil + case "fill": + return DefinableProcessor{Slug: strings.ToLower(id), Converter: func(img image.Image, width, height int, filter imaging.ResampleFilter) *image.NRGBA { + return imaging.Fill(img, width, height, imaging.Center, filter) + }}, nil + case "thumbnail": + return DefinableProcessor{Slug: strings.ToLower(id), Converter: imaging.Thumbnail}, nil + default: + switch strings.ToLower(fileType) { + case typeGif: + return DefinableProcessor{Converter: imaging.Resize}, nil + default: + return DefinableProcessor{Converter: imaging.Thumbnail}, nil + } + } +} diff --git a/services/thumbnails/pkg/thumbnail/processor_test.go b/services/thumbnails/pkg/thumbnail/processor_test.go new file mode 100644 index 00000000000..febda33b817 --- /dev/null +++ b/services/thumbnails/pkg/thumbnail/processor_test.go @@ -0,0 +1,98 @@ +package thumbnail_test + +import ( + "testing" + + "github.com/disintegration/imaging" + tAssert "github.com/stretchr/testify/assert" + + "github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail" +) + +func TestProcessorFor(t *testing.T) { + tests := []struct { + id string + fileType string + wantP thumbnail.Processor + wantE error + }{ + { + id: "fit", + fileType: "", + wantP: thumbnail.DefinableProcessor{Slug: "fit", Converter: imaging.Fit}, + wantE: nil, + }, + { + id: "fit", + fileType: "jpg", + wantP: thumbnail.DefinableProcessor{Slug: "fit"}, + wantE: nil, + }, + { + id: "FIT", + fileType: "jpg", + wantP: thumbnail.DefinableProcessor{Slug: "fit"}, + wantE: nil, + }, + { + id: "resize", + fileType: "jpg", + wantP: thumbnail.DefinableProcessor{Slug: "resize"}, + wantE: nil, + }, + { + id: "RESIZE", + fileType: "jpg", + wantP: thumbnail.DefinableProcessor{Slug: "resize"}, + wantE: nil, + }, + { + id: "fill", + fileType: "jpg", + wantP: thumbnail.DefinableProcessor{Slug: "fill"}, + wantE: nil, + }, + { + id: "FILL", + fileType: "jpg", + wantP: thumbnail.DefinableProcessor{Slug: "fill"}, + wantE: nil, + }, + { + id: "thumbnail", + fileType: "jpg", + wantP: thumbnail.DefinableProcessor{Slug: "thumbnail"}, + wantE: nil, + }, + { + id: "THUMBNAIL", + fileType: "jpg", + wantP: thumbnail.DefinableProcessor{Slug: "thumbnail"}, + wantE: nil, + }, + { + id: "", + fileType: "jpg", + wantP: thumbnail.DefinableProcessor{}, + wantE: nil, + }, + { + id: "", + fileType: "gif", + wantP: thumbnail.DefinableProcessor{}, + wantE: nil, + }, + } + + assert := tAssert.New(t) + + for _, tt := range tests { + tt := tt + t.Run("", func(t *testing.T) { + p, e := thumbnail.ProcessorFor(tt.id, tt.fileType) + assert.Equal(p.ID(), tt.wantP.ID()) + assert.Equal(e, tt.wantE) + }) + } + +} diff --git a/services/thumbnails/pkg/thumbnail/storage/filesystem.go b/services/thumbnails/pkg/thumbnail/storage/filesystem.go index 692669d3024..dd18e88f577 100644 --- a/services/thumbnails/pkg/thumbnail/storage/filesystem.go +++ b/services/thumbnails/pkg/thumbnail/storage/filesystem.go @@ -5,10 +5,12 @@ import ( "os" "path/filepath" "strconv" + "strings" + + "github.com/pkg/errors" "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/config" - "github.com/pkg/errors" ) const ( @@ -82,7 +84,14 @@ func (s FileSystem) Put(key string, img []byte) error { func (s FileSystem) BuildKey(r Request) string { checksum := r.Checksum filetype := r.Types[0] - filename := strconv.Itoa(r.Resolution.Dx()) + "x" + strconv.Itoa(r.Resolution.Dy()) + "." + filetype - return filepath.Join(checksum[:2], checksum[2:4], checksum[4:], filename) + parts := []string{strconv.Itoa(r.Resolution.Dx()), "x", strconv.Itoa(r.Resolution.Dy())} + + if r.Characteristic != "" { + parts = append(parts, "-", r.Characteristic) + } + + parts = append(parts, ".", filetype) + + return filepath.Join(checksum[:2], checksum[2:4], checksum[4:], strings.Join(parts, "")) } diff --git a/services/thumbnails/pkg/thumbnail/storage/filesystem_test.go b/services/thumbnails/pkg/thumbnail/storage/filesystem_test.go new file mode 100644 index 00000000000..a139a9199b1 --- /dev/null +++ b/services/thumbnails/pkg/thumbnail/storage/filesystem_test.go @@ -0,0 +1,65 @@ +package storage_test + +import ( + "image" + "testing" + + tAssert "github.com/stretchr/testify/assert" + + "github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail/storage" +) + +func TestFileSystem_BuildKey(t *testing.T) { + tests := []struct { + r storage.Request + want string + }{ + { + r: storage.Request{ + Checksum: "120EA8A25E5D487BF68B5F7096440019", + Types: []string{"png", "jpg"}, + Resolution: image.Rectangle{ + Min: image.Point{ + X: 1, + Y: 2, + }, + Max: image.Point{ + X: 3, + Y: 4, + }, + }, + Characteristic: "", + }, + want: "12/0E/A8A25E5D487BF68B5F7096440019/2x2.png", + }, + { + r: storage.Request{ + Checksum: "120EA8A25E5D487BF68B5F7096440019", + Types: []string{"png", "jpg"}, + Resolution: image.Rectangle{ + Min: image.Point{ + X: 1, + Y: 2, + }, + Max: image.Point{ + X: 3, + Y: 4, + }, + }, + Characteristic: "fill", + }, + want: "12/0E/A8A25E5D487BF68B5F7096440019/2x2-fill.png", + }, + } + + s := storage.FileSystem{} + assert := tAssert.New(t) + + for _, tt := range tests { + tt := tt + t.Run("", func(t *testing.T) { + assert.Equal(s.BuildKey(tt.r), tt.want) + }) + } + +} diff --git a/services/thumbnails/pkg/thumbnail/storage/inmemory.go b/services/thumbnails/pkg/thumbnail/storage/inmemory.go index 1c7befd888e..17683185181 100644 --- a/services/thumbnails/pkg/thumbnail/storage/inmemory.go +++ b/services/thumbnails/pkg/thumbnail/storage/inmemory.go @@ -38,7 +38,13 @@ func (s InMemory) BuildKey(r Request) string { parts := []string{ r.Checksum, r.Resolution.String(), - strings.Join(r.Types, ","), } + + if r.Characteristic != "" { + parts = append(parts, r.Characteristic) + } + + parts = append(parts, strings.Join(r.Types, ",")) + return strings.Join(parts, "+") } diff --git a/services/thumbnails/pkg/thumbnail/storage/inmemory_test.go b/services/thumbnails/pkg/thumbnail/storage/inmemory_test.go new file mode 100644 index 00000000000..9f33a6ae494 --- /dev/null +++ b/services/thumbnails/pkg/thumbnail/storage/inmemory_test.go @@ -0,0 +1,65 @@ +package storage_test + +import ( + "image" + "testing" + + tAssert "github.com/stretchr/testify/assert" + + "github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail/storage" +) + +func TestInMemory_BuildKey(t *testing.T) { + tests := []struct { + r storage.Request + want string + }{ + { + r: storage.Request{ + Checksum: "cs", + Types: []string{"png", "jpg"}, + Resolution: image.Rectangle{ + Min: image.Point{ + X: 1, + Y: 2, + }, + Max: image.Point{ + X: 3, + Y: 4, + }, + }, + Characteristic: "", + }, + want: "cs+(1,2)-(3,4)+png,jpg", + }, + { + r: storage.Request{ + Checksum: "cs", + Types: []string{"png", "jpg"}, + Resolution: image.Rectangle{ + Min: image.Point{ + X: 1, + Y: 2, + }, + Max: image.Point{ + X: 3, + Y: 4, + }, + }, + Characteristic: "fill", + }, + want: "cs+(1,2)-(3,4)+fill+png,jpg", + }, + } + + s := storage.InMemory{} + assert := tAssert.New(t) + + for _, tt := range tests { + tt := tt + t.Run("", func(t *testing.T) { + assert.Equal(s.BuildKey(tt.r), tt.want) + }) + } + +} diff --git a/services/thumbnails/pkg/thumbnail/storage/storage.go b/services/thumbnails/pkg/thumbnail/storage/storage.go index 691fdca5af3..591b05bdc12 100644 --- a/services/thumbnails/pkg/thumbnail/storage/storage.go +++ b/services/thumbnails/pkg/thumbnail/storage/storage.go @@ -15,6 +15,12 @@ type Request struct { Types []string // The resolution of the thumbnail Resolution image.Rectangle + // Characteristic defines the different image characteristics, + // for example, if its scaled up to fit in the bounding box or not, + // is it a chroma version of the image, and so on... + // the main propose for this is to be able to differentiate between images which have + // the same resolution but different characteristics. + Characteristic string } // Storage defines the interface for a thumbnail store. diff --git a/services/thumbnails/pkg/thumbnail/thumbnail.go b/services/thumbnails/pkg/thumbnail/thumbnail.go index 70fcdc5b20e..b3de987709d 100644 --- a/services/thumbnails/pkg/thumbnail/thumbnail.go +++ b/services/thumbnails/pkg/thumbnail/thumbnail.go @@ -30,6 +30,7 @@ type Request struct { Encoder Encoder Generator Generator Checksum string + Processor Processor } // Manager is responsible for generating thumbnails @@ -69,7 +70,7 @@ func (s SimpleManager) Generate(r Request, img interface{}) (string, error) { match = s.resolutions.ClosestMatch(r.Resolution, m.Bounds()) } - thumbnail, err := r.Generator.GenerateThumbnail(match, img) + thumbnail, err := r.Generator.Generate(match, img, r.Processor) if err != nil { return "", err } @@ -98,9 +99,10 @@ func (s SimpleManager) GetThumbnail(key string) ([]byte, error) { func mapToStorageRequest(r Request) storage.Request { return storage.Request{ - Checksum: r.Checksum, - Resolution: r.Resolution, - Types: r.Encoder.Types(), + Checksum: r.Checksum, + Resolution: r.Resolution, + Types: r.Encoder.Types(), + Characteristic: r.Processor.ID(), } } @@ -115,7 +117,7 @@ func IsMimeTypeSupported(m string) bool { } // PrepareRequest prepare the request based on image parameters -func PrepareRequest(width, height int, tType, checksum string) (Request, error) { +func PrepareRequest(width, height int, tType, checksum, pID string) (Request, error) { generator, err := GeneratorForType(tType) if err != nil { return Request{}, err @@ -124,11 +126,16 @@ func PrepareRequest(width, height int, tType, checksum string) (Request, error) if err != nil { return Request{}, err } + processor, err := ProcessorFor(pID, tType) + if err != nil { + return Request{}, err + } return Request{ Resolution: image.Rect(0, 0, width, height), Generator: generator, Encoder: encoder, Checksum: checksum, + Processor: processor, }, nil } diff --git a/services/thumbnails/pkg/thumbnail/thumbnail_test.go b/services/thumbnails/pkg/thumbnail/thumbnail_test.go index 45303dcc871..fc4c9796429 100644 --- a/services/thumbnails/pkg/thumbnail/thumbnail_test.go +++ b/services/thumbnails/pkg/thumbnail/thumbnail_test.go @@ -4,9 +4,11 @@ import ( "image" "os" "path/filepath" - "reflect" "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail/storage" ) @@ -118,13 +120,15 @@ func TestPrepareRequest(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := PrepareRequest(tt.args.width, tt.args.height, tt.args.tType, tt.args.checksum) + got, err := PrepareRequest(tt.args.width, tt.args.height, tt.args.tType, tt.args.checksum, "") if (err != nil) != tt.wantErr { t.Errorf("PrepareRequest() error = %v, wantErr %v", err, tt.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("PrepareRequest() got = %v, want %v", got, tt.want) + + // func's are not reflactable, ignore + if diff := cmp.Diff(tt.want, got, cmpopts.IgnoreFields(Request{}, "Processor")); diff != "" { + t.Errorf("PrepareRequest(): %v", diff) } }) } diff --git a/services/webdav/pkg/dav/requests/thumbnail.go b/services/webdav/pkg/dav/requests/thumbnail.go index 0c703d37253..2a5aa7da57f 100644 --- a/services/webdav/pkg/dav/requests/thumbnail.go +++ b/services/webdav/pkg/dav/requests/thumbnail.go @@ -36,6 +36,8 @@ type ThumbnailRequest struct { Height int32 // In case of a public share the public link token. PublicLinkToken string + // Indicates which image processor to use + Processor string // The Identifier from the requested URL Identifier string } @@ -73,6 +75,7 @@ func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) { Extension: filepath.Ext(fp), Width: int32(width), Height: int32(height), + Processor: q.Get("processor"), PublicLinkToken: chi.URLParam(r, "token"), Identifier: id, }, nil diff --git a/services/webdav/pkg/service/v0/service.go b/services/webdav/pkg/service/v0/service.go index 4c1535f3fec..b1655c2b8c0 100644 --- a/services/webdav/pkg/service/v0/service.go +++ b/services/webdav/pkg/service/v0/service.go @@ -243,6 +243,7 @@ func (g Webdav) SpacesThumbnail(w http.ResponseWriter, r *http.Request) { ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), Width: tr.Width, Height: tr.Height, + Processor: tr.Processor, Source: &thumbnailssvc.GetThumbnailRequest_Cs3Source{ Cs3Source: &thumbnailsmsg.CS3Source{ Path: fullPath, @@ -335,6 +336,7 @@ func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) { ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), Width: tr.Width, Height: tr.Height, + Processor: tr.Processor, Source: &thumbnailssvc.GetThumbnailRequest_Cs3Source{ Cs3Source: &thumbnailsmsg.CS3Source{ Path: fullPath, @@ -375,6 +377,7 @@ func (g Webdav) PublicThumbnail(w http.ResponseWriter, r *http.Request) { ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), Width: tr.Width, Height: tr.Height, + Processor: tr.Processor, Source: &thumbnailssvc.GetThumbnailRequest_WebdavSource{ WebdavSource: &thumbnailsmsg.WebdavSource{ Url: g.config.OcisPublicURL + r.URL.RequestURI(), @@ -416,6 +419,7 @@ func (g Webdav) PublicThumbnailHead(w http.ResponseWriter, r *http.Request) { ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), Width: tr.Width, Height: tr.Height, + Processor: tr.Processor, Source: &thumbnailssvc.GetThumbnailRequest_WebdavSource{ WebdavSource: &thumbnailsmsg.WebdavSource{ Url: g.config.OcisPublicURL + r.URL.RequestURI(),