@nghiavuive/random-image 1.0.2 → 1.2.0

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
@@ -1,46 +1,196 @@
1
1
  # random-image
2
2
 
3
- A generic utility library to fetch random images from various providers like Unsplash and Pexels.
3
+ A generic utility library to fetch random images from various providers like Unsplash and Pexels, with built-in download capabilities and CLI support.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install random-image
8
+ npm install @nghiavuive/random-image
9
9
  ```
10
10
 
11
- ## Usage
11
+ ## CLI Usage
12
+
13
+ You can use the CLI without installation via `npx`:
14
+
15
+ ```bash
16
+ npx @nghiavuive/random-image fetch --query "nature" --download
17
+ ```
18
+
19
+ Or install globally:
20
+
21
+ ```bash
22
+ npm install -g @nghiavuive/random-image
23
+ random-image fetch --query "mountains" --download
24
+ ```
25
+
26
+ ### CLI Setup
27
+
28
+ Set environment variables for the providers you want to use:
29
+
30
+ ```bash
31
+ export UNSPLASH_KEY="your_unsplash_access_key"
32
+ export PEXELS_KEY="your_pexels_api_key"
33
+ export PIXABAY_KEY="your_pixabay_api_key"
34
+ ```
35
+
36
+ Or create a `.env` file in your project:
37
+
38
+ ```env
39
+ UNSPLASH_KEY=your_unsplash_access_key
40
+ PEXELS_KEY=your_pexels_api_key
41
+ PIXABAY_KEY=your_pixabay_api_key
42
+ ```
43
+
44
+ ### CLI Commands
45
+
46
+ #### Fetch Command
47
+
48
+ ```bash
49
+ # Basic usage - uses random provider from available API keys
50
+ random-image fetch
51
+
52
+ # Fetch with search query
53
+ random-image fetch --query "nature"
54
+
55
+ # Fetch and download
56
+ random-image fetch --query "mountains" --download
57
+
58
+ # Specify provider
59
+ random-image fetch --provider unsplash --query "ocean"
60
+
61
+ # Custom dimensions
62
+ random-image fetch --width 1920 --height 1080 --query "sunset"
63
+
64
+ # Download to custom directory
65
+ random-image fetch --query "cats" --download ./my-images
66
+
67
+ # Download with custom filename
68
+ random-image fetch --query "dogs" --download --filename "my-dog.jpg"
69
+
70
+ # Download with overwrite
71
+ random-image fetch --download --overwrite
72
+
73
+ # Generate random UUID filename
74
+ random-image fetch --download --no-keep-original-name
75
+
76
+ # Full example with all options
77
+ random-image fetch \
78
+ --provider pexels \
79
+ --query "abstract art" \
80
+ --width 2560 \
81
+ --height 1440 \
82
+ --orientation landscape \
83
+ --download ./wallpapers \
84
+ --filename "wallpaper.jpg" \
85
+ --overwrite
86
+ ```
87
+
88
+ #### CLI Flags
89
+
90
+ - `-q, --query <search>`: Search query for the image
91
+ - `-w, --width <number>`: Width of the image
92
+ - `-h, --height <number>`: Height of the image
93
+ - `--quality <number>`: Quality of the image (0-100)
94
+ - `--orientation <type>`: Image orientation (`landscape` or `portrait`)
95
+ - `-p, --provider <name>`: Provider to use (`unsplash`, `pexels`, `pixabay`, or `random`)
96
+ - `-d, --download [path]`: Download the image (default: `./downloads`)
97
+ - `--filename <name>`: Custom filename for downloaded image
98
+ - `--overwrite`: Overwrite existing files
99
+ - `--no-keep-original-name`: Generate random UUID filename instead of keeping original
100
+
101
+ **Random Provider Mode**: If you don't specify `--provider` or use `--provider random`, the CLI will randomly select from providers that have API keys configured. This is useful for distributing requests across multiple services.
102
+
103
+ ## Programmatic Usage
12
104
 
13
105
  You need to obtain API keys from the respective providers:
14
106
  - [Unsplash Developers](https://unsplash.com/developers)
15
107
  - [Pexels API](https://www.pexels.com/api/)
16
108
 
109
+ ### Basic Usage - Fetching Random Images
110
+
17
111
  ```typescript
18
- import { RandomImage, UnsplashProvider, PexelsProvider } from 'random-image';
112
+ import { RandomImage, UnsplashProvider, PexelsProvider } from '@nghiavuive/random-image';
19
113
 
20
114
  // Using Unsplash
21
115
  const unsplash = new UnsplashProvider('YOUR_UNSPLASH_ACCESS_KEY');
22
116
  const fetcher = new RandomImage(unsplash);
23
117
 
24
- fetcher.getRandom({
118
+ const image = await fetcher.getRandom({
25
119
  width: 1920,
26
120
  height: 1080,
27
121
  query: 'nature'
28
- }).then(image => {
29
- console.log(image.url); // URL to the image
30
- console.log(image.author); // Photographer's name
31
122
  });
32
123
 
124
+ console.log(image.url); // URL to the image
125
+ console.log(image.author); // Photographer's name
126
+
33
127
  // Using Pexels
34
128
  const pexels = new PexelsProvider('YOUR_PEXELS_API_KEY');
35
129
  const pexelsFetcher = new RandomImage(pexels);
36
130
 
37
- pexelsFetcher.getRandom({
131
+ const pexelsImage = await pexelsFetcher.getRandom({
38
132
  width: 800,
39
133
  height: 600,
40
134
  query: 'cats'
41
- }).then(image => {
42
- console.log(image.url);
43
135
  });
136
+
137
+ console.log(pexelsImage.url);
138
+ ```
139
+
140
+ ### Downloading Images
141
+
142
+ The library now supports downloading images directly to your local filesystem:
143
+
144
+ ```typescript
145
+ import { RandomImage, UnsplashProvider } from '@nghiavuive/random-image';
146
+
147
+ const provider = new UnsplashProvider('YOUR_API_KEY');
148
+ const fetcher = new RandomImage(provider);
149
+
150
+ // Fetch and download a random image
151
+ const image = await fetcher.getRandom({ query: 'mountains' });
152
+
153
+ // Download with auto-generated filename
154
+ const filePath = await fetcher.download(image, './downloads');
155
+ console.log(`Image saved to: ${filePath}`);
156
+
157
+ // Download with custom filename
158
+ const customPath = await fetcher.download(
159
+ image,
160
+ './downloads',
161
+ { filename: 'my-mountain.jpg' }
162
+ );
163
+
164
+ // Download with overwrite option
165
+ const overwritePath = await fetcher.download(
166
+ image,
167
+ './downloads',
168
+ {
169
+ filename: 'mountain.jpg',
170
+ overwrite: true // Will replace existing file
171
+ }
172
+ );
173
+
174
+ // Download directly from URL
175
+ await fetcher.download(
176
+ 'https://example.com/image.jpg',
177
+ './downloads',
178
+ { filename: 'direct-download.jpg' }
179
+ );
180
+
181
+ // Download with UUID-based random filename
182
+ await fetcher.download(
183
+ image,
184
+ './downloads',
185
+ { keepOriginalName: false }
186
+ );
187
+
188
+ // Download keeping original filename (default behavior)
189
+ await fetcher.download(
190
+ image,
191
+ './downloads',
192
+ { keepOriginalName: true }
193
+ );
44
194
  ```
45
195
 
46
196
  ## API
@@ -51,24 +201,74 @@ The main class to interact with.
51
201
  ```typescript
52
202
  class RandomImage {
53
203
  constructor(provider: ImageProvider);
204
+
205
+ // Fetch a random image
54
206
  getRandom(options: ImageOptions): Promise<ImageResult>;
207
+
208
+ // Download an image to local filesystem
209
+ download(
210
+ imageUrl: string | ImageResult,
211
+ destinationPath: string,
212
+ options?: DownloadOptions
213
+ ): Promise<string>;
55
214
  }
56
215
  ```
57
216
 
58
217
  ### `ImageOptions`
59
- - `width` (number): Desired width of the image.
60
- - `height` (number): Desired height of the image.
61
- - `quality` (number): Quality (0-100) if supported.
62
- - `query` (string): Search query (e.g. "nature", "city").
218
+ Configuration for fetching random images:
219
+ - `width` (number, optional): Desired width of the image.
220
+ - `height` (number, optional): Desired height of the image.
221
+ - `quality` (number, optional): Quality (0-100) if supported by provider.
222
+ - `query` (string, optional): Search query (e.g. "nature", "city").
223
+ - `orientation` ("landscape" | "portrait", optional): Image orientation.
224
+
225
+ ### `DownloadOptions`
226
+ Configuration for downloading images:
227
+ - `filename` (string, optional): Custom filename for the downloaded image. If provided, this takes precedence over `keepOriginalName`.
228
+ - `overwrite` (boolean, optional): Whether to overwrite existing files. Default is `false`. If `false` and file exists, an error will be thrown.
229
+ - `keepOriginalName` (boolean, optional): Controls filename generation when `filename` is not provided:
230
+ - `true` or `undefined` (default): Extract and use the original filename from the URL
231
+ - `false`: Generate a random UUID-based filename
63
232
 
64
233
  ### `ImageResult`
234
+ Result object containing image information:
65
235
  - `url` (string): Direct URL to the image.
66
236
  - `width` (number): Width of the image.
67
237
  - `height` (number): Height of the image.
68
238
  - `author` (string): Name of the photographer.
69
- - `authorUrl` (string): URL to photographer's profile.
239
+ - `authorUrl` (string, optional): URL to photographer's profile.
70
240
  - `originalUrl` (string): URL to the original photo page.
71
241
 
242
+ ## Features
243
+
244
+ ### CLI Features
245
+ - ✅ Command-line interface for quick image fetching
246
+ - ✅ Random provider selection when multiple API keys are available
247
+ - ✅ Environment variable support for API keys
248
+ - ✅ Direct download from command line
249
+ - ✅ All library features available via CLI flags
250
+
251
+ ### Library Features
252
+
253
+ ### Download Functionality
254
+ - ✅ Flexible filename options:
255
+ - Custom filename
256
+ - Keep original filename from URL (default)
257
+ - Generate random UUID-based filename
258
+ - ✅ Overwrite protection
259
+ - ✅ Automatic file extension detection
260
+ - ✅ Support for both HTTP and HTTPS
261
+ - ✅ Automatic redirect handling (up to 5 redirects)
262
+ - ✅ Stream-based downloading for memory efficiency
263
+ - ✅ Error handling with cleanup of partial downloads
264
+
265
+ ## Supported Providers
266
+
267
+ - **Unsplash**: High-quality photos with attribution
268
+ - **Pexels**: Free stock photos and videos
269
+ - **Pixabay**: Free images and videos
270
+
72
271
  ## License
73
272
 
74
273
  ISC
274
+
package/dist/cli.js ADDED
@@ -0,0 +1,346 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli.ts
27
+ var import_commander = require("commander");
28
+
29
+ // src/providers/unsplash.ts
30
+ var import_axios = __toESM(require("axios"));
31
+ var UnsplashProvider = class {
32
+ constructor(accessKey) {
33
+ this.accessKey = accessKey;
34
+ }
35
+ async fetchRandomImage(options) {
36
+ const response = await import_axios.default.get("https://api.unsplash.com/photos/random", {
37
+ headers: {
38
+ Authorization: `Client-ID ${this.accessKey}`
39
+ },
40
+ params: {
41
+ query: options.query,
42
+ orientation: options.orientation
43
+ }
44
+ });
45
+ const data = response.data;
46
+ const photo = Array.isArray(data) ? data[0] : data;
47
+ const baseUrl = photo.urls.raw;
48
+ const sizeParams = new URLSearchParams();
49
+ if (options.height || options.width) {
50
+ sizeParams.append("fit", "crop");
51
+ sizeParams.append("crop", "entropy");
52
+ }
53
+ if (options.width) sizeParams.append("w", options.width.toString());
54
+ if (options.height) sizeParams.append("h", options.height.toString());
55
+ if (options.quality) sizeParams.append("q", options.quality.toString());
56
+ const finalUrl = `${baseUrl}&${sizeParams.toString()}`;
57
+ return {
58
+ url: finalUrl,
59
+ width: options.width || photo.width,
60
+ height: options.height || photo.height,
61
+ author: photo.user.name,
62
+ authorUrl: photo.user.links.html,
63
+ originalUrl: photo.links.html
64
+ // Link to photo page
65
+ };
66
+ }
67
+ };
68
+
69
+ // src/providers/pexels.ts
70
+ var import_axios2 = __toESM(require("axios"));
71
+ var PexelsProvider = class {
72
+ constructor(apiKey) {
73
+ this.apiKey = apiKey;
74
+ }
75
+ async fetchRandomImage(options) {
76
+ const endpoint = options.query ? "https://api.pexels.com/v1/search" : "https://api.pexels.com/v1/curated";
77
+ const randomPage = Math.floor(Math.random() * 100) + 1;
78
+ const params = {
79
+ per_page: 1,
80
+ page: randomPage
81
+ };
82
+ if (options.query) params.query = options.query;
83
+ const response = await import_axios2.default.get(endpoint, {
84
+ headers: {
85
+ Authorization: this.apiKey
86
+ },
87
+ params
88
+ });
89
+ const data = response.data;
90
+ if (!data.photos || data.photos.length === 0) {
91
+ throw new Error("No images found on Pexels");
92
+ }
93
+ const photo = data.photos[0];
94
+ const baseUrl = photo.src.original;
95
+ const sizeParams = new URLSearchParams();
96
+ sizeParams.append("auto", "compress");
97
+ sizeParams.append("cs", "tinysrgb");
98
+ if (options.width) sizeParams.append("w", options.width.toString());
99
+ if (options.height) sizeParams.append("h", options.height.toString());
100
+ const finalUrl = `${baseUrl}?${sizeParams.toString()}`;
101
+ return {
102
+ url: finalUrl,
103
+ width: options.width || photo.width,
104
+ height: options.height || photo.height,
105
+ author: photo.photographer,
106
+ authorUrl: photo.photographer_url,
107
+ originalUrl: photo.url
108
+ };
109
+ }
110
+ };
111
+
112
+ // src/providers/pixabay.ts
113
+ var import_axios3 = __toESM(require("axios"));
114
+ var PixabayProvider = class {
115
+ constructor(apiKey) {
116
+ this.apiKey = apiKey;
117
+ }
118
+ async fetchRandomImage(options) {
119
+ const params = {
120
+ key: this.apiKey,
121
+ q: options.query || "",
122
+ per_page: 20
123
+ // Fetch a few to pick randomly
124
+ };
125
+ if (options.orientation) {
126
+ params.orientation = options.orientation === "portrait" ? "vertical" : "horizontal";
127
+ }
128
+ const response = await import_axios3.default.get("https://pixabay.com/api/", {
129
+ params
130
+ });
131
+ const hits = response.data.hits;
132
+ if (!hits || hits.length === 0) {
133
+ throw new Error("No images found");
134
+ }
135
+ const randomHit = hits[Math.floor(Math.random() * hits.length)];
136
+ return {
137
+ url: randomHit.largeImageURL || randomHit.webformatURL,
138
+ width: randomHit.imageWidth || randomHit.webformatWidth,
139
+ height: randomHit.imageHeight || randomHit.webformatHeight,
140
+ author: randomHit.user,
141
+ authorUrl: `https://pixabay.com/users/${randomHit.user}-${randomHit.user_id}/`,
142
+ originalUrl: randomHit.pageURL
143
+ };
144
+ }
145
+ };
146
+
147
+ // src/image-fetcher.ts
148
+ var fs = __toESM(require("fs"));
149
+ var path = __toESM(require("path"));
150
+ var import_axios4 = __toESM(require("axios"));
151
+ var import_uuid = require("uuid");
152
+ var RandomImage = class {
153
+ constructor(provider) {
154
+ this.provider = provider;
155
+ }
156
+ /**
157
+ * Fetches a random image based on the provided options.
158
+ * @param options - Configuration options for the image (width, height, query, etc.)
159
+ * @returns A promise that resolves to an ImageResult object.
160
+ */
161
+ async getRandom(options = {}) {
162
+ return this.provider.fetchRandomImage(options);
163
+ }
164
+ /**
165
+ * Downloads an image to a specified directory.
166
+ * @param imageUrl - The URL of the image to download (can be a string or ImageResult object)
167
+ * @param destinationPath - The directory path where the image will be saved
168
+ * @param options - Download options (filename, overwrite, etc.)
169
+ * @returns A promise that resolves to the full path of the downloaded file
170
+ */
171
+ async download(imageUrl, destinationPath, options = {}) {
172
+ const url = typeof imageUrl === "string" ? imageUrl : imageUrl.url;
173
+ if (!fs.existsSync(destinationPath)) {
174
+ fs.mkdirSync(destinationPath, { recursive: true });
175
+ }
176
+ let finalFilename;
177
+ if (options.filename) {
178
+ finalFilename = options.filename;
179
+ } else if (options.keepOriginalName) {
180
+ const urlPath = new URL(url).pathname;
181
+ const urlFilename = path.basename(urlPath);
182
+ if (urlFilename && urlFilename.length > 0 && urlFilename !== "/") {
183
+ finalFilename = urlFilename;
184
+ } else {
185
+ const ext = this.getExtensionFromUrl(url) || ".jpg";
186
+ finalFilename = `${(0, import_uuid.v7)()}${ext}`;
187
+ }
188
+ } else {
189
+ const ext = this.getExtensionFromUrl(url) || ".jpg";
190
+ finalFilename = `${(0, import_uuid.v7)()}${ext}`;
191
+ }
192
+ if (!path.extname(finalFilename)) {
193
+ finalFilename += ".jpg";
194
+ }
195
+ const fullPath = path.join(destinationPath, finalFilename);
196
+ if (fs.existsSync(fullPath) && !options.overwrite) {
197
+ throw new Error(`File already exists: ${fullPath}. Set overwrite: true to replace it.`);
198
+ }
199
+ try {
200
+ const response = await import_axios4.default.get(url, {
201
+ responseType: "stream",
202
+ maxRedirects: 5
203
+ // Automatically handle redirects
204
+ });
205
+ const writer = fs.createWriteStream(fullPath);
206
+ response.data.pipe(writer);
207
+ return new Promise((resolve2, reject) => {
208
+ writer.on("finish", () => {
209
+ resolve2(fullPath);
210
+ });
211
+ writer.on("error", (err) => {
212
+ fs.unlink(fullPath, () => {
213
+ });
214
+ reject(err);
215
+ });
216
+ response.data.on("error", (err) => {
217
+ writer.close();
218
+ fs.unlink(fullPath, () => {
219
+ });
220
+ reject(err);
221
+ });
222
+ });
223
+ } catch (error) {
224
+ if (import_axios4.default.isAxiosError(error)) {
225
+ throw new Error(`Failed to download image: ${error.message}`);
226
+ }
227
+ throw error;
228
+ }
229
+ }
230
+ /**
231
+ * Helper method to extract file extension from URL
232
+ * @param url - The URL to extract extension from
233
+ * @returns The file extension (e.g., '.jpg', '.png') or null
234
+ */
235
+ getExtensionFromUrl(url) {
236
+ try {
237
+ const urlPath = new URL(url).pathname;
238
+ const ext = path.extname(urlPath);
239
+ return ext || null;
240
+ } catch {
241
+ return null;
242
+ }
243
+ }
244
+ };
245
+
246
+ // src/cli.ts
247
+ var path2 = __toESM(require("path"));
248
+ var dotenv = __toESM(require("dotenv"));
249
+ var fs2 = __toESM(require("fs"));
250
+ var possibleEnvPaths = [
251
+ path2.resolve(process.cwd(), ".env"),
252
+ // Current working directory
253
+ path2.resolve(__dirname, ".env"),
254
+ // Same directory as the CLI script (dist/)
255
+ path2.resolve(__dirname, "..", ".env")
256
+ // Parent directory (root of project)
257
+ ];
258
+ for (const envPath of possibleEnvPaths) {
259
+ if (fs2.existsSync(envPath)) {
260
+ dotenv.config({ path: envPath });
261
+ console.log(`Loaded environment from: ${envPath}`);
262
+ break;
263
+ }
264
+ }
265
+ var program = new import_commander.Command();
266
+ program.name("random-image").description("CLI tool to fetch random images from various providers").version("1.2.0");
267
+ program.command("fetch").description("Fetch a random image from image providers").option("-q, --query <search>", "Search query for the image").option("-w, --width <number>", "Width of the image", parseInt).option("-h, --height <number>", "Height of the image", parseInt).option("--quality <number>", "Quality of the image (0-100)", parseInt).option("--orientation <type>", "Image orientation (landscape or portrait)").option("-p, --provider <name>", "Provider to use (unsplash, pexels, pixabay, or random)", "random").option("-d, --download [path]", "Download the image to specified directory (default: ./downloads)").option("--filename <name>", "Custom filename for downloaded image").option("--overwrite", "Overwrite existing files", false).option("--no-keep-original-name", "Generate random UUID filename instead of keeping original").action(async (options) => {
268
+ try {
269
+ const providers = {
270
+ unsplash: process.env.UNSPLASH_KEY,
271
+ pexels: process.env.PEXELS_KEY,
272
+ pixabay: process.env.PIXABAY_KEY
273
+ };
274
+ let providerName = options.provider.toLowerCase();
275
+ if (providerName === "random") {
276
+ const availableProviders = Object.entries(providers).filter(([_, key]) => key).map(([name]) => name);
277
+ if (availableProviders.length === 0) {
278
+ console.error("Error: No API keys found in environment variables.");
279
+ console.error("Please set at least one of: UNSPLASH_KEY, PEXELS_KEY, PIXABAY_KEY");
280
+ process.exit(1);
281
+ }
282
+ providerName = availableProviders[Math.floor(Math.random() * availableProviders.length)];
283
+ console.log(`Using random provider: ${providerName}`);
284
+ }
285
+ let provider;
286
+ const apiKey = providers[providerName];
287
+ if (!apiKey) {
288
+ console.error(`Error: API key for ${providerName} not found.`);
289
+ console.error(`Please set ${providerName.toUpperCase()}_KEY environment variable.`);
290
+ process.exit(1);
291
+ }
292
+ switch (providerName) {
293
+ case "unsplash":
294
+ provider = new UnsplashProvider(apiKey);
295
+ break;
296
+ case "pexels":
297
+ provider = new PexelsProvider(apiKey);
298
+ break;
299
+ case "pixabay":
300
+ provider = new PixabayProvider(apiKey);
301
+ break;
302
+ default:
303
+ console.error(`Error: Unknown provider "${providerName}"`);
304
+ console.error("Available providers: unsplash, pexels, pixabay, random");
305
+ process.exit(1);
306
+ }
307
+ const fetcher = new RandomImage(provider);
308
+ const fetchOptions = {};
309
+ if (options.query) fetchOptions.query = options.query;
310
+ if (options.width) fetchOptions.width = options.width;
311
+ if (options.height) fetchOptions.height = options.height;
312
+ if (options.quality) fetchOptions.quality = options.quality;
313
+ if (options.orientation) fetchOptions.orientation = options.orientation;
314
+ console.log("Fetching random image...");
315
+ const image = await fetcher.getRandom(fetchOptions);
316
+ console.log("\n\u2713 Image fetched successfully!");
317
+ console.log(` URL: ${image.url}`);
318
+ console.log(` Size: ${image.width}x${image.height}`);
319
+ console.log(` Author: ${image.author}`);
320
+ if (image.authorUrl) console.log(` Author URL: ${image.authorUrl}`);
321
+ console.log(` Original: ${image.originalUrl}`);
322
+ if (options.download !== void 0) {
323
+ const downloadPath = typeof options.download === "string" ? options.download : "./downloads";
324
+ console.log(`
325
+ Downloading to ${path2.resolve(downloadPath)}...`);
326
+ const downloadOptions = {
327
+ overwrite: options.overwrite,
328
+ keepOriginalName: options.keepOriginalName
329
+ };
330
+ if (options.filename) {
331
+ downloadOptions.filename = options.filename;
332
+ }
333
+ const filePath = await fetcher.download(image, downloadPath, downloadOptions);
334
+ console.log(`\u2713 Downloaded: ${filePath}`);
335
+ }
336
+ } catch (error) {
337
+ if (error instanceof Error) {
338
+ console.error(`
339
+ Error: ${error.message}`);
340
+ } else {
341
+ console.error("\nAn unexpected error occurred");
342
+ }
343
+ process.exit(1);
344
+ }
345
+ });
346
+ program.parse(process.argv);