simple-photo-gallery 2.0.7 → 2.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -79,6 +79,21 @@ For advanced usage, customization, and deployment options, see the comprehensive
79
79
  - **[Gallery Configuration](./docs/configuration.md)** - Manual editing of `gallery.json` and advanced features like sections
80
80
  - **[Deployment Guide](./docs/deployment.md)** - Guidelines for hosting your gallery
81
81
 
82
+ ## Telemetry
83
+
84
+ Simple Photo Gallery collects optional anonymised usage telemetry to help us understand which features are the most helpful and to prioritise improvements. No personal data or information about your photos is collected. You need to opt-in the first time you use a command for telemetry to be collected. When the CI or DO_NOT_TRACK environment variables are set, telemetry is disabled.
85
+
86
+ You can manage telemetry at any time:
87
+
88
+ ```bash
89
+ spg telemetry -s 0 # opt out of telemetry
90
+ spg telemetry -s 1 # opt back in
91
+ ```
92
+
93
+ Every command also accepts `--telemetry 0` or `--telemetry 1` to temporarily override your global preference.
94
+
95
+ For more information about what data is collected please visit the [telemetry page](https://simple.photo/telemetry).
96
+
82
97
  ## Python Version
83
98
 
84
99
  The old Python version of Simple Photo Gallery V1 is still available [here](https://github.com/haltakov/simple-photo-gallery), but is now deprecated.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import process2 from 'process';
2
+ import process3, { stdout } from 'process';
3
3
  import { Command } from 'commander';
4
4
  import { LogLevels, createConsola } from 'consola';
5
5
  import { execSync, spawn } from 'child_process';
@@ -11,6 +11,10 @@ import sharp2 from 'sharp';
11
11
  import ExifReader from 'exifreader';
12
12
  import { encode } from 'blurhash';
13
13
  import ffprobe from 'node-ffprobe';
14
+ import os from 'os';
15
+ import Conf from 'conf';
16
+ import axios from 'axios';
17
+ import { compareSemVer, parseSemVer } from 'semver-parser';
14
18
 
15
19
  var ThumbnailSchema = z.object({
16
20
  path: z.string(),
@@ -62,6 +66,7 @@ var GalleryDataSchema = z.object({
62
66
  metadata: GalleryMetadataSchema,
63
67
  galleryOutputPath: z.string().optional(),
64
68
  mediaBaseUrl: z.string().optional(),
69
+ analyticsScript: z.string().optional(),
65
70
  sections: z.array(GallerySectionSchema),
66
71
  subGalleries: z.object({ title: z.string(), galleries: z.array(SubGallerySchema) })
67
72
  });
@@ -250,6 +255,12 @@ function handleFileProcessingError(error, filename, ui) {
250
255
  }
251
256
  ui.debug(error);
252
257
  }
258
+ function parseTelemetryOption(value) {
259
+ if (value !== "0" && value !== "1") {
260
+ throw new Error("Telemetry option must be either 0 or 1.");
261
+ }
262
+ return value;
263
+ }
253
264
  async function getFileMtime(filePath) {
254
265
  const stats = await promises.stat(filePath);
255
266
  return stats.mtime;
@@ -460,7 +471,7 @@ async function thumbnails(options, ui) {
460
471
  const galleryDirs = findGalleries(options.gallery, options.recursive);
461
472
  if (galleryDirs.length === 0) {
462
473
  ui.error("No galleries found.");
463
- return;
474
+ return { processedGalleryCount: 0, processedMediaCount: 0 };
464
475
  }
465
476
  let totalGalleries = 0;
466
477
  let totalProcessed = 0;
@@ -474,6 +485,7 @@ async function thumbnails(options, ui) {
474
485
  ui.box(
475
486
  `Created thumbnails for ${totalGalleries} ${totalGalleries === 1 ? "gallery" : "galleries"} with ${totalProcessed} media ${totalProcessed === 1 ? "file" : "files"}`
476
487
  );
488
+ return { processedGalleryCount: totalGalleries, processedMediaCount: totalProcessed };
477
489
  } catch (error) {
478
490
  ui.error("Error creating thumbnails");
479
491
  throw error;
@@ -525,10 +537,10 @@ async function buildGallery(galleryDir, templateDir, ui, baseUrl) {
525
537
  galleryData.mediaBaseUrl = baseUrl;
526
538
  fs6.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));
527
539
  }
528
- ui.debug("Building gallery form template");
540
+ ui.debug("Building gallery from template");
529
541
  try {
530
- process2.env.GALLERY_JSON_PATH = galleryJsonPath;
531
- process2.env.GALLERY_OUTPUT_DIR = path4.join(galleryDir, "gallery");
542
+ process3.env.GALLERY_JSON_PATH = galleryJsonPath;
543
+ process3.env.GALLERY_OUTPUT_DIR = path4.join(galleryDir, "gallery");
532
544
  execSync("npx astro build", { cwd: templateDir, stdio: ui.level === LogLevels.debug ? "inherit" : "ignore" });
533
545
  } catch (error) {
534
546
  ui.error(`Build failed for ${galleryDir}`);
@@ -550,7 +562,7 @@ async function build(options, ui) {
550
562
  const galleryDirs = findGalleries(options.gallery, options.recursive);
551
563
  if (galleryDirs.length === 0) {
552
564
  ui.error("No galleries found.");
553
- return;
565
+ return { processedGalleryCount: 0 };
554
566
  }
555
567
  const themePath = await import.meta.resolve("@simple-photo-gallery/theme-modern/package.json");
556
568
  const themeDir = path4.dirname(new URL(themePath).pathname);
@@ -561,6 +573,7 @@ async function build(options, ui) {
561
573
  ++totalGalleries;
562
574
  }
563
575
  ui.box(`Built ${totalGalleries} ${totalGalleries === 1 ? "gallery" : "galleries"} successfully`);
576
+ return { processedGalleryCount: totalGalleries };
564
577
  } catch (error) {
565
578
  if (error instanceof Error && error.message.includes("Cannot find package")) {
566
579
  ui.error("Theme package not found: @simple-photo-gallery/theme-modern/package.json");
@@ -597,23 +610,25 @@ async function cleanGallery(galleryDir, ui) {
597
610
  } else {
598
611
  ui.info(`No gallery files found at: ${galleryDir}`);
599
612
  }
613
+ return { processedGalleryCount: filesRemoved };
600
614
  }
601
615
  async function clean(options, ui) {
602
616
  try {
603
617
  const basePath = path4.resolve(options.gallery);
604
618
  if (!fs6.existsSync(basePath)) {
605
619
  ui.error(`Directory does not exist: ${basePath}`);
606
- return;
620
+ return { processedGalleryCount: 0 };
607
621
  }
608
622
  const galleryDirs = findGalleries(basePath, options.recursive);
609
623
  if (galleryDirs.length === 0) {
610
624
  ui.info("No galleries found to clean.");
611
- return;
625
+ return { processedGalleryCount: 0 };
612
626
  }
613
627
  for (const dir of galleryDirs) {
614
628
  await cleanGallery(dir, ui);
615
629
  }
616
630
  ui.box(`Successfully cleaned ${galleryDirs.length} ${galleryDirs.length === 1 ? "gallery" : "galleries"}`);
631
+ return { processedGalleryCount: galleryDirs.length };
617
632
  } catch (error) {
618
633
  ui.error("Error cleaning galleries");
619
634
  throw error;
@@ -778,19 +793,259 @@ async function init(options, ui) {
778
793
  ui.box(
779
794
  `Created ${result.totalGalleries} ${result.totalGalleries === 1 ? "gallery" : "galleries"} with ${result.totalFiles} media ${result.totalFiles === 1 ? "file" : "files"}`
780
795
  );
796
+ return {
797
+ processedMediaCount: result.totalFiles,
798
+ processedGalleryCount: result.totalGalleries
799
+ };
781
800
  } catch (error) {
782
801
  ui.error("Error initializing gallery");
783
802
  throw error;
784
803
  }
785
804
  }
786
805
 
806
+ // src/modules/telemetry/index.ts
807
+ async function telemetry(options, ui, telemetryService2) {
808
+ const updates = {};
809
+ if (options.state === void 0) {
810
+ const current = telemetryService2.getStoredPreference();
811
+ if (current === void 0) {
812
+ ui.info("Telemetry preference not set yet. It will be requested on next command run.");
813
+ updates.telemetryStatus = "unset";
814
+ } else {
815
+ ui.info(`Telemetry is currently ${current ? "enabled" : "disabled"}.`);
816
+ updates.telemetryEnabled = current;
817
+ }
818
+ } else {
819
+ telemetryService2.setStoredPreference(options.state === "1");
820
+ ui.success(`Anonymous telemetry ${options.state === "1" ? "enabled" : "disabled"}.`);
821
+ updates.telemetryEnabled = options.state === "1";
822
+ }
823
+ return updates;
824
+ }
825
+ var ApiTelemetryClient = class {
826
+ constructor() {
827
+ this.endpoint = "https://tools.simple.photo/api/telemetry";
828
+ }
829
+ async record(event) {
830
+ try {
831
+ axios.post(this.endpoint, event, {
832
+ headers: {
833
+ "content-type": "application/json",
834
+ "user-agent": `simple-photo-gallery/${event.packageVersion} (${process3.platform}; ${process3.arch})`
835
+ }
836
+ });
837
+ } catch {
838
+ }
839
+ }
840
+ };
841
+ var ConsoleTelemetryClient = class {
842
+ async record(event) {
843
+ const serialized = JSON.stringify(event, null, 2);
844
+ stdout.write(`TELEMETRY EVENT: ${serialized}
845
+ `);
846
+ }
847
+ };
848
+
849
+ // src/modules/telemetry/service/index.ts
850
+ var CONFIG_KEY = "telemetryEnabled";
851
+ var TelemetryService = class {
852
+ constructor(packageName, packageVersion, ui) {
853
+ this.packageName = packageName;
854
+ this.packageVersion = packageVersion;
855
+ this.ui = ui;
856
+ this.config = new Conf({ projectName: packageName });
857
+ }
858
+ /** Returns the stored telemetry preference, if any. */
859
+ getStoredPreference() {
860
+ return this.config.get(CONFIG_KEY);
861
+ }
862
+ /** Updates the persisted telemetry preference. */
863
+ setStoredPreference(enabled) {
864
+ this.config.set(CONFIG_KEY, enabled);
865
+ }
866
+ /** Determines whether telemetry should be collected for this run. */
867
+ async shouldCollectTelemetry(override) {
868
+ if (override) {
869
+ return override === "1";
870
+ }
871
+ if (process3.env.CI || process3.env.DO_NOT_TRACK) {
872
+ return false;
873
+ }
874
+ if (process3.env.SPG_TELEMETRY) {
875
+ return process3.env.SPG_TELEMETRY === "1";
876
+ }
877
+ const stored = this.getStoredPreference();
878
+ if (stored === void 0) {
879
+ const consent = await this.promptForConsent();
880
+ this.setStoredPreference(consent);
881
+ return consent;
882
+ } else {
883
+ return stored;
884
+ }
885
+ }
886
+ /** Builds and dispatches a telemetry event when telemetry is enabled. */
887
+ async emit(params) {
888
+ const event = this.buildEvent(params);
889
+ try {
890
+ const client = await this.getClient();
891
+ if (client) {
892
+ await client.record(event);
893
+ }
894
+ } catch (error) {
895
+ this.ui.debug("Error recording telemetry event", error);
896
+ }
897
+ }
898
+ /** Builds a telemetry event. */
899
+ buildEvent({
900
+ command,
901
+ argumentsProvided,
902
+ globalOptionsProvided,
903
+ metrics,
904
+ success,
905
+ error,
906
+ startedAt
907
+ }) {
908
+ const now = Date.now();
909
+ const event = {
910
+ command,
911
+ argumentsProvided,
912
+ globalOptionsProvided,
913
+ timestamp: new Date(now).toISOString(),
914
+ durationMs: now - startedAt,
915
+ packageName: this.packageName,
916
+ packageVersion: this.packageVersion,
917
+ nodeVersion: process3.version,
918
+ osPlatform: os.platform(),
919
+ osRelease: os.release(),
920
+ osArch: os.arch(),
921
+ result: success ? "success" : "error"
922
+ };
923
+ if (metrics && Object.keys(metrics).length > 0) {
924
+ event.metrics = metrics;
925
+ }
926
+ if (!success && error) {
927
+ event.errorName = error.name;
928
+ event.errorMessage = error.message;
929
+ }
930
+ return event;
931
+ }
932
+ /** Prompts the user for consent to collect telemetry. */
933
+ async promptForConsent() {
934
+ this.ui.info(
935
+ "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."
936
+ );
937
+ const answer = await this.ui.prompt("Would you like to enable telemetry?", {
938
+ type: "confirm",
939
+ initial: true
940
+ });
941
+ if (!answer) {
942
+ this.ui.info('Anonymous telemetry disabled. You can re-enable it with "spg telemetry 1".');
943
+ return false;
944
+ }
945
+ this.ui.success("Thank you! Telemetry will help us improve Simple Photo Gallery.");
946
+ return true;
947
+ }
948
+ /** Returns the telemetry client. */
949
+ getClient() {
950
+ if (!this.client) {
951
+ switch (process3.env.SPG_TELEMETRY_PROVIDER) {
952
+ case "none": {
953
+ this.client = void 0;
954
+ break;
955
+ }
956
+ case "console": {
957
+ this.client = new ConsoleTelemetryClient();
958
+ break;
959
+ }
960
+ case "api": {
961
+ this.client = new ApiTelemetryClient();
962
+ break;
963
+ }
964
+ default: {
965
+ this.client = new ApiTelemetryClient();
966
+ break;
967
+ }
968
+ }
969
+ }
970
+ return this.client;
971
+ }
972
+ };
973
+ var NPM_REGISTRY_URL = "https://registry.npmjs.org";
974
+ var CHECK_TIMEOUT_MS = 3e3;
975
+ async function fetchLatestStableVersion(packageName) {
976
+ try {
977
+ const response = await axios.get(`${NPM_REGISTRY_URL}/${packageName}`, {
978
+ timeout: CHECK_TIMEOUT_MS,
979
+ headers: {
980
+ Accept: "application/vnd.npm.install-v1+json"
981
+ }
982
+ });
983
+ const versions = Object.keys(response.data.versions);
984
+ const stableVersions = versions.filter((version) => {
985
+ try {
986
+ const parsed = parseSemVer(version);
987
+ return !parsed.pre || parsed.pre.length === 0;
988
+ } catch {
989
+ return false;
990
+ }
991
+ });
992
+ if (stableVersions.length === 0) {
993
+ return void 0;
994
+ }
995
+ stableVersions.sort(compareSemVer);
996
+ return stableVersions[stableVersions.length - 1];
997
+ } catch {
998
+ return void 0;
999
+ }
1000
+ }
1001
+ function checkForUpdates(packageName, currentVersion) {
1002
+ return fetchLatestStableVersion(packageName).then((latestStableVersion) => {
1003
+ if (!latestStableVersion) {
1004
+ return null;
1005
+ }
1006
+ try {
1007
+ if (compareSemVer(latestStableVersion, currentVersion) > 0) {
1008
+ return { currentVersion, latestVersion: latestStableVersion };
1009
+ }
1010
+ } catch {
1011
+ }
1012
+ return null;
1013
+ });
1014
+ }
1015
+ function displayUpdateNotification(updateInfo, ui) {
1016
+ const { currentVersion, latestVersion } = updateInfo;
1017
+ const message = [
1018
+ `New version available: ${currentVersion} \u2192 ${latestVersion}`,
1019
+ "",
1020
+ "Run the following commands to update:",
1021
+ "",
1022
+ " npm install -g simple-photo-gallery@latest"
1023
+ ].join("\n");
1024
+ ui.box(message);
1025
+ }
1026
+ async function waitForUpdateCheck(checkPromise) {
1027
+ try {
1028
+ let timeoutId;
1029
+ const timeoutPromise = new Promise((resolve) => {
1030
+ timeoutId = setTimeout(() => resolve(null), 5e3);
1031
+ });
1032
+ const result = await Promise.race([checkPromise, timeoutPromise]);
1033
+ clearTimeout(timeoutId);
1034
+ return result;
1035
+ } catch {
1036
+ return null;
1037
+ }
1038
+ }
1039
+
787
1040
  // package.json
788
1041
  var package_default = {
789
- version: "2.0.7"};
1042
+ name: "simple-photo-gallery",
1043
+ version: "2.0.9"};
790
1044
 
791
1045
  // src/index.ts
792
1046
  var program = new Command();
793
- program.name("gallery").description("Simple Photo Gallery CLI").version(package_default.version).option("-v, --verbose", "Verbose output (debug level)", false).option("-q, --quiet", "Minimal output (only warnings/errors)", false).showHelpAfterError(true);
1047
+ var telemetryService = new TelemetryService(package_default.name, package_default.version, createConsolaUI(program.opts()));
1048
+ program.name("gallery").description("Simple Photo Gallery CLI").version(package_default.version).option("-v, --verbose", "Verbose output (debug level)", false).option("-q, --quiet", "Minimal output (only warnings/errors)", false).option("--telemetry <state>", "Enable (1) or disable (0) telemetry for this command", parseTelemetryOption).showHelpAfterError(true);
794
1049
  function createConsolaUI(globalOpts) {
795
1050
  let level = LogLevels.info;
796
1051
  if (globalOpts.quiet) {
@@ -802,28 +1057,62 @@ function createConsolaUI(globalOpts) {
802
1057
  level
803
1058
  }).withTag("simple-photo-gallery");
804
1059
  }
805
- function withConsolaUI(handler) {
806
- return async (opts) => {
1060
+ function collectCommandArguments(command) {
1061
+ return command.options.map((option) => ({ name: option.attributeName(), source: command.getOptionValueSource(option.attributeName()) })).filter((option) => option.source && option.source !== "default").map((option) => option.name);
1062
+ }
1063
+ function collectGlobalArguments() {
1064
+ return program.options.map((option) => ({ name: option.attributeName(), source: program.getOptionValueSource(option.attributeName()) })).filter((option) => option.source && option.source !== "default").map((option) => option.name);
1065
+ }
1066
+ function withCommandContext(handler) {
1067
+ return async (rawOpts, command) => {
1068
+ const { telemetry: telemetryOverride, ...commandOptions } = rawOpts;
807
1069
  const ui = createConsolaUI(program.opts());
1070
+ const shouldCollectTelemetry = await telemetryService.shouldCollectTelemetry(telemetryOverride);
1071
+ const startedAt = Date.now();
1072
+ const updateCheckPromise = checkForUpdates(package_default.name, package_default.version);
1073
+ let success = false;
1074
+ let metrics;
1075
+ let errorInfo;
808
1076
  try {
809
- await handler(opts, ui);
1077
+ const result = await handler(commandOptions, ui, command);
1078
+ if (result && Object.keys(result).length > 0) {
1079
+ metrics = result;
1080
+ }
1081
+ success = true;
810
1082
  } catch (error) {
811
1083
  ui.debug(error);
812
- process2.exitCode = 1;
1084
+ errorInfo = error instanceof Error ? { name: error.name, message: error.message } : { name: "UnknownError", message: String(error) };
1085
+ process3.exitCode = 1;
1086
+ }
1087
+ const updateInfo = await waitForUpdateCheck(updateCheckPromise);
1088
+ if (updateInfo) {
1089
+ displayUpdateNotification(updateInfo, ui);
1090
+ }
1091
+ if (shouldCollectTelemetry) {
1092
+ await telemetryService.emit({
1093
+ command: command.name(),
1094
+ argumentsProvided: collectCommandArguments(command),
1095
+ globalOptionsProvided: collectGlobalArguments(),
1096
+ metrics,
1097
+ success,
1098
+ error: errorInfo,
1099
+ startedAt
1100
+ });
813
1101
  }
814
1102
  };
815
1103
  }
816
1104
  program.command("init").description("Initialize a gallery by scaning a folder for images and videos").option(
817
1105
  "-p, --photos <path>",
818
1106
  "Path to the folder where the photos are stored. Default: current working directory",
819
- process2.cwd()
1107
+ process3.cwd()
820
1108
  ).option(
821
1109
  "-g, --gallery <path>",
822
1110
  "Path to the directory where the gallery will be initialized. Default: same directory as the photos folder"
823
- ).option("-r, --recursive", "Recursively create galleries from all photos subdirectories", false).option("-d, --default", "Use default gallery settings instead of asking the user", false).action(withConsolaUI(init));
824
- program.command("thumbnails").description("Create thumbnails for all media files in the gallery").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process2.cwd()).option("-r, --recursive", "Scan subdirectories recursively", false).action(withConsolaUI(thumbnails));
825
- program.command("build").description("Build the HTML gallery in the specified directory").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process2.cwd()).option("-r, --recursive", "Scan subdirectories recursively", false).option("-b, --base-url <url>", "Base URL where the photos are hosted").action(withConsolaUI(build));
826
- program.command("clean").description("Remove all gallery files and folders (index.html, gallery/)").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process2.cwd()).option("-r, --recursive", "Clean subdirectories recursively", false).action(withConsolaUI(clean));
1111
+ ).option("-r, --recursive", "Recursively create galleries from all photos subdirectories", false).option("-d, --default", "Use default gallery settings instead of asking the user", false).action(withCommandContext((options, ui) => init(options, ui)));
1112
+ program.command("thumbnails").description("Create thumbnails for all media files in the gallery").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process3.cwd()).option("-r, --recursive", "Scan subdirectories recursively", false).action(withCommandContext((options, ui) => thumbnails(options, ui)));
1113
+ program.command("build").description("Build the HTML gallery in the specified directory").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process3.cwd()).option("-r, --recursive", "Scan subdirectories recursively", false).option("-b, --base-url <url>", "Base URL where the photos are hosted").action(withCommandContext((options, ui) => build(options, ui)));
1114
+ program.command("clean").description("Remove all gallery files and folders (index.html, gallery/)").option("-g, --gallery <path>", "Path to the directory of the gallery. Default: current working directory", process3.cwd()).option("-r, --recursive", "Clean subdirectories recursively", false).action(withCommandContext((options, ui) => clean(options, ui)));
1115
+ program.command("telemetry").description("Manage anonymous telemetry preferences. Use 1 to enable, 0 to disable, or no argument to check status").option("-s, --state <state>", "Enable (1) or disable (0) telemetry", parseTelemetryOption).action(withCommandContext((options, ui) => telemetry(options, ui, telemetryService)));
827
1116
  program.parse();
828
1117
  //# sourceMappingURL=index.js.map
829
1118
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../common/src/gallery.ts","../src/config/index.ts","../src/utils/image.ts","../src/modules/build/utils/index.ts","../src/utils/index.ts","../src/modules/thumbnails/utils/index.ts","../src/utils/blurhash.ts","../src/utils/video.ts","../src/modules/thumbnails/index.ts","../src/modules/build/index.ts","../src/modules/clean/index.ts","../src/modules/init/utils/index.ts","../src/modules/init/index.ts","../package.json","../src/index.ts"],"names":["sharp","path","fs","process","LogLevels","result"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\n/** Zod schema for thumbnail metadata including path and dimensions */\nexport const ThumbnailSchema = z.object({\n path: z.string(),\n pathRetina: z.string(),\n width: z.number(),\n height: z.number(),\n blurHash: z.string().optional(),\n});\n\n/** Zod schema for media file metadata including type, dimensions, and thumbnail info */\nexport const MediaFileSchema = z.object({\n type: z.enum(['image', 'video']),\n path: z.string(),\n alt: z.string().optional(),\n width: z.number(),\n height: z.number(),\n thumbnail: ThumbnailSchema.optional(),\n lastMediaTimestamp: z.string().optional(),\n});\n\n/** Zod schema for a gallery section containing title, description, and media files */\nexport const GallerySectionSchema = z.object({\n title: z.string().optional(),\n description: z.string().optional(),\n images: z.array(MediaFileSchema),\n});\n\n/** Zod schema for sub-gallery metadata including title, header image, and path */\nexport const SubGallerySchema = z.object({\n title: z.string(),\n headerImage: z.string(),\n path: z.string(),\n});\n\n/** Zod schema for complete gallery data including metadata, sections, and sub-galleries */\nexport const GalleryMetadataSchema = z.object({\n image: z.string().optional(),\n imageWidth: z.number().optional(),\n imageHeight: z.number().optional(),\n ogUrl: z.string().optional(),\n ogType: z.string().optional(),\n ogSiteName: z.string().optional(),\n twitterSite: z.string().optional(),\n twitterCreator: z.string().optional(),\n author: z.string().optional(),\n keywords: z.string().optional(),\n canonicalUrl: z.string().optional(),\n language: z.string().optional(),\n robots: z.string().optional(),\n});\n\n/** Zod schema for complete gallery data including metadata, sections, and sub-galleries */\nexport const GalleryDataSchema = z.object({\n title: z.string(),\n description: z.string(),\n url: z.string().optional(),\n headerImage: z.string(),\n thumbnailSize: z.number().optional(),\n metadata: GalleryMetadataSchema,\n galleryOutputPath: z.string().optional(),\n mediaBaseUrl: z.string().optional(),\n sections: z.array(GallerySectionSchema),\n subGalleries: z.object({ title: z.string(), galleries: z.array(SubGallerySchema) }),\n});\n\n/** TypeScript type for thumbnail metadata */\nexport type Thumbnail = z.infer<typeof ThumbnailSchema>;\n\n/** TypeScript type for media file metadata */\nexport type MediaFile = z.infer<typeof MediaFileSchema>;\n\n/** TypeScript type for gallery section data */\nexport type GallerySection = z.infer<typeof GallerySectionSchema>;\n\n/** TypeScript type for sub-gallery metadata */\nexport type SubGallery = z.infer<typeof SubGallerySchema>;\n\n/** TypeScript type for gallery metadata */\nexport type GalleryMetadata = z.infer<typeof GalleryMetadataSchema>;\n\n/** TypeScript type for complete gallery data structure */\nexport type GalleryData = z.infer<typeof GalleryDataSchema>;\n","/** Default thumbnail size in pixels */\nexport const DEFAULT_THUMBNAIL_SIZE = 300;\n\n/** Set of supported image file extensions */\nexport const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp', '.tiff', '.tif', '.svg', '.avif']);\n\n/** Set of supported video file extensions */\nexport const VIDEO_EXTENSIONS = new Set(['.mp4', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv', '.m4v', '.3gp']);\n\n/** Set of supported header image landscape widths */\nexport const HEADER_IMAGE_LANDSCAPE_WIDTHS = [3840, 2560, 1920, 1280, 960, 640];\n\n/** Set of supported header image portrait widths */\nexport const HEADER_IMAGE_PORTRAIT_WIDTHS = [1080, 720, 480, 360];\n","import ExifReader from 'exifreader';\nimport sharp from 'sharp';\n\nimport type { Dimensions, ImageWithMetadata } from '../types';\nimport type { FormatEnum, Metadata, Sharp } from 'sharp';\n\n/**\n * Loads an image and auto-rotates it based on EXIF orientation.\n * @param imagePath - Path to the image file\n * @returns Promise resolving to Sharp image instance\n */\nexport async function loadImage(imagePath: string): Promise<Sharp> {\n return sharp(imagePath).rotate();\n}\n\n/**\n * Loads an image and its metadata, auto-rotating it based on EXIF orientation and swapping dimensions if needed.\n * @param imagePath - Path to the image file\n * @returns Promise resolving to ImageWithMetadata object containing Sharp image instance and metadata\n */\nexport async function loadImageWithMetadata(imagePath: string): Promise<ImageWithMetadata> {\n const image = sharp(imagePath);\n const metadata = await image.metadata();\n\n // Auto-rotate based on EXIF orientation\n image.rotate();\n\n // EXIF orientation values 5, 6, 7, 8 require dimension swap after rotation\n const needsDimensionSwap = metadata.orientation && metadata.orientation >= 5 && metadata.orientation <= 8;\n\n // Update metadata with swapped dimensions if needed\n if (needsDimensionSwap) {\n const originalWidth = metadata.width;\n metadata.width = metadata.height;\n metadata.height = originalWidth;\n }\n\n return { image, metadata };\n}\n\n/**\n * Utility function to resize and save thumbnail using Sharp. The functions avoids upscaling the image and only reduces the size if necessary.\n * @param image - Sharp image instance\n * @param outputPath - Path where thumbnail should be saved\n * @param width - Target width for thumbnail\n * @param height - Target height for thumbnail\n */\nexport async function resizeImage(\n image: Sharp,\n outputPath: string,\n width: number,\n height: number,\n format: keyof FormatEnum = 'avif',\n): Promise<void> {\n // Resize the image without enlarging it\n await image.resize(width, height, { withoutEnlargement: true }).toFormat(format).toFile(outputPath);\n}\n\n/**\n * Crops and resizes an image to a target aspect ratio, avoiding upscaling the image.\n * @param image - Sharp image instance\n * @param outputPath - Path where the image should be saved\n * @param width - Target width for the image\n * @param height - Target height for the image\n */\nexport async function cropAndResizeImage(\n image: Sharp,\n outputPath: string,\n width: number,\n height: number,\n format: keyof FormatEnum = 'avif',\n): Promise<void> {\n // Apply resize with cover fit and without enlargement\n await image\n .resize(width, height, {\n fit: 'cover',\n withoutEnlargement: true,\n })\n .toFormat(format)\n .toFile(outputPath);\n}\n\n/**\n * Extracts description from image EXIF data\n * @param metadata - Sharp metadata object containing EXIF data\n * @returns Promise resolving to image description or undefined if not found\n */\nexport async function getImageDescription(imagePath: string): Promise<string | undefined> {\n try {\n const tags = await ExifReader.load(imagePath);\n\n // Description\n if (tags.description?.description) return tags.description.description;\n\n // ImageDescription\n if (tags.ImageDescription?.description) return tags.ImageDescription.description;\n\n // UserComment\n if (\n tags.UserComment &&\n typeof tags.UserComment === 'object' &&\n tags.UserComment !== null &&\n 'description' in tags.UserComment\n ) {\n return (tags.UserComment as { description: string }).description;\n }\n\n // ExtDescrAccessibility\n if (tags.ExtDescrAccessibility?.description) return tags.ExtDescrAccessibility.description;\n\n // Caption/Abstract\n if (tags['Caption/Abstract']?.description) return tags['Caption/Abstract'].description;\n\n // XP Title\n if (tags.XPTitle?.description) return tags.XPTitle.description;\n\n // XP Comment\n if (tags.XPComment?.description) return tags.XPComment.description;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Creates regular and retina thumbnails for an image while maintaining aspect ratio\n * @param image - Sharp image instance\n * @param metadata - Image metadata containing dimensions\n * @param outputPath - Path where thumbnail should be saved\n * @param outputPathRetina - Path where retina thumbnail should be saved\n * @param size - Target size of the longer side of the thumbnail\n * @returns Promise resolving to thumbnail dimensions\n */\nexport async function createImageThumbnails(\n image: Sharp,\n metadata: Metadata,\n outputPath: string,\n outputPathRetina: string,\n size: number,\n): Promise<Dimensions> {\n // Get the original dimensions\n const originalWidth = metadata.width || 0;\n const originalHeight = metadata.height || 0;\n\n if (originalWidth === 0 || originalHeight === 0) {\n throw new Error('Invalid image dimensions');\n }\n\n // Calculate the new size maintaining aspect ratio\n const aspectRatio = originalWidth / originalHeight;\n\n let width: number;\n let height: number;\n\n if (originalWidth > originalHeight) {\n width = size;\n height = Math.round(size / aspectRatio);\n } else {\n width = Math.round(size * aspectRatio);\n height = size;\n }\n\n // Resize the image and create the thumbnails\n await resizeImage(image, outputPath, width, height);\n await resizeImage(image, outputPathRetina, width * 2, height * 2);\n\n // Return the dimensions of the thumbnail\n return { width, height };\n}\n","import { Buffer } from 'node:buffer';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport sharp from 'sharp';\n\nimport { HEADER_IMAGE_LANDSCAPE_WIDTHS, HEADER_IMAGE_PORTRAIT_WIDTHS } from '../../../config';\nimport { cropAndResizeImage, loadImage } from '../../../utils/image';\n\nimport type { ConsolaInstance } from 'consola';\n\n/** __dirname workaround for ESM modules */\nconst __dirname = path.dirname(new URL(import.meta.url).pathname);\n\n/**\n * Helper function to resolve paths relative to current file\n * @param segments - Path segments to resolve relative to current directory\n * @returns Resolved absolute path\n */\nexport function resolveFromCurrentDir(...segments: string[]): string {\n return path.resolve(__dirname, ...segments);\n}\n\n/**\n * Creates a social media card image for a gallery\n * @param headerPhotoPath - Path to the header photo\n * @param title - Title of the gallery\n * @param ouputPath - Output path for the social media card image\n * @param ui - ConsolaInstance for logging\n */\nexport async function createGallerySocialMediaCardImage(\n headerPhotoPath: string,\n title: string,\n ouputPath: string,\n ui: ConsolaInstance,\n): Promise<void> {\n ui.start(`Creating social media card image`);\n\n if (fs.existsSync(ouputPath)) {\n ui.success(`Social media card image already exists`);\n return;\n }\n\n // Read and resize the header image to 1200x631 using fit\n const image = await loadImage(headerPhotoPath);\n const resizedImageBuffer = await image.resize(1200, 631, { fit: 'cover' }).jpeg({ quality: 90 }).toBuffer();\n\n // Save the resized image as social media card\n const outputPath = ouputPath;\n await sharp(resizedImageBuffer).toFile(outputPath);\n\n // Create SVG with title and description\n const svgText = `\n <svg width=\"1200\" height=\"631\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <style>\n .title { font-family: Arial, sans-serif; font-size: 96px; font-weight: bold; fill: white; text-anchor: middle; }\n .description { font-family: Arial, sans-serif; font-size: 48px; fill: white; text-anchor: middle; }\n </style>\n </defs>\n <text x=\"600\" y=\"250\" class=\"title\">${title}</text>\n </svg>\n `;\n\n // Composite the text overlay on top of the resized image\n const finalImageBuffer = await sharp(resizedImageBuffer)\n .composite([{ input: Buffer.from(svgText), top: 0, left: 0 }])\n .jpeg({ quality: 90 })\n .toBuffer();\n\n // Save the final image with text overlay\n await sharp(finalImageBuffer).toFile(outputPath);\n\n ui.success(`Created social media card image successfully`);\n}\n\nexport async function createOptimizedHeaderImage(\n headerPhotoPath: string,\n outputFolder: string,\n ui: ConsolaInstance,\n): Promise<void> {\n ui.start(`Creating optimized header images`);\n\n const image = await loadImage(headerPhotoPath);\n\n // Create landscape header images\n const landscapeYFactor = 3 / 4;\n for (const width of HEADER_IMAGE_LANDSCAPE_WIDTHS) {\n ui.debug(`Creating landscape header image ${width}`);\n\n if (fs.existsSync(path.join(outputFolder, `header_landscape_${width}.avif`))) {\n ui.debug(`Landscape header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_landscape_${width}.avif`),\n width,\n width * landscapeYFactor,\n 'avif',\n );\n }\n\n if (fs.existsSync(path.join(outputFolder, `header_landscape_${width}.jpg`))) {\n ui.debug(`Landscape header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_landscape_${width}.jpg`),\n width,\n width * landscapeYFactor,\n 'jpg',\n );\n }\n }\n\n // Create portrait header images\n const portraitYFactor = 4 / 3;\n for (const width of HEADER_IMAGE_PORTRAIT_WIDTHS) {\n ui.debug(`Creating portrait header image ${width}`);\n\n if (fs.existsSync(path.join(outputFolder, `header_portrait_${width}.avif`))) {\n ui.debug(`Portrait header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_portrait_${width}.avif`),\n width,\n width * portraitYFactor,\n 'avif',\n );\n }\n\n if (fs.existsSync(path.join(outputFolder, `header_portrait_${width}.jpg`))) {\n ui.debug(`Portrait header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_portrait_${width}.jpg`),\n width,\n width * portraitYFactor,\n 'jpg',\n );\n }\n }\n\n ui.success(`Created optimized header image successfully`);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Finds all gallery directories that contain a gallery/gallery.json file.\n *\n * @param basePath - The base directory to search from\n * @param recursive - Whether to search subdirectories recursively\n * @returns Array of paths to directories containing gallery/gallery.json files\n */\nexport function findGalleries(basePath: string, recursive: boolean): string[] {\n const galleryDirs: string[] = [];\n\n // Check basePath itself\n const galleryJsonPath = path.join(basePath, 'gallery', 'gallery.json');\n if (fs.existsSync(galleryJsonPath)) {\n galleryDirs.push(basePath);\n }\n\n // If recursive, search all subdirectories\n if (recursive) {\n try {\n const entries = fs.readdirSync(basePath, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name !== 'gallery') {\n const subPath = path.join(basePath, entry.name);\n const subResults = findGalleries(subPath, recursive);\n galleryDirs.push(...subResults);\n }\n }\n } catch {\n // Silently ignore errors when reading directories\n }\n }\n\n return galleryDirs;\n}\n\n/**\n * Handles file processing errors with appropriate user-friendly messages\n * @param error - The error that occurred during file processing\n * @param filename - Name of the file that caused the error\n * @param ui - ConsolaInstance for logging messages\n */\nexport function handleFileProcessingError(error: unknown, filename: string, ui: ConsolaInstance): void {\n if (error instanceof Error && (error.message.includes('ffprobe') || error.message.includes('ffmpeg'))) {\n // Handle ffmpeg error\n ui.warn(\n `Error processing ${filename}: ffprobe (part of ffmpeg) is required to process videos. Please install ffmpeg and ensure it is available in your PATH`,\n );\n } else if (error instanceof Error && error.message.includes('unsupported image format')) {\n // Handle unsupported image format error\n ui.warn(`Error processing ${filename}: unsupported image format`);\n } else {\n // Handle unknown error\n ui.warn(`Error processing ${filename}`);\n }\n\n ui.debug(error);\n}\n","import { promises as fs } from 'node:fs';\n\n/**\n * Gets the last modification time of a file\n * @param filePath - Path to the file\n * @returns Promise resolving to the file's modification date\n */\nexport async function getFileMtime(filePath: string): Promise<Date> {\n const stats = await fs.stat(filePath);\n return stats.mtime;\n}\n","import { encode } from 'blurhash';\n\nimport { loadImage } from './image';\n\n/**\n * Generates a BlurHash from an image file or Sharp instance\n * @param imagePath - Path to image file or Sharp instance\n * @param componentX - Number of x components (default: 4)\n * @param componentY - Number of y components (default: 3)\n * @returns Promise resolving to BlurHash string\n */\nexport async function generateBlurHash(imagePath: string, componentX: number = 4, componentY: number = 3): Promise<string> {\n const image = await loadImage(imagePath);\n\n // Resize to small size for BlurHash computation to improve performance\n // BlurHash doesn't need high resolution\n const { data, info } = await image\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n // Convert to Uint8ClampedArray format expected by blurhash\n const pixels = new Uint8ClampedArray(data.buffer);\n\n // Generate BlurHash\n return encode(pixels, info.width, info.height, componentX, componentY);\n}\n","import { spawn } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\n\nimport ffprobe from 'node-ffprobe';\nimport sharp from 'sharp';\n\nimport { resizeImage } from './image';\n\nimport type { Dimensions } from '../types';\nimport type { Buffer } from 'node:buffer';\n\n/**\n * Gets video dimensions using ffprobe\n * @param filePath - Path to the video file\n * @returns Promise resolving to video dimensions\n * @throws Error if no video stream found or invalid dimensions\n */\nexport async function getVideoDimensions(filePath: string): Promise<Dimensions> {\n const data = await ffprobe(filePath);\n const videoStream = data.streams.find((stream) => stream.codec_type === 'video');\n\n if (!videoStream) {\n throw new Error('No video stream found');\n }\n\n const dimensions = {\n width: videoStream.width || 0,\n height: videoStream.height || 0,\n };\n\n if (dimensions.width === 0 || dimensions.height === 0) {\n throw new Error('Invalid video dimensions');\n }\n\n return dimensions;\n}\n\n/**\n * Creates regular and retina thumbnails for a video by extracting the first frame\n * @param inputPath - Path to the video file\n * @param videoDimensions - Original video dimensions\n * @param outputPath - Path where thumbnail should be saved\n * @param outputPathRetina - Path where retina thumbnail should be saved\n * @param height - Target height for thumbnail\n * @param verbose - Whether to enable verbose ffmpeg output\n * @returns Promise resolving to thumbnail dimensions\n */\nexport async function createVideoThumbnails(\n inputPath: string,\n videoDimensions: Dimensions,\n outputPath: string,\n outputPathRetina: string,\n height: number,\n verbose: boolean = false,\n): Promise<Dimensions> {\n // Calculate width maintaining aspect ratio\n const aspectRatio = videoDimensions.width / videoDimensions.height;\n const width = Math.round(height * aspectRatio);\n\n // Use ffmpeg to extract first frame as a temporary file, then process with sharp\n const tempFramePath = `${outputPath}.temp.png`;\n\n return new Promise((resolve, reject) => {\n // Extract first frame using ffmpeg\n const ffmpeg = spawn('ffmpeg', [\n '-i',\n inputPath,\n '-vframes',\n '1',\n '-y',\n '-loglevel',\n verbose ? 'error' : 'quiet',\n tempFramePath,\n ]);\n\n ffmpeg.stderr.on('data', (data: Buffer) => {\n // FFmpeg writes normal output to stderr, so we don't treat this as an error\n console.log(`ffmpeg: ${data.toString()}`);\n });\n\n ffmpeg.on('close', async (code: number) => {\n if (code === 0) {\n try {\n // Process the extracted frame with sharp\n const frameImage = sharp(tempFramePath);\n await resizeImage(frameImage, outputPath, width, height);\n await resizeImage(frameImage, outputPathRetina, width * 2, height * 2);\n\n // Clean up temporary file\n try {\n await fs.unlink(tempFramePath);\n } catch {\n // Ignore cleanup errors\n }\n\n resolve({ width, height });\n } catch (sharpError) {\n reject(new Error(`Failed to process extracted frame: ${sharpError}`));\n }\n } else {\n reject(new Error(`ffmpeg exited with code ${code}`));\n }\n });\n\n ffmpeg.on('error', (error: Error) => {\n reject(new Error(`Failed to start ffmpeg: ${error.message}`));\n });\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GalleryDataSchema, type MediaFile } from '@simple-photo-gallery/common/src/gallery';\nimport { LogLevels, type ConsolaInstance } from 'consola';\n\nimport { getFileMtime } from './utils';\n\nimport { DEFAULT_THUMBNAIL_SIZE } from '../../config';\nimport { findGalleries, handleFileProcessingError } from '../../utils';\nimport { generateBlurHash } from '../../utils/blurhash';\nimport { getImageDescription, createImageThumbnails, loadImageWithMetadata } from '../../utils/image';\nimport { getVideoDimensions, createVideoThumbnails } from '../../utils/video';\n\nimport type { ThumbnailOptions } from './types';\n\n/**\n * Processes an image file to create thumbnail and extract metadata\n * @param imagePath - Path to the image file\n * @param thumbnailPath - Path where thumbnail should be saved\n * @param thumbnailPathRetina - Path where retina thumbnail should be saved\n * @param thumbnailSize - Target size for thumbnail\n * @param lastMediaTimestamp - Optional timestamp to check if processing can be skipped\n * @returns Promise resolving to updated MediaFile or undefined if skipped\n */\nexport async function processImage(\n imagePath: string,\n thumbnailPath: string,\n thumbnailPathRetina: string,\n thumbnailSize: number,\n lastMediaTimestamp?: Date,\n): Promise<MediaFile | undefined> {\n // Get the last media timestamp\n const fileMtime = await getFileMtime(imagePath);\n\n // Check if processing of the file can be skipped\n if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs.existsSync(thumbnailPath)) {\n return undefined;\n }\n\n // Load the image and get metadata first to check orientation\n const { image, metadata } = await loadImageWithMetadata(imagePath);\n\n // Get the image dimensions\n const imageDimensions = {\n width: metadata.width || 0,\n height: metadata.height || 0,\n };\n\n if (imageDimensions.width === 0 || imageDimensions.height === 0) {\n throw new Error('Invalid image dimensions');\n }\n\n // Get the image description\n const description = await getImageDescription(imagePath);\n\n // Create the thumbnails\n const thumbnailDimensions = await createImageThumbnails(\n image,\n metadata,\n thumbnailPath,\n thumbnailPathRetina,\n thumbnailSize,\n );\n\n // Generate BlurHash from the thumbnail\n const blurHash = await generateBlurHash(thumbnailPath);\n\n // Return the updated media file\n return {\n type: 'image',\n path: imagePath,\n alt: description,\n width: imageDimensions.width,\n height: imageDimensions.height,\n thumbnail: {\n path: thumbnailPath,\n pathRetina: thumbnailPathRetina,\n width: thumbnailDimensions.width,\n height: thumbnailDimensions.height,\n blurHash,\n },\n lastMediaTimestamp: fileMtime.toISOString(),\n };\n}\n\n/**\n * Processes a video file to create thumbnail and extract metadata\n * @param videoPath - Path to the video file\n * @param thumbnailPath - Path where thumbnail should be saved\n * @param thumbnailPathRetina - Path where retina thumbnail should be saved\n * @param thumbnailSize - Target size for thumbnail\n * @param verbose - Whether to enable verbose output\n * @param lastMediaTimestamp - Optional timestamp to check if processing can be skipped\n * @returns Promise resolving to updated MediaFile or undefined if skipped\n */\nasync function processVideo(\n videoPath: string,\n thumbnailPath: string,\n thumbnailPathRetina: string,\n thumbnailSize: number,\n verbose: boolean,\n lastMediaTimestamp?: Date,\n): Promise<MediaFile | undefined> {\n // Get the last media timestamp\n const fileMtime = await getFileMtime(videoPath);\n\n // Check if processing of the file can be skipped\n if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs.existsSync(thumbnailPath)) {\n return undefined;\n }\n\n // Get the video dimensions\n const videoDimensions = await getVideoDimensions(videoPath);\n\n // Create the thumbnail\n const thumbnailDimensions = await createVideoThumbnails(\n videoPath,\n videoDimensions,\n thumbnailPath,\n thumbnailPathRetina,\n thumbnailSize,\n verbose,\n );\n\n // Generate BlurHash from the thumbnail\n const blurHash = await generateBlurHash(thumbnailPath);\n\n return {\n type: 'video',\n path: videoPath,\n alt: undefined,\n width: videoDimensions.width,\n height: videoDimensions.height,\n thumbnail: {\n path: thumbnailPath,\n pathRetina: thumbnailPathRetina,\n width: thumbnailDimensions.width,\n height: thumbnailDimensions.height,\n blurHash,\n },\n lastMediaTimestamp: fileMtime.toISOString(),\n };\n}\n\n/**\n * Processes a media file to generate thumbnails and update metadata\n * @param mediaFile - Media file to process\n * @param galleryDir - Gallery directory path\n * @param thumbnailsPath - Directory where thumbnails are stored\n * @param thumbnailSize - Target size for thumbnails\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to updated MediaFile\n */\nasync function processMediaFile(\n mediaFile: MediaFile,\n galleryDir: string,\n thumbnailsPath: string,\n thumbnailSize: number,\n ui: ConsolaInstance,\n): Promise<MediaFile> {\n try {\n // Resolve the path relative to the gallery.json file location, not the gallery directory\n const galleryJsonDir = path.join(galleryDir, 'gallery');\n const filePath = path.resolve(path.join(galleryJsonDir, mediaFile.path));\n\n const fileName = path.basename(filePath);\n const fileNameWithoutExt = path.parse(fileName).name;\n const thumbnailFileName = `${fileNameWithoutExt}.avif`;\n const thumbnailPath = path.join(thumbnailsPath, thumbnailFileName);\n const thumbnailPathRetina = thumbnailPath.replace('.avif', '@2x.avif');\n const relativeThumbnailPath = path.relative(galleryJsonDir, thumbnailPath);\n const relativeThumbnailRetinaPath = path.relative(galleryJsonDir, thumbnailPathRetina);\n\n const lastMediaTimestamp = mediaFile.lastMediaTimestamp ? new Date(mediaFile.lastMediaTimestamp) : undefined;\n const verbose = ui.level === LogLevels.debug;\n\n ui.debug(` Processing ${mediaFile.type}: ${fileName}`);\n\n const updatedMediaFile = await (mediaFile.type === 'image'\n ? processImage(filePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, lastMediaTimestamp)\n : processVideo(filePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, verbose, lastMediaTimestamp));\n\n if (!updatedMediaFile) {\n ui.debug(` Skipping ${fileName} because it has already been processed`);\n\n // Check if we need to generate BlurHash for existing thumbnail\n if (mediaFile.thumbnail && !mediaFile.thumbnail.blurHash && fs.existsSync(thumbnailPath)) {\n try {\n const blurHash = await generateBlurHash(thumbnailPath);\n return {\n ...mediaFile,\n thumbnail: {\n ...mediaFile.thumbnail,\n blurHash,\n },\n };\n } catch (error) {\n ui.debug(` Failed to generate BlurHash for ${fileName}:`, error);\n }\n }\n\n return mediaFile;\n }\n\n updatedMediaFile.path = mediaFile.path;\n if (updatedMediaFile.thumbnail) {\n updatedMediaFile.thumbnail.path = relativeThumbnailPath;\n updatedMediaFile.thumbnail.pathRetina = relativeThumbnailRetinaPath;\n }\n\n return updatedMediaFile;\n } catch (error) {\n handleFileProcessingError(error, path.basename(mediaFile.path), ui);\n\n return mediaFile;\n }\n}\n\n/**\n * Processes all media files in a gallery to generate thumbnails\n * @param galleryDir - Directory containing the gallery\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to the number of files processed\n */\nexport async function processGalleryThumbnails(galleryDir: string, ui: ConsolaInstance): Promise<number> {\n const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');\n const thumbnailsPath = path.join(galleryDir, 'gallery', 'images');\n\n ui.start(`Creating thumbnails: ${galleryDir}`);\n\n try {\n // Ensure thumbnails directory exists\n fs.mkdirSync(thumbnailsPath, { recursive: true });\n\n // Read gallery.json\n const galleryContent = fs.readFileSync(galleryJsonPath, 'utf8');\n const galleryData = GalleryDataSchema.parse(JSON.parse(galleryContent));\n\n const thumbnailSize = galleryData.thumbnailSize || DEFAULT_THUMBNAIL_SIZE;\n\n // Process all sections and their images\n let processedCount = 0;\n for (const section of galleryData.sections) {\n for (const [index, mediaFile] of section.images.entries()) {\n section.images[index] = await processMediaFile(mediaFile, galleryDir, thumbnailsPath, thumbnailSize, ui);\n }\n\n processedCount += section.images.length;\n }\n\n // Write updated gallery.json\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n ui.success(`Created thumbnails for ${processedCount} media files`);\n\n return processedCount;\n } catch (error) {\n ui.error(`Error creating thumbnails for ${galleryDir}`);\n throw error;\n }\n}\n\n/**\n * Main thumbnails command implementation - generates thumbnails for all galleries\n * @param options - Options specifying gallery path and recursion settings\n * @param ui - ConsolaInstance for logging\n */\nexport async function thumbnails(options: ThumbnailOptions, ui: ConsolaInstance): Promise<void> {\n try {\n // Find all gallery directories\n const galleryDirs = findGalleries(options.gallery, options.recursive);\n if (galleryDirs.length === 0) {\n ui.error('No galleries found.');\n return;\n }\n\n // Process each gallery directory\n let totalGalleries = 0;\n let totalProcessed = 0;\n for (const galleryDir of galleryDirs) {\n const processed = await processGalleryThumbnails(galleryDir, ui);\n\n if (processed > 0) {\n ++totalGalleries;\n totalProcessed += processed;\n }\n }\n\n ui.box(\n `Created thumbnails for ${totalGalleries} ${totalGalleries === 1 ? 'gallery' : 'galleries'} with ${totalProcessed} media ${totalProcessed === 1 ? 'file' : 'files'}`,\n );\n } catch (error) {\n ui.error('Error creating thumbnails');\n throw error;\n }\n}\n","import { execSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport process from 'node:process';\n\nimport { type GalleryData, GalleryDataSchema } from '@simple-photo-gallery/common/src/gallery';\nimport { LogLevels, type ConsolaInstance } from 'consola';\n\nimport { createGallerySocialMediaCardImage, createOptimizedHeaderImage } from './utils';\n\nimport { findGalleries } from '../../utils';\nimport { processGalleryThumbnails } from '../thumbnails';\n\nimport type { BuildOptions } from './types';\n\n/**\n * Checks if a file path refers to a file one folder up from the current directory\n * @param filePath - The file path to check\n * @returns True if the file is exactly one folder up (../filename)\n */\nfunction checkFileIsOneFolderUp(filePath: string): boolean {\n const normalizedPath = path.normalize(filePath);\n const pathParts = normalizedPath.split(path.sep);\n return pathParts.length === 2 && pathParts[0] === '..';\n}\n\n/**\n * Copies photos from gallery subdirectory to main directory when needed\n * @param galleryData - Gallery data containing image paths\n * @param galleryDir - Base gallery directory\n * @param ui - ConsolaInstance for logging\n */\nfunction copyPhotos(galleryData: GalleryData, galleryDir: string, ui: ConsolaInstance): void {\n for (const section of galleryData.sections) {\n for (const image of section.images) {\n if (!checkFileIsOneFolderUp(image.path)) {\n const sourcePath = path.join(galleryDir, 'gallery', image.path);\n const fileName = path.basename(image.path);\n const destPath = path.join(galleryDir, fileName);\n\n ui.debug(`Copying photo to ${destPath}`);\n fs.copyFileSync(sourcePath, destPath);\n }\n }\n }\n}\n\n/**\n * Builds a single gallery by generating thumbnails and creating HTML output\n * @param galleryDir - Directory containing the gallery\n * @param templateDir - Directory containing the Astro template\n * @param ui - ConsolaInstance for logging\n * @param baseUrl - Optional base URL for hosting photos\n */\nasync function buildGallery(galleryDir: string, templateDir: string, ui: ConsolaInstance, baseUrl?: string): Promise<void> {\n ui.start(`Building gallery ${galleryDir}`);\n\n // Generate the thumbnails if needed\n await processGalleryThumbnails(galleryDir, ui);\n\n // Read the gallery.json file\n const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');\n const galleryContent = fs.readFileSync(galleryJsonPath, 'utf8');\n const galleryData = GalleryDataSchema.parse(JSON.parse(galleryContent));\n const socialMediaCardImagePath = path.join(galleryDir, 'gallery', 'images', 'social-media-card.jpg');\n const headerImagePath = path.resolve(path.join(galleryDir, 'gallery'), galleryData.headerImage);\n\n // Create the gallery social media card image\n await createGallerySocialMediaCardImage(headerImagePath, galleryData.title, socialMediaCardImagePath, ui);\n galleryData.metadata.image =\n galleryData.metadata.image || `${galleryData.url || ''}/${path.relative(galleryDir, socialMediaCardImagePath)}`;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n // Create optimized header image\n await createOptimizedHeaderImage(headerImagePath, path.join(galleryDir, 'gallery', 'images'), ui);\n\n // Check if the photos need to be copied. Not needed if the baseUrl is provided.\n if (!baseUrl) {\n const shouldCopyPhotos = galleryData.sections.some((section) =>\n section.images.some((image) => !checkFileIsOneFolderUp(image.path)),\n );\n\n if (\n shouldCopyPhotos &&\n (await ui.prompt('All photos need to be copied. Are you sure you want to continue?', { type: 'confirm' }))\n ) {\n ui.debug('Copying photos');\n copyPhotos(galleryData, galleryDir, ui);\n }\n }\n\n // If the baseUrl is provided, update the gallery.json file\n if (baseUrl) {\n ui.debug('Updating gallery.json with baseUrl');\n galleryData.mediaBaseUrl = baseUrl;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n\n // Build the template\n ui.debug('Building gallery form template');\n try {\n // Set the environment variable for the gallery.json path that will be used by the template\n process.env.GALLERY_JSON_PATH = galleryJsonPath;\n process.env.GALLERY_OUTPUT_DIR = path.join(galleryDir, 'gallery');\n\n execSync('npx astro build', { cwd: templateDir, stdio: ui.level === LogLevels.debug ? 'inherit' : 'ignore' });\n } catch (error) {\n ui.error(`Build failed for ${galleryDir}`);\n throw error;\n }\n\n // Copy the build output to the output directory\n const outputDir = path.join(galleryDir, 'gallery');\n const buildDir = path.join(outputDir, '_build');\n ui.debug(`Copying build output to ${outputDir}`);\n fs.cpSync(buildDir, outputDir, { recursive: true });\n\n // Move the index.html to the gallery directory\n ui.debug('Moving index.html to gallery directory');\n fs.copyFileSync(path.join(outputDir, 'index.html'), path.join(galleryDir, 'index.html'));\n fs.rmSync(path.join(outputDir, 'index.html'));\n\n // Clean up the _build directory\n ui.debug('Cleaning up build directory');\n fs.rmSync(buildDir, { recursive: true, force: true });\n\n ui.success(`Gallery built successfully`);\n}\n\n/**\n * Main build command implementation - builds HTML galleries from gallery.json files\n * @param options - Options specifying gallery path, recursion, and base URL\n * @param ui - ConsolaInstance for logging\n */\nexport async function build(options: BuildOptions, ui: ConsolaInstance): Promise<void> {\n try {\n // Find all gallery directories\n const galleryDirs = findGalleries(options.gallery, options.recursive);\n if (galleryDirs.length === 0) {\n ui.error('No galleries found.');\n return;\n }\n\n // Get the astro theme directory from the default one\n const themePath = await import.meta.resolve('@simple-photo-gallery/theme-modern/package.json');\n const themeDir = path.dirname(new URL(themePath).pathname);\n\n // Process each gallery directory\n let totalGalleries = 0;\n for (const dir of galleryDirs) {\n const baseUrl = options.baseUrl ? `${options.baseUrl}${path.relative(options.gallery, dir)}` : undefined;\n await buildGallery(path.resolve(dir), themeDir, ui, baseUrl);\n\n ++totalGalleries;\n }\n\n ui.box(`Built ${totalGalleries} ${totalGalleries === 1 ? 'gallery' : 'galleries'} successfully`);\n } catch (error) {\n if (error instanceof Error && error.message.includes('Cannot find package')) {\n ui.error('Theme package not found: @simple-photo-gallery/theme-modern/package.json');\n } else {\n ui.error('Error building gallery');\n }\n\n throw error;\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { findGalleries } from '../../utils';\n\nimport type { CleanOptions } from './types';\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Clean gallery files from a single directory\n * @param galleryDir - Directory containing a gallery\n * @param ui - Consola instance for logging\n */\nasync function cleanGallery(galleryDir: string, ui: ConsolaInstance): Promise<void> {\n let filesRemoved = 0;\n\n // Remove index.html file from the gallery directory\n const indexHtmlPath = path.join(galleryDir, 'index.html');\n if (fs.existsSync(indexHtmlPath)) {\n try {\n fs.rmSync(indexHtmlPath);\n ui.debug(`Removed: ${indexHtmlPath}`);\n filesRemoved++;\n } catch (error) {\n ui.warn(`Failed to remove index.html: ${error}`);\n }\n }\n\n // Remove gallery directory and all its contents\n const galleryPath = path.join(galleryDir, 'gallery');\n if (fs.existsSync(galleryPath)) {\n try {\n fs.rmSync(galleryPath, { recursive: true, force: true });\n ui.debug(`Removed directory: ${galleryPath}`);\n filesRemoved++;\n } catch (error) {\n ui.warn(`Failed to remove gallery directory: ${error}`);\n }\n }\n\n if (filesRemoved > 0) {\n ui.success(`Cleaned gallery at: ${galleryDir}`);\n } else {\n ui.info(`No gallery files found at: ${galleryDir}`);\n }\n}\n\n/**\n * Clean command implementation\n * Removes all gallery-related files and directories\n */\nexport async function clean(options: CleanOptions, ui: ConsolaInstance): Promise<void> {\n try {\n const basePath = path.resolve(options.gallery);\n\n // Check if the base path exists\n if (!fs.existsSync(basePath)) {\n ui.error(`Directory does not exist: ${basePath}`);\n return;\n }\n\n // Find all gallery directories\n const galleryDirs = findGalleries(basePath, options.recursive);\n\n if (galleryDirs.length === 0) {\n ui.info('No galleries found to clean.');\n return;\n }\n\n // Clean each gallery directory\n for (const dir of galleryDirs) {\n await cleanGallery(dir, ui);\n }\n\n ui.box(`Successfully cleaned ${galleryDirs.length} ${galleryDirs.length === 1 ? 'gallery' : 'galleries'}`);\n } catch (error) {\n ui.error('Error cleaning galleries');\n throw error;\n }\n}\n","import path from 'node:path';\n\nimport { IMAGE_EXTENSIONS, VIDEO_EXTENSIONS } from '../../../config';\n\nimport type { MediaFileType } from '../types';\n\n/**\n * Determines the media file type based on file extension\n * @param fileName - Name of the file to check\n * @returns Media file type ('image' or 'video') or null if not supported\n */\nexport function getMediaFileType(fileName: string): MediaFileType | null {\n const ext = path.extname(fileName).toLowerCase();\n if (IMAGE_EXTENSIONS.has(ext)) return 'image';\n if (VIDEO_EXTENSIONS.has(ext)) return 'video';\n return null;\n}\n\n/**\n * Converts a folder name into a properly capitalized title\n * @param folderName - The folder name to convert\n * @returns Formatted title with proper capitalization\n */\nexport function capitalizeTitle(folderName: string): string {\n return folderName\n .replace('-', ' ')\n .replace('_', ' ')\n .split(' ')\n .map((word: string) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n","import { promises as fs } from 'node:fs';\nimport path from 'node:path';\n\nimport { capitalizeTitle, getMediaFileType } from './utils';\n\nimport type { GallerySettingsFromUser, ProcessDirectoryResult, ScanDirectoryResult, ScanOptions, SubGallery } from './types';\nimport type { MediaFile } from '@simple-photo-gallery/common/src/gallery';\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Scans a directory for media files and subdirectories\n * @param dirPath - Path to the directory to scan\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to scan results with media files and subdirectories\n */\nasync function scanDirectory(dirPath: string, ui: ConsolaInstance): Promise<ScanDirectoryResult> {\n const mediaFiles: MediaFile[] = [];\n const subGalleryDirectories: string[] = [];\n\n try {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isFile()) {\n const fullPath = path.join(dirPath, entry.name);\n const mediaType = getMediaFileType(entry.name);\n\n if (mediaType) {\n const mediaFile: MediaFile = {\n type: mediaType,\n path: fullPath,\n width: 0,\n height: 0,\n };\n\n mediaFiles.push(mediaFile);\n }\n } else if (entry.isDirectory() && entry.name !== 'gallery') {\n subGalleryDirectories.push(path.join(dirPath, entry.name));\n }\n }\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n ui.error(`Directory does not exist: ${dirPath}`);\n } else if (error instanceof Error && error.message.includes('ENOTDIR')) {\n ui.error(`Path is not a directory: ${dirPath}`);\n } else {\n ui.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n throw error;\n }\n\n return { mediaFiles, subGalleryDirectories };\n}\n\n/**\n * Prompts the user for gallery settings through interactive CLI\n * @param galleryName - Name of the gallery directory\n * @param defaultImage - Default header image path\n * @param ui - ConsolaInstance for prompting and logging\n * @returns Promise resolving to user-provided gallery settings\n */\nasync function getGallerySettingsFromUser(\n galleryName: string,\n defaultImage: string,\n ui: ConsolaInstance,\n): Promise<GallerySettingsFromUser> {\n ui.info(`Enter gallery settings for the gallery in folder \"${galleryName}\"`);\n\n const title = await ui.prompt('Enter gallery title', { type: 'text', default: 'My Gallery', placeholder: 'My Gallery' });\n const description = await ui.prompt('Enter gallery description', {\n type: 'text',\n default: 'My gallery with fantastic photos.',\n placeholder: 'My gallery with fantastic photos.',\n });\n const url = await ui.prompt('Enter the URL where the gallery will be hosted (important for social media image)', {\n type: 'text',\n default: '',\n placeholder: '',\n });\n const headerImageName = await ui.prompt('Enter the name of the header image', {\n type: 'text',\n default: defaultImage,\n placeholder: defaultImage,\n });\n\n const headerImage = path.join('..', headerImageName);\n\n return { title, description, url, headerImage };\n}\n\n/**\n * Creates a gallery.json file with media files and settings\n * @param mediaFiles - Array of media files to include in gallery\n * @param galleryJsonPath - Path where gallery.json should be created\n * @param subGalleries - Array of sub-galleries to include\n * @param useDefaultSettings - Whether to use default settings or prompt user\n * @param ui - ConsolaInstance for prompting and logging\n */\nasync function createGalleryJson(\n mediaFiles: MediaFile[],\n galleryJsonPath: string,\n subGalleries: SubGallery[] = [],\n useDefaultSettings: boolean,\n ui: ConsolaInstance,\n): Promise<void> {\n const galleryDir = path.dirname(galleryJsonPath);\n\n // Convert media file paths to be relative to gallery.json\n const relativeMediaFiles = mediaFiles.map((file) => ({\n ...file,\n path: path.relative(galleryDir, file.path),\n }));\n\n // Convert subGallery header image paths to be relative to gallery.json\n const relativeSubGalleries = subGalleries.map((subGallery) => ({\n ...subGallery,\n headerImage: subGallery.headerImage ? path.relative(galleryDir, subGallery.headerImage) : '',\n }));\n\n let galleryData = {\n title: 'My Gallery',\n description: 'My gallery with fantastic photos.',\n headerImage: relativeMediaFiles[0]?.path || '',\n metadata: {},\n sections: [\n {\n images: relativeMediaFiles,\n },\n ],\n subGalleries: {\n title: 'Sub Galleries',\n galleries: relativeSubGalleries,\n },\n };\n\n if (!useDefaultSettings) {\n galleryData = {\n ...galleryData,\n ...(await getGallerySettingsFromUser(\n path.basename(path.join(galleryDir, '..')),\n path.basename(mediaFiles[0]?.path || ''),\n ui,\n )),\n };\n }\n\n await fs.writeFile(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n}\n\n/**\n * Processes a directory and its subdirectories to create galleries\n * @param scanPath - Path to scan for media files\n * @param outputPath - Path where gallery should be created\n * @param recursive - Whether to process subdirectories recursively\n * @param useDefaultSettings - Whether to use default settings or prompt user\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to processing results\n */\nasync function processDirectory(\n scanPath: string,\n outputPath: string,\n recursive: boolean,\n useDefaultSettings: boolean,\n ui: ConsolaInstance,\n): Promise<ProcessDirectoryResult> {\n ui.start(`Scanning ${scanPath}`);\n\n let totalFiles = 0;\n let totalGalleries = 1;\n const subGalleries: SubGallery[] = [];\n\n // Scan current directory for media files\n const { mediaFiles, subGalleryDirectories } = await scanDirectory(scanPath, ui);\n totalFiles += mediaFiles.length;\n\n // Process subdirectories only if recursive mode is enabled\n if (recursive) {\n for (const subGalleryDir of subGalleryDirectories) {\n const result = await processDirectory(\n subGalleryDir,\n path.join(outputPath, path.basename(subGalleryDir)),\n recursive,\n useDefaultSettings,\n ui,\n );\n\n totalFiles += result.totalFiles;\n totalGalleries += result.totalGalleries;\n\n // If the result contains a valid subGallery, add it to the list\n if (result.subGallery) {\n subGalleries.push(result.subGallery);\n }\n }\n }\n\n // Create gallery.json if there are media files or subGalleries\n if (mediaFiles.length > 0 || subGalleries.length > 0) {\n const galleryPath = path.join(outputPath, 'gallery');\n const galleryJsonPath = path.join(galleryPath, 'gallery.json');\n\n try {\n // Create output directory\n await fs.mkdir(galleryPath, { recursive: true });\n\n // Create gallery.json for this directory\n await createGalleryJson(mediaFiles, galleryJsonPath, subGalleries, useDefaultSettings, ui);\n\n ui.success(\n `Create gallery with ${mediaFiles.length} files and ${subGalleries.length} subgalleries at: ${galleryJsonPath}`,\n );\n } catch (error) {\n ui.error(`Error creating gallery.json at ${galleryJsonPath}`);\n throw error;\n }\n }\n\n // Return result with suGgallery info if this directory has media files\n const result: ProcessDirectoryResult = { totalFiles, totalGalleries };\n\n // If this directory has media files or subGalleries, create a subGallery in the result\n if (mediaFiles.length > 0 || subGalleries.length > 0) {\n const dirName = path.basename(scanPath);\n result.subGallery = {\n title: capitalizeTitle(dirName),\n headerImage: mediaFiles[0]?.path || '',\n path: path.join('..', dirName),\n };\n }\n\n return result;\n}\n\n/**\n * Main init command implementation - scans directories and creates gallery.json files\n * @param options - Options specifying paths, recursion, and default settings\n * @param ui - ConsolaInstance for logging and user prompts\n */\nexport async function init(options: ScanOptions, ui: ConsolaInstance): Promise<void> {\n try {\n const scanPath = path.resolve(options.photos);\n const outputPath = options.gallery ? path.resolve(options.gallery) : scanPath;\n\n // Process the directory tree with the specified recursion setting\n const result = await processDirectory(scanPath, outputPath, options.recursive, options.default, ui);\n\n ui.box(\n `Created ${result.totalGalleries} ${result.totalGalleries === 1 ? 'gallery' : 'galleries'} with ${result.totalFiles} media ${result.totalFiles === 1 ? 'file' : 'files'}`,\n );\n } catch (error) {\n ui.error('Error initializing gallery');\n throw error;\n }\n}\n","{\n \"name\": \"simple-photo-gallery\",\n \"version\": \"2.0.7\",\n \"description\": \"Simple Photo Gallery CLI\",\n \"license\": \"MIT\",\n \"author\": \"Vladimir Haltakov, Tomasz Rusin\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/SimplePhotoGallery/core\"\n },\n \"homepage\": \"https://simple.photo\",\n \"files\": [\n \"dist\"\n ],\n \"bin\": {\n \"simple-photo-gallery\": \"./dist/index.js\",\n \"spg\": \"./dist/index.js\"\n },\n \"scripts\": {\n \"gallery\": \"tsx src/index.ts\",\n \"clean\": \"rm -rf dist\",\n \"compile\": \"tsc --noEmit\",\n \"build\": \"yarn clean && tsup\",\n \"check\": \"tsc --noEmit && eslint . && prettier --check .\",\n \"lint\": \"eslint .\",\n \"lint:fix\": \"eslint . --fix\",\n \"format\": \"prettier --write .\",\n \"format:fix\": \"prettier --write .\",\n \"test\": \"jest\",\n \"prepublish\": \"npm run build\"\n },\n \"dependencies\": {\n \"@simple-photo-gallery/theme-modern\": \"2.0.2\",\n \"blurhash\": \"^2.0.5\",\n \"commander\": \"^12.0.0\",\n \"consola\": \"^3.4.2\",\n \"exifreader\": \"^4.32.0\",\n \"node-ffprobe\": \"^3.0.0\",\n \"sharp\": \"^0.33.0\",\n \"zod\": \"^4.0.14\"\n },\n \"devDependencies\": {\n \"@eslint/eslintrc\": \"^3.3.1\",\n \"@eslint/js\": \"^9.30.1\",\n \"@simple-photo-gallery/common\": \"workspace:*\",\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/jest\": \"^30.0.0\",\n \"@types/node\": \"^24.0.10\",\n \"@typescript-eslint/eslint-plugin\": \"^8.38.0\",\n \"@typescript-eslint/parser\": \"^8.38.0\",\n \"eslint\": \"^9.30.1\",\n \"eslint-config-prettier\": \"^10.1.5\",\n \"eslint-import-resolver-alias\": \"^1.1.2\",\n \"eslint-import-resolver-typescript\": \"^4.4.4\",\n \"eslint-plugin-import\": \"^2.31.0\",\n \"eslint-plugin-prettier\": \"^5.5.1\",\n \"eslint-plugin-unicorn\": \"^59.0.1\",\n \"fs-extra\": \"^11.3.0\",\n \"jest\": \"^30.0.5\",\n \"prettier\": \"^3.4.2\",\n \"ts-jest\": \"^29.4.1\",\n \"tsup\": \"^8.5.0\",\n \"tsx\": \"^4.20.3\",\n \"typescript\": \"^5.9.0\",\n \"typescript-eslint\": \"^8.38.0\"\n },\n \"type\": \"module\"\n}\n","#!/usr/bin/env node\n\nimport process from 'node:process';\n\nimport { Command } from 'commander';\nimport { createConsola, LogLevels, type ConsolaInstance } from 'consola';\n\nimport { build } from './modules/build';\nimport { clean } from './modules/clean';\nimport { init } from './modules/init';\nimport { thumbnails } from './modules/thumbnails';\n\nimport packageJson from '../package.json' with { type: 'json' };\n\n/** Command line interface program instance */\nconst program = new Command();\n\nprogram\n .name('gallery')\n .description('Simple Photo Gallery CLI')\n .version(packageJson.version)\n .option('-v, --verbose', 'Verbose output (debug level)', false)\n .option('-q, --quiet', 'Minimal output (only warnings/errors)', false)\n .showHelpAfterError(true);\n\n/**\n * Creates a Consola UI instance with appropriate log level based on global options\n * @param globalOpts - Global command options containing verbose/quiet flags\n * @returns ConsolaInstance configured with appropriate log level and tag\n */\nfunction createConsolaUI(globalOpts: ReturnType<typeof program.opts>): ConsolaInstance {\n let level = LogLevels.info;\n\n if (globalOpts.quiet) {\n level = LogLevels.warn;\n } else if (globalOpts.verbose) {\n level = LogLevels.debug;\n }\n\n return createConsola({\n level,\n }).withTag('simple-photo-gallery');\n}\n\n/**\n * Higher-order function that wraps command handlers to provide ConsolaUI instance\n * @param handler - Command handler function that receives options and UI instance\n * @returns Wrapped handler function that creates UI and handles errors\n */\nfunction withConsolaUI<O>(handler: (opts: O, ui: ConsolaInstance) => Promise<void> | void) {\n return async (opts: O) => {\n const ui = createConsolaUI(program.opts());\n try {\n await handler(opts, ui);\n } catch (error) {\n ui.debug(error);\n\n process.exitCode = 1;\n }\n };\n}\n\nprogram\n .command('init')\n .description('Initialize a gallery by scaning a folder for images and videos')\n .option(\n '-p, --photos <path>',\n 'Path to the folder where the photos are stored. Default: current working directory',\n process.cwd(),\n )\n .option(\n '-g, --gallery <path>',\n 'Path to the directory where the gallery will be initialized. Default: same directory as the photos folder',\n )\n .option('-r, --recursive', 'Recursively create galleries from all photos subdirectories', false)\n .option('-d, --default', 'Use default gallery settings instead of asking the user', false)\n .action(withConsolaUI(init));\n\nprogram\n .command('thumbnails')\n .description('Create thumbnails for all media files in the gallery')\n .option('-g, --gallery <path>', 'Path to the directory of the gallery. Default: current working directory', process.cwd())\n .option('-r, --recursive', 'Scan subdirectories recursively', false)\n .action(withConsolaUI(thumbnails));\n\nprogram\n .command('build')\n .description('Build the HTML gallery in the specified directory')\n .option('-g, --gallery <path>', 'Path to the directory of the gallery. Default: current working directory', process.cwd())\n .option('-r, --recursive', 'Scan subdirectories recursively', false)\n .option('-b, --base-url <url>', 'Base URL where the photos are hosted')\n .action(withConsolaUI(build));\n\nprogram\n .command('clean')\n .description('Remove all gallery files and folders (index.html, gallery/)')\n .option('-g, --gallery <path>', 'Path to the directory of the gallery. Default: current working directory', process.cwd())\n .option('-r, --recursive', 'Clean subdirectories recursively', false)\n .action(withConsolaUI(clean));\n\nprogram.parse();\n"]}
1
+ {"version":3,"sources":["../../common/src/gallery.ts","../src/config/index.ts","../src/utils/image.ts","../src/modules/build/utils/index.ts","../src/utils/index.ts","../src/modules/thumbnails/utils/index.ts","../src/utils/blurhash.ts","../src/utils/video.ts","../src/modules/thumbnails/index.ts","../src/modules/build/index.ts","../src/modules/clean/index.ts","../src/modules/init/utils/index.ts","../src/modules/init/index.ts","../src/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","process","LogLevels","result","telemetryService","axios"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\n/** Zod schema for thumbnail metadata including path and dimensions */\nexport const ThumbnailSchema = z.object({\n path: z.string(),\n pathRetina: z.string(),\n width: z.number(),\n height: z.number(),\n blurHash: z.string().optional(),\n});\n\n/** Zod schema for media file metadata including type, dimensions, and thumbnail info */\nexport const MediaFileSchema = z.object({\n type: z.enum(['image', 'video']),\n path: z.string(),\n alt: z.string().optional(),\n width: z.number(),\n height: z.number(),\n thumbnail: ThumbnailSchema.optional(),\n lastMediaTimestamp: z.string().optional(),\n});\n\n/** Zod schema for a gallery section containing title, description, and media files */\nexport const GallerySectionSchema = z.object({\n title: z.string().optional(),\n description: z.string().optional(),\n images: z.array(MediaFileSchema),\n});\n\n/** Zod schema for sub-gallery metadata including title, header image, and path */\nexport const SubGallerySchema = z.object({\n title: z.string(),\n headerImage: z.string(),\n path: z.string(),\n});\n\n/** Zod schema for complete gallery data including metadata, sections, and sub-galleries */\nexport const GalleryMetadataSchema = z.object({\n image: z.string().optional(),\n imageWidth: z.number().optional(),\n imageHeight: z.number().optional(),\n ogUrl: z.string().optional(),\n ogType: z.string().optional(),\n ogSiteName: z.string().optional(),\n twitterSite: z.string().optional(),\n twitterCreator: z.string().optional(),\n author: z.string().optional(),\n keywords: z.string().optional(),\n canonicalUrl: z.string().optional(),\n language: z.string().optional(),\n robots: z.string().optional(),\n});\n\n/** Zod schema for complete gallery data including metadata, sections, and sub-galleries */\nexport const GalleryDataSchema = z.object({\n title: z.string(),\n description: z.string(),\n url: z.string().optional(),\n headerImage: z.string(),\n thumbnailSize: z.number().optional(),\n metadata: GalleryMetadataSchema,\n galleryOutputPath: z.string().optional(),\n mediaBaseUrl: z.string().optional(),\n analyticsScript: z.string().optional(),\n sections: z.array(GallerySectionSchema),\n subGalleries: z.object({ title: z.string(), galleries: z.array(SubGallerySchema) }),\n});\n\n/** TypeScript type for thumbnail metadata */\nexport type Thumbnail = z.infer<typeof ThumbnailSchema>;\n\n/** TypeScript type for media file metadata */\nexport type MediaFile = z.infer<typeof MediaFileSchema>;\n\n/** TypeScript type for gallery section data */\nexport type GallerySection = z.infer<typeof GallerySectionSchema>;\n\n/** TypeScript type for sub-gallery metadata */\nexport type SubGallery = z.infer<typeof SubGallerySchema>;\n\n/** TypeScript type for gallery metadata */\nexport type GalleryMetadata = z.infer<typeof GalleryMetadataSchema>;\n\n/** TypeScript type for complete gallery data structure */\nexport type GalleryData = z.infer<typeof GalleryDataSchema>;\n","/** Default thumbnail size in pixels */\nexport const DEFAULT_THUMBNAIL_SIZE = 300;\n\n/** Set of supported image file extensions */\nexport const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp', '.tiff', '.tif', '.svg', '.avif']);\n\n/** Set of supported video file extensions */\nexport const VIDEO_EXTENSIONS = new Set(['.mp4', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv', '.m4v', '.3gp']);\n\n/** Set of supported header image landscape widths */\nexport const HEADER_IMAGE_LANDSCAPE_WIDTHS = [3840, 2560, 1920, 1280, 960, 640];\n\n/** Set of supported header image portrait widths */\nexport const HEADER_IMAGE_PORTRAIT_WIDTHS = [1080, 720, 480, 360];\n","import ExifReader from 'exifreader';\nimport sharp from 'sharp';\n\nimport type { Dimensions, ImageWithMetadata } from '../types';\nimport type { FormatEnum, Metadata, Sharp } from 'sharp';\n\n/**\n * Loads an image and auto-rotates it based on EXIF orientation.\n * @param imagePath - Path to the image file\n * @returns Promise resolving to Sharp image instance\n */\nexport async function loadImage(imagePath: string): Promise<Sharp> {\n return sharp(imagePath).rotate();\n}\n\n/**\n * Loads an image and its metadata, auto-rotating it based on EXIF orientation and swapping dimensions if needed.\n * @param imagePath - Path to the image file\n * @returns Promise resolving to ImageWithMetadata object containing Sharp image instance and metadata\n */\nexport async function loadImageWithMetadata(imagePath: string): Promise<ImageWithMetadata> {\n const image = sharp(imagePath);\n const metadata = await image.metadata();\n\n // Auto-rotate based on EXIF orientation\n image.rotate();\n\n // EXIF orientation values 5, 6, 7, 8 require dimension swap after rotation\n const needsDimensionSwap = metadata.orientation && metadata.orientation >= 5 && metadata.orientation <= 8;\n\n // Update metadata with swapped dimensions if needed\n if (needsDimensionSwap) {\n const originalWidth = metadata.width;\n metadata.width = metadata.height;\n metadata.height = originalWidth;\n }\n\n return { image, metadata };\n}\n\n/**\n * Utility function to resize and save thumbnail using Sharp. The functions avoids upscaling the image and only reduces the size if necessary.\n * @param image - Sharp image instance\n * @param outputPath - Path where thumbnail should be saved\n * @param width - Target width for thumbnail\n * @param height - Target height for thumbnail\n */\nexport async function resizeImage(\n image: Sharp,\n outputPath: string,\n width: number,\n height: number,\n format: keyof FormatEnum = 'avif',\n): Promise<void> {\n // Resize the image without enlarging it\n await image.resize(width, height, { withoutEnlargement: true }).toFormat(format).toFile(outputPath);\n}\n\n/**\n * Crops and resizes an image to a target aspect ratio, avoiding upscaling the image.\n * @param image - Sharp image instance\n * @param outputPath - Path where the image should be saved\n * @param width - Target width for the image\n * @param height - Target height for the image\n */\nexport async function cropAndResizeImage(\n image: Sharp,\n outputPath: string,\n width: number,\n height: number,\n format: keyof FormatEnum = 'avif',\n): Promise<void> {\n // Apply resize with cover fit and without enlargement\n await image\n .resize(width, height, {\n fit: 'cover',\n withoutEnlargement: true,\n })\n .toFormat(format)\n .toFile(outputPath);\n}\n\n/**\n * Extracts description from image EXIF data\n * @param metadata - Sharp metadata object containing EXIF data\n * @returns Promise resolving to image description or undefined if not found\n */\nexport async function getImageDescription(imagePath: string): Promise<string | undefined> {\n try {\n const tags = await ExifReader.load(imagePath);\n\n // Description\n if (tags.description?.description) return tags.description.description;\n\n // ImageDescription\n if (tags.ImageDescription?.description) return tags.ImageDescription.description;\n\n // UserComment\n if (\n tags.UserComment &&\n typeof tags.UserComment === 'object' &&\n tags.UserComment !== null &&\n 'description' in tags.UserComment\n ) {\n return (tags.UserComment as { description: string }).description;\n }\n\n // ExtDescrAccessibility\n if (tags.ExtDescrAccessibility?.description) return tags.ExtDescrAccessibility.description;\n\n // Caption/Abstract\n if (tags['Caption/Abstract']?.description) return tags['Caption/Abstract'].description;\n\n // XP Title\n if (tags.XPTitle?.description) return tags.XPTitle.description;\n\n // XP Comment\n if (tags.XPComment?.description) return tags.XPComment.description;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Creates regular and retina thumbnails for an image while maintaining aspect ratio\n * @param image - Sharp image instance\n * @param metadata - Image metadata containing dimensions\n * @param outputPath - Path where thumbnail should be saved\n * @param outputPathRetina - Path where retina thumbnail should be saved\n * @param size - Target size of the longer side of the thumbnail\n * @returns Promise resolving to thumbnail dimensions\n */\nexport async function createImageThumbnails(\n image: Sharp,\n metadata: Metadata,\n outputPath: string,\n outputPathRetina: string,\n size: number,\n): Promise<Dimensions> {\n // Get the original dimensions\n const originalWidth = metadata.width || 0;\n const originalHeight = metadata.height || 0;\n\n if (originalWidth === 0 || originalHeight === 0) {\n throw new Error('Invalid image dimensions');\n }\n\n // Calculate the new size maintaining aspect ratio\n const aspectRatio = originalWidth / originalHeight;\n\n let width: number;\n let height: number;\n\n if (originalWidth > originalHeight) {\n width = size;\n height = Math.round(size / aspectRatio);\n } else {\n width = Math.round(size * aspectRatio);\n height = size;\n }\n\n // Resize the image and create the thumbnails\n await resizeImage(image, outputPath, width, height);\n await resizeImage(image, outputPathRetina, width * 2, height * 2);\n\n // Return the dimensions of the thumbnail\n return { width, height };\n}\n","import { Buffer } from 'node:buffer';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport sharp from 'sharp';\n\nimport { HEADER_IMAGE_LANDSCAPE_WIDTHS, HEADER_IMAGE_PORTRAIT_WIDTHS } from '../../../config';\nimport { cropAndResizeImage, loadImage } from '../../../utils/image';\n\nimport type { ConsolaInstance } from 'consola';\n\n/** __dirname workaround for ESM modules */\nconst __dirname = path.dirname(new URL(import.meta.url).pathname);\n\n/**\n * Helper function to resolve paths relative to current file\n * @param segments - Path segments to resolve relative to current directory\n * @returns Resolved absolute path\n */\nexport function resolveFromCurrentDir(...segments: string[]): string {\n return path.resolve(__dirname, ...segments);\n}\n\n/**\n * Creates a social media card image for a gallery\n * @param headerPhotoPath - Path to the header photo\n * @param title - Title of the gallery\n * @param ouputPath - Output path for the social media card image\n * @param ui - ConsolaInstance for logging\n */\nexport async function createGallerySocialMediaCardImage(\n headerPhotoPath: string,\n title: string,\n ouputPath: string,\n ui: ConsolaInstance,\n): Promise<void> {\n ui.start(`Creating social media card image`);\n\n if (fs.existsSync(ouputPath)) {\n ui.success(`Social media card image already exists`);\n return;\n }\n\n // Read and resize the header image to 1200x631 using fit\n const image = await loadImage(headerPhotoPath);\n const resizedImageBuffer = await image.resize(1200, 631, { fit: 'cover' }).jpeg({ quality: 90 }).toBuffer();\n\n // Save the resized image as social media card\n const outputPath = ouputPath;\n await sharp(resizedImageBuffer).toFile(outputPath);\n\n // Create SVG with title and description\n const svgText = `\n <svg width=\"1200\" height=\"631\" xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <style>\n .title { font-family: Arial, sans-serif; font-size: 96px; font-weight: bold; fill: white; text-anchor: middle; }\n .description { font-family: Arial, sans-serif; font-size: 48px; fill: white; text-anchor: middle; }\n </style>\n </defs>\n <text x=\"600\" y=\"250\" class=\"title\">${title}</text>\n </svg>\n `;\n\n // Composite the text overlay on top of the resized image\n const finalImageBuffer = await sharp(resizedImageBuffer)\n .composite([{ input: Buffer.from(svgText), top: 0, left: 0 }])\n .jpeg({ quality: 90 })\n .toBuffer();\n\n // Save the final image with text overlay\n await sharp(finalImageBuffer).toFile(outputPath);\n\n ui.success(`Created social media card image successfully`);\n}\n\nexport async function createOptimizedHeaderImage(\n headerPhotoPath: string,\n outputFolder: string,\n ui: ConsolaInstance,\n): Promise<void> {\n ui.start(`Creating optimized header images`);\n\n const image = await loadImage(headerPhotoPath);\n\n // Create landscape header images\n const landscapeYFactor = 3 / 4;\n for (const width of HEADER_IMAGE_LANDSCAPE_WIDTHS) {\n ui.debug(`Creating landscape header image ${width}`);\n\n if (fs.existsSync(path.join(outputFolder, `header_landscape_${width}.avif`))) {\n ui.debug(`Landscape header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_landscape_${width}.avif`),\n width,\n width * landscapeYFactor,\n 'avif',\n );\n }\n\n if (fs.existsSync(path.join(outputFolder, `header_landscape_${width}.jpg`))) {\n ui.debug(`Landscape header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_landscape_${width}.jpg`),\n width,\n width * landscapeYFactor,\n 'jpg',\n );\n }\n }\n\n // Create portrait header images\n const portraitYFactor = 4 / 3;\n for (const width of HEADER_IMAGE_PORTRAIT_WIDTHS) {\n ui.debug(`Creating portrait header image ${width}`);\n\n if (fs.existsSync(path.join(outputFolder, `header_portrait_${width}.avif`))) {\n ui.debug(`Portrait header image ${width} AVIF already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_portrait_${width}.avif`),\n width,\n width * portraitYFactor,\n 'avif',\n );\n }\n\n if (fs.existsSync(path.join(outputFolder, `header_portrait_${width}.jpg`))) {\n ui.debug(`Portrait header image ${width} JPG already exists`);\n } else {\n await cropAndResizeImage(\n image.clone(),\n path.join(outputFolder, `header_portrait_${width}.jpg`),\n width,\n width * portraitYFactor,\n 'jpg',\n );\n }\n }\n\n ui.success(`Created optimized header image successfully`);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Finds all gallery directories that contain a gallery/gallery.json file.\n *\n * @param basePath - The base directory to search from\n * @param recursive - Whether to search subdirectories recursively\n * @returns Array of paths to directories containing gallery/gallery.json files\n */\nexport function findGalleries(basePath: string, recursive: boolean): string[] {\n const galleryDirs: string[] = [];\n\n // Check basePath itself\n const galleryJsonPath = path.join(basePath, 'gallery', 'gallery.json');\n if (fs.existsSync(galleryJsonPath)) {\n galleryDirs.push(basePath);\n }\n\n // If recursive, search all subdirectories\n if (recursive) {\n try {\n const entries = fs.readdirSync(basePath, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name !== 'gallery') {\n const subPath = path.join(basePath, entry.name);\n const subResults = findGalleries(subPath, recursive);\n galleryDirs.push(...subResults);\n }\n }\n } catch {\n // Silently ignore errors when reading directories\n }\n }\n\n return galleryDirs;\n}\n\n/**\n * Handles file processing errors with appropriate user-friendly messages\n * @param error - The error that occurred during file processing\n * @param filename - Name of the file that caused the error\n * @param ui - ConsolaInstance for logging messages\n */\nexport function handleFileProcessingError(error: unknown, filename: string, ui: ConsolaInstance): void {\n if (error instanceof Error && (error.message.includes('ffprobe') || error.message.includes('ffmpeg'))) {\n // Handle ffmpeg error\n ui.warn(\n `Error processing ${filename}: ffprobe (part of ffmpeg) is required to process videos. Please install ffmpeg and ensure it is available in your PATH`,\n );\n } else if (error instanceof Error && error.message.includes('unsupported image format')) {\n // Handle unsupported image format error\n ui.warn(`Error processing ${filename}: unsupported image format`);\n } else {\n // Handle unknown error\n ui.warn(`Error processing ${filename}`);\n }\n\n ui.debug(error);\n}\n\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 { promises as fs } from 'node:fs';\n\n/**\n * Gets the last modification time of a file\n * @param filePath - Path to the file\n * @returns Promise resolving to the file's modification date\n */\nexport async function getFileMtime(filePath: string): Promise<Date> {\n const stats = await fs.stat(filePath);\n return stats.mtime;\n}\n","import { encode } from 'blurhash';\n\nimport { loadImage } from './image';\n\n/**\n * Generates a BlurHash from an image file or Sharp instance\n * @param imagePath - Path to image file or Sharp instance\n * @param componentX - Number of x components (default: 4)\n * @param componentY - Number of y components (default: 3)\n * @returns Promise resolving to BlurHash string\n */\nexport async function generateBlurHash(imagePath: string, componentX: number = 4, componentY: number = 3): Promise<string> {\n const image = await loadImage(imagePath);\n\n // Resize to small size for BlurHash computation to improve performance\n // BlurHash doesn't need high resolution\n const { data, info } = await image\n .resize(32, 32, { fit: 'inside' })\n .ensureAlpha()\n .raw()\n .toBuffer({ resolveWithObject: true });\n\n // Convert to Uint8ClampedArray format expected by blurhash\n const pixels = new Uint8ClampedArray(data.buffer);\n\n // Generate BlurHash\n return encode(pixels, info.width, info.height, componentX, componentY);\n}\n","import { spawn } from 'node:child_process';\nimport { promises as fs } from 'node:fs';\n\nimport ffprobe from 'node-ffprobe';\nimport sharp from 'sharp';\n\nimport { resizeImage } from './image';\n\nimport type { Dimensions } from '../types';\nimport type { Buffer } from 'node:buffer';\n\n/**\n * Gets video dimensions using ffprobe\n * @param filePath - Path to the video file\n * @returns Promise resolving to video dimensions\n * @throws Error if no video stream found or invalid dimensions\n */\nexport async function getVideoDimensions(filePath: string): Promise<Dimensions> {\n const data = await ffprobe(filePath);\n const videoStream = data.streams.find((stream) => stream.codec_type === 'video');\n\n if (!videoStream) {\n throw new Error('No video stream found');\n }\n\n const dimensions = {\n width: videoStream.width || 0,\n height: videoStream.height || 0,\n };\n\n if (dimensions.width === 0 || dimensions.height === 0) {\n throw new Error('Invalid video dimensions');\n }\n\n return dimensions;\n}\n\n/**\n * Creates regular and retina thumbnails for a video by extracting the first frame\n * @param inputPath - Path to the video file\n * @param videoDimensions - Original video dimensions\n * @param outputPath - Path where thumbnail should be saved\n * @param outputPathRetina - Path where retina thumbnail should be saved\n * @param height - Target height for thumbnail\n * @param verbose - Whether to enable verbose ffmpeg output\n * @returns Promise resolving to thumbnail dimensions\n */\nexport async function createVideoThumbnails(\n inputPath: string,\n videoDimensions: Dimensions,\n outputPath: string,\n outputPathRetina: string,\n height: number,\n verbose: boolean = false,\n): Promise<Dimensions> {\n // Calculate width maintaining aspect ratio\n const aspectRatio = videoDimensions.width / videoDimensions.height;\n const width = Math.round(height * aspectRatio);\n\n // Use ffmpeg to extract first frame as a temporary file, then process with sharp\n const tempFramePath = `${outputPath}.temp.png`;\n\n return new Promise((resolve, reject) => {\n // Extract first frame using ffmpeg\n const ffmpeg = spawn('ffmpeg', [\n '-i',\n inputPath,\n '-vframes',\n '1',\n '-y',\n '-loglevel',\n verbose ? 'error' : 'quiet',\n tempFramePath,\n ]);\n\n ffmpeg.stderr.on('data', (data: Buffer) => {\n // FFmpeg writes normal output to stderr, so we don't treat this as an error\n console.log(`ffmpeg: ${data.toString()}`);\n });\n\n ffmpeg.on('close', async (code: number) => {\n if (code === 0) {\n try {\n // Process the extracted frame with sharp\n const frameImage = sharp(tempFramePath);\n await resizeImage(frameImage, outputPath, width, height);\n await resizeImage(frameImage, outputPathRetina, width * 2, height * 2);\n\n // Clean up temporary file\n try {\n await fs.unlink(tempFramePath);\n } catch {\n // Ignore cleanup errors\n }\n\n resolve({ width, height });\n } catch (sharpError) {\n reject(new Error(`Failed to process extracted frame: ${sharpError}`));\n }\n } else {\n reject(new Error(`ffmpeg exited with code ${code}`));\n }\n });\n\n ffmpeg.on('error', (error: Error) => {\n reject(new Error(`Failed to start ffmpeg: ${error.message}`));\n });\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { GalleryDataSchema, type MediaFile } from '@simple-photo-gallery/common/src/gallery';\nimport { LogLevels, type ConsolaInstance } from 'consola';\n\nimport { getFileMtime } from './utils';\n\nimport { DEFAULT_THUMBNAIL_SIZE } from '../../config';\nimport { findGalleries, handleFileProcessingError } from '../../utils';\nimport { generateBlurHash } from '../../utils/blurhash';\nimport { getImageDescription, createImageThumbnails, loadImageWithMetadata } from '../../utils/image';\nimport { getVideoDimensions, createVideoThumbnails } from '../../utils/video';\n\nimport type { ThumbnailOptions } from './types';\nimport type { CommandResultSummary } from '../telemetry/types';\n\n/**\n * Processes an image file to create thumbnail and extract metadata\n * @param imagePath - Path to the image file\n * @param thumbnailPath - Path where thumbnail should be saved\n * @param thumbnailPathRetina - Path where retina thumbnail should be saved\n * @param thumbnailSize - Target size for thumbnail\n * @param lastMediaTimestamp - Optional timestamp to check if processing can be skipped\n * @returns Promise resolving to updated MediaFile or undefined if skipped\n */\nexport async function processImage(\n imagePath: string,\n thumbnailPath: string,\n thumbnailPathRetina: string,\n thumbnailSize: number,\n lastMediaTimestamp?: Date,\n): Promise<MediaFile | undefined> {\n // Get the last media timestamp\n const fileMtime = await getFileMtime(imagePath);\n\n // Check if processing of the file can be skipped\n if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs.existsSync(thumbnailPath)) {\n return undefined;\n }\n\n // Load the image and get metadata first to check orientation\n const { image, metadata } = await loadImageWithMetadata(imagePath);\n\n // Get the image dimensions\n const imageDimensions = {\n width: metadata.width || 0,\n height: metadata.height || 0,\n };\n\n if (imageDimensions.width === 0 || imageDimensions.height === 0) {\n throw new Error('Invalid image dimensions');\n }\n\n // Get the image description\n const description = await getImageDescription(imagePath);\n\n // Create the thumbnails\n const thumbnailDimensions = await createImageThumbnails(\n image,\n metadata,\n thumbnailPath,\n thumbnailPathRetina,\n thumbnailSize,\n );\n\n // Generate BlurHash from the thumbnail\n const blurHash = await generateBlurHash(thumbnailPath);\n\n // Return the updated media file\n return {\n type: 'image',\n path: imagePath,\n alt: description,\n width: imageDimensions.width,\n height: imageDimensions.height,\n thumbnail: {\n path: thumbnailPath,\n pathRetina: thumbnailPathRetina,\n width: thumbnailDimensions.width,\n height: thumbnailDimensions.height,\n blurHash,\n },\n lastMediaTimestamp: fileMtime.toISOString(),\n };\n}\n\n/**\n * Processes a video file to create thumbnail and extract metadata\n * @param videoPath - Path to the video file\n * @param thumbnailPath - Path where thumbnail should be saved\n * @param thumbnailPathRetina - Path where retina thumbnail should be saved\n * @param thumbnailSize - Target size for thumbnail\n * @param verbose - Whether to enable verbose output\n * @param lastMediaTimestamp - Optional timestamp to check if processing can be skipped\n * @returns Promise resolving to updated MediaFile or undefined if skipped\n */\nasync function processVideo(\n videoPath: string,\n thumbnailPath: string,\n thumbnailPathRetina: string,\n thumbnailSize: number,\n verbose: boolean,\n lastMediaTimestamp?: Date,\n): Promise<MediaFile | undefined> {\n // Get the last media timestamp\n const fileMtime = await getFileMtime(videoPath);\n\n // Check if processing of the file can be skipped\n if (lastMediaTimestamp && fileMtime <= lastMediaTimestamp && fs.existsSync(thumbnailPath)) {\n return undefined;\n }\n\n // Get the video dimensions\n const videoDimensions = await getVideoDimensions(videoPath);\n\n // Create the thumbnail\n const thumbnailDimensions = await createVideoThumbnails(\n videoPath,\n videoDimensions,\n thumbnailPath,\n thumbnailPathRetina,\n thumbnailSize,\n verbose,\n );\n\n // Generate BlurHash from the thumbnail\n const blurHash = await generateBlurHash(thumbnailPath);\n\n return {\n type: 'video',\n path: videoPath,\n alt: undefined,\n width: videoDimensions.width,\n height: videoDimensions.height,\n thumbnail: {\n path: thumbnailPath,\n pathRetina: thumbnailPathRetina,\n width: thumbnailDimensions.width,\n height: thumbnailDimensions.height,\n blurHash,\n },\n lastMediaTimestamp: fileMtime.toISOString(),\n };\n}\n\n/**\n * Processes a media file to generate thumbnails and update metadata\n * @param mediaFile - Media file to process\n * @param galleryDir - Gallery directory path\n * @param thumbnailsPath - Directory where thumbnails are stored\n * @param thumbnailSize - Target size for thumbnails\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to updated MediaFile\n */\nasync function processMediaFile(\n mediaFile: MediaFile,\n galleryDir: string,\n thumbnailsPath: string,\n thumbnailSize: number,\n ui: ConsolaInstance,\n): Promise<MediaFile> {\n try {\n // Resolve the path relative to the gallery.json file location, not the gallery directory\n const galleryJsonDir = path.join(galleryDir, 'gallery');\n const filePath = path.resolve(path.join(galleryJsonDir, mediaFile.path));\n\n const fileName = path.basename(filePath);\n const fileNameWithoutExt = path.parse(fileName).name;\n const thumbnailFileName = `${fileNameWithoutExt}.avif`;\n const thumbnailPath = path.join(thumbnailsPath, thumbnailFileName);\n const thumbnailPathRetina = thumbnailPath.replace('.avif', '@2x.avif');\n const relativeThumbnailPath = path.relative(galleryJsonDir, thumbnailPath);\n const relativeThumbnailRetinaPath = path.relative(galleryJsonDir, thumbnailPathRetina);\n\n const lastMediaTimestamp = mediaFile.lastMediaTimestamp ? new Date(mediaFile.lastMediaTimestamp) : undefined;\n const verbose = ui.level === LogLevels.debug;\n\n ui.debug(` Processing ${mediaFile.type}: ${fileName}`);\n\n const updatedMediaFile = await (mediaFile.type === 'image'\n ? processImage(filePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, lastMediaTimestamp)\n : processVideo(filePath, thumbnailPath, thumbnailPathRetina, thumbnailSize, verbose, lastMediaTimestamp));\n\n if (!updatedMediaFile) {\n ui.debug(` Skipping ${fileName} because it has already been processed`);\n\n // Check if we need to generate BlurHash for existing thumbnail\n if (mediaFile.thumbnail && !mediaFile.thumbnail.blurHash && fs.existsSync(thumbnailPath)) {\n try {\n const blurHash = await generateBlurHash(thumbnailPath);\n return {\n ...mediaFile,\n thumbnail: {\n ...mediaFile.thumbnail,\n blurHash,\n },\n };\n } catch (error) {\n ui.debug(` Failed to generate BlurHash for ${fileName}:`, error);\n }\n }\n\n return mediaFile;\n }\n\n updatedMediaFile.path = mediaFile.path;\n if (updatedMediaFile.thumbnail) {\n updatedMediaFile.thumbnail.path = relativeThumbnailPath;\n updatedMediaFile.thumbnail.pathRetina = relativeThumbnailRetinaPath;\n }\n\n return updatedMediaFile;\n } catch (error) {\n handleFileProcessingError(error, path.basename(mediaFile.path), ui);\n\n return mediaFile;\n }\n}\n\n/**\n * Processes all media files in a gallery to generate thumbnails\n * @param galleryDir - Directory containing the gallery\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to the number of files processed\n */\nexport async function processGalleryThumbnails(galleryDir: string, ui: ConsolaInstance): Promise<number> {\n const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');\n const thumbnailsPath = path.join(galleryDir, 'gallery', 'images');\n\n ui.start(`Creating thumbnails: ${galleryDir}`);\n\n try {\n // Ensure thumbnails directory exists\n fs.mkdirSync(thumbnailsPath, { recursive: true });\n\n // Read gallery.json\n const galleryContent = fs.readFileSync(galleryJsonPath, 'utf8');\n const galleryData = GalleryDataSchema.parse(JSON.parse(galleryContent));\n\n const thumbnailSize = galleryData.thumbnailSize || DEFAULT_THUMBNAIL_SIZE;\n\n // Process all sections and their images\n let processedCount = 0;\n for (const section of galleryData.sections) {\n for (const [index, mediaFile] of section.images.entries()) {\n section.images[index] = await processMediaFile(mediaFile, galleryDir, thumbnailsPath, thumbnailSize, ui);\n }\n\n processedCount += section.images.length;\n }\n\n // Write updated gallery.json\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n ui.success(`Created thumbnails for ${processedCount} media files`);\n\n return processedCount;\n } catch (error) {\n ui.error(`Error creating thumbnails for ${galleryDir}`);\n throw error;\n }\n}\n\n/**\n * Main thumbnails command implementation - generates thumbnails for all galleries\n * @param options - Options specifying gallery path and recursion settings\n * @param ui - ConsolaInstance for logging\n */\nexport async function thumbnails(options: ThumbnailOptions, ui: ConsolaInstance): Promise<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 { type GalleryData, GalleryDataSchema } from '@simple-photo-gallery/common/src/gallery';\nimport { LogLevels, type ConsolaInstance } from 'consola';\n\nimport { createGallerySocialMediaCardImage, createOptimizedHeaderImage } from './utils';\n\nimport { findGalleries } from '../../utils';\nimport { processGalleryThumbnails } from '../thumbnails';\n\nimport type { BuildOptions } from './types';\nimport type { CommandResultSummary } from '../telemetry/types';\n\n/**\n * Checks if a file path refers to a file one folder up from the current directory\n * @param filePath - The file path to check\n * @returns True if the file is exactly one folder up (../filename)\n */\nfunction checkFileIsOneFolderUp(filePath: string): boolean {\n const normalizedPath = path.normalize(filePath);\n const pathParts = normalizedPath.split(path.sep);\n return pathParts.length === 2 && pathParts[0] === '..';\n}\n\n/**\n * Copies photos from gallery subdirectory to main directory when needed\n * @param galleryData - Gallery data containing image paths\n * @param galleryDir - Base gallery directory\n * @param ui - ConsolaInstance for logging\n */\nfunction copyPhotos(galleryData: GalleryData, galleryDir: string, ui: ConsolaInstance): void {\n for (const section of galleryData.sections) {\n for (const image of section.images) {\n if (!checkFileIsOneFolderUp(image.path)) {\n const sourcePath = path.join(galleryDir, 'gallery', image.path);\n const fileName = path.basename(image.path);\n const destPath = path.join(galleryDir, fileName);\n\n ui.debug(`Copying photo to ${destPath}`);\n fs.copyFileSync(sourcePath, destPath);\n }\n }\n }\n}\n\n/**\n * Builds a single gallery by generating thumbnails and creating HTML output\n * @param galleryDir - Directory containing the gallery\n * @param templateDir - Directory containing the Astro template\n * @param ui - ConsolaInstance for logging\n * @param baseUrl - Optional base URL for hosting photos\n */\nasync function buildGallery(galleryDir: string, templateDir: string, ui: ConsolaInstance, baseUrl?: string): Promise<void> {\n ui.start(`Building gallery ${galleryDir}`);\n\n // Generate the thumbnails if needed\n await processGalleryThumbnails(galleryDir, ui);\n\n // Read the gallery.json file\n const galleryJsonPath = path.join(galleryDir, 'gallery', 'gallery.json');\n const galleryContent = fs.readFileSync(galleryJsonPath, 'utf8');\n const galleryData = GalleryDataSchema.parse(JSON.parse(galleryContent));\n const socialMediaCardImagePath = path.join(galleryDir, 'gallery', 'images', 'social-media-card.jpg');\n const headerImagePath = path.resolve(path.join(galleryDir, 'gallery'), galleryData.headerImage);\n\n // Create the gallery social media card image\n await createGallerySocialMediaCardImage(headerImagePath, galleryData.title, socialMediaCardImagePath, ui);\n galleryData.metadata.image =\n galleryData.metadata.image || `${galleryData.url || ''}/${path.relative(galleryDir, socialMediaCardImagePath)}`;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n\n // Create optimized header image\n await createOptimizedHeaderImage(headerImagePath, path.join(galleryDir, 'gallery', 'images'), ui);\n\n // Check if the photos need to be copied. Not needed if the baseUrl is provided.\n if (!baseUrl) {\n const shouldCopyPhotos = galleryData.sections.some((section) =>\n section.images.some((image) => !checkFileIsOneFolderUp(image.path)),\n );\n\n if (\n shouldCopyPhotos &&\n (await ui.prompt('All photos need to be copied. Are you sure you want to continue?', { type: 'confirm' }))\n ) {\n ui.debug('Copying photos');\n copyPhotos(galleryData, galleryDir, ui);\n }\n }\n\n // If the baseUrl is provided, update the gallery.json file\n if (baseUrl) {\n ui.debug('Updating gallery.json with baseUrl');\n galleryData.mediaBaseUrl = baseUrl;\n fs.writeFileSync(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n }\n\n // Build the template\n ui.debug('Building gallery 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 await buildGallery(path.resolve(dir), themeDir, ui, baseUrl);\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 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/src/gallery';\nimport type { ConsolaInstance } from 'consola';\n\n/**\n * Scans a directory for media files and subdirectories\n * @param dirPath - Path to the directory to scan\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to scan results with media files and subdirectories\n */\nasync function scanDirectory(dirPath: string, ui: ConsolaInstance): Promise<ScanDirectoryResult> {\n const mediaFiles: MediaFile[] = [];\n const subGalleryDirectories: string[] = [];\n\n try {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n if (entry.isFile()) {\n const fullPath = path.join(dirPath, entry.name);\n const mediaType = getMediaFileType(entry.name);\n\n if (mediaType) {\n const mediaFile: MediaFile = {\n type: mediaType,\n path: fullPath,\n width: 0,\n height: 0,\n };\n\n mediaFiles.push(mediaFile);\n }\n } else if (entry.isDirectory() && entry.name !== 'gallery') {\n subGalleryDirectories.push(path.join(dirPath, entry.name));\n }\n }\n } catch (error) {\n if (error instanceof Error && error.message.includes('ENOENT')) {\n ui.error(`Directory does not exist: ${dirPath}`);\n } else if (error instanceof Error && error.message.includes('ENOTDIR')) {\n ui.error(`Path is not a directory: ${dirPath}`);\n } else {\n ui.error(`Error scanning directory ${dirPath}:`, error);\n }\n\n throw error;\n }\n\n return { mediaFiles, subGalleryDirectories };\n}\n\n/**\n * Prompts the user for gallery settings through interactive CLI\n * @param galleryName - Name of the gallery directory\n * @param defaultImage - Default header image path\n * @param ui - ConsolaInstance for prompting and logging\n * @returns Promise resolving to user-provided gallery settings\n */\nasync function getGallerySettingsFromUser(\n galleryName: string,\n defaultImage: string,\n ui: ConsolaInstance,\n): Promise<GallerySettingsFromUser> {\n ui.info(`Enter gallery settings for the gallery in folder \"${galleryName}\"`);\n\n const title = await ui.prompt('Enter gallery title', { type: 'text', default: 'My Gallery', placeholder: 'My Gallery' });\n const description = await ui.prompt('Enter gallery description', {\n type: 'text',\n default: 'My gallery with fantastic photos.',\n placeholder: 'My gallery with fantastic photos.',\n });\n const url = await ui.prompt('Enter the URL where the gallery will be hosted (important for social media image)', {\n type: 'text',\n default: '',\n placeholder: '',\n });\n const headerImageName = await ui.prompt('Enter the name of the header image', {\n type: 'text',\n default: defaultImage,\n placeholder: defaultImage,\n });\n\n const headerImage = path.join('..', headerImageName);\n\n return { title, description, url, headerImage };\n}\n\n/**\n * Creates a gallery.json file with media files and settings\n * @param mediaFiles - Array of media files to include in gallery\n * @param galleryJsonPath - Path where gallery.json should be created\n * @param subGalleries - Array of sub-galleries to include\n * @param useDefaultSettings - Whether to use default settings or prompt user\n * @param ui - ConsolaInstance for prompting and logging\n */\nasync function createGalleryJson(\n mediaFiles: MediaFile[],\n galleryJsonPath: string,\n subGalleries: SubGallery[] = [],\n useDefaultSettings: boolean,\n ui: ConsolaInstance,\n): Promise<void> {\n const galleryDir = path.dirname(galleryJsonPath);\n\n // Convert media file paths to be relative to gallery.json\n const relativeMediaFiles = mediaFiles.map((file) => ({\n ...file,\n path: path.relative(galleryDir, file.path),\n }));\n\n // Convert subGallery header image paths to be relative to gallery.json\n const relativeSubGalleries = subGalleries.map((subGallery) => ({\n ...subGallery,\n headerImage: subGallery.headerImage ? path.relative(galleryDir, subGallery.headerImage) : '',\n }));\n\n let galleryData = {\n title: 'My Gallery',\n description: 'My gallery with fantastic photos.',\n headerImage: relativeMediaFiles[0]?.path || '',\n metadata: {},\n sections: [\n {\n images: relativeMediaFiles,\n },\n ],\n subGalleries: {\n title: 'Sub Galleries',\n galleries: relativeSubGalleries,\n },\n };\n\n if (!useDefaultSettings) {\n galleryData = {\n ...galleryData,\n ...(await getGallerySettingsFromUser(\n path.basename(path.join(galleryDir, '..')),\n path.basename(mediaFiles[0]?.path || ''),\n ui,\n )),\n };\n }\n\n await fs.writeFile(galleryJsonPath, JSON.stringify(galleryData, null, 2));\n}\n\n/**\n * Processes a directory and its subdirectories to create galleries\n * @param scanPath - Path to scan for media files\n * @param outputPath - Path where gallery should be created\n * @param recursive - Whether to process subdirectories recursively\n * @param useDefaultSettings - Whether to use default settings or prompt user\n * @param ui - ConsolaInstance for logging\n * @returns Promise resolving to processing results\n */\nasync function processDirectory(\n scanPath: string,\n outputPath: string,\n recursive: boolean,\n useDefaultSettings: boolean,\n ui: ConsolaInstance,\n): Promise<ProcessDirectoryResult> {\n ui.start(`Scanning ${scanPath}`);\n\n let totalFiles = 0;\n let totalGalleries = 1;\n const subGalleries: SubGallery[] = [];\n\n // Scan current directory for media files\n const { mediaFiles, subGalleryDirectories } = await scanDirectory(scanPath, ui);\n totalFiles += mediaFiles.length;\n\n // Process subdirectories only if recursive mode is enabled\n if (recursive) {\n for (const subGalleryDir of subGalleryDirectories) {\n const result = await processDirectory(\n subGalleryDir,\n path.join(outputPath, path.basename(subGalleryDir)),\n recursive,\n useDefaultSettings,\n ui,\n );\n\n totalFiles += result.totalFiles;\n totalGalleries += result.totalGalleries;\n\n // If the result contains a valid subGallery, add it to the list\n if (result.subGallery) {\n subGalleries.push(result.subGallery);\n }\n }\n }\n\n // Create gallery.json if there are media files or subGalleries\n if (mediaFiles.length > 0 || subGalleries.length > 0) {\n const galleryPath = path.join(outputPath, 'gallery');\n const galleryJsonPath = path.join(galleryPath, 'gallery.json');\n\n try {\n // Create output directory\n await fs.mkdir(galleryPath, { recursive: true });\n\n // Create gallery.json for this directory\n await createGalleryJson(mediaFiles, galleryJsonPath, subGalleries, useDefaultSettings, ui);\n\n ui.success(\n `Create gallery with ${mediaFiles.length} files and ${subGalleries.length} subgalleries at: ${galleryJsonPath}`,\n );\n } catch (error) {\n ui.error(`Error creating gallery.json at ${galleryJsonPath}`);\n throw error;\n }\n }\n\n // Return result with suGgallery info if this directory has media files\n const result: ProcessDirectoryResult = { totalFiles, totalGalleries };\n\n // If this directory has media files or subGalleries, create a subGallery in the result\n if (mediaFiles.length > 0 || subGalleries.length > 0) {\n const dirName = path.basename(scanPath);\n result.subGallery = {\n title: capitalizeTitle(dirName),\n headerImage: mediaFiles[0]?.path || '',\n path: path.join('..', dirName),\n };\n }\n\n return result;\n}\n\n/**\n * Main init command implementation - scans directories and creates gallery.json files\n * @param options - Options specifying paths, recursion, and default settings\n * @param ui - ConsolaInstance for logging and user prompts\n */\nexport async function init(options: ScanOptions, ui: ConsolaInstance): Promise<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(scanPath, outputPath, options.recursive, options.default, ui);\n\n ui.box(\n `Created ${result.totalGalleries} ${result.totalGalleries === 1 ? 'gallery' : 'galleries'} with ${result.totalFiles} media ${result.totalFiles === 1 ? 'file' : 'files'}`,\n );\n\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 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.9\",\n \"description\": \"Simple Photo Gallery CLI\",\n \"license\": \"MIT\",\n \"author\": \"Vladimir Haltakov, Tomasz Rusin\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/SimplePhotoGallery/core\"\n },\n \"homepage\": \"https://simple.photo\",\n \"files\": [\n \"dist\"\n ],\n \"bin\": {\n \"simple-photo-gallery\": \"./dist/index.js\",\n \"spg\": \"./dist/index.js\"\n },\n \"scripts\": {\n \"gallery\": \"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 \"prepublish\": \"npm run build\"\n },\n \"dependencies\": {\n \"@simple-photo-gallery/theme-modern\": \"2.0.3\",\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 \"@simple-photo-gallery/common\": \"workspace:*\",\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/jest\": \"^30.0.0\",\n \"@types/node\": \"^24.0.10\",\n \"@typescript-eslint/eslint-plugin\": \"^8.38.0\",\n \"@typescript-eslint/parser\": \"^8.38.0\",\n \"eslint\": \"^9.30.1\",\n \"eslint-config-prettier\": \"^10.1.5\",\n \"eslint-import-resolver-alias\": \"^1.1.2\",\n \"eslint-import-resolver-typescript\": \"^4.4.4\",\n \"eslint-plugin-import\": \"^2.31.0\",\n \"eslint-plugin-prettier\": \"^5.5.1\",\n \"eslint-plugin-unicorn\": \"^59.0.1\",\n \"fs-extra\": \"^11.3.0\",\n \"jest\": \"^30.0.5\",\n \"prettier\": \"^3.4.2\",\n \"ts-jest\": \"^29.4.1\",\n \"tsup\": \"^8.5.0\",\n \"tsx\": \"^4.20.3\",\n \"typescript\": \"^5.9.0\",\n \"typescript-eslint\": \"^8.38.0\"\n },\n \"type\": \"module\"\n}\n","#!/usr/bin/env node\n\nimport process from 'node:process';\n\nimport { Command } from 'commander';\nimport { createConsola, LogLevels, type ConsolaInstance } from 'consola';\n\nimport { build } from './modules/build';\nimport { clean } from './modules/clean';\nimport { init } from './modules/init';\nimport { 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 .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 .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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-photo-gallery",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "Simple Photo Gallery CLI",
5
5
  "license": "MIT",
6
6
  "author": "Vladimir Haltakov, Tomasz Rusin",
@@ -17,7 +17,7 @@
17
17
  "spg": "./dist/index.js"
18
18
  },
19
19
  "scripts": {
20
- "gallery": "tsx src/index.ts",
20
+ "gallery": "SPG_TELEMETRY_PROVIDER=none tsx src/index.ts",
21
21
  "clean": "rm -rf dist",
22
22
  "compile": "tsc --noEmit",
23
23
  "build": "yarn clean && tsup",
@@ -30,12 +30,15 @@
30
30
  "prepublish": "npm run build"
31
31
  },
32
32
  "dependencies": {
33
- "@simple-photo-gallery/theme-modern": "2.0.2",
33
+ "@simple-photo-gallery/theme-modern": "2.0.3",
34
+ "axios": "^1.12.2",
34
35
  "blurhash": "^2.0.5",
35
36
  "commander": "^12.0.0",
37
+ "conf": "^13.0.1",
36
38
  "consola": "^3.4.2",
37
39
  "exifreader": "^4.32.0",
38
40
  "node-ffprobe": "^3.0.0",
41
+ "semver-parser": "^4.1.8",
39
42
  "sharp": "^0.33.0",
40
43
  "zod": "^4.0.14"
41
44
  },