From 29bdbc5d6fb04f7314b5e7907f5db0ed660bc1b7 Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Sat, 29 Feb 2020 23:12:08 -0700
Subject: [PATCH] Add an unstable route for downloading blurhashed images

---
 api/features/feature_routes.go  |  1 +
 api/unstable/blurhash_render.go | 68 +++++++++++++++++++++++++++++++++
 api/webserver/webserver.go      |  2 +
 config.sample.yaml              |  3 +-
 4 files changed, 73 insertions(+), 1 deletion(-)
 create mode 100644 api/unstable/blurhash_render.go

diff --git a/api/features/feature_routes.go b/api/features/feature_routes.go
index 644f1ad2..2bd328ae 100644
--- a/api/features/feature_routes.go
+++ b/api/features/feature_routes.go
@@ -5,6 +5,7 @@ import (
 )
 
 const MSC2448UploadRoute = "/_matrix/media/unstable/xyz.amorgan/upload"
+const MSC2448AltRenderRoute = "/_matrix/media/unstable/io.t2bot.msc2448/blurhash/{blurhash:[^/]+}"
 
 func IsRoute(r *http.Request, route string) bool {
 	uri := r.URL.Path
diff --git a/api/unstable/blurhash_render.go b/api/unstable/blurhash_render.go
new file mode 100644
index 00000000..9c46c78c
--- /dev/null
+++ b/api/unstable/blurhash_render.go
@@ -0,0 +1,68 @@
+package unstable
+
+import (
+	"bytes"
+	"image/png"
+	"net/http"
+	"strconv"
+
+	"github.com/buckket/go-blurhash"
+	"github.com/gorilla/mux"
+	"github.com/turt2live/matrix-media-repo/api"
+	"github.com/turt2live/matrix-media-repo/api/r0"
+	"github.com/turt2live/matrix-media-repo/common/rcontext"
+	"github.com/turt2live/matrix-media-repo/util"
+)
+
+func RenderBlurhash(r *http.Request, rctx rcontext.RequestContext, user api.UserInfo) interface{} {
+	params := mux.Vars(r)
+
+	hash := params["blurhash"]
+
+	width := rctx.Config.Features.MSC2448Blurhash.MaxRenderWidth / 2
+	height := rctx.Config.Features.MSC2448Blurhash.MaxRenderHeight / 2
+
+	wstr := r.URL.Query().Get("width")
+	hstr := r.URL.Query().Get("height")
+
+	if wstr != "" {
+		i, err := strconv.Atoi(wstr)
+		if err != nil {
+			return api.BadRequest("width must be an integer")
+		}
+		width = i
+	}
+	if hstr != "" {
+		i, err := strconv.Atoi(hstr)
+		if err != nil {
+			return api.BadRequest("height must be an integer")
+		}
+		height = i
+	}
+
+	if width > rctx.Config.Features.MSC2448Blurhash.MaxRenderWidth {
+		width = rctx.Config.Features.MSC2448Blurhash.MaxRenderWidth
+	}
+	if height > rctx.Config.Features.MSC2448Blurhash.MaxRenderHeight {
+		height = rctx.Config.Features.MSC2448Blurhash.MaxRenderHeight
+	}
+
+	img, err := blurhash.Decode(hash, width, height, rctx.Config.Features.MSC2448Blurhash.Punch)
+	if err != nil {
+		rctx.Log.Error(err)
+		return api.InternalServerError("Unexpected error rendering blurhash")
+	}
+	buf := &bytes.Buffer{}
+	err = png.Encode(buf, img)
+	if err != nil {
+		rctx.Log.Error(err)
+		return api.InternalServerError("Unexpected error rendering blurhash")
+	}
+
+	return &r0.DownloadMediaResponse{
+		ContentType: "image/png",
+		Filename:    "blurhash.png",
+		SizeBytes:   int64(buf.Len()),
+		Data:        util.BufferToStream(buf), // convert to stream to avoid console spam
+	}
+}
diff --git a/api/webserver/webserver.go b/api/webserver/webserver.go
index 8b8506ff..f2fa2f0f 100644
--- a/api/webserver/webserver.go
+++ b/api/webserver/webserver.go
@@ -74,6 +74,7 @@ func Init() *sync.WaitGroup {
 	appendToImportHandler := handler{api.RepoAdminRoute(custom.AppendToImport), "append_to_import", counter, false}
 	stopImportHandler := handler{api.RepoAdminRoute(custom.StopImport), "stop_import", counter, false}
 	versionHandler := handler{api.AccessTokenOptionalRoute(custom.GetVersion), "get_version", counter, false}
+	blurhashRenderHandler := handler{api.AccessTokenRequiredRoute(unstable.RenderBlurhash), "render_blurhash", counter, false}
 
 	routes := make(map[string]route)
 	// r0 is typically clients and v1 is typically servers. v1 is deprecated.
@@ -140,6 +141,7 @@ func Init() *sync.WaitGroup {
 
 	if config.Get().Features.MSC2448Blurhash.Enabled {
 		routes[features.MSC2448UploadRoute] = route{"POST", uploadHandler}
+		routes[features.MSC2448AltRenderRoute] = route{"GET", blurhashRenderHandler}
 	}
 
 	for routePath, route := range routes {
diff --git a/config.sample.yaml b/config.sample.yaml
index 4b0f87f5..74c20775 100644
--- a/config.sample.yaml
+++ b/config.sample.yaml
@@ -402,7 +402,8 @@ featureSupport:
     # Whether or not this MSC is enabled for use in the media repo
     enabled: false
 
-    # Maximum dimensions for converting a blurhash to an image
+    # Maximum dimensions for converting a blurhash to an image. When no width and
+    # height options are supplied, the default will be half these values.
     maxWidth: 1024
     maxHeight: 1024
 
-- 
GitLab