From f1ead422fe5a282af5712041f9be55d27e008984 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Mon, 4 Mar 2024 17:44:21 +0100 Subject: [PATCH] Implement output format conversion to JPEG/WEBP/PNG --- README.md | 2 +- src/ext/kemal.cr | 58 +++++++++++++++++++++++++++++++++++---- src/gphoto2/web/routes.cr | 13 +++++++-- 3 files changed, 63 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c1d1ec0..be3e328 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ REST web API for the [libgphoto2](http://www.gphoto.org/) library. You can use i #### `/cameras/:id/blob` -- `GET /cameras/:id/blob/*filepath[?download=true&as_jpeg=true&width=512&height=768]` +- `GET /cameras/:id/blob/*filepath[?download=true&format=jpeg/webp/png&width=512&height=768]` - `DELETE /cameras/:id/blob/*filepath` #### `/cameras/:id/zip` diff --git a/src/ext/kemal.cr b/src/ext/kemal.cr index ea676ca..091a621 100644 --- a/src/ext/kemal.cr +++ b/src/ext/kemal.cr @@ -45,10 +45,56 @@ end private MAGICKLOAD_EXTENSIONS = %w(.arw .cin .cr2 .crw .nef .orf .raf .x3f) -def send_file_as_jpeg(env, file : GPhoto2::CameraFile, width : Int? = nil, height : Int? = nil, disposition = nil) - file_path = Path[file.path] +enum ImageOutputFormat + JPEG + WEBP + PNG + + def self.from_path?(path : Path) + case path.extension.downcase + when ".jpeg", ".jpg" then JPEG + when ".webp" then WEBP + when ".png" then PNG + end + end + + def extension + case self + in JPEG then ".jpg" + in WEBP then ".webp" + in PNG then ".png" + end + end + + def mime_type + case self + in JPEG then "image/jpeg" + in WEBP then "image/webp" + in PNG then "image/png" + end + end + + def to_slice(image : Vips::Image, **options) + case self + in JPEG then image.jpegsave_buffer(**options) + in WEBP then image.webpsave_buffer(**options) + in PNG then image.pngsave_buffer(**options) + end + end +end + +def send_file_as(env, file : GPhoto2::CameraFile, format : ImageOutputFormat = :jpeg, width : Int? = nil, height : Int? = nil, disposition = nil) + path = Path[file.path] + ext = path.extension.downcase + + same_type = (format.extension == ext) + if same_type && !(width || height) + send_file env, file, + disposition: disposition + return + end - if file_path.extension.downcase.in?(MAGICKLOAD_EXTENSIONS) + if ext.in?(MAGICKLOAD_EXTENSIONS) image, _ = Vips::Image.magickload_buffer(file.to_slice) else image = Vips::Image.new_from_buffer(file.to_slice) @@ -72,9 +118,9 @@ def send_file_as_jpeg(env, file : GPhoto2::CameraFile, width : Int? = nil, heigh disposition ||= "inline" - send_file env, image.jpegsave_buffer, - mime_type: "image/jpeg", - filename: "#{file_path.stem}.jpg", + send_file env, format.to_slice(image), + mime_type: format.mime_type, + filename: "#{path.stem}#{format.extension}", disposition: disposition end end diff --git a/src/gphoto2/web/routes.cr b/src/gphoto2/web/routes.cr index 6e4a970..209aa4a 100644 --- a/src/gphoto2/web/routes.cr +++ b/src/gphoto2/web/routes.cr @@ -132,7 +132,10 @@ get "/cameras/:id/blob/*filepath" do |env| filepath = env.params.url["filepath"] path = Path.posix(filepath) - as_jpeg = env.params.query["as_jpeg"]? == "true" + if format = env.params.query["format"]?.presence + format = ImageOutputFormat.parse?(format) || raise ArgumentError.new \ + "Format must be one of: #{ImageOutputFormat.values.join(", ", &.to_s.downcase)} or auto" + end if width = env.params.query["width"]?.presence width = width.to_i? || raise ArgumentError.new("Width must be an integer") @@ -151,8 +154,12 @@ get "/cameras/:id/blob/*filepath" do |env| if request_accepts_json?(env.request) send_json env, file else - if as_jpeg || width - send_file_as_jpeg env, file, + if format || width + format ||= ImageOutputFormat.from_path?(path) + format ||= ImageOutputFormat::JPEG + + send_file_as env, file, + format: format, width: width, height: height, disposition: disposition