simple-photo-gallery 2.0.11-rc.1 → 2.0.11-rc.10
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 +1250 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.js +44 -135
- package/dist/index.js.map +1 -1
- package/dist/lib/index.cjs +166 -0
- package/dist/lib/index.cjs.map +1 -0
- package/dist/lib/index.d.cts +97 -0
- package/dist/lib/index.d.ts +97 -0
- package/dist/lib/index.js +148 -0
- package/dist/lib/index.js.map +1 -0
- package/package.json +20 -4
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
173
|
+
ui?.start(`Cleaning up old header images`);
|
|
190
174
|
if (!fs8.existsSync(outputFolder)) {
|
|
191
|
-
ui
|
|
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
|
|
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
|
|
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
|
|
196
|
+
ui?.success(`Deleted ${deletedCount} old header image(s)`);
|
|
213
197
|
} else {
|
|
214
|
-
ui
|
|
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,
|
|
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
|
|
877
|
+
ui?.debug(`Removed: ${indexHtmlPath}`);
|
|
969
878
|
filesRemoved++;
|
|
970
879
|
} catch (error) {
|
|
971
|
-
ui
|
|
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
|
|
887
|
+
ui?.debug(`Removed directory: ${galleryPath}`);
|
|
979
888
|
filesRemoved++;
|
|
980
889
|
} catch (error) {
|
|
981
|
-
ui
|
|
890
|
+
ui?.warn(`Failed to remove gallery directory: ${error}`);
|
|
982
891
|
}
|
|
983
892
|
}
|
|
984
893
|
if (filesRemoved > 0) {
|
|
985
|
-
ui
|
|
894
|
+
ui?.success(`Cleaned gallery at: ${galleryDir}`);
|
|
986
895
|
} else {
|
|
987
|
-
ui
|
|
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.
|
|
1160
|
+
version: "2.0.11-rc.10"};
|
|
1252
1161
|
|
|
1253
1162
|
// src/index.ts
|
|
1254
1163
|
var program = new Command();
|