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.cjs CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- var process3 = require('process');
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');
8
8
  var fs8 = require('fs');
9
- var path7 = require('path');
9
+ var path = require('path');
10
10
  var buffer = require('buffer');
11
+ var url = require('url');
11
12
  var sharp2 = require('sharp');
12
13
  var blurhash = require('blurhash');
13
14
  var common = require('@simple-photo-gallery/common');
@@ -21,9 +22,9 @@ var semverParser = require('semver-parser');
21
22
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
22
23
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
23
24
 
24
- var process3__default = /*#__PURE__*/_interopDefault(process3);
25
+ var process4__default = /*#__PURE__*/_interopDefault(process4);
25
26
  var fs8__default = /*#__PURE__*/_interopDefault(fs8);
26
- var path7__default = /*#__PURE__*/_interopDefault(path7);
27
+ var path__default = /*#__PURE__*/_interopDefault(path);
27
28
  var sharp2__default = /*#__PURE__*/_interopDefault(sharp2);
28
29
  var ExifReader__default = /*#__PURE__*/_interopDefault(ExifReader);
29
30
  var ffprobe__default = /*#__PURE__*/_interopDefault(ffprobe);
@@ -91,10 +92,42 @@ async function generateBlurHash(imagePath, componentX = 4, componentY = 3) {
91
92
  }
92
93
 
93
94
  // src/modules/build/utils/index.ts
94
- path7__default.default.dirname(new URL((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))).pathname);
95
+ var __dirname$1 = path__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))));
96
+ var SOCIAL_CARD_FONT_RELATIVE_PATH = path__default.default.join("assets", "fonts", "dejavu", "DejaVuSans-Bold.ttf");
97
+ var socialCardFontBase64;
98
+ function resolveFromCurrentDir(...segments) {
99
+ return path__default.default.resolve(__dirname$1, ...segments);
100
+ }
101
+ function findSocialCardFontPath() {
102
+ const fontCandidates = [
103
+ resolveFromCurrentDir("../../../../", SOCIAL_CARD_FONT_RELATIVE_PATH),
104
+ path__default.default.resolve(__dirname$1, "../", SOCIAL_CARD_FONT_RELATIVE_PATH),
105
+ path__default.default.resolve(__dirname$1, "../../", SOCIAL_CARD_FONT_RELATIVE_PATH),
106
+ path__default.default.resolve(process4__default.default.cwd(), SOCIAL_CARD_FONT_RELATIVE_PATH),
107
+ path__default.default.resolve(process4__default.default.cwd(), "../", SOCIAL_CARD_FONT_RELATIVE_PATH)
108
+ ];
109
+ for (const candidate of fontCandidates) {
110
+ if (fs8__default.default.existsSync(candidate)) {
111
+ return candidate;
112
+ }
113
+ }
114
+ return null;
115
+ }
116
+ function getSocialCardFontBase64() {
117
+ if (socialCardFontBase64 !== void 0) {
118
+ return socialCardFontBase64;
119
+ }
120
+ const fontPath = findSocialCardFontPath();
121
+ if (!fontPath) {
122
+ socialCardFontBase64 = null;
123
+ return null;
124
+ }
125
+ socialCardFontBase64 = fs8__default.default.readFileSync(fontPath).toString("base64");
126
+ return socialCardFontBase64;
127
+ }
95
128
  async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPath, ui) {
96
129
  ui?.start(`Creating social media card image`);
97
- const headerBasename = path7__default.default.basename(headerPhotoPath, path7__default.default.extname(headerPhotoPath));
130
+ const headerBasename = path__default.default.basename(headerPhotoPath, path__default.default.extname(headerPhotoPath));
98
131
  if (fs8__default.default.existsSync(ouputPath)) {
99
132
  ui?.success(`Social media card image already exists`);
100
133
  return headerBasename;
@@ -103,11 +136,21 @@ async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPa
103
136
  const resizedImageBuffer = await image.resize(1200, 631, { fit: "cover" }).jpeg({ quality: 90 }).toBuffer();
104
137
  const outputPath = ouputPath;
105
138
  await sharp2__default.default(resizedImageBuffer).toFile(outputPath);
139
+ const fontBase64 = getSocialCardFontBase64();
140
+ const fontFace = fontBase64 ? `
141
+ @font-face {
142
+ font-family: 'DejaVu Sans';
143
+ src: url('data:font/ttf;base64,${fontBase64}') format('truetype');
144
+ font-weight: 700;
145
+ font-style: normal;
146
+ }` : "";
147
+ const fontFamily = fontBase64 ? "'DejaVu Sans', Arial, sans-serif" : "Arial, sans-serif";
106
148
  const svgText = `
107
149
  <svg width="1200" height="631" xmlns="http://www.w3.org/2000/svg">
108
150
  <defs>
109
151
  <style>
110
- .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; }
152
+ ${fontFace}
153
+ .title { font-family: ${fontFamily}; font-size: 96px; font-weight: bold; fill: white; stroke: black; stroke-width: 5; paint-order: stroke; text-anchor: middle; }
111
154
  </style>
112
155
  </defs>
113
156
  <text x="600" y="250" class="title">${title}</text>
@@ -121,7 +164,7 @@ async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPa
121
164
  async function createOptimizedHeaderImage(headerPhotoPath, outputFolder, ui) {
122
165
  ui?.start(`Creating optimized header images`);
123
166
  const image = await loadImage(headerPhotoPath);
124
- const headerBasename = path7__default.default.basename(headerPhotoPath, path7__default.default.extname(headerPhotoPath));
167
+ const headerBasename = path__default.default.basename(headerPhotoPath, path__default.default.extname(headerPhotoPath));
125
168
  const generatedFiles = [];
126
169
  ui?.debug("Generating blurhash for header image");
127
170
  const blurHash = await generateBlurHash(headerPhotoPath);
@@ -130,22 +173,22 @@ async function createOptimizedHeaderImage(headerPhotoPath, outputFolder, ui) {
130
173
  ui?.debug(`Creating landscape header image ${width}`);
131
174
  const avifFilename = `${headerBasename}_landscape_${width}.avif`;
132
175
  const jpgFilename = `${headerBasename}_landscape_${width}.jpg`;
133
- if (fs8__default.default.existsSync(path7__default.default.join(outputFolder, avifFilename))) {
176
+ if (fs8__default.default.existsSync(path__default.default.join(outputFolder, avifFilename))) {
134
177
  ui?.debug(`Landscape header image ${width} AVIF already exists`);
135
178
  } else {
136
179
  await cropAndResizeImage(
137
180
  image.clone(),
138
- path7__default.default.join(outputFolder, avifFilename),
181
+ path__default.default.join(outputFolder, avifFilename),
139
182
  width,
140
183
  width * landscapeYFactor,
141
184
  "avif"
142
185
  );
143
186
  }
144
187
  generatedFiles.push(avifFilename);
145
- if (fs8__default.default.existsSync(path7__default.default.join(outputFolder, jpgFilename))) {
188
+ if (fs8__default.default.existsSync(path__default.default.join(outputFolder, jpgFilename))) {
146
189
  ui?.debug(`Landscape header image ${width} JPG already exists`);
147
190
  } else {
148
- await cropAndResizeImage(image.clone(), path7__default.default.join(outputFolder, jpgFilename), width, width * landscapeYFactor, "jpg");
191
+ await cropAndResizeImage(image.clone(), path__default.default.join(outputFolder, jpgFilename), width, width * landscapeYFactor, "jpg");
149
192
  }
150
193
  generatedFiles.push(jpgFilename);
151
194
  }
@@ -154,16 +197,16 @@ async function createOptimizedHeaderImage(headerPhotoPath, outputFolder, ui) {
154
197
  ui?.debug(`Creating portrait header image ${width}`);
155
198
  const avifFilename = `${headerBasename}_portrait_${width}.avif`;
156
199
  const jpgFilename = `${headerBasename}_portrait_${width}.jpg`;
157
- if (fs8__default.default.existsSync(path7__default.default.join(outputFolder, avifFilename))) {
200
+ if (fs8__default.default.existsSync(path__default.default.join(outputFolder, avifFilename))) {
158
201
  ui?.debug(`Portrait header image ${width} AVIF already exists`);
159
202
  } else {
160
- await cropAndResizeImage(image.clone(), path7__default.default.join(outputFolder, avifFilename), width, width * portraitYFactor, "avif");
203
+ await cropAndResizeImage(image.clone(), path__default.default.join(outputFolder, avifFilename), width, width * portraitYFactor, "avif");
161
204
  }
162
205
  generatedFiles.push(avifFilename);
163
- if (fs8__default.default.existsSync(path7__default.default.join(outputFolder, jpgFilename))) {
206
+ if (fs8__default.default.existsSync(path__default.default.join(outputFolder, jpgFilename))) {
164
207
  ui?.debug(`Portrait header image ${width} JPG already exists`);
165
208
  } else {
166
- await cropAndResizeImage(image.clone(), path7__default.default.join(outputFolder, jpgFilename), width, width * portraitYFactor, "jpg");
209
+ await cropAndResizeImage(image.clone(), path__default.default.join(outputFolder, jpgFilename), width, width * portraitYFactor, "jpg");
167
210
  }
168
211
  generatedFiles.push(jpgFilename);
169
212
  }
@@ -196,12 +239,12 @@ function cleanupOldHeaderImages(outputFolder, currentHeaderBasename, ui) {
196
239
  const landscapeMatch = file.match(/^(.+)_landscape_\d+\.(avif|jpg)$/);
197
240
  const portraitMatch = file.match(/^(.+)_portrait_\d+\.(avif|jpg)$/);
198
241
  if (landscapeMatch && landscapeMatch[1] !== currentHeaderBasename) {
199
- const filePath = path7__default.default.join(outputFolder, file);
242
+ const filePath = path__default.default.join(outputFolder, file);
200
243
  ui?.debug(`Deleting old landscape header image: ${file}`);
201
244
  fs8__default.default.unlinkSync(filePath);
202
245
  deletedCount++;
203
246
  } else if (portraitMatch && portraitMatch[1] !== currentHeaderBasename) {
204
- const filePath = path7__default.default.join(outputFolder, file);
247
+ const filePath = path__default.default.join(outputFolder, file);
205
248
  ui?.debug(`Deleting old portrait header image: ${file}`);
206
249
  fs8__default.default.unlinkSync(filePath);
207
250
  deletedCount++;
@@ -215,7 +258,7 @@ function cleanupOldHeaderImages(outputFolder, currentHeaderBasename, ui) {
215
258
  }
216
259
  function findGalleries(basePath, recursive) {
217
260
  const galleryDirs = [];
218
- const galleryJsonPath = path7__default.default.join(basePath, "gallery", "gallery.json");
261
+ const galleryJsonPath = path__default.default.join(basePath, "gallery", "gallery.json");
219
262
  if (fs8__default.default.existsSync(galleryJsonPath)) {
220
263
  galleryDirs.push(basePath);
221
264
  }
@@ -224,7 +267,7 @@ function findGalleries(basePath, recursive) {
224
267
  const entries = fs8__default.default.readdirSync(basePath, { withFileTypes: true });
225
268
  for (const entry of entries) {
226
269
  if (entry.isDirectory() && entry.name !== "gallery") {
227
- const subPath = path7__default.default.join(basePath, entry.name);
270
+ const subPath = path__default.default.join(basePath, entry.name);
228
271
  const subResults = findGalleries(subPath, recursive);
229
272
  galleryDirs.push(...subResults);
230
273
  }
@@ -283,20 +326,20 @@ function migrateGalleryJson(deprecatedGalleryData, galleryJsonPath, ui) {
283
326
  ui.start("Old gallery.json format detected. Migrating gallery.json to the new data format.");
284
327
  let mediaBasePath;
285
328
  const imagePath = deprecatedGalleryData.sections[0].images[0].path;
286
- if (imagePath && imagePath !== path7__default.default.join("..", path7__default.default.basename(imagePath))) {
287
- mediaBasePath = path7__default.default.resolve(path7__default.default.join(path7__default.default.dirname(galleryJsonPath)), path7__default.default.dirname(imagePath));
329
+ if (imagePath && imagePath !== path__default.default.join("..", path__default.default.basename(imagePath))) {
330
+ mediaBasePath = path__default.default.resolve(path__default.default.join(path__default.default.dirname(galleryJsonPath)), path__default.default.dirname(imagePath));
288
331
  }
289
332
  const sections = deprecatedGalleryData.sections.map((section) => ({
290
333
  ...section,
291
334
  images: section.images.map((image) => ({
292
335
  ...image,
293
336
  path: void 0,
294
- filename: path7__default.default.basename(image.path)
337
+ filename: path__default.default.basename(image.path)
295
338
  }))
296
339
  }));
297
340
  const galleryData = {
298
341
  ...deprecatedGalleryData,
299
- headerImage: path7__default.default.basename(deprecatedGalleryData.headerImage),
342
+ headerImage: path__default.default.basename(deprecatedGalleryData.headerImage),
300
343
  sections,
301
344
  mediaBasePath
302
345
  };
@@ -308,7 +351,7 @@ function migrateGalleryJson(deprecatedGalleryData, galleryJsonPath, ui) {
308
351
  return galleryData;
309
352
  }
310
353
  function getMediaFileType(fileName) {
311
- const ext = path7__default.default.extname(fileName).toLowerCase();
354
+ const ext = path__default.default.extname(fileName).toLowerCase();
312
355
  if (IMAGE_EXTENSIONS.has(ext)) return "image";
313
356
  if (VIDEO_EXTENSIONS.has(ext)) return "video";
314
357
  return null;
@@ -336,7 +379,7 @@ async function scanDirectory(dirPath, ui) {
336
379
  mediaFiles.push(mediaFile);
337
380
  }
338
381
  } else if (entry.isDirectory() && entry.name !== "gallery") {
339
- subGalleryDirectories.push(path7__default.default.join(dirPath, entry.name));
382
+ subGalleryDirectories.push(path__default.default.join(dirPath, entry.name));
340
383
  }
341
384
  }
342
385
  } catch (error) {
@@ -371,13 +414,13 @@ async function getGallerySettingsFromUser(galleryName, defaultImage, ui) {
371
414
  });
372
415
  return { title, description, url, headerImage };
373
416
  }
374
- async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries = [], useDefaultSettings, ui) {
375
- const galleryDir = path7__default.default.dirname(galleryJsonPath);
376
- const isSameLocation = path7__default.default.relative(scanPath, path7__default.default.join(galleryDir, "..")) === "";
417
+ async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries = [], useDefaultSettings, ctaBanner, ui) {
418
+ const galleryDir = path__default.default.dirname(galleryJsonPath);
419
+ const isSameLocation = path__default.default.relative(scanPath, path__default.default.join(galleryDir, "..")) === "";
377
420
  const mediaBasePath = isSameLocation ? void 0 : scanPath;
378
421
  const relativeSubGalleries = subGalleries.map((subGallery) => ({
379
422
  ...subGallery,
380
- headerImage: subGallery.headerImage ? path7__default.default.relative(galleryDir, subGallery.headerImage) : ""
423
+ headerImage: subGallery.headerImage ? path__default.default.relative(galleryDir, subGallery.headerImage) : ""
381
424
  }));
382
425
  let galleryData = {
383
426
  title: "My Gallery",
@@ -393,14 +436,15 @@ async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalle
393
436
  subGalleries: {
394
437
  title: "Sub Galleries",
395
438
  galleries: relativeSubGalleries
396
- }
439
+ },
440
+ ...ctaBanner !== void 0 && { ctaBanner }
397
441
  };
398
442
  if (!useDefaultSettings) {
399
443
  galleryData = {
400
444
  ...galleryData,
401
445
  ...await getGallerySettingsFromUser(
402
- path7__default.default.basename(path7__default.default.join(galleryDir, "..")),
403
- path7__default.default.basename(mediaFiles[0]?.filename || ""),
446
+ path__default.default.basename(path__default.default.join(galleryDir, "..")),
447
+ path__default.default.basename(mediaFiles[0]?.filename || ""),
404
448
  ui
405
449
  )
406
450
  };
@@ -408,8 +452,8 @@ async function createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalle
408
452
  await fs8.promises.writeFile(galleryJsonPath, JSON.stringify(galleryData, null, 2));
409
453
  }
410
454
  async function galleryExists(outputPath) {
411
- const galleryPath = path7__default.default.join(outputPath, "gallery");
412
- const galleryJsonPath = path7__default.default.join(galleryPath, "gallery.json");
455
+ const galleryPath = path__default.default.join(outputPath, "gallery");
456
+ const galleryJsonPath = path__default.default.join(galleryPath, "gallery.json");
413
457
  try {
414
458
  await fs8.promises.access(galleryJsonPath);
415
459
  return true;
@@ -417,7 +461,7 @@ async function galleryExists(outputPath) {
417
461
  return false;
418
462
  }
419
463
  }
420
- async function processDirectory(scanPath, outputPath, recursive, useDefaultSettings, force, ui) {
464
+ async function processDirectory(scanPath, outputPath, recursive, useDefaultSettings, force, ctaBanner, ui) {
421
465
  ui.start(`Scanning ${scanPath}`);
422
466
  let totalFiles = 0;
423
467
  let totalGalleries = 1;
@@ -428,10 +472,11 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
428
472
  for (const subGalleryDir of subGalleryDirectories) {
429
473
  const result2 = await processDirectory(
430
474
  subGalleryDir,
431
- path7__default.default.join(outputPath, path7__default.default.basename(subGalleryDir)),
475
+ path__default.default.join(outputPath, path__default.default.basename(subGalleryDir)),
432
476
  recursive,
433
477
  useDefaultSettings,
434
478
  force,
479
+ ctaBanner,
435
480
  ui
436
481
  );
437
482
  totalFiles += result2.totalFiles;
@@ -442,8 +487,8 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
442
487
  }
443
488
  }
444
489
  if (mediaFiles.length > 0 || subGalleries.length > 0) {
445
- const galleryPath = path7__default.default.join(outputPath, "gallery");
446
- const galleryJsonPath = path7__default.default.join(galleryPath, "gallery.json");
490
+ const galleryPath = path__default.default.join(outputPath, "gallery");
491
+ const galleryJsonPath = path__default.default.join(galleryPath, "gallery.json");
447
492
  const exists = await galleryExists(outputPath);
448
493
  if (exists && !force) {
449
494
  const shouldOverride = await ui.prompt(`Gallery already exists at ${galleryJsonPath}. Do you want to override it?`, {
@@ -457,7 +502,7 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
457
502
  }
458
503
  try {
459
504
  await fs8.promises.mkdir(galleryPath, { recursive: true });
460
- await createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries, useDefaultSettings, ui);
505
+ await createGalleryJson(mediaFiles, galleryJsonPath, scanPath, subGalleries, useDefaultSettings, ctaBanner, ui);
461
506
  ui.success(
462
507
  `Create gallery with ${mediaFiles.length} files and ${subGalleries.length} subgalleries at: ${galleryJsonPath}`
463
508
  );
@@ -468,20 +513,28 @@ async function processDirectory(scanPath, outputPath, recursive, useDefaultSetti
468
513
  }
469
514
  const result = { totalFiles, totalGalleries };
470
515
  if (mediaFiles.length > 0 || subGalleries.length > 0) {
471
- const dirName = path7__default.default.basename(scanPath);
516
+ const dirName = path__default.default.basename(scanPath);
472
517
  result.subGallery = {
473
518
  title: capitalizeTitle(dirName),
474
519
  headerImage: mediaFiles[0]?.filename || "",
475
- path: path7__default.default.join("..", dirName)
520
+ path: path__default.default.join("..", dirName)
476
521
  };
477
522
  }
478
523
  return result;
479
524
  }
480
525
  async function init(options, ui) {
481
526
  try {
482
- const scanPath = path7__default.default.resolve(options.photos);
483
- const outputPath = options.gallery ? path7__default.default.resolve(options.gallery) : scanPath;
484
- const result = await processDirectory(scanPath, outputPath, options.recursive, options.default, options.force, ui);
527
+ const scanPath = path__default.default.resolve(options.photos);
528
+ const outputPath = options.gallery ? path__default.default.resolve(options.gallery) : scanPath;
529
+ const result = await processDirectory(
530
+ scanPath,
531
+ outputPath,
532
+ options.recursive,
533
+ options.default,
534
+ options.force,
535
+ options.ctaBanner,
536
+ ui
537
+ );
485
538
  ui.box(
486
539
  `Created ${result.totalGalleries} ${result.totalGalleries === 1 ? "gallery" : "galleries"} with ${result.totalFiles} media ${result.totalFiles === 1 ? "file" : "files"}`
487
540
  );
@@ -596,7 +649,7 @@ async function processImage(imagePath, thumbnailPath, thumbnailPathRetina, thumb
596
649
  const blurHash = await generateBlurHash(thumbnailPath);
597
650
  return {
598
651
  type: "image",
599
- filename: path7__default.default.basename(imagePath),
652
+ filename: path__default.default.basename(imagePath),
600
653
  alt: description,
601
654
  width: imageDimensions.width,
602
655
  height: imageDimensions.height,
@@ -627,7 +680,7 @@ async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumb
627
680
  const blurHash = await generateBlurHash(thumbnailPath);
628
681
  return {
629
682
  type: "video",
630
- filename: path7__default.default.basename(videoPath),
683
+ filename: path__default.default.basename(videoPath),
631
684
  alt: void 0,
632
685
  width: videoDimensions.width,
633
686
  height: videoDimensions.height,
@@ -643,11 +696,11 @@ async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumb
643
696
  }
644
697
  async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbnailSize, ui) {
645
698
  try {
646
- const filePath = path7__default.default.resolve(path7__default.default.join(mediaBasePath, mediaFile.filename));
699
+ const filePath = path__default.default.resolve(path__default.default.join(mediaBasePath, mediaFile.filename));
647
700
  const fileName = mediaFile.filename;
648
- const fileNameWithoutExt = path7__default.default.parse(fileName).name;
701
+ const fileNameWithoutExt = path__default.default.parse(fileName).name;
649
702
  const thumbnailFileName = `${fileNameWithoutExt}.avif`;
650
- const thumbnailPath = path7__default.default.join(thumbnailsPath, thumbnailFileName);
703
+ const thumbnailPath = path__default.default.join(thumbnailsPath, thumbnailFileName);
651
704
  const thumbnailPathRetina = thumbnailPath.replace(".avif", "@2x.avif");
652
705
  const lastMediaTimestamp = mediaFile.lastMediaTimestamp ? new Date(mediaFile.lastMediaTimestamp) : void 0;
653
706
  const verbose = ui.level === consola.LogLevels.debug;
@@ -673,8 +726,14 @@ async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbn
673
726
  }
674
727
  updatedMediaFile.filename = mediaFile.filename;
675
728
  if (updatedMediaFile.thumbnail) {
676
- updatedMediaFile.thumbnail.path = path7__default.default.basename(thumbnailPath);
677
- updatedMediaFile.thumbnail.pathRetina = path7__default.default.basename(thumbnailPathRetina);
729
+ updatedMediaFile.thumbnail.path = path__default.default.basename(thumbnailPath);
730
+ updatedMediaFile.thumbnail.pathRetina = path__default.default.basename(thumbnailPathRetina);
731
+ if (mediaFile.thumbnail?.baseUrl) {
732
+ updatedMediaFile.thumbnail.baseUrl = mediaFile.thumbnail.baseUrl;
733
+ }
734
+ }
735
+ if (mediaFile.url) {
736
+ updatedMediaFile.url = mediaFile.url;
678
737
  }
679
738
  return updatedMediaFile;
680
739
  } catch (error) {
@@ -683,14 +742,14 @@ async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbn
683
742
  }
684
743
  }
685
744
  async function processGalleryThumbnails(galleryDir, ui) {
686
- const galleryJsonPath = path7__default.default.join(galleryDir, "gallery", "gallery.json");
687
- const thumbnailsPath = path7__default.default.join(galleryDir, "gallery", "images");
745
+ const galleryJsonPath = path__default.default.join(galleryDir, "gallery", "gallery.json");
746
+ const thumbnailsPath = path__default.default.join(galleryDir, "gallery", "images");
688
747
  ui.start(`Creating thumbnails: ${galleryDir}`);
689
748
  try {
690
749
  fs8__default.default.mkdirSync(thumbnailsPath, { recursive: true });
691
750
  const galleryData = parseGalleryJson(galleryJsonPath, ui);
692
751
  const thumbnailSize = galleryData.thumbnailSize || DEFAULT_THUMBNAIL_SIZE;
693
- const mediaBasePath = galleryData.mediaBasePath ?? path7__default.default.join(galleryDir);
752
+ const mediaBasePath = galleryData.mediaBasePath ?? path__default.default.join(galleryDir);
694
753
  let processedCount = 0;
695
754
  for (const section of galleryData.sections) {
696
755
  for (const [index, mediaFile] of section.images.entries()) {
@@ -737,8 +796,8 @@ function copyPhotos(galleryData, galleryDir, ui) {
737
796
  for (const section of galleryData.sections) {
738
797
  for (const image of section.images) {
739
798
  if (galleryData.mediaBasePath) {
740
- const sourcePath = path7__default.default.join(galleryData.mediaBasePath, image.filename);
741
- const destPath = path7__default.default.join(galleryDir, image.filename);
799
+ const sourcePath = path__default.default.join(galleryData.mediaBasePath, image.filename);
800
+ const destPath = path__default.default.join(galleryDir, image.filename);
742
801
  ui.debug(`Copying photo to ${destPath}`);
743
802
  fs8__default.default.copyFileSync(sourcePath, destPath);
744
803
  }
@@ -777,17 +836,17 @@ async function scanAndAppendNewFiles(galleryDir, galleryJsonPath, galleryData, u
777
836
  }
778
837
  async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnails, ui, baseUrl, thumbsBaseUrl) {
779
838
  ui.start(`Building gallery ${galleryDir}`);
780
- const galleryJsonPath = path7__default.default.join(galleryDir, "gallery", "gallery.json");
839
+ const galleryJsonPath = path__default.default.join(galleryDir, "gallery", "gallery.json");
781
840
  let galleryData = parseGalleryJson(galleryJsonPath, ui);
782
841
  if (scan) {
783
842
  galleryData = await scanAndAppendNewFiles(galleryDir, galleryJsonPath, galleryData, ui);
784
843
  }
785
- const socialMediaCardImagePath = path7__default.default.join(galleryDir, "gallery", "images", "social-media-card.jpg");
844
+ const socialMediaCardImagePath = path__default.default.join(galleryDir, "gallery", "images", "social-media-card.jpg");
786
845
  const mediaBasePath = galleryData.mediaBasePath;
787
846
  const mediaBaseUrl = baseUrl || galleryData.mediaBaseUrl;
788
- const headerImagePath = mediaBasePath ? path7__default.default.join(mediaBasePath, galleryData.headerImage) : path7__default.default.resolve(galleryDir, galleryData.headerImage);
789
- const imagesFolder = path7__default.default.join(galleryDir, "gallery", "images");
790
- const currentHeaderBasename = path7__default.default.basename(headerImagePath, path7__default.default.extname(headerImagePath));
847
+ const headerImagePath = mediaBasePath ? path__default.default.join(mediaBasePath, galleryData.headerImage) : path__default.default.resolve(galleryDir, galleryData.headerImage);
848
+ const imagesFolder = path__default.default.join(galleryDir, "gallery", "images");
849
+ const currentHeaderBasename = path__default.default.basename(headerImagePath, path__default.default.extname(headerImagePath));
791
850
  if (shouldCreateThumbnails) {
792
851
  if (!fs8__default.default.existsSync(imagesFolder)) {
793
852
  fs8__default.default.mkdirSync(imagesFolder, { recursive: true });
@@ -830,7 +889,7 @@ async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnail
830
889
  }
831
890
  if (!galleryData.metadata.image) {
832
891
  ui.debug("Updating gallery.json with social media card URL");
833
- galleryData.metadata.image = thumbsBaseUrl ? `${thumbsBaseUrl}/${path7__default.default.basename(socialMediaCardImagePath)}` : `${galleryData.url || ""}/${path7__default.default.relative(galleryDir, socialMediaCardImagePath)}`;
892
+ galleryData.metadata.image = thumbsBaseUrl ? `${thumbsBaseUrl}/${path__default.default.basename(socialMediaCardImagePath)}` : `${galleryData.url || ""}/${path__default.default.relative(galleryDir, socialMediaCardImagePath)}`;
834
893
  fs8__default.default.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
835
894
  }
836
895
  if (shouldCreateThumbnails) {
@@ -838,20 +897,20 @@ async function buildGallery(galleryDir, templateDir, scan, shouldCreateThumbnail
838
897
  }
839
898
  ui.debug("Building gallery from template");
840
899
  try {
841
- process3__default.default.env.GALLERY_JSON_PATH = galleryJsonPath;
842
- process3__default.default.env.GALLERY_OUTPUT_DIR = path7__default.default.join(galleryDir, "gallery");
900
+ process4__default.default.env.GALLERY_JSON_PATH = galleryJsonPath;
901
+ process4__default.default.env.GALLERY_OUTPUT_DIR = path__default.default.join(galleryDir, "gallery");
843
902
  child_process.execSync("npx astro build", { cwd: templateDir, stdio: ui.level === consola.LogLevels.debug ? "inherit" : "ignore" });
844
903
  } catch (error) {
845
904
  ui.error(`Build failed for ${galleryDir}`);
846
905
  throw error;
847
906
  }
848
- const outputDir = path7__default.default.join(galleryDir, "gallery");
849
- const buildDir = path7__default.default.join(outputDir, "_build");
907
+ const outputDir = path__default.default.join(galleryDir, "gallery");
908
+ const buildDir = path__default.default.join(outputDir, "_build");
850
909
  ui.debug(`Copying build output to ${outputDir}`);
851
910
  fs8__default.default.cpSync(buildDir, outputDir, { recursive: true });
852
911
  ui.debug("Moving index.html to gallery directory");
853
- fs8__default.default.copyFileSync(path7__default.default.join(outputDir, "index.html"), path7__default.default.join(galleryDir, "index.html"));
854
- fs8__default.default.rmSync(path7__default.default.join(outputDir, "index.html"));
912
+ fs8__default.default.copyFileSync(path__default.default.join(outputDir, "index.html"), path__default.default.join(galleryDir, "index.html"));
913
+ fs8__default.default.rmSync(path__default.default.join(outputDir, "index.html"));
855
914
  ui.debug("Cleaning up build directory");
856
915
  fs8__default.default.rmSync(buildDir, { recursive: true, force: true });
857
916
  ui.success(`Gallery built successfully`);
@@ -864,12 +923,12 @@ async function build(options, ui) {
864
923
  return { processedGalleryCount: 0 };
865
924
  }
866
925
  const themePath = await undefined("@simple-photo-gallery/theme-modern/package.json");
867
- const themeDir = path7__default.default.dirname(new URL(themePath).pathname);
926
+ const themeDir = path__default.default.dirname(new URL(themePath).pathname);
868
927
  let totalGalleries = 0;
869
928
  for (const dir of galleryDirs) {
870
- const baseUrl = options.baseUrl ? `${options.baseUrl}${path7__default.default.relative(options.gallery, dir)}` : void 0;
871
- const thumbsBaseUrl = options.thumbsBaseUrl ? `${options.thumbsBaseUrl}${path7__default.default.relative(options.gallery, dir)}` : void 0;
872
- await buildGallery(path7__default.default.resolve(dir), themeDir, options.scan, options.thumbnails, ui, baseUrl, thumbsBaseUrl);
929
+ const baseUrl = options.baseUrl ? `${options.baseUrl}${path__default.default.relative(options.gallery, dir)}` : void 0;
930
+ const thumbsBaseUrl = options.thumbsBaseUrl ? `${options.thumbsBaseUrl}${path__default.default.relative(options.gallery, dir)}` : void 0;
931
+ await buildGallery(path__default.default.resolve(dir), themeDir, options.scan, options.thumbnails, ui, baseUrl, thumbsBaseUrl);
873
932
  ++totalGalleries;
874
933
  }
875
934
  ui.box(`Built ${totalGalleries} ${totalGalleries === 1 ? "gallery" : "galleries"} successfully`);
@@ -885,7 +944,7 @@ async function build(options, ui) {
885
944
  }
886
945
  async function cleanGallery(galleryDir, ui) {
887
946
  let filesRemoved = 0;
888
- const indexHtmlPath = path7__default.default.join(galleryDir, "index.html");
947
+ const indexHtmlPath = path__default.default.join(galleryDir, "index.html");
889
948
  if (fs8__default.default.existsSync(indexHtmlPath)) {
890
949
  try {
891
950
  fs8__default.default.rmSync(indexHtmlPath);
@@ -895,7 +954,7 @@ async function cleanGallery(galleryDir, ui) {
895
954
  ui?.warn(`Failed to remove index.html: ${error}`);
896
955
  }
897
956
  }
898
- const galleryPath = path7__default.default.join(galleryDir, "gallery");
957
+ const galleryPath = path__default.default.join(galleryDir, "gallery");
899
958
  if (fs8__default.default.existsSync(galleryPath)) {
900
959
  try {
901
960
  fs8__default.default.rmSync(galleryPath, { recursive: true, force: true });
@@ -914,7 +973,7 @@ async function cleanGallery(galleryDir, ui) {
914
973
  }
915
974
  async function clean(options, ui) {
916
975
  try {
917
- const basePath = path7__default.default.resolve(options.gallery);
976
+ const basePath = path__default.default.resolve(options.gallery);
918
977
  if (!fs8__default.default.existsSync(basePath)) {
919
978
  ui.error(`Directory does not exist: ${basePath}`);
920
979
  return { processedGalleryCount: 0 };
@@ -963,7 +1022,7 @@ var ApiTelemetryClient = class {
963
1022
  axios__default.default.post(this.endpoint, event, {
964
1023
  headers: {
965
1024
  "content-type": "application/json",
966
- "user-agent": `simple-photo-gallery/${event.packageVersion} (${process3__default.default.platform}; ${process3__default.default.arch})`
1025
+ "user-agent": `simple-photo-gallery/${event.packageVersion} (${process4__default.default.platform}; ${process4__default.default.arch})`
967
1026
  }
968
1027
  });
969
1028
  } catch {
@@ -973,7 +1032,7 @@ var ApiTelemetryClient = class {
973
1032
  var ConsoleTelemetryClient = class {
974
1033
  async record(event) {
975
1034
  const serialized = JSON.stringify(event, null, 2);
976
- process3.stdout.write(`TELEMETRY EVENT: ${serialized}
1035
+ process4.stdout.write(`TELEMETRY EVENT: ${serialized}
977
1036
  `);
978
1037
  }
979
1038
  };
@@ -1000,11 +1059,11 @@ var TelemetryService = class {
1000
1059
  if (override) {
1001
1060
  return override === "1";
1002
1061
  }
1003
- if (process3__default.default.env.CI || process3__default.default.env.DO_NOT_TRACK) {
1062
+ if (process4__default.default.env.CI || process4__default.default.env.DO_NOT_TRACK) {
1004
1063
  return false;
1005
1064
  }
1006
- if (process3__default.default.env.SPG_TELEMETRY) {
1007
- return process3__default.default.env.SPG_TELEMETRY === "1";
1065
+ if (process4__default.default.env.SPG_TELEMETRY) {
1066
+ return process4__default.default.env.SPG_TELEMETRY === "1";
1008
1067
  }
1009
1068
  const stored = this.getStoredPreference();
1010
1069
  if (stored === void 0) {
@@ -1046,7 +1105,7 @@ var TelemetryService = class {
1046
1105
  durationMs: now - startedAt,
1047
1106
  packageName: this.packageName,
1048
1107
  packageVersion: this.packageVersion,
1049
- nodeVersion: process3__default.default.version,
1108
+ nodeVersion: process4__default.default.version,
1050
1109
  osPlatform: os__default.default.platform(),
1051
1110
  osRelease: os__default.default.release(),
1052
1111
  osArch: os__default.default.arch(),
@@ -1080,7 +1139,7 @@ var TelemetryService = class {
1080
1139
  /** Returns the telemetry client. */
1081
1140
  getClient() {
1082
1141
  if (!this.client) {
1083
- switch (process3__default.default.env.SPG_TELEMETRY_PROVIDER) {
1142
+ switch (process4__default.default.env.SPG_TELEMETRY_PROVIDER) {
1084
1143
  case "none": {
1085
1144
  this.client = void 0;
1086
1145
  break;
@@ -1172,7 +1231,7 @@ async function waitForUpdateCheck(checkPromise) {
1172
1231
  // package.json
1173
1232
  var package_default = {
1174
1233
  name: "simple-photo-gallery",
1175
- version: "2.0.11-rc.9"};
1234
+ version: "2.0.12"};
1176
1235
 
1177
1236
  // src/index.ts
1178
1237
  var program = new commander.Command();
@@ -1214,7 +1273,7 @@ function withCommandContext(handler) {
1214
1273
  } catch (error) {
1215
1274
  ui.debug(error);
1216
1275
  errorInfo = error instanceof Error ? { name: error.name, message: error.message } : { name: "UnknownError", message: String(error) };
1217
- process3__default.default.exitCode = 1;
1276
+ process4__default.default.exitCode = 1;
1218
1277
  }
1219
1278
  const updateInfo = await waitForUpdateCheck(updateCheckPromise);
1220
1279
  if (updateInfo) {
@@ -1236,14 +1295,14 @@ function withCommandContext(handler) {
1236
1295
  program.command("init").description("Initialize a gallery by scaning a folder for images and videos").option(
1237
1296
  "-p, --photos <path>",
1238
1297
  "Path to the folder where the photos are stored. Default: current working directory",
1239
- process3__default.default.cwd()
1298
+ process4__default.default.cwd()
1240
1299
  ).option(
1241
1300
  "-g, --gallery <path>",
1242
1301
  "Path to the directory where the gallery will be initialized. Default: same directory as the photos folder"
1243
- ).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)));
1244
- 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__default.default.cwd()).option("-r, --recursive", "Scan subdirectories recursively", false).action(withCommandContext((options, ui) => thumbnails(options, ui)));
1245
- 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__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).action(withCommandContext((options, ui) => build(options, ui)));
1246
- 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__default.default.cwd()).option("-r, --recursive", "Clean subdirectories recursively", false).action(withCommandContext((options, ui) => clean(options, ui)));
1302
+ ).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)));
1303
+ 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).action(withCommandContext((options, ui) => thumbnails(options, ui)));
1304
+ 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).action(withCommandContext((options, ui) => build(options, ui)));
1305
+ 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)));
1247
1306
  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)));
1248
1307
  program.parse();
1249
1308
  //# sourceMappingURL=index.cjs.map