simple-photo-gallery 2.0.11-rc.9 → 2.0.12

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.js CHANGED
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import process3, { stdout } from 'process';
2
+ import process4, { stdout } from 'process';
3
3
  import { Command } from 'commander';
4
4
  import { LogLevels, createConsola } from 'consola';
5
5
  import { execSync, spawn } from 'child_process';
6
6
  import fs8, { promises } from 'fs';
7
- import path7 from 'path';
7
+ import path from 'path';
8
8
  import { Buffer } from 'buffer';
9
+ import { fileURLToPath } from 'url';
9
10
  import sharp2 from 'sharp';
10
11
  import { encode } from 'blurhash';
11
12
  import { GalleryDataSchema, GalleryDataDeprecatedSchema } from '@simple-photo-gallery/common';
@@ -76,10 +77,42 @@ async function generateBlurHash(imagePath, componentX = 4, componentY = 3) {
76
77
  }
77
78
 
78
79
  // src/modules/build/utils/index.ts
79
- path7.dirname(new URL(import.meta.url).pathname);
80
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
81
+ var SOCIAL_CARD_FONT_RELATIVE_PATH = path.join("assets", "fonts", "dejavu", "DejaVuSans-Bold.ttf");
82
+ var socialCardFontBase64;
83
+ function resolveFromCurrentDir(...segments) {
84
+ return path.resolve(__dirname, ...segments);
85
+ }
86
+ function findSocialCardFontPath() {
87
+ const fontCandidates = [
88
+ resolveFromCurrentDir("../../../../", SOCIAL_CARD_FONT_RELATIVE_PATH),
89
+ path.resolve(__dirname, "../", SOCIAL_CARD_FONT_RELATIVE_PATH),
90
+ path.resolve(__dirname, "../../", SOCIAL_CARD_FONT_RELATIVE_PATH),
91
+ path.resolve(process4.cwd(), SOCIAL_CARD_FONT_RELATIVE_PATH),
92
+ path.resolve(process4.cwd(), "../", SOCIAL_CARD_FONT_RELATIVE_PATH)
93
+ ];
94
+ for (const candidate of fontCandidates) {
95
+ if (fs8.existsSync(candidate)) {
96
+ return candidate;
97
+ }
98
+ }
99
+ return null;
100
+ }
101
+ function getSocialCardFontBase64() {
102
+ if (socialCardFontBase64 !== void 0) {
103
+ return socialCardFontBase64;
104
+ }
105
+ const fontPath = findSocialCardFontPath();
106
+ if (!fontPath) {
107
+ socialCardFontBase64 = null;
108
+ return null;
109
+ }
110
+ socialCardFontBase64 = fs8.readFileSync(fontPath).toString("base64");
111
+ return socialCardFontBase64;
112
+ }
80
113
  async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPath, ui) {
81
114
  ui?.start(`Creating social media card image`);
82
- const headerBasename = path7.basename(headerPhotoPath, path7.extname(headerPhotoPath));
115
+ const headerBasename = path.basename(headerPhotoPath, path.extname(headerPhotoPath));
83
116
  if (fs8.existsSync(ouputPath)) {
84
117
  ui?.success(`Social media card image already exists`);
85
118
  return headerBasename;
@@ -88,11 +121,21 @@ async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPa
88
121
  const resizedImageBuffer = await image.resize(1200, 631, { fit: "cover" }).jpeg({ quality: 90 }).toBuffer();
89
122
  const outputPath = ouputPath;
90
123
  await sharp2(resizedImageBuffer).toFile(outputPath);
124
+ const fontBase64 = getSocialCardFontBase64();
125
+ const fontFace = fontBase64 ? `
126
+ @font-face {
127
+ font-family: 'DejaVu Sans';
128
+ src: url('data:font/ttf;base64,${fontBase64}') format('truetype');
129
+ font-weight: 700;
130
+ font-style: normal;
131
+ }` : "";
132
+ const fontFamily = fontBase64 ? "'DejaVu Sans', Arial, sans-serif" : "Arial, sans-serif";
91
133
  const svgText = `
92
134
  <svg width="1200" height="631" xmlns="http://www.w3.org/2000/svg">
93
135
  <defs>
94
136
  <style>
95
- .title { font-family: Arial, sans-serif; font-size: 96px; font-weight: bold; fill: white; stroke: black; stroke-width: 5; paint-order: stroke; text-anchor: middle; }
137
+ ${fontFace}
138
+ .title { font-family: ${fontFamily}; font-size: 96px; font-weight: bold; fill: white; stroke: black; stroke-width: 5; paint-order: stroke; text-anchor: middle; }
96
139
  </style>
97
140
  </defs>
98
141
  <text x="600" y="250" class="title">${title}</text>
@@ -106,7 +149,7 @@ async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPa
106
149
  async function createOptimizedHeaderImage(headerPhotoPath, outputFolder, ui) {
107
150
  ui?.start(`Creating optimized header images`);
108
151
  const image = await loadImage(headerPhotoPath);
109
- const headerBasename = path7.basename(headerPhotoPath, path7.extname(headerPhotoPath));
152
+ const headerBasename = path.basename(headerPhotoPath, path.extname(headerPhotoPath));
110
153
  const generatedFiles = [];
111
154
  ui?.debug("Generating blurhash for header image");
112
155
  const blurHash = await generateBlurHash(headerPhotoPath);
@@ -115,22 +158,22 @@ async function createOptimizedHeaderImage(headerPhotoPath, outputFolder, ui) {
115
158
  ui?.debug(`Creating landscape header image ${width}`);
116
159
  const avifFilename = `${headerBasename}_landscape_${width}.avif`;
117
160
  const jpgFilename = `${headerBasename}_landscape_${width}.jpg`;
118
- if (fs8.existsSync(path7.join(outputFolder, avifFilename))) {
161
+ if (fs8.existsSync(path.join(outputFolder, avifFilename))) {
119
162
  ui?.debug(`Landscape header image ${width} AVIF already exists`);
120
163
  } else {
121
164
  await cropAndResizeImage(
122
165
  image.clone(),
123
- path7.join(outputFolder, avifFilename),
166
+ path.join(outputFolder, avifFilename),
124
167
  width,
125
168
  width * landscapeYFactor,
126
169
  "avif"
127
170
  );
128
171
  }
129
172
  generatedFiles.push(avifFilename);
130
- if (fs8.existsSync(path7.join(outputFolder, jpgFilename))) {
173
+ if (fs8.existsSync(path.join(outputFolder, jpgFilename))) {
131
174
  ui?.debug(`Landscape header image ${width} JPG already exists`);
132
175
  } else {
133
- await cropAndResizeImage(image.clone(), path7.join(outputFolder, jpgFilename), width, width * landscapeYFactor, "jpg");
176
+ await cropAndResizeImage(image.clone(), path.join(outputFolder, jpgFilename), width, width * landscapeYFactor, "jpg");
134
177
  }
135
178
  generatedFiles.push(jpgFilename);
136
179
  }
@@ -139,16 +182,16 @@ async function createOptimizedHeaderImage(headerPhotoPath, outputFolder, ui) {
139
182
  ui?.debug(`Creating portrait header image ${width}`);
140
183
  const avifFilename = `${headerBasename}_portrait_${width}.avif`;
141
184
  const jpgFilename = `${headerBasename}_portrait_${width}.jpg`;
142
- if (fs8.existsSync(path7.join(outputFolder, avifFilename))) {
185
+ if (fs8.existsSync(path.join(outputFolder, avifFilename))) {
143
186
  ui?.debug(`Portrait header image ${width} AVIF already exists`);
144
187
  } else {
145
- await cropAndResizeImage(image.clone(), path7.join(outputFolder, avifFilename), width, width * portraitYFactor, "avif");
188
+ await cropAndResizeImage(image.clone(), path.join(outputFolder, avifFilename), width, width * portraitYFactor, "avif");
146
189
  }
147
190
  generatedFiles.push(avifFilename);
148
- if (fs8.existsSync(path7.join(outputFolder, jpgFilename))) {
191
+ if (fs8.existsSync(path.join(outputFolder, jpgFilename))) {
149
192
  ui?.debug(`Portrait header image ${width} JPG already exists`);
150
193
  } else {
151
- await cropAndResizeImage(image.clone(), path7.join(outputFolder, jpgFilename), width, width * portraitYFactor, "jpg");
194
+ await cropAndResizeImage(image.clone(), path.join(outputFolder, jpgFilename), width, width * portraitYFactor, "jpg");
152
195
  }
153
196
  generatedFiles.push(jpgFilename);
154
197
  }
@@ -181,12 +224,12 @@ function cleanupOldHeaderImages(outputFolder, currentHeaderBasename, ui) {
181
224
  const landscapeMatch = file.match(/^(.+)_landscape_\d+\.(avif|jpg)$/);
182
225
  const portraitMatch = file.match(/^(.+)_portrait_\d+\.(avif|jpg)$/);
183
226
  if (landscapeMatch && landscapeMatch[1] !== currentHeaderBasename) {
184
- const filePath = path7.join(outputFolder, file);
227
+ const filePath = path.join(outputFolder, file);
185
228
  ui?.debug(`Deleting old landscape header image: ${file}`);
186
229
  fs8.unlinkSync(filePath);
187
230
  deletedCount++;
188
231
  } else if (portraitMatch && portraitMatch[1] !== currentHeaderBasename) {
189
- const filePath = path7.join(outputFolder, file);
232
+ const filePath = path.join(outputFolder, file);
190
233
  ui?.debug(`Deleting old portrait header image: ${file}`);
191
234
  fs8.unlinkSync(filePath);
192
235
  deletedCount++;
@@ -200,7 +243,7 @@ function cleanupOldHeaderImages(outputFolder, currentHeaderBasename, ui) {
200
243
  }
201
244
  function findGalleries(basePath, recursive) {
202
245
  const galleryDirs = [];
203
- const galleryJsonPath = path7.join(basePath, "gallery", "gallery.json");
246
+ const galleryJsonPath = path.join(basePath, "gallery", "gallery.json");
204
247
  if (fs8.existsSync(galleryJsonPath)) {
205
248
  galleryDirs.push(basePath);
206
249
  }
@@ -209,7 +252,7 @@ function findGalleries(basePath, recursive) {
209
252
  const entries = fs8.readdirSync(basePath, { withFileTypes: true });
210
253
  for (const entry of entries) {
211
254
  if (entry.isDirectory() && entry.name !== "gallery") {
212
- const subPath = path7.join(basePath, entry.name);
255
+ const subPath = path.join(basePath, entry.name);
213
256
  const subResults = findGalleries(subPath, recursive);
214
257
  galleryDirs.push(...subResults);
215
258
  }
@@ -268,20 +311,20 @@ function migrateGalleryJson(deprecatedGalleryData, galleryJsonPath, ui) {
268
311
  ui.start("Old gallery.json format detected. Migrating gallery.json to the new data format.");
269
312
  let mediaBasePath;
270
313
  const imagePath = deprecatedGalleryData.sections[0].images[0].path;
271
- if (imagePath && imagePath !== path7.join("..", path7.basename(imagePath))) {
272
- mediaBasePath = path7.resolve(path7.join(path7.dirname(galleryJsonPath)), path7.dirname(imagePath));
314
+ if (imagePath && imagePath !== path.join("..", path.basename(imagePath))) {
315
+ mediaBasePath = path.resolve(path.join(path.dirname(galleryJsonPath)), path.dirname(imagePath));
273
316
  }
274
317
  const sections = deprecatedGalleryData.sections.map((section) => ({
275
318
  ...section,
276
319
  images: section.images.map((image) => ({
277
320
  ...image,
278
321
  path: void 0,
279
- filename: path7.basename(image.path)
322
+ filename: path.basename(image.path)
280
323
  }))
281
324
  }));
282
325
  const galleryData = {
283
326
  ...deprecatedGalleryData,
284
- headerImage: path7.basename(deprecatedGalleryData.headerImage),
327
+ headerImage: path.basename(deprecatedGalleryData.headerImage),
285
328
  sections,
286
329
  mediaBasePath
287
330
  };
@@ -293,7 +336,7 @@ function migrateGalleryJson(deprecatedGalleryData, galleryJsonPath, ui) {
293
336
  return galleryData;
294
337
  }
295
338
  function getMediaFileType(fileName) {
296
- const ext = path7.extname(fileName).toLowerCase();
339
+ const ext = path.extname(fileName).toLowerCase();
297
340
  if (IMAGE_EXTENSIONS.has(ext)) return "image";
298
341
  if (VIDEO_EXTENSIONS.has(ext)) return "video";
299
342
  return null;
@@ -321,7 +364,7 @@ async function scanDirectory(dirPath, ui) {
321
364
  mediaFiles.push(mediaFile);
322
365
  }
323
366
  } else if (entry.isDirectory() && entry.name !== "gallery") {
324
- subGalleryDirectories.push(path7.join(dirPath, entry.name));
367
+ subGalleryDirectories.push(path.join(dirPath, entry.name));
325
368
  }
326
369
  }
327
370
  } catch (error) {
@@ -356,13 +399,13 @@ async function getGallerySettingsFromUser(galleryName, defaultImage, ui) {
356
399
  });
357
400
  return { title, description, url, headerImage };
358
401
  }
359
- async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries = [], useDefaultSettings, ui) {
360
- const galleryDir = path7.dirname(galleryJsonPath);
361
- const isSameLocation = path7.relative(scanPath, path7.join(galleryDir, "..")) === "";
402
+ async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries = [], useDefaultSettings, ctaBanner, ui) {
403
+ const galleryDir = path.dirname(galleryJsonPath);
404
+ const isSameLocation = path.relative(scanPath, path.join(galleryDir, "..")) === "";
362
405
  const mediaBasePath = isSameLocation ? void 0 : scanPath;
363
406
  const relativeSubGalleries = subGalleries.map((subGallery) => ({
364
407
  ...subGallery,
365
- headerImage: subGallery.headerImage ? path7.relative(galleryDir, subGallery.headerImage) : ""
408
+ headerImage: subGallery.headerImage ? path.relative(galleryDir, subGallery.headerImage) : ""
366
409
  }));
367
410
  let galleryData = {
368
411
  title: "My Gallery",
@@ -378,14 +421,15 @@ async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalle
378
421
  subGalleries: {
379
422
  title: "Sub Galleries",
380
423
  galleries: relativeSubGalleries
381
- }
424
+ },
425
+ ...ctaBanner !== void 0 && { ctaBanner }
382
426
  };
383
427
  if (!useDefaultSettings) {
384
428
  galleryData = {
385
429
  ...galleryData,
386
430
  ...await getGallerySettingsFromUser(
387
- path7.basename(path7.join(galleryDir, "..")),
388
- path7.basename(mediaFiles[0]?.filename || ""),
431
+ path.basename(path.join(galleryDir, "..")),
432
+ path.basename(mediaFiles[0]?.filename || ""),
389
433
  ui
390
434
  )
391
435
  };
@@ -393,8 +437,8 @@ async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalle
393
437
  await promises.writeFile(galleryJsonPath, JSON.stringify(galleryData, null, 2));
394
438
  }
395
439
  async function galleryExists(outputPath) {
396
- const galleryPath = path7.join(outputPath, "gallery");
397
- const galleryJsonPath = path7.join(galleryPath, "gallery.json");
440
+ const galleryPath = path.join(outputPath, "gallery");
441
+ const galleryJsonPath = path.join(galleryPath, "gallery.json");
398
442
  try {
399
443
  await promises.access(galleryJsonPath);
400
444
  return true;
@@ -402,7 +446,7 @@ async function galleryExists(outputPath) {
402
446
  return false;
403
447
  }
404
448
  }
405
- async function processDirectory(scanPath, outputPath, recursive, useDefaultSettings, force, ui) {
449
+ async function processDirectory(scanPath, outputPath, recursive, useDefaultSettings, force, ctaBanner, ui) {
406
450
  ui.start(`Scanning ${scanPath}`);
407
451
  let totalFiles = 0;
408
452
  let totalGalleries = 1;
@@ -413,10 +457,11 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
413
457
  for (const subGalleryDir of subGalleryDirectories) {
414
458
  const result2 = await processDirectory(
415
459
  subGalleryDir,
416
- path7.join(outputPath, path7.basename(subGalleryDir)),
460
+ path.join(outputPath, path.basename(subGalleryDir)),
417
461
  recursive,
418
462
  useDefaultSettings,
419
463
  force,
464
+ ctaBanner,
420
465
  ui
421
466
  );
422
467
  totalFiles += result2.totalFiles;
@@ -427,8 +472,8 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
427
472
  }
428
473
  }
429
474
  if (mediaFiles.length > 0 || subGalleries.length > 0) {
430
- const galleryPath = path7.join(outputPath, "gallery");
431
- const galleryJsonPath = path7.join(galleryPath, "gallery.json");
475
+ const galleryPath = path.join(outputPath, "gallery");
476
+ const galleryJsonPath = path.join(galleryPath, "gallery.json");
432
477
  const exists = await galleryExists(outputPath);
433
478
  if (exists && !force) {
434
479
  const shouldOverride = await ui.prompt(`Gallery already exists at ${galleryJsonPath}. Do you want to override it?`, {
@@ -442,7 +487,7 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
442
487
  }
443
488
  try {
444
489
  await promises.mkdir(galleryPath, { recursive: true });
445
- await createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries, useDefaultSettings, ui);
490
+ await createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries, useDefaultSettings, ctaBanner, ui);
446
491
  ui.success(
447
492
  `Create gallery with ${mediaFiles.length} files and ${subGalleries.length} subgalleries at: ${galleryJsonPath}`
448
493
  );
@@ -453,20 +498,28 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
453
498
  }
454
499
  const result = { totalFiles, totalGalleries };
455
500
  if (mediaFiles.length > 0 || subGalleries.length > 0) {
456
- const dirName = path7.basename(scanPath);
501
+ const dirName = path.basename(scanPath);
457
502
  result.subGallery = {
458
503
  title: capitalizeTitle(dirName),
459
504
  headerImage: mediaFiles[0]?.filename || "",
460
- path: path7.join("..", dirName)
505
+ path: path.join("..", dirName)
461
506
  };
462
507
  }
463
508
  return result;
464
509
  }
465
510
  async function init(options, ui) {
466
511
  try {
467
- const scanPath = path7.resolve(options.photos);
468
- const outputPath = options.gallery ? path7.resolve(options.gallery) : scanPath;
469
- const result = await processDirectory(scanPath, outputPath, options.recursive, options.default, options.force, ui);
512
+ const scanPath = path.resolve(options.photos);
513
+ const outputPath = options.gallery ? path.resolve(options.gallery) : scanPath;
514
+ const result = await processDirectory(
515
+ scanPath,
516
+ outputPath,
517
+ options.recursive,
518
+ options.default,
519
+ options.force,
520
+ options.ctaBanner,
521
+ ui
522
+ );
470
523
  ui.box(
471
524
  `Created ${result.totalGalleries} ${result.totalGalleries === 1 ? "gallery" : "galleries"} with ${result.totalFiles} media ${result.totalFiles === 1 ? "file" : "files"}`
472
525
  );
@@ -581,7 +634,7 @@ async function processImage(imagePath, thumbnailPath, thumbnailPathRetina, thumb
581
634
  const blurHash = await generateBlurHash(thumbnailPath);
582
635
  return {
583
636
  type: "image",
584
- filename: path7.basename(imagePath),
637
+ filename: path.basename(imagePath),
585
638
  alt: description,
586
639
  width: imageDimensions.width,
587
640
  height: imageDimensions.height,
@@ -612,7 +665,7 @@ async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumb
612
665
  const blurHash = await generateBlurHash(thumbnailPath);
613
666
  return {
614
667
  type: "video",
615
- filename: path7.basename(videoPath),
668
+ filename: path.basename(videoPath),
616
669
  alt: void 0,
617
670
  width: videoDimensions.width,
618
671
  height: videoDimensions.height,
@@ -628,11 +681,11 @@ async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumb
628
681
  }
629
682
  async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbnailSize, ui) {
630
683
  try {
631
- const filePath = path7.resolve(path7.join(mediaBasePath, mediaFile.filename));
684
+ const filePath = path.resolve(path.join(mediaBasePath, mediaFile.filename));
632
685
  const fileName = mediaFile.filename;
633
- const fileNameWithoutExt = path7.parse(fileName).name;
686
+ const fileNameWithoutExt = path.parse(fileName).name;
634
687
  const thumbnailFileName = `${fileNameWithoutExt}.avif`;
635
- const thumbnailPath = path7.join(thumbnailsPath, thumbnailFileName);
688
+ const thumbnailPath = path.join(thumbnailsPath, thumbnailFileName);
636
689
  const thumbnailPathRetina = thumbnailPath.replace(".avif", "@2x.avif");
637
690
  const lastMediaTimestamp = mediaFile.lastMediaTimestamp ? new Date(mediaFile.lastMediaTimestamp) : void 0;
638
691
  const verbose = ui.level === LogLevels.debug;
@@ -658,8 +711,14 @@ async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbn
658
711
  }
659
712
  updatedMediaFile.filename = mediaFile.filename;
660
713
  if (updatedMediaFile.thumbnail) {
661
- updatedMediaFile.thumbnail.path = path7.basename(thumbnailPath);
662
- updatedMediaFile.thumbnail.pathRetina = path7.basename(thumbnailPathRetina);
714
+ updatedMediaFile.thumbnail.path = path.basename(thumbnailPath);
715
+ updatedMediaFile.thumbnail.pathRetina = path.basename(thumbnailPathRetina);
716
+ if (mediaFile.thumbnail?.baseUrl) {
717
+ updatedMediaFile.thumbnail.baseUrl = mediaFile.thumbnail.baseUrl;
718
+ }
719
+ }
720
+ if (mediaFile.url) {
721
+ updatedMediaFile.url = mediaFile.url;
663
722
  }
664
723
  return updatedMediaFile;
665
724
  } catch (error) {
@@ -668,14 +727,14 @@ async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbn
668
727
  }
669
728
  }
670
729
  async function processGalleryThumbnails(galleryDir, ui) {
671
- const galleryJsonPath = path7.join(galleryDir, "gallery", "gallery.json");
672
- const thumbnailsPath = path7.join(galleryDir, "gallery", "images");
730
+ const galleryJsonPath = path.join(galleryDir, "gallery", "gallery.json");
731
+ const thumbnailsPath = path.join(galleryDir, "gallery", "images");
673
732
  ui.start(`Creating thumbnails: ${galleryDir}`);
674
733
  try {
675
734
  fs8.mkdirSync(thumbnailsPath, { recursive: true });
676
735
  const galleryData = parseGalleryJson(galleryJsonPath, ui);
677
736
  const thumbnailSize = galleryData.thumbnailSize || DEFAULT_THUMBNAIL_SIZE;
678
- const mediaBasePath = galleryData.mediaBasePath ?? path7.join(galleryDir);
737
+ const mediaBasePath = galleryData.mediaBasePath ?? path.join(galleryDir);
679
738
  let processedCount = 0;
680
739
  for (const section of galleryData.sections) {
681
740
  for (const [index, mediaFile] of section.images.entries()) {
@@ -722,8 +781,8 @@ function copyPhotos(galleryData, galleryDir, ui) {
722
781
  for (const section of galleryData.sections) {
723
782
  for (const image of section.images) {
724
783
  if (galleryData.mediaBasePath) {
725
- const sourcePath = path7.join(galleryData.mediaBasePath, image.filename);
726
- const destPath = path7.join(galleryDir, image.filename);
784
+ const sourcePath = path.join(galleryData.mediaBasePath, image.filename);
785
+ const destPath = path.join(galleryDir, image.filename);
727
786
  ui.debug(`Copying photo to ${destPath}`);
728
787
  fs8.copyFileSync(sourcePath, destPath);
729
788
  }
@@ -762,17 +821,17 @@ async function scanAndAppendNewFiles(galleryDir, galleryJsonPath, galleryData, u
762
821
  }
763
822
  async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnails, ui, baseUrl, thumbsBaseUrl) {
764
823
  ui.start(`Building gallery ${galleryDir}`);
765
- const galleryJsonPath = path7.join(galleryDir, "gallery", "gallery.json");
824
+ const galleryJsonPath = path.join(galleryDir, "gallery", "gallery.json");
766
825
  let galleryData = parseGalleryJson(galleryJsonPath, ui);
767
826
  if (scan) {
768
827
  galleryData = await scanAndAppendNewFiles(galleryDir, galleryJsonPath, galleryData, ui);
769
828
  }
770
- const socialMediaCardImagePath = path7.join(galleryDir, "gallery", "images", "social-media-card.jpg");
829
+ const socialMediaCardImagePath = path.join(galleryDir, "gallery", "images", "social-media-card.jpg");
771
830
  const mediaBasePath = galleryData.mediaBasePath;
772
831
  const mediaBaseUrl = baseUrl || galleryData.mediaBaseUrl;
773
- const headerImagePath = mediaBasePath ? path7.join(mediaBasePath, galleryData.headerImage) : path7.resolve(galleryDir, galleryData.headerImage);
774
- const imagesFolder = path7.join(galleryDir, "gallery", "images");
775
- const currentHeaderBasename = path7.basename(headerImagePath, path7.extname(headerImagePath));
832
+ const headerImagePath = mediaBasePath ? path.join(mediaBasePath, galleryData.headerImage) : path.resolve(galleryDir, galleryData.headerImage);
833
+ const imagesFolder = path.join(galleryDir, "gallery", "images");
834
+ const currentHeaderBasename = path.basename(headerImagePath, path.extname(headerImagePath));
776
835
  if (shouldCreateThumbnails) {
777
836
  if (!fs8.existsSync(imagesFolder)) {
778
837
  fs8.mkdirSync(imagesFolder, { recursive: true });
@@ -815,7 +874,7 @@ async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnail
815
874
  }
816
875
  if (!galleryData.metadata.image) {
817
876
  ui.debug("Updating gallery.json with social media card URL");
818
- galleryData.metadata.image = thumbsBaseUrl ? `${thumbsBaseUrl}/${path7.basename(socialMediaCardImagePath)}` : `${galleryData.url || ""}/${path7.relative(galleryDir, socialMediaCardImagePath)}`;
877
+ galleryData.metadata.image = thumbsBaseUrl ? `${thumbsBaseUrl}/${path.basename(socialMediaCardImagePath)}` : `${galleryData.url || ""}/${path.relative(galleryDir, socialMediaCardImagePath)}`;
819
878
  fs8.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
820
879
  }
821
880
  if (shouldCreateThumbnails) {
@@ -823,20 +882,20 @@ async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnail
823
882
  }
824
883
  ui.debug("Building gallery from template");
825
884
  try {
826
- process3.env.GALLERY_JSON_PATH = galleryJsonPath;
827
- process3.env.GALLERY_OUTPUT_DIR = path7.join(galleryDir, "gallery");
885
+ process4.env.GALLERY_JSON_PATH = galleryJsonPath;
886
+ process4.env.GALLERY_OUTPUT_DIR = path.join(galleryDir, "gallery");
828
887
  execSync("npx astro build", { cwd: templateDir, stdio: ui.level === LogLevels.debug ? "inherit" : "ignore" });
829
888
  } catch (error) {
830
889
  ui.error(`Build failed for ${galleryDir}`);
831
890
  throw error;
832
891
  }
833
- const outputDir = path7.join(galleryDir, "gallery");
834
- const buildDir = path7.join(outputDir, "_build");
892
+ const outputDir = path.join(galleryDir, "gallery");
893
+ const buildDir = path.join(outputDir, "_build");
835
894
  ui.debug(`Copying build output to ${outputDir}`);
836
895
  fs8.cpSync(buildDir, outputDir, { recursive: true });
837
896
  ui.debug("Moving index.html to gallery directory");
838
- fs8.copyFileSync(path7.join(outputDir, "index.html"), path7.join(galleryDir, "index.html"));
839
- fs8.rmSync(path7.join(outputDir, "index.html"));
897
+ fs8.copyFileSync(path.join(outputDir, "index.html"), path.join(galleryDir, "index.html"));
898
+ fs8.rmSync(path.join(outputDir, "index.html"));
840
899
  ui.debug("Cleaning up build directory");
841
900
  fs8.rmSync(buildDir, { recursive: true, force: true });
842
901
  ui.success(`Gallery built successfully`);
@@ -849,12 +908,12 @@ async function build(options, ui) {
849
908
  return { processedGalleryCount: 0 };
850
909
  }
851
910
  const themePath = await import.meta.resolve("@simple-photo-gallery/theme-modern/package.json");
852
- const themeDir = path7.dirname(new URL(themePath).pathname);
911
+ const themeDir = path.dirname(new URL(themePath).pathname);
853
912
  let totalGalleries = 0;
854
913
  for (const dir of galleryDirs) {
855
- const baseUrl = options.baseUrl ? `${options.baseUrl}${path7.relative(options.gallery, dir)}` : void 0;
856
- const thumbsBaseUrl = options.thumbsBaseUrl ? `${options.thumbsBaseUrl}${path7.relative(options.gallery, dir)}` : void 0;
857
- await buildGallery(path7.resolve(dir), themeDir, options.scan, options.thumbnails, ui, baseUrl, thumbsBaseUrl);
914
+ const baseUrl = options.baseUrl ? `${options.baseUrl}${path.relative(options.gallery, dir)}` : void 0;
915
+ const thumbsBaseUrl = options.thumbsBaseUrl ? `${options.thumbsBaseUrl}${path.relative(options.gallery, dir)}` : void 0;
916
+ await buildGallery(path.resolve(dir), themeDir, options.scan, options.thumbnails, ui, baseUrl, thumbsBaseUrl);
858
917
  ++totalGalleries;
859
918
  }
860
919
  ui.box(`Built ${totalGalleries} ${totalGalleries === 1 ? "gallery" : "galleries"} successfully`);
@@ -870,7 +929,7 @@ async function build(options, ui) {
870
929
  }
871
930
  async function cleanGallery(galleryDir, ui) {
872
931
  let filesRemoved = 0;
873
- const indexHtmlPath = path7.join(galleryDir, "index.html");
932
+ const indexHtmlPath = path.join(galleryDir, "index.html");
874
933
  if (fs8.existsSync(indexHtmlPath)) {
875
934
  try {
876
935
  fs8.rmSync(indexHtmlPath);
@@ -880,7 +939,7 @@ async function cleanGallery(galleryDir, ui) {
880
939
  ui?.warn(`Failed to remove index.html: ${error}`);
881
940
  }
882
941
  }
883
- const galleryPath = path7.join(galleryDir, "gallery");
942
+ const galleryPath = path.join(galleryDir, "gallery");
884
943
  if (fs8.existsSync(galleryPath)) {
885
944
  try {
886
945
  fs8.rmSync(galleryPath, { recursive: true, force: true });
@@ -899,7 +958,7 @@ async function cleanGallery(galleryDir, ui) {
899
958
  }
900
959
  async function clean(options, ui) {
901
960
  try {
902
- const basePath = path7.resolve(options.gallery);
961
+ const basePath = path.resolve(options.gallery);
903
962
  if (!fs8.existsSync(basePath)) {
904
963
  ui.error(`Directory does not exist: ${basePath}`);
905
964
  return { processedGalleryCount: 0 };
@@ -948,7 +1007,7 @@ var ApiTelemetryClient = class {
948
1007
  axios.post(this.endpoint, event, {
949
1008
  headers: {
950
1009
  "content-type": "application/json",
951
- "user-agent": `simple-photo-gallery/${event.packageVersion} (${process3.platform}; ${process3.arch})`
1010
+ "user-agent": `simple-photo-gallery/${event.packageVersion} (${process4.platform}; ${process4.arch})`
952
1011
  }
953
1012
  });
954
1013
  } catch {
@@ -985,11 +1044,11 @@ var TelemetryService = class {
985
1044
  if (override) {
986
1045
  return override === "1";
987
1046
  }
988
- if (process3.env.CI || process3.env.DO_NOT_TRACK) {
1047
+ if (process4.env.CI || process4.env.DO_NOT_TRACK) {
989
1048
  return false;
990
1049
  }
991
- if (process3.env.SPG_TELEMETRY) {
992
- return process3.env.SPG_TELEMETRY === "1";
1050
+ if (process4.env.SPG_TELEMETRY) {
1051
+ return process4.env.SPG_TELEMETRY === "1";
993
1052
  }
994
1053
  const stored = this.getStoredPreference();
995
1054
  if (stored === void 0) {
@@ -1031,7 +1090,7 @@ var TelemetryService = class {
1031
1090
  durationMs: now - startedAt,
1032
1091
  packageName: this.packageName,
1033
1092
  packageVersion: this.packageVersion,
1034
- nodeVersion: process3.version,
1093
+ nodeVersion: process4.version,
1035
1094
  osPlatform: os.platform(),
1036
1095
  osRelease: os.release(),
1037
1096
  osArch: os.arch(),
@@ -1065,7 +1124,7 @@ var TelemetryService = class {
1065
1124
  /** Returns the telemetry client. */
1066
1125
  getClient() {
1067
1126
  if (!this.client) {
1068
- switch (process3.env.SPG_TELEMETRY_PROVIDER) {
1127
+ switch (process4.env.SPG_TELEMETRY_PROVIDER) {
1069
1128
  case "none": {
1070
1129
  this.client = void 0;
1071
1130
  break;
@@ -1157,7 +1216,7 @@ async function waitForUpdateCheck(checkPromise) {
1157
1216
  // package.json
1158
1217
  var package_default = {
1159
1218
  name: "simple-photo-gallery",
1160
- version: "2.0.11-rc.9"};
1219
+ version: "2.0.12"};
1161
1220
 
1162
1221
  // src/index.ts
1163
1222
  var program = new Command();
@@ -1199,7 +1258,7 @@ function withCommandContext(handler) {
1199
1258
  } catch (error) {
1200
1259
  ui.debug(error);
1201
1260
  errorInfo = error instanceof Error ? { name: error.name, message: error.message } : { name: "UnknownError", message: String(error) };
1202
- process3.exitCode = 1;
1261
+ process4.exitCode = 1;
1203
1262
  }
1204
1263
  const updateInfo = await waitForUpdateCheck(updateCheckPromise);
1205
1264
  if (updateInfo) {
@@ -1221,14 +1280,14 @@ function withCommandContext(handler) {
1221
1280
  program.command("init").description("Initialize a gallery by scaning a folder for images and videos").option(
1222
1281
  "-p, --photos <path>",
1223
1282
  "Path to the folder where the photos are stored. Default: current working directory",
1224
- process3.cwd()
1283
+ process4.cwd()
1225
1284
  ).option(
1226
1285
  "-g, --gallery <path>",
1227
1286
  "Path to the directory where the gallery will be initialized. Default: same directory as the photos folder"
1228
- ).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).action(withCommandContext((options, ui) => init(options, ui)));
1229
- 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", process3.cwd()).option("-r, --recursive", "Scan subdirectories recursively", false).action(withCommandContext((options, ui) => thumbnails(options, ui)));
1230
- 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", process3.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).action(withCommandContext((options, ui) => build(options, ui)));
1231
- 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", process3.cwd()).option("-r, --recursive", "Clean subdirectories recursively", false).action(withCommandContext((options, ui) => clean(options, ui)));
1287
+ ).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)));
1288
+ 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.cwd()).option("-r, --recursive", "Scan subdirectories recursively", false).action(withCommandContext((options, ui) => thumbnails(options, ui)));
1289
+ 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.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).action(withCommandContext((options, ui) => build(options, ui)));
1290
+ 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.cwd()).option("-r, --recursive", "Clean subdirectories recursively", false).action(withCommandContext((options, ui) => clean(options, ui)));
1232
1291
  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)));
1233
1292
  program.parse();
1234
1293
  //# sourceMappingURL=index.js.map