simple-photo-gallery 2.0.16 → 2.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/config/index.ts","../src/utils/image.ts","../src/utils/blurhash.ts","../src/modules/build/utils/index.ts","../src/utils/index.ts","../src/utils/gallery.ts","../src/modules/init/utils/index.ts","../src/modules/init/index.ts","../src/modules/thumbnails/utils/index.ts","../src/utils/descriptions.ts","../src/utils/video.ts","../src/modules/thumbnails/index.ts","../src/modules/build/index.ts","../src/modules/clean/index.ts","../src/modules/telemetry/index.ts","../src/modules/telemetry/clients/api.ts","../src/modules/telemetry/clients/console.ts","../src/modules/telemetry/service/index.ts","../src/utils/version.ts","../package.json","../src/index.ts"],"names":["sharp","path","fs","result","process","LogLevels","telemetryService","axios"],"mappings":"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","file":"index.js","sourcesContent":["/** 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 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 * 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 { 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 { 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 { generateBlurHash } from '../../../utils/blurhash';\nimport { cropAndResizeImage, loadImage } from '../../../utils/image';\n\nimport type { ConsolaInstance } from 'consola';\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 * @returns The basename of the header photo used\n */\nexport async function createGallerySocialMediaCardImage(\n headerPhotoPath: string,\n title: string,\n ouputPath: string,\n ui?: ConsolaInstance,\n): Promise<string> {\n ui?.start(`Creating social media card image`);\n\n const headerBasename = path.basename(headerPhotoPath, path.extname(headerPhotoPath));\n\n if (fs.existsSync(ouputPath)) {\n ui?.success(`Social media card image already exists`);\n return headerBasename;\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; stroke: black; stroke-width: 5; paint-order: stroke; 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 return headerBasename;\n}\n\n/**\n * Creates optimized header images for different orientations and sizes\n * @param headerPhotoPath - Path to the header photo\n * @param outputFolder - Folder where header images should be saved\n * @param ui - ConsolaInstance for logging\n * @returns Object containing the header basename, array of generated file paths, and blurhash\n */\nexport async function createOptimizedHeaderImage(\n headerPhotoPath: string,\n outputFolder: string,\n ui?: ConsolaInstance,\n): Promise<{ headerBasename: string; generatedFiles: string[]; blurHash: string }> {\n ui?.start(`Creating optimized header images`);\n\n const image = await loadImage(headerPhotoPath);\n const headerBasename = path.basename(headerPhotoPath, path.extname(headerPhotoPath));\n const generatedFiles: string[] = [];\n\n // Generate blurhash for the header image\n ui?.debug('Generating blurhash for header image');\n const blurHash = await generateBlurHash(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 const avifFilename = `${headerBasename}_landscape_${width}.avif`;\n const jpgFilename = `${headerBasename}_landscape_${width}.jpg`;\n\n if (fs.existsSync(path.join(outputFolder, avifFilename))) {\n ui?.debug(`Landscape header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, avifFilename),\n width,\n width * landscapeYFactor,\n 'avif',\n );\n }\n generatedFiles.push(avifFilename);\n\n if (fs.existsSync(path.join(outputFolder, jpgFilename))) {\n ui?.debug(`Landscape header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(image.clone(), path.join(outputFolder, jpgFilename), width, width * landscapeYFactor, 'jpg');\n }\n generatedFiles.push(jpgFilename);\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 const avifFilename = `${headerBasename}_portrait_${width}.avif`;\n const jpgFilename = `${headerBasename}_portrait_${width}.jpg`;\n\n if (fs.existsSync(path.join(outputFolder, avifFilename))) {\n ui?.debug(`Portrait header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(image.clone(), path.join(outputFolder, avifFilename), width, width * portraitYFactor, 'avif');\n }\n generatedFiles.push(avifFilename);\n\n if (fs.existsSync(path.join(outputFolder, jpgFilename))) {\n ui?.debug(`Portrait header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(image.clone(), path.join(outputFolder, jpgFilename), width, width * portraitYFactor, 'jpg');\n }\n generatedFiles.push(jpgFilename);\n }\n\n ui?.success(`Created optimized header image successfully`);\n return { headerBasename, generatedFiles, blurHash };\n}\n\n/**\n * Checks if there are old header images with a different basename than the current one\n * @param outputFolder - Folder containing the header images\n * @param currentHeaderBasename - Basename of the current header image\n * @returns True if old header images with different basename exist, false otherwise\n */\nexport function hasOldHeaderImages(outputFolder: string, currentHeaderBasename: string): boolean {\n if (!fs.existsSync(outputFolder)) {\n return false;\n }\n\n const files = fs.readdirSync(outputFolder);\n\n for (const file of files) {\n // Check if file is a header image (landscape or portrait) with different basename\n const landscapeMatch = file.match(/^(.+)_landscape_\\d+\\.(avif|jpg)$/);\n const portraitMatch = file.match(/^(.+)_portrait_\\d+\\.(avif|jpg)$/);\n\n if (\n (landscapeMatch && landscapeMatch[1] !== currentHeaderBasename) ||\n (portraitMatch && portraitMatch[1] !== currentHeaderBasename)\n ) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Cleans up old header images that don't match the current header image\n * @param outputFolder - Folder containing the header images\n * @param currentHeaderBasename - Basename of the current header image\n * @param ui - ConsolaInstance for logging\n */\nexport function cleanupOldHeaderImages(outputFolder: string, currentHeaderBasename: string, ui?: ConsolaInstance): void {\n ui?.start(`Cleaning up old header images`);\n\n if (!fs.existsSync(outputFolder)) {\n ui?.debug(`Output folder ${outputFolder} does not exist, skipping cleanup`);\n return;\n }\n\n const files = fs.readdirSync(outputFolder);\n let deletedCount = 0;\n\n for (const file of files) {\n // Check if file is a header image (landscape or portrait) with different basename\n const landscapeMatch = file.match(/^(.+)_landscape_\\d+\\.(avif|jpg)$/);\n const portraitMatch = file.match(/^(.+)_portrait_\\d+\\.(avif|jpg)$/);\n\n if (landscapeMatch && landscapeMatch[1] !== currentHeaderBasename) {\n const filePath = path.join(outputFolder, file);\n ui?.debug(`Deleting old landscape header image: ${file}`);\n fs.unlinkSync(filePath);\n deletedCount++;\n } else if (portraitMatch && portraitMatch[1] !== currentHeaderBasename) {\n const filePath = path.join(outputFolder, file);\n ui?.debug(`Deleting old portrait header image: ${file}`);\n fs.unlinkSync(filePath);\n deletedCount++;\n }\n }\n\n if (deletedCount > 0) {\n ui?.success(`Deleted ${deletedCount} old header image(s)`);\n } else {\n ui?.debug(`No old header images to clean up`);\n }\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\n/**\n * Parses the telemetry option\n * @param value - The value to parse\n * @returns The parsed telemetry option\n */\nexport function parseTelemetryOption(value: string): '0' | '1' {\n if (value !== '0' && value !== '1') {\n throw new Error('Telemetry option must be either 0 or 1.');\n }\n\n return value;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GalleryDataDeprecatedSchema, GalleryDataSchema } from '@simple-photo-gallery/common';\n\nimport type { GalleryData, GalleryDataDeprecated } from '@simple-photo-gallery/common';\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Parses a gallery.json file and returns the gallery data\n * @param galleryJsonPath - Path to the gallery.json file\n * @returns Gallery data\n */\nexport function parseGalleryJson(galleryJsonPath: string, ui: ConsolaInstance): GalleryData {\n let galleryContent: string;\n try {\n galleryContent = fs.readFileSync(galleryJsonPath, 'utf8');\n } catch (error) {\n ui.error('Error parsing gallery.json: file not found');\n throw error;\n }\n\n let galleryJSON: unknown;\n try {\n galleryJSON = JSON.parse(galleryContent);\n } catch (error) {\n ui.error('Error parsing gallery.json: invalid JSON');\n throw error;\n }\n\n try {\n return GalleryDataSchema.parse(galleryJSON);\n } catch (error) {\n // Try to parse the JSON using the deprecated schema\n try {\n const deprecatedGalleryData = GalleryDataDeprecatedSchema.parse(galleryJSON);\n\n // Migrate the gallery data to the new schema\n return migrateGalleryJson(deprecatedGalleryData, galleryJsonPath, ui);\n } catch {\n ui.error('Error parsing gallery.json: invalid gallery data');\n throw error;\n }\n }\n}\n\n/**\n * Migrates gallery data from the deprecated schema to the new schema\n *\n * @param deprecatedGalleryData\n * @param galleryJsonPath\n * @param ui\n * @returns\n */\nexport function migrateGalleryJson(\n deprecatedGalleryData: GalleryDataDeprecated,\n galleryJsonPath: string,\n ui: ConsolaInstance,\n): GalleryData {\n ui.start('Old gallery.json format detected. Migrating gallery.json to the new data format.');\n\n // Check if a mediaBasePath should be used\n let mediaBasePath: string | undefined;\n\n const imagePath = deprecatedGalleryData.sections[0].images[0].path;\n if (imagePath && imagePath !== path.join('..', path.basename(imagePath))) {\n mediaBasePath = path.resolve(path.join(path.dirname(galleryJsonPath)), path.dirname(imagePath));\n }\n\n // Transform all images to contain filename instead of path for each section\n const sections = deprecatedGalleryData.sections.map((section) => ({\n ...section,\n images: section.images.map((image) => ({\n ...image,\n path: undefined,\n filename: path.basename(image.path),\n })),\n }));\n\n const galleryData = {\n ...deprecatedGalleryData,\n headerImage: path.basename(deprecatedGalleryData.headerImage),\n sections,\n mediaBasePath,\n };\n\n // Backup the old gallery.json file\n ui.debug('Backing up old gallery.json file');\n fs.copyFileSync(galleryJsonPath, `${galleryJsonPath}.old`);\n\n // Write the gallery data to the gallery.json file\n ui.debug('Writing gallery data to gallery.json file');\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n ui.success('Gallery data migrated to the new data format successfully.');\n\n return galleryData;\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 { CommandResultSummary } from '../telemetry/types';\nimport type { MediaFile } from '@simple-photo-gallery/common';\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 */\nexport async 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 mediaType = getMediaFileType(entry.name);\n\n if (mediaType) {\n const mediaFile: MediaFile = {\n type: mediaType,\n filename: entry.name,\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 headerImage = await ui.prompt('Enter the name of the header image', {\n type: 'text',\n default: defaultImage,\n placeholder: defaultImage,\n });\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 scanPath - Path to the directory that was scanned\n * @param subGalleries - Array of sub-galleries to include\n * @param useDefaultSettings - Whether to use default settings or prompt user\n * @param ctaBanner - Whether to add a Simple Photo Gallery call-to-action banner\n * @param ui - ConsolaInstance for prompting and logging\n */\nasync function createGalleryJson(\n mediaFiles: MediaFile[],\n galleryJsonPath: string,\n scanPath: string,\n subGalleries: SubGallery[] = [],\n useDefaultSettings: boolean,\n ctaBanner: boolean | undefined,\n ui: ConsolaInstance,\n): Promise<void> {\n const galleryDir = path.dirname(galleryJsonPath);\n\n // If the gallery is stored in the same location as the media files, use a relative base path, otherwise use an absolute path\n const isSameLocation = path.relative(scanPath, path.join(galleryDir, '..')) === '';\n const mediaBasePath = isSameLocation ? undefined : scanPath;\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: mediaFiles[0]?.filename || '',\n mediaBasePath: mediaBasePath,\n metadata: {},\n sections: [\n {\n images: mediaFiles,\n },\n ],\n subGalleries: {\n title: 'Sub Galleries',\n galleries: relativeSubGalleries,\n },\n ...(ctaBanner !== undefined && { ctaBanner }),\n };\n\n if (!useDefaultSettings) {\n galleryData = {\n ...galleryData,\n ...(await getGallerySettingsFromUser(\n path.basename(path.join(galleryDir, '..')),\n path.basename(mediaFiles[0]?.filename || ''),\n ui,\n )),\n };\n }\n\n await fs.writeFile(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n}\n\n/**\n * Checks if a gallery already exists in the specified directory\n * @param outputPath - Path where gallery would be created\n * @returns Promise resolving to true if gallery exists, false otherwise\n */\nasync function galleryExists(outputPath: string): Promise<boolean> {\n const galleryPath = path.join(outputPath, 'gallery');\n const galleryJsonPath = path.join(galleryPath, 'gallery.json');\n\n try {\n await fs.access(galleryJsonPath);\n return true;\n } catch {\n return false;\n }\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 force - Whether to force override existing galleries without prompting\n * @param ctaBanner - Whether to add a Simple Photo Gallery call-to-action banner\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 force: boolean,\n ctaBanner: boolean | undefined,\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 force,\n ctaBanner,\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 // Check if gallery already exists\n const exists = await galleryExists(outputPath);\n\n if (exists && !force) {\n // Ask user if they want to override\n const shouldOverride = await ui.prompt(`Gallery already exists at ${galleryJsonPath}. Do you want to override it?`, {\n type: 'confirm',\n default: false,\n });\n\n if (!shouldOverride) {\n ui.info('Skipping gallery creation');\n return { totalFiles: 0, totalGalleries: 0 };\n }\n }\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, scanPath, subGalleries, useDefaultSettings, ctaBanner, 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]?.filename || '',\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<CommandResultSummary> {\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(\n scanPath,\n outputPath,\n options.recursive,\n options.default,\n options.force,\n options.ctaBanner,\n ui,\n );\n\n ui.box(\n `Created ${result.totalGalleries} ${result.totalGalleries === 1 ? 'gallery' : 'galleries'} with ${result.totalFiles} media ${result.totalFiles === 1 ? 'file' : 'files'}`,\n );\n\n return {\n processedMediaCount: result.totalFiles,\n processedGalleryCount: result.totalGalleries,\n };\n } catch (error) {\n ui.error('Error initializing gallery');\n throw error;\n }\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 ExifReader from 'exifreader';\n\n/**\n * Extracts description from image EXIF data\n * @param image - Image path or File object\n * @returns Promise resolving to image description or undefined if not found\n */\nexport async function getImageDescription(image: string | File): Promise<string | undefined> {\n try {\n const tags = await ExifReader.load(image);\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","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 { 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 } from '../../utils/descriptions';\nimport { parseGalleryJson } from '../../utils/gallery';\nimport { createImageThumbnails, loadImageWithMetadata } from '../../utils/image';\nimport { getVideoDimensions, createVideoThumbnails } from '../../utils/video';\n\nimport type { ThumbnailOptions } from './types';\nimport type { CommandResultSummary } from '../telemetry/types';\nimport type { MediaFile } from '@simple-photo-gallery/common';\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 filename: path.basename(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 filename: path.basename(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 mediaBasePath - Base path for the media files\n * @param galleryDir - Path to the gallery directory\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 mediaBasePath: string,\n thumbnailsPath: string,\n thumbnailSize: number,\n ui: ConsolaInstance,\n): Promise<MediaFile> {\n try {\n // Resolve the path relative to the mediaBasePath\n const filePath = path.resolve(path.join(mediaBasePath, mediaFile.filename));\n\n const fileName = mediaFile.filename;\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\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.filename = mediaFile.filename;\n if (updatedMediaFile.thumbnail) {\n updatedMediaFile.thumbnail.path = path.basename(thumbnailPath);\n updatedMediaFile.thumbnail.pathRetina = path.basename(thumbnailPathRetina);\n // Preserve baseUrl from existing thumbnail if it exists\n if (mediaFile.thumbnail?.baseUrl) {\n updatedMediaFile.thumbnail.baseUrl = mediaFile.thumbnail.baseUrl;\n }\n }\n\n if (mediaFile.url) {\n updatedMediaFile.url = mediaFile.url;\n }\n\n return updatedMediaFile;\n } catch (error) {\n handleFileProcessingError(error, mediaFile.filename, ui);\n\n return { ...mediaFile, thumbnail: undefined };\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 galleryData = parseGalleryJson(galleryJsonPath, ui);\n\n const thumbnailSize = galleryData.thumbnailSize || DEFAULT_THUMBNAIL_SIZE;\n\n // If the mediaBasePath is not set, use the gallery directory\n const mediaBasePath = galleryData.mediaBasePath ?? path.join(galleryDir);\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, mediaBasePath, 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<CommandResultSummary> {\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 { processedGalleryCount: 0, processedMediaCount: 0 };\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\n return { processedGalleryCount: totalGalleries, processedMediaCount: totalProcessed };\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 { LogLevels, type ConsolaInstance } from 'consola';\n\nimport {\n cleanupOldHeaderImages,\n createGallerySocialMediaCardImage,\n createOptimizedHeaderImage,\n hasOldHeaderImages,\n} from './utils';\n\nimport { findGalleries } from '../../utils';\nimport { parseGalleryJson } from '../../utils/gallery';\nimport { scanDirectory } from '../init';\nimport { processGalleryThumbnails } from '../thumbnails';\n\nimport type { BuildOptions } from './types';\nimport type { CommandResultSummary } from '../telemetry/types';\nimport type { GalleryData } from '@simple-photo-gallery/common';\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 (galleryData.mediaBasePath) {\n const sourcePath = path.join(galleryData.mediaBasePath, image.filename);\n const destPath = path.join(galleryDir, image.filename);\n\n ui.debug(`Copying photo to ${destPath}`);\n fs.copyFileSync(sourcePath, destPath);\n }\n }\n }\n}\n\n/**\n * Scans a directory for new media files and appends them to the gallery.json\n * @param galleryDir - Directory containing the gallery\n * @param galleryJsonPath - Path to the gallery.json file\n * @param galleryData - Current gallery data\n * @param ui - ConsolaInstance for logging\n * @returns Updated gallery data with new files appended\n */\nasync function scanAndAppendNewFiles(\n galleryDir: string,\n galleryJsonPath: string,\n galleryData: GalleryData,\n ui: ConsolaInstance,\n): Promise<GalleryData> {\n // Determine the directory to scan based on mediaBasePath\n const scanPath = galleryData.mediaBasePath || galleryDir;\n\n ui.debug(`Scanning ${scanPath} for new media files`);\n\n // Use the scanDirectory function from init module to get all media files\n let scanResult;\n try {\n scanResult = await scanDirectory(scanPath, ui);\n } catch {\n ui.debug(`Could not scan directory ${scanPath}`);\n return galleryData;\n }\n\n // Get all existing filenames from all sections\n const existingFilenames = new Set<string>(\n galleryData.sections.flatMap((section) => section.images.map((image) => image.filename)),\n );\n\n // Filter out files that already exist in the gallery\n const newMediaFiles = scanResult.mediaFiles.filter((file) => !existingFilenames.has(file.filename));\n\n // If there are new files, append them to the last section\n if (newMediaFiles.length > 0) {\n ui.info(`Found ${newMediaFiles.length} new media ${newMediaFiles.length === 1 ? 'file' : 'files'}`);\n\n // Get the last section (or create one if no sections exist)\n if (galleryData.sections.length === 0) {\n galleryData.sections.push({ images: [] });\n }\n\n const lastSectionIndex = galleryData.sections.length - 1;\n const lastSection = galleryData.sections[lastSectionIndex];\n\n // Append new files to the last section\n lastSection.images.push(...newMediaFiles);\n\n // Save the updated gallery.json\n ui.debug('Updating gallery.json with new files');\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n ui.success(`Added ${newMediaFiles.length} new ${newMediaFiles.length === 1 ? 'file' : 'files'} to gallery.json`);\n } else {\n ui.debug('No new media files found');\n }\n\n return galleryData;\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 scan - Whether to scan for new media files\n * @param shouldCreateThumbnails - Whether to create thumbnails\n * @param ui - ConsolaInstance for logging\n * @param baseUrl - Optional base URL for hosting photos\n * @param thumbsBaseUrl - Optional base URL for hosting thumbnails\n */\nasync function buildGallery(\n galleryDir: string,\n templateDir: string,\n scan: boolean,\n shouldCreateThumbnails: boolean,\n ui: ConsolaInstance,\n baseUrl?: string,\n thumbsBaseUrl?: string,\n): Promise<void> {\n ui.start(`Building gallery ${galleryDir}`);\n\n // Read the gallery.json file\n const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');\n let galleryData = parseGalleryJson(galleryJsonPath, ui);\n\n // Scan for new media files and append them to the gallery.json\n if (scan) {\n galleryData = await scanAndAppendNewFiles(galleryDir, galleryJsonPath, galleryData, ui);\n }\n\n const socialMediaCardImagePath = path.join(galleryDir, 'gallery', 'images', 'social-media-card.jpg');\n const mediaBasePath = galleryData.mediaBasePath;\n const mediaBaseUrl = baseUrl || galleryData.mediaBaseUrl;\n const headerImagePath = mediaBasePath\n ? path.join(mediaBasePath, galleryData.headerImage)\n : path.resolve(galleryDir, galleryData.headerImage);\n\n const imagesFolder = path.join(galleryDir, 'gallery', 'images');\n const currentHeaderBasename = path.basename(headerImagePath, path.extname(headerImagePath));\n\n if (shouldCreateThumbnails) {\n // Create the images folder if it doesn't exist\n if (!fs.existsSync(imagesFolder)) {\n fs.mkdirSync(imagesFolder, { recursive: true });\n }\n\n // Check if header image has changed by looking for old header images\n const headerImageChanged = hasOldHeaderImages(imagesFolder, currentHeaderBasename);\n\n if (headerImageChanged) {\n ui.info('Header image changed, cleaning up old assets');\n\n // Clean up old header images\n cleanupOldHeaderImages(imagesFolder, currentHeaderBasename, ui);\n\n // Delete old social media card since header image changed\n if (fs.existsSync(socialMediaCardImagePath)) {\n fs.unlinkSync(socialMediaCardImagePath);\n ui.debug('Deleted old social media card');\n }\n }\n\n // Create the gallery social media card image\n await createGallerySocialMediaCardImage(headerImagePath, galleryData.title, socialMediaCardImagePath, ui);\n\n // Create optimized header image and generate blurhash\n const { blurHash } = await createOptimizedHeaderImage(headerImagePath, imagesFolder, ui);\n\n // Save the blurhash to gallery.json if it changed\n if (galleryData.headerImageBlurHash !== blurHash) {\n ui.debug('Updating gallery.json with header image blurhash');\n galleryData.headerImageBlurHash = blurHash;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n }\n\n // Ask the user if the photos should be copied if there is not baseUrl and mediaBasePath is set\n if (!mediaBaseUrl && mediaBasePath) {\n const shouldCopyPhotos = await ui.prompt('All photos need to be copied. Are you sure you want to continue?', {\n type: 'confirm',\n });\n\n if (shouldCopyPhotos) {\n ui.debug('Copying photos');\n copyPhotos(galleryData, galleryDir, ui);\n }\n }\n\n // If the baseUrl is provided, update the gallery.json file if needed\n if (mediaBaseUrl && galleryData.mediaBaseUrl !== mediaBaseUrl) {\n ui.debug('Updating gallery.json with baseUrl');\n galleryData.mediaBaseUrl = mediaBaseUrl;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n\n // If the thumbsBaseUrl is provided, update the gallery.json file if needed\n if (thumbsBaseUrl && galleryData.thumbsBaseUrl !== thumbsBaseUrl) {\n ui.debug('Updating gallery.json with thumbsBaseUrl');\n galleryData.thumbsBaseUrl = thumbsBaseUrl;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n\n // Set the social media card URL if changed\n\n if (!galleryData.metadata.image) {\n ui.debug('Updating gallery.json with social media card URL');\n\n galleryData.metadata.image = thumbsBaseUrl\n ? `${thumbsBaseUrl}/${path.basename(socialMediaCardImagePath)}`\n : `${galleryData.url || ''}/${path.relative(galleryDir, socialMediaCardImagePath)}`;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n\n // Generate the thumbnails if needed\n if (shouldCreateThumbnails) {\n await processGalleryThumbnails(galleryDir, ui);\n }\n\n // Build the template\n ui.debug('Building gallery from 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<CommandResultSummary> {\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 { processedGalleryCount: 0 };\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 const thumbsBaseUrl = options.thumbsBaseUrl\n ? `${options.thumbsBaseUrl}${path.relative(options.gallery, dir)}`\n : undefined;\n\n await buildGallery(path.resolve(dir), themeDir, options.scan, options.thumbnails, ui, baseUrl, thumbsBaseUrl);\n\n ++totalGalleries;\n }\n\n ui.box(`Built ${totalGalleries} ${totalGalleries === 1 ? 'gallery' : 'galleries'} successfully`);\n\n return { processedGalleryCount: totalGalleries };\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 { CommandResultSummary } from '../telemetry/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<CommandResultSummary> {\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 return { processedGalleryCount: filesRemoved };\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<CommandResultSummary> {\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 { processedGalleryCount: 0 };\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 { processedGalleryCount: 0 };\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\n return { processedGalleryCount: galleryDirs.length };\n } catch (error) {\n ui.error('Error cleaning galleries');\n throw error;\n }\n}\n","import type { TelemetryService } from './service';\nimport type { CommandResultSummary, TelemetryOptions } from './types';\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Handles the `gallery telemetry` command allowing users to configure telemetry preferences.\n */\nexport async function telemetry(\n options: TelemetryOptions,\n ui: ConsolaInstance,\n telemetryService: TelemetryService,\n): Promise<CommandResultSummary> {\n const updates: CommandResultSummary = {};\n\n // Return the status if no state is provided\n if (options.state === undefined) {\n const current = telemetryService.getStoredPreference();\n if (current === undefined) {\n ui.info('Telemetry preference not set yet. It will be requested on next command run.');\n updates.telemetryStatus = 'unset';\n } else {\n ui.info(`Telemetry is currently ${current ? 'enabled' : 'disabled'}.`);\n updates.telemetryEnabled = current;\n }\n } else {\n // Set the telemetry preference\n telemetryService.setStoredPreference(options.state === '1');\n ui.success(`Anonymous telemetry ${options.state === '1' ? 'enabled' : 'disabled'}.`);\n updates.telemetryEnabled = options.state === '1';\n }\n\n return updates;\n}\n","import process from 'node:process';\n\nimport axios from 'axios';\n\nimport type { TelemetryClient, TelemetryEvent } from '../types';\n\n/**\n * Telemetry client that sends events to the Simple Photo Gallery telemetry API.\n */\nexport class ApiTelemetryClient implements TelemetryClient {\n private readonly endpoint = 'https://tools.simple.photo/api/telemetry';\n\n async record(event: TelemetryEvent): Promise<void> {\n try {\n axios.post(this.endpoint, event, {\n headers: {\n 'content-type': 'application/json',\n 'user-agent': `simple-photo-gallery/${event.packageVersion} (${process.platform}; ${process.arch})`,\n },\n });\n } catch {\n // Swallow network errors - telemetry must never interrupt the CLI flow.\n }\n }\n}\n","import { stdout } from 'node:process';\n\nimport type { TelemetryClient, TelemetryEvent } from '../types';\n\n/**\n * Telemetry client that prints events to the console.\n * Useful for local development and testing without sending network requests.\n */\nexport class ConsoleTelemetryClient implements TelemetryClient {\n async record(event: TelemetryEvent): Promise<void> {\n const serialized = JSON.stringify(event, null, 2);\n\n stdout.write(`TELEMETRY EVENT: ${serialized}\\n`);\n }\n}\n","import os from 'node:os';\nimport process from 'node:process';\n\nimport Conf from 'conf';\n\nimport { ApiTelemetryClient } from '../clients/api';\nimport { ConsoleTelemetryClient } from '../clients/console';\n\nimport type { CommandResultSummary, TelemetryClient, TelemetryConfigSchema, TelemetryEvent } from '../types';\nimport type { ConsolaInstance } from 'consola';\n\nconst CONFIG_KEY = 'telemetryEnabled';\n\ninterface BuildEventParams {\n command: string;\n argumentsProvided: string[];\n globalOptionsProvided: string[];\n metrics?: CommandResultSummary;\n success: boolean;\n error?: { name: string; message: string };\n startedAt: number;\n}\n\n/**\n * Manages telemetry configuration, consent and dispatching events to a concrete client.\n */\nexport class TelemetryService {\n private readonly config: Conf<TelemetryConfigSchema>;\n private readonly packageName: string;\n private readonly packageVersion: string;\n private readonly ui: ConsolaInstance;\n\n private client?: TelemetryClient;\n\n constructor(packageName: string, packageVersion: string, ui: ConsolaInstance) {\n this.packageName = packageName;\n this.packageVersion = packageVersion;\n this.ui = ui;\n this.config = new Conf<TelemetryConfigSchema>({ projectName: packageName });\n }\n\n /** Returns the stored telemetry preference, if any. */\n getStoredPreference(): boolean | undefined {\n return this.config.get(CONFIG_KEY);\n }\n\n /** Updates the persisted telemetry preference. */\n setStoredPreference(enabled: boolean): void {\n this.config.set(CONFIG_KEY, enabled);\n }\n\n /** Determines whether telemetry should be collected for this run. */\n async shouldCollectTelemetry(override?: '0' | '1'): Promise<boolean> {\n /** Command-level override */\n if (override) {\n return override === '1';\n }\n\n /** CI or DO_NOT_TRACK environment variables */\n if (process.env.CI || process.env.DO_NOT_TRACK) {\n return false;\n }\n\n /** SPG_TELEMETRY environment variable */\n if (process.env.SPG_TELEMETRY) {\n return process.env.SPG_TELEMETRY === '1';\n }\n\n /** Stored preference */\n const stored = this.getStoredPreference();\n\n if (stored === undefined) {\n // Ask the user if no consent was given yet\n const consent = await this.promptForConsent();\n this.setStoredPreference(consent);\n return consent;\n } else {\n // Return the stored preference\n return stored;\n }\n }\n\n /** Builds and dispatches a telemetry event when telemetry is enabled. */\n async emit(params: BuildEventParams): Promise<void> {\n const event = this.buildEvent(params);\n\n try {\n const client = await this.getClient();\n\n if (client) {\n await client.record(event);\n }\n } catch (error) {\n this.ui.debug('Error recording telemetry event', error);\n }\n }\n\n /** Builds a telemetry event. */\n private buildEvent({\n command,\n argumentsProvided,\n globalOptionsProvided,\n metrics,\n success,\n error,\n startedAt,\n }: BuildEventParams): TelemetryEvent {\n const now = Date.now();\n\n const event: TelemetryEvent = {\n command,\n argumentsProvided,\n globalOptionsProvided,\n timestamp: new Date(now).toISOString(),\n durationMs: now - startedAt,\n packageName: this.packageName,\n packageVersion: this.packageVersion,\n nodeVersion: process.version,\n osPlatform: os.platform(),\n osRelease: os.release(),\n osArch: os.arch(),\n result: success ? 'success' : 'error',\n };\n\n if (metrics && Object.keys(metrics).length > 0) {\n event.metrics = metrics;\n }\n\n if (!success && error) {\n event.errorName = error.name;\n event.errorMessage = error.message;\n }\n\n return event;\n }\n\n /** Prompts the user for consent to collect telemetry. */\n private async promptForConsent(): Promise<boolean> {\n this.ui.info(\n 'Simple Photo Gallery collects anonymous usage telemetry to improve the CLI. No personal data or information about your photos is collected. For more information, see https://simple.photo/telemetry.',\n );\n\n const answer = await this.ui.prompt('Would you like to enable telemetry?', {\n type: 'confirm',\n initial: true,\n });\n\n if (!answer) {\n this.ui.info('Anonymous telemetry disabled. You can re-enable it with \"spg telemetry 1\".');\n return false;\n }\n\n this.ui.success('Thank you! Telemetry will help us improve Simple Photo Gallery.');\n return true;\n }\n\n /** Returns the telemetry client. */\n private getClient(): TelemetryClient | undefined {\n if (!this.client) {\n // Create the client based on the environment variable\n switch (process.env.SPG_TELEMETRY_PROVIDER) {\n case 'none': {\n this.client = undefined;\n break;\n }\n case 'console': {\n this.client = new ConsoleTelemetryClient();\n break;\n }\n case 'api': {\n this.client = new ApiTelemetryClient();\n break;\n }\n default: {\n this.client = new ApiTelemetryClient();\n break;\n }\n }\n }\n\n return this.client;\n }\n}\n","import axios from 'axios';\nimport { compareSemVer, parseSemVer } from 'semver-parser';\n\nimport type { ConsolaInstance } from 'consola';\n\nconst NPM_REGISTRY_URL = 'https://registry.npmjs.org';\nconst CHECK_TIMEOUT_MS = 3000; // 3 seconds timeout for the check\n\ninterface PackageInfo {\n versions: Record<string, unknown>;\n}\n\n/**\n * Fetches the latest stable version of a package from npm registry\n * Uses abbreviated metadata for faster response times\n * @param packageName - Name of the package to check\n * @returns Promise resolving to the latest stable version or undefined if error\n */\nasync function fetchLatestStableVersion(packageName: string): Promise<string | undefined> {\n try {\n // Use abbreviated metadata endpoint for faster response\n const response = await axios.get<PackageInfo>(`${NPM_REGISTRY_URL}/${packageName}`, {\n timeout: CHECK_TIMEOUT_MS,\n headers: {\n Accept: 'application/vnd.npm.install-v1+json',\n },\n });\n\n // Get all version numbers\n const versions = Object.keys(response.data.versions);\n\n // Filter to only stable versions (no pre-release identifiers)\n const stableVersions = versions.filter((version) => {\n try {\n const parsed = parseSemVer(version);\n return !parsed.pre || parsed.pre.length === 0;\n } catch {\n return false;\n }\n });\n\n if (stableVersions.length === 0) {\n return undefined;\n }\n\n // Sort versions and get the latest stable one\n stableVersions.sort(compareSemVer);\n\n // eslint-disable-next-line unicorn/prefer-at\n return stableVersions[stableVersions.length - 1];\n } catch {\n // Silently fail on any error (network, timeout, etc.)\n return undefined;\n }\n}\n\n/**\n * Starts a background check for package updates\n * Returns a promise that resolves to update info or null\n * Only returns info if the latest stable version is newer than current\n * @param packageName - Name of the package\n * @param currentVersion - Current version of the package\n * @returns Promise resolving to update info or null\n */\nexport function checkForUpdates(\n packageName: string,\n currentVersion: string,\n): Promise<{ currentVersion: string; latestVersion: string } | null> {\n return fetchLatestStableVersion(packageName).then((latestStableVersion) => {\n if (!latestStableVersion) {\n return null;\n }\n\n try {\n // Check if the latest stable version is newer than current\n if (compareSemVer(latestStableVersion, currentVersion) > 0) {\n return { currentVersion, latestVersion: latestStableVersion };\n }\n } catch {\n // If semver parsing fails, silently ignore\n }\n\n return null;\n });\n}\n\n/**\n * Displays an update notification box if an update is available\n * @param updateInfo - Information about the available update\n * @param ui - Consola UI instance for displaying the message\n */\nexport function displayUpdateNotification(\n updateInfo: { currentVersion: string; latestVersion: string },\n ui: ConsolaInstance,\n): void {\n const { currentVersion, latestVersion } = updateInfo;\n\n const message = [\n `New version available: ${currentVersion} → ${latestVersion}`,\n '',\n 'Run the following commands to update:',\n '',\n ' npm install -g simple-photo-gallery@latest',\n ].join('\\n');\n\n ui.box(message);\n}\n\n/**\n * Waits for the update check promise with a timeout\n * If the check is not complete within the timeout, returns null\n * @param checkPromise - Promise from checkForUpdates\n * @returns Promise resolving to update info or null\n */\nexport async function waitForUpdateCheck(\n checkPromise: Promise<{ currentVersion: string; latestVersion: string } | null>,\n): Promise<{ currentVersion: string; latestVersion: string } | null> {\n try {\n let timeoutId: ReturnType<typeof setTimeout>;\n const timeoutPromise = new Promise<null>((resolve) => {\n timeoutId = setTimeout(() => resolve(null), 5000);\n });\n\n const result = await Promise.race([checkPromise, timeoutPromise]);\n clearTimeout(timeoutId!);\n return result;\n } catch {\n // Silently fail on any error\n return null;\n }\n}\n","{\n \"name\": \"simple-photo-gallery\",\n \"version\": \"2.0.16\",\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 \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n },\n \"./lib\": {\n \"types\": \"./dist/lib/index.d.ts\",\n \"default\": \"./dist/lib/index.js\"\n },\n \"./lib/browser\": {\n \"types\": \"./dist/lib/browser.d.ts\",\n \"default\": \"./dist/lib/browser.js\"\n }\n },\n \"scripts\": {\n \"gallery\": \"SPG_TELEMETRY_PROVIDER=none 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 \"test:coverage\": \"jest --coverage\",\n \"prepublish\": \"yarn build\"\n },\n \"dependencies\": {\n \"@simple-photo-gallery/common\": \"1.0.5\",\n \"@simple-photo-gallery/theme-modern\": \"2.0.16\",\n \"axios\": \"^1.12.2\",\n \"blurhash\": \"^2.0.5\",\n \"commander\": \"^12.0.0\",\n \"conf\": \"^13.0.1\",\n \"consola\": \"^3.4.2\",\n \"exifreader\": \"^4.32.0\",\n \"node-ffprobe\": \"^3.0.0\",\n \"semver-parser\": \"^4.1.8\",\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 \"@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 { telemetry } from './modules/telemetry';\nimport { TelemetryService } from './modules/telemetry/service';\nimport { thumbnails } from './modules/thumbnails';\nimport { parseTelemetryOption } from './utils';\nimport { checkForUpdates, displayUpdateNotification, waitForUpdateCheck } from './utils/version';\n\nimport packageJson from '../package.json' with { type: 'json' };\n\nimport type { CommandResultSummary } from './modules/telemetry/types';\nimport type { TelemetryOption } from './types';\nimport type { Command as CommanderCommand } from 'commander';\n\n/** Command line interface program instance */\nconst program = new Command();\n\nconst telemetryService = new TelemetryService(packageJson.name, packageJson.version, createConsolaUI(program.opts()));\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 .option('--telemetry <state>', 'Enable (1) or disable (0) telemetry for this command', parseTelemetryOption)\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 * Collects the command arguments\n * @param command - The commander command\n * @returns The command arguments\n */\nfunction collectCommandArguments(command: CommanderCommand): string[] {\n return command.options\n .map((option) => ({ name: option.attributeName(), source: command.getOptionValueSource(option.attributeName()) }))\n .filter((option) => option.source && option.source !== 'default')\n .map((option) => option.name);\n}\n\n/**\n * Collects the global arguments\n * @returns The global arguments\n */\nfunction collectGlobalArguments(): string[] {\n return program.options\n .map((option) => ({ name: option.attributeName(), source: program.getOptionValueSource(option.attributeName()) }))\n .filter((option) => option.source && option.source !== 'default')\n .map((option) => option.name);\n}\n\n/**\n * Higher-order function that wraps command handlers to provide Consola UI and telemetry instrumentation.\n * @param handler - Command handler function that receives options, UI instance and commander command\n * @returns Wrapped handler function that creates UI, handles errors and records telemetry\n */\nfunction withCommandContext<O>(\n handler: (\n opts: O,\n ui: ConsolaInstance,\n command: CommanderCommand,\n ) => Promise<CommandResultSummary | void> | CommandResultSummary | void,\n) {\n return async (rawOpts: O & TelemetryOption, command: CommanderCommand) => {\n const { telemetry: telemetryOverride, ...commandOptions } = rawOpts as O & TelemetryOption;\n\n // Create the Consola UI instance\n const ui = createConsolaUI(program.opts());\n\n // Determine if telemetry should be collected\n const shouldCollectTelemetry = await telemetryService.shouldCollectTelemetry(telemetryOverride);\n\n // Get the started at timestamp\n const startedAt = Date.now();\n\n // Start updates check\n const updateCheckPromise = checkForUpdates(packageJson.name, packageJson.version);\n\n // Run the command handler\n let success = false;\n let metrics: CommandResultSummary | undefined;\n let errorInfo: { name: string; message: string } | undefined;\n try {\n const result = await handler(commandOptions as O, ui, command);\n if (result && Object.keys(result).length > 0) {\n metrics = result;\n }\n\n success = true;\n } catch (error) {\n ui.debug(error);\n\n errorInfo =\n error instanceof Error\n ? { name: error.name, message: error.message }\n : { name: 'UnknownError', message: String(error) };\n\n process.exitCode = 1;\n }\n\n // Wait for the updates check\n const updateInfo = await waitForUpdateCheck(updateCheckPromise);\n if (updateInfo) {\n displayUpdateNotification(updateInfo, ui);\n }\n\n if (shouldCollectTelemetry) {\n await telemetryService.emit({\n command: command.name(),\n argumentsProvided: collectCommandArguments(command),\n globalOptionsProvided: collectGlobalArguments(),\n metrics,\n success,\n error: errorInfo,\n startedAt,\n });\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 .option('-f, --force', 'Force override existing galleries without asking', false)\n .option('--cta-banner', 'Add a Simple Photo Gallery call-to-action banner to the end of the gallery', false)\n .action(withCommandContext((options, ui) => init(options, ui)));\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(withCommandContext((options, ui) => thumbnails(options, ui)));\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 .option('-t, --thumbs-base-url <url>', 'Base URL where the thumbnails are hosted')\n .option('--no-thumbnails', 'Skip creating thumbnails when building the gallery', true)\n .option('--no-scan', 'Do not scan for new photos when building the gallery', true)\n .action(withCommandContext((options, ui) => build(options, ui)));\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(withCommandContext((options, ui) => clean(options, ui)));\n\nprogram\n .command('telemetry')\n .description('Manage anonymous telemetry preferences. Use 1 to enable, 0 to disable, or no argument to check status')\n .option('-s, --state <state>', 'Enable (1) or disable (0) telemetry', parseTelemetryOption)\n .action(withCommandContext((options, ui) => telemetry(options, ui, telemetryService)));\n\nprogram.parse();\n"]}
1
+ {"version":3,"sources":["../src/config/index.ts","../src/utils/image.ts","../src/utils/blurhash.ts","../src/modules/build/utils/index.ts","../src/utils/index.ts","../src/utils/gallery.ts","../src/modules/init/utils/index.ts","../src/modules/init/index.ts","../src/modules/thumbnails/utils/index.ts","../src/utils/descriptions.ts","../src/utils/video.ts","../src/modules/thumbnails/index.ts","../src/modules/build/index.ts","../src/modules/clean/index.ts","../src/modules/telemetry/index.ts","../src/modules/telemetry/clients/api.ts","../src/modules/telemetry/clients/console.ts","../src/modules/telemetry/service/index.ts","../src/utils/version.ts","../package.json","../src/index.ts"],"names":["sharp","path","fs","result","process","LogLevels","telemetryService","axios"],"mappings":"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","file":"index.js","sourcesContent":["/** 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 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 * 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 { 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 { 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 { generateBlurHash } from '../../../utils/blurhash';\nimport { cropAndResizeImage, loadImage } from '../../../utils/image';\n\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Wraps text into multiple lines based on a maximum character width\n * @param text - The text to wrap\n * @param maxCharsPerLine - Maximum number of characters per line (approximate)\n * @returns Array of text lines\n */\nfunction wrapText(text: string, maxCharsPerLine: number): string[] {\n const words = text.split(' ');\n const lines: string[] = [];\n let currentLine = '';\n\n for (const word of words) {\n const testLine = currentLine ? `${currentLine} ${word}` : word;\n\n // If a single word is longer than max, force it on its own line\n if (word.length > maxCharsPerLine) {\n if (currentLine) {\n lines.push(currentLine);\n currentLine = '';\n }\n lines.push(word);\n } else if (testLine.length > maxCharsPerLine && currentLine) {\n // If the test line is too long and we have words in current line, start new line\n lines.push(currentLine);\n currentLine = word;\n } else {\n currentLine = testLine;\n }\n }\n\n // Add the last line\n if (currentLine) {\n lines.push(currentLine);\n }\n\n return lines;\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 * @returns The basename of the header photo used\n */\nexport async function createGallerySocialMediaCardImage(\n headerPhotoPath: string,\n title: string,\n ouputPath: string,\n ui?: ConsolaInstance,\n): Promise<string> {\n ui?.start(`Creating social media card image`);\n\n const headerBasename = path.basename(headerPhotoPath, path.extname(headerPhotoPath));\n\n if (fs.existsSync(ouputPath)) {\n ui?.success(`Social media card image already exists`);\n return headerBasename;\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 // Configuration for text rendering\n const CANVAS_WIDTH = 1200;\n const CANVAS_HEIGHT = 631;\n const FONT_SIZE = 72;\n const MARGIN = 50; // Margin from edges\n const CHAR_WIDTH_RATIO = 0.6; // Approximate ratio of character width to font size for Arial bold\n\n // Calculate maximum characters per line based on canvas width and font size\n const usableWidth = CANVAS_WIDTH - 2 * MARGIN;\n const maxCharsPerLine = Math.floor(usableWidth / (FONT_SIZE * CHAR_WIDTH_RATIO));\n const lines = wrapText(title, maxCharsPerLine);\n\n // Calculate vertical positioning for bottom-left alignment\n const lineHeight = FONT_SIZE * 1.2; // 20% spacing between lines\n const totalTextHeight = FONT_SIZE + (lines.length - 1) * lineHeight; // First line + spacing for additional lines\n const startY = CANVAS_HEIGHT - MARGIN - totalTextHeight + FONT_SIZE; // Bottom aligned with margin\n\n // Create SVG with title split into multiple lines using tspan elements (left aligned)\n const leftX = MARGIN;\n const tspanElements = lines\n .map((line, index) => {\n const yPosition = startY + index * lineHeight;\n // Escape special XML characters in the line text\n /* eslint-disable unicorn/prefer-string-replace-all */\n const escapedLine = line\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;');\n /* eslint-enable unicorn/prefer-string-replace-all */\n return `<tspan x=\"${leftX}\" y=\"${yPosition}\">${escapedLine}</tspan>`;\n })\n .join('\\n ');\n\n const svgText = `\n <svg width=\"${CANVAS_WIDTH}\" height=\"${CANVAS_HEIGHT}\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient id=\"darkGradient\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n <stop offset=\"0%\" style=\"stop-color:rgb(0,0,0);stop-opacity:0\" />\n <stop offset=\"100%\" style=\"stop-color:rgb(0,0,0);stop-opacity:0.65\" />\n </linearGradient>\n <style>\n .title { font-family: 'Arial, sans-serif'; font-size: ${FONT_SIZE}px; font-weight: bold; fill: white; text-anchor: start; }\n </style>\n </defs>\n <rect x=\"0\" y=\"0\" width=\"${CANVAS_WIDTH}\" height=\"${CANVAS_HEIGHT}\" fill=\"url(#darkGradient)\" />\n <text x=\"${leftX}\" class=\"title\">\n ${tspanElements}\n </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 return headerBasename;\n}\n\n/**\n * Creates optimized header images for different orientations and sizes\n * @param headerPhotoPath - Path to the header photo\n * @param outputFolder - Folder where header images should be saved\n * @param ui - ConsolaInstance for logging\n * @returns Object containing the header basename, array of generated file paths, and blurhash\n */\nexport async function createOptimizedHeaderImage(\n headerPhotoPath: string,\n outputFolder: string,\n ui?: ConsolaInstance,\n): Promise<{ headerBasename: string; generatedFiles: string[]; blurHash: string }> {\n ui?.start(`Creating optimized header images`);\n\n const image = await loadImage(headerPhotoPath);\n const headerBasename = path.basename(headerPhotoPath, path.extname(headerPhotoPath));\n const generatedFiles: string[] = [];\n\n // Generate blurhash for the header image\n ui?.debug('Generating blurhash for header image');\n const blurHash = await generateBlurHash(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 const avifFilename = `${headerBasename}_landscape_${width}.avif`;\n const jpgFilename = `${headerBasename}_landscape_${width}.jpg`;\n\n if (fs.existsSync(path.join(outputFolder, avifFilename))) {\n ui?.debug(`Landscape header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, avifFilename),\n width,\n width * landscapeYFactor,\n 'avif',\n );\n }\n generatedFiles.push(avifFilename);\n\n if (fs.existsSync(path.join(outputFolder, jpgFilename))) {\n ui?.debug(`Landscape header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(image.clone(), path.join(outputFolder, jpgFilename), width, width * landscapeYFactor, 'jpg');\n }\n generatedFiles.push(jpgFilename);\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 const avifFilename = `${headerBasename}_portrait_${width}.avif`;\n const jpgFilename = `${headerBasename}_portrait_${width}.jpg`;\n\n if (fs.existsSync(path.join(outputFolder, avifFilename))) {\n ui?.debug(`Portrait header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(image.clone(), path.join(outputFolder, avifFilename), width, width * portraitYFactor, 'avif');\n }\n generatedFiles.push(avifFilename);\n\n if (fs.existsSync(path.join(outputFolder, jpgFilename))) {\n ui?.debug(`Portrait header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(image.clone(), path.join(outputFolder, jpgFilename), width, width * portraitYFactor, 'jpg');\n }\n generatedFiles.push(jpgFilename);\n }\n\n ui?.success(`Created optimized header image successfully`);\n return { headerBasename, generatedFiles, blurHash };\n}\n\n/**\n * Checks if there are old header images with a different basename than the current one\n * @param outputFolder - Folder containing the header images\n * @param currentHeaderBasename - Basename of the current header image\n * @returns True if old header images with different basename exist, false otherwise\n */\nexport function hasOldHeaderImages(outputFolder: string, currentHeaderBasename: string): boolean {\n if (!fs.existsSync(outputFolder)) {\n return false;\n }\n\n const files = fs.readdirSync(outputFolder);\n\n for (const file of files) {\n // Check if file is a header image (landscape or portrait) with different basename\n const landscapeMatch = file.match(/^(.+)_landscape_\\d+\\.(avif|jpg)$/);\n const portraitMatch = file.match(/^(.+)_portrait_\\d+\\.(avif|jpg)$/);\n\n if (\n (landscapeMatch && landscapeMatch[1] !== currentHeaderBasename) ||\n (portraitMatch && portraitMatch[1] !== currentHeaderBasename)\n ) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Cleans up old header images that don't match the current header image\n * @param outputFolder - Folder containing the header images\n * @param currentHeaderBasename - Basename of the current header image\n * @param ui - ConsolaInstance for logging\n */\nexport function cleanupOldHeaderImages(outputFolder: string, currentHeaderBasename: string, ui?: ConsolaInstance): void {\n ui?.start(`Cleaning up old header images`);\n\n if (!fs.existsSync(outputFolder)) {\n ui?.debug(`Output folder ${outputFolder} does not exist, skipping cleanup`);\n return;\n }\n\n const files = fs.readdirSync(outputFolder);\n let deletedCount = 0;\n\n for (const file of files) {\n // Check if file is a header image (landscape or portrait) with different basename\n const landscapeMatch = file.match(/^(.+)_landscape_\\d+\\.(avif|jpg)$/);\n const portraitMatch = file.match(/^(.+)_portrait_\\d+\\.(avif|jpg)$/);\n\n if (landscapeMatch && landscapeMatch[1] !== currentHeaderBasename) {\n const filePath = path.join(outputFolder, file);\n ui?.debug(`Deleting old landscape header image: ${file}`);\n fs.unlinkSync(filePath);\n deletedCount++;\n } else if (portraitMatch && portraitMatch[1] !== currentHeaderBasename) {\n const filePath = path.join(outputFolder, file);\n ui?.debug(`Deleting old portrait header image: ${file}`);\n fs.unlinkSync(filePath);\n deletedCount++;\n }\n }\n\n if (deletedCount > 0) {\n ui?.success(`Deleted ${deletedCount} old header image(s)`);\n } else {\n ui?.debug(`No old header images to clean up`);\n }\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\n/**\n * Parses the telemetry option\n * @param value - The value to parse\n * @returns The parsed telemetry option\n */\nexport function parseTelemetryOption(value: string): '0' | '1' {\n if (value !== '0' && value !== '1') {\n throw new Error('Telemetry option must be either 0 or 1.');\n }\n\n return value;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GalleryDataDeprecatedSchema, GalleryDataSchema } from '@simple-photo-gallery/common';\n\nimport type { GalleryData, GalleryDataDeprecated } from '@simple-photo-gallery/common';\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Parses a gallery.json file and returns the gallery data\n * @param galleryJsonPath - Path to the gallery.json file\n * @returns Gallery data\n */\nexport function parseGalleryJson(galleryJsonPath: string, ui: ConsolaInstance): GalleryData {\n let galleryContent: string;\n try {\n galleryContent = fs.readFileSync(galleryJsonPath, 'utf8');\n } catch (error) {\n ui.error('Error parsing gallery.json: file not found');\n throw error;\n }\n\n let galleryJSON: unknown;\n try {\n galleryJSON = JSON.parse(galleryContent);\n } catch (error) {\n ui.error('Error parsing gallery.json: invalid JSON');\n throw error;\n }\n\n try {\n return GalleryDataSchema.parse(galleryJSON);\n } catch (error) {\n // Try to parse the JSON using the deprecated schema\n try {\n const deprecatedGalleryData = GalleryDataDeprecatedSchema.parse(galleryJSON);\n\n // Migrate the gallery data to the new schema\n return migrateGalleryJson(deprecatedGalleryData, galleryJsonPath, ui);\n } catch {\n ui.error('Error parsing gallery.json: invalid gallery data');\n throw error;\n }\n }\n}\n\n/**\n * Migrates gallery data from the deprecated schema to the new schema\n *\n * @param deprecatedGalleryData\n * @param galleryJsonPath\n * @param ui\n * @returns\n */\nexport function migrateGalleryJson(\n deprecatedGalleryData: GalleryDataDeprecated,\n galleryJsonPath: string,\n ui: ConsolaInstance,\n): GalleryData {\n ui.start('Old gallery.json format detected. Migrating gallery.json to the new data format.');\n\n // Check if a mediaBasePath should be used\n let mediaBasePath: string | undefined;\n\n const imagePath = deprecatedGalleryData.sections[0].images[0].path;\n if (imagePath && imagePath !== path.join('..', path.basename(imagePath))) {\n mediaBasePath = path.resolve(path.join(path.dirname(galleryJsonPath)), path.dirname(imagePath));\n }\n\n // Transform all images to contain filename instead of path for each section\n const sections = deprecatedGalleryData.sections.map((section) => ({\n ...section,\n images: section.images.map((image) => ({\n ...image,\n path: undefined,\n filename: path.basename(image.path),\n })),\n }));\n\n const galleryData = {\n ...deprecatedGalleryData,\n headerImage: path.basename(deprecatedGalleryData.headerImage),\n sections,\n mediaBasePath,\n };\n\n // Backup the old gallery.json file\n ui.debug('Backing up old gallery.json file');\n fs.copyFileSync(galleryJsonPath, `${galleryJsonPath}.old`);\n\n // Write the gallery data to the gallery.json file\n ui.debug('Writing gallery data to gallery.json file');\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n ui.success('Gallery data migrated to the new data format successfully.');\n\n return galleryData;\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 { CommandResultSummary } from '../telemetry/types';\nimport type { MediaFile } from '@simple-photo-gallery/common';\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 */\nexport async 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 mediaType = getMediaFileType(entry.name);\n\n if (mediaType) {\n const mediaFile: MediaFile = {\n type: mediaType,\n filename: entry.name,\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 headerImage = await ui.prompt('Enter the name of the header image', {\n type: 'text',\n default: defaultImage,\n placeholder: defaultImage,\n });\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 scanPath - Path to the directory that was scanned\n * @param subGalleries - Array of sub-galleries to include\n * @param useDefaultSettings - Whether to use default settings or prompt user\n * @param ctaBanner - Whether to add a Simple Photo Gallery call-to-action banner\n * @param ui - ConsolaInstance for prompting and logging\n */\nasync function createGalleryJson(\n mediaFiles: MediaFile[],\n galleryJsonPath: string,\n scanPath: string,\n subGalleries: SubGallery[] = [],\n useDefaultSettings: boolean,\n ctaBanner: boolean | undefined,\n ui: ConsolaInstance,\n): Promise<void> {\n const galleryDir = path.dirname(galleryJsonPath);\n\n // If the gallery is stored in the same location as the media files, use a relative base path, otherwise use an absolute path\n const isSameLocation = path.relative(scanPath, path.join(galleryDir, '..')) === '';\n const mediaBasePath = isSameLocation ? undefined : scanPath;\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: mediaFiles[0]?.filename || '',\n mediaBasePath: mediaBasePath,\n metadata: {},\n sections: [\n {\n images: mediaFiles,\n },\n ],\n subGalleries: {\n title: 'Sub Galleries',\n galleries: relativeSubGalleries,\n },\n ...(ctaBanner !== undefined && { ctaBanner }),\n };\n\n if (!useDefaultSettings) {\n galleryData = {\n ...galleryData,\n ...(await getGallerySettingsFromUser(\n path.basename(path.join(galleryDir, '..')),\n path.basename(mediaFiles[0]?.filename || ''),\n ui,\n )),\n };\n }\n\n await fs.writeFile(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n}\n\n/**\n * Checks if a gallery already exists in the specified directory\n * @param outputPath - Path where gallery would be created\n * @returns Promise resolving to true if gallery exists, false otherwise\n */\nasync function galleryExists(outputPath: string): Promise<boolean> {\n const galleryPath = path.join(outputPath, 'gallery');\n const galleryJsonPath = path.join(galleryPath, 'gallery.json');\n\n try {\n await fs.access(galleryJsonPath);\n return true;\n } catch {\n return false;\n }\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 force - Whether to force override existing galleries without prompting\n * @param ctaBanner - Whether to add a Simple Photo Gallery call-to-action banner\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 force: boolean,\n ctaBanner: boolean | undefined,\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 force,\n ctaBanner,\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 // Check if gallery already exists\n const exists = await galleryExists(outputPath);\n\n if (exists && !force) {\n // Ask user if they want to override\n const shouldOverride = await ui.prompt(`Gallery already exists at ${galleryJsonPath}. Do you want to override it?`, {\n type: 'confirm',\n default: false,\n });\n\n if (!shouldOverride) {\n ui.info('Skipping gallery creation');\n return { totalFiles: 0, totalGalleries: 0 };\n }\n }\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, scanPath, subGalleries, useDefaultSettings, ctaBanner, 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]?.filename || '',\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<CommandResultSummary> {\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(\n scanPath,\n outputPath,\n options.recursive,\n options.default,\n options.force,\n options.ctaBanner,\n ui,\n );\n\n ui.box(\n `Created ${result.totalGalleries} ${result.totalGalleries === 1 ? 'gallery' : 'galleries'} with ${result.totalFiles} media ${result.totalFiles === 1 ? 'file' : 'files'}`,\n );\n\n return {\n processedMediaCount: result.totalFiles,\n processedGalleryCount: result.totalGalleries,\n };\n } catch (error) {\n ui.error('Error initializing gallery');\n throw error;\n }\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 ExifReader from 'exifreader';\n\n/**\n * Extracts description from image EXIF data\n * @param image - Image path or File object\n * @returns Promise resolving to image description or undefined if not found\n */\nexport async function getImageDescription(image: string | File): Promise<string | undefined> {\n try {\n const tags = await ExifReader.load(image);\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","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 { 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 } from '../../utils/descriptions';\nimport { parseGalleryJson } from '../../utils/gallery';\nimport { createImageThumbnails, loadImageWithMetadata } from '../../utils/image';\nimport { getVideoDimensions, createVideoThumbnails } from '../../utils/video';\n\nimport type { ThumbnailOptions } from './types';\nimport type { CommandResultSummary } from '../telemetry/types';\nimport type { MediaFile } from '@simple-photo-gallery/common';\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 filename: path.basename(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 filename: path.basename(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 mediaBasePath - Base path for the media files\n * @param galleryDir - Path to the gallery directory\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 mediaBasePath: string,\n thumbnailsPath: string,\n thumbnailSize: number,\n ui: ConsolaInstance,\n): Promise<MediaFile> {\n try {\n // Resolve the path relative to the mediaBasePath\n const filePath = path.resolve(path.join(mediaBasePath, mediaFile.filename));\n\n const fileName = mediaFile.filename;\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\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.filename = mediaFile.filename;\n if (updatedMediaFile.thumbnail) {\n updatedMediaFile.thumbnail.path = path.basename(thumbnailPath);\n updatedMediaFile.thumbnail.pathRetina = path.basename(thumbnailPathRetina);\n // Preserve baseUrl from existing thumbnail if it exists\n if (mediaFile.thumbnail?.baseUrl) {\n updatedMediaFile.thumbnail.baseUrl = mediaFile.thumbnail.baseUrl;\n }\n }\n\n if (mediaFile.url) {\n updatedMediaFile.url = mediaFile.url;\n }\n\n return updatedMediaFile;\n } catch (error) {\n handleFileProcessingError(error, mediaFile.filename, ui);\n\n return { ...mediaFile, thumbnail: undefined };\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 galleryData = parseGalleryJson(galleryJsonPath, ui);\n\n const thumbnailSize = galleryData.thumbnailSize || DEFAULT_THUMBNAIL_SIZE;\n\n // If the mediaBasePath is not set, use the gallery directory\n const mediaBasePath = galleryData.mediaBasePath ?? path.join(galleryDir);\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, mediaBasePath, 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<CommandResultSummary> {\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 { processedGalleryCount: 0, processedMediaCount: 0 };\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\n return { processedGalleryCount: totalGalleries, processedMediaCount: totalProcessed };\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 { LogLevels, type ConsolaInstance } from 'consola';\n\nimport {\n cleanupOldHeaderImages,\n createGallerySocialMediaCardImage,\n createOptimizedHeaderImage,\n hasOldHeaderImages,\n} from './utils';\n\nimport { findGalleries } from '../../utils';\nimport { parseGalleryJson } from '../../utils/gallery';\nimport { scanDirectory } from '../init';\nimport { processGalleryThumbnails } from '../thumbnails';\n\nimport type { BuildOptions } from './types';\nimport type { CommandResultSummary } from '../telemetry/types';\nimport type { GalleryData } from '@simple-photo-gallery/common';\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 (galleryData.mediaBasePath) {\n const sourcePath = path.join(galleryData.mediaBasePath, image.filename);\n const destPath = path.join(galleryDir, image.filename);\n\n ui.debug(`Copying photo to ${destPath}`);\n fs.copyFileSync(sourcePath, destPath);\n }\n }\n }\n}\n\n/**\n * Scans a directory for new media files and appends them to the gallery.json\n * @param galleryDir - Directory containing the gallery\n * @param galleryJsonPath - Path to the gallery.json file\n * @param galleryData - Current gallery data\n * @param ui - ConsolaInstance for logging\n * @returns Updated gallery data with new files appended\n */\nasync function scanAndAppendNewFiles(\n galleryDir: string,\n galleryJsonPath: string,\n galleryData: GalleryData,\n ui: ConsolaInstance,\n): Promise<GalleryData> {\n // Determine the directory to scan based on mediaBasePath\n const scanPath = galleryData.mediaBasePath || galleryDir;\n\n ui.debug(`Scanning ${scanPath} for new media files`);\n\n // Use the scanDirectory function from init module to get all media files\n let scanResult;\n try {\n scanResult = await scanDirectory(scanPath, ui);\n } catch {\n ui.debug(`Could not scan directory ${scanPath}`);\n return galleryData;\n }\n\n // Get all existing filenames from all sections\n const existingFilenames = new Set<string>(\n galleryData.sections.flatMap((section) => section.images.map((image) => image.filename)),\n );\n\n // Filter out files that already exist in the gallery\n const newMediaFiles = scanResult.mediaFiles.filter((file) => !existingFilenames.has(file.filename));\n\n // If there are new files, append them to the last section\n if (newMediaFiles.length > 0) {\n ui.info(`Found ${newMediaFiles.length} new media ${newMediaFiles.length === 1 ? 'file' : 'files'}`);\n\n // Get the last section (or create one if no sections exist)\n if (galleryData.sections.length === 0) {\n galleryData.sections.push({ images: [] });\n }\n\n const lastSectionIndex = galleryData.sections.length - 1;\n const lastSection = galleryData.sections[lastSectionIndex];\n\n // Append new files to the last section\n lastSection.images.push(...newMediaFiles);\n\n // Save the updated gallery.json\n ui.debug('Updating gallery.json with new files');\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n ui.success(`Added ${newMediaFiles.length} new ${newMediaFiles.length === 1 ? 'file' : 'files'} to gallery.json`);\n } else {\n ui.debug('No new media files found');\n }\n\n return galleryData;\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 scan - Whether to scan for new media files\n * @param shouldCreateThumbnails - Whether to create thumbnails\n * @param ui - ConsolaInstance for logging\n * @param baseUrl - Optional base URL for hosting photos\n * @param thumbsBaseUrl - Optional base URL for hosting thumbnails\n */\nasync function buildGallery(\n galleryDir: string,\n templateDir: string,\n scan: boolean,\n shouldCreateThumbnails: boolean,\n ui: ConsolaInstance,\n baseUrl?: string,\n thumbsBaseUrl?: string,\n): Promise<void> {\n ui.start(`Building gallery ${galleryDir}`);\n\n // Read the gallery.json file\n const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');\n let galleryData = parseGalleryJson(galleryJsonPath, ui);\n\n // Scan for new media files and append them to the gallery.json\n if (scan) {\n galleryData = await scanAndAppendNewFiles(galleryDir, galleryJsonPath, galleryData, ui);\n }\n\n const socialMediaCardImagePath = path.join(galleryDir, 'gallery', 'images', 'social-media-card.jpg');\n const mediaBasePath = galleryData.mediaBasePath;\n const mediaBaseUrl = baseUrl || galleryData.mediaBaseUrl;\n const headerImagePath = mediaBasePath\n ? path.join(mediaBasePath, galleryData.headerImage)\n : path.resolve(galleryDir, galleryData.headerImage);\n\n const imagesFolder = path.join(galleryDir, 'gallery', 'images');\n const currentHeaderBasename = path.basename(headerImagePath, path.extname(headerImagePath));\n\n if (shouldCreateThumbnails) {\n // Create the images folder if it doesn't exist\n if (!fs.existsSync(imagesFolder)) {\n fs.mkdirSync(imagesFolder, { recursive: true });\n }\n\n // Check if header image has changed by looking for old header images\n const headerImageChanged = hasOldHeaderImages(imagesFolder, currentHeaderBasename);\n\n if (headerImageChanged) {\n ui.info('Header image changed, cleaning up old assets');\n\n // Clean up old header images\n cleanupOldHeaderImages(imagesFolder, currentHeaderBasename, ui);\n\n // Delete old social media card since header image changed\n if (fs.existsSync(socialMediaCardImagePath)) {\n fs.unlinkSync(socialMediaCardImagePath);\n ui.debug('Deleted old social media card');\n }\n }\n\n // Create the gallery social media card image\n await createGallerySocialMediaCardImage(headerImagePath, galleryData.title, socialMediaCardImagePath, ui);\n\n // Create optimized header image and generate blurhash\n const { blurHash } = await createOptimizedHeaderImage(headerImagePath, imagesFolder, ui);\n\n // Save the blurhash to gallery.json if it changed\n if (galleryData.headerImageBlurHash !== blurHash) {\n ui.debug('Updating gallery.json with header image blurhash');\n galleryData.headerImageBlurHash = blurHash;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n }\n\n // Ask the user if the photos should be copied if there is not baseUrl and mediaBasePath is set\n if (!mediaBaseUrl && mediaBasePath) {\n const shouldCopyPhotos = await ui.prompt('All photos need to be copied. Are you sure you want to continue?', {\n type: 'confirm',\n });\n\n if (shouldCopyPhotos) {\n ui.debug('Copying photos');\n copyPhotos(galleryData, galleryDir, ui);\n }\n }\n\n // If the baseUrl is provided, update the gallery.json file if needed\n if (mediaBaseUrl && galleryData.mediaBaseUrl !== mediaBaseUrl) {\n ui.debug('Updating gallery.json with baseUrl');\n galleryData.mediaBaseUrl = mediaBaseUrl;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n\n // If the thumbsBaseUrl is provided, update the gallery.json file if needed\n if (thumbsBaseUrl && galleryData.thumbsBaseUrl !== thumbsBaseUrl) {\n ui.debug('Updating gallery.json with thumbsBaseUrl');\n galleryData.thumbsBaseUrl = thumbsBaseUrl;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n\n // Set the social media card URL if changed\n\n if (!galleryData.metadata.image) {\n ui.debug('Updating gallery.json with social media card URL');\n\n galleryData.metadata.image = thumbsBaseUrl\n ? `${thumbsBaseUrl}/${path.basename(socialMediaCardImagePath)}`\n : `${galleryData.url || ''}/${path.relative(galleryDir, socialMediaCardImagePath)}`;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n\n // Generate the thumbnails if needed\n if (shouldCreateThumbnails) {\n await processGalleryThumbnails(galleryDir, ui);\n }\n\n // Build the template\n ui.debug('Building gallery from 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<CommandResultSummary> {\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 { processedGalleryCount: 0 };\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 const thumbsBaseUrl = options.thumbsBaseUrl\n ? `${options.thumbsBaseUrl}${path.relative(options.gallery, dir)}`\n : undefined;\n\n await buildGallery(path.resolve(dir), themeDir, options.scan, options.thumbnails, ui, baseUrl, thumbsBaseUrl);\n\n ++totalGalleries;\n }\n\n ui.box(`Built ${totalGalleries} ${totalGalleries === 1 ? 'gallery' : 'galleries'} successfully`);\n\n return { processedGalleryCount: totalGalleries };\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 { CommandResultSummary } from '../telemetry/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<CommandResultSummary> {\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 return { processedGalleryCount: filesRemoved };\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<CommandResultSummary> {\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 { processedGalleryCount: 0 };\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 { processedGalleryCount: 0 };\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\n return { processedGalleryCount: galleryDirs.length };\n } catch (error) {\n ui.error('Error cleaning galleries');\n throw error;\n }\n}\n","import type { TelemetryService } from './service';\nimport type { CommandResultSummary, TelemetryOptions } from './types';\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Handles the `gallery telemetry` command allowing users to configure telemetry preferences.\n */\nexport async function telemetry(\n options: TelemetryOptions,\n ui: ConsolaInstance,\n telemetryService: TelemetryService,\n): Promise<CommandResultSummary> {\n const updates: CommandResultSummary = {};\n\n // Return the status if no state is provided\n if (options.state === undefined) {\n const current = telemetryService.getStoredPreference();\n if (current === undefined) {\n ui.info('Telemetry preference not set yet. It will be requested on next command run.');\n updates.telemetryStatus = 'unset';\n } else {\n ui.info(`Telemetry is currently ${current ? 'enabled' : 'disabled'}.`);\n updates.telemetryEnabled = current;\n }\n } else {\n // Set the telemetry preference\n telemetryService.setStoredPreference(options.state === '1');\n ui.success(`Anonymous telemetry ${options.state === '1' ? 'enabled' : 'disabled'}.`);\n updates.telemetryEnabled = options.state === '1';\n }\n\n return updates;\n}\n","import process from 'node:process';\n\nimport axios from 'axios';\n\nimport type { TelemetryClient, TelemetryEvent } from '../types';\n\n/**\n * Telemetry client that sends events to the Simple Photo Gallery telemetry API.\n */\nexport class ApiTelemetryClient implements TelemetryClient {\n private readonly endpoint = 'https://tools.simple.photo/api/telemetry';\n\n async record(event: TelemetryEvent): Promise<void> {\n try {\n axios.post(this.endpoint, event, {\n headers: {\n 'content-type': 'application/json',\n 'user-agent': `simple-photo-gallery/${event.packageVersion} (${process.platform}; ${process.arch})`,\n },\n });\n } catch {\n // Swallow network errors - telemetry must never interrupt the CLI flow.\n }\n }\n}\n","import { stdout } from 'node:process';\n\nimport type { TelemetryClient, TelemetryEvent } from '../types';\n\n/**\n * Telemetry client that prints events to the console.\n * Useful for local development and testing without sending network requests.\n */\nexport class ConsoleTelemetryClient implements TelemetryClient {\n async record(event: TelemetryEvent): Promise<void> {\n const serialized = JSON.stringify(event, null, 2);\n\n stdout.write(`TELEMETRY EVENT: ${serialized}\\n`);\n }\n}\n","import os from 'node:os';\nimport process from 'node:process';\n\nimport Conf from 'conf';\n\nimport { ApiTelemetryClient } from '../clients/api';\nimport { ConsoleTelemetryClient } from '../clients/console';\n\nimport type { CommandResultSummary, TelemetryClient, TelemetryConfigSchema, TelemetryEvent } from '../types';\nimport type { ConsolaInstance } from 'consola';\n\nconst CONFIG_KEY = 'telemetryEnabled';\n\ninterface BuildEventParams {\n command: string;\n argumentsProvided: string[];\n globalOptionsProvided: string[];\n metrics?: CommandResultSummary;\n success: boolean;\n error?: { name: string; message: string };\n startedAt: number;\n}\n\n/**\n * Manages telemetry configuration, consent and dispatching events to a concrete client.\n */\nexport class TelemetryService {\n private readonly config: Conf<TelemetryConfigSchema>;\n private readonly packageName: string;\n private readonly packageVersion: string;\n private readonly ui: ConsolaInstance;\n\n private client?: TelemetryClient;\n\n constructor(packageName: string, packageVersion: string, ui: ConsolaInstance) {\n this.packageName = packageName;\n this.packageVersion = packageVersion;\n this.ui = ui;\n this.config = new Conf<TelemetryConfigSchema>({ projectName: packageName });\n }\n\n /** Returns the stored telemetry preference, if any. */\n getStoredPreference(): boolean | undefined {\n return this.config.get(CONFIG_KEY);\n }\n\n /** Updates the persisted telemetry preference. */\n setStoredPreference(enabled: boolean): void {\n this.config.set(CONFIG_KEY, enabled);\n }\n\n /** Determines whether telemetry should be collected for this run. */\n async shouldCollectTelemetry(override?: '0' | '1'): Promise<boolean> {\n /** Command-level override */\n if (override) {\n return override === '1';\n }\n\n /** CI or DO_NOT_TRACK environment variables */\n if (process.env.CI || process.env.DO_NOT_TRACK) {\n return false;\n }\n\n /** SPG_TELEMETRY environment variable */\n if (process.env.SPG_TELEMETRY) {\n return process.env.SPG_TELEMETRY === '1';\n }\n\n /** Stored preference */\n const stored = this.getStoredPreference();\n\n if (stored === undefined) {\n // Ask the user if no consent was given yet\n const consent = await this.promptForConsent();\n this.setStoredPreference(consent);\n return consent;\n } else {\n // Return the stored preference\n return stored;\n }\n }\n\n /** Builds and dispatches a telemetry event when telemetry is enabled. */\n async emit(params: BuildEventParams): Promise<void> {\n const event = this.buildEvent(params);\n\n try {\n const client = await this.getClient();\n\n if (client) {\n await client.record(event);\n }\n } catch (error) {\n this.ui.debug('Error recording telemetry event', error);\n }\n }\n\n /** Builds a telemetry event. */\n private buildEvent({\n command,\n argumentsProvided,\n globalOptionsProvided,\n metrics,\n success,\n error,\n startedAt,\n }: BuildEventParams): TelemetryEvent {\n const now = Date.now();\n\n const event: TelemetryEvent = {\n command,\n argumentsProvided,\n globalOptionsProvided,\n timestamp: new Date(now).toISOString(),\n durationMs: now - startedAt,\n packageName: this.packageName,\n packageVersion: this.packageVersion,\n nodeVersion: process.version,\n osPlatform: os.platform(),\n osRelease: os.release(),\n osArch: os.arch(),\n result: success ? 'success' : 'error',\n };\n\n if (metrics && Object.keys(metrics).length > 0) {\n event.metrics = metrics;\n }\n\n if (!success && error) {\n event.errorName = error.name;\n event.errorMessage = error.message;\n }\n\n return event;\n }\n\n /** Prompts the user for consent to collect telemetry. */\n private async promptForConsent(): Promise<boolean> {\n this.ui.info(\n 'Simple Photo Gallery collects anonymous usage telemetry to improve the CLI. No personal data or information about your photos is collected. For more information, see https://simple.photo/telemetry.',\n );\n\n const answer = await this.ui.prompt('Would you like to enable telemetry?', {\n type: 'confirm',\n initial: true,\n });\n\n if (!answer) {\n this.ui.info('Anonymous telemetry disabled. You can re-enable it with \"spg telemetry 1\".');\n return false;\n }\n\n this.ui.success('Thank you! Telemetry will help us improve Simple Photo Gallery.');\n return true;\n }\n\n /** Returns the telemetry client. */\n private getClient(): TelemetryClient | undefined {\n if (!this.client) {\n // Create the client based on the environment variable\n switch (process.env.SPG_TELEMETRY_PROVIDER) {\n case 'none': {\n this.client = undefined;\n break;\n }\n case 'console': {\n this.client = new ConsoleTelemetryClient();\n break;\n }\n case 'api': {\n this.client = new ApiTelemetryClient();\n break;\n }\n default: {\n this.client = new ApiTelemetryClient();\n break;\n }\n }\n }\n\n return this.client;\n }\n}\n","import axios from 'axios';\nimport { compareSemVer, parseSemVer } from 'semver-parser';\n\nimport type { ConsolaInstance } from 'consola';\n\nconst NPM_REGISTRY_URL = 'https://registry.npmjs.org';\nconst CHECK_TIMEOUT_MS = 3000; // 3 seconds timeout for the check\n\ninterface PackageInfo {\n versions: Record<string, unknown>;\n}\n\n/**\n * Fetches the latest stable version of a package from npm registry\n * Uses abbreviated metadata for faster response times\n * @param packageName - Name of the package to check\n * @returns Promise resolving to the latest stable version or undefined if error\n */\nasync function fetchLatestStableVersion(packageName: string): Promise<string | undefined> {\n try {\n // Use abbreviated metadata endpoint for faster response\n const response = await axios.get<PackageInfo>(`${NPM_REGISTRY_URL}/${packageName}`, {\n timeout: CHECK_TIMEOUT_MS,\n headers: {\n Accept: 'application/vnd.npm.install-v1+json',\n },\n });\n\n // Get all version numbers\n const versions = Object.keys(response.data.versions);\n\n // Filter to only stable versions (no pre-release identifiers)\n const stableVersions = versions.filter((version) => {\n try {\n const parsed = parseSemVer(version);\n return !parsed.pre || parsed.pre.length === 0;\n } catch {\n return false;\n }\n });\n\n if (stableVersions.length === 0) {\n return undefined;\n }\n\n // Sort versions and get the latest stable one\n stableVersions.sort(compareSemVer);\n\n // eslint-disable-next-line unicorn/prefer-at\n return stableVersions[stableVersions.length - 1];\n } catch {\n // Silently fail on any error (network, timeout, etc.)\n return undefined;\n }\n}\n\n/**\n * Starts a background check for package updates\n * Returns a promise that resolves to update info or null\n * Only returns info if the latest stable version is newer than current\n * @param packageName - Name of the package\n * @param currentVersion - Current version of the package\n * @returns Promise resolving to update info or null\n */\nexport function checkForUpdates(\n packageName: string,\n currentVersion: string,\n): Promise<{ currentVersion: string; latestVersion: string } | null> {\n return fetchLatestStableVersion(packageName).then((latestStableVersion) => {\n if (!latestStableVersion) {\n return null;\n }\n\n try {\n // Check if the latest stable version is newer than current\n if (compareSemVer(latestStableVersion, currentVersion) > 0) {\n return { currentVersion, latestVersion: latestStableVersion };\n }\n } catch {\n // If semver parsing fails, silently ignore\n }\n\n return null;\n });\n}\n\n/**\n * Displays an update notification box if an update is available\n * @param updateInfo - Information about the available update\n * @param ui - Consola UI instance for displaying the message\n */\nexport function displayUpdateNotification(\n updateInfo: { currentVersion: string; latestVersion: string },\n ui: ConsolaInstance,\n): void {\n const { currentVersion, latestVersion } = updateInfo;\n\n const message = [\n `New version available: ${currentVersion} → ${latestVersion}`,\n '',\n 'Run the following commands to update:',\n '',\n ' npm install -g simple-photo-gallery@latest',\n ].join('\\n');\n\n ui.box(message);\n}\n\n/**\n * Waits for the update check promise with a timeout\n * If the check is not complete within the timeout, returns null\n * @param checkPromise - Promise from checkForUpdates\n * @returns Promise resolving to update info or null\n */\nexport async function waitForUpdateCheck(\n checkPromise: Promise<{ currentVersion: string; latestVersion: string } | null>,\n): Promise<{ currentVersion: string; latestVersion: string } | null> {\n try {\n let timeoutId: ReturnType<typeof setTimeout>;\n const timeoutPromise = new Promise<null>((resolve) => {\n timeoutId = setTimeout(() => resolve(null), 5000);\n });\n\n const result = await Promise.race([checkPromise, timeoutPromise]);\n clearTimeout(timeoutId!);\n return result;\n } catch {\n // Silently fail on any error\n return null;\n }\n}\n","{\n \"name\": \"simple-photo-gallery\",\n \"version\": \"2.0.18\",\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 \"main\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n },\n \"./lib\": {\n \"types\": \"./dist/lib/index.d.ts\",\n \"default\": \"./dist/lib/index.js\"\n },\n \"./lib/browser\": {\n \"types\": \"./dist/lib/browser.d.ts\",\n \"default\": \"./dist/lib/browser.js\"\n }\n },\n \"scripts\": {\n \"gallery\": \"SPG_TELEMETRY_PROVIDER=none 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 \"test:coverage\": \"jest --coverage\",\n \"prepublish\": \"yarn build\"\n },\n \"dependencies\": {\n \"@simple-photo-gallery/common\": \"1.0.6\",\n \"@simple-photo-gallery/theme-modern\": \"2.0.18\",\n \"axios\": \"^1.12.2\",\n \"blurhash\": \"^2.0.5\",\n \"commander\": \"^12.0.0\",\n \"conf\": \"^13.0.1\",\n \"consola\": \"^3.4.2\",\n \"exifreader\": \"^4.32.0\",\n \"node-ffprobe\": \"^3.0.0\",\n \"semver-parser\": \"^4.1.8\",\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 \"@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 { telemetry } from './modules/telemetry';\nimport { TelemetryService } from './modules/telemetry/service';\nimport { thumbnails } from './modules/thumbnails';\nimport { parseTelemetryOption } from './utils';\nimport { checkForUpdates, displayUpdateNotification, waitForUpdateCheck } from './utils/version';\n\nimport packageJson from '../package.json' with { type: 'json' };\n\nimport type { CommandResultSummary } from './modules/telemetry/types';\nimport type { TelemetryOption } from './types';\nimport type { Command as CommanderCommand } from 'commander';\n\n/** Command line interface program instance */\nconst program = new Command();\n\nconst telemetryService = new TelemetryService(packageJson.name, packageJson.version, createConsolaUI(program.opts()));\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 .option('--telemetry <state>', 'Enable (1) or disable (0) telemetry for this command', parseTelemetryOption)\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 * Collects the command arguments\n * @param command - The commander command\n * @returns The command arguments\n */\nfunction collectCommandArguments(command: CommanderCommand): string[] {\n return command.options\n .map((option) => ({ name: option.attributeName(), source: command.getOptionValueSource(option.attributeName()) }))\n .filter((option) => option.source && option.source !== 'default')\n .map((option) => option.name);\n}\n\n/**\n * Collects the global arguments\n * @returns The global arguments\n */\nfunction collectGlobalArguments(): string[] {\n return program.options\n .map((option) => ({ name: option.attributeName(), source: program.getOptionValueSource(option.attributeName()) }))\n .filter((option) => option.source && option.source !== 'default')\n .map((option) => option.name);\n}\n\n/**\n * Higher-order function that wraps command handlers to provide Consola UI and telemetry instrumentation.\n * @param handler - Command handler function that receives options, UI instance and commander command\n * @returns Wrapped handler function that creates UI, handles errors and records telemetry\n */\nfunction withCommandContext<O>(\n handler: (\n opts: O,\n ui: ConsolaInstance,\n command: CommanderCommand,\n ) => Promise<CommandResultSummary | void> | CommandResultSummary | void,\n) {\n return async (rawOpts: O & TelemetryOption, command: CommanderCommand) => {\n const { telemetry: telemetryOverride, ...commandOptions } = rawOpts as O & TelemetryOption;\n\n // Create the Consola UI instance\n const ui = createConsolaUI(program.opts());\n\n // Determine if telemetry should be collected\n const shouldCollectTelemetry = await telemetryService.shouldCollectTelemetry(telemetryOverride);\n\n // Get the started at timestamp\n const startedAt = Date.now();\n\n // Start updates check\n const updateCheckPromise = checkForUpdates(packageJson.name, packageJson.version);\n\n // Run the command handler\n let success = false;\n let metrics: CommandResultSummary | undefined;\n let errorInfo: { name: string; message: string } | undefined;\n try {\n const result = await handler(commandOptions as O, ui, command);\n if (result && Object.keys(result).length > 0) {\n metrics = result;\n }\n\n success = true;\n } catch (error) {\n ui.debug(error);\n\n errorInfo =\n error instanceof Error\n ? { name: error.name, message: error.message }\n : { name: 'UnknownError', message: String(error) };\n\n process.exitCode = 1;\n }\n\n // Wait for the updates check\n const updateInfo = await waitForUpdateCheck(updateCheckPromise);\n if (updateInfo) {\n displayUpdateNotification(updateInfo, ui);\n }\n\n if (shouldCollectTelemetry) {\n await telemetryService.emit({\n command: command.name(),\n argumentsProvided: collectCommandArguments(command),\n globalOptionsProvided: collectGlobalArguments(),\n metrics,\n success,\n error: errorInfo,\n startedAt,\n });\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 .option('-f, --force', 'Force override existing galleries without asking', false)\n .option('--cta-banner', 'Add a Simple Photo Gallery call-to-action banner to the end of the gallery', false)\n .action(withCommandContext((options, ui) => init(options, ui)));\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(withCommandContext((options, ui) => thumbnails(options, ui)));\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 .option('-t, --thumbs-base-url <url>', 'Base URL where the thumbnails are hosted')\n .option('--no-thumbnails', 'Skip creating thumbnails when building the gallery', true)\n .option('--no-scan', 'Do not scan for new photos when building the gallery', true)\n .action(withCommandContext((options, ui) => build(options, ui)));\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(withCommandContext((options, ui) => clean(options, ui)));\n\nprogram\n .command('telemetry')\n .description('Manage anonymous telemetry preferences. Use 1 to enable, 0 to disable, or no argument to check status')\n .option('-s, --state <state>', 'Enable (1) or disable (0) telemetry', parseTelemetryOption)\n .action(withCommandContext((options, ui) => telemetry(options, ui, telemetryService)));\n\nprogram.parse();\n"]}