@gallop.software/studio 1.0.5 → 1.1.0
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.
- package/dist/{chunk-FDWPNRNZ.mjs → chunk-CIS6B4SP.mjs} +7 -2
- package/dist/chunk-CIS6B4SP.mjs.map +1 -0
- package/dist/{chunk-WJJHVPLT.js → chunk-VSOTEQ7X.js} +7 -2
- package/dist/chunk-VSOTEQ7X.js.map +1 -0
- package/dist/handlers/index.js +80 -73
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/index.mjs +57 -50
- package/dist/handlers/index.mjs.map +1 -1
- package/dist/index.d.mts +13 -7
- package/dist/index.d.ts +13 -7
- package/dist/index.js +2 -2
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-FDWPNRNZ.mjs.map +0 -1
- package/dist/chunk-WJJHVPLT.js.map +0 -1
package/dist/handlers/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getAllThumbnailPaths,
|
|
3
|
-
getThumbnailPath
|
|
4
|
-
|
|
3
|
+
getThumbnailPath,
|
|
4
|
+
isProcessed
|
|
5
|
+
} from "../chunk-CIS6B4SP.mjs";
|
|
5
6
|
|
|
6
7
|
// src/handlers/index.ts
|
|
7
8
|
import { NextResponse as NextResponse5 } from "next/server";
|
|
@@ -15,7 +16,7 @@ import path5 from "path";
|
|
|
15
16
|
import { promises as fs } from "fs";
|
|
16
17
|
import path from "path";
|
|
17
18
|
async function loadMeta() {
|
|
18
|
-
const metaPath = path.join(process.cwd(), "_data", "
|
|
19
|
+
const metaPath = path.join(process.cwd(), "_data", "_studio.json");
|
|
19
20
|
try {
|
|
20
21
|
const content = await fs.readFile(metaPath, "utf-8");
|
|
21
22
|
return JSON.parse(content);
|
|
@@ -26,7 +27,7 @@ async function loadMeta() {
|
|
|
26
27
|
async function saveMeta(meta) {
|
|
27
28
|
const dataDir = path.join(process.cwd(), "_data");
|
|
28
29
|
await fs.mkdir(dataDir, { recursive: true });
|
|
29
|
-
const metaPath = path.join(dataDir, "
|
|
30
|
+
const metaPath = path.join(dataDir, "_studio.json");
|
|
30
31
|
const ordered = {};
|
|
31
32
|
if (meta._cdns) {
|
|
32
33
|
ordered._cdns = meta._cdns;
|
|
@@ -106,16 +107,18 @@ import { promises as fs2 } from "fs";
|
|
|
106
107
|
import path3 from "path";
|
|
107
108
|
import sharp from "sharp";
|
|
108
109
|
import { encode } from "blurhash";
|
|
110
|
+
var FULL_MAX_WIDTH = 2560;
|
|
109
111
|
var DEFAULT_SIZES = {
|
|
110
|
-
small: { width: 300, suffix: "-sm" },
|
|
111
|
-
medium: { width: 700, suffix: "-md" },
|
|
112
|
-
large: { width: 1400, suffix: "-lg" }
|
|
112
|
+
small: { width: 300, suffix: "-sm", key: "sm" },
|
|
113
|
+
medium: { width: 700, suffix: "-md", key: "md" },
|
|
114
|
+
large: { width: 1400, suffix: "-lg", key: "lg" }
|
|
113
115
|
};
|
|
114
116
|
async function processImage(buffer, imageKey) {
|
|
115
117
|
const sharpInstance = sharp(buffer);
|
|
116
118
|
const metadata = await sharpInstance.metadata();
|
|
117
119
|
const originalWidth = metadata.width || 0;
|
|
118
120
|
const originalHeight = metadata.height || 0;
|
|
121
|
+
const ratio = originalHeight / originalWidth;
|
|
119
122
|
const keyWithoutSlash = imageKey.startsWith("/") ? imageKey.slice(1) : imageKey;
|
|
120
123
|
const baseName = path3.basename(keyWithoutSlash, path3.extname(keyWithoutSlash));
|
|
121
124
|
const ext = path3.extname(keyWithoutSlash).toLowerCase();
|
|
@@ -124,19 +127,34 @@ async function processImage(buffer, imageKey) {
|
|
|
124
127
|
await fs2.mkdir(imagesPath, { recursive: true });
|
|
125
128
|
const isPng = ext === ".png";
|
|
126
129
|
const outputExt = isPng ? ".png" : ".jpg";
|
|
130
|
+
const entry = {
|
|
131
|
+
o: [originalWidth, originalHeight]
|
|
132
|
+
};
|
|
127
133
|
const fullFileName = imageDir === "." ? `${baseName}${outputExt}` : `${imageDir}/${baseName}${outputExt}`;
|
|
128
134
|
const fullPath = path3.join(process.cwd(), "public", "images", fullFileName);
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
let fullWidth = originalWidth;
|
|
136
|
+
let fullHeight = originalHeight;
|
|
137
|
+
if (originalWidth > FULL_MAX_WIDTH) {
|
|
138
|
+
fullWidth = FULL_MAX_WIDTH;
|
|
139
|
+
fullHeight = Math.round(FULL_MAX_WIDTH * ratio);
|
|
140
|
+
if (isPng) {
|
|
141
|
+
await sharp(buffer).resize(fullWidth, fullHeight).png({ quality: 85 }).toFile(fullPath);
|
|
142
|
+
} else {
|
|
143
|
+
await sharp(buffer).resize(fullWidth, fullHeight).jpeg({ quality: 85 }).toFile(fullPath);
|
|
144
|
+
}
|
|
131
145
|
} else {
|
|
132
|
-
|
|
146
|
+
if (isPng) {
|
|
147
|
+
await sharp(buffer).png({ quality: 85 }).toFile(fullPath);
|
|
148
|
+
} else {
|
|
149
|
+
await sharp(buffer).jpeg({ quality: 85 }).toFile(fullPath);
|
|
150
|
+
}
|
|
133
151
|
}
|
|
152
|
+
entry.f = [fullWidth, fullHeight];
|
|
134
153
|
for (const [, sizeConfig] of Object.entries(DEFAULT_SIZES)) {
|
|
135
|
-
const { width: maxWidth, suffix } = sizeConfig;
|
|
154
|
+
const { width: maxWidth, suffix, key } = sizeConfig;
|
|
136
155
|
if (originalWidth <= maxWidth) {
|
|
137
156
|
continue;
|
|
138
157
|
}
|
|
139
|
-
const ratio = originalHeight / originalWidth;
|
|
140
158
|
const newHeight = Math.round(maxWidth * ratio);
|
|
141
159
|
const sizeFileName = `${baseName}${suffix}${outputExt}`;
|
|
142
160
|
const sizeFilePath = imageDir === "." ? sizeFileName : `${imageDir}/${sizeFileName}`;
|
|
@@ -146,14 +164,11 @@ async function processImage(buffer, imageKey) {
|
|
|
146
164
|
} else {
|
|
147
165
|
await sharp(buffer).resize(maxWidth, newHeight).jpeg({ quality: 80 }).toFile(sizePath);
|
|
148
166
|
}
|
|
167
|
+
entry[key] = [maxWidth, newHeight];
|
|
149
168
|
}
|
|
150
169
|
const { data, info } = await sharp(buffer).resize(32, 32, { fit: "inside" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
151
|
-
|
|
152
|
-
return
|
|
153
|
-
w: originalWidth,
|
|
154
|
-
h: originalHeight,
|
|
155
|
-
b: blurhash
|
|
156
|
-
};
|
|
170
|
+
entry.b = encode(new Uint8ClampedArray(data), info.width, info.height, 4, 4);
|
|
171
|
+
return entry;
|
|
157
172
|
}
|
|
158
173
|
|
|
159
174
|
// src/handlers/utils/cdn.ts
|
|
@@ -400,7 +415,8 @@ async function handleList(request) {
|
|
|
400
415
|
let thumbnail;
|
|
401
416
|
let hasThumbnail = false;
|
|
402
417
|
let fileSize;
|
|
403
|
-
|
|
418
|
+
const entryIsProcessed = isProcessed(entry);
|
|
419
|
+
if (isImage && entryIsProcessed) {
|
|
404
420
|
const thumbPath = getThumbnailPath(key, "sm");
|
|
405
421
|
if (isPushedToCloud && entry.c !== void 0) {
|
|
406
422
|
const cdnUrl = cdnUrls[entry.c];
|
|
@@ -443,12 +459,12 @@ async function handleList(request) {
|
|
|
443
459
|
size: fileSize,
|
|
444
460
|
thumbnail,
|
|
445
461
|
hasThumbnail,
|
|
446
|
-
isProcessed:
|
|
462
|
+
isProcessed: entryIsProcessed,
|
|
447
463
|
cdnPushed: isPushedToCloud,
|
|
448
464
|
cdnBaseUrl: fileCdnUrl,
|
|
449
465
|
isRemote,
|
|
450
466
|
isProtected: isInsideImagesFolder,
|
|
451
|
-
dimensions: entry.
|
|
467
|
+
dimensions: entry.o ? { width: entry.o[0], height: entry.o[1] } : void 0
|
|
452
468
|
});
|
|
453
469
|
}
|
|
454
470
|
}
|
|
@@ -480,7 +496,8 @@ async function handleSearch(request) {
|
|
|
480
496
|
const isRemote = isPushedToCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl);
|
|
481
497
|
let thumbnail;
|
|
482
498
|
let hasThumbnail = false;
|
|
483
|
-
|
|
499
|
+
const entryIsProcessed = isProcessed(entry);
|
|
500
|
+
if (isImage && entryIsProcessed) {
|
|
484
501
|
const thumbPath = getThumbnailPath(key, "sm");
|
|
485
502
|
if (isPushedToCloud && entry.c !== void 0) {
|
|
486
503
|
const cdnUrl = cdnUrls[entry.c];
|
|
@@ -514,11 +531,11 @@ async function handleSearch(request) {
|
|
|
514
531
|
type: "file",
|
|
515
532
|
thumbnail,
|
|
516
533
|
hasThumbnail,
|
|
517
|
-
isProcessed:
|
|
534
|
+
isProcessed: entryIsProcessed,
|
|
518
535
|
cdnPushed: isPushedToCloud,
|
|
519
536
|
cdnBaseUrl: fileCdnUrl,
|
|
520
537
|
isRemote,
|
|
521
|
-
dimensions: entry.
|
|
538
|
+
dimensions: entry.o ? { width: entry.o[0], height: entry.o[1] } : void 0
|
|
522
539
|
});
|
|
523
540
|
}
|
|
524
541
|
return NextResponse.json({ items });
|
|
@@ -949,7 +966,7 @@ async function handleMoveStream(request) {
|
|
|
949
966
|
const fileCdnUrl = isInCloud && entry.c !== void 0 ? cdnUrls[entry.c] : void 0;
|
|
950
967
|
const isRemote = isInCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl);
|
|
951
968
|
const isPushedToR2 = isInCloud && r2PublicUrl && fileCdnUrl === r2PublicUrl;
|
|
952
|
-
const hasProcessedThumbnails = entry
|
|
969
|
+
const hasProcessedThumbnails = isProcessed(entry);
|
|
953
970
|
try {
|
|
954
971
|
if (isRemote && isImage) {
|
|
955
972
|
const remoteUrl = `${fileCdnUrl}${oldKey}`;
|
|
@@ -957,8 +974,7 @@ async function handleMoveStream(request) {
|
|
|
957
974
|
await fs5.mkdir(path6.dirname(newAbsolutePath), { recursive: true });
|
|
958
975
|
await fs5.writeFile(newAbsolutePath, buffer);
|
|
959
976
|
const newEntry = {
|
|
960
|
-
|
|
961
|
-
h: entry?.h,
|
|
977
|
+
o: entry?.o,
|
|
962
978
|
b: entry?.b
|
|
963
979
|
};
|
|
964
980
|
delete meta[oldKey];
|
|
@@ -968,17 +984,13 @@ async function handleMoveStream(request) {
|
|
|
968
984
|
const buffer = await downloadFromCdn(oldKey);
|
|
969
985
|
await fs5.mkdir(path6.dirname(newAbsolutePath), { recursive: true });
|
|
970
986
|
await fs5.writeFile(newAbsolutePath, buffer);
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
h: entry?.h,
|
|
987
|
+
let newEntry = {
|
|
988
|
+
o: entry?.o,
|
|
974
989
|
b: entry?.b
|
|
975
990
|
};
|
|
976
991
|
if (hasProcessedThumbnails) {
|
|
977
992
|
const processedEntry = await processImage(buffer, newKey);
|
|
978
|
-
newEntry
|
|
979
|
-
newEntry.h = processedEntry.h;
|
|
980
|
-
newEntry.b = processedEntry.b;
|
|
981
|
-
newEntry.p = 1;
|
|
993
|
+
newEntry = { ...newEntry, ...processedEntry };
|
|
982
994
|
}
|
|
983
995
|
await uploadOriginalToCdn(newKey);
|
|
984
996
|
if (hasProcessedThumbnails) {
|
|
@@ -1144,7 +1156,7 @@ async function handleSync(request) {
|
|
|
1144
1156
|
})
|
|
1145
1157
|
);
|
|
1146
1158
|
urlsToPurge.push(`${publicUrl}${imageKey}`);
|
|
1147
|
-
if (!isRemote && entry
|
|
1159
|
+
if (!isRemote && isProcessed(entry)) {
|
|
1148
1160
|
for (const thumbPath of getAllThumbnailPaths(imageKey)) {
|
|
1149
1161
|
const localPath = path7.join(process.cwd(), "public", thumbPath);
|
|
1150
1162
|
try {
|
|
@@ -1240,7 +1252,6 @@ async function handleReprocess(request) {
|
|
|
1240
1252
|
}
|
|
1241
1253
|
}
|
|
1242
1254
|
const updatedEntry = await processImage(buffer, imageKey);
|
|
1243
|
-
updatedEntry.p = 1;
|
|
1244
1255
|
if (isInOurR2) {
|
|
1245
1256
|
updatedEntry.c = existingCdnIndex;
|
|
1246
1257
|
await uploadToCdn(imageKey);
|
|
@@ -1297,7 +1308,7 @@ async function handleProcessAllStream() {
|
|
|
1297
1308
|
for (const [key, entry] of getFileEntries(meta)) {
|
|
1298
1309
|
const fileName = path7.basename(key);
|
|
1299
1310
|
if (!isImageFile(fileName)) continue;
|
|
1300
|
-
if (!entry
|
|
1311
|
+
if (!isProcessed(entry)) {
|
|
1301
1312
|
imagesToProcess.push({ key, entry });
|
|
1302
1313
|
} else {
|
|
1303
1314
|
alreadyProcessed++;
|
|
@@ -1347,10 +1358,10 @@ async function handleProcessAllStream() {
|
|
|
1347
1358
|
await fs6.writeFile(destPath, buffer);
|
|
1348
1359
|
meta[key] = {
|
|
1349
1360
|
...entry,
|
|
1350
|
-
|
|
1351
|
-
h: 0,
|
|
1361
|
+
o: [0, 0],
|
|
1352
1362
|
b: "",
|
|
1353
|
-
|
|
1363
|
+
f: [0, 0]
|
|
1364
|
+
// SVG has "full" to indicate processed
|
|
1354
1365
|
};
|
|
1355
1366
|
if (isRemote) {
|
|
1356
1367
|
delete meta[key].c;
|
|
@@ -1359,7 +1370,6 @@ async function handleProcessAllStream() {
|
|
|
1359
1370
|
const processedEntry = await processImage(buffer, key);
|
|
1360
1371
|
meta[key] = {
|
|
1361
1372
|
...processedEntry,
|
|
1362
|
-
p: 1,
|
|
1363
1373
|
...isInOurR2 ? { c: existingCdnIndex } : {}
|
|
1364
1374
|
};
|
|
1365
1375
|
}
|
|
@@ -1556,7 +1566,7 @@ async function handleScanStream() {
|
|
|
1556
1566
|
if (isImage) {
|
|
1557
1567
|
const ext = path8.extname(relativePath).toLowerCase();
|
|
1558
1568
|
if (ext === ".svg") {
|
|
1559
|
-
meta[imageKey] = {
|
|
1569
|
+
meta[imageKey] = { o: [0, 0], b: "" };
|
|
1560
1570
|
} else {
|
|
1561
1571
|
try {
|
|
1562
1572
|
const buffer = await fs7.readFile(fullPath);
|
|
@@ -1564,12 +1574,11 @@ async function handleScanStream() {
|
|
|
1564
1574
|
const { data, info } = await sharp3(buffer).resize(32, 32, { fit: "inside" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
1565
1575
|
const blurhash = encode2(new Uint8ClampedArray(data), info.width, info.height, 4, 4);
|
|
1566
1576
|
meta[imageKey] = {
|
|
1567
|
-
|
|
1568
|
-
h: metadata.height || 0,
|
|
1577
|
+
o: [metadata.width || 0, metadata.height || 0],
|
|
1569
1578
|
b: blurhash
|
|
1570
1579
|
};
|
|
1571
1580
|
} catch {
|
|
1572
|
-
meta[imageKey] = {
|
|
1581
|
+
meta[imageKey] = { o: [0, 0] };
|
|
1573
1582
|
}
|
|
1574
1583
|
}
|
|
1575
1584
|
} else {
|
|
@@ -1586,7 +1595,7 @@ async function handleScanStream() {
|
|
|
1586
1595
|
const expectedThumbnails = /* @__PURE__ */ new Set();
|
|
1587
1596
|
const fileEntries = getFileEntries(meta);
|
|
1588
1597
|
for (const [imageKey, entry] of fileEntries) {
|
|
1589
|
-
if (entry.c === void 0 && entry
|
|
1598
|
+
if (entry.c === void 0 && isProcessed(entry)) {
|
|
1590
1599
|
for (const thumbPath of getAllThumbnailPaths(imageKey)) {
|
|
1591
1600
|
expectedThumbnails.add(thumbPath);
|
|
1592
1601
|
}
|
|
@@ -1719,8 +1728,7 @@ async function processRemoteImage(url) {
|
|
|
1719
1728
|
const { data, info } = await sharp4(buffer).resize(32, 32, { fit: "inside" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
1720
1729
|
const blurhash = encode3(new Uint8ClampedArray(data), info.width, info.height, 4, 4);
|
|
1721
1730
|
return {
|
|
1722
|
-
|
|
1723
|
-
h: metadata.height || 0,
|
|
1731
|
+
o: [metadata.width || 0, metadata.height || 0],
|
|
1724
1732
|
b: blurhash
|
|
1725
1733
|
};
|
|
1726
1734
|
}
|
|
@@ -1766,8 +1774,7 @@ async function handleImportUrls(request) {
|
|
|
1766
1774
|
const cdnIndex = getOrAddCdnIndex(meta, base);
|
|
1767
1775
|
const imageData = await processRemoteImage(url);
|
|
1768
1776
|
setMetaEntry(meta, path9, {
|
|
1769
|
-
|
|
1770
|
-
h: imageData.h,
|
|
1777
|
+
o: imageData.o,
|
|
1771
1778
|
b: imageData.b,
|
|
1772
1779
|
c: cdnIndex
|
|
1773
1780
|
});
|