simple-photo-gallery 2.0.5 → 2.0.7

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/README.md CHANGED
@@ -31,7 +31,15 @@ This is a free, open-source tool that you can use to generate galleries and self
31
31
 
32
32
  ## Quick Start
33
33
 
34
- The fastest way to create a gallery is to use `npx` in your photos folder:
34
+ To create a gallery you need to install he `simple-photo-gallery` package globally and then you can use the `spg` command:
35
+
36
+ ```bash
37
+ npm install -g simple-photo-gallery@latest
38
+ spg init
39
+ spg build
40
+ ```
41
+
42
+ Alternatively you can use `npx` to run the commands directly:
35
43
 
36
44
  ```bash
37
45
  npx simple-photo-gallery@latest init
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ import fs6, { promises } from 'fs';
7
7
  import path4 from 'path';
8
8
  import { z } from 'zod';
9
9
  import { Buffer } from 'buffer';
10
- import sharp from 'sharp';
10
+ import sharp2 from 'sharp';
11
11
  import ExifReader from 'exifreader';
12
12
  import { encode } from 'blurhash';
13
13
  import ffprobe from 'node-ffprobe';
@@ -72,6 +72,21 @@ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png", ".gif",
72
72
  var VIDEO_EXTENSIONS = /* @__PURE__ */ new Set([".mp4", ".avi", ".mov", ".wmv", ".flv", ".webm", ".mkv", ".m4v", ".3gp"]);
73
73
  var HEADER_IMAGE_LANDSCAPE_WIDTHS = [3840, 2560, 1920, 1280, 960, 640];
74
74
  var HEADER_IMAGE_PORTRAIT_WIDTHS = [1080, 720, 480, 360];
75
+ async function loadImage(imagePath) {
76
+ return sharp2(imagePath).rotate();
77
+ }
78
+ async function loadImageWithMetadata(imagePath) {
79
+ const image = sharp2(imagePath);
80
+ const metadata = await image.metadata();
81
+ image.rotate();
82
+ const needsDimensionSwap = metadata.orientation && metadata.orientation >= 5 && metadata.orientation <= 8;
83
+ if (needsDimensionSwap) {
84
+ const originalWidth = metadata.width;
85
+ metadata.width = metadata.height;
86
+ metadata.height = originalWidth;
87
+ }
88
+ return { image, metadata };
89
+ }
75
90
  async function resizeImage(image, outputPath, width, height, format = "avif") {
76
91
  await image.resize(width, height, { withoutEnlargement: true }).toFormat(format).toFile(outputPath);
77
92
  }
@@ -126,9 +141,10 @@ async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPa
126
141
  ui.success(`Social media card image already exists`);
127
142
  return;
128
143
  }
129
- const resizedImageBuffer = await sharp(headerPhotoPath).resize(1200, 631, { fit: "cover" }).jpeg({ quality: 90 }).toBuffer();
144
+ const image = await loadImage(headerPhotoPath);
145
+ const resizedImageBuffer = await image.resize(1200, 631, { fit: "cover" }).jpeg({ quality: 90 }).toBuffer();
130
146
  const outputPath = ouputPath;
131
- await sharp(resizedImageBuffer).toFile(outputPath);
147
+ await sharp2(resizedImageBuffer).toFile(outputPath);
132
148
  const svgText = `
133
149
  <svg width="1200" height="631" xmlns="http://www.w3.org/2000/svg">
134
150
  <defs>
@@ -140,13 +156,13 @@ async function createGallerySocialMediaCardImage(headerPhotoPath, title, ouputPa
140
156
  <text x="600" y="250" class="title">${title}</text>
141
157
  </svg>
142
158
  `;
143
- const finalImageBuffer = await sharp(resizedImageBuffer).composite([{ input: Buffer.from(svgText), top: 0, left: 0 }]).jpeg({ quality: 90 }).toBuffer();
144
- await sharp(finalImageBuffer).toFile(outputPath);
159
+ const finalImageBuffer = await sharp2(resizedImageBuffer).composite([{ input: Buffer.from(svgText), top: 0, left: 0 }]).jpeg({ quality: 90 }).toBuffer();
160
+ await sharp2(finalImageBuffer).toFile(outputPath);
145
161
  ui.success(`Created social media card image successfully`);
146
162
  }
147
163
  async function createOptimizedHeaderImage(headerPhotoPath, outputFolder, ui) {
148
164
  ui.start(`Creating optimized header images`);
149
- const image = sharp(headerPhotoPath);
165
+ const image = await loadImage(headerPhotoPath);
150
166
  const landscapeYFactor = 3 / 4;
151
167
  for (const width of HEADER_IMAGE_LANDSCAPE_WIDTHS) {
152
168
  ui.debug(`Creating landscape header image ${width}`);
@@ -239,7 +255,7 @@ async function getFileMtime(filePath) {
239
255
  return stats.mtime;
240
256
  }
241
257
  async function generateBlurHash(imagePath, componentX = 4, componentY = 3) {
242
- const image = sharp(imagePath);
258
+ const image = await loadImage(imagePath);
243
259
  const { data, info } = await image.resize(32, 32, { fit: "inside" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
244
260
  const pixels = new Uint8ClampedArray(data.buffer);
245
261
  return encode(pixels, info.width, info.height, componentX, componentY);
@@ -280,7 +296,7 @@ async function createVideoThumbnails(inputPath, videoDimensions, outputPath, out
280
296
  ffmpeg.on("close", async (code) => {
281
297
  if (code === 0) {
282
298
  try {
283
- const frameImage = sharp(tempFramePath);
299
+ const frameImage = sharp2(tempFramePath);
284
300
  await resizeImage(frameImage, outputPath, width, height);
285
301
  await resizeImage(frameImage, outputPathRetina, width * 2, height * 2);
286
302
  try {
@@ -307,8 +323,7 @@ async function processImage(imagePath, thumbnailPath, thumbnailPathRetina, thumb
307
323
  if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs6.existsSync(thumbnailPath)) {
308
324
  return void 0;
309
325
  }
310
- const image = sharp(imagePath);
311
- const metadata = await image.metadata();
326
+ const { image, metadata } = await loadImageWithMetadata(imagePath);
312
327
  const imageDimensions = {
313
328
  width: metadata.width || 0,
314
329
  height: metadata.height || 0
@@ -769,9 +784,13 @@ async function init(options, ui) {
769
784
  }
770
785
  }
771
786
 
787
+ // package.json
788
+ var package_default = {
789
+ version: "2.0.7"};
790
+
772
791
  // src/index.ts
773
792
  var program = new Command();
774
- program.name("gallery").description("Simple Photo Gallery CLI").version("0.0.1").option("-v, --verbose", "Verbose output (debug level)", false).option("-q, --quiet", "Minimal output (only warnings/errors)", false).showHelpAfterError(true);
793
+ program.name("gallery").description("Simple Photo Gallery CLI").version(package_default.version).option("-v, --verbose", "Verbose output (debug level)", false).option("-q, --quiet", "Minimal output (only warnings/errors)", false).showHelpAfterError(true);
775
794
  function createConsolaUI(globalOpts) {
776
795
  let level = LogLevels.info;
777
796
  if (globalOpts.quiet) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../common/src/gallery.ts","../src/config/index.ts","../src/utils/image.ts","../src/modules/build/utils/index.ts","../src/utils/index.ts","../src/modules/thumbnails/utils/index.ts","../src/utils/blurhash.ts","../src/utils/video.ts","../src/modules/thumbnails/index.ts","../src/modules/build/index.ts","../src/modules/clean/index.ts","../src/modules/init/utils/index.ts","../src/modules/init/index.ts","../src/index.ts"],"names":["path","fs","sharp","process","LogLevels","result"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\n/** Zod schema for thumbnail metadata including path and dimensions */\nexport const ThumbnailSchema = z.object({\n path: z.string(),\n pathRetina: z.string(),\n width: z.number(),\n height: z.number(),\n blurHash: z.string().optional(),\n});\n\n/** Zod schema for media file metadata including type, dimensions, and thumbnail info */\nexport const MediaFileSchema = z.object({\n type: z.enum(['image', 'video']),\n path: z.string(),\n alt: z.string().optional(),\n width: z.number(),\n height: z.number(),\n thumbnail: ThumbnailSchema.optional(),\n lastMediaTimestamp: z.string().optional(),\n});\n\n/** Zod schema for a gallery section containing title, description, and media files */\nexport const GallerySectionSchema = z.object({\n title: z.string().optional(),\n description: z.string().optional(),\n images: z.array(MediaFileSchema),\n});\n\n/** Zod schema for sub-gallery metadata including title, header image, and path */\nexport const SubGallerySchema = z.object({\n title: z.string(),\n headerImage: z.string(),\n path: z.string(),\n});\n\n/** Zod schema for complete gallery data including metadata, sections, and sub-galleries */\nexport const GalleryMetadataSchema = z.object({\n image: z.string().optional(),\n imageWidth: z.number().optional(),\n imageHeight: z.number().optional(),\n ogUrl: z.string().optional(),\n ogType: z.string().optional(),\n ogSiteName: z.string().optional(),\n twitterSite: z.string().optional(),\n twitterCreator: z.string().optional(),\n author: z.string().optional(),\n keywords: z.string().optional(),\n canonicalUrl: z.string().optional(),\n language: z.string().optional(),\n robots: z.string().optional(),\n});\n\n/** Zod schema for complete gallery data including metadata, sections, and sub-galleries */\nexport const GalleryDataSchema = z.object({\n title: z.string(),\n description: z.string(),\n url: z.string().optional(),\n headerImage: z.string(),\n thumbnailSize: z.number().optional(),\n metadata: GalleryMetadataSchema,\n galleryOutputPath: z.string().optional(),\n mediaBaseUrl: z.string().optional(),\n sections: z.array(GallerySectionSchema),\n subGalleries: z.object({ title: z.string(), galleries: z.array(SubGallerySchema) }),\n});\n\n/** TypeScript type for thumbnail metadata */\nexport type Thumbnail = z.infer<typeof ThumbnailSchema>;\n\n/** TypeScript type for media file metadata */\nexport type MediaFile = z.infer<typeof MediaFileSchema>;\n\n/** TypeScript type for gallery section data */\nexport type GallerySection = z.infer<typeof GallerySectionSchema>;\n\n/** TypeScript type for sub-gallery metadata */\nexport type SubGallery = z.infer<typeof SubGallerySchema>;\n\n/** TypeScript type for gallery metadata */\nexport type GalleryMetadata = z.infer<typeof GalleryMetadataSchema>;\n\n/** TypeScript type for complete gallery data structure */\nexport type GalleryData = z.infer<typeof GalleryDataSchema>;\n","/** Default thumbnail size in pixels */\nexport const DEFAULT_THUMBNAIL_SIZE = 300;\n\n/** Set of supported image file extensions */\nexport const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp', '.tiff', '.tif', '.svg', '.avif']);\n\n/** Set of supported video file extensions */\nexport const VIDEO_EXTENSIONS = new Set(['.mp4', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv', '.m4v', '.3gp']);\n\n/** Set of supported header image landscape widths */\nexport const HEADER_IMAGE_LANDSCAPE_WIDTHS = [3840, 2560, 1920, 1280, 960, 640];\n\n/** Set of supported header image portrait widths */\nexport const HEADER_IMAGE_PORTRAIT_WIDTHS = [1080, 720, 480, 360];\n","import ExifReader from 'exifreader';\n\nimport type { Dimensions } from '../types';\nimport type { FormatEnum, Metadata, Sharp } from 'sharp';\n\n/**\n * Utility function to resize and save thumbnail using Sharp. The functions avoids upscaling the image and only reduces the size if necessary.\n * @param image - Sharp image instance\n * @param outputPath - Path where thumbnail should be saved\n * @param width - Target width for thumbnail\n * @param height - Target height for thumbnail\n */\nexport async function resizeImage(\n image: Sharp,\n outputPath: string,\n width: number,\n height: number,\n format: keyof FormatEnum = 'avif',\n): Promise<void> {\n // Resize the image without enlarging it\n await image.resize(width, height, { withoutEnlargement: true }).toFormat(format).toFile(outputPath);\n}\n\n/**\n * Crops and resizes an image to a target aspect ratio, avoiding upscaling the image.\n * @param image - Sharp image instance\n * @param outputPath - Path where the image should be saved\n * @param width - Target width for the image\n * @param height - Target height for the image\n */\nexport async function cropAndResizeImage(\n image: Sharp,\n outputPath: string,\n width: number,\n height: number,\n format: keyof FormatEnum = 'avif',\n): Promise<void> {\n // Apply resize with cover fit and without enlargement\n await image\n .resize(width, height, {\n fit: 'cover',\n withoutEnlargement: true,\n })\n .toFormat(format)\n .toFile(outputPath);\n}\n\n/**\n * Extracts description from image EXIF data\n * @param metadata - Sharp metadata object containing EXIF data\n * @returns Promise resolving to image description or undefined if not found\n */\nexport async function getImageDescription(imagePath: string): Promise<string | undefined> {\n try {\n const tags = await ExifReader.load(imagePath);\n\n // Description\n if (tags.description?.description) return tags.description.description;\n\n // ImageDescription\n if (tags.ImageDescription?.description) return tags.ImageDescription.description;\n\n // UserComment\n if (\n tags.UserComment &&\n typeof tags.UserComment === 'object' &&\n tags.UserComment !== null &&\n 'description' in tags.UserComment\n ) {\n return (tags.UserComment as { description: string }).description;\n }\n\n // ExtDescrAccessibility\n if (tags.ExtDescrAccessibility?.description) return tags.ExtDescrAccessibility.description;\n\n // Caption/Abstract\n if (tags['Caption/Abstract']?.description) return tags['Caption/Abstract'].description;\n\n // XP Title\n if (tags.XPTitle?.description) return tags.XPTitle.description;\n\n // XP Comment\n if (tags.XPComment?.description) return tags.XPComment.description;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Creates regular and retina thumbnails for an image while maintaining aspect ratio\n * @param image - Sharp image instance\n * @param metadata - Image metadata containing dimensions\n * @param outputPath - Path where thumbnail should be saved\n * @param outputPathRetina - Path where retina thumbnail should be saved\n * @param size - Target size of the longer side of the thumbnail\n * @returns Promise resolving to thumbnail dimensions\n */\nexport async function createImageThumbnails(\n image: Sharp,\n metadata: Metadata,\n outputPath: string,\n outputPathRetina: string,\n size: number,\n): Promise<Dimensions> {\n // Get the original dimensions\n const originalWidth = metadata.width || 0;\n const originalHeight = metadata.height || 0;\n\n if (originalWidth === 0 || originalHeight === 0) {\n throw new Error('Invalid image dimensions');\n }\n\n // Calculate the new size maintaining aspect ratio\n const aspectRatio = originalWidth / originalHeight;\n\n let width: number;\n let height: number;\n\n if (originalWidth > originalHeight) {\n width = size;\n height = Math.round(size / aspectRatio);\n } else {\n width = Math.round(size * aspectRatio);\n height = size;\n }\n\n // Resize the image and create the thumbnails\n await resizeImage(image, outputPath, width, height);\n await resizeImage(image, outputPathRetina, width * 2, height * 2);\n\n // Return the dimensions of the thumbnail\n return { width, height };\n}\n","import { Buffer } from 'node:buffer';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport sharp from 'sharp';\n\nimport { HEADER_IMAGE_LANDSCAPE_WIDTHS, HEADER_IMAGE_PORTRAIT_WIDTHS } from '../../../config';\nimport { cropAndResizeImage } from '../../../utils/image';\n\nimport type { ConsolaInstance } from 'consola';\n\n/** __dirname workaround for ESM modules */\nconst __dirname = path.dirname(new URL(import.meta.url).pathname);\n\n/**\n * Helper function to resolve paths relative to current file\n * @param segments - Path segments to resolve relative to current directory\n * @returns Resolved absolute path\n */\nexport function resolveFromCurrentDir(...segments: string[]): string {\n return path.resolve(__dirname, ...segments);\n}\n\n/**\n * Creates a social media card image for a gallery\n * @param headerPhotoPath - Path to the header photo\n * @param title - Title of the gallery\n * @param ouputPath - Output path for the social media card image\n * @param ui - ConsolaInstance for logging\n */\nexport async function createGallerySocialMediaCardImage(\n headerPhotoPath: string,\n title: string,\n ouputPath: string,\n ui: ConsolaInstance,\n): Promise<void> {\n ui.start(`Creating social media card image`);\n\n if (fs.existsSync(ouputPath)) {\n ui.success(`Social media card image already exists`);\n return;\n }\n\n // Read and resize the header image to 1200x631 using fit\n const resizedImageBuffer = await sharp(headerPhotoPath)\n .resize(1200, 631, { fit: 'cover' })\n .jpeg({ quality: 90 })\n .toBuffer();\n\n // Save the resized image as social media card\n const outputPath = ouputPath;\n await sharp(resizedImageBuffer).toFile(outputPath);\n\n // Create SVG with title and description\n const svgText = `\n <svg width=\"1200\" height=\"631\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <style>\n .title { font-family: Arial, sans-serif; font-size: 96px; font-weight: bold; fill: white; text-anchor: middle; }\n .description { font-family: Arial, sans-serif; font-size: 48px; fill: white; text-anchor: middle; }\n </style>\n </defs>\n <text x=\"600\" y=\"250\" class=\"title\">${title}</text>\n </svg>\n `;\n\n // Composite the text overlay on top of the resized image\n const finalImageBuffer = await sharp(resizedImageBuffer)\n .composite([{ input: Buffer.from(svgText), top: 0, left: 0 }])\n .jpeg({ quality: 90 })\n .toBuffer();\n\n // Save the final image with text overlay\n await sharp(finalImageBuffer).toFile(outputPath);\n\n ui.success(`Created social media card image successfully`);\n}\n\nexport async function createOptimizedHeaderImage(\n headerPhotoPath: string,\n outputFolder: string,\n ui: ConsolaInstance,\n): Promise<void> {\n ui.start(`Creating optimized header images`);\n\n const image = sharp(headerPhotoPath);\n\n // Create landscape header images\n const landscapeYFactor = 3 / 4;\n for (const width of HEADER_IMAGE_LANDSCAPE_WIDTHS) {\n ui.debug(`Creating landscape header image ${width}`);\n\n if (fs.existsSync(path.join(outputFolder, `header_landscape_${width}.avif`))) {\n ui.debug(`Landscape header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_landscape_${width}.avif`),\n width,\n width * landscapeYFactor,\n 'avif',\n );\n }\n\n if (fs.existsSync(path.join(outputFolder, `header_landscape_${width}.jpg`))) {\n ui.debug(`Landscape header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_landscape_${width}.jpg`),\n width,\n width * landscapeYFactor,\n 'jpg',\n );\n }\n }\n\n // Create portrait header images\n const portraitYFactor = 4 / 3;\n for (const width of HEADER_IMAGE_PORTRAIT_WIDTHS) {\n ui.debug(`Creating portrait header image ${width}`);\n\n if (fs.existsSync(path.join(outputFolder, `header_portrait_${width}.avif`))) {\n ui.debug(`Portrait header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_portrait_${width}.avif`),\n width,\n width * portraitYFactor,\n 'avif',\n );\n }\n\n if (fs.existsSync(path.join(outputFolder, `header_portrait_${width}.jpg`))) {\n ui.debug(`Portrait header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_portrait_${width}.jpg`),\n width,\n width * portraitYFactor,\n 'jpg',\n );\n }\n }\n\n ui.success(`Created optimized header image successfully`);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Finds all gallery directories that contain a gallery/gallery.json file.\n *\n * @param basePath - The base directory to search from\n * @param recursive - Whether to search subdirectories recursively\n * @returns Array of paths to directories containing gallery/gallery.json files\n */\nexport function findGalleries(basePath: string, recursive: boolean): string[] {\n const galleryDirs: string[] = [];\n\n // Check basePath itself\n const galleryJsonPath = path.join(basePath, 'gallery', 'gallery.json');\n if (fs.existsSync(galleryJsonPath)) {\n galleryDirs.push(basePath);\n }\n\n // If recursive, search all subdirectories\n if (recursive) {\n try {\n const entries = fs.readdirSync(basePath, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name !== 'gallery') {\n const subPath = path.join(basePath, entry.name);\n const subResults = findGalleries(subPath, recursive);\n galleryDirs.push(...subResults);\n }\n }\n } catch {\n // Silently ignore errors when reading directories\n }\n }\n\n return galleryDirs;\n}\n\n/**\n * Handles file processing errors with appropriate user-friendly messages\n * @param error - The error that occurred during file processing\n * @param filename - Name of the file that caused the error\n * @param ui - ConsolaInstance for logging messages\n */\nexport function handleFileProcessingError(error: unknown, filename: string, ui: ConsolaInstance): void {\n if (error instanceof Error && (error.message.includes('ffprobe') || error.message.includes('ffmpeg'))) {\n // Handle ffmpeg error\n ui.warn(\n `Error processing ${filename}: ffprobe (part of ffmpeg) is required to process videos. Please install ffmpeg and ensure it is available in your PATH`,\n );\n } else if (error instanceof Error && error.message.includes('unsupported image format')) {\n // Handle unsupported image format error\n ui.warn(`Error processing ${filename}: unsupported image format`);\n } else {\n // Handle unknown error\n ui.warn(`Error processing ${filename}`);\n }\n\n ui.debug(error);\n}\n","import { promises as fs } from 'node:fs';\n\n/**\n * Gets the last modification time of a file\n * @param filePath - Path to the file\n * @returns Promise resolving to the file's modification date\n */\nexport async function getFileMtime(filePath: string): Promise<Date> {\n const stats = await fs.stat(filePath);\n return stats.mtime;\n}\n","import { encode } from 'blurhash';\nimport sharp from 'sharp';\n\n/**\n * Generates a BlurHash from an image file or Sharp instance\n * @param imagePath - Path to image file or Sharp instance\n * @param componentX - Number of x components (default: 4)\n * @param componentY - Number of y components (default: 3)\n * @returns Promise resolving to BlurHash string\n */\nexport async function generateBlurHash(imagePath: string, componentX: number = 4, componentY: number = 3): Promise<string> {\n const image = sharp(imagePath);\n\n // Resize to small size for BlurHash computation to improve performance\n // BlurHash doesn't need high resolution\n const { data, info } = await image\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n // Convert to Uint8ClampedArray format expected by blurhash\n const pixels = new Uint8ClampedArray(data.buffer);\n\n // Generate BlurHash\n return encode(pixels, info.width, info.height, componentX, componentY);\n}\n","import { spawn } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\n\nimport ffprobe from 'node-ffprobe';\nimport sharp from 'sharp';\n\nimport { resizeImage } from './image';\n\nimport type { Dimensions } from '../types';\nimport type { Buffer } from 'node:buffer';\n\n/**\n * Gets video dimensions using ffprobe\n * @param filePath - Path to the video file\n * @returns Promise resolving to video dimensions\n * @throws Error if no video stream found or invalid dimensions\n */\nexport async function getVideoDimensions(filePath: string): Promise<Dimensions> {\n const data = await ffprobe(filePath);\n const videoStream = data.streams.find((stream) => stream.codec_type === 'video');\n\n if (!videoStream) {\n throw new Error('No video stream found');\n }\n\n const dimensions = {\n width: videoStream.width || 0,\n height: videoStream.height || 0,\n };\n\n if (dimensions.width === 0 || dimensions.height === 0) {\n throw new Error('Invalid video dimensions');\n }\n\n return dimensions;\n}\n\n/**\n * Creates regular and retina thumbnails for a video by extracting the first frame\n * @param inputPath - Path to the video file\n * @param videoDimensions - Original video dimensions\n * @param outputPath - Path where thumbnail should be saved\n * @param outputPathRetina - Path where retina thumbnail should be saved\n * @param height - Target height for thumbnail\n * @param verbose - Whether to enable verbose ffmpeg output\n * @returns Promise resolving to thumbnail dimensions\n */\nexport async function createVideoThumbnails(\n inputPath: string,\n videoDimensions: Dimensions,\n outputPath: string,\n outputPathRetina: string,\n height: number,\n verbose: boolean = false,\n): Promise<Dimensions> {\n // Calculate width maintaining aspect ratio\n const aspectRatio = videoDimensions.width / videoDimensions.height;\n const width = Math.round(height * aspectRatio);\n\n // Use ffmpeg to extract first frame as a temporary file, then process with sharp\n const tempFramePath = `${outputPath}.temp.png`;\n\n return new Promise((resolve, reject) => {\n // Extract first frame using ffmpeg\n const ffmpeg = spawn('ffmpeg', [\n '-i',\n inputPath,\n '-vframes',\n '1',\n '-y',\n '-loglevel',\n verbose ? 'error' : 'quiet',\n tempFramePath,\n ]);\n\n ffmpeg.stderr.on('data', (data: Buffer) => {\n // FFmpeg writes normal output to stderr, so we don't treat this as an error\n console.log(`ffmpeg: ${data.toString()}`);\n });\n\n ffmpeg.on('close', async (code: number) => {\n if (code === 0) {\n try {\n // Process the extracted frame with sharp\n const frameImage = sharp(tempFramePath);\n await resizeImage(frameImage, outputPath, width, height);\n await resizeImage(frameImage, outputPathRetina, width * 2, height * 2);\n\n // Clean up temporary file\n try {\n await fs.unlink(tempFramePath);\n } catch {\n // Ignore cleanup errors\n }\n\n resolve({ width, height });\n } catch (sharpError) {\n reject(new Error(`Failed to process extracted frame: ${sharpError}`));\n }\n } else {\n reject(new Error(`ffmpeg exited with code ${code}`));\n }\n });\n\n ffmpeg.on('error', (error: Error) => {\n reject(new Error(`Failed to start ffmpeg: ${error.message}`));\n });\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GalleryDataSchema, type MediaFile } from '@simple-photo-gallery/common/src/gallery';\nimport { LogLevels, type ConsolaInstance } from 'consola';\nimport sharp from 'sharp';\n\nimport { getFileMtime } from './utils';\n\nimport { DEFAULT_THUMBNAIL_SIZE } from '../../config';\nimport { findGalleries, handleFileProcessingError } from '../../utils';\nimport { generateBlurHash } from '../../utils/blurhash';\nimport { getImageDescription, createImageThumbnails } from '../../utils/image';\nimport { getVideoDimensions, createVideoThumbnails } from '../../utils/video';\n\nimport type { ThumbnailOptions } from './types';\n\n/**\n * Processes an image file to create thumbnail and extract metadata\n * @param imagePath - Path to the image file\n * @param thumbnailPath - Path where thumbnail should be saved\n * @param thumbnailPathRetina - Path where retina thumbnail should be saved\n * @param thumbnailSize - Target size for thumbnail\n * @param lastMediaTimestamp - Optional timestamp to check if processing can be skipped\n * @returns Promise resolving to updated MediaFile or undefined if skipped\n */\nasync function processImage(\n imagePath: string,\n thumbnailPath: string,\n thumbnailPathRetina: string,\n thumbnailSize: number,\n lastMediaTimestamp?: Date,\n): Promise<MediaFile | undefined> {\n // Get the last media timestamp\n const fileMtime = await getFileMtime(imagePath);\n\n // Check if processing of the file can be skipped\n if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs.existsSync(thumbnailPath)) {\n return undefined;\n }\n\n // Load the image\n const image = sharp(imagePath);\n const metadata = await image.metadata();\n\n // Get the image dimensions\n const imageDimensions = {\n width: metadata.width || 0,\n height: metadata.height || 0,\n };\n\n if (imageDimensions.width === 0 || imageDimensions.height === 0) {\n throw new Error('Invalid image dimensions');\n }\n\n // Get the image description\n const description = await getImageDescription(imagePath);\n\n // Create the thumbnails\n const thumbnailDimensions = await createImageThumbnails(\n image,\n metadata,\n thumbnailPath,\n thumbnailPathRetina,\n thumbnailSize,\n );\n\n // Generate BlurHash from the thumbnail\n const blurHash = await generateBlurHash(thumbnailPath);\n\n // Return the updated media file\n return {\n type: 'image',\n path: imagePath,\n alt: description,\n width: imageDimensions.width,\n height: imageDimensions.height,\n thumbnail: {\n path: thumbnailPath,\n pathRetina: thumbnailPathRetina,\n width: thumbnailDimensions.width,\n height: thumbnailDimensions.height,\n blurHash,\n },\n lastMediaTimestamp: fileMtime.toISOString(),\n };\n}\n\n/**\n * Processes a video file to create thumbnail and extract metadata\n * @param videoPath - Path to the video file\n * @param thumbnailPath - Path where thumbnail should be saved\n * @param thumbnailPathRetina - Path where retina thumbnail should be saved\n * @param thumbnailSize - Target size for thumbnail\n * @param verbose - Whether to enable verbose output\n * @param lastMediaTimestamp - Optional timestamp to check if processing can be skipped\n * @returns Promise resolving to updated MediaFile or undefined if skipped\n */\nasync function processVideo(\n videoPath: string,\n thumbnailPath: string,\n thumbnailPathRetina: string,\n thumbnailSize: number,\n verbose: boolean,\n lastMediaTimestamp?: Date,\n): Promise<MediaFile | undefined> {\n // Get the last media timestamp\n const fileMtime = await getFileMtime(videoPath);\n\n // Check if processing of the file can be skipped\n if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs.existsSync(thumbnailPath)) {\n return undefined;\n }\n\n // Get the video dimensions\n const videoDimensions = await getVideoDimensions(videoPath);\n\n // Create the thumbnail\n const thumbnailDimensions = await createVideoThumbnails(\n videoPath,\n videoDimensions,\n thumbnailPath,\n thumbnailPathRetina,\n thumbnailSize,\n verbose,\n );\n\n // Generate BlurHash from the thumbnail\n const blurHash = await generateBlurHash(thumbnailPath);\n\n return {\n type: 'video',\n path: videoPath,\n alt: undefined,\n width: videoDimensions.width,\n height: videoDimensions.height,\n thumbnail: {\n path: thumbnailPath,\n pathRetina: thumbnailPathRetina,\n width: thumbnailDimensions.width,\n height: thumbnailDimensions.height,\n blurHash,\n },\n lastMediaTimestamp: fileMtime.toISOString(),\n };\n}\n\n/**\n * Processes a media file to generate thumbnails and update metadata\n * @param mediaFile - Media file to process\n * @param galleryDir - Gallery directory path\n * @param thumbnailsPath - Directory where thumbnails are stored\n * @param thumbnailSize - Target size for thumbnails\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to updated MediaFile\n */\nasync function processMediaFile(\n mediaFile: MediaFile,\n galleryDir: string,\n thumbnailsPath: string,\n thumbnailSize: number,\n ui: ConsolaInstance,\n): Promise<MediaFile> {\n try {\n // Resolve the path relative to the gallery.json file location, not the gallery directory\n const galleryJsonDir = path.join(galleryDir, 'gallery');\n const filePath = path.resolve(path.join(galleryJsonDir, mediaFile.path));\n\n const fileName = path.basename(filePath);\n const fileNameWithoutExt = path.parse(fileName).name;\n const thumbnailFileName = `${fileNameWithoutExt}.avif`;\n const thumbnailPath = path.join(thumbnailsPath, thumbnailFileName);\n const thumbnailPathRetina = thumbnailPath.replace('.avif', '@2x.avif');\n const relativeThumbnailPath = path.relative(galleryJsonDir, thumbnailPath);\n const relativeThumbnailRetinaPath = path.relative(galleryJsonDir, thumbnailPathRetina);\n\n const lastMediaTimestamp = mediaFile.lastMediaTimestamp ? new Date(mediaFile.lastMediaTimestamp) : undefined;\n const verbose = ui.level === LogLevels.debug;\n\n ui.debug(` Processing ${mediaFile.type}: ${fileName}`);\n\n const updatedMediaFile = await (mediaFile.type === 'image'\n ? processImage(filePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, lastMediaTimestamp)\n : processVideo(filePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, verbose, lastMediaTimestamp));\n\n if (!updatedMediaFile) {\n ui.debug(` Skipping ${fileName} because it has already been processed`);\n\n // Check if we need to generate BlurHash for existing thumbnail\n if (mediaFile.thumbnail && !mediaFile.thumbnail.blurHash && fs.existsSync(thumbnailPath)) {\n try {\n const blurHash = await generateBlurHash(thumbnailPath);\n return {\n ...mediaFile,\n thumbnail: {\n ...mediaFile.thumbnail,\n blurHash,\n },\n };\n } catch (error) {\n ui.debug(` Failed to generate BlurHash for ${fileName}:`, error);\n }\n }\n\n return mediaFile;\n }\n\n updatedMediaFile.path = mediaFile.path;\n if (updatedMediaFile.thumbnail) {\n updatedMediaFile.thumbnail.path = relativeThumbnailPath;\n updatedMediaFile.thumbnail.pathRetina = relativeThumbnailRetinaPath;\n }\n\n return updatedMediaFile;\n } catch (error) {\n handleFileProcessingError(error, path.basename(mediaFile.path), ui);\n\n return mediaFile;\n }\n}\n\n/**\n * Processes all media files in a gallery to generate thumbnails\n * @param galleryDir - Directory containing the gallery\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to the number of files processed\n */\nexport async function processGalleryThumbnails(galleryDir: string, ui: ConsolaInstance): Promise<number> {\n const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');\n const thumbnailsPath = path.join(galleryDir, 'gallery', 'images');\n\n ui.start(`Creating thumbnails: ${galleryDir}`);\n\n try {\n // Ensure thumbnails directory exists\n fs.mkdirSync(thumbnailsPath, { recursive: true });\n\n // Read gallery.json\n const galleryContent = fs.readFileSync(galleryJsonPath, 'utf8');\n const galleryData = GalleryDataSchema.parse(JSON.parse(galleryContent));\n\n const thumbnailSize = galleryData.thumbnailSize || DEFAULT_THUMBNAIL_SIZE;\n\n // Process all sections and their images\n let processedCount = 0;\n for (const section of galleryData.sections) {\n for (const [index, mediaFile] of section.images.entries()) {\n section.images[index] = await processMediaFile(mediaFile, galleryDir, thumbnailsPath, thumbnailSize, ui);\n }\n\n processedCount += section.images.length;\n }\n\n // Write updated gallery.json\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n ui.success(`Created thumbnails for ${processedCount} media files`);\n\n return processedCount;\n } catch (error) {\n ui.error(`Error creating thumbnails for ${galleryDir}`);\n throw error;\n }\n}\n\n/**\n * Main thumbnails command implementation - generates thumbnails for all galleries\n * @param options - Options specifying gallery path and recursion settings\n * @param ui - ConsolaInstance for logging\n */\nexport async function thumbnails(options: ThumbnailOptions, ui: ConsolaInstance): Promise<void> {\n try {\n // Find all gallery directories\n const galleryDirs = findGalleries(options.gallery, options.recursive);\n if (galleryDirs.length === 0) {\n ui.error('No galleries found.');\n return;\n }\n\n // Process each gallery directory\n let totalGalleries = 0;\n let totalProcessed = 0;\n for (const galleryDir of galleryDirs) {\n const processed = await processGalleryThumbnails(galleryDir, ui);\n\n if (processed > 0) {\n ++totalGalleries;\n totalProcessed += processed;\n }\n }\n\n ui.box(\n `Created thumbnails for ${totalGalleries} ${totalGalleries === 1 ? 'gallery' : 'galleries'} with ${totalProcessed} media ${totalProcessed === 1 ? 'file' : 'files'}`,\n );\n } catch (error) {\n ui.error('Error creating thumbnails');\n throw error;\n }\n}\n","import { execSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\n\nimport { type GalleryData, GalleryDataSchema } from '@simple-photo-gallery/common/src/gallery';\nimport { LogLevels, type ConsolaInstance } from 'consola';\n\nimport { createGallerySocialMediaCardImage, createOptimizedHeaderImage } from './utils';\n\nimport { findGalleries } from '../../utils';\nimport { processGalleryThumbnails } from '../thumbnails';\n\nimport type { BuildOptions } from './types';\n\n/**\n * Checks if a file path refers to a file one folder up from the current directory\n * @param filePath - The file path to check\n * @returns True if the file is exactly one folder up (../filename)\n */\nfunction checkFileIsOneFolderUp(filePath: string): boolean {\n const normalizedPath = path.normalize(filePath);\n const pathParts = normalizedPath.split(path.sep);\n return pathParts.length === 2 && pathParts[0] === '..';\n}\n\n/**\n * Copies photos from gallery subdirectory to main directory when needed\n * @param galleryData - Gallery data containing image paths\n * @param galleryDir - Base gallery directory\n * @param ui - ConsolaInstance for logging\n */\nfunction copyPhotos(galleryData: GalleryData, galleryDir: string, ui: ConsolaInstance): void {\n for (const section of galleryData.sections) {\n for (const image of section.images) {\n if (!checkFileIsOneFolderUp(image.path)) {\n const sourcePath = path.join(galleryDir, 'gallery', image.path);\n const fileName = path.basename(image.path);\n const destPath = path.join(galleryDir, fileName);\n\n ui.debug(`Copying photo to ${destPath}`);\n fs.copyFileSync(sourcePath, destPath);\n }\n }\n }\n}\n\n/**\n * Builds a single gallery by generating thumbnails and creating HTML output\n * @param galleryDir - Directory containing the gallery\n * @param templateDir - Directory containing the Astro template\n * @param ui - ConsolaInstance for logging\n * @param baseUrl - Optional base URL for hosting photos\n */\nasync function buildGallery(galleryDir: string, templateDir: string, ui: ConsolaInstance, baseUrl?: string): Promise<void> {\n ui.start(`Building gallery ${galleryDir}`);\n\n // Generate the thumbnails if needed\n await processGalleryThumbnails(galleryDir, ui);\n\n // Read the gallery.json file\n const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');\n const galleryContent = fs.readFileSync(galleryJsonPath, 'utf8');\n const galleryData = GalleryDataSchema.parse(JSON.parse(galleryContent));\n const socialMediaCardImagePath = path.join(galleryDir, 'gallery', 'images', 'social-media-card.jpg');\n const headerImagePath = path.resolve(path.join(galleryDir, 'gallery'), galleryData.headerImage);\n\n // Create the gallery social media card image\n await createGallerySocialMediaCardImage(headerImagePath, galleryData.title, socialMediaCardImagePath, ui);\n galleryData.metadata.image =\n galleryData.metadata.image || `${galleryData.url || ''}/${path.relative(galleryDir, socialMediaCardImagePath)}`;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n // Create optimized header image\n await createOptimizedHeaderImage(headerImagePath, path.join(galleryDir, 'gallery', 'images'), ui);\n\n // Check if the photos need to be copied. Not needed if the baseUrl is provided.\n if (!baseUrl) {\n const shouldCopyPhotos = galleryData.sections.some((section) =>\n section.images.some((image) => !checkFileIsOneFolderUp(image.path)),\n );\n\n if (\n shouldCopyPhotos &&\n (await ui.prompt('All photos need to be copied. Are you sure you want to continue?', { type: 'confirm' }))\n ) {\n ui.debug('Copying photos');\n copyPhotos(galleryData, galleryDir, ui);\n }\n }\n\n // If the baseUrl is provided, update the gallery.json file\n if (baseUrl) {\n ui.debug('Updating gallery.json with baseUrl');\n galleryData.mediaBaseUrl = baseUrl;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n\n // Build the template\n ui.debug('Building gallery form template');\n try {\n // Set the environment variable for the gallery.json path that will be used by the template\n process.env.GALLERY_JSON_PATH = galleryJsonPath;\n process.env.GALLERY_OUTPUT_DIR = path.join(galleryDir, 'gallery');\n\n execSync('npx astro build', { cwd: templateDir, stdio: ui.level === LogLevels.debug ? 'inherit' : 'ignore' });\n } catch (error) {\n ui.error(`Build failed for ${galleryDir}`);\n throw error;\n }\n\n // Copy the build output to the output directory\n const outputDir = path.join(galleryDir, 'gallery');\n const buildDir = path.join(outputDir, '_build');\n ui.debug(`Copying build output to ${outputDir}`);\n fs.cpSync(buildDir, outputDir, { recursive: true });\n\n // Move the index.html to the gallery directory\n ui.debug('Moving index.html to gallery directory');\n fs.copyFileSync(path.join(outputDir, 'index.html'), path.join(galleryDir, 'index.html'));\n fs.rmSync(path.join(outputDir, 'index.html'));\n\n // Clean up the _build directory\n ui.debug('Cleaning up build directory');\n fs.rmSync(buildDir, { recursive: true, force: true });\n\n ui.success(`Gallery built successfully`);\n}\n\n/**\n * Main build command implementation - builds HTML galleries from gallery.json files\n * @param options - Options specifying gallery path, recursion, and base URL\n * @param ui - ConsolaInstance for logging\n */\nexport async function build(options: BuildOptions, ui: ConsolaInstance): Promise<void> {\n try {\n // Find all gallery directories\n const galleryDirs = findGalleries(options.gallery, options.recursive);\n if (galleryDirs.length === 0) {\n ui.error('No galleries found.');\n return;\n }\n\n // Get the astro theme directory from the default one\n const themePath = await import.meta.resolve('@simple-photo-gallery/theme-modern/package.json');\n const themeDir = path.dirname(new URL(themePath).pathname);\n\n // Process each gallery directory\n let totalGalleries = 0;\n for (const dir of galleryDirs) {\n const baseUrl = options.baseUrl ? `${options.baseUrl}${path.relative(options.gallery, dir)}` : undefined;\n await buildGallery(path.resolve(dir), themeDir, ui, baseUrl);\n\n ++totalGalleries;\n }\n\n ui.box(`Built ${totalGalleries} ${totalGalleries === 1 ? 'gallery' : 'galleries'} successfully`);\n } catch (error) {\n if (error instanceof Error && error.message.includes('Cannot find package')) {\n ui.error('Theme package not found: @simple-photo-gallery/theme-modern/package.json');\n } else {\n ui.error('Error building gallery');\n }\n\n throw error;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { findGalleries } from '../../utils';\n\nimport type { CleanOptions } from './types';\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Clean gallery files from a single directory\n * @param galleryDir - Directory containing a gallery\n * @param ui - Consola instance for logging\n */\nasync function cleanGallery(galleryDir: string, ui: ConsolaInstance): Promise<void> {\n let filesRemoved = 0;\n\n // Remove index.html file from the gallery directory\n const indexHtmlPath = path.join(galleryDir, 'index.html');\n if (fs.existsSync(indexHtmlPath)) {\n try {\n fs.rmSync(indexHtmlPath);\n ui.debug(`Removed: ${indexHtmlPath}`);\n filesRemoved++;\n } catch (error) {\n ui.warn(`Failed to remove index.html: ${error}`);\n }\n }\n\n // Remove gallery directory and all its contents\n const galleryPath = path.join(galleryDir, 'gallery');\n if (fs.existsSync(galleryPath)) {\n try {\n fs.rmSync(galleryPath, { recursive: true, force: true });\n ui.debug(`Removed directory: ${galleryPath}`);\n filesRemoved++;\n } catch (error) {\n ui.warn(`Failed to remove gallery directory: ${error}`);\n }\n }\n\n if (filesRemoved > 0) {\n ui.success(`Cleaned gallery at: ${galleryDir}`);\n } else {\n ui.info(`No gallery files found at: ${galleryDir}`);\n }\n}\n\n/**\n * Clean command implementation\n * Removes all gallery-related files and directories\n */\nexport async function clean(options: CleanOptions, ui: ConsolaInstance): Promise<void> {\n try {\n const basePath = path.resolve(options.gallery);\n\n // Check if the base path exists\n if (!fs.existsSync(basePath)) {\n ui.error(`Directory does not exist: ${basePath}`);\n return;\n }\n\n // Find all gallery directories\n const galleryDirs = findGalleries(basePath, options.recursive);\n\n if (galleryDirs.length === 0) {\n ui.info('No galleries found to clean.');\n return;\n }\n\n // Clean each gallery directory\n for (const dir of galleryDirs) {\n await cleanGallery(dir, ui);\n }\n\n ui.box(`Successfully cleaned ${galleryDirs.length} ${galleryDirs.length === 1 ? 'gallery' : 'galleries'}`);\n } catch (error) {\n ui.error('Error cleaning galleries');\n throw error;\n }\n}\n","import path from 'node:path';\n\nimport { IMAGE_EXTENSIONS, VIDEO_EXTENSIONS } from '../../../config';\n\nimport type { MediaFileType } from '../types';\n\n/**\n * Determines the media file type based on file extension\n * @param fileName - Name of the file to check\n * @returns Media file type ('image' or 'video') or null if not supported\n */\nexport function getMediaFileType(fileName: string): MediaFileType | null {\n const ext = path.extname(fileName).toLowerCase();\n if (IMAGE_EXTENSIONS.has(ext)) return 'image';\n if (VIDEO_EXTENSIONS.has(ext)) return 'video';\n return null;\n}\n\n/**\n * Converts a folder name into a properly capitalized title\n * @param folderName - The folder name to convert\n * @returns Formatted title with proper capitalization\n */\nexport function capitalizeTitle(folderName: string): string {\n return folderName\n .replace('-', ' ')\n .replace('_', ' ')\n .split(' ')\n .map((word: string) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport { capitalizeTitle, getMediaFileType } from './utils';\n\nimport type { GallerySettingsFromUser, ProcessDirectoryResult, ScanDirectoryResult, ScanOptions, SubGallery } from './types';\nimport type { MediaFile } from '@simple-photo-gallery/common/src/gallery';\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Scans a directory for media files and subdirectories\n * @param dirPath - Path to the directory to scan\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to scan results with media files and subdirectories\n */\nasync function scanDirectory(dirPath: string, ui: ConsolaInstance): Promise<ScanDirectoryResult> {\n const mediaFiles: MediaFile[] = [];\n const subGalleryDirectories: string[] = [];\n\n try {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isFile()) {\n const fullPath = path.join(dirPath, entry.name);\n const mediaType = getMediaFileType(entry.name);\n\n if (mediaType) {\n const mediaFile: MediaFile = {\n type: mediaType,\n path: fullPath,\n width: 0,\n height: 0,\n };\n\n mediaFiles.push(mediaFile);\n }\n } else if (entry.isDirectory() && entry.name !== 'gallery') {\n subGalleryDirectories.push(path.join(dirPath, entry.name));\n }\n }\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n ui.error(`Directory does not exist: ${dirPath}`);\n } else if (error instanceof Error && error.message.includes('ENOTDIR')) {\n ui.error(`Path is not a directory: ${dirPath}`);\n } else {\n ui.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n throw error;\n }\n\n return { mediaFiles, subGalleryDirectories };\n}\n\n/**\n * Prompts the user for gallery settings through interactive CLI\n * @param galleryName - Name of the gallery directory\n * @param defaultImage - Default header image path\n * @param ui - ConsolaInstance for prompting and logging\n * @returns Promise resolving to user-provided gallery settings\n */\nasync function getGallerySettingsFromUser(\n galleryName: string,\n defaultImage: string,\n ui: ConsolaInstance,\n): Promise<GallerySettingsFromUser> {\n ui.info(`Enter gallery settings for the gallery in folder \"${galleryName}\"`);\n\n const title = await ui.prompt('Enter gallery title', { type: 'text', default: 'My Gallery', placeholder: 'My Gallery' });\n const description = await ui.prompt('Enter gallery description', {\n type: 'text',\n default: 'My gallery with fantastic photos.',\n placeholder: 'My gallery with fantastic photos.',\n });\n const url = await ui.prompt('Enter the URL where the gallery will be hosted (important for social media image)', {\n type: 'text',\n default: '',\n placeholder: '',\n });\n const headerImageName = await ui.prompt('Enter the name of the header image', {\n type: 'text',\n default: defaultImage,\n placeholder: defaultImage,\n });\n\n const headerImage = path.join('..', headerImageName);\n\n return { title, description, url, headerImage };\n}\n\n/**\n * Creates a gallery.json file with media files and settings\n * @param mediaFiles - Array of media files to include in gallery\n * @param galleryJsonPath - Path where gallery.json should be created\n * @param subGalleries - Array of sub-galleries to include\n * @param useDefaultSettings - Whether to use default settings or prompt user\n * @param ui - ConsolaInstance for prompting and logging\n */\nasync function createGalleryJson(\n mediaFiles: MediaFile[],\n galleryJsonPath: string,\n subGalleries: SubGallery[] = [],\n useDefaultSettings: boolean,\n ui: ConsolaInstance,\n): Promise<void> {\n const galleryDir = path.dirname(galleryJsonPath);\n\n // Convert media file paths to be relative to gallery.json\n const relativeMediaFiles = mediaFiles.map((file) => ({\n ...file,\n path: path.relative(galleryDir, file.path),\n }));\n\n // Convert subGallery header image paths to be relative to gallery.json\n const relativeSubGalleries = subGalleries.map((subGallery) => ({\n ...subGallery,\n headerImage: subGallery.headerImage ? path.relative(galleryDir, subGallery.headerImage) : '',\n }));\n\n let galleryData = {\n title: 'My Gallery',\n description: 'My gallery with fantastic photos.',\n headerImage: relativeMediaFiles[0]?.path || '',\n metadata: {},\n sections: [\n {\n images: relativeMediaFiles,\n },\n ],\n subGalleries: {\n title: 'Sub Galleries',\n galleries: relativeSubGalleries,\n },\n };\n\n if (!useDefaultSettings) {\n galleryData = {\n ...galleryData,\n ...(await getGallerySettingsFromUser(\n path.basename(path.join(galleryDir, '..')),\n path.basename(mediaFiles[0]?.path || ''),\n ui,\n )),\n };\n }\n\n await fs.writeFile(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n}\n\n/**\n * Processes a directory and its subdirectories to create galleries\n * @param scanPath - Path to scan for media files\n * @param outputPath - Path where gallery should be created\n * @param recursive - Whether to process subdirectories recursively\n * @param useDefaultSettings - Whether to use default settings or prompt user\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to processing results\n */\nasync function processDirectory(\n scanPath: string,\n outputPath: string,\n recursive: boolean,\n useDefaultSettings: boolean,\n ui: ConsolaInstance,\n): Promise<ProcessDirectoryResult> {\n ui.start(`Scanning ${scanPath}`);\n\n let totalFiles = 0;\n let totalGalleries = 1;\n const subGalleries: SubGallery[] = [];\n\n // Scan current directory for media files\n const { mediaFiles, subGalleryDirectories } = await scanDirectory(scanPath, ui);\n totalFiles += mediaFiles.length;\n\n // Process subdirectories only if recursive mode is enabled\n if (recursive) {\n for (const subGalleryDir of subGalleryDirectories) {\n const result = await processDirectory(\n subGalleryDir,\n path.join(outputPath, path.basename(subGalleryDir)),\n recursive,\n useDefaultSettings,\n ui,\n );\n\n totalFiles += result.totalFiles;\n totalGalleries += result.totalGalleries;\n\n // If the result contains a valid subGallery, add it to the list\n if (result.subGallery) {\n subGalleries.push(result.subGallery);\n }\n }\n }\n\n // Create gallery.json if there are media files or subGalleries\n if (mediaFiles.length > 0 || subGalleries.length > 0) {\n const galleryPath = path.join(outputPath, 'gallery');\n const galleryJsonPath = path.join(galleryPath, 'gallery.json');\n\n try {\n // Create output directory\n await fs.mkdir(galleryPath, { recursive: true });\n\n // Create gallery.json for this directory\n await createGalleryJson(mediaFiles, galleryJsonPath, subGalleries, useDefaultSettings, ui);\n\n ui.success(\n `Create gallery with ${mediaFiles.length} files and ${subGalleries.length} subgalleries at: ${galleryJsonPath}`,\n );\n } catch (error) {\n ui.error(`Error creating gallery.json at ${galleryJsonPath}`);\n throw error;\n }\n }\n\n // Return result with suGgallery info if this directory has media files\n const result: ProcessDirectoryResult = { totalFiles, totalGalleries };\n\n // If this directory has media files or subGalleries, create a subGallery in the result\n if (mediaFiles.length > 0 || subGalleries.length > 0) {\n const dirName = path.basename(scanPath);\n result.subGallery = {\n title: capitalizeTitle(dirName),\n headerImage: mediaFiles[0]?.path || '',\n path: path.join('..', dirName),\n };\n }\n\n return result;\n}\n\n/**\n * Main init command implementation - scans directories and creates gallery.json files\n * @param options - Options specifying paths, recursion, and default settings\n * @param ui - ConsolaInstance for logging and user prompts\n */\nexport async function init(options: ScanOptions, ui: ConsolaInstance): Promise<void> {\n try {\n const scanPath = path.resolve(options.photos);\n const outputPath = options.gallery ? path.resolve(options.gallery) : scanPath;\n\n // Process the directory tree with the specified recursion setting\n const result = await processDirectory(scanPath, outputPath, options.recursive, options.default, ui);\n\n ui.box(\n `Created ${result.totalGalleries} ${result.totalGalleries === 1 ? 'gallery' : 'galleries'} with ${result.totalFiles} media ${result.totalFiles === 1 ? 'file' : 'files'}`,\n );\n } catch (error) {\n ui.error('Error initializing gallery');\n throw error;\n }\n}\n","#!/usr/bin/env node\n\nimport process from 'node:process';\n\nimport { Command } from 'commander';\nimport { createConsola, LogLevels, type ConsolaInstance } from 'consola';\n\nimport { build } from './modules/build';\nimport { clean } from './modules/clean';\nimport { init } from './modules/init';\nimport { thumbnails } from './modules/thumbnails';\n\n/** Command line interface program instance */\nconst program = new Command();\n\nprogram\n .name('gallery')\n .description('Simple Photo Gallery CLI')\n .version('0.0.1')\n .option('-v, --verbose', 'Verbose output (debug level)', false)\n .option('-q, --quiet', 'Minimal output (only warnings/errors)', false)\n .showHelpAfterError(true);\n\n/**\n * Creates a Consola UI instance with appropriate log level based on global options\n * @param globalOpts - Global command options containing verbose/quiet flags\n * @returns ConsolaInstance configured with appropriate log level and tag\n */\nfunction createConsolaUI(globalOpts: ReturnType<typeof program.opts>): ConsolaInstance {\n let level = LogLevels.info;\n\n if (globalOpts.quiet) {\n level = LogLevels.warn;\n } else if (globalOpts.verbose) {\n level = LogLevels.debug;\n }\n\n return createConsola({\n level,\n }).withTag('simple-photo-gallery');\n}\n\n/**\n * Higher-order function that wraps command handlers to provide ConsolaUI instance\n * @param handler - Command handler function that receives options and UI instance\n * @returns Wrapped handler function that creates UI and handles errors\n */\nfunction withConsolaUI<O>(handler: (opts: O, ui: ConsolaInstance) => Promise<void> | void) {\n return async (opts: O) => {\n const ui = createConsolaUI(program.opts());\n try {\n await handler(opts, ui);\n } catch (error) {\n ui.debug(error);\n\n process.exitCode = 1;\n }\n };\n}\n\nprogram\n .command('init')\n .description('Initialize a gallery by scaning a folder for images and videos')\n .option(\n '-p, --photos <path>',\n 'Path to the folder where the photos are stored. Default: current working directory',\n process.cwd(),\n )\n .option(\n '-g, --gallery <path>',\n 'Path to the directory where the gallery will be initialized. Default: same directory as the photos folder',\n )\n .option('-r, --recursive', 'Recursively create galleries from all photos subdirectories', false)\n .option('-d, --default', 'Use default gallery settings instead of asking the user', false)\n .action(withConsolaUI(init));\n\nprogram\n .command('thumbnails')\n .description('Create thumbnails for all media files in the gallery')\n .option('-g, --gallery <path>', 'Path to the directory of the gallery. Default: current working directory', process.cwd())\n .option('-r, --recursive', 'Scan subdirectories recursively', false)\n .action(withConsolaUI(thumbnails));\n\nprogram\n .command('build')\n .description('Build the HTML gallery in the specified directory')\n .option('-g, --gallery <path>', 'Path to the directory of the gallery. Default: current working directory', process.cwd())\n .option('-r, --recursive', 'Scan subdirectories recursively', false)\n .option('-b, --base-url <url>', 'Base URL where the photos are hosted')\n .action(withConsolaUI(build));\n\nprogram\n .command('clean')\n .description('Remove all gallery files and folders (index.html, gallery/)')\n .option('-g, --gallery <path>', 'Path to the directory of the gallery. Default: current working directory', process.cwd())\n .option('-r, --recursive', 'Clean subdirectories recursively', false)\n .action(withConsolaUI(clean));\n\nprogram.parse();\n"]}
1
+ {"version":3,"sources":["../../common/src/gallery.ts","../src/config/index.ts","../src/utils/image.ts","../src/modules/build/utils/index.ts","../src/utils/index.ts","../src/modules/thumbnails/utils/index.ts","../src/utils/blurhash.ts","../src/utils/video.ts","../src/modules/thumbnails/index.ts","../src/modules/build/index.ts","../src/modules/clean/index.ts","../src/modules/init/utils/index.ts","../src/modules/init/index.ts","../package.json","../src/index.ts"],"names":["sharp","path","fs","process","LogLevels","result"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\n/** Zod schema for thumbnail metadata including path and dimensions */\nexport const ThumbnailSchema = z.object({\n path: z.string(),\n pathRetina: z.string(),\n width: z.number(),\n height: z.number(),\n blurHash: z.string().optional(),\n});\n\n/** Zod schema for media file metadata including type, dimensions, and thumbnail info */\nexport const MediaFileSchema = z.object({\n type: z.enum(['image', 'video']),\n path: z.string(),\n alt: z.string().optional(),\n width: z.number(),\n height: z.number(),\n thumbnail: ThumbnailSchema.optional(),\n lastMediaTimestamp: z.string().optional(),\n});\n\n/** Zod schema for a gallery section containing title, description, and media files */\nexport const GallerySectionSchema = z.object({\n title: z.string().optional(),\n description: z.string().optional(),\n images: z.array(MediaFileSchema),\n});\n\n/** Zod schema for sub-gallery metadata including title, header image, and path */\nexport const SubGallerySchema = z.object({\n title: z.string(),\n headerImage: z.string(),\n path: z.string(),\n});\n\n/** Zod schema for complete gallery data including metadata, sections, and sub-galleries */\nexport const GalleryMetadataSchema = z.object({\n image: z.string().optional(),\n imageWidth: z.number().optional(),\n imageHeight: z.number().optional(),\n ogUrl: z.string().optional(),\n ogType: z.string().optional(),\n ogSiteName: z.string().optional(),\n twitterSite: z.string().optional(),\n twitterCreator: z.string().optional(),\n author: z.string().optional(),\n keywords: z.string().optional(),\n canonicalUrl: z.string().optional(),\n language: z.string().optional(),\n robots: z.string().optional(),\n});\n\n/** Zod schema for complete gallery data including metadata, sections, and sub-galleries */\nexport const GalleryDataSchema = z.object({\n title: z.string(),\n description: z.string(),\n url: z.string().optional(),\n headerImage: z.string(),\n thumbnailSize: z.number().optional(),\n metadata: GalleryMetadataSchema,\n galleryOutputPath: z.string().optional(),\n mediaBaseUrl: z.string().optional(),\n sections: z.array(GallerySectionSchema),\n subGalleries: z.object({ title: z.string(), galleries: z.array(SubGallerySchema) }),\n});\n\n/** TypeScript type for thumbnail metadata */\nexport type Thumbnail = z.infer<typeof ThumbnailSchema>;\n\n/** TypeScript type for media file metadata */\nexport type MediaFile = z.infer<typeof MediaFileSchema>;\n\n/** TypeScript type for gallery section data */\nexport type GallerySection = z.infer<typeof GallerySectionSchema>;\n\n/** TypeScript type for sub-gallery metadata */\nexport type SubGallery = z.infer<typeof SubGallerySchema>;\n\n/** TypeScript type for gallery metadata */\nexport type GalleryMetadata = z.infer<typeof GalleryMetadataSchema>;\n\n/** TypeScript type for complete gallery data structure */\nexport type GalleryData = z.infer<typeof GalleryDataSchema>;\n","/** Default thumbnail size in pixels */\nexport const DEFAULT_THUMBNAIL_SIZE = 300;\n\n/** Set of supported image file extensions */\nexport const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp', '.tiff', '.tif', '.svg', '.avif']);\n\n/** Set of supported video file extensions */\nexport const VIDEO_EXTENSIONS = new Set(['.mp4', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv', '.m4v', '.3gp']);\n\n/** Set of supported header image landscape widths */\nexport const HEADER_IMAGE_LANDSCAPE_WIDTHS = [3840, 2560, 1920, 1280, 960, 640];\n\n/** Set of supported header image portrait widths */\nexport const HEADER_IMAGE_PORTRAIT_WIDTHS = [1080, 720, 480, 360];\n","import ExifReader from 'exifreader';\nimport sharp from 'sharp';\n\nimport type { Dimensions, ImageWithMetadata } from '../types';\nimport type { FormatEnum, Metadata, Sharp } from 'sharp';\n\n/**\n * Loads an image and auto-rotates it based on EXIF orientation.\n * @param imagePath - Path to the image file\n * @returns Promise resolving to Sharp image instance\n */\nexport async function loadImage(imagePath: string): Promise<Sharp> {\n return sharp(imagePath).rotate();\n}\n\n/**\n * Loads an image and its metadata, auto-rotating it based on EXIF orientation and swapping dimensions if needed.\n * @param imagePath - Path to the image file\n * @returns Promise resolving to ImageWithMetadata object containing Sharp image instance and metadata\n */\nexport async function loadImageWithMetadata(imagePath: string): Promise<ImageWithMetadata> {\n const image = sharp(imagePath);\n const metadata = await image.metadata();\n\n // Auto-rotate based on EXIF orientation\n image.rotate();\n\n // EXIF orientation values 5, 6, 7, 8 require dimension swap after rotation\n const needsDimensionSwap = metadata.orientation && metadata.orientation >= 5 && metadata.orientation <= 8;\n\n // Update metadata with swapped dimensions if needed\n if (needsDimensionSwap) {\n const originalWidth = metadata.width;\n metadata.width = metadata.height;\n metadata.height = originalWidth;\n }\n\n return { image, metadata };\n}\n\n/**\n * Utility function to resize and save thumbnail using Sharp. The functions avoids upscaling the image and only reduces the size if necessary.\n * @param image - Sharp image instance\n * @param outputPath - Path where thumbnail should be saved\n * @param width - Target width for thumbnail\n * @param height - Target height for thumbnail\n */\nexport async function resizeImage(\n image: Sharp,\n outputPath: string,\n width: number,\n height: number,\n format: keyof FormatEnum = 'avif',\n): Promise<void> {\n // Resize the image without enlarging it\n await image.resize(width, height, { withoutEnlargement: true }).toFormat(format).toFile(outputPath);\n}\n\n/**\n * Crops and resizes an image to a target aspect ratio, avoiding upscaling the image.\n * @param image - Sharp image instance\n * @param outputPath - Path where the image should be saved\n * @param width - Target width for the image\n * @param height - Target height for the image\n */\nexport async function cropAndResizeImage(\n image: Sharp,\n outputPath: string,\n width: number,\n height: number,\n format: keyof FormatEnum = 'avif',\n): Promise<void> {\n // Apply resize with cover fit and without enlargement\n await image\n .resize(width, height, {\n fit: 'cover',\n withoutEnlargement: true,\n })\n .toFormat(format)\n .toFile(outputPath);\n}\n\n/**\n * Extracts description from image EXIF data\n * @param metadata - Sharp metadata object containing EXIF data\n * @returns Promise resolving to image description or undefined if not found\n */\nexport async function getImageDescription(imagePath: string): Promise<string | undefined> {\n try {\n const tags = await ExifReader.load(imagePath);\n\n // Description\n if (tags.description?.description) return tags.description.description;\n\n // ImageDescription\n if (tags.ImageDescription?.description) return tags.ImageDescription.description;\n\n // UserComment\n if (\n tags.UserComment &&\n typeof tags.UserComment === 'object' &&\n tags.UserComment !== null &&\n 'description' in tags.UserComment\n ) {\n return (tags.UserComment as { description: string }).description;\n }\n\n // ExtDescrAccessibility\n if (tags.ExtDescrAccessibility?.description) return tags.ExtDescrAccessibility.description;\n\n // Caption/Abstract\n if (tags['Caption/Abstract']?.description) return tags['Caption/Abstract'].description;\n\n // XP Title\n if (tags.XPTitle?.description) return tags.XPTitle.description;\n\n // XP Comment\n if (tags.XPComment?.description) return tags.XPComment.description;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Creates regular and retina thumbnails for an image while maintaining aspect ratio\n * @param image - Sharp image instance\n * @param metadata - Image metadata containing dimensions\n * @param outputPath - Path where thumbnail should be saved\n * @param outputPathRetina - Path where retina thumbnail should be saved\n * @param size - Target size of the longer side of the thumbnail\n * @returns Promise resolving to thumbnail dimensions\n */\nexport async function createImageThumbnails(\n image: Sharp,\n metadata: Metadata,\n outputPath: string,\n outputPathRetina: string,\n size: number,\n): Promise<Dimensions> {\n // Get the original dimensions\n const originalWidth = metadata.width || 0;\n const originalHeight = metadata.height || 0;\n\n if (originalWidth === 0 || originalHeight === 0) {\n throw new Error('Invalid image dimensions');\n }\n\n // Calculate the new size maintaining aspect ratio\n const aspectRatio = originalWidth / originalHeight;\n\n let width: number;\n let height: number;\n\n if (originalWidth > originalHeight) {\n width = size;\n height = Math.round(size / aspectRatio);\n } else {\n width = Math.round(size * aspectRatio);\n height = size;\n }\n\n // Resize the image and create the thumbnails\n await resizeImage(image, outputPath, width, height);\n await resizeImage(image, outputPathRetina, width * 2, height * 2);\n\n // Return the dimensions of the thumbnail\n return { width, height };\n}\n","import { Buffer } from 'node:buffer';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport sharp from 'sharp';\n\nimport { HEADER_IMAGE_LANDSCAPE_WIDTHS, HEADER_IMAGE_PORTRAIT_WIDTHS } from '../../../config';\nimport { cropAndResizeImage, loadImage } from '../../../utils/image';\n\nimport type { ConsolaInstance } from 'consola';\n\n/** __dirname workaround for ESM modules */\nconst __dirname = path.dirname(new URL(import.meta.url).pathname);\n\n/**\n * Helper function to resolve paths relative to current file\n * @param segments - Path segments to resolve relative to current directory\n * @returns Resolved absolute path\n */\nexport function resolveFromCurrentDir(...segments: string[]): string {\n return path.resolve(__dirname, ...segments);\n}\n\n/**\n * Creates a social media card image for a gallery\n * @param headerPhotoPath - Path to the header photo\n * @param title - Title of the gallery\n * @param ouputPath - Output path for the social media card image\n * @param ui - ConsolaInstance for logging\n */\nexport async function createGallerySocialMediaCardImage(\n headerPhotoPath: string,\n title: string,\n ouputPath: string,\n ui: ConsolaInstance,\n): Promise<void> {\n ui.start(`Creating social media card image`);\n\n if (fs.existsSync(ouputPath)) {\n ui.success(`Social media card image already exists`);\n return;\n }\n\n // Read and resize the header image to 1200x631 using fit\n const image = await loadImage(headerPhotoPath);\n const resizedImageBuffer = await image.resize(1200, 631, { fit: 'cover' }).jpeg({ quality: 90 }).toBuffer();\n\n // Save the resized image as social media card\n const outputPath = ouputPath;\n await sharp(resizedImageBuffer).toFile(outputPath);\n\n // Create SVG with title and description\n const svgText = `\n <svg width=\"1200\" height=\"631\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <style>\n .title { font-family: Arial, sans-serif; font-size: 96px; font-weight: bold; fill: white; text-anchor: middle; }\n .description { font-family: Arial, sans-serif; font-size: 48px; fill: white; text-anchor: middle; }\n </style>\n </defs>\n <text x=\"600\" y=\"250\" class=\"title\">${title}</text>\n </svg>\n `;\n\n // Composite the text overlay on top of the resized image\n const finalImageBuffer = await sharp(resizedImageBuffer)\n .composite([{ input: Buffer.from(svgText), top: 0, left: 0 }])\n .jpeg({ quality: 90 })\n .toBuffer();\n\n // Save the final image with text overlay\n await sharp(finalImageBuffer).toFile(outputPath);\n\n ui.success(`Created social media card image successfully`);\n}\n\nexport async function createOptimizedHeaderImage(\n headerPhotoPath: string,\n outputFolder: string,\n ui: ConsolaInstance,\n): Promise<void> {\n ui.start(`Creating optimized header images`);\n\n const image = await loadImage(headerPhotoPath);\n\n // Create landscape header images\n const landscapeYFactor = 3 / 4;\n for (const width of HEADER_IMAGE_LANDSCAPE_WIDTHS) {\n ui.debug(`Creating landscape header image ${width}`);\n\n if (fs.existsSync(path.join(outputFolder, `header_landscape_${width}.avif`))) {\n ui.debug(`Landscape header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_landscape_${width}.avif`),\n width,\n width * landscapeYFactor,\n 'avif',\n );\n }\n\n if (fs.existsSync(path.join(outputFolder, `header_landscape_${width}.jpg`))) {\n ui.debug(`Landscape header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_landscape_${width}.jpg`),\n width,\n width * landscapeYFactor,\n 'jpg',\n );\n }\n }\n\n // Create portrait header images\n const portraitYFactor = 4 / 3;\n for (const width of HEADER_IMAGE_PORTRAIT_WIDTHS) {\n ui.debug(`Creating portrait header image ${width}`);\n\n if (fs.existsSync(path.join(outputFolder, `header_portrait_${width}.avif`))) {\n ui.debug(`Portrait header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_portrait_${width}.avif`),\n width,\n width * portraitYFactor,\n 'avif',\n );\n }\n\n if (fs.existsSync(path.join(outputFolder, `header_portrait_${width}.jpg`))) {\n ui.debug(`Portrait header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_portrait_${width}.jpg`),\n width,\n width * portraitYFactor,\n 'jpg',\n );\n }\n }\n\n ui.success(`Created optimized header image successfully`);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Finds all gallery directories that contain a gallery/gallery.json file.\n *\n * @param basePath - The base directory to search from\n * @param recursive - Whether to search subdirectories recursively\n * @returns Array of paths to directories containing gallery/gallery.json files\n */\nexport function findGalleries(basePath: string, recursive: boolean): string[] {\n const galleryDirs: string[] = [];\n\n // Check basePath itself\n const galleryJsonPath = path.join(basePath, 'gallery', 'gallery.json');\n if (fs.existsSync(galleryJsonPath)) {\n galleryDirs.push(basePath);\n }\n\n // If recursive, search all subdirectories\n if (recursive) {\n try {\n const entries = fs.readdirSync(basePath, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name !== 'gallery') {\n const subPath = path.join(basePath, entry.name);\n const subResults = findGalleries(subPath, recursive);\n galleryDirs.push(...subResults);\n }\n }\n } catch {\n // Silently ignore errors when reading directories\n }\n }\n\n return galleryDirs;\n}\n\n/**\n * Handles file processing errors with appropriate user-friendly messages\n * @param error - The error that occurred during file processing\n * @param filename - Name of the file that caused the error\n * @param ui - ConsolaInstance for logging messages\n */\nexport function handleFileProcessingError(error: unknown, filename: string, ui: ConsolaInstance): void {\n if (error instanceof Error && (error.message.includes('ffprobe') || error.message.includes('ffmpeg'))) {\n // Handle ffmpeg error\n ui.warn(\n `Error processing ${filename}: ffprobe (part of ffmpeg) is required to process videos. Please install ffmpeg and ensure it is available in your PATH`,\n );\n } else if (error instanceof Error && error.message.includes('unsupported image format')) {\n // Handle unsupported image format error\n ui.warn(`Error processing ${filename}: unsupported image format`);\n } else {\n // Handle unknown error\n ui.warn(`Error processing ${filename}`);\n }\n\n ui.debug(error);\n}\n","import { promises as fs } from 'node:fs';\n\n/**\n * Gets the last modification time of a file\n * @param filePath - Path to the file\n * @returns Promise resolving to the file's modification date\n */\nexport async function getFileMtime(filePath: string): Promise<Date> {\n const stats = await fs.stat(filePath);\n return stats.mtime;\n}\n","import { encode } from 'blurhash';\n\nimport { loadImage } from './image';\n\n/**\n * Generates a BlurHash from an image file or Sharp instance\n * @param imagePath - Path to image file or Sharp instance\n * @param componentX - Number of x components (default: 4)\n * @param componentY - Number of y components (default: 3)\n * @returns Promise resolving to BlurHash string\n */\nexport async function generateBlurHash(imagePath: string, componentX: number = 4, componentY: number = 3): Promise<string> {\n const image = await loadImage(imagePath);\n\n // Resize to small size for BlurHash computation to improve performance\n // BlurHash doesn't need high resolution\n const { data, info } = await image\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n // Convert to Uint8ClampedArray format expected by blurhash\n const pixels = new Uint8ClampedArray(data.buffer);\n\n // Generate BlurHash\n return encode(pixels, info.width, info.height, componentX, componentY);\n}\n","import { spawn } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\n\nimport ffprobe from 'node-ffprobe';\nimport sharp from 'sharp';\n\nimport { resizeImage } from './image';\n\nimport type { Dimensions } from '../types';\nimport type { Buffer } from 'node:buffer';\n\n/**\n * Gets video dimensions using ffprobe\n * @param filePath - Path to the video file\n * @returns Promise resolving to video dimensions\n * @throws Error if no video stream found or invalid dimensions\n */\nexport async function getVideoDimensions(filePath: string): Promise<Dimensions> {\n const data = await ffprobe(filePath);\n const videoStream = data.streams.find((stream) => stream.codec_type === 'video');\n\n if (!videoStream) {\n throw new Error('No video stream found');\n }\n\n const dimensions = {\n width: videoStream.width || 0,\n height: videoStream.height || 0,\n };\n\n if (dimensions.width === 0 || dimensions.height === 0) {\n throw new Error('Invalid video dimensions');\n }\n\n return dimensions;\n}\n\n/**\n * Creates regular and retina thumbnails for a video by extracting the first frame\n * @param inputPath - Path to the video file\n * @param videoDimensions - Original video dimensions\n * @param outputPath - Path where thumbnail should be saved\n * @param outputPathRetina - Path where retina thumbnail should be saved\n * @param height - Target height for thumbnail\n * @param verbose - Whether to enable verbose ffmpeg output\n * @returns Promise resolving to thumbnail dimensions\n */\nexport async function createVideoThumbnails(\n inputPath: string,\n videoDimensions: Dimensions,\n outputPath: string,\n outputPathRetina: string,\n height: number,\n verbose: boolean = false,\n): Promise<Dimensions> {\n // Calculate width maintaining aspect ratio\n const aspectRatio = videoDimensions.width / videoDimensions.height;\n const width = Math.round(height * aspectRatio);\n\n // Use ffmpeg to extract first frame as a temporary file, then process with sharp\n const tempFramePath = `${outputPath}.temp.png`;\n\n return new Promise((resolve, reject) => {\n // Extract first frame using ffmpeg\n const ffmpeg = spawn('ffmpeg', [\n '-i',\n inputPath,\n '-vframes',\n '1',\n '-y',\n '-loglevel',\n verbose ? 'error' : 'quiet',\n tempFramePath,\n ]);\n\n ffmpeg.stderr.on('data', (data: Buffer) => {\n // FFmpeg writes normal output to stderr, so we don't treat this as an error\n console.log(`ffmpeg: ${data.toString()}`);\n });\n\n ffmpeg.on('close', async (code: number) => {\n if (code === 0) {\n try {\n // Process the extracted frame with sharp\n const frameImage = sharp(tempFramePath);\n await resizeImage(frameImage, outputPath, width, height);\n await resizeImage(frameImage, outputPathRetina, width * 2, height * 2);\n\n // Clean up temporary file\n try {\n await fs.unlink(tempFramePath);\n } catch {\n // Ignore cleanup errors\n }\n\n resolve({ width, height });\n } catch (sharpError) {\n reject(new Error(`Failed to process extracted frame: ${sharpError}`));\n }\n } else {\n reject(new Error(`ffmpeg exited with code ${code}`));\n }\n });\n\n ffmpeg.on('error', (error: Error) => {\n reject(new Error(`Failed to start ffmpeg: ${error.message}`));\n });\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GalleryDataSchema, type MediaFile } from '@simple-photo-gallery/common/src/gallery';\nimport { LogLevels, type ConsolaInstance } from 'consola';\n\nimport { getFileMtime } from './utils';\n\nimport { DEFAULT_THUMBNAIL_SIZE } from '../../config';\nimport { findGalleries, handleFileProcessingError } from '../../utils';\nimport { generateBlurHash } from '../../utils/blurhash';\nimport { getImageDescription, createImageThumbnails, loadImageWithMetadata } from '../../utils/image';\nimport { getVideoDimensions, createVideoThumbnails } from '../../utils/video';\n\nimport type { ThumbnailOptions } from './types';\n\n/**\n * Processes an image file to create thumbnail and extract metadata\n * @param imagePath - Path to the image file\n * @param thumbnailPath - Path where thumbnail should be saved\n * @param thumbnailPathRetina - Path where retina thumbnail should be saved\n * @param thumbnailSize - Target size for thumbnail\n * @param lastMediaTimestamp - Optional timestamp to check if processing can be skipped\n * @returns Promise resolving to updated MediaFile or undefined if skipped\n */\nexport async function processImage(\n imagePath: string,\n thumbnailPath: string,\n thumbnailPathRetina: string,\n thumbnailSize: number,\n lastMediaTimestamp?: Date,\n): Promise<MediaFile | undefined> {\n // Get the last media timestamp\n const fileMtime = await getFileMtime(imagePath);\n\n // Check if processing of the file can be skipped\n if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs.existsSync(thumbnailPath)) {\n return undefined;\n }\n\n // Load the image and get metadata first to check orientation\n const { image, metadata } = await loadImageWithMetadata(imagePath);\n\n // Get the image dimensions\n const imageDimensions = {\n width: metadata.width || 0,\n height: metadata.height || 0,\n };\n\n if (imageDimensions.width === 0 || imageDimensions.height === 0) {\n throw new Error('Invalid image dimensions');\n }\n\n // Get the image description\n const description = await getImageDescription(imagePath);\n\n // Create the thumbnails\n const thumbnailDimensions = await createImageThumbnails(\n image,\n metadata,\n thumbnailPath,\n thumbnailPathRetina,\n thumbnailSize,\n );\n\n // Generate BlurHash from the thumbnail\n const blurHash = await generateBlurHash(thumbnailPath);\n\n // Return the updated media file\n return {\n type: 'image',\n path: imagePath,\n alt: description,\n width: imageDimensions.width,\n height: imageDimensions.height,\n thumbnail: {\n path: thumbnailPath,\n pathRetina: thumbnailPathRetina,\n width: thumbnailDimensions.width,\n height: thumbnailDimensions.height,\n blurHash,\n },\n lastMediaTimestamp: fileMtime.toISOString(),\n };\n}\n\n/**\n * Processes a video file to create thumbnail and extract metadata\n * @param videoPath - Path to the video file\n * @param thumbnailPath - Path where thumbnail should be saved\n * @param thumbnailPathRetina - Path where retina thumbnail should be saved\n * @param thumbnailSize - Target size for thumbnail\n * @param verbose - Whether to enable verbose output\n * @param lastMediaTimestamp - Optional timestamp to check if processing can be skipped\n * @returns Promise resolving to updated MediaFile or undefined if skipped\n */\nasync function processVideo(\n videoPath: string,\n thumbnailPath: string,\n thumbnailPathRetina: string,\n thumbnailSize: number,\n verbose: boolean,\n lastMediaTimestamp?: Date,\n): Promise<MediaFile | undefined> {\n // Get the last media timestamp\n const fileMtime = await getFileMtime(videoPath);\n\n // Check if processing of the file can be skipped\n if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs.existsSync(thumbnailPath)) {\n return undefined;\n }\n\n // Get the video dimensions\n const videoDimensions = await getVideoDimensions(videoPath);\n\n // Create the thumbnail\n const thumbnailDimensions = await createVideoThumbnails(\n videoPath,\n videoDimensions,\n thumbnailPath,\n thumbnailPathRetina,\n thumbnailSize,\n verbose,\n );\n\n // Generate BlurHash from the thumbnail\n const blurHash = await generateBlurHash(thumbnailPath);\n\n return {\n type: 'video',\n path: videoPath,\n alt: undefined,\n width: videoDimensions.width,\n height: videoDimensions.height,\n thumbnail: {\n path: thumbnailPath,\n pathRetina: thumbnailPathRetina,\n width: thumbnailDimensions.width,\n height: thumbnailDimensions.height,\n blurHash,\n },\n lastMediaTimestamp: fileMtime.toISOString(),\n };\n}\n\n/**\n * Processes a media file to generate thumbnails and update metadata\n * @param mediaFile - Media file to process\n * @param galleryDir - Gallery directory path\n * @param thumbnailsPath - Directory where thumbnails are stored\n * @param thumbnailSize - Target size for thumbnails\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to updated MediaFile\n */\nasync function processMediaFile(\n mediaFile: MediaFile,\n galleryDir: string,\n thumbnailsPath: string,\n thumbnailSize: number,\n ui: ConsolaInstance,\n): Promise<MediaFile> {\n try {\n // Resolve the path relative to the gallery.json file location, not the gallery directory\n const galleryJsonDir = path.join(galleryDir, 'gallery');\n const filePath = path.resolve(path.join(galleryJsonDir, mediaFile.path));\n\n const fileName = path.basename(filePath);\n const fileNameWithoutExt = path.parse(fileName).name;\n const thumbnailFileName = `${fileNameWithoutExt}.avif`;\n const thumbnailPath = path.join(thumbnailsPath, thumbnailFileName);\n const thumbnailPathRetina = thumbnailPath.replace('.avif', '@2x.avif');\n const relativeThumbnailPath = path.relative(galleryJsonDir, thumbnailPath);\n const relativeThumbnailRetinaPath = path.relative(galleryJsonDir, thumbnailPathRetina);\n\n const lastMediaTimestamp = mediaFile.lastMediaTimestamp ? new Date(mediaFile.lastMediaTimestamp) : undefined;\n const verbose = ui.level === LogLevels.debug;\n\n ui.debug(` Processing ${mediaFile.type}: ${fileName}`);\n\n const updatedMediaFile = await (mediaFile.type === 'image'\n ? processImage(filePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, lastMediaTimestamp)\n : processVideo(filePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, verbose, lastMediaTimestamp));\n\n if (!updatedMediaFile) {\n ui.debug(` Skipping ${fileName} because it has already been processed`);\n\n // Check if we need to generate BlurHash for existing thumbnail\n if (mediaFile.thumbnail && !mediaFile.thumbnail.blurHash && fs.existsSync(thumbnailPath)) {\n try {\n const blurHash = await generateBlurHash(thumbnailPath);\n return {\n ...mediaFile,\n thumbnail: {\n ...mediaFile.thumbnail,\n blurHash,\n },\n };\n } catch (error) {\n ui.debug(` Failed to generate BlurHash for ${fileName}:`, error);\n }\n }\n\n return mediaFile;\n }\n\n updatedMediaFile.path = mediaFile.path;\n if (updatedMediaFile.thumbnail) {\n updatedMediaFile.thumbnail.path = relativeThumbnailPath;\n updatedMediaFile.thumbnail.pathRetina = relativeThumbnailRetinaPath;\n }\n\n return updatedMediaFile;\n } catch (error) {\n handleFileProcessingError(error, path.basename(mediaFile.path), ui);\n\n return mediaFile;\n }\n}\n\n/**\n * Processes all media files in a gallery to generate thumbnails\n * @param galleryDir - Directory containing the gallery\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to the number of files processed\n */\nexport async function processGalleryThumbnails(galleryDir: string, ui: ConsolaInstance): Promise<number> {\n const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');\n const thumbnailsPath = path.join(galleryDir, 'gallery', 'images');\n\n ui.start(`Creating thumbnails: ${galleryDir}`);\n\n try {\n // Ensure thumbnails directory exists\n fs.mkdirSync(thumbnailsPath, { recursive: true });\n\n // Read gallery.json\n const galleryContent = fs.readFileSync(galleryJsonPath, 'utf8');\n const galleryData = GalleryDataSchema.parse(JSON.parse(galleryContent));\n\n const thumbnailSize = galleryData.thumbnailSize || DEFAULT_THUMBNAIL_SIZE;\n\n // Process all sections and their images\n let processedCount = 0;\n for (const section of galleryData.sections) {\n for (const [index, mediaFile] of section.images.entries()) {\n section.images[index] = await processMediaFile(mediaFile, galleryDir, thumbnailsPath, thumbnailSize, ui);\n }\n\n processedCount += section.images.length;\n }\n\n // Write updated gallery.json\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n ui.success(`Created thumbnails for ${processedCount} media files`);\n\n return processedCount;\n } catch (error) {\n ui.error(`Error creating thumbnails for ${galleryDir}`);\n throw error;\n }\n}\n\n/**\n * Main thumbnails command implementation - generates thumbnails for all galleries\n * @param options - Options specifying gallery path and recursion settings\n * @param ui - ConsolaInstance for logging\n */\nexport async function thumbnails(options: ThumbnailOptions, ui: ConsolaInstance): Promise<void> {\n try {\n // Find all gallery directories\n const galleryDirs = findGalleries(options.gallery, options.recursive);\n if (galleryDirs.length === 0) {\n ui.error('No galleries found.');\n return;\n }\n\n // Process each gallery directory\n let totalGalleries = 0;\n let totalProcessed = 0;\n for (const galleryDir of galleryDirs) {\n const processed = await processGalleryThumbnails(galleryDir, ui);\n\n if (processed > 0) {\n ++totalGalleries;\n totalProcessed += processed;\n }\n }\n\n ui.box(\n `Created thumbnails for ${totalGalleries} ${totalGalleries === 1 ? 'gallery' : 'galleries'} with ${totalProcessed} media ${totalProcessed === 1 ? 'file' : 'files'}`,\n );\n } catch (error) {\n ui.error('Error creating thumbnails');\n throw error;\n }\n}\n","import { execSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\n\nimport { type GalleryData, GalleryDataSchema } from '@simple-photo-gallery/common/src/gallery';\nimport { LogLevels, type ConsolaInstance } from 'consola';\n\nimport { createGallerySocialMediaCardImage, createOptimizedHeaderImage } from './utils';\n\nimport { findGalleries } from '../../utils';\nimport { processGalleryThumbnails } from '../thumbnails';\n\nimport type { BuildOptions } from './types';\n\n/**\n * Checks if a file path refers to a file one folder up from the current directory\n * @param filePath - The file path to check\n * @returns True if the file is exactly one folder up (../filename)\n */\nfunction checkFileIsOneFolderUp(filePath: string): boolean {\n const normalizedPath = path.normalize(filePath);\n const pathParts = normalizedPath.split(path.sep);\n return pathParts.length === 2 && pathParts[0] === '..';\n}\n\n/**\n * Copies photos from gallery subdirectory to main directory when needed\n * @param galleryData - Gallery data containing image paths\n * @param galleryDir - Base gallery directory\n * @param ui - ConsolaInstance for logging\n */\nfunction copyPhotos(galleryData: GalleryData, galleryDir: string, ui: ConsolaInstance): void {\n for (const section of galleryData.sections) {\n for (const image of section.images) {\n if (!checkFileIsOneFolderUp(image.path)) {\n const sourcePath = path.join(galleryDir, 'gallery', image.path);\n const fileName = path.basename(image.path);\n const destPath = path.join(galleryDir, fileName);\n\n ui.debug(`Copying photo to ${destPath}`);\n fs.copyFileSync(sourcePath, destPath);\n }\n }\n }\n}\n\n/**\n * Builds a single gallery by generating thumbnails and creating HTML output\n * @param galleryDir - Directory containing the gallery\n * @param templateDir - Directory containing the Astro template\n * @param ui - ConsolaInstance for logging\n * @param baseUrl - Optional base URL for hosting photos\n */\nasync function buildGallery(galleryDir: string, templateDir: string, ui: ConsolaInstance, baseUrl?: string): Promise<void> {\n ui.start(`Building gallery ${galleryDir}`);\n\n // Generate the thumbnails if needed\n await processGalleryThumbnails(galleryDir, ui);\n\n // Read the gallery.json file\n const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');\n const galleryContent = fs.readFileSync(galleryJsonPath, 'utf8');\n const galleryData = GalleryDataSchema.parse(JSON.parse(galleryContent));\n const socialMediaCardImagePath = path.join(galleryDir, 'gallery', 'images', 'social-media-card.jpg');\n const headerImagePath = path.resolve(path.join(galleryDir, 'gallery'), galleryData.headerImage);\n\n // Create the gallery social media card image\n await createGallerySocialMediaCardImage(headerImagePath, galleryData.title, socialMediaCardImagePath, ui);\n galleryData.metadata.image =\n galleryData.metadata.image || `${galleryData.url || ''}/${path.relative(galleryDir, socialMediaCardImagePath)}`;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n // Create optimized header image\n await createOptimizedHeaderImage(headerImagePath, path.join(galleryDir, 'gallery', 'images'), ui);\n\n // Check if the photos need to be copied. Not needed if the baseUrl is provided.\n if (!baseUrl) {\n const shouldCopyPhotos = galleryData.sections.some((section) =>\n section.images.some((image) => !checkFileIsOneFolderUp(image.path)),\n );\n\n if (\n shouldCopyPhotos &&\n (await ui.prompt('All photos need to be copied. Are you sure you want to continue?', { type: 'confirm' }))\n ) {\n ui.debug('Copying photos');\n copyPhotos(galleryData, galleryDir, ui);\n }\n }\n\n // If the baseUrl is provided, update the gallery.json file\n if (baseUrl) {\n ui.debug('Updating gallery.json with baseUrl');\n galleryData.mediaBaseUrl = baseUrl;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n\n // Build the template\n ui.debug('Building gallery form template');\n try {\n // Set the environment variable for the gallery.json path that will be used by the template\n process.env.GALLERY_JSON_PATH = galleryJsonPath;\n process.env.GALLERY_OUTPUT_DIR = path.join(galleryDir, 'gallery');\n\n execSync('npx astro build', { cwd: templateDir, stdio: ui.level === LogLevels.debug ? 'inherit' : 'ignore' });\n } catch (error) {\n ui.error(`Build failed for ${galleryDir}`);\n throw error;\n }\n\n // Copy the build output to the output directory\n const outputDir = path.join(galleryDir, 'gallery');\n const buildDir = path.join(outputDir, '_build');\n ui.debug(`Copying build output to ${outputDir}`);\n fs.cpSync(buildDir, outputDir, { recursive: true });\n\n // Move the index.html to the gallery directory\n ui.debug('Moving index.html to gallery directory');\n fs.copyFileSync(path.join(outputDir, 'index.html'), path.join(galleryDir, 'index.html'));\n fs.rmSync(path.join(outputDir, 'index.html'));\n\n // Clean up the _build directory\n ui.debug('Cleaning up build directory');\n fs.rmSync(buildDir, { recursive: true, force: true });\n\n ui.success(`Gallery built successfully`);\n}\n\n/**\n * Main build command implementation - builds HTML galleries from gallery.json files\n * @param options - Options specifying gallery path, recursion, and base URL\n * @param ui - ConsolaInstance for logging\n */\nexport async function build(options: BuildOptions, ui: ConsolaInstance): Promise<void> {\n try {\n // Find all gallery directories\n const galleryDirs = findGalleries(options.gallery, options.recursive);\n if (galleryDirs.length === 0) {\n ui.error('No galleries found.');\n return;\n }\n\n // Get the astro theme directory from the default one\n const themePath = await import.meta.resolve('@simple-photo-gallery/theme-modern/package.json');\n const themeDir = path.dirname(new URL(themePath).pathname);\n\n // Process each gallery directory\n let totalGalleries = 0;\n for (const dir of galleryDirs) {\n const baseUrl = options.baseUrl ? `${options.baseUrl}${path.relative(options.gallery, dir)}` : undefined;\n await buildGallery(path.resolve(dir), themeDir, ui, baseUrl);\n\n ++totalGalleries;\n }\n\n ui.box(`Built ${totalGalleries} ${totalGalleries === 1 ? 'gallery' : 'galleries'} successfully`);\n } catch (error) {\n if (error instanceof Error && error.message.includes('Cannot find package')) {\n ui.error('Theme package not found: @simple-photo-gallery/theme-modern/package.json');\n } else {\n ui.error('Error building gallery');\n }\n\n throw error;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { findGalleries } from '../../utils';\n\nimport type { CleanOptions } from './types';\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Clean gallery files from a single directory\n * @param galleryDir - Directory containing a gallery\n * @param ui - Consola instance for logging\n */\nasync function cleanGallery(galleryDir: string, ui: ConsolaInstance): Promise<void> {\n let filesRemoved = 0;\n\n // Remove index.html file from the gallery directory\n const indexHtmlPath = path.join(galleryDir, 'index.html');\n if (fs.existsSync(indexHtmlPath)) {\n try {\n fs.rmSync(indexHtmlPath);\n ui.debug(`Removed: ${indexHtmlPath}`);\n filesRemoved++;\n } catch (error) {\n ui.warn(`Failed to remove index.html: ${error}`);\n }\n }\n\n // Remove gallery directory and all its contents\n const galleryPath = path.join(galleryDir, 'gallery');\n if (fs.existsSync(galleryPath)) {\n try {\n fs.rmSync(galleryPath, { recursive: true, force: true });\n ui.debug(`Removed directory: ${galleryPath}`);\n filesRemoved++;\n } catch (error) {\n ui.warn(`Failed to remove gallery directory: ${error}`);\n }\n }\n\n if (filesRemoved > 0) {\n ui.success(`Cleaned gallery at: ${galleryDir}`);\n } else {\n ui.info(`No gallery files found at: ${galleryDir}`);\n }\n}\n\n/**\n * Clean command implementation\n * Removes all gallery-related files and directories\n */\nexport async function clean(options: CleanOptions, ui: ConsolaInstance): Promise<void> {\n try {\n const basePath = path.resolve(options.gallery);\n\n // Check if the base path exists\n if (!fs.existsSync(basePath)) {\n ui.error(`Directory does not exist: ${basePath}`);\n return;\n }\n\n // Find all gallery directories\n const galleryDirs = findGalleries(basePath, options.recursive);\n\n if (galleryDirs.length === 0) {\n ui.info('No galleries found to clean.');\n return;\n }\n\n // Clean each gallery directory\n for (const dir of galleryDirs) {\n await cleanGallery(dir, ui);\n }\n\n ui.box(`Successfully cleaned ${galleryDirs.length} ${galleryDirs.length === 1 ? 'gallery' : 'galleries'}`);\n } catch (error) {\n ui.error('Error cleaning galleries');\n throw error;\n }\n}\n","import path from 'node:path';\n\nimport { IMAGE_EXTENSIONS, VIDEO_EXTENSIONS } from '../../../config';\n\nimport type { MediaFileType } from '../types';\n\n/**\n * Determines the media file type based on file extension\n * @param fileName - Name of the file to check\n * @returns Media file type ('image' or 'video') or null if not supported\n */\nexport function getMediaFileType(fileName: string): MediaFileType | null {\n const ext = path.extname(fileName).toLowerCase();\n if (IMAGE_EXTENSIONS.has(ext)) return 'image';\n if (VIDEO_EXTENSIONS.has(ext)) return 'video';\n return null;\n}\n\n/**\n * Converts a folder name into a properly capitalized title\n * @param folderName - The folder name to convert\n * @returns Formatted title with proper capitalization\n */\nexport function capitalizeTitle(folderName: string): string {\n return folderName\n .replace('-', ' ')\n .replace('_', ' ')\n .split(' ')\n .map((word: string) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport { capitalizeTitle, getMediaFileType } from './utils';\n\nimport type { GallerySettingsFromUser, ProcessDirectoryResult, ScanDirectoryResult, ScanOptions, SubGallery } from './types';\nimport type { MediaFile } from '@simple-photo-gallery/common/src/gallery';\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Scans a directory for media files and subdirectories\n * @param dirPath - Path to the directory to scan\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to scan results with media files and subdirectories\n */\nasync function scanDirectory(dirPath: string, ui: ConsolaInstance): Promise<ScanDirectoryResult> {\n const mediaFiles: MediaFile[] = [];\n const subGalleryDirectories: string[] = [];\n\n try {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isFile()) {\n const fullPath = path.join(dirPath, entry.name);\n const mediaType = getMediaFileType(entry.name);\n\n if (mediaType) {\n const mediaFile: MediaFile = {\n type: mediaType,\n path: fullPath,\n width: 0,\n height: 0,\n };\n\n mediaFiles.push(mediaFile);\n }\n } else if (entry.isDirectory() && entry.name !== 'gallery') {\n subGalleryDirectories.push(path.join(dirPath, entry.name));\n }\n }\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n ui.error(`Directory does not exist: ${dirPath}`);\n } else if (error instanceof Error && error.message.includes('ENOTDIR')) {\n ui.error(`Path is not a directory: ${dirPath}`);\n } else {\n ui.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n throw error;\n }\n\n return { mediaFiles, subGalleryDirectories };\n}\n\n/**\n * Prompts the user for gallery settings through interactive CLI\n * @param galleryName - Name of the gallery directory\n * @param defaultImage - Default header image path\n * @param ui - ConsolaInstance for prompting and logging\n * @returns Promise resolving to user-provided gallery settings\n */\nasync function getGallerySettingsFromUser(\n galleryName: string,\n defaultImage: string,\n ui: ConsolaInstance,\n): Promise<GallerySettingsFromUser> {\n ui.info(`Enter gallery settings for the gallery in folder \"${galleryName}\"`);\n\n const title = await ui.prompt('Enter gallery title', { type: 'text', default: 'My Gallery', placeholder: 'My Gallery' });\n const description = await ui.prompt('Enter gallery description', {\n type: 'text',\n default: 'My gallery with fantastic photos.',\n placeholder: 'My gallery with fantastic photos.',\n });\n const url = await ui.prompt('Enter the URL where the gallery will be hosted (important for social media image)', {\n type: 'text',\n default: '',\n placeholder: '',\n });\n const headerImageName = await ui.prompt('Enter the name of the header image', {\n type: 'text',\n default: defaultImage,\n placeholder: defaultImage,\n });\n\n const headerImage = path.join('..', headerImageName);\n\n return { title, description, url, headerImage };\n}\n\n/**\n * Creates a gallery.json file with media files and settings\n * @param mediaFiles - Array of media files to include in gallery\n * @param galleryJsonPath - Path where gallery.json should be created\n * @param subGalleries - Array of sub-galleries to include\n * @param useDefaultSettings - Whether to use default settings or prompt user\n * @param ui - ConsolaInstance for prompting and logging\n */\nasync function createGalleryJson(\n mediaFiles: MediaFile[],\n galleryJsonPath: string,\n subGalleries: SubGallery[] = [],\n useDefaultSettings: boolean,\n ui: ConsolaInstance,\n): Promise<void> {\n const galleryDir = path.dirname(galleryJsonPath);\n\n // Convert media file paths to be relative to gallery.json\n const relativeMediaFiles = mediaFiles.map((file) => ({\n ...file,\n path: path.relative(galleryDir, file.path),\n }));\n\n // Convert subGallery header image paths to be relative to gallery.json\n const relativeSubGalleries = subGalleries.map((subGallery) => ({\n ...subGallery,\n headerImage: subGallery.headerImage ? path.relative(galleryDir, subGallery.headerImage) : '',\n }));\n\n let galleryData = {\n title: 'My Gallery',\n description: 'My gallery with fantastic photos.',\n headerImage: relativeMediaFiles[0]?.path || '',\n metadata: {},\n sections: [\n {\n images: relativeMediaFiles,\n },\n ],\n subGalleries: {\n title: 'Sub Galleries',\n galleries: relativeSubGalleries,\n },\n };\n\n if (!useDefaultSettings) {\n galleryData = {\n ...galleryData,\n ...(await getGallerySettingsFromUser(\n path.basename(path.join(galleryDir, '..')),\n path.basename(mediaFiles[0]?.path || ''),\n ui,\n )),\n };\n }\n\n await fs.writeFile(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n}\n\n/**\n * Processes a directory and its subdirectories to create galleries\n * @param scanPath - Path to scan for media files\n * @param outputPath - Path where gallery should be created\n * @param recursive - Whether to process subdirectories recursively\n * @param useDefaultSettings - Whether to use default settings or prompt user\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to processing results\n */\nasync function processDirectory(\n scanPath: string,\n outputPath: string,\n recursive: boolean,\n useDefaultSettings: boolean,\n ui: ConsolaInstance,\n): Promise<ProcessDirectoryResult> {\n ui.start(`Scanning ${scanPath}`);\n\n let totalFiles = 0;\n let totalGalleries = 1;\n const subGalleries: SubGallery[] = [];\n\n // Scan current directory for media files\n const { mediaFiles, subGalleryDirectories } = await scanDirectory(scanPath, ui);\n totalFiles += mediaFiles.length;\n\n // Process subdirectories only if recursive mode is enabled\n if (recursive) {\n for (const subGalleryDir of subGalleryDirectories) {\n const result = await processDirectory(\n subGalleryDir,\n path.join(outputPath, path.basename(subGalleryDir)),\n recursive,\n useDefaultSettings,\n ui,\n );\n\n totalFiles += result.totalFiles;\n totalGalleries += result.totalGalleries;\n\n // If the result contains a valid subGallery, add it to the list\n if (result.subGallery) {\n subGalleries.push(result.subGallery);\n }\n }\n }\n\n // Create gallery.json if there are media files or subGalleries\n if (mediaFiles.length > 0 || subGalleries.length > 0) {\n const galleryPath = path.join(outputPath, 'gallery');\n const galleryJsonPath = path.join(galleryPath, 'gallery.json');\n\n try {\n // Create output directory\n await fs.mkdir(galleryPath, { recursive: true });\n\n // Create gallery.json for this directory\n await createGalleryJson(mediaFiles, galleryJsonPath, subGalleries, useDefaultSettings, ui);\n\n ui.success(\n `Create gallery with ${mediaFiles.length} files and ${subGalleries.length} subgalleries at: ${galleryJsonPath}`,\n );\n } catch (error) {\n ui.error(`Error creating gallery.json at ${galleryJsonPath}`);\n throw error;\n }\n }\n\n // Return result with suGgallery info if this directory has media files\n const result: ProcessDirectoryResult = { totalFiles, totalGalleries };\n\n // If this directory has media files or subGalleries, create a subGallery in the result\n if (mediaFiles.length > 0 || subGalleries.length > 0) {\n const dirName = path.basename(scanPath);\n result.subGallery = {\n title: capitalizeTitle(dirName),\n headerImage: mediaFiles[0]?.path || '',\n path: path.join('..', dirName),\n };\n }\n\n return result;\n}\n\n/**\n * Main init command implementation - scans directories and creates gallery.json files\n * @param options - Options specifying paths, recursion, and default settings\n * @param ui - ConsolaInstance for logging and user prompts\n */\nexport async function init(options: ScanOptions, ui: ConsolaInstance): Promise<void> {\n try {\n const scanPath = path.resolve(options.photos);\n const outputPath = options.gallery ? path.resolve(options.gallery) : scanPath;\n\n // Process the directory tree with the specified recursion setting\n const result = await processDirectory(scanPath, outputPath, options.recursive, options.default, ui);\n\n ui.box(\n `Created ${result.totalGalleries} ${result.totalGalleries === 1 ? 'gallery' : 'galleries'} with ${result.totalFiles} media ${result.totalFiles === 1 ? 'file' : 'files'}`,\n );\n } catch (error) {\n ui.error('Error initializing gallery');\n throw error;\n }\n}\n","{\n \"name\": \"simple-photo-gallery\",\n \"version\": \"2.0.7\",\n \"description\": \"Simple Photo Gallery CLI\",\n \"license\": \"MIT\",\n \"author\": \"Vladimir Haltakov, Tomasz Rusin\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/SimplePhotoGallery/core\"\n },\n \"homepage\": \"https://simple.photo\",\n \"files\": [\n \"dist\"\n ],\n \"bin\": {\n \"simple-photo-gallery\": \"./dist/index.js\",\n \"spg\": \"./dist/index.js\"\n },\n \"scripts\": {\n \"gallery\": \"tsx src/index.ts\",\n \"clean\": \"rm -rf dist\",\n \"compile\": \"tsc --noEmit\",\n \"build\": \"yarn clean && tsup\",\n \"check\": \"tsc --noEmit && eslint . && prettier --check .\",\n \"lint\": \"eslint .\",\n \"lint:fix\": \"eslint . --fix\",\n \"format\": \"prettier --write .\",\n \"format:fix\": \"prettier --write .\",\n \"test\": \"jest\",\n \"prepublish\": \"npm run build\"\n },\n \"dependencies\": {\n \"@simple-photo-gallery/theme-modern\": \"2.0.2\",\n \"blurhash\": \"^2.0.5\",\n \"commander\": \"^12.0.0\",\n \"consola\": \"^3.4.2\",\n \"exifreader\": \"^4.32.0\",\n \"node-ffprobe\": \"^3.0.0\",\n \"sharp\": \"^0.33.0\",\n \"zod\": \"^4.0.14\"\n },\n \"devDependencies\": {\n \"@eslint/eslintrc\": \"^3.3.1\",\n \"@eslint/js\": \"^9.30.1\",\n \"@simple-photo-gallery/common\": \"workspace:*\",\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/jest\": \"^30.0.0\",\n \"@types/node\": \"^24.0.10\",\n \"@typescript-eslint/eslint-plugin\": \"^8.38.0\",\n \"@typescript-eslint/parser\": \"^8.38.0\",\n \"eslint\": \"^9.30.1\",\n \"eslint-config-prettier\": \"^10.1.5\",\n \"eslint-import-resolver-alias\": \"^1.1.2\",\n \"eslint-import-resolver-typescript\": \"^4.4.4\",\n \"eslint-plugin-import\": \"^2.31.0\",\n \"eslint-plugin-prettier\": \"^5.5.1\",\n \"eslint-plugin-unicorn\": \"^59.0.1\",\n \"fs-extra\": \"^11.3.0\",\n \"jest\": \"^30.0.5\",\n \"prettier\": \"^3.4.2\",\n \"ts-jest\": \"^29.4.1\",\n \"tsup\": \"^8.5.0\",\n \"tsx\": \"^4.20.3\",\n \"typescript\": \"^5.9.0\",\n \"typescript-eslint\": \"^8.38.0\"\n },\n \"type\": \"module\"\n}\n","#!/usr/bin/env node\n\nimport process from 'node:process';\n\nimport { Command } from 'commander';\nimport { createConsola, LogLevels, type ConsolaInstance } from 'consola';\n\nimport { build } from './modules/build';\nimport { clean } from './modules/clean';\nimport { init } from './modules/init';\nimport { thumbnails } from './modules/thumbnails';\n\nimport packageJson from '../package.json' with { type: 'json' };\n\n/** Command line interface program instance */\nconst program = new Command();\n\nprogram\n .name('gallery')\n .description('Simple Photo Gallery CLI')\n .version(packageJson.version)\n .option('-v, --verbose', 'Verbose output (debug level)', false)\n .option('-q, --quiet', 'Minimal output (only warnings/errors)', false)\n .showHelpAfterError(true);\n\n/**\n * Creates a Consola UI instance with appropriate log level based on global options\n * @param globalOpts - Global command options containing verbose/quiet flags\n * @returns ConsolaInstance configured with appropriate log level and tag\n */\nfunction createConsolaUI(globalOpts: ReturnType<typeof program.opts>): ConsolaInstance {\n let level = LogLevels.info;\n\n if (globalOpts.quiet) {\n level = LogLevels.warn;\n } else if (globalOpts.verbose) {\n level = LogLevels.debug;\n }\n\n return createConsola({\n level,\n }).withTag('simple-photo-gallery');\n}\n\n/**\n * Higher-order function that wraps command handlers to provide ConsolaUI instance\n * @param handler - Command handler function that receives options and UI instance\n * @returns Wrapped handler function that creates UI and handles errors\n */\nfunction withConsolaUI<O>(handler: (opts: O, ui: ConsolaInstance) => Promise<void> | void) {\n return async (opts: O) => {\n const ui = createConsolaUI(program.opts());\n try {\n await handler(opts, ui);\n } catch (error) {\n ui.debug(error);\n\n process.exitCode = 1;\n }\n };\n}\n\nprogram\n .command('init')\n .description('Initialize a gallery by scaning a folder for images and videos')\n .option(\n '-p, --photos <path>',\n 'Path to the folder where the photos are stored. Default: current working directory',\n process.cwd(),\n )\n .option(\n '-g, --gallery <path>',\n 'Path to the directory where the gallery will be initialized. Default: same directory as the photos folder',\n )\n .option('-r, --recursive', 'Recursively create galleries from all photos subdirectories', false)\n .option('-d, --default', 'Use default gallery settings instead of asking the user', false)\n .action(withConsolaUI(init));\n\nprogram\n .command('thumbnails')\n .description('Create thumbnails for all media files in the gallery')\n .option('-g, --gallery <path>', 'Path to the directory of the gallery. Default: current working directory', process.cwd())\n .option('-r, --recursive', 'Scan subdirectories recursively', false)\n .action(withConsolaUI(thumbnails));\n\nprogram\n .command('build')\n .description('Build the HTML gallery in the specified directory')\n .option('-g, --gallery <path>', 'Path to the directory of the gallery. Default: current working directory', process.cwd())\n .option('-r, --recursive', 'Scan subdirectories recursively', false)\n .option('-b, --base-url <url>', 'Base URL where the photos are hosted')\n .action(withConsolaUI(build));\n\nprogram\n .command('clean')\n .description('Remove all gallery files and folders (index.html, gallery/)')\n .option('-g, --gallery <path>', 'Path to the directory of the gallery. Default: current working directory', process.cwd())\n .option('-r, --recursive', 'Clean subdirectories recursively', false)\n .action(withConsolaUI(clean));\n\nprogram.parse();\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-photo-gallery",
3
- "version": "2.0.5",
3
+ "version": "2.0.7",
4
4
  "description": "Simple Photo Gallery CLI",
5
5
  "license": "MIT",
6
6
  "author": "Vladimir Haltakov, Tomasz Rusin",