@gallop.software/studio 0.1.24 → 0.1.26
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/{StudioUI-YO6WPG5E.js → StudioUI-N7DC5H5U.js} +264 -39
- package/dist/StudioUI-N7DC5H5U.js.map +1 -0
- package/dist/{StudioUI-F2C4N66F.mjs → StudioUI-VH2C7QCI.mjs} +269 -44
- package/dist/StudioUI-VH2C7QCI.mjs.map +1 -0
- package/dist/handlers.d.mts +2 -24
- package/dist/handlers.d.ts +2 -24
- package/dist/handlers.js +175 -149
- package/dist/handlers.js.map +1 -1
- package/dist/handlers.mjs +175 -149
- package/dist/handlers.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{types-DIXDq6Cy.d.mts → types-lg2VkHIb.d.mts} +1 -1
- package/dist/{types-DIXDq6Cy.d.ts → types-lg2VkHIb.d.ts} +1 -1
- package/package.json +1 -1
- package/dist/StudioUI-F2C4N66F.mjs.map +0 -1
- package/dist/StudioUI-YO6WPG5E.js.map +0 -1
package/dist/handlers.js
CHANGED
|
@@ -22,8 +22,8 @@ async function GET(request) {
|
|
|
22
22
|
if (route === "scan") {
|
|
23
23
|
return handleScan();
|
|
24
24
|
}
|
|
25
|
-
if (route === "count-
|
|
26
|
-
return
|
|
25
|
+
if (route === "count-images") {
|
|
26
|
+
return handleCountImages();
|
|
27
27
|
}
|
|
28
28
|
return _server.NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
29
29
|
}
|
|
@@ -46,7 +46,7 @@ async function POST(request) {
|
|
|
46
46
|
return handleReprocess(request);
|
|
47
47
|
}
|
|
48
48
|
if (route === "process-all") {
|
|
49
|
-
return
|
|
49
|
+
return handleProcessAllStream();
|
|
50
50
|
}
|
|
51
51
|
return _server.NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
52
52
|
}
|
|
@@ -494,10 +494,9 @@ async function handleReprocess(request) {
|
|
|
494
494
|
return _server.NextResponse.json({ error: "Failed to reprocess images" }, { status: 500 });
|
|
495
495
|
}
|
|
496
496
|
}
|
|
497
|
-
async function
|
|
497
|
+
async function handleCountImages() {
|
|
498
498
|
try {
|
|
499
|
-
const
|
|
500
|
-
const unprocessedImages = [];
|
|
499
|
+
const allImages = [];
|
|
501
500
|
async function scanPublicFolder(dir, relativePath = "") {
|
|
502
501
|
try {
|
|
503
502
|
const entries = await _fs.promises.readdir(dir, { withFileTypes: true });
|
|
@@ -509,9 +508,7 @@ async function handleCountUnprocessed() {
|
|
|
509
508
|
if (entry.isDirectory()) {
|
|
510
509
|
await scanPublicFolder(fullPath, relPath);
|
|
511
510
|
} else if (isImageFile(entry.name)) {
|
|
512
|
-
|
|
513
|
-
unprocessedImages.push(relPath);
|
|
514
|
-
}
|
|
511
|
+
allImages.push(relPath);
|
|
515
512
|
}
|
|
516
513
|
}
|
|
517
514
|
} catch (e7) {
|
|
@@ -520,163 +517,192 @@ async function handleCountUnprocessed() {
|
|
|
520
517
|
const publicDir = _path2.default.join(process.cwd(), "public");
|
|
521
518
|
await scanPublicFolder(publicDir);
|
|
522
519
|
return _server.NextResponse.json({
|
|
523
|
-
count:
|
|
524
|
-
images:
|
|
520
|
+
count: allImages.length,
|
|
521
|
+
images: allImages
|
|
525
522
|
});
|
|
526
523
|
} catch (error) {
|
|
527
|
-
console.error("Failed to count
|
|
528
|
-
return _server.NextResponse.json({ error: "Failed to count
|
|
524
|
+
console.error("Failed to count images:", error);
|
|
525
|
+
return _server.NextResponse.json({ error: "Failed to count images" }, { status: 500 });
|
|
529
526
|
}
|
|
530
527
|
}
|
|
531
|
-
async function
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
528
|
+
async function handleProcessAllStream() {
|
|
529
|
+
const encoder = new TextEncoder();
|
|
530
|
+
const stream = new ReadableStream({
|
|
531
|
+
async start(controller) {
|
|
532
|
+
const sendEvent = (data) => {
|
|
533
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
534
|
+
|
|
535
|
+
`));
|
|
536
|
+
};
|
|
539
537
|
try {
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
await
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
538
|
+
const meta = await loadMeta();
|
|
539
|
+
const processed = [];
|
|
540
|
+
const errors = [];
|
|
541
|
+
const orphansRemoved = [];
|
|
542
|
+
const allImages = [];
|
|
543
|
+
async function scanPublicFolder(dir, relativePath = "") {
|
|
544
|
+
try {
|
|
545
|
+
const entries = await _fs.promises.readdir(dir, { withFileTypes: true });
|
|
546
|
+
for (const entry of entries) {
|
|
547
|
+
if (entry.name.startsWith(".")) continue;
|
|
548
|
+
const fullPath = _path2.default.join(dir, entry.name);
|
|
549
|
+
const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
|
550
|
+
if (relPath === "images" || relPath.startsWith("images/")) continue;
|
|
551
|
+
if (entry.isDirectory()) {
|
|
552
|
+
await scanPublicFolder(fullPath, relPath);
|
|
553
|
+
} else if (isImageFile(entry.name)) {
|
|
554
|
+
allImages.push({ key: relPath, fullPath });
|
|
555
|
+
}
|
|
551
556
|
}
|
|
557
|
+
} catch (e8) {
|
|
552
558
|
}
|
|
553
559
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
560
|
+
const publicDir = _path2.default.join(process.cwd(), "public");
|
|
561
|
+
await scanPublicFolder(publicDir);
|
|
562
|
+
const total = allImages.length;
|
|
563
|
+
sendEvent({ type: "start", total });
|
|
564
|
+
for (let i = 0; i < allImages.length; i++) {
|
|
565
|
+
const { key, fullPath } = allImages[i];
|
|
566
|
+
sendEvent({
|
|
567
|
+
type: "progress",
|
|
568
|
+
current: i + 1,
|
|
569
|
+
total,
|
|
570
|
+
percent: Math.round((i + 1) / total * 100),
|
|
571
|
+
currentFile: key
|
|
572
|
+
});
|
|
573
|
+
try {
|
|
574
|
+
const buffer = await _fs.promises.readFile(fullPath);
|
|
575
|
+
const ext = _path2.default.extname(key).toLowerCase();
|
|
576
|
+
const isSvg = ext === ".svg";
|
|
577
|
+
if (isSvg) {
|
|
578
|
+
const imageDir = _path2.default.dirname(key);
|
|
579
|
+
const imagesPath = _path2.default.join(process.cwd(), "public", "images", imageDir === "." ? "" : imageDir);
|
|
580
|
+
await _fs.promises.mkdir(imagesPath, { recursive: true });
|
|
581
|
+
const fileName = _path2.default.basename(key);
|
|
582
|
+
const destPath = _path2.default.join(imagesPath, fileName);
|
|
583
|
+
await _fs.promises.writeFile(destPath, buffer);
|
|
584
|
+
const sizePath = `/images/${imageDir === "." ? "" : imageDir + "/"}${fileName}`;
|
|
585
|
+
meta.images[key] = {
|
|
586
|
+
original: {
|
|
587
|
+
path: `/${key}`,
|
|
588
|
+
width: 0,
|
|
589
|
+
height: 0,
|
|
590
|
+
fileSize: buffer.length
|
|
591
|
+
},
|
|
592
|
+
sizes: {
|
|
593
|
+
full: { path: sizePath, width: 0, height: 0 },
|
|
594
|
+
large: { path: sizePath, width: 0, height: 0 },
|
|
595
|
+
medium: { path: sizePath, width: 0, height: 0 },
|
|
596
|
+
small: { path: sizePath, width: 0, height: 0 }
|
|
597
|
+
},
|
|
598
|
+
blurhash: "",
|
|
599
|
+
dominantColor: "#888888",
|
|
600
|
+
cdn: null
|
|
601
|
+
};
|
|
602
|
+
} else {
|
|
603
|
+
const existingEntry = meta.images[key];
|
|
604
|
+
const baseEntry = existingEntry || {
|
|
605
|
+
original: {
|
|
606
|
+
path: `/${key}`,
|
|
607
|
+
width: 0,
|
|
608
|
+
height: 0,
|
|
609
|
+
fileSize: buffer.length
|
|
610
|
+
},
|
|
611
|
+
sizes: {
|
|
612
|
+
full: { path: "", width: 0, height: 0 },
|
|
613
|
+
large: { path: "", width: 0, height: 0 },
|
|
614
|
+
medium: { path: "", width: 0, height: 0 },
|
|
615
|
+
small: { path: "", width: 0, height: 0 }
|
|
616
|
+
},
|
|
617
|
+
blurhash: "",
|
|
618
|
+
dominantColor: "#888888",
|
|
619
|
+
cdn: null
|
|
620
|
+
};
|
|
621
|
+
const processedEntry = await processImage(buffer, baseEntry, key);
|
|
622
|
+
meta.images[key] = processedEntry;
|
|
623
|
+
}
|
|
624
|
+
processed.push(key);
|
|
625
|
+
} catch (error) {
|
|
626
|
+
console.error(`Failed to process ${key}:`, error);
|
|
627
|
+
errors.push(key);
|
|
628
|
+
}
|
|
609
629
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
orphansRemoved.push(publicPath);
|
|
637
|
-
} catch (err) {
|
|
638
|
-
console.error(`Failed to remove orphan ${publicPath}:`, err);
|
|
630
|
+
sendEvent({ type: "cleanup", message: "Removing orphaned thumbnails..." });
|
|
631
|
+
const trackedPaths = /* @__PURE__ */ new Set();
|
|
632
|
+
for (const entry of Object.values(meta.images)) {
|
|
633
|
+
for (const sizeData of Object.values(entry.sizes)) {
|
|
634
|
+
trackedPaths.add(sizeData.path);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
async function findOrphans(dir, relativePath = "") {
|
|
638
|
+
try {
|
|
639
|
+
const entries = await _fs.promises.readdir(dir, { withFileTypes: true });
|
|
640
|
+
for (const entry of entries) {
|
|
641
|
+
if (entry.name.startsWith(".")) continue;
|
|
642
|
+
const fullPath = _path2.default.join(dir, entry.name);
|
|
643
|
+
const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
|
644
|
+
if (entry.isDirectory()) {
|
|
645
|
+
await findOrphans(fullPath, relPath);
|
|
646
|
+
} else if (isImageFile(entry.name)) {
|
|
647
|
+
const publicPath = `/images/${relPath}`;
|
|
648
|
+
if (!trackedPaths.has(publicPath)) {
|
|
649
|
+
try {
|
|
650
|
+
await _fs.promises.unlink(fullPath);
|
|
651
|
+
orphansRemoved.push(publicPath);
|
|
652
|
+
} catch (err) {
|
|
653
|
+
console.error(`Failed to remove orphan ${publicPath}:`, err);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
639
656
|
}
|
|
640
657
|
}
|
|
658
|
+
} catch (e9) {
|
|
641
659
|
}
|
|
642
660
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
isEmpty
|
|
661
|
+
const imagesDir = _path2.default.join(process.cwd(), "public", "images");
|
|
662
|
+
await findOrphans(imagesDir);
|
|
663
|
+
async function removeEmptyDirs(dir) {
|
|
664
|
+
try {
|
|
665
|
+
const entries = await _fs.promises.readdir(dir, { withFileTypes: true });
|
|
666
|
+
let isEmpty = true;
|
|
667
|
+
for (const entry of entries) {
|
|
668
|
+
if (entry.isDirectory()) {
|
|
669
|
+
const subDirEmpty = await removeEmptyDirs(_path2.default.join(dir, entry.name));
|
|
670
|
+
if (!subDirEmpty) isEmpty = false;
|
|
671
|
+
} else {
|
|
672
|
+
isEmpty = false;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
if (isEmpty && dir !== imagesDir) {
|
|
676
|
+
await _fs.promises.rmdir(dir);
|
|
677
|
+
}
|
|
678
|
+
return isEmpty;
|
|
679
|
+
} catch (e10) {
|
|
680
|
+
return true;
|
|
658
681
|
}
|
|
659
682
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
683
|
+
await removeEmptyDirs(imagesDir);
|
|
684
|
+
await saveMeta(meta);
|
|
685
|
+
sendEvent({
|
|
686
|
+
type: "complete",
|
|
687
|
+
processed: processed.length,
|
|
688
|
+
orphansRemoved: orphansRemoved.length,
|
|
689
|
+
errors: errors.length
|
|
690
|
+
});
|
|
691
|
+
} catch (error) {
|
|
692
|
+
console.error("Failed to process all:", error);
|
|
693
|
+
sendEvent({ type: "error", message: "Failed to process images" });
|
|
694
|
+
} finally {
|
|
695
|
+
controller.close();
|
|
666
696
|
}
|
|
667
697
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
} catch (error) {
|
|
677
|
-
console.error("Failed to process all:", error);
|
|
678
|
-
return _server.NextResponse.json({ error: "Failed to process all images" }, { status: 500 });
|
|
679
|
-
}
|
|
698
|
+
});
|
|
699
|
+
return new Response(stream, {
|
|
700
|
+
headers: {
|
|
701
|
+
"Content-Type": "text/event-stream",
|
|
702
|
+
"Cache-Control": "no-cache",
|
|
703
|
+
"Connection": "keep-alive"
|
|
704
|
+
}
|
|
705
|
+
});
|
|
680
706
|
}
|
|
681
707
|
async function loadMeta() {
|
|
682
708
|
const metaPath = _path2.default.join(process.cwd(), "_data", "_meta.json");
|