simple-photo-gallery 2.0.11-rc.1 → 2.0.11-rc.11

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
@@ -8,8 +8,8 @@ import path7 from 'path';
8
8
  import { Buffer } from 'buffer';
9
9
  import sharp2 from 'sharp';
10
10
  import { encode } from 'blurhash';
11
+ import { GalleryDataSchema, GalleryDataDeprecatedSchema } from '@simple-photo-gallery/common';
11
12
  import ExifReader from 'exifreader';
12
- import { z } from 'zod';
13
13
  import ffprobe from 'node-ffprobe';
14
14
  import os from 'os';
15
15
  import Conf from 'conf';
@@ -46,22 +46,6 @@ async function cropAndResizeImage(image, outputPath, width, height, format = "av
46
46
  withoutEnlargement: true
47
47
  }).toFormat(format).toFile(outputPath);
48
48
  }
49
- async function getImageDescription(imagePath) {
50
- try {
51
- const tags = await ExifReader.load(imagePath);
52
- if (tags.description?.description) return tags.description.description;
53
- if (tags.ImageDescription?.description) return tags.ImageDescription.description;
54
- if (tags.UserComment && typeof tags.UserComment === "object" && tags.UserComment !== null && "description" in tags.UserComment) {
55
- return tags.UserComment.description;
56
- }
57
- if (tags.ExtDescrAccessibility?.description) return tags.ExtDescrAccessibility.description;
58
- if (tags["Caption/Abstract"]?.description) return tags["Caption/Abstract"].description;
59
- if (tags.XPTitle?.description) return tags.XPTitle.description;
60
- if (tags.XPComment?.description) return tags.XPComment.description;
61
- } catch {
62
- return void 0;
63
- }
64
- }
65
49
  async function createImageThumbnails(image, metadata, outputPath, outputPathRetina, size) {
66
50
  const originalWidth = metadata.width || 0;
67
51
  const originalHeight = metadata.height || 0;
@@ -94,10 +78,10 @@ async function generateBlurHash(imagePath, componentX = 4, componentY = 3) {
94
78
  // src/modules/build/utils/index.ts
95
79
  path7.dirname(new URL(import.meta.url).pathname);
96
80
  async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPath, ui) {
97
- ui.start(`Creating social media card image`);
81
+ ui?.start(`Creating social media card image`);
98
82
  const headerBasename = path7.basename(headerPhotoPath, path7.extname(headerPhotoPath));
99
83
  if (fs8.existsSync(ouputPath)) {
100
- ui.success(`Social media card image already exists`);
84
+ ui?.success(`Social media card image already exists`);
101
85
  return headerBasename;
102
86
  }
103
87
  const image = await loadImage(headerPhotoPath);
@@ -116,23 +100,23 @@ async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPa
116
100
  `;
117
101
  const finalImageBuffer = await sharp2(resizedImageBuffer).composite([{ input: Buffer.from(svgText), top: 0, left: 0 }]).jpeg({ quality: 90 }).toBuffer();
118
102
  await sharp2(finalImageBuffer).toFile(outputPath);
119
- ui.success(`Created social media card image successfully`);
103
+ ui?.success(`Created social media card image successfully`);
120
104
  return headerBasename;
121
105
  }
122
106
  async function createOptimizedHeaderImage(headerPhotoPath, outputFolder, ui) {
123
- ui.start(`Creating optimized header images`);
107
+ ui?.start(`Creating optimized header images`);
124
108
  const image = await loadImage(headerPhotoPath);
125
109
  const headerBasename = path7.basename(headerPhotoPath, path7.extname(headerPhotoPath));
126
110
  const generatedFiles = [];
127
- ui.debug("Generating blurhash for header image");
111
+ ui?.debug("Generating blurhash for header image");
128
112
  const blurHash = await generateBlurHash(headerPhotoPath);
129
113
  const landscapeYFactor = 3 / 4;
130
114
  for (const width of HEADER_IMAGE_LANDSCAPE_WIDTHS) {
131
- ui.debug(`Creating landscape header image ${width}`);
115
+ ui?.debug(`Creating landscape header image ${width}`);
132
116
  const avifFilename = `${headerBasename}_landscape_${width}.avif`;
133
117
  const jpgFilename = `${headerBasename}_landscape_${width}.jpg`;
134
118
  if (fs8.existsSync(path7.join(outputFolder, avifFilename))) {
135
- ui.debug(`Landscape header image ${width} AVIF already exists`);
119
+ ui?.debug(`Landscape header image ${width} AVIF already exists`);
136
120
  } else {
137
121
  await cropAndResizeImage(
138
122
  image.clone(),
@@ -144,7 +128,7 @@ async function createOptimizedHeaderImage(headerPhotoPath, outputFolder, ui) {
144
128
  }
145
129
  generatedFiles.push(avifFilename);
146
130
  if (fs8.existsSync(path7.join(outputFolder, jpgFilename))) {
147
- ui.debug(`Landscape header image ${width} JPG already exists`);
131
+ ui?.debug(`Landscape header image ${width} JPG already exists`);
148
132
  } else {
149
133
  await cropAndResizeImage(image.clone(), path7.join(outputFolder, jpgFilename), width, width * landscapeYFactor, "jpg");
150
134
  }
@@ -152,23 +136,23 @@ async function createOptimizedHeaderImage(headerPhotoPath, outputFolder, ui) {
152
136
  }
153
137
  const portraitYFactor = 4 / 3;
154
138
  for (const width of HEADER_IMAGE_PORTRAIT_WIDTHS) {
155
- ui.debug(`Creating portrait header image ${width}`);
139
+ ui?.debug(`Creating portrait header image ${width}`);
156
140
  const avifFilename = `${headerBasename}_portrait_${width}.avif`;
157
141
  const jpgFilename = `${headerBasename}_portrait_${width}.jpg`;
158
142
  if (fs8.existsSync(path7.join(outputFolder, avifFilename))) {
159
- ui.debug(`Portrait header image ${width} AVIF already exists`);
143
+ ui?.debug(`Portrait header image ${width} AVIF already exists`);
160
144
  } else {
161
145
  await cropAndResizeImage(image.clone(), path7.join(outputFolder, avifFilename), width, width * portraitYFactor, "avif");
162
146
  }
163
147
  generatedFiles.push(avifFilename);
164
148
  if (fs8.existsSync(path7.join(outputFolder, jpgFilename))) {
165
- ui.debug(`Portrait header image ${width} JPG already exists`);
149
+ ui?.debug(`Portrait header image ${width} JPG already exists`);
166
150
  } else {
167
151
  await cropAndResizeImage(image.clone(), path7.join(outputFolder, jpgFilename), width, width * portraitYFactor, "jpg");
168
152
  }
169
153
  generatedFiles.push(jpgFilename);
170
154
  }
171
- ui.success(`Created optimized header image successfully`);
155
+ ui?.success(`Created optimized header image successfully`);
172
156
  return { headerBasename, generatedFiles, blurHash };
173
157
  }
174
158
  function hasOldHeaderImages(outputFolder, currentHeaderBasename) {
@@ -186,9 +170,9 @@ function hasOldHeaderImages(outputFolder, currentHeaderBasename) {
186
170
  return false;
187
171
  }
188
172
  function cleanupOldHeaderImages(outputFolder, currentHeaderBasename, ui) {
189
- ui.start(`Cleaning up old header images`);
173
+ ui?.start(`Cleaning up old header images`);
190
174
  if (!fs8.existsSync(outputFolder)) {
191
- ui.debug(`Output folder ${outputFolder} does not exist, skipping cleanup`);
175
+ ui?.debug(`Output folder ${outputFolder} does not exist, skipping cleanup`);
192
176
  return;
193
177
  }
194
178
  const files = fs8.readdirSync(outputFolder);
@@ -198,20 +182,20 @@ function cleanupOldHeaderImages(outputFolder, currentHeaderBasename, ui) {
198
182
  const portraitMatch = file.match(/^(.+)_portrait_\d+\.(avif|jpg)$/);
199
183
  if (landscapeMatch && landscapeMatch[1] !== currentHeaderBasename) {
200
184
  const filePath = path7.join(outputFolder, file);
201
- ui.debug(`Deleting old landscape header image: ${file}`);
185
+ ui?.debug(`Deleting old landscape header image: ${file}`);
202
186
  fs8.unlinkSync(filePath);
203
187
  deletedCount++;
204
188
  } else if (portraitMatch && portraitMatch[1] !== currentHeaderBasename) {
205
189
  const filePath = path7.join(outputFolder, file);
206
- ui.debug(`Deleting old portrait header image: ${file}`);
190
+ ui?.debug(`Deleting old portrait header image: ${file}`);
207
191
  fs8.unlinkSync(filePath);
208
192
  deletedCount++;
209
193
  }
210
194
  }
211
195
  if (deletedCount > 0) {
212
- ui.success(`Deleted ${deletedCount} old header image(s)`);
196
+ ui?.success(`Deleted ${deletedCount} old header image(s)`);
213
197
  } else {
214
- ui.debug(`No old header images to clean up`);
198
+ ui?.debug(`No old header images to clean up`);
215
199
  }
216
200
  }
217
201
  function findGalleries(basePath, recursive) {
@@ -253,90 +237,6 @@ function parseTelemetryOption(value) {
253
237
  }
254
238
  return value;
255
239
  }
256
- var ThumbnailSchema = z.object({
257
- path: z.string(),
258
- pathRetina: z.string(),
259
- width: z.number(),
260
- height: z.number(),
261
- blurHash: z.string().optional()
262
- });
263
- var MediaFileSchema = z.object({
264
- type: z.enum(["image", "video"]),
265
- filename: z.string(),
266
- alt: z.string().optional(),
267
- width: z.number(),
268
- height: z.number(),
269
- thumbnail: ThumbnailSchema.optional(),
270
- lastMediaTimestamp: z.string().optional()
271
- });
272
- var MediaFileDeprecatedSchema = z.object({
273
- type: z.enum(["image", "video"]),
274
- path: z.string(),
275
- alt: z.string().optional(),
276
- width: z.number(),
277
- height: z.number(),
278
- thumbnail: ThumbnailSchema.optional(),
279
- lastMediaTimestamp: z.string().optional()
280
- });
281
- var GallerySectionSchema = z.object({
282
- title: z.string().optional(),
283
- description: z.string().optional(),
284
- images: z.array(MediaFileSchema)
285
- });
286
- var GallerySectionDeprecatedSchema = z.object({
287
- title: z.string().optional(),
288
- description: z.string().optional(),
289
- images: z.array(MediaFileDeprecatedSchema)
290
- });
291
- var SubGallerySchema = z.object({
292
- title: z.string(),
293
- headerImage: z.string(),
294
- path: z.string()
295
- });
296
- var GalleryMetadataSchema = z.object({
297
- image: z.string().optional(),
298
- imageWidth: z.number().optional(),
299
- imageHeight: z.number().optional(),
300
- ogUrl: z.string().optional(),
301
- ogType: z.string().optional(),
302
- ogSiteName: z.string().optional(),
303
- twitterSite: z.string().optional(),
304
- twitterCreator: z.string().optional(),
305
- author: z.string().optional(),
306
- keywords: z.string().optional(),
307
- canonicalUrl: z.string().optional(),
308
- language: z.string().optional(),
309
- robots: z.string().optional()
310
- });
311
- var GalleryDataSchema = z.object({
312
- title: z.string(),
313
- description: z.string(),
314
- mediaBasePath: z.string().optional(),
315
- url: z.string().optional(),
316
- headerImage: z.string(),
317
- headerImageBlurHash: z.string().optional(),
318
- thumbnailSize: z.number().optional(),
319
- metadata: GalleryMetadataSchema,
320
- mediaBaseUrl: z.string().optional(),
321
- thumbsBaseUrl: z.string().optional(),
322
- analyticsScript: z.string().optional(),
323
- sections: z.array(GallerySectionSchema),
324
- subGalleries: z.object({ title: z.string(), galleries: z.array(SubGallerySchema) })
325
- });
326
- var GalleryDataDeprecatedSchema = z.object({
327
- title: z.string(),
328
- description: z.string(),
329
- url: z.string().optional(),
330
- headerImage: z.string(),
331
- thumbnailSize: z.number().optional(),
332
- metadata: GalleryMetadataSchema,
333
- mediaBaseUrl: z.string().optional(),
334
- analyticsScript: z.string().optional(),
335
- sections: z.array(GallerySectionDeprecatedSchema),
336
- subGalleries: z.object({ title: z.string(), galleries: z.array(SubGallerySchema) })
337
- });
338
-
339
- // src/utils/gallery.ts
340
240
  function parseGalleryJson(galleryJsonPath, ui) {
341
241
  let galleryContent;
342
242
  try {
@@ -583,6 +483,22 @@ async function getFileMtime(filePath) {
583
483
  const stats = await promises.stat(filePath);
584
484
  return stats.mtime;
585
485
  }
486
+ async function getImageDescription(image) {
487
+ try {
488
+ const tags = await ExifReader.load(image);
489
+ if (tags.description?.description) return tags.description.description;
490
+ if (tags.ImageDescription?.description) return tags.ImageDescription.description;
491
+ if (tags.UserComment && typeof tags.UserComment === "object" && tags.UserComment !== null && "description" in tags.UserComment) {
492
+ return tags.UserComment.description;
493
+ }
494
+ if (tags.ExtDescrAccessibility?.description) return tags.ExtDescrAccessibility.description;
495
+ if (tags["Caption/Abstract"]?.description) return tags["Caption/Abstract"].description;
496
+ if (tags.XPTitle?.description) return tags.XPTitle.description;
497
+ if (tags.XPComment?.description) return tags.XPComment.description;
498
+ } catch {
499
+ return void 0;
500
+ }
501
+ }
586
502
  async function getVideoDimensions(filePath) {
587
503
  const data = await ffprobe(filePath);
588
504
  const videoStream = data.streams.find((stream) => stream.codec_type === "video");
@@ -710,7 +626,7 @@ async function processVideo(videoPath, thumbnailPath, thumbnailPathRetina, thumb
710
626
  lastMediaTimestamp: fileMtime.toISOString()
711
627
  };
712
628
  }
713
- async function processMediaFile(mediaFile, mediaBasePath, galleryDir, thumbnailsPath, thumbnailSize, ui) {
629
+ async function processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbnailSize, ui) {
714
630
  try {
715
631
  const filePath = path7.resolve(path7.join(mediaBasePath, mediaFile.filename));
716
632
  const fileName = mediaFile.filename;
@@ -763,14 +679,7 @@ async function processGalleryThumbnails(galleryDir, ui) {
763
679
  let processedCount = 0;
764
680
  for (const section of galleryData.sections) {
765
681
  for (const [index, mediaFile] of section.images.entries()) {
766
- section.images[index] = await processMediaFile(
767
- mediaFile,
768
- mediaBasePath,
769
- galleryDir,
770
- thumbnailsPath,
771
- thumbnailSize,
772
- ui
773
- );
682
+ section.images[index] = await processMediaFile(mediaFile, mediaBasePath, thumbnailsPath, thumbnailSize, ui);
774
683
  }
775
684
  processedCount += section.images.length;
776
685
  }
@@ -965,26 +874,26 @@ async function cleanGallery(galleryDir, ui) {
965
874
  if (fs8.existsSync(indexHtmlPath)) {
966
875
  try {
967
876
  fs8.rmSync(indexHtmlPath);
968
- ui.debug(`Removed: ${indexHtmlPath}`);
877
+ ui?.debug(`Removed: ${indexHtmlPath}`);
969
878
  filesRemoved++;
970
879
  } catch (error) {
971
- ui.warn(`Failed to remove index.html: ${error}`);
880
+ ui?.warn(`Failed to remove index.html: ${error}`);
972
881
  }
973
882
  }
974
883
  const galleryPath = path7.join(galleryDir, "gallery");
975
884
  if (fs8.existsSync(galleryPath)) {
976
885
  try {
977
886
  fs8.rmSync(galleryPath, { recursive: true, force: true });
978
- ui.debug(`Removed directory: ${galleryPath}`);
887
+ ui?.debug(`Removed directory: ${galleryPath}`);
979
888
  filesRemoved++;
980
889
  } catch (error) {
981
- ui.warn(`Failed to remove gallery directory: ${error}`);
890
+ ui?.warn(`Failed to remove gallery directory: ${error}`);
982
891
  }
983
892
  }
984
893
  if (filesRemoved > 0) {
985
- ui.success(`Cleaned gallery at: ${galleryDir}`);
894
+ ui?.success(`Cleaned gallery at: ${galleryDir}`);
986
895
  } else {
987
- ui.info(`No gallery files found at: ${galleryDir}`);
896
+ ui?.info(`No gallery files found at: ${galleryDir}`);
988
897
  }
989
898
  return { processedGalleryCount: filesRemoved };
990
899
  }
@@ -1248,7 +1157,7 @@ async function waitForUpdateCheck(checkPromise) {
1248
1157
  // package.json
1249
1158
  var package_default = {
1250
1159
  name: "simple-photo-gallery",
1251
- version: "2.0.11-rc.1"};
1160
+ version: "2.0.11-rc.11"};
1252
1161
 
1253
1162
  // src/index.ts
1254
1163
  var program = new Command();