simple-photo-gallery 2.0.17 → 2.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/index.cjs +418 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +415 -51
- package/dist/index.js.map +1 -1
- package/dist/lib/index.cjs +80 -8
- package/dist/lib/index.cjs.map +1 -1
- package/dist/lib/index.d.cts +8 -4
- package/dist/lib/index.d.ts +8 -4
- package/dist/lib/index.js +80 -8
- package/dist/lib/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var process4 = require('process');
|
|
5
5
|
var commander = require('commander');
|
|
6
6
|
var consola = require('consola');
|
|
7
7
|
var child_process = require('child_process');
|
|
@@ -11,16 +11,19 @@ var buffer = require('buffer');
|
|
|
11
11
|
var sharp2 = require('sharp');
|
|
12
12
|
var blurhash = require('blurhash');
|
|
13
13
|
var common = require('@simple-photo-gallery/common');
|
|
14
|
+
var theme = require('@simple-photo-gallery/common/theme');
|
|
14
15
|
var ExifReader = require('exifreader');
|
|
15
16
|
var ffprobe = require('node-ffprobe');
|
|
17
|
+
var url = require('url');
|
|
16
18
|
var os = require('os');
|
|
17
19
|
var Conf = require('conf');
|
|
18
20
|
var axios = require('axios');
|
|
19
21
|
var semverParser = require('semver-parser');
|
|
20
22
|
|
|
23
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
21
24
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
22
25
|
|
|
23
|
-
var
|
|
26
|
+
var process4__default = /*#__PURE__*/_interopDefault(process4);
|
|
24
27
|
var fs8__default = /*#__PURE__*/_interopDefault(fs8);
|
|
25
28
|
var path7__default = /*#__PURE__*/_interopDefault(path7);
|
|
26
29
|
var sharp2__default = /*#__PURE__*/_interopDefault(sharp2);
|
|
@@ -31,7 +34,6 @@ var Conf__default = /*#__PURE__*/_interopDefault(Conf);
|
|
|
31
34
|
var axios__default = /*#__PURE__*/_interopDefault(axios);
|
|
32
35
|
|
|
33
36
|
// src/config/index.ts
|
|
34
|
-
var DEFAULT_THUMBNAIL_SIZE = 300;
|
|
35
37
|
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".tiff", ".tif", ".svg", ".avif"]);
|
|
36
38
|
var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([".mp4", ".avi", ".mov", ".wmv", ".flv", ".webm", ".mkv", ".m4v", ".3gp"]);
|
|
37
39
|
var HEADER_IMAGE_LANDSCAPE_WIDTHS = [3840, 2560, 1920, 1280, 960, 640];
|
|
@@ -60,7 +62,7 @@ async function cropAndResizeImage(image, outputPath, width, height, format = "av
|
|
|
60
62
|
withoutEnlargement: true
|
|
61
63
|
}).toFormat(format).toFile(outputPath);
|
|
62
64
|
}
|
|
63
|
-
async function createImageThumbnails(image, metadata, outputPath, outputPathRetina, size) {
|
|
65
|
+
async function createImageThumbnails(image, metadata, outputPath, outputPathRetina, size, sizeDimension = "auto") {
|
|
64
66
|
const originalWidth = metadata.width || 0;
|
|
65
67
|
const originalHeight = metadata.height || 0;
|
|
66
68
|
if (originalWidth === 0 || originalHeight === 0) {
|
|
@@ -69,12 +71,20 @@ async function createImageThumbnails(image, metadata, outputPath, outputPathReti
|
|
|
69
71
|
const aspectRatio = originalWidth / originalHeight;
|
|
70
72
|
let width;
|
|
71
73
|
let height;
|
|
72
|
-
if (
|
|
74
|
+
if (sizeDimension === "width") {
|
|
73
75
|
width = size;
|
|
74
76
|
height = Math.round(size / aspectRatio);
|
|
75
|
-
} else {
|
|
77
|
+
} else if (sizeDimension === "height") {
|
|
76
78
|
width = Math.round(size * aspectRatio);
|
|
77
79
|
height = size;
|
|
80
|
+
} else {
|
|
81
|
+
if (originalWidth > originalHeight) {
|
|
82
|
+
width = size;
|
|
83
|
+
height = Math.round(size / aspectRatio);
|
|
84
|
+
} else {
|
|
85
|
+
width = Math.round(size * aspectRatio);
|
|
86
|
+
height = size;
|
|
87
|
+
}
|
|
78
88
|
}
|
|
79
89
|
await resizeImage(image, outputPath, width, height);
|
|
80
90
|
await resizeImage(image, outputPathRetina, width * 2, height * 2);
|
|
@@ -90,6 +100,30 @@ async function generateBlurHash(imagePath, componentX = 4, componentY = 3) {
|
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
// src/modules/build/utils/index.ts
|
|
103
|
+
function wrapText(text, maxCharsPerLine) {
|
|
104
|
+
const words = text.split(" ");
|
|
105
|
+
const lines = [];
|
|
106
|
+
let currentLine = "";
|
|
107
|
+
for (const word of words) {
|
|
108
|
+
const testLine = currentLine ? `${currentLine} ${word}` : word;
|
|
109
|
+
if (word.length > maxCharsPerLine) {
|
|
110
|
+
if (currentLine) {
|
|
111
|
+
lines.push(currentLine);
|
|
112
|
+
currentLine = "";
|
|
113
|
+
}
|
|
114
|
+
lines.push(word);
|
|
115
|
+
} else if (testLine.length > maxCharsPerLine && currentLine) {
|
|
116
|
+
lines.push(currentLine);
|
|
117
|
+
currentLine = word;
|
|
118
|
+
} else {
|
|
119
|
+
currentLine = testLine;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (currentLine) {
|
|
123
|
+
lines.push(currentLine);
|
|
124
|
+
}
|
|
125
|
+
return lines;
|
|
126
|
+
}
|
|
93
127
|
async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPath, ui) {
|
|
94
128
|
ui?.start(`Creating social media card image`);
|
|
95
129
|
const headerBasename = path7__default.default.basename(headerPhotoPath, path7__default.default.extname(headerPhotoPath));
|
|
@@ -101,14 +135,38 @@ async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPa
|
|
|
101
135
|
const resizedImageBuffer = await image.resize(1200, 631, { fit: "cover" }).jpeg({ quality: 90 }).toBuffer();
|
|
102
136
|
const outputPath = ouputPath;
|
|
103
137
|
await sharp2__default.default(resizedImageBuffer).toFile(outputPath);
|
|
138
|
+
const CANVAS_WIDTH = 1200;
|
|
139
|
+
const CANVAS_HEIGHT = 631;
|
|
140
|
+
const FONT_SIZE = 72;
|
|
141
|
+
const MARGIN = 50;
|
|
142
|
+
const CHAR_WIDTH_RATIO = 0.6;
|
|
143
|
+
const usableWidth = CANVAS_WIDTH - 2 * MARGIN;
|
|
144
|
+
const maxCharsPerLine = Math.floor(usableWidth / (FONT_SIZE * CHAR_WIDTH_RATIO));
|
|
145
|
+
const lines = wrapText(title, maxCharsPerLine);
|
|
146
|
+
const lineHeight = FONT_SIZE * 1.2;
|
|
147
|
+
const totalTextHeight = FONT_SIZE + (lines.length - 1) * lineHeight;
|
|
148
|
+
const startY = CANVAS_HEIGHT - MARGIN - totalTextHeight + FONT_SIZE;
|
|
149
|
+
const leftX = MARGIN;
|
|
150
|
+
const tspanElements = lines.map((line, index) => {
|
|
151
|
+
const yPosition = startY + index * lineHeight;
|
|
152
|
+
const escapedLine = line.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
153
|
+
return `<tspan x="${leftX}" y="${yPosition}">${escapedLine}</tspan>`;
|
|
154
|
+
}).join("\n ");
|
|
104
155
|
const svgText = `
|
|
105
|
-
<svg width="
|
|
156
|
+
<svg width="${CANVAS_WIDTH}" height="${CANVAS_HEIGHT}" xmlns="http://www.w3.org/2000/svg">
|
|
106
157
|
<defs>
|
|
158
|
+
<linearGradient id="darkGradient" x1="0%" y1="0%" x2="0%" y2="100%">
|
|
159
|
+
<stop offset="0%" style="stop-color:rgb(0,0,0);stop-opacity:0" />
|
|
160
|
+
<stop offset="100%" style="stop-color:rgb(0,0,0);stop-opacity:0.65" />
|
|
161
|
+
</linearGradient>
|
|
107
162
|
<style>
|
|
108
|
-
.title { font-family: 'Arial, sans-serif'; font-size:
|
|
163
|
+
.title { font-family: 'Arial, sans-serif'; font-size: ${FONT_SIZE}px; font-weight: bold; fill: white; text-anchor: start; }
|
|
109
164
|
</style>
|
|
110
165
|
</defs>
|
|
111
|
-
<
|
|
166
|
+
<rect x="0" y="0" width="${CANVAS_WIDTH}" height="${CANVAS_HEIGHT}" fill="url(#darkGradient)" />
|
|
167
|
+
<text x="${leftX}" class="title">
|
|
168
|
+
${tspanElements}
|
|
169
|
+
</text>
|
|
112
170
|
</svg>
|
|
113
171
|
`;
|
|
114
172
|
const finalImageBuffer = await sharp2__default.default(resizedImageBuffer).composite([{ input: buffer.Buffer.from(svgText), top: 0, left: 0 }]).jpeg({ quality: 90 }).toBuffer();
|
|
@@ -292,11 +350,15 @@ function migrateGalleryJson(deprecatedGalleryData, galleryJsonPath, ui) {
|
|
|
292
350
|
filename: path7__default.default.basename(image.path)
|
|
293
351
|
}))
|
|
294
352
|
}));
|
|
353
|
+
const thumbnails2 = deprecatedGalleryData.thumbnailSize === void 0 ? void 0 : { size: deprecatedGalleryData.thumbnailSize };
|
|
295
354
|
const galleryData = {
|
|
296
355
|
...deprecatedGalleryData,
|
|
356
|
+
thumbnailSize: void 0,
|
|
357
|
+
// Remove old field
|
|
297
358
|
headerImage: path7__default.default.basename(deprecatedGalleryData.headerImage),
|
|
298
359
|
sections,
|
|
299
|
-
mediaBasePath
|
|
360
|
+
mediaBasePath,
|
|
361
|
+
thumbnails: thumbnails2
|
|
300
362
|
};
|
|
301
363
|
ui.debug("Backing up old gallery.json file");
|
|
302
364
|
fs8__default.default.copyFileSync(galleryJsonPath, `${galleryJsonPath}.old`);
|
|
@@ -367,9 +429,18 @@ async function getGallerySettingsFromUser(galleryName, defaultImage, ui) {
|
|
|
367
429
|
default: defaultImage,
|
|
368
430
|
placeholder: defaultImage
|
|
369
431
|
});
|
|
370
|
-
return {
|
|
432
|
+
return {
|
|
433
|
+
title,
|
|
434
|
+
description,
|
|
435
|
+
url,
|
|
436
|
+
headerImage,
|
|
437
|
+
thumbnails: {
|
|
438
|
+
size: "",
|
|
439
|
+
edge: void 0
|
|
440
|
+
}
|
|
441
|
+
};
|
|
371
442
|
}
|
|
372
|
-
async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries = [], useDefaultSettings, ctaBanner, ui) {
|
|
443
|
+
async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries = [], useDefaultSettings, ctaBanner, configOptions, ui) {
|
|
373
444
|
const galleryDir = path7__default.default.dirname(galleryJsonPath);
|
|
374
445
|
const isSameLocation = path7__default.default.relative(scanPath, path7__default.default.join(galleryDir, "..")) === "";
|
|
375
446
|
const mediaBasePath = isSameLocation ? void 0 : scanPath;
|
|
@@ -377,12 +448,23 @@ async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalle
|
|
|
377
448
|
...subGallery,
|
|
378
449
|
headerImage: subGallery.headerImage ? path7__default.default.relative(galleryDir, subGallery.headerImage) : ""
|
|
379
450
|
}));
|
|
451
|
+
const thumbnailsConfig = {};
|
|
452
|
+
if (configOptions.thumbnailSize !== void 0) {
|
|
453
|
+
thumbnailsConfig.size = configOptions.thumbnailSize;
|
|
454
|
+
}
|
|
455
|
+
if (configOptions.thumbnailEdge !== void 0) {
|
|
456
|
+
thumbnailsConfig.edge = configOptions.thumbnailEdge;
|
|
457
|
+
}
|
|
380
458
|
let galleryData = {
|
|
381
459
|
title: "My Gallery",
|
|
382
460
|
description: "My gallery with fantastic photos.",
|
|
383
461
|
headerImage: mediaFiles[0]?.filename || "",
|
|
384
462
|
mediaBasePath,
|
|
385
463
|
metadata: {},
|
|
464
|
+
// Include theme if provided via CLI
|
|
465
|
+
...configOptions.theme && { theme: configOptions.theme },
|
|
466
|
+
// Include thumbnails if any values were set via CLI
|
|
467
|
+
...Object.keys(thumbnailsConfig).length > 0 && { thumbnails: thumbnailsConfig },
|
|
386
468
|
sections: [
|
|
387
469
|
{
|
|
388
470
|
images: mediaFiles
|
|
@@ -395,13 +477,15 @@ async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalle
|
|
|
395
477
|
...ctaBanner !== void 0 && { ctaBanner }
|
|
396
478
|
};
|
|
397
479
|
if (!useDefaultSettings) {
|
|
480
|
+
const userSettings = await getGallerySettingsFromUser(
|
|
481
|
+
path7__default.default.basename(path7__default.default.join(galleryDir, "..")),
|
|
482
|
+
path7__default.default.basename(mediaFiles[0]?.filename || ""),
|
|
483
|
+
ui
|
|
484
|
+
);
|
|
485
|
+
const { thumbnails: thumbnails2, ...otherSettings } = userSettings;
|
|
398
486
|
galleryData = {
|
|
399
487
|
...galleryData,
|
|
400
|
-
...
|
|
401
|
-
path7__default.default.basename(path7__default.default.join(galleryDir, "..")),
|
|
402
|
-
path7__default.default.basename(mediaFiles[0]?.filename || ""),
|
|
403
|
-
ui
|
|
404
|
-
)
|
|
488
|
+
...otherSettings
|
|
405
489
|
};
|
|
406
490
|
}
|
|
407
491
|
await fs8.promises.writeFile(galleryJsonPath, JSON.stringify(galleryData, null, 2));
|
|
@@ -416,7 +500,7 @@ async function galleryExists(outputPath) {
|
|
|
416
500
|
return false;
|
|
417
501
|
}
|
|
418
502
|
}
|
|
419
|
-
async function processDirectory(scanPath, outputPath, recursive, useDefaultSettings, force, ctaBanner, ui) {
|
|
503
|
+
async function processDirectory(scanPath, outputPath, recursive, useDefaultSettings, force, ctaBanner, configOptions, ui) {
|
|
420
504
|
ui.start(`Scanning ${scanPath}`);
|
|
421
505
|
let totalFiles = 0;
|
|
422
506
|
let totalGalleries = 1;
|
|
@@ -432,6 +516,7 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
|
|
|
432
516
|
useDefaultSettings,
|
|
433
517
|
force,
|
|
434
518
|
ctaBanner,
|
|
519
|
+
configOptions,
|
|
435
520
|
ui
|
|
436
521
|
);
|
|
437
522
|
totalFiles += result2.totalFiles;
|
|
@@ -457,7 +542,16 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
|
|
|
457
542
|
}
|
|
458
543
|
try {
|
|
459
544
|
await fs8.promises.mkdir(galleryPath, { recursive: true });
|
|
460
|
-
await createGalleryJson(
|
|
545
|
+
await createGalleryJson(
|
|
546
|
+
mediaFiles,
|
|
547
|
+
galleryJsonPath,
|
|
548
|
+
scanPath,
|
|
549
|
+
subGalleries,
|
|
550
|
+
useDefaultSettings,
|
|
551
|
+
ctaBanner,
|
|
552
|
+
configOptions,
|
|
553
|
+
ui
|
|
554
|
+
);
|
|
461
555
|
ui.success(
|
|
462
556
|
`Create gallery with ${mediaFiles.length} files and ${subGalleries.length} subgalleries at: ${galleryJsonPath}`
|
|
463
557
|
);
|
|
@@ -481,6 +575,11 @@ async function init(options, ui) {
|
|
|
481
575
|
try {
|
|
482
576
|
const scanPath = path7__default.default.resolve(options.photos);
|
|
483
577
|
const outputPath = options.gallery ? path7__default.default.resolve(options.gallery) : scanPath;
|
|
578
|
+
const configOptions = {
|
|
579
|
+
theme: options.theme,
|
|
580
|
+
thumbnailSize: options.thumbnailSize,
|
|
581
|
+
thumbnailEdge: options.thumbnailEdge
|
|
582
|
+
};
|
|
484
583
|
const result = await processDirectory(
|
|
485
584
|
scanPath,
|
|
486
585
|
outputPath,
|
|
@@ -488,6 +587,7 @@ async function init(options, ui) {
|
|
|
488
587
|
options.default,
|
|
489
588
|
options.force,
|
|
490
589
|
options.ctaBanner,
|
|
590
|
+
configOptions,
|
|
491
591
|
ui
|
|
492
592
|
);
|
|
493
593
|
ui.box(
|
|
@@ -537,9 +637,25 @@ async function getVideoDimensions(filePath) {
|
|
|
537
637
|
}
|
|
538
638
|
return dimensions;
|
|
539
639
|
}
|
|
540
|
-
async function createVideoThumbnails(inputPath, videoDimensions, outputPath, outputPathRetina,
|
|
640
|
+
async function createVideoThumbnails(inputPath, videoDimensions, outputPath, outputPathRetina, size, sizeDimension = "auto", verbose = false) {
|
|
541
641
|
const aspectRatio = videoDimensions.width / videoDimensions.height;
|
|
542
|
-
|
|
642
|
+
let width;
|
|
643
|
+
let height;
|
|
644
|
+
if (sizeDimension === "width") {
|
|
645
|
+
width = size;
|
|
646
|
+
height = Math.round(size / aspectRatio);
|
|
647
|
+
} else if (sizeDimension === "height") {
|
|
648
|
+
width = Math.round(size * aspectRatio);
|
|
649
|
+
height = size;
|
|
650
|
+
} else {
|
|
651
|
+
if (videoDimensions.width > videoDimensions.height) {
|
|
652
|
+
width = size;
|
|
653
|
+
height = Math.round(size / aspectRatio);
|
|
654
|
+
} else {
|
|
655
|
+
width = Math.round(size * aspectRatio);
|
|
656
|
+
height = size;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
543
659
|
const tempFramePath = `${outputPath}.temp.png`;
|
|
544
660
|
return new Promise((resolve, reject) => {
|
|
545
661
|
const ffmpeg = child_process.spawn("ffmpeg", [
|
|
@@ -580,7 +696,7 @@ async function createVideoThumbnails(inputPath, videoDimensions, outputPath, out
|
|
|
580
696
|
}
|
|
581
697
|
|
|
582
698
|
// src/modules/thumbnails/index.ts
|
|
583
|
-
async function processImage(imagePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, lastMediaTimestamp) {
|
|
699
|
+
async function processImage(imagePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, thumbnailSizeDimension = "auto", lastMediaTimestamp) {
|
|
584
700
|
const fileMtime = await getFileMtime(imagePath);
|
|
585
701
|
if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs8__default.default.existsSync(thumbnailPath)) {
|
|
586
702
|
return void 0;
|
|
@@ -599,7 +715,8 @@ async function processImage(imagePath, thumbnailPath, thumbnailPathRetina, thumb
|
|
|
599
715
|
metadata,
|
|
600
716
|
thumbnailPath,
|
|
601
717
|
thumbnailPathRetina,
|
|
602
|
-
thumbnailSize
|
|
718
|
+
thumbnailSize,
|
|
719
|
+
thumbnailSizeDimension
|
|
603
720
|
);
|
|
604
721
|
const blurHash = await generateBlurHash(thumbnailPath);
|
|
605
722
|
return {
|
|
@@ -618,7 +735,7 @@ async function processImage(imagePath, thumbnailPath, thumbnailPathRetina, thumb
|
|
|
618
735
|
lastMediaTimestamp: fileMtime.toISOString()
|
|
619
736
|
};
|
|
620
737
|
}
|
|
621
|
-
async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumbnailSize, verbose, lastMediaTimestamp) {
|
|
738
|
+
async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumbnailSize, thumbnailSizeDimension = "auto", verbose, lastMediaTimestamp) {
|
|
622
739
|
const fileMtime = await getFileMtime(videoPath);
|
|
623
740
|
if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs8__default.default.existsSync(thumbnailPath)) {
|
|
624
741
|
return void 0;
|
|
@@ -630,6 +747,7 @@ async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumb
|
|
|
630
747
|
thumbnailPath,
|
|
631
748
|
thumbnailPathRetina,
|
|
632
749
|
thumbnailSize,
|
|
750
|
+
thumbnailSizeDimension,
|
|
633
751
|
verbose
|
|
634
752
|
);
|
|
635
753
|
const blurHash = await generateBlurHash(thumbnailPath);
|
|
@@ -649,7 +767,7 @@ async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumb
|
|
|
649
767
|
lastMediaTimestamp: fileMtime.toISOString()
|
|
650
768
|
};
|
|
651
769
|
}
|
|
652
|
-
async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath,
|
|
770
|
+
async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbnailConfig, ui) {
|
|
653
771
|
try {
|
|
654
772
|
const filePath = path7__default.default.resolve(path7__default.default.join(mediaBasePath, mediaFile.filename));
|
|
655
773
|
const fileName = mediaFile.filename;
|
|
@@ -660,7 +778,22 @@ async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbn
|
|
|
660
778
|
const lastMediaTimestamp = mediaFile.lastMediaTimestamp ? new Date(mediaFile.lastMediaTimestamp) : void 0;
|
|
661
779
|
const verbose = ui.level === consola.LogLevels.debug;
|
|
662
780
|
ui.debug(` Processing ${mediaFile.type}: ${fileName}`);
|
|
663
|
-
const updatedMediaFile = await (mediaFile.type === "image" ? processImage(
|
|
781
|
+
const updatedMediaFile = await (mediaFile.type === "image" ? processImage(
|
|
782
|
+
filePath,
|
|
783
|
+
thumbnailPath,
|
|
784
|
+
thumbnailPathRetina,
|
|
785
|
+
thumbnailConfig.size,
|
|
786
|
+
thumbnailConfig.edge,
|
|
787
|
+
lastMediaTimestamp
|
|
788
|
+
) : processVideo(
|
|
789
|
+
filePath,
|
|
790
|
+
thumbnailPath,
|
|
791
|
+
thumbnailPathRetina,
|
|
792
|
+
thumbnailConfig.size,
|
|
793
|
+
thumbnailConfig.edge,
|
|
794
|
+
verbose,
|
|
795
|
+
lastMediaTimestamp
|
|
796
|
+
));
|
|
664
797
|
if (!updatedMediaFile) {
|
|
665
798
|
ui.debug(` Skipping ${fileName} because it has already been processed`);
|
|
666
799
|
if (mediaFile.thumbnail && !mediaFile.thumbnail.blurHash && fs8__default.default.existsSync(thumbnailPath)) {
|
|
@@ -696,19 +829,30 @@ async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbn
|
|
|
696
829
|
return { ...mediaFile, thumbnail: void 0 };
|
|
697
830
|
}
|
|
698
831
|
}
|
|
699
|
-
async function processGalleryThumbnails(galleryDir, ui) {
|
|
832
|
+
async function processGalleryThumbnails(galleryDir, ui, cliThumbnailConfig) {
|
|
700
833
|
const galleryJsonPath = path7__default.default.join(galleryDir, "gallery", "gallery.json");
|
|
701
834
|
const thumbnailsPath = path7__default.default.join(galleryDir, "gallery", "images");
|
|
702
835
|
ui.start(`Creating thumbnails: ${galleryDir}`);
|
|
703
836
|
try {
|
|
704
837
|
fs8__default.default.mkdirSync(thumbnailsPath, { recursive: true });
|
|
705
838
|
const galleryData = parseGalleryJson(galleryJsonPath, ui);
|
|
706
|
-
const
|
|
839
|
+
const galleryThumbnailConfig = theme.extractThumbnailConfigFromGallery(galleryData);
|
|
840
|
+
let themeConfig;
|
|
841
|
+
if (galleryData.theme) {
|
|
842
|
+
try {
|
|
843
|
+
const themeDir = await resolveThemeDir(galleryData.theme, ui);
|
|
844
|
+
themeConfig = theme.loadThemeConfig(themeDir);
|
|
845
|
+
} catch {
|
|
846
|
+
ui.debug(`Could not load theme config from ${galleryData.theme}, using defaults`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
const thumbnailConfig = theme.mergeThumbnailConfig(cliThumbnailConfig, galleryThumbnailConfig, themeConfig);
|
|
850
|
+
ui.debug(`Thumbnail config: size=${thumbnailConfig.size}, edge=${thumbnailConfig.edge}`);
|
|
707
851
|
const mediaBasePath = galleryData.mediaBasePath ?? path7__default.default.join(galleryDir);
|
|
708
852
|
let processedCount = 0;
|
|
709
853
|
for (const section of galleryData.sections) {
|
|
710
854
|
for (const [index, mediaFile] of section.images.entries()) {
|
|
711
|
-
section.images[index] = await processMediaFile(mediaFile, mediaBasePath, thumbnailsPath,
|
|
855
|
+
section.images[index] = await processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbnailConfig, ui);
|
|
712
856
|
}
|
|
713
857
|
processedCount += section.images.length;
|
|
714
858
|
}
|
|
@@ -727,10 +871,11 @@ async function thumbnails(options, ui) {
|
|
|
727
871
|
ui.error("No galleries found.");
|
|
728
872
|
return { processedGalleryCount: 0, processedMediaCount: 0 };
|
|
729
873
|
}
|
|
874
|
+
const cliThumbnailConfig = options.thumbnailSize !== void 0 || options.thumbnailEdge !== void 0 ? { size: options.thumbnailSize, edge: options.thumbnailEdge } : void 0;
|
|
730
875
|
let totalGalleries = 0;
|
|
731
876
|
let totalProcessed = 0;
|
|
732
877
|
for (const galleryDir of galleryDirs) {
|
|
733
|
-
const processed = await processGalleryThumbnails(galleryDir, ui);
|
|
878
|
+
const processed = await processGalleryThumbnails(galleryDir, ui, cliThumbnailConfig);
|
|
734
879
|
if (processed > 0) {
|
|
735
880
|
++totalGalleries;
|
|
736
881
|
totalProcessed += processed;
|
|
@@ -789,7 +934,7 @@ async function scanAndAppendNewFiles(galleryDir, galleryJsonPath, galleryData, u
|
|
|
789
934
|
}
|
|
790
935
|
return galleryData;
|
|
791
936
|
}
|
|
792
|
-
async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnails, ui, baseUrl, thumbsBaseUrl) {
|
|
937
|
+
async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnails, ui, baseUrl, thumbsBaseUrl, cliThumbnailConfig, cliTheme) {
|
|
793
938
|
ui.start(`Building gallery ${galleryDir}`);
|
|
794
939
|
const galleryJsonPath = path7__default.default.join(galleryDir, "gallery", "gallery.json");
|
|
795
940
|
let galleryData = parseGalleryJson(galleryJsonPath, ui);
|
|
@@ -842,18 +987,35 @@ async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnail
|
|
|
842
987
|
galleryData.thumbsBaseUrl = thumbsBaseUrl;
|
|
843
988
|
fs8__default.default.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
|
|
844
989
|
}
|
|
990
|
+
if (cliTheme && galleryData.theme !== cliTheme) {
|
|
991
|
+
ui.debug("Updating gallery.json with theme");
|
|
992
|
+
galleryData.theme = cliTheme;
|
|
993
|
+
fs8__default.default.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
|
|
994
|
+
}
|
|
995
|
+
if (cliThumbnailConfig) {
|
|
996
|
+
const needsUpdate = cliThumbnailConfig.size !== void 0 && galleryData.thumbnails?.size !== cliThumbnailConfig.size || cliThumbnailConfig.edge !== void 0 && galleryData.thumbnails?.edge !== cliThumbnailConfig.edge;
|
|
997
|
+
if (needsUpdate) {
|
|
998
|
+
ui.debug("Updating gallery.json with thumbnail settings");
|
|
999
|
+
galleryData.thumbnails = {
|
|
1000
|
+
...galleryData.thumbnails,
|
|
1001
|
+
...cliThumbnailConfig.size !== void 0 && { size: cliThumbnailConfig.size },
|
|
1002
|
+
...cliThumbnailConfig.edge !== void 0 && { edge: cliThumbnailConfig.edge }
|
|
1003
|
+
};
|
|
1004
|
+
fs8__default.default.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
845
1007
|
if (!galleryData.metadata.image) {
|
|
846
1008
|
ui.debug("Updating gallery.json with social media card URL");
|
|
847
1009
|
galleryData.metadata.image = thumbsBaseUrl ? `${thumbsBaseUrl}/${path7__default.default.basename(socialMediaCardImagePath)}` : `${galleryData.url || ""}/${path7__default.default.relative(galleryDir, socialMediaCardImagePath)}`;
|
|
848
1010
|
fs8__default.default.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
|
|
849
1011
|
}
|
|
850
1012
|
if (shouldCreateThumbnails) {
|
|
851
|
-
await processGalleryThumbnails(galleryDir, ui);
|
|
1013
|
+
await processGalleryThumbnails(galleryDir, ui, cliThumbnailConfig);
|
|
852
1014
|
}
|
|
853
1015
|
ui.debug("Building gallery from template");
|
|
854
1016
|
try {
|
|
855
|
-
|
|
856
|
-
|
|
1017
|
+
process4__default.default.env.GALLERY_JSON_PATH = galleryJsonPath;
|
|
1018
|
+
process4__default.default.env.GALLERY_OUTPUT_DIR = path7__default.default.join(galleryDir, "gallery");
|
|
857
1019
|
child_process.execSync("npx astro build", { cwd: templateDir, stdio: ui.level === consola.LogLevels.debug ? "inherit" : "ignore" });
|
|
858
1020
|
} catch (error) {
|
|
859
1021
|
ui.error(`Build failed for ${galleryDir}`);
|
|
@@ -870,6 +1032,25 @@ async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnail
|
|
|
870
1032
|
fs8__default.default.rmSync(buildDir, { recursive: true, force: true });
|
|
871
1033
|
ui.success(`Gallery built successfully`);
|
|
872
1034
|
}
|
|
1035
|
+
function isLocalThemePath(theme) {
|
|
1036
|
+
return theme.startsWith("./") || theme.startsWith("../") || theme.startsWith("/");
|
|
1037
|
+
}
|
|
1038
|
+
async function resolveThemeDir(theme, ui) {
|
|
1039
|
+
if (isLocalThemePath(theme)) {
|
|
1040
|
+
const themeDir = path7__default.default.resolve(theme);
|
|
1041
|
+
const packageJsonPath = path7__default.default.join(themeDir, "package.json");
|
|
1042
|
+
if (!fs8__default.default.existsSync(packageJsonPath)) {
|
|
1043
|
+
throw new Error(`Theme directory not found or invalid: ${themeDir}. package.json not found.`);
|
|
1044
|
+
}
|
|
1045
|
+
ui.debug(`Using local theme: ${themeDir}`);
|
|
1046
|
+
return themeDir;
|
|
1047
|
+
} else {
|
|
1048
|
+
const themePath = await undefined(`${theme}/package.json`);
|
|
1049
|
+
const themeDir = path7__default.default.dirname(new URL(themePath).pathname);
|
|
1050
|
+
ui.debug(`Using npm theme package: ${theme} (${themeDir})`);
|
|
1051
|
+
return themeDir;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
873
1054
|
async function build(options, ui) {
|
|
874
1055
|
try {
|
|
875
1056
|
const galleryDirs = findGalleries(options.gallery, options.recursive);
|
|
@@ -877,20 +1058,41 @@ async function build(options, ui) {
|
|
|
877
1058
|
ui.error("No galleries found.");
|
|
878
1059
|
return { processedGalleryCount: 0 };
|
|
879
1060
|
}
|
|
880
|
-
const
|
|
881
|
-
const themeDir = path7__default.default.dirname(new URL(themePath).pathname);
|
|
1061
|
+
const cliThumbnailConfig = options.thumbnailSize !== void 0 || options.thumbnailEdge !== void 0 ? { size: options.thumbnailSize, edge: options.thumbnailEdge } : void 0;
|
|
882
1062
|
let totalGalleries = 0;
|
|
883
1063
|
for (const dir of galleryDirs) {
|
|
1064
|
+
const galleryJsonPath = path7__default.default.join(dir, "gallery", "gallery.json");
|
|
1065
|
+
const galleryData = parseGalleryJson(galleryJsonPath, ui);
|
|
1066
|
+
const themeIdentifier = options.theme || galleryData.theme || "@simple-photo-gallery/theme-modern";
|
|
1067
|
+
const themeDir = await resolveThemeDir(themeIdentifier, ui);
|
|
884
1068
|
const baseUrl = options.baseUrl ? `${options.baseUrl}${path7__default.default.relative(options.gallery, dir)}` : void 0;
|
|
885
1069
|
const thumbsBaseUrl = options.thumbsBaseUrl ? `${options.thumbsBaseUrl}${path7__default.default.relative(options.gallery, dir)}` : void 0;
|
|
886
|
-
await buildGallery(
|
|
1070
|
+
await buildGallery(
|
|
1071
|
+
path7__default.default.resolve(dir),
|
|
1072
|
+
themeDir,
|
|
1073
|
+
options.scan,
|
|
1074
|
+
options.thumbnails,
|
|
1075
|
+
ui,
|
|
1076
|
+
baseUrl,
|
|
1077
|
+
thumbsBaseUrl,
|
|
1078
|
+
cliThumbnailConfig,
|
|
1079
|
+
options.theme
|
|
1080
|
+
);
|
|
887
1081
|
++totalGalleries;
|
|
888
1082
|
}
|
|
889
1083
|
ui.box(`Built ${totalGalleries} ${totalGalleries === 1 ? "gallery" : "galleries"} successfully`);
|
|
890
1084
|
return { processedGalleryCount: totalGalleries };
|
|
891
1085
|
} catch (error) {
|
|
892
|
-
if (error instanceof Error
|
|
893
|
-
|
|
1086
|
+
if (error instanceof Error) {
|
|
1087
|
+
if (error.message.includes("Cannot find package")) {
|
|
1088
|
+
ui.error(
|
|
1089
|
+
`Theme package not found: ${options.theme || "@simple-photo-gallery/theme-modern"}. Make sure it's installed.`
|
|
1090
|
+
);
|
|
1091
|
+
} else if (error.message.includes("Theme directory not found") || error.message.includes("package.json not found")) {
|
|
1092
|
+
ui.error(error.message);
|
|
1093
|
+
} else {
|
|
1094
|
+
ui.error("Error building gallery");
|
|
1095
|
+
}
|
|
894
1096
|
} else {
|
|
895
1097
|
ui.error("Error building gallery");
|
|
896
1098
|
}
|
|
@@ -948,6 +1150,162 @@ async function clean(options, ui) {
|
|
|
948
1150
|
throw error;
|
|
949
1151
|
}
|
|
950
1152
|
}
|
|
1153
|
+
function findMonorepoRoot(startDir) {
|
|
1154
|
+
let dir = path7__default.default.resolve(startDir);
|
|
1155
|
+
while (true) {
|
|
1156
|
+
const pkgPath = path7__default.default.join(dir, "package.json");
|
|
1157
|
+
if (fs8__default.default.existsSync(pkgPath)) {
|
|
1158
|
+
try {
|
|
1159
|
+
const pkg = JSON.parse(fs8__default.default.readFileSync(pkgPath, "utf8"));
|
|
1160
|
+
if (pkg && typeof pkg === "object" && "workspaces" in pkg) {
|
|
1161
|
+
return dir;
|
|
1162
|
+
}
|
|
1163
|
+
} catch {
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
const parent = path7__default.default.dirname(dir);
|
|
1167
|
+
if (parent === dir) {
|
|
1168
|
+
return void 0;
|
|
1169
|
+
}
|
|
1170
|
+
dir = parent;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
function validateThemeName(name) {
|
|
1174
|
+
if (!name || name.trim().length === 0) {
|
|
1175
|
+
throw new Error("Theme name cannot be empty");
|
|
1176
|
+
}
|
|
1177
|
+
if (!/^[a-z0-9-]+$/i.test(name)) {
|
|
1178
|
+
throw new Error("Theme name can only contain letters, numbers, and hyphens");
|
|
1179
|
+
}
|
|
1180
|
+
return true;
|
|
1181
|
+
}
|
|
1182
|
+
async function ensureDirectory(dirPath, ui) {
|
|
1183
|
+
try {
|
|
1184
|
+
await fs8__default.default.promises.mkdir(dirPath, { recursive: true });
|
|
1185
|
+
ui.debug(`Created directory: ${dirPath}`);
|
|
1186
|
+
} catch (error) {
|
|
1187
|
+
if (error instanceof Error && "code" in error && error.code !== "EEXIST") {
|
|
1188
|
+
throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
var EXCLUDE_PATTERNS = ["node_modules", ".astro", "dist", "_build", ".git", "*.log", ".DS_Store"];
|
|
1193
|
+
function shouldExclude(name) {
|
|
1194
|
+
if (name === "README.md" || name === "README_BASE.md") {
|
|
1195
|
+
return true;
|
|
1196
|
+
}
|
|
1197
|
+
return EXCLUDE_PATTERNS.some((pattern) => {
|
|
1198
|
+
if (pattern.includes("*")) {
|
|
1199
|
+
const regexPattern = pattern.split("*").join(".*");
|
|
1200
|
+
const regex = new RegExp(regexPattern);
|
|
1201
|
+
return regex.test(name);
|
|
1202
|
+
}
|
|
1203
|
+
return name === pattern;
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
async function copyDirectory(src, dest, ui) {
|
|
1207
|
+
await fs8__default.default.promises.mkdir(dest, { recursive: true });
|
|
1208
|
+
const entries = await fs8__default.default.promises.readdir(src, { withFileTypes: true });
|
|
1209
|
+
for (const entry of entries) {
|
|
1210
|
+
if (shouldExclude(entry.name)) {
|
|
1211
|
+
ui.debug(`Skipping excluded file/directory: ${entry.name}`);
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
const srcPath = path7__default.default.join(src, entry.name);
|
|
1215
|
+
const destPath = path7__default.default.join(dest, entry.name);
|
|
1216
|
+
if (entry.isDirectory()) {
|
|
1217
|
+
await copyDirectory(srcPath, destPath, ui);
|
|
1218
|
+
} else {
|
|
1219
|
+
await fs8__default.default.promises.copyFile(srcPath, destPath);
|
|
1220
|
+
ui.debug(`Copied file: ${destPath}`);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
function findBaseThemePath() {
|
|
1225
|
+
const moduleDir = path7__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
1226
|
+
const bundledTemplatePath = path7__default.default.resolve(moduleDir, "../../../src/modules/create-theme/templates/base");
|
|
1227
|
+
if (fs8__default.default.existsSync(bundledTemplatePath)) {
|
|
1228
|
+
return bundledTemplatePath;
|
|
1229
|
+
}
|
|
1230
|
+
const monorepoRoot = findMonorepoRoot(process4__default.default.cwd());
|
|
1231
|
+
const workspaceRoot = monorepoRoot ?? process4__default.default.cwd();
|
|
1232
|
+
const workspaceBaseThemePath = path7__default.default.join(workspaceRoot, "themes", "base");
|
|
1233
|
+
if (fs8__default.default.existsSync(workspaceBaseThemePath)) {
|
|
1234
|
+
return workspaceBaseThemePath;
|
|
1235
|
+
}
|
|
1236
|
+
throw new Error(
|
|
1237
|
+
`Base theme template not found. Tried:
|
|
1238
|
+
- ${bundledTemplatePath}
|
|
1239
|
+
- ${workspaceBaseThemePath}
|
|
1240
|
+
|
|
1241
|
+
Please ensure the templates are included in the package or themes/base exists in the workspace.`
|
|
1242
|
+
);
|
|
1243
|
+
}
|
|
1244
|
+
async function updatePackageJson(themeDir, themeName, ui) {
|
|
1245
|
+
const packageJsonPath = path7__default.default.join(themeDir, "package.json");
|
|
1246
|
+
const packageJsonContent = await fs8__default.default.promises.readFile(packageJsonPath, "utf8");
|
|
1247
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
1248
|
+
packageJson.name = themeName;
|
|
1249
|
+
await fs8__default.default.promises.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n", "utf8");
|
|
1250
|
+
ui.debug(`Updated package.json with theme name: ${themeName}`);
|
|
1251
|
+
}
|
|
1252
|
+
async function createReadmeFromBase(baseThemePath, themeDir, themeName, ui) {
|
|
1253
|
+
const readmeBasePath = path7__default.default.join(baseThemePath, "README_BASE.md");
|
|
1254
|
+
const readmePath = path7__default.default.join(themeDir, "README.md");
|
|
1255
|
+
if (!fs8__default.default.existsSync(readmeBasePath)) {
|
|
1256
|
+
throw new Error(`README_BASE.md not found in template: ${readmeBasePath}`);
|
|
1257
|
+
}
|
|
1258
|
+
let readme = await fs8__default.default.promises.readFile(readmeBasePath, "utf8");
|
|
1259
|
+
const displayName = themeName.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
1260
|
+
readme = readme.replaceAll("{THEME_NAME}", displayName);
|
|
1261
|
+
readme = readme.replaceAll("{THEME_NAME_LOWER}", displayName.toLowerCase());
|
|
1262
|
+
await fs8__default.default.promises.writeFile(readmePath, readme, "utf8");
|
|
1263
|
+
ui.debug(`Created README.md from README_BASE.md for theme: ${themeName}`);
|
|
1264
|
+
}
|
|
1265
|
+
async function createTheme(options, ui) {
|
|
1266
|
+
try {
|
|
1267
|
+
validateThemeName(options.name);
|
|
1268
|
+
let themeDir;
|
|
1269
|
+
if (options.path) {
|
|
1270
|
+
themeDir = path7__default.default.resolve(options.path);
|
|
1271
|
+
} else {
|
|
1272
|
+
const monorepoRoot = findMonorepoRoot(process4__default.default.cwd());
|
|
1273
|
+
const baseDir = monorepoRoot ?? process4__default.default.cwd();
|
|
1274
|
+
const themesBaseDir = path7__default.default.resolve(baseDir, "themes");
|
|
1275
|
+
themeDir = path7__default.default.join(themesBaseDir, options.name);
|
|
1276
|
+
if (!fs8__default.default.existsSync(themesBaseDir)) {
|
|
1277
|
+
await ensureDirectory(themesBaseDir, ui);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
if (fs8__default.default.existsSync(themeDir)) {
|
|
1281
|
+
throw new Error(`Theme directory already exists: ${themeDir}. Cannot overwrite existing theme.`);
|
|
1282
|
+
}
|
|
1283
|
+
ui.start(`Creating theme: ${options.name}`);
|
|
1284
|
+
const baseThemePath = findBaseThemePath();
|
|
1285
|
+
ui.debug(`Using base theme from: ${baseThemePath}`);
|
|
1286
|
+
ui.debug("Copying base theme files...");
|
|
1287
|
+
await copyDirectory(baseThemePath, themeDir, ui);
|
|
1288
|
+
ui.debug("Updating theme-specific files...");
|
|
1289
|
+
await updatePackageJson(themeDir, options.name, ui);
|
|
1290
|
+
await createReadmeFromBase(baseThemePath, themeDir, options.name, ui);
|
|
1291
|
+
ui.success(`Theme created successfully at: ${themeDir}`);
|
|
1292
|
+
ui.info(`
|
|
1293
|
+
Next steps:`);
|
|
1294
|
+
ui.info(`1. cd ${themeDir}`);
|
|
1295
|
+
ui.info(`2. yarn install`);
|
|
1296
|
+
ui.info(`3. Customize your theme in src/pages/index.astro`);
|
|
1297
|
+
ui.info(`4. Initialize a gallery (run from directory with your images): spg init -p <images-folder>`);
|
|
1298
|
+
ui.info(`5. Build a gallery with your theme: spg build --theme ${themeDir} -g <gallery-folder>`);
|
|
1299
|
+
return { processedGalleryCount: 0 };
|
|
1300
|
+
} catch (error) {
|
|
1301
|
+
if (error instanceof Error) {
|
|
1302
|
+
ui.error(error.message);
|
|
1303
|
+
} else {
|
|
1304
|
+
ui.error("Failed to create theme");
|
|
1305
|
+
}
|
|
1306
|
+
throw error;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
951
1309
|
|
|
952
1310
|
// src/modules/telemetry/index.ts
|
|
953
1311
|
async function telemetry(options, ui, telemetryService2) {
|
|
@@ -977,7 +1335,7 @@ var ApiTelemetryClient = class {
|
|
|
977
1335
|
axios__default.default.post(this.endpoint, event, {
|
|
978
1336
|
headers: {
|
|
979
1337
|
"content-type": "application/json",
|
|
980
|
-
"user-agent": `simple-photo-gallery/${event.packageVersion} (${
|
|
1338
|
+
"user-agent": `simple-photo-gallery/${event.packageVersion} (${process4__default.default.platform}; ${process4__default.default.arch})`
|
|
981
1339
|
}
|
|
982
1340
|
});
|
|
983
1341
|
} catch {
|
|
@@ -987,7 +1345,7 @@ var ApiTelemetryClient = class {
|
|
|
987
1345
|
var ConsoleTelemetryClient = class {
|
|
988
1346
|
async record(event) {
|
|
989
1347
|
const serialized = JSON.stringify(event, null, 2);
|
|
990
|
-
|
|
1348
|
+
process4.stdout.write(`TELEMETRY EVENT: ${serialized}
|
|
991
1349
|
`);
|
|
992
1350
|
}
|
|
993
1351
|
};
|
|
@@ -1014,11 +1372,11 @@ var TelemetryService = class {
|
|
|
1014
1372
|
if (override) {
|
|
1015
1373
|
return override === "1";
|
|
1016
1374
|
}
|
|
1017
|
-
if (
|
|
1375
|
+
if (process4__default.default.env.CI || process4__default.default.env.DO_NOT_TRACK) {
|
|
1018
1376
|
return false;
|
|
1019
1377
|
}
|
|
1020
|
-
if (
|
|
1021
|
-
return
|
|
1378
|
+
if (process4__default.default.env.SPG_TELEMETRY) {
|
|
1379
|
+
return process4__default.default.env.SPG_TELEMETRY === "1";
|
|
1022
1380
|
}
|
|
1023
1381
|
const stored = this.getStoredPreference();
|
|
1024
1382
|
if (stored === void 0) {
|
|
@@ -1060,7 +1418,7 @@ var TelemetryService = class {
|
|
|
1060
1418
|
durationMs: now - startedAt,
|
|
1061
1419
|
packageName: this.packageName,
|
|
1062
1420
|
packageVersion: this.packageVersion,
|
|
1063
|
-
nodeVersion:
|
|
1421
|
+
nodeVersion: process4__default.default.version,
|
|
1064
1422
|
osPlatform: os__default.default.platform(),
|
|
1065
1423
|
osRelease: os__default.default.release(),
|
|
1066
1424
|
osArch: os__default.default.arch(),
|
|
@@ -1094,7 +1452,7 @@ var TelemetryService = class {
|
|
|
1094
1452
|
/** Returns the telemetry client. */
|
|
1095
1453
|
getClient() {
|
|
1096
1454
|
if (!this.client) {
|
|
1097
|
-
switch (
|
|
1455
|
+
switch (process4__default.default.env.SPG_TELEMETRY_PROVIDER) {
|
|
1098
1456
|
case "none": {
|
|
1099
1457
|
this.client = void 0;
|
|
1100
1458
|
break;
|
|
@@ -1186,7 +1544,7 @@ async function waitForUpdateCheck(checkPromise) {
|
|
|
1186
1544
|
// package.json
|
|
1187
1545
|
var package_default = {
|
|
1188
1546
|
name: "simple-photo-gallery",
|
|
1189
|
-
version: "2.0
|
|
1547
|
+
version: "2.1.0"};
|
|
1190
1548
|
|
|
1191
1549
|
// src/index.ts
|
|
1192
1550
|
var program = new commander.Command();
|
|
@@ -1228,7 +1586,7 @@ function withCommandContext(handler) {
|
|
|
1228
1586
|
} catch (error) {
|
|
1229
1587
|
ui.debug(error);
|
|
1230
1588
|
errorInfo = error instanceof Error ? { name: error.name, message: error.message } : { name: "UnknownError", message: String(error) };
|
|
1231
|
-
|
|
1589
|
+
process4__default.default.exitCode = 1;
|
|
1232
1590
|
}
|
|
1233
1591
|
const updateInfo = await waitForUpdateCheck(updateCheckPromise);
|
|
1234
1592
|
if (updateInfo) {
|
|
@@ -1250,14 +1608,21 @@ function withCommandContext(handler) {
|
|
|
1250
1608
|
program.command("init").description("Initialize a gallery by scaning a folder for images and videos").option(
|
|
1251
1609
|
"-p, --photos <path>",
|
|
1252
1610
|
"Path to the folder where the photos are stored. Default: current working directory",
|
|
1253
|
-
|
|
1611
|
+
process4__default.default.cwd()
|
|
1254
1612
|
).option(
|
|
1255
1613
|
"-g, --gallery <path>",
|
|
1256
1614
|
"Path to the directory where the gallery will be initialized. Default: same directory as the photos folder"
|
|
1257
|
-
).option("-r, --recursive", "Recursively create galleries from all photos subdirectories", false).option("-d, --default", "Use default gallery settings instead of asking the user", false).option("-f, --force", "Force override existing galleries without asking", false).option("--cta-banner", "Add a Simple Photo Gallery call-to-action banner to the end of the gallery", false).action(withCommandContext((options, ui) => init(options, ui)));
|
|
1258
|
-
program.command("thumbnails").description("Create thumbnails for all media files in the gallery").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory",
|
|
1259
|
-
program.command("build").description("Build the HTML gallery in the specified directory").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory",
|
|
1260
|
-
|
|
1615
|
+
).option("-r, --recursive", "Recursively create galleries from all photos subdirectories", false).option("-d, --default", "Use default gallery settings instead of asking the user", false).option("-f, --force", "Force override existing galleries without asking", false).option("--cta-banner", "Add a Simple Photo Gallery call-to-action banner to the end of the gallery", false).option("--theme <package|path>", "Theme package name or local path to store in gallery.json").option("--thumbnail-size <pixels>", "Thumbnail size in pixels to store in gallery.json", Number.parseInt).option("--thumbnail-edge <mode>", "How thumbnail size is applied: auto, width, or height").action(withCommandContext((options, ui) => init(options, ui)));
|
|
1616
|
+
program.command("thumbnails").description("Create thumbnails for all media files in the gallery").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process4__default.default.cwd()).option("-r, --recursive", "Scan subdirectories recursively", false).option("--thumbnail-size <pixels>", "Override thumbnail size in pixels", Number.parseInt).option("--thumbnail-edge <mode>", "Override how thumbnail size is applied: auto, width, or height").action(withCommandContext((options, ui) => thumbnails(options, ui)));
|
|
1617
|
+
program.command("build").description("Build the HTML gallery in the specified directory").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process4__default.default.cwd()).option("-r, --recursive", "Scan subdirectories recursively", false).option("-b, --base-url <url>", "Base URL where the photos are hosted").option("-t, --thumbs-base-url <url>", "Base URL where the thumbnails are hosted").option("--no-thumbnails", "Skip creating thumbnails when building the gallery", true).option("--no-scan", "Do not scan for new photos when building the gallery", true).option(
|
|
1618
|
+
"--theme <package|path>",
|
|
1619
|
+
"Theme package name (e.g., @simple-photo-gallery/theme-modern) or local path (e.g., ./themes/my-theme)"
|
|
1620
|
+
).option("--thumbnail-size <pixels>", "Override thumbnail size in pixels", Number.parseInt).option("--thumbnail-edge <mode>", "Override how thumbnail size is applied: auto, width, or height").action(withCommandContext((options, ui) => build(options, ui)));
|
|
1621
|
+
program.command("clean").description("Remove all gallery files and folders (index.html, gallery/)").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process4__default.default.cwd()).option("-r, --recursive", "Clean subdirectories recursively", false).action(withCommandContext((options, ui) => clean(options, ui)));
|
|
1622
|
+
program.command("create-theme").description("Create a new theme template").argument("<name>", "Name of the theme to create").option("-p, --path <path>", "Path where the theme should be created. Default: ./themes/<name>").action(async (name, options, command) => {
|
|
1623
|
+
const handler = withCommandContext((opts, ui) => createTheme({ name, path: opts.path }, ui));
|
|
1624
|
+
await handler(options, command);
|
|
1625
|
+
});
|
|
1261
1626
|
program.command("telemetry").description("Manage anonymous telemetry preferences. Use 1 to enable, 0 to disable, or no argument to check status").option("-s, --state <state>", "Enable (1) or disable (0) telemetry", parseTelemetryOption).action(withCommandContext((options, ui) => telemetry(options, ui, telemetryService)));
|
|
1262
1627
|
program.parse();
|
|
1263
1628
|
//# sourceMappingURL=index.cjs.map
|