koishi-plugin-memesluna 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/lib/index.js +34 -42
  2. package/package.json +2 -2
package/lib/index.js CHANGED
@@ -64,15 +64,6 @@ function hashImageBuffer(buffer) {
64
64
  return (0, import_crypto.createHash)("sha256").update(buffer).digest("hex");
65
65
  }
66
66
  __name(hashImageBuffer, "hashImageBuffer");
67
- function loadJimp() {
68
- try {
69
- const jimpModule = require("jimp");
70
- return jimpModule.Jimp || jimpModule.default?.Jimp || null;
71
- } catch {
72
- return null;
73
- }
74
- }
75
- __name(loadJimp, "loadJimp");
76
67
  function loadOptionalSharp() {
77
68
  try {
78
69
  const packageName = "sharp";
@@ -83,6 +74,15 @@ function loadOptionalSharp() {
83
74
  }
84
75
  }
85
76
  __name(loadOptionalSharp, "loadOptionalSharp");
77
+ function loadPhoton() {
78
+ try {
79
+ const packageName = "@cf-wasm/photon/node";
80
+ return require(packageName);
81
+ } catch {
82
+ return null;
83
+ }
84
+ }
85
+ __name(loadPhoton, "loadPhoton");
86
86
  function countHexBitDistance(a, b) {
87
87
  if (!a || !b || a.length !== b.length) return 64;
88
88
  let distance = 0;
@@ -104,28 +104,15 @@ function lumaFromRgba(data, offset) {
104
104
  return 0.299 * r + 0.587 * g + 0.114 * b;
105
105
  }
106
106
  __name(lumaFromRgba, "lumaFromRgba");
107
- function averageCellLuma(data, width, height, cellX, cellY) {
108
- const xStart = Math.floor(cellX * width / DHASH_WIDTH);
109
- const xEnd = Math.max(xStart + 1, Math.floor((cellX + 1) * width / DHASH_WIDTH));
110
- const yStart = Math.floor(cellY * height / DHASH_HEIGHT);
111
- const yEnd = Math.max(yStart + 1, Math.floor((cellY + 1) * height / DHASH_HEIGHT));
112
- let total = 0;
113
- let count = 0;
114
- for (let y = yStart; y < Math.min(yEnd, height); y++) {
115
- for (let x = xStart; x < Math.min(xEnd, width); x++) {
116
- total += lumaFromRgba(data, (y * width + x) * 4);
117
- count++;
118
- }
119
- }
120
- return count ? total / count : 0;
121
- }
122
- __name(averageCellLuma, "averageCellLuma");
123
- function buildDhashFromLumaGrid(samples) {
107
+ function buildDhashFromRgbaPixels(raw) {
108
+ if (raw.length < DHASH_WIDTH * DHASH_HEIGHT * 4) return "";
124
109
  let bits = "";
125
110
  for (let y = 0; y < DHASH_HEIGHT; y++) {
126
- const row = y * DHASH_WIDTH;
111
+ const row = y * DHASH_WIDTH * 4;
127
112
  for (let x = 0; x < DHASH_WIDTH - 1; x++) {
128
- bits += samples[row + x] > samples[row + x + 1] ? "1" : "0";
113
+ const left = lumaFromRgba(raw, row + x * 4);
114
+ const right = lumaFromRgba(raw, row + (x + 1) * 4);
115
+ bits += left > right ? "1" : "0";
129
116
  }
130
117
  }
131
118
  let hex = "";
@@ -134,7 +121,7 @@ function buildDhashFromLumaGrid(samples) {
134
121
  }
135
122
  return hex;
136
123
  }
137
- __name(buildDhashFromLumaGrid, "buildDhashFromLumaGrid");
124
+ __name(buildDhashFromRgbaPixels, "buildDhashFromRgbaPixels");
138
125
  var RESERVED_PATHS = /* @__PURE__ */ new Set([
139
126
  "config",
140
127
  "admin",
@@ -357,21 +344,26 @@ var MemesLunaService = class extends import_koishi.Service {
357
344
  await this.backfillImageFingerprints();
358
345
  }
359
346
  async getImagePerceptualHash(buffer) {
360
- const Jimp = loadJimp();
361
- if (Jimp) {
347
+ const photon = loadPhoton();
348
+ if (photon) {
349
+ let inputImage = null;
350
+ let resizedImage = null;
362
351
  try {
363
- const image = await Jimp.read(buffer);
364
- const { data, width, height } = image.bitmap;
365
- if (!data || !width || !height) return "";
366
- const samples = [];
367
- for (let y = 0; y < DHASH_HEIGHT; y++) {
368
- for (let x = 0; x < DHASH_WIDTH; x++) {
369
- samples.push(averageCellLuma(data, width, height, x, y));
370
- }
371
- }
372
- return buildDhashFromLumaGrid(samples);
352
+ inputImage = photon.PhotonImage.new_from_byteslice(new Uint8Array(buffer));
353
+ resizedImage = photon.resize(inputImage, DHASH_WIDTH, DHASH_HEIGHT, photon.SamplingFilter.Nearest);
354
+ const hash = buildDhashFromRgbaPixels(resizedImage.get_raw_pixels());
355
+ if (hash) return hash;
373
356
  } catch (error) {
374
- this.ctx.logger("memesluna").debug(`Jimp failed to calculate perceptual hash: ${error.message}`);
357
+ this.ctx.logger("memesluna").debug(`Failed to calculate perceptual hash with photon: ${error.message}`);
358
+ } finally {
359
+ try {
360
+ resizedImage?.free?.();
361
+ } catch {
362
+ }
363
+ try {
364
+ inputImage?.free?.();
365
+ } catch {
366
+ }
375
367
  }
376
368
  }
377
369
  const sharp = loadOptionalSharp();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-memesluna",
3
3
  "description": "Image Forward service for Koishi with ChatLuna integration",
4
- "version": "0.4.2",
4
+ "version": "0.4.4",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "types": "lib/index.d.ts",
@@ -41,7 +41,7 @@
41
41
  "node": ">=18.0.0"
42
42
  },
43
43
  "dependencies": {
44
- "jimp": "^1.6.1",
44
+ "@cf-wasm/photon": "^0.3.6",
45
45
  "koishi-plugin-chatluna": "^1.3.0-alpha.49",
46
46
  "koishi-plugin-chatluna-storage-service": "^1.0.6"
47
47
  },