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:
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
Reference in New Issue
Block a user