diff options
| author | Fuwn <[email protected]> | 2025-07-01 03:46:10 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2025-07-01 03:46:10 -0700 |
| commit | 6ffa4e3e35a10336aa8e7d6fa369cb51ee36ec20 (patch) | |
| tree | 157be9643023a1adc63afef5555ff5305bdd9e71 | |
| parent | eb81c62b839285b9d904bdff393ed2291468e41e (diff) | |
| download | mayu-6ffa4e3e35a10336aa8e7d6fa369cb51ee36ec20.tar.xz mayu-6ffa4e3e35a10336aa8e7d6fa369cb51ee36ec20.zip | |
feat: Pre-cache themes in memory
| -rw-r--r-- | src/cache.gleam | 65 | ||||
| -rw-r--r-- | src/mayu.gleam | 4 | ||||
| -rw-r--r-- | src/request.gleam | 3 | ||||
| -rw-r--r-- | src/svg.gleam | 45 |
4 files changed, 86 insertions, 31 deletions
diff --git a/src/cache.gleam b/src/cache.gleam new file mode 100644 index 0000000..bc9144b --- /dev/null +++ b/src/cache.gleam @@ -0,0 +1,65 @@ +import gleam/dict.{type Dict} +import gleam/int +import gleam/list +import gleam/option.{type Option} +import gleam/result +import image +import simplifile + +pub type CachedImage { + CachedImage(data: BitArray, info: image.ImageInformation) +} + +pub type ThemeCache = + Dict(String, Dict(Int, CachedImage)) + +pub fn load_themes() { + list.fold( + case simplifile.read_directory("./themes") { + Ok(files) -> files + Error(_) -> [] + }, + dict.new(), + fn(accumulated_themes, theme) { + dict.insert( + accumulated_themes, + theme, + list.range(0, 9) + |> list.fold(dict.new(), fn(accumulated_digits, digit) { + case + simplifile.read_bits( + from: "./themes/" + <> theme + <> "/" + <> int.to_string(digit) + <> "." + <> case theme { + "gelbooru-h" | "moebooru-h" | "lain" | "garukura" -> "png" + _ -> "gif" + }, + ) + { + Ok(image_data) -> { + case image.get_image_information(image_data) { + Ok(info) -> + dict.insert( + accumulated_digits, + digit, + CachedImage(data: image_data, info: info), + ) + Error(_) -> accumulated_digits + } + } + Error(_) -> accumulated_digits + } + }), + ) + }, + ) +} + +pub fn get_image(cache, theme, digit) -> Option(CachedImage) { + dict.get(cache, theme) + |> result.then(fn(theme_images) { dict.get(theme_images, digit) }) + |> option.from_result +} diff --git a/src/mayu.gleam b/src/mayu.gleam index 95442e8..2c3d78e 100644 --- a/src/mayu.gleam +++ b/src/mayu.gleam @@ -1,3 +1,4 @@ +import cache import database import gleam/erlang/process import mist @@ -10,6 +11,7 @@ pub fn main() { wisp.configure_logger() let _ = simplifile.create_directory("./data") + let image_cache = cache.load_themes() use connection <- sqlight.with_connection("./data/count.db") @@ -18,7 +20,7 @@ pub fn main() { let secret_key_base = wisp.random_string(64) let assert Ok(_) = wisp.mist_handler( - fn(request) { request.handle(request, connection) }, + fn(request) { request.handle(request, connection, image_cache) }, secret_key_base, ) |> mist.new diff --git a/src/request.gleam b/src/request.gleam index d726a03..c66e66d 100644 --- a/src/request.gleam +++ b/src/request.gleam @@ -19,7 +19,7 @@ fn middleware(request, handle) { handle(request) } -pub fn handle(request, connection) { +pub fn handle(request, connection, image_cache) { use _ <- middleware(request) case wisp.path_segments(request) { @@ -50,6 +50,7 @@ pub fn handle(request, connection) { |> wisp.set_header("Content-Type", "image/svg+xml") |> wisp.string_body( svg.xml( + image_cache, case list.key_find(wisp.get_query(request), "theme") { Ok(theme) -> theme _ -> "asoul" diff --git a/src/svg.gleam b/src/svg.gleam index d9de64f..884d6cf 100644 --- a/src/svg.gleam +++ b/src/svg.gleam @@ -1,9 +1,10 @@ +import cache import gleam/bit_array import gleam/int import gleam/list +import gleam/option.{Some} import gleam/string_builder import image -import simplifile type XmlImages { XmlImages(xml: String, width: Int, height: Int) @@ -21,43 +22,29 @@ fn image(data, image: image.ImageInformation, width) { ) <> "\"/>" } -fn images(theme, digits, width, height, svgs) { +fn images(image_cache, theme, digits, width, height, svgs) { case digits { [] -> XmlImages(string_builder.to_string(svgs), width, height) [digit, ..rest] -> - case - simplifile.read_bits( - from: "./themes/" - <> theme - <> "/" - <> int.to_string(digit) - <> "." - <> case theme { - "gelbooru-h" | "moebooru-h" | "lain" | "garukura" -> "png" - _ -> "gif" - }, - ) - { - Ok(data) -> - case image.get_image_information(data) { - Ok(information) -> - images( - theme, - rest, - width + information.width, - int.max(height, information.height), - string_builder.append(svgs, image(data, information, width)), - ) - Error(_) -> XmlImages(string_builder.to_string(svgs), width, height) - } - Error(_) -> XmlImages(string_builder.to_string(svgs), width, height) + case cache.get_image(image_cache, theme, digit) { + Some(cached) -> + images( + image_cache, + theme, + rest, + width + cached.info.width, + int.max(height, cached.info.height), + string_builder.append(svgs, image(cached.data, cached.info, width)), + ) + _ -> images(image_cache, theme, rest, width, height, svgs) } } } -pub fn xml(theme, number, padding) { +pub fn xml(image_cache, theme, number, padding) { let xml = images( + image_cache, theme, { let assert Ok(digits) = int.digits(number, 10) |