feature(thumbnails): add the ability to define custom image processors (#7409)

* feature(thumbnails): add the ability to define custom image processors

* fix(ci): add exported member comment

* docs(thumbnails): mention processors in readme

* fix: codacy and code review feedback

* fix: thumbnail readme markdown

Co-authored-by: Martin <github@diemattels.at>

---------

Co-authored-by: Martin <github@diemattels.at>
This commit is contained in:
Florian Schade
2023-10-17 09:44:44 +02:00
committed by GitHub
co-authored by Martin
parent 224f439e08
commit 9abcd8a7f3
18 changed files with 458 additions and 97 deletions
@@ -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
@@ -36,6 +36,8 @@ type GetThumbnailRequest struct {
Width int32 `protobuf:"varint,3,opt,name=width,proto3" json:"width,omitempty"` Width int32 `protobuf:"varint,3,opt,name=width,proto3" json:"width,omitempty"`
// The height of the thumbnail // The height of the thumbnail
Height int32 `protobuf:"varint,4,opt,name=height,proto3" json:"height,omitempty"` 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: // Types that are assignable to Source:
// //
// *GetThumbnailRequest_WebdavSource // *GetThumbnailRequest_WebdavSource
@@ -103,6 +105,13 @@ func (x *GetThumbnailRequest) GetHeight() int32 {
return 0 return 0
} }
func (x *GetThumbnailRequest) GetProcessor() string {
if x != nil {
return x.Processor
}
return ""
}
func (m *GetThumbnailRequest) GetSource() isGetThumbnailRequest_Source { func (m *GetThumbnailRequest) GetSource() isGetThumbnailRequest_Source {
if m != nil { if m != nil {
return m.Source return m.Source
@@ -129,11 +138,11 @@ type isGetThumbnailRequest_Source interface {
} }
type GetThumbnailRequest_WebdavSource struct { 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 { 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() {} 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1c,
0x0a, 0x0d, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28,
0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x09, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x50, 0x0a, 0x0d,
0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, 0x20,
0x2e, 0x76, 0x30, 0x2e, 0x57, 0x65, 0x62, 0x64, 0x61, 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61,
0x48, 0x00, 0x52, 0x0c, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x67, 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76,
0x12, 0x47, 0x0a, 0x0a, 0x63, 0x73, 0x33, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, 0x30, 0x2e, 0x57, 0x65, 0x62, 0x64, 0x61, 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x52, 0x0c, 0x77, 0x65, 0x62, 0x64, 0x61, 0x76, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x47,
0x61, 0x67, 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a, 0x0a, 0x63, 0x73, 0x33, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01,
0x76, 0x30, 0x2e, 0x43, 0x53, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x63, 0x73, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30,
0x72, 0x63, 0x65, 0x22, 0x7e, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x2e, 0x43, 0x53, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x09, 0x63, 0x73,
0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x33, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x08, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x61, 0x74, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x65, 0x22, 0x7e, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69,
0x28, 0x09, 0x52, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x61, 0x74,
0x12, 0x25, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x6b, 0x61, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x52, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x25,
0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72,
0x79, 0x70, 0x65, 0x32, 0x87, 0x01, 0x0a, 0x10, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, 0x79, 0x70,
0x6c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x74, 0x79, 0x70,
0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x30, 0x2e, 0x6f, 0x63, 0x69, 0x73, 0x2e, 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, 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, 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, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xe9, 0x02, 0x5a, 0x41,
0x73, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2e, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c,
0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x76, 0x30, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x67, 0x65,
0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0xe9, 0x02, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69,
0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2f, 0x76,
0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x30, 0x92, 0x41, 0xa2, 0x02, 0x12, 0xb8, 0x01, 0x0a, 0x22, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f,
0x67, 0x65, 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x75, 0x64, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x53, 0x63, 0x61, 0x6c,
0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x65, 0x20, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x47, 0x0a, 0x0d,
0x2f, 0x76, 0x30, 0x92, 0x41, 0xa2, 0x02, 0x12, 0xb8, 0x01, 0x0a, 0x22, 0x6f, 0x77, 0x6e, 0x43, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12, 0x20, 0x68,
0x6c, 0x6f, 0x75, 0x64, 0x20, 0x49, 0x6e, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x65, 0x20, 0x53, 0x63, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x61, 0x6c, 0x65, 0x20, 0x74, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x22, 0x47, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x1a,
0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x20, 0x47, 0x6d, 0x62, 0x48, 0x12, 0x14, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75,
0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x2a, 0x42, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2d,
0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x32, 0x2e, 0x30, 0x12, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x69, 0x74,
0x73, 0x1a, 0x14, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x40, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
0x6f, 0x75, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x2a, 0x42, 0x0a, 0x0a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x74, 0x65,
0x65, 0x2d, 0x32, 0x2e, 0x30, 0x12, 0x34, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x30, 0x2e, 0x30,
0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x2a, 0x02, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
0x75, 0x64, 0x2f, 0x6f, 0x63, 0x69, 0x73, 0x2f, 0x62, 0x6c, 0x6f, 0x62, 0x2f, 0x6d, 0x61, 0x73, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
0x74, 0x65, 0x72, 0x2f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x32, 0x05, 0x31, 0x2e, 0x30, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3d, 0x0a, 0x10, 0x44, 0x65, 0x76, 0x65,
0x2e, 0x30, 0x2a, 0x02, 0x01, 0x02, 0x32, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x29, 0x68, 0x74,
0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x3a, 0x10, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x6f, 0x77, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x64,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x6a, 0x73, 0x6f, 0x6e, 0x72, 0x3d, 0x0a, 0x10, 0x44, 0x65, 0x65, 0x76, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x68, 0x75, 0x6d,
0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x72, 0x20, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x12, 0x29, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x2f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
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 ( var (
@@ -47,9 +47,11 @@ message GetThumbnailRequest {
int32 width = 3; int32 width = 3;
// The height of the thumbnail // The height of the thumbnail
int32 height = 4; int32 height = 4;
// Indicates which image processor to use
string processor = 5;
oneof source { oneof source {
ocis.messages.thumbnails.v0.WebdavSource webdav_source = 5; ocis.messages.thumbnails.v0.WebdavSource webdav_source = 6;
ocis.messages.thumbnails.v0.CS3Source cs3_source = 6; ocis.messages.thumbnails.v0.CS3Source cs3_source = 7;
} }
} }
+17 -6
View File
@@ -10,9 +10,9 @@ The relevant environment variables defining file locations are:
- (2) `STORAGE_USERS_OCIS_ROOT` - (2) `STORAGE_USERS_OCIS_ROOT`
- (3) `THUMBNAILS_FILESYSTEMSTORAGE_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. (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. (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. (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. 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: Example:
Requested: 18x12 Requested: 18x12
Available: 30x20, 15x10, 9x6 Available: 30x20, 15x10, 9x6
Returned: 15x10 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 ## Deleting Thumbnails
@@ -16,6 +16,10 @@ import (
"github.com/cs3org/reva/v2/pkg/storagespace" "github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/cs3org/reva/v2/pkg/utils" "github.com/cs3org/reva/v2/pkg/utils"
"github.com/golang-jwt/jwt/v4" "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" "github.com/owncloud/ocis/v2/ocis-pkg/log"
thumbnailssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/thumbnails/v0" thumbnailssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/thumbnails/v0"
"github.com/owncloud/ocis/v2/services/thumbnails/pkg/preprocessor" "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" 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"
"github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail/imgsource" "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. // NewService returns a service implementation for Service.
@@ -124,7 +125,7 @@ func (g Thumbnail) handleCS3Source(ctx context.Context, req *thumbnailssvc.GetTh
if tType == "" { if tType == "" {
tType = req.GetThumbnailType().String() 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 { if err != nil {
return "", merrors.BadRequest(g.serviceID, err.Error()) return "", merrors.BadRequest(g.serviceID, err.Error())
} }
@@ -207,7 +208,7 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *thumbnailssvc.Ge
if tType == "" { if tType == "" {
tType = req.GetThumbnailType().String() 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 { if err != nil {
return "", merrors.BadRequest(g.serviceID, err.Error()) return "", merrors.BadRequest(g.serviceID, err.Error())
} }
@@ -114,8 +114,8 @@ func EncoderForType(fileType string) (Encoder, error) {
} }
// GetExtForMime return the supported extension by mime // GetExtForMime return the supported extension by mime
func GetExtForMime(mime string) string { func GetExtForMime(fileType string) string {
ext := strings.TrimPrefix(strings.TrimSpace(strings.ToLower(mime)), "image/") ext := strings.TrimPrefix(strings.TrimSpace(strings.ToLower(fileType)), "image/")
switch ext { switch ext {
case typeJpg, typeJpeg, typePng, typeGif: case typeJpg, typeJpeg, typePng, typeGif:
return ext return ext
+13 -16
View File
@@ -1,7 +1,6 @@
package thumbnail package thumbnail
import ( import (
"errors"
"image" "image"
"image/color" "image/color"
"image/draw" "image/draw"
@@ -11,36 +10,34 @@ import (
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
) )
var ( // Generator generates a web friendly file version.
// 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")
)
type Generator interface { 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{} 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) m, ok := img.(image.Image)
if !ok { 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{} 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 // Code inspired by https://github.com/willnorris/gifresize/blob/db93a7e1dcb1c279f7eeb99cc6d90b9e2e23e871/gifresize.go
m, ok := img.(*gif.GIF) m, ok := img.(*gif.GIF)
if !ok { if !ok {
return nil, ErrInvalidType2 return nil, ErrInvalidType
} }
// Create a new RGBA image to hold the incremental frames. // Create a new RGBA image to hold the incremental frames.
srcX, srcY := m.Config.Width, m.Config.Height srcX, srcY := m.Config.Width, m.Config.Height
@@ -51,8 +48,8 @@ func (g GifGenerator) GenerateThumbnail(size image.Rectangle, img interface{}) (
bounds := frame.Bounds() bounds := frame.Bounds()
prev := tmp prev := tmp
draw.Draw(tmp, bounds, frame, bounds.Min, draw.Over) draw.Draw(tmp, bounds, frame, bounds.Min, draw.Over)
scaled := imaging.Resize(tmp, size.Dx(), size.Dy(), imaging.Lanczos) processed := processor.Process(tmp, size.Dx(), size.Dy(), imaging.Lanczos)
m.Image[i] = g.imageToPaletted(scaled, frame.Palette) m.Image[i] = g.imageToPaletted(processed, frame.Palette)
switch m.Disposal[i] { switch m.Disposal[i] {
case gif.DisposalBackground: case gif.DisposalBackground:
@@ -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
}
}
}
@@ -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)
})
}
}
@@ -5,10 +5,12 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings"
"github.com/pkg/errors"
"github.com/owncloud/ocis/v2/ocis-pkg/log" "github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/services/thumbnails/pkg/config" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/config"
"github.com/pkg/errors"
) )
const ( const (
@@ -82,7 +84,14 @@ func (s FileSystem) Put(key string, img []byte) error {
func (s FileSystem) BuildKey(r Request) string { func (s FileSystem) BuildKey(r Request) string {
checksum := r.Checksum checksum := r.Checksum
filetype := r.Types[0] 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, ""))
} }
@@ -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)
})
}
}
@@ -38,7 +38,13 @@ func (s InMemory) BuildKey(r Request) string {
parts := []string{ parts := []string{
r.Checksum, r.Checksum,
r.Resolution.String(), 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, "+") return strings.Join(parts, "+")
} }
@@ -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)
})
}
}
@@ -15,6 +15,12 @@ type Request struct {
Types []string Types []string
// The resolution of the thumbnail // The resolution of the thumbnail
Resolution image.Rectangle 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. // Storage defines the interface for a thumbnail store.
+12 -5
View File
@@ -30,6 +30,7 @@ type Request struct {
Encoder Encoder Encoder Encoder
Generator Generator Generator Generator
Checksum string Checksum string
Processor Processor
} }
// Manager is responsible for generating thumbnails // 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()) 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 { if err != nil {
return "", err return "", err
} }
@@ -98,9 +99,10 @@ func (s SimpleManager) GetThumbnail(key string) ([]byte, error) {
func mapToStorageRequest(r Request) storage.Request { func mapToStorageRequest(r Request) storage.Request {
return storage.Request{ return storage.Request{
Checksum: r.Checksum, Checksum: r.Checksum,
Resolution: r.Resolution, Resolution: r.Resolution,
Types: r.Encoder.Types(), 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 // 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) generator, err := GeneratorForType(tType)
if err != nil { if err != nil {
return Request{}, err return Request{}, err
@@ -124,11 +126,16 @@ func PrepareRequest(width, height int, tType, checksum string) (Request, error)
if err != nil { if err != nil {
return Request{}, err return Request{}, err
} }
processor, err := ProcessorFor(pID, tType)
if err != nil {
return Request{}, err
}
return Request{ return Request{
Resolution: image.Rect(0, 0, width, height), Resolution: image.Rect(0, 0, width, height),
Generator: generator, Generator: generator,
Encoder: encoder, Encoder: encoder,
Checksum: checksum, Checksum: checksum,
Processor: processor,
}, nil }, nil
} }
@@ -4,9 +4,11 @@ import (
"image" "image"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"testing" "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/ocis-pkg/log"
"github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail/storage" "github.com/owncloud/ocis/v2/services/thumbnails/pkg/thumbnail/storage"
) )
@@ -118,13 +120,15 @@ func TestPrepareRequest(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { 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 { if (err != nil) != tt.wantErr {
t.Errorf("PrepareRequest() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("PrepareRequest() error = %v, wantErr %v", err, tt.wantErr)
return 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)
} }
}) })
} }
@@ -36,6 +36,8 @@ type ThumbnailRequest struct {
Height int32 Height int32
// In case of a public share the public link token. // In case of a public share the public link token.
PublicLinkToken string PublicLinkToken string
// Indicates which image processor to use
Processor string
// The Identifier from the requested URL // The Identifier from the requested URL
Identifier string Identifier string
} }
@@ -73,6 +75,7 @@ func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) {
Extension: filepath.Ext(fp), Extension: filepath.Ext(fp),
Width: int32(width), Width: int32(width),
Height: int32(height), Height: int32(height),
Processor: q.Get("processor"),
PublicLinkToken: chi.URLParam(r, "token"), PublicLinkToken: chi.URLParam(r, "token"),
Identifier: id, Identifier: id,
}, nil }, nil
@@ -243,6 +243,7 @@ func (g Webdav) SpacesThumbnail(w http.ResponseWriter, r *http.Request) {
ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
Width: tr.Width, Width: tr.Width,
Height: tr.Height, Height: tr.Height,
Processor: tr.Processor,
Source: &thumbnailssvc.GetThumbnailRequest_Cs3Source{ Source: &thumbnailssvc.GetThumbnailRequest_Cs3Source{
Cs3Source: &thumbnailsmsg.CS3Source{ Cs3Source: &thumbnailsmsg.CS3Source{
Path: fullPath, Path: fullPath,
@@ -335,6 +336,7 @@ func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) {
ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
Width: tr.Width, Width: tr.Width,
Height: tr.Height, Height: tr.Height,
Processor: tr.Processor,
Source: &thumbnailssvc.GetThumbnailRequest_Cs3Source{ Source: &thumbnailssvc.GetThumbnailRequest_Cs3Source{
Cs3Source: &thumbnailsmsg.CS3Source{ Cs3Source: &thumbnailsmsg.CS3Source{
Path: fullPath, Path: fullPath,
@@ -375,6 +377,7 @@ func (g Webdav) PublicThumbnail(w http.ResponseWriter, r *http.Request) {
ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
Width: tr.Width, Width: tr.Width,
Height: tr.Height, Height: tr.Height,
Processor: tr.Processor,
Source: &thumbnailssvc.GetThumbnailRequest_WebdavSource{ Source: &thumbnailssvc.GetThumbnailRequest_WebdavSource{
WebdavSource: &thumbnailsmsg.WebdavSource{ WebdavSource: &thumbnailsmsg.WebdavSource{
Url: g.config.OcisPublicURL + r.URL.RequestURI(), 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, ".")), ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
Width: tr.Width, Width: tr.Width,
Height: tr.Height, Height: tr.Height,
Processor: tr.Processor,
Source: &thumbnailssvc.GetThumbnailRequest_WebdavSource{ Source: &thumbnailssvc.GetThumbnailRequest_WebdavSource{
WebdavSource: &thumbnailsmsg.WebdavSource{ WebdavSource: &thumbnailsmsg.WebdavSource{
Url: g.config.OcisPublicURL + r.URL.RequestURI(), Url: g.config.OcisPublicURL + r.URL.RequestURI(),