thumbhash icon indicating copy to clipboard operation
thumbhash copied to clipboard

Deno support?

Open kierancrown opened this issue 1 year ago • 2 comments

Does anybody have experience getting this to run in Deno?

My output is constantly looking like this

Uint8Array(24) [
  0, 0, 0, 7, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0
]

While my input looks like this

ArrayBuffer {
  [Uint8Contents]: <ff d8 ff e0 00 10 4a 46 49 46 00 01 01 00 00 48 00 48 00 00 ff e1 00 8c 45 78 69 66 00 00 4d 4d 00 2a 00 00 00 08 00 05 01 12 00 03 00 00 00 01 00 01 00 00 01 1a 00 05 00 00 00 01 00 00 00 4a 01 1b 00 05 00 00 00 01 00 00 00 52 01 28 00 03 00 00 00 01 00 02 00 00 87 69 00 04 00 00 00 01 00 00 00 5a ... 3143 more bytes>,
  byteLength: 3243
}

I've been trying to get this to work for a few days now. Wondering how this works in Deno as it works perfectly fine in NodeJS. Maybe due to the Buffer object

kierancrown avatar May 03 '24 10:05 kierancrown

Some thoughts:

  • Do you have some code to reproduce your issue?

  • Are you calling rgbaToThumbHash in Deno? That expects decoded RGBA data as input, but the input you posted here looks like a JPEG file instead of RGBA (byteLength is not a multiple of 4 and the data starts with ff d8). You're supposed to decode the image first.

evanw avatar May 03 '24 14:05 evanw

Some thoughts:

  • Do you have some code to reproduce your issue?
  • Are you calling rgbaToThumbHash in Deno? That expects decoded RGBA data as input, but the input you posted here looks like a JPEG file instead of RGBA (byteLength is not a multiple of 4 and the data starts with ff d8). You're supposed to decode the image first.

Sure this is the example I've managed to cobble together so far based on the NodeJS example. Do you know of a way to convert into the expected format? Sharp isn't available for Deno. I'm using ImageMagick but it obviously isn't exporting in the same way

// Setup type definitions for built-in Supabase Runtime APIs
/// <reference types="https://esm.sh/v135/@supabase/[email protected]/src/edge-runtime.d.ts" />

import { createClient } from "https://esm.sh/@supabase/[email protected]";
import {
  ImageMagick,
  IMagickImage,
  initialize,
  MagickFormat,
} from "https://deno.land/x/imagemagick_deno/mod.ts";
import { Buffer } from "jsr:@std/io/buffer";
import * as ThumbHash from "npm:[email protected]";

const corsHeaders = {
  "Access-Control-Allow-Origin": "*",
  "Access-Control-Allow-Headers": "authorization, x-client-info, apikey",
};

interface WebhookPayload {
  type: "INSERT";
  table: string;
  record: any;
  schema: "public";
}

const supabase = createClient(
  Deno.env.get("SUPABASE_URL")!,
  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!
);

const resizeImage = (image: Uint8Array): Promise<Uint8Array> => {
  return new Promise((resolve) => {
    (async () => {
      // Initialize ImageMagick
      await initialize();
      await ImageMagick.read(image, async (img: IMagickImage) => {
        img.resize(100, 100);

        await img.write(MagickFormat.Jpeg, (data: Uint8Array) => {
          resolve(data);
        });
      });
    })();
  });
};

Deno.serve(async (req) => {
  const payload: WebhookPayload = await req.json();
  console.log("Request received", JSON.stringify(payload, null, 2));

  try {
    // Get the trigger data which contains the path of the image
    const triggerData = payload.record;

    // Download the image from the bucket based on the provided path
    const { data: imageData, error: imageError } = await supabase.storage
      .from("user_profile_pictures")
      .download(triggerData.path);
    if (imageError) throw imageError;

    console.log((await imageData.arrayBuffer()).byteLength);

    // Convert image data to buffer
    const imageBuffer = await imageData.arrayBuffer();
    const input = new Uint8Array(imageBuffer, 0, imageBuffer.byteLength);
    const resizedImageBuffer = await resizeImage(input);

    const binaryThumbHash = ThumbHash.rgbaToThumbHash(
      100,
      100,
      resizedImageBuffer
    );
    
    console.log("binaryThumbHash:", new Buffer(binaryThumbHash));

    // Update the database with the generated thumbhash
    const { error: updateError } = await supabase
      .from(payload.table)
      .update({ thumbhash: binaryThumbHash })
      .eq("path", triggerData.path);
    if (updateError) throw updateError;

    return new Response(JSON.stringify({ success: true }), {
      headers: { ...corsHeaders, "Content-Type": "application/json" },
      status: 200,
    });
  } catch (error) {
    console.error(error);

    return new Response(JSON.stringify({ error: error.message }), {
      headers: { ...corsHeaders, "Content-Type": "application/json" },
      status: 400,
    });
  }
});

kierancrown avatar May 03 '24 14:05 kierancrown