simple-photo-gallery 0.0.4 → 0.0.6

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/src/index.js DELETED
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env node
2
- import process from 'node:process';
3
- import { Command } from 'commander';
4
- import { build } from './modules/build';
5
- import { init } from './modules/init';
6
- import { thumbnails } from './modules/thumbnails';
7
- const program = new Command();
8
- program.name('gallery').description('Simple Photo Gallery CLI').version('0.0.1');
9
- program
10
- .command('init')
11
- .description('Initialize a gallery by scaning a folder for images and videos')
12
- .option('-p, --path <path>', 'Path where the gallery should be initialized. Default: current working directory', process.cwd())
13
- .option('-o, --output <path>', 'Output directory for the gallery.json file', '')
14
- .option('-r, --recursive', 'Scan subdirectories recursively', false)
15
- .action(init);
16
- program
17
- .command('thumbnails')
18
- .description('Create thumbnails for all media files in the gallery')
19
- .option('-p, --path <path>', 'Path to the folder containing the gallery.json file. Default: current working directory', process.cwd())
20
- .option('-s, --size <size>', 'Thumbnail height in pixels', '200')
21
- .option('-r, --recursive', 'Scan subdirectories recursively', false)
22
- .action(thumbnails);
23
- program
24
- .command('build')
25
- .description('Build the HTML gallery in the specified directory')
26
- .option('-p, --path <path>', 'Path to the folder containing the gallery.json file. Default: current working directory', process.cwd())
27
- .option('-r, --recursive', 'Scan subdirectories recursively', false)
28
- .action(build);
29
- program.parse();
@@ -1,57 +0,0 @@
1
- import { execSync } from 'node:child_process';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import process from 'node:process';
5
- import { findGalleries } from '../../utils';
6
- function buildGallery(galleryDir, templateDir) {
7
- // Make sure the gallery.json file exists
8
- const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');
9
- if (!fs.existsSync(galleryJsonPath)) {
10
- console.log(`No gallery/gallery.json found in ${galleryDir}`);
11
- return;
12
- }
13
- // Build the template
14
- const originalEnv = { ...process.env };
15
- try {
16
- // Set the environment variable for the gallery.json path that will be used by the template
17
- process.env.GALLERY_JSON_PATH = galleryJsonPath;
18
- process.env.GALLERY_OUTPUT_DIR = path.join(galleryDir, 'gallery');
19
- execSync('yarn build', { cwd: templateDir, stdio: 'inherit' });
20
- }
21
- catch (error) {
22
- console.error(error);
23
- console.error(`Build failed for ${galleryDir}`);
24
- return;
25
- }
26
- finally {
27
- // Restore original environment and gallery.json
28
- process.env = originalEnv;
29
- }
30
- // Copy the build output to the output directory
31
- const outputDir = path.join(galleryDir, 'gallery');
32
- const buildDir = path.join(outputDir, '_build');
33
- fs.cpSync(buildDir, outputDir, { recursive: true });
34
- // Move the index.html to the gallery directory
35
- fs.copyFileSync(path.join(outputDir, 'index.html'), path.join(galleryDir, 'index.html'));
36
- fs.rmSync(path.join(outputDir, 'index.html'));
37
- // Clean up the _build directory
38
- console.log('Cleaning up build directory...');
39
- fs.rmSync(buildDir, { recursive: true, force: true });
40
- }
41
- export async function build(options) {
42
- // Get the template directory
43
- // Resolve the theme-modern package directory
44
- const themePath = await import.meta.resolve('@simple-photo-gallery/theme-modern/package.json');
45
- const themeDir = path.dirname(new URL(themePath).pathname);
46
- // Find all gallery directories
47
- const galleryDirs = findGalleries(options.path, options.recursive);
48
- // If no galleries are found, exit
49
- if (galleryDirs.length === 0) {
50
- console.log('No gallery/gallery.json files found.');
51
- return;
52
- }
53
- // Process each gallery
54
- for (const dir of galleryDirs) {
55
- buildGallery(path.resolve(dir), themeDir);
56
- }
57
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,7 +0,0 @@
1
- import path from 'node:path';
2
- // __dirname workaround for ESM modules
3
- const __dirname = path.dirname(new URL(import.meta.url).pathname);
4
- // Helper function to resolve paths relative to current file
5
- export const resolveFromCurrentDir = (...segments) => {
6
- return path.resolve(__dirname, ...segments);
7
- };
@@ -1,4 +0,0 @@
1
- // Image extensions
2
- export const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.tiff', '.tif', '.svg']);
3
- // Video extensions
4
- export const VIDEO_EXTENSIONS = new Set(['.mp4', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv', '.m4v', '.3gp']);
@@ -1,156 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
- import path from 'node:path';
3
- import { capitalizeTitle, getImageMetadata, getVideoDimensions, isMediaFile } from './utils';
4
- async function scanDirectory(dirPath) {
5
- const mediaFiles = [];
6
- try {
7
- const entries = await fs.readdir(dirPath, { withFileTypes: true });
8
- for (const entry of entries) {
9
- if (entry.isFile()) {
10
- const fullPath = path.join(dirPath, entry.name);
11
- const mediaType = isMediaFile(entry.name);
12
- if (mediaType) {
13
- console.log(`Processing ${mediaType}: ${entry.name}`);
14
- let metadata = { width: 0, height: 0 };
15
- try {
16
- if (mediaType === 'image') {
17
- metadata = await getImageMetadata(fullPath);
18
- }
19
- else if (mediaType === 'video') {
20
- try {
21
- const videoDimensions = await getVideoDimensions(fullPath);
22
- metadata = { ...videoDimensions };
23
- }
24
- catch (videoError) {
25
- if (typeof videoError === 'object' &&
26
- videoError !== null &&
27
- 'message' in videoError &&
28
- typeof videoError.message === 'string' &&
29
- (videoError.message.includes('ffprobe') ||
30
- videoError.message.includes('ffmpeg'))) {
31
- console.error(`Error: ffprobe (part of ffmpeg) is required to process videos. Please install ffmpeg and ensure it is available in your PATH. Skipping video: ${entry.name}`);
32
- }
33
- else {
34
- console.error(`Error processing video ${entry.name}:`, videoError);
35
- }
36
- continue; // Skip this file
37
- }
38
- }
39
- }
40
- catch (mediaError) {
41
- console.error(`Error processing file ${entry.name}:`, mediaError);
42
- continue; // Skip this file
43
- }
44
- const mediaFile = {
45
- type: mediaType,
46
- path: fullPath,
47
- width: metadata.width,
48
- height: metadata.height,
49
- };
50
- if (metadata.description) {
51
- mediaFile.alt = metadata.description;
52
- }
53
- mediaFiles.push(mediaFile);
54
- }
55
- }
56
- }
57
- }
58
- catch (error) {
59
- console.error(`Error scanning directory ${dirPath}:`, error);
60
- }
61
- return mediaFiles;
62
- }
63
- async function createGalleryJson(mediaFiles, galleryJsonPath, subGalleries = []) {
64
- const galleryDir = path.dirname(galleryJsonPath);
65
- // Convert media file paths to be relative to gallery.json
66
- const relativeMediaFiles = mediaFiles.map((file) => ({
67
- ...file,
68
- path: path.relative(galleryDir, file.path),
69
- }));
70
- // Convert subGallery header image paths to be relative to gallery.json
71
- const relativeSubGalleries = subGalleries.map((subGallery) => ({
72
- ...subGallery,
73
- headerImage: subGallery.headerImage ? path.relative(galleryDir, subGallery.headerImage) : '',
74
- }));
75
- const galleryData = {
76
- title: 'My Gallery',
77
- description: 'My gallery with fantastic photos.',
78
- headerImage: relativeMediaFiles[0]?.path || '',
79
- metadata: { ogUrl: '' },
80
- sections: [
81
- {
82
- images: relativeMediaFiles,
83
- },
84
- ],
85
- subGalleries: {
86
- title: 'Sub Galleries',
87
- galleries: relativeSubGalleries,
88
- },
89
- };
90
- await fs.writeFile(galleryJsonPath, JSON.stringify(galleryData, null, 2));
91
- }
92
- async function processDirectory(dirPath, options) {
93
- let totalFiles = 0;
94
- const subGalleries = [];
95
- // Scan current directory for media files
96
- const mediaFiles = await scanDirectory(dirPath);
97
- totalFiles += mediaFiles.length;
98
- // Process subdirectories only if recursive mode is enabled
99
- if (options.recursive) {
100
- try {
101
- const entries = await fs.readdir(dirPath, { withFileTypes: true });
102
- for (const entry of entries) {
103
- if (entry.isDirectory() && entry.name !== 'gallery') {
104
- const subDirPath = path.join(dirPath, entry.name);
105
- const result = await processDirectory(subDirPath, options);
106
- totalFiles += result.totalFiles;
107
- // If the subdirectory had media files, add it as a subGallery
108
- if (result.subGallery) {
109
- subGalleries.push(result.subGallery);
110
- }
111
- }
112
- }
113
- }
114
- catch (error) {
115
- console.error(`Error reading directory ${dirPath}:`, error);
116
- }
117
- }
118
- // Create gallery.json if there are media files or subGalleries
119
- if (mediaFiles.length > 0 || subGalleries.length > 0) {
120
- const outputPath = options.output
121
- ? path.resolve(options.output, path.relative(path.resolve(options.path), dirPath), 'gallery')
122
- : path.join(dirPath, 'gallery');
123
- const galleryJsonPath = path.join(outputPath, 'gallery.json');
124
- // Create output directory
125
- await fs.mkdir(outputPath, { recursive: true });
126
- // Create gallery.json for this directory
127
- await createGalleryJson(mediaFiles, galleryJsonPath, subGalleries);
128
- console.log(`Gallery JSON created at: ${galleryJsonPath} (${mediaFiles.length} files, ${subGalleries.length} subgalleries)`);
129
- }
130
- // Return result with suGgallery info if this directory has media files
131
- const result = { totalFiles };
132
- if (mediaFiles.length > 0 || subGalleries.length > 0) {
133
- const dirName = path.basename(dirPath);
134
- result.subGallery = {
135
- title: capitalizeTitle(dirName),
136
- headerImage: mediaFiles[0]?.path || '',
137
- path: path.join('..', dirName),
138
- };
139
- }
140
- return result;
141
- }
142
- export async function init(options) {
143
- const scanPath = path.resolve(options.path);
144
- console.log(`Scanning directory: ${scanPath}`);
145
- console.log(`Recursive: ${options.recursive}`);
146
- try {
147
- // Ensure scan path exists
148
- await fs.access(scanPath);
149
- // Process the directory tree with the specified recursion setting
150
- const result = await processDirectory(scanPath, options);
151
- console.log(`Total files processed: ${result.totalFiles}`);
152
- }
153
- catch (error) {
154
- throw new Error(`Error during scan: ${error}`);
155
- }
156
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,93 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
- import path from 'node:path';
3
- import exifReader from 'exif-reader';
4
- import ffprobe from 'node-ffprobe';
5
- import sharp from 'sharp';
6
- import { IMAGE_EXTENSIONS, VIDEO_EXTENSIONS } from '../const';
7
- export async function getImageMetadata(filePath) {
8
- try {
9
- const metadata = await sharp(filePath).metadata();
10
- let description;
11
- // Extract description from EXIF data
12
- if (metadata.exif) {
13
- try {
14
- const exifData = exifReader(metadata.exif);
15
- if (exifData.Image?.ImageDescription) {
16
- description = exifData.Image.ImageDescription;
17
- }
18
- else if (exifData.Image?.Description) {
19
- description = exifData.Image.Description.toString();
20
- }
21
- }
22
- catch {
23
- // EXIF parsing failed, but that's OK
24
- }
25
- }
26
- return {
27
- width: metadata.width || 0,
28
- height: metadata.height || 0,
29
- description,
30
- };
31
- }
32
- catch (error) {
33
- console.warn(`Warning: Could not get metadata for image ${filePath}:`, error);
34
- return { width: 0, height: 0 };
35
- }
36
- }
37
- export async function getVideoDimensions(filePath) {
38
- try {
39
- const data = await ffprobe(filePath);
40
- const videoStream = data.streams.find((stream) => stream.codec_type === 'video');
41
- if (videoStream) {
42
- return {
43
- width: videoStream.width || 0,
44
- height: videoStream.height || 0,
45
- };
46
- }
47
- return { width: 0, height: 0 };
48
- }
49
- catch (error) {
50
- console.warn(`Warning: Could not get dimensions for video ${filePath}:`, error);
51
- return { width: 0, height: 0 };
52
- }
53
- }
54
- export function isMediaFile(fileName) {
55
- const ext = path.extname(fileName).toLowerCase();
56
- if (IMAGE_EXTENSIONS.has(ext))
57
- return 'image';
58
- if (VIDEO_EXTENSIONS.has(ext))
59
- return 'video';
60
- return null;
61
- }
62
- export function capitalizeTitle(folderName) {
63
- return folderName
64
- .replace('-', ' ')
65
- .replace('_', ' ')
66
- .split(' ')
67
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
68
- .join(' ');
69
- }
70
- export async function hasMediaFiles(dirPath) {
71
- try {
72
- const entries = await fs.readdir(dirPath, { withFileTypes: true });
73
- // Check for media files in current directory
74
- for (const entry of entries) {
75
- if (entry.isFile() && isMediaFile(entry.name)) {
76
- return true;
77
- }
78
- }
79
- // Check subdirectories recursively
80
- for (const entry of entries) {
81
- if (entry.isDirectory() && entry.name !== 'gallery') {
82
- const subDirPath = path.join(dirPath, entry.name);
83
- if (await hasMediaFiles(subDirPath)) {
84
- return true;
85
- }
86
- }
87
- }
88
- }
89
- catch (error) {
90
- console.error(`Error checking for media files in ${dirPath}:`, error);
91
- }
92
- return false;
93
- }
@@ -1,98 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { createImageThumbnail, createVideoThumbnail } from './utils';
4
- import { GalleryDataSchema } from '../../types';
5
- import { findGalleries } from '../../utils';
6
- const processGallery = async (galleryDir, size) => {
7
- const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');
8
- const thumbnailsPath = path.join(galleryDir, 'gallery', 'thumbnails');
9
- console.log(`\nProcessing gallery in: ${galleryDir}`);
10
- try {
11
- // Ensure thumbnails directory exists
12
- fs.mkdirSync(thumbnailsPath, { recursive: true });
13
- // Read gallery.json
14
- const galleryContent = fs.readFileSync(galleryJsonPath, 'utf8');
15
- const galleryData = GalleryDataSchema.parse(JSON.parse(galleryContent));
16
- // Process all sections and their images
17
- let processedCount = 0;
18
- for (const section of galleryData.sections) {
19
- for (const [index, mediaFile] of section.images.entries()) {
20
- section.images[index] = await processMediaFile(mediaFile, galleryDir, thumbnailsPath, size);
21
- }
22
- processedCount += section.images.length;
23
- }
24
- // Write updated gallery.json
25
- fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
26
- return processedCount;
27
- }
28
- catch (error) {
29
- console.error(`Error creating thumbnails for ${galleryDir}:`, error);
30
- return 0;
31
- }
32
- };
33
- async function processMediaFile(mediaFile, galleryDir, thumbnailsPath, size) {
34
- try {
35
- // Resolve the path relative to the gallery.json file location, not the gallery directory
36
- const galleryJsonDir = path.join(galleryDir, 'gallery');
37
- const filePath = path.resolve(path.join(galleryJsonDir, mediaFile.path));
38
- const fileName = path.basename(filePath);
39
- const fileNameWithoutExt = path.parse(fileName).name;
40
- const thumbnailFileName = `${fileNameWithoutExt}.jpg`;
41
- const thumbnailPath = path.join(thumbnailsPath, thumbnailFileName);
42
- const relativeThumbnailPath = path.relative(galleryJsonDir, thumbnailPath);
43
- console.log(`Processing ${mediaFile.type}: ${fileName}`);
44
- let thumbnailDimensions;
45
- if (mediaFile.type === 'image') {
46
- thumbnailDimensions = await createImageThumbnail(filePath, thumbnailPath, size);
47
- }
48
- else if (mediaFile.type === 'video') {
49
- thumbnailDimensions = await createVideoThumbnail(filePath, thumbnailPath, size);
50
- }
51
- else {
52
- console.warn(`Unknown media type: ${mediaFile.type}, skipping...`);
53
- return mediaFile;
54
- }
55
- // Update media file with thumbnail information
56
- const updatedMediaFile = {
57
- ...mediaFile,
58
- thumbnail: {
59
- path: relativeThumbnailPath,
60
- width: thumbnailDimensions.width,
61
- height: thumbnailDimensions.height,
62
- },
63
- };
64
- return updatedMediaFile;
65
- }
66
- catch (error) {
67
- if (error instanceof Error && error.message === 'FFMPEG_NOT_AVAILABLE') {
68
- console.warn(`⚠ Skipping video thumbnail (ffmpeg not available): ${path.basename(mediaFile.path)}`);
69
- }
70
- else {
71
- console.error(`Error processing ${mediaFile.path}:`, error);
72
- }
73
- return mediaFile;
74
- }
75
- }
76
- export async function thumbnails(options) {
77
- const size = Number.parseInt(options.size);
78
- // Find all gallery directories
79
- const galleryDirs = findGalleries(options.path, options.recursive);
80
- // If no galleries are found, exit
81
- if (galleryDirs.length === 0) {
82
- console.log('No gallery/gallery.json files found.');
83
- return;
84
- }
85
- // Process each gallery directory
86
- let totalProcessed = 0;
87
- for (const galleryDir of galleryDirs) {
88
- const processed = await processGallery(galleryDir, size);
89
- totalProcessed += processed;
90
- }
91
- // Log processing stats
92
- if (options.recursive) {
93
- console.log(`Completed processing ${totalProcessed} total media files across ${galleryDirs.length} galleries.`);
94
- }
95
- else {
96
- console.log(`Completed processing ${totalProcessed} media files.`);
97
- }
98
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,127 +0,0 @@
1
- import { spawn } from 'node:child_process';
2
- import { promises as fs } from 'node:fs';
3
- import path from 'node:path';
4
- import ffprobe from 'node-ffprobe';
5
- import sharp from 'sharp';
6
- // Check if ffmpeg is available
7
- async function checkFfmpegAvailability() {
8
- return new Promise((resolve) => {
9
- const ffmpeg = spawn('ffmpeg', ['-version']);
10
- ffmpeg.on('close', (code) => {
11
- resolve(code === 0);
12
- });
13
- ffmpeg.on('error', () => {
14
- resolve(false);
15
- });
16
- });
17
- }
18
- // Utility function to resize and save thumbnail
19
- async function resizeAndSaveThumbnail(image, outputPath, width, height) {
20
- await image.resize(width, height, { withoutEnlargement: true }).jpeg({ quality: 90 }).toFile(outputPath);
21
- }
22
- export async function createImageThumbnail(inputPath, outputPath, height) {
23
- try {
24
- // Check if input file exists
25
- await fs.access(inputPath);
26
- // Create thumbnail using sharp
27
- const image = sharp(inputPath);
28
- const metadata = await image.metadata();
29
- const originalWidth = metadata.width || 0;
30
- const originalHeight = metadata.height || 0;
31
- if (originalWidth === 0 || originalHeight === 0) {
32
- throw new Error('Invalid image dimensions');
33
- }
34
- // Calculate width maintaining aspect ratio
35
- const aspectRatio = originalWidth / originalHeight;
36
- const width = Math.round(height * aspectRatio);
37
- await resizeAndSaveThumbnail(image, outputPath, width, height);
38
- return { width, height };
39
- }
40
- catch (error) {
41
- throw new Error(`Failed to create image thumbnail for ${inputPath}: ${error}`);
42
- }
43
- }
44
- export async function createVideoThumbnail(inputPath, outputPath, height) {
45
- try {
46
- // Check if input file exists
47
- await fs.access(inputPath);
48
- // Check if ffmpeg is available
49
- const ffmpegAvailable = await checkFfmpegAvailability();
50
- if (!ffmpegAvailable) {
51
- console.warn(`Warning: ffmpeg is not available. Skipping thumbnail creation for video: ${path.basename(inputPath)}`);
52
- throw new Error('FFMPEG_NOT_AVAILABLE');
53
- }
54
- // Get video metadata using ffprobe
55
- const videoData = await ffprobe(inputPath);
56
- const videoStream = videoData.streams.find((stream) => stream.codec_type === 'video');
57
- if (!videoStream) {
58
- throw new Error('No video stream found');
59
- }
60
- const originalWidth = videoStream.width || 0;
61
- const originalHeight = videoStream.height || 0;
62
- if (originalWidth === 0 || originalHeight === 0) {
63
- throw new Error('Invalid video dimensions');
64
- }
65
- // Calculate width maintaining aspect ratio
66
- const aspectRatio = originalWidth / originalHeight;
67
- const width = Math.round(height * aspectRatio);
68
- // Use ffmpeg to extract first frame as a temporary file, then process with sharp
69
- const tempFramePath = `${outputPath}.temp.png`;
70
- return new Promise((resolve, reject) => {
71
- // Extract first frame using ffmpeg
72
- const ffmpeg = spawn('ffmpeg', [
73
- '-i',
74
- inputPath,
75
- '-vframes',
76
- '1',
77
- '-y', // Overwrite output file
78
- tempFramePath,
79
- ]);
80
- ffmpeg.stderr.on('data', (data) => {
81
- // FFmpeg writes normal output to stderr, so we don't treat this as an error
82
- console.debug(`ffmpeg: ${data.toString()}`);
83
- });
84
- ffmpeg.on('close', async (code) => {
85
- if (code === 0) {
86
- try {
87
- // Process the extracted frame with sharp
88
- const frameImage = sharp(tempFramePath);
89
- await resizeAndSaveThumbnail(frameImage, outputPath, width, height);
90
- // Clean up temporary file
91
- try {
92
- await fs.unlink(tempFramePath);
93
- }
94
- catch {
95
- // Ignore cleanup errors
96
- }
97
- resolve({ width, height });
98
- }
99
- catch (sharpError) {
100
- reject(new Error(`Failed to process extracted frame: ${sharpError}`));
101
- }
102
- }
103
- else {
104
- reject(new Error(`ffmpeg exited with code ${code}`));
105
- }
106
- });
107
- ffmpeg.on('error', (error) => {
108
- reject(new Error(`Failed to start ffmpeg: ${error.message}`));
109
- });
110
- });
111
- }
112
- catch (error) {
113
- if (error instanceof Error && error.message === 'FFMPEG_NOT_AVAILABLE') {
114
- // Re-throw the specific error for graceful handling upstream
115
- throw error;
116
- }
117
- if (typeof error === 'object' &&
118
- error !== null &&
119
- 'message' in error &&
120
- typeof error.message === 'string' &&
121
- (error.message.includes('ffmpeg') ||
122
- error.message.includes('ffprobe'))) {
123
- throw new Error(`Error: ffmpeg is required to process videos. Please install ffmpeg and ensure it is available in your PATH. Failed to process: ${path.basename(inputPath)}`);
124
- }
125
- throw new Error(`Failed to create video thumbnail for ${inputPath}: ${error}`);
126
- }
127
- }
@@ -1,35 +0,0 @@
1
- import { z } from 'zod';
2
- export const ThumbnailSchema = z.object({
3
- path: z.string(),
4
- width: z.number(),
5
- height: z.number(),
6
- });
7
- export const MediaFileSchema = z.object({
8
- type: z.enum(['image', 'video']),
9
- path: z.string(),
10
- alt: z.string().optional(),
11
- width: z.number(),
12
- height: z.number(),
13
- thumbnail: ThumbnailSchema.optional(),
14
- });
15
- export const GallerySectionSchema = z.object({
16
- title: z.string().optional(),
17
- description: z.string().optional(),
18
- images: z.array(MediaFileSchema),
19
- });
20
- export const SubGallerySchema = z.object({
21
- title: z.string(),
22
- headerImage: z.string(),
23
- path: z.string(),
24
- });
25
- export const GalleryDataSchema = z.object({
26
- title: z.string(),
27
- description: z.string(),
28
- headerImage: z.string(),
29
- metadata: z.object({
30
- ogUrl: z.string(),
31
- }),
32
- galleryOutputPath: z.string().optional(),
33
- sections: z.array(GallerySectionSchema),
34
- subGalleries: z.object({ title: z.string(), galleries: z.array(SubGallerySchema) }),
35
- });
@@ -1,34 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- /**
4
- * Finds all gallery directories that contain a gallery/gallery.json file.
5
- *
6
- * @param basePath - The base directory to search from
7
- * @param recursive - Whether to search subdirectories recursively
8
- * @returns Array of paths to directories containing gallery/gallery.json files
9
- */
10
- export const findGalleries = (basePath, recursive) => {
11
- const galleryDirs = [];
12
- // Check basePath itself
13
- const galleryJsonPath = path.join(basePath, 'gallery', 'gallery.json');
14
- if (fs.existsSync(galleryJsonPath)) {
15
- galleryDirs.push(basePath);
16
- }
17
- // If recursive, search all subdirectories
18
- if (recursive) {
19
- try {
20
- const entries = fs.readdirSync(basePath, { withFileTypes: true });
21
- for (const entry of entries) {
22
- if (entry.isDirectory() && entry.name !== 'gallery') {
23
- const subPath = path.join(basePath, entry.name);
24
- const subResults = findGalleries(subPath, recursive);
25
- galleryDirs.push(...subResults);
26
- }
27
- }
28
- }
29
- catch {
30
- // Silently ignore errors when reading directories
31
- }
32
- }
33
- return galleryDirs;
34
- };