@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 +216 -16
- package/dist/cli.js +346 -0
- package/dist/cli.mjs +377 -0
- package/dist/index.d.mts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +84 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -4
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
7
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// src/types.ts
|
|
11
|
+
var init_types = __esm({
|
|
12
|
+
"src/types.ts"() {
|
|
13
|
+
"use strict";
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// src/providers/unsplash.ts
|
|
18
|
+
import axios from "axios";
|
|
19
|
+
var UnsplashProvider;
|
|
20
|
+
var init_unsplash = __esm({
|
|
21
|
+
"src/providers/unsplash.ts"() {
|
|
22
|
+
"use strict";
|
|
23
|
+
UnsplashProvider = class {
|
|
24
|
+
constructor(accessKey) {
|
|
25
|
+
this.accessKey = accessKey;
|
|
26
|
+
}
|
|
27
|
+
async fetchRandomImage(options) {
|
|
28
|
+
const response = await axios.get("https://api.unsplash.com/photos/random", {
|
|
29
|
+
headers: {
|
|
30
|
+
Authorization: `Client-ID ${this.accessKey}`
|
|
31
|
+
},
|
|
32
|
+
params: {
|
|
33
|
+
query: options.query,
|
|
34
|
+
orientation: options.orientation
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
const data = response.data;
|
|
38
|
+
const photo = Array.isArray(data) ? data[0] : data;
|
|
39
|
+
const baseUrl = photo.urls.raw;
|
|
40
|
+
const sizeParams = new URLSearchParams();
|
|
41
|
+
if (options.height || options.width) {
|
|
42
|
+
sizeParams.append("fit", "crop");
|
|
43
|
+
sizeParams.append("crop", "entropy");
|
|
44
|
+
}
|
|
45
|
+
if (options.width) sizeParams.append("w", options.width.toString());
|
|
46
|
+
if (options.height) sizeParams.append("h", options.height.toString());
|
|
47
|
+
if (options.quality) sizeParams.append("q", options.quality.toString());
|
|
48
|
+
const finalUrl = `${baseUrl}&${sizeParams.toString()}`;
|
|
49
|
+
return {
|
|
50
|
+
url: finalUrl,
|
|
51
|
+
width: options.width || photo.width,
|
|
52
|
+
height: options.height || photo.height,
|
|
53
|
+
author: photo.user.name,
|
|
54
|
+
authorUrl: photo.user.links.html,
|
|
55
|
+
originalUrl: photo.links.html
|
|
56
|
+
// Link to photo page
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// src/providers/pexels.ts
|
|
64
|
+
import axios2 from "axios";
|
|
65
|
+
var PexelsProvider;
|
|
66
|
+
var init_pexels = __esm({
|
|
67
|
+
"src/providers/pexels.ts"() {
|
|
68
|
+
"use strict";
|
|
69
|
+
PexelsProvider = class {
|
|
70
|
+
constructor(apiKey) {
|
|
71
|
+
this.apiKey = apiKey;
|
|
72
|
+
}
|
|
73
|
+
async fetchRandomImage(options) {
|
|
74
|
+
const endpoint = options.query ? "https://api.pexels.com/v1/search" : "https://api.pexels.com/v1/curated";
|
|
75
|
+
const randomPage = Math.floor(Math.random() * 100) + 1;
|
|
76
|
+
const params = {
|
|
77
|
+
per_page: 1,
|
|
78
|
+
page: randomPage
|
|
79
|
+
};
|
|
80
|
+
if (options.query) params.query = options.query;
|
|
81
|
+
const response = await axios2.get(endpoint, {
|
|
82
|
+
headers: {
|
|
83
|
+
Authorization: this.apiKey
|
|
84
|
+
},
|
|
85
|
+
params
|
|
86
|
+
});
|
|
87
|
+
const data = response.data;
|
|
88
|
+
if (!data.photos || data.photos.length === 0) {
|
|
89
|
+
throw new Error("No images found on Pexels");
|
|
90
|
+
}
|
|
91
|
+
const photo = data.photos[0];
|
|
92
|
+
const baseUrl = photo.src.original;
|
|
93
|
+
const sizeParams = new URLSearchParams();
|
|
94
|
+
sizeParams.append("auto", "compress");
|
|
95
|
+
sizeParams.append("cs", "tinysrgb");
|
|
96
|
+
if (options.width) sizeParams.append("w", options.width.toString());
|
|
97
|
+
if (options.height) sizeParams.append("h", options.height.toString());
|
|
98
|
+
const finalUrl = `${baseUrl}?${sizeParams.toString()}`;
|
|
99
|
+
return {
|
|
100
|
+
url: finalUrl,
|
|
101
|
+
width: options.width || photo.width,
|
|
102
|
+
height: options.height || photo.height,
|
|
103
|
+
author: photo.photographer,
|
|
104
|
+
authorUrl: photo.photographer_url,
|
|
105
|
+
originalUrl: photo.url
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// src/providers/pixabay.ts
|
|
113
|
+
import axios3 from "axios";
|
|
114
|
+
var PixabayProvider;
|
|
115
|
+
var init_pixabay = __esm({
|
|
116
|
+
"src/providers/pixabay.ts"() {
|
|
117
|
+
"use strict";
|
|
118
|
+
PixabayProvider = class {
|
|
119
|
+
constructor(apiKey) {
|
|
120
|
+
this.apiKey = apiKey;
|
|
121
|
+
}
|
|
122
|
+
async fetchRandomImage(options) {
|
|
123
|
+
const params = {
|
|
124
|
+
key: this.apiKey,
|
|
125
|
+
q: options.query || "",
|
|
126
|
+
per_page: 20
|
|
127
|
+
// Fetch a few to pick randomly
|
|
128
|
+
};
|
|
129
|
+
if (options.orientation) {
|
|
130
|
+
params.orientation = options.orientation === "portrait" ? "vertical" : "horizontal";
|
|
131
|
+
}
|
|
132
|
+
const response = await axios3.get("https://pixabay.com/api/", {
|
|
133
|
+
params
|
|
134
|
+
});
|
|
135
|
+
const hits = response.data.hits;
|
|
136
|
+
if (!hits || hits.length === 0) {
|
|
137
|
+
throw new Error("No images found");
|
|
138
|
+
}
|
|
139
|
+
const randomHit = hits[Math.floor(Math.random() * hits.length)];
|
|
140
|
+
return {
|
|
141
|
+
url: randomHit.largeImageURL || randomHit.webformatURL,
|
|
142
|
+
width: randomHit.imageWidth || randomHit.webformatWidth,
|
|
143
|
+
height: randomHit.imageHeight || randomHit.webformatHeight,
|
|
144
|
+
author: randomHit.user,
|
|
145
|
+
authorUrl: `https://pixabay.com/users/${randomHit.user}-${randomHit.user_id}/`,
|
|
146
|
+
originalUrl: randomHit.pageURL
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// src/image-fetcher.ts
|
|
154
|
+
import * as fs from "fs";
|
|
155
|
+
import * as path from "path";
|
|
156
|
+
import axios4 from "axios";
|
|
157
|
+
import { v7 as uuidv4 } from "uuid";
|
|
158
|
+
var RandomImage;
|
|
159
|
+
var init_image_fetcher = __esm({
|
|
160
|
+
"src/image-fetcher.ts"() {
|
|
161
|
+
"use strict";
|
|
162
|
+
RandomImage = class {
|
|
163
|
+
constructor(provider) {
|
|
164
|
+
this.provider = provider;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Fetches a random image based on the provided options.
|
|
168
|
+
* @param options - Configuration options for the image (width, height, query, etc.)
|
|
169
|
+
* @returns A promise that resolves to an ImageResult object.
|
|
170
|
+
*/
|
|
171
|
+
async getRandom(options = {}) {
|
|
172
|
+
return this.provider.fetchRandomImage(options);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Downloads an image to a specified directory.
|
|
176
|
+
* @param imageUrl - The URL of the image to download (can be a string or ImageResult object)
|
|
177
|
+
* @param destinationPath - The directory path where the image will be saved
|
|
178
|
+
* @param options - Download options (filename, overwrite, etc.)
|
|
179
|
+
* @returns A promise that resolves to the full path of the downloaded file
|
|
180
|
+
*/
|
|
181
|
+
async download(imageUrl, destinationPath, options = {}) {
|
|
182
|
+
const url = typeof imageUrl === "string" ? imageUrl : imageUrl.url;
|
|
183
|
+
if (!fs.existsSync(destinationPath)) {
|
|
184
|
+
fs.mkdirSync(destinationPath, { recursive: true });
|
|
185
|
+
}
|
|
186
|
+
let finalFilename;
|
|
187
|
+
if (options.filename) {
|
|
188
|
+
finalFilename = options.filename;
|
|
189
|
+
} else if (options.keepOriginalName) {
|
|
190
|
+
const urlPath = new URL(url).pathname;
|
|
191
|
+
const urlFilename = path.basename(urlPath);
|
|
192
|
+
if (urlFilename && urlFilename.length > 0 && urlFilename !== "/") {
|
|
193
|
+
finalFilename = urlFilename;
|
|
194
|
+
} else {
|
|
195
|
+
const ext = this.getExtensionFromUrl(url) || ".jpg";
|
|
196
|
+
finalFilename = `${uuidv4()}${ext}`;
|
|
197
|
+
}
|
|
198
|
+
} else {
|
|
199
|
+
const ext = this.getExtensionFromUrl(url) || ".jpg";
|
|
200
|
+
finalFilename = `${uuidv4()}${ext}`;
|
|
201
|
+
}
|
|
202
|
+
if (!path.extname(finalFilename)) {
|
|
203
|
+
finalFilename += ".jpg";
|
|
204
|
+
}
|
|
205
|
+
const fullPath = path.join(destinationPath, finalFilename);
|
|
206
|
+
if (fs.existsSync(fullPath) && !options.overwrite) {
|
|
207
|
+
throw new Error(`File already exists: ${fullPath}. Set overwrite: true to replace it.`);
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
const response = await axios4.get(url, {
|
|
211
|
+
responseType: "stream",
|
|
212
|
+
maxRedirects: 5
|
|
213
|
+
// Automatically handle redirects
|
|
214
|
+
});
|
|
215
|
+
const writer = fs.createWriteStream(fullPath);
|
|
216
|
+
response.data.pipe(writer);
|
|
217
|
+
return new Promise((resolve, reject) => {
|
|
218
|
+
writer.on("finish", () => {
|
|
219
|
+
resolve(fullPath);
|
|
220
|
+
});
|
|
221
|
+
writer.on("error", (err) => {
|
|
222
|
+
fs.unlink(fullPath, () => {
|
|
223
|
+
});
|
|
224
|
+
reject(err);
|
|
225
|
+
});
|
|
226
|
+
response.data.on("error", (err) => {
|
|
227
|
+
writer.close();
|
|
228
|
+
fs.unlink(fullPath, () => {
|
|
229
|
+
});
|
|
230
|
+
reject(err);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
} catch (error) {
|
|
234
|
+
if (axios4.isAxiosError(error)) {
|
|
235
|
+
throw new Error(`Failed to download image: ${error.message}`);
|
|
236
|
+
}
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Helper method to extract file extension from URL
|
|
242
|
+
* @param url - The URL to extract extension from
|
|
243
|
+
* @returns The file extension (e.g., '.jpg', '.png') or null
|
|
244
|
+
*/
|
|
245
|
+
getExtensionFromUrl(url) {
|
|
246
|
+
try {
|
|
247
|
+
const urlPath = new URL(url).pathname;
|
|
248
|
+
const ext = path.extname(urlPath);
|
|
249
|
+
return ext || null;
|
|
250
|
+
} catch {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// src/index.ts
|
|
259
|
+
var init_index = __esm({
|
|
260
|
+
"src/index.ts"() {
|
|
261
|
+
"use strict";
|
|
262
|
+
init_types();
|
|
263
|
+
init_unsplash();
|
|
264
|
+
init_pexels();
|
|
265
|
+
init_pixabay();
|
|
266
|
+
init_image_fetcher();
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// src/cli.ts
|
|
271
|
+
import { Command } from "commander";
|
|
272
|
+
import * as path2 from "path";
|
|
273
|
+
import * as dotenv from "dotenv";
|
|
274
|
+
import * as fs2 from "fs";
|
|
275
|
+
var require_cli = __commonJS({
|
|
276
|
+
"src/cli.ts"() {
|
|
277
|
+
init_index();
|
|
278
|
+
var possibleEnvPaths = [
|
|
279
|
+
path2.resolve(process.cwd(), ".env"),
|
|
280
|
+
// Current working directory
|
|
281
|
+
path2.resolve(__dirname, ".env"),
|
|
282
|
+
// Same directory as the CLI script (dist/)
|
|
283
|
+
path2.resolve(__dirname, "..", ".env")
|
|
284
|
+
// Parent directory (root of project)
|
|
285
|
+
];
|
|
286
|
+
for (const envPath of possibleEnvPaths) {
|
|
287
|
+
if (fs2.existsSync(envPath)) {
|
|
288
|
+
dotenv.config({ path: envPath });
|
|
289
|
+
console.log(`Loaded environment from: ${envPath}`);
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
var program = new Command();
|
|
294
|
+
program.name("random-image").description("CLI tool to fetch random images from various providers").version("1.2.0");
|
|
295
|
+
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) => {
|
|
296
|
+
try {
|
|
297
|
+
const providers = {
|
|
298
|
+
unsplash: process.env.UNSPLASH_KEY,
|
|
299
|
+
pexels: process.env.PEXELS_KEY,
|
|
300
|
+
pixabay: process.env.PIXABAY_KEY
|
|
301
|
+
};
|
|
302
|
+
let providerName = options.provider.toLowerCase();
|
|
303
|
+
if (providerName === "random") {
|
|
304
|
+
const availableProviders = Object.entries(providers).filter(([_, key]) => key).map(([name]) => name);
|
|
305
|
+
if (availableProviders.length === 0) {
|
|
306
|
+
console.error("Error: No API keys found in environment variables.");
|
|
307
|
+
console.error("Please set at least one of: UNSPLASH_KEY, PEXELS_KEY, PIXABAY_KEY");
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
providerName = availableProviders[Math.floor(Math.random() * availableProviders.length)];
|
|
311
|
+
console.log(`Using random provider: ${providerName}`);
|
|
312
|
+
}
|
|
313
|
+
let provider;
|
|
314
|
+
const apiKey = providers[providerName];
|
|
315
|
+
if (!apiKey) {
|
|
316
|
+
console.error(`Error: API key for ${providerName} not found.`);
|
|
317
|
+
console.error(`Please set ${providerName.toUpperCase()}_KEY environment variable.`);
|
|
318
|
+
process.exit(1);
|
|
319
|
+
}
|
|
320
|
+
switch (providerName) {
|
|
321
|
+
case "unsplash":
|
|
322
|
+
provider = new UnsplashProvider(apiKey);
|
|
323
|
+
break;
|
|
324
|
+
case "pexels":
|
|
325
|
+
provider = new PexelsProvider(apiKey);
|
|
326
|
+
break;
|
|
327
|
+
case "pixabay":
|
|
328
|
+
provider = new PixabayProvider(apiKey);
|
|
329
|
+
break;
|
|
330
|
+
default:
|
|
331
|
+
console.error(`Error: Unknown provider "${providerName}"`);
|
|
332
|
+
console.error("Available providers: unsplash, pexels, pixabay, random");
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
const fetcher = new RandomImage(provider);
|
|
336
|
+
const fetchOptions = {};
|
|
337
|
+
if (options.query) fetchOptions.query = options.query;
|
|
338
|
+
if (options.width) fetchOptions.width = options.width;
|
|
339
|
+
if (options.height) fetchOptions.height = options.height;
|
|
340
|
+
if (options.quality) fetchOptions.quality = options.quality;
|
|
341
|
+
if (options.orientation) fetchOptions.orientation = options.orientation;
|
|
342
|
+
console.log("Fetching random image...");
|
|
343
|
+
const image = await fetcher.getRandom(fetchOptions);
|
|
344
|
+
console.log("\n\u2713 Image fetched successfully!");
|
|
345
|
+
console.log(` URL: ${image.url}`);
|
|
346
|
+
console.log(` Size: ${image.width}x${image.height}`);
|
|
347
|
+
console.log(` Author: ${image.author}`);
|
|
348
|
+
if (image.authorUrl) console.log(` Author URL: ${image.authorUrl}`);
|
|
349
|
+
console.log(` Original: ${image.originalUrl}`);
|
|
350
|
+
if (options.download !== void 0) {
|
|
351
|
+
const downloadPath = typeof options.download === "string" ? options.download : "./downloads";
|
|
352
|
+
console.log(`
|
|
353
|
+
Downloading to ${path2.resolve(downloadPath)}...`);
|
|
354
|
+
const downloadOptions = {
|
|
355
|
+
overwrite: options.overwrite,
|
|
356
|
+
keepOriginalName: options.keepOriginalName
|
|
357
|
+
};
|
|
358
|
+
if (options.filename) {
|
|
359
|
+
downloadOptions.filename = options.filename;
|
|
360
|
+
}
|
|
361
|
+
const filePath = await fetcher.download(image, downloadPath, downloadOptions);
|
|
362
|
+
console.log(`\u2713 Downloaded: ${filePath}`);
|
|
363
|
+
}
|
|
364
|
+
} catch (error) {
|
|
365
|
+
if (error instanceof Error) {
|
|
366
|
+
console.error(`
|
|
367
|
+
Error: ${error.message}`);
|
|
368
|
+
} else {
|
|
369
|
+
console.error("\nAn unexpected error occurred");
|
|
370
|
+
}
|
|
371
|
+
process.exit(1);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
program.parse(process.argv);
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
export default require_cli();
|
package/dist/index.d.mts
CHANGED
|
@@ -16,6 +16,11 @@ interface ImageResult {
|
|
|
16
16
|
interface ImageProvider {
|
|
17
17
|
fetchRandomImage(options: ImageOptions): Promise<ImageResult>;
|
|
18
18
|
}
|
|
19
|
+
interface DownloadOptions {
|
|
20
|
+
filename?: string;
|
|
21
|
+
overwrite?: boolean;
|
|
22
|
+
keepOriginalName?: boolean;
|
|
23
|
+
}
|
|
19
24
|
|
|
20
25
|
declare class UnsplashProvider implements ImageProvider {
|
|
21
26
|
private accessKey;
|
|
@@ -44,6 +49,20 @@ declare class RandomImage {
|
|
|
44
49
|
* @returns A promise that resolves to an ImageResult object.
|
|
45
50
|
*/
|
|
46
51
|
getRandom(options?: ImageOptions): Promise<ImageResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Downloads an image to a specified directory.
|
|
54
|
+
* @param imageUrl - The URL of the image to download (can be a string or ImageResult object)
|
|
55
|
+
* @param destinationPath - The directory path where the image will be saved
|
|
56
|
+
* @param options - Download options (filename, overwrite, etc.)
|
|
57
|
+
* @returns A promise that resolves to the full path of the downloaded file
|
|
58
|
+
*/
|
|
59
|
+
download(imageUrl: string | ImageResult, destinationPath: string, options?: DownloadOptions): Promise<string>;
|
|
60
|
+
/**
|
|
61
|
+
* Helper method to extract file extension from URL
|
|
62
|
+
* @param url - The URL to extract extension from
|
|
63
|
+
* @returns The file extension (e.g., '.jpg', '.png') or null
|
|
64
|
+
*/
|
|
65
|
+
private getExtensionFromUrl;
|
|
47
66
|
}
|
|
48
67
|
|
|
49
|
-
export { type ImageOptions, type ImageProvider, type ImageResult, PexelsProvider, PixabayProvider, RandomImage, UnsplashProvider };
|
|
68
|
+
export { type DownloadOptions, type ImageOptions, type ImageProvider, type ImageResult, PexelsProvider, PixabayProvider, RandomImage, UnsplashProvider };
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,11 @@ interface ImageResult {
|
|
|
16
16
|
interface ImageProvider {
|
|
17
17
|
fetchRandomImage(options: ImageOptions): Promise<ImageResult>;
|
|
18
18
|
}
|
|
19
|
+
interface DownloadOptions {
|
|
20
|
+
filename?: string;
|
|
21
|
+
overwrite?: boolean;
|
|
22
|
+
keepOriginalName?: boolean;
|
|
23
|
+
}
|
|
19
24
|
|
|
20
25
|
declare class UnsplashProvider implements ImageProvider {
|
|
21
26
|
private accessKey;
|
|
@@ -44,6 +49,20 @@ declare class RandomImage {
|
|
|
44
49
|
* @returns A promise that resolves to an ImageResult object.
|
|
45
50
|
*/
|
|
46
51
|
getRandom(options?: ImageOptions): Promise<ImageResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Downloads an image to a specified directory.
|
|
54
|
+
* @param imageUrl - The URL of the image to download (can be a string or ImageResult object)
|
|
55
|
+
* @param destinationPath - The directory path where the image will be saved
|
|
56
|
+
* @param options - Download options (filename, overwrite, etc.)
|
|
57
|
+
* @returns A promise that resolves to the full path of the downloaded file
|
|
58
|
+
*/
|
|
59
|
+
download(imageUrl: string | ImageResult, destinationPath: string, options?: DownloadOptions): Promise<string>;
|
|
60
|
+
/**
|
|
61
|
+
* Helper method to extract file extension from URL
|
|
62
|
+
* @param url - The URL to extract extension from
|
|
63
|
+
* @returns The file extension (e.g., '.jpg', '.png') or null
|
|
64
|
+
*/
|
|
65
|
+
private getExtensionFromUrl;
|
|
47
66
|
}
|
|
48
67
|
|
|
49
|
-
export { type ImageOptions, type ImageProvider, type ImageResult, PexelsProvider, PixabayProvider, RandomImage, UnsplashProvider };
|
|
68
|
+
export { type DownloadOptions, type ImageOptions, type ImageProvider, type ImageResult, PexelsProvider, PixabayProvider, RandomImage, UnsplashProvider };
|
package/dist/index.js
CHANGED
|
@@ -156,6 +156,10 @@ var PixabayProvider = class {
|
|
|
156
156
|
};
|
|
157
157
|
|
|
158
158
|
// src/image-fetcher.ts
|
|
159
|
+
var fs = __toESM(require("fs"));
|
|
160
|
+
var path = __toESM(require("path"));
|
|
161
|
+
var import_axios4 = __toESM(require("axios"));
|
|
162
|
+
var import_uuid = require("uuid");
|
|
159
163
|
var RandomImage = class {
|
|
160
164
|
constructor(provider) {
|
|
161
165
|
this.provider = provider;
|
|
@@ -168,6 +172,86 @@ var RandomImage = class {
|
|
|
168
172
|
async getRandom(options = {}) {
|
|
169
173
|
return this.provider.fetchRandomImage(options);
|
|
170
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Downloads an image to a specified directory.
|
|
177
|
+
* @param imageUrl - The URL of the image to download (can be a string or ImageResult object)
|
|
178
|
+
* @param destinationPath - The directory path where the image will be saved
|
|
179
|
+
* @param options - Download options (filename, overwrite, etc.)
|
|
180
|
+
* @returns A promise that resolves to the full path of the downloaded file
|
|
181
|
+
*/
|
|
182
|
+
async download(imageUrl, destinationPath, options = {}) {
|
|
183
|
+
const url = typeof imageUrl === "string" ? imageUrl : imageUrl.url;
|
|
184
|
+
if (!fs.existsSync(destinationPath)) {
|
|
185
|
+
fs.mkdirSync(destinationPath, { recursive: true });
|
|
186
|
+
}
|
|
187
|
+
let finalFilename;
|
|
188
|
+
if (options.filename) {
|
|
189
|
+
finalFilename = options.filename;
|
|
190
|
+
} else if (options.keepOriginalName) {
|
|
191
|
+
const urlPath = new URL(url).pathname;
|
|
192
|
+
const urlFilename = path.basename(urlPath);
|
|
193
|
+
if (urlFilename && urlFilename.length > 0 && urlFilename !== "/") {
|
|
194
|
+
finalFilename = urlFilename;
|
|
195
|
+
} else {
|
|
196
|
+
const ext = this.getExtensionFromUrl(url) || ".jpg";
|
|
197
|
+
finalFilename = `${(0, import_uuid.v7)()}${ext}`;
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
const ext = this.getExtensionFromUrl(url) || ".jpg";
|
|
201
|
+
finalFilename = `${(0, import_uuid.v7)()}${ext}`;
|
|
202
|
+
}
|
|
203
|
+
if (!path.extname(finalFilename)) {
|
|
204
|
+
finalFilename += ".jpg";
|
|
205
|
+
}
|
|
206
|
+
const fullPath = path.join(destinationPath, finalFilename);
|
|
207
|
+
if (fs.existsSync(fullPath) && !options.overwrite) {
|
|
208
|
+
throw new Error(`File already exists: ${fullPath}. Set overwrite: true to replace it.`);
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const response = await import_axios4.default.get(url, {
|
|
212
|
+
responseType: "stream",
|
|
213
|
+
maxRedirects: 5
|
|
214
|
+
// Automatically handle redirects
|
|
215
|
+
});
|
|
216
|
+
const writer = fs.createWriteStream(fullPath);
|
|
217
|
+
response.data.pipe(writer);
|
|
218
|
+
return new Promise((resolve, reject) => {
|
|
219
|
+
writer.on("finish", () => {
|
|
220
|
+
resolve(fullPath);
|
|
221
|
+
});
|
|
222
|
+
writer.on("error", (err) => {
|
|
223
|
+
fs.unlink(fullPath, () => {
|
|
224
|
+
});
|
|
225
|
+
reject(err);
|
|
226
|
+
});
|
|
227
|
+
response.data.on("error", (err) => {
|
|
228
|
+
writer.close();
|
|
229
|
+
fs.unlink(fullPath, () => {
|
|
230
|
+
});
|
|
231
|
+
reject(err);
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (import_axios4.default.isAxiosError(error)) {
|
|
236
|
+
throw new Error(`Failed to download image: ${error.message}`);
|
|
237
|
+
}
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Helper method to extract file extension from URL
|
|
243
|
+
* @param url - The URL to extract extension from
|
|
244
|
+
* @returns The file extension (e.g., '.jpg', '.png') or null
|
|
245
|
+
*/
|
|
246
|
+
getExtensionFromUrl(url) {
|
|
247
|
+
try {
|
|
248
|
+
const urlPath = new URL(url).pathname;
|
|
249
|
+
const ext = path.extname(urlPath);
|
|
250
|
+
return ext || null;
|
|
251
|
+
} catch {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
171
255
|
};
|
|
172
256
|
// Annotate the CommonJS export names for ESM import in node:
|
|
173
257
|
0 && (module.exports = {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/providers/unsplash.ts","../src/providers/pexels.ts","../src/providers/pixabay.ts","../src/image-fetcher.ts"],"sourcesContent":["export * from \"./types\";\nexport * from \"./providers/unsplash\";\nexport * from \"./providers/pexels\";\nexport * from \"./providers/pixabay\";\nexport * from \"./image-fetcher\";\n","import axios from \"axios\";\nimport { ImageProvider, ImageOptions, ImageResult } from \"../types\";\n\nexport class UnsplashProvider implements ImageProvider {\n private accessKey: string;\n\n constructor(accessKey: string) {\n this.accessKey = accessKey;\n }\n\n async fetchRandomImage(options: ImageOptions): Promise<ImageResult> {\n const response = await axios.get(\"https://api.unsplash.com/photos/random\", {\n headers: {\n Authorization: `Client-ID ${this.accessKey}`,\n },\n params: {\n query: options.query,\n orientation: options.orientation,\n },\n });\n\n const data = response.data;\n\n // Handle single random photo (array if count param used, but here generic assumes one)\n const photo = Array.isArray(data) ? data[0] : data;\n\n // Construct resized URL\n // Unsplash uses Imgix. We can append parameters.\n const baseUrl = photo.urls.raw;\n const sizeParams = new URLSearchParams();\n if (options.height || options.width) {\n sizeParams.append(\"fit\", \"crop\");\n sizeParams.append(\"crop\", \"entropy\");\n }\n if (options.width) sizeParams.append(\"w\", options.width.toString());\n if (options.height) sizeParams.append(\"h\", options.height.toString());\n if (options.quality) sizeParams.append(\"q\", options.quality.toString());\n\n const finalUrl = `${baseUrl}&${sizeParams.toString()}`;\n\n return {\n url: finalUrl,\n width: options.width || photo.width,\n height: options.height || photo.height,\n author: photo.user.name,\n authorUrl: photo.user.links.html,\n originalUrl: photo.links.html, // Link to photo page\n };\n }\n}\n","import axios from \"axios\";\nimport { ImageProvider, ImageOptions, ImageResult } from \"../types\";\n\nexport class PexelsProvider implements ImageProvider {\n private apiKey: string;\n\n constructor(apiKey: string) {\n this.apiKey = apiKey;\n }\n\n async fetchRandomImage(options: ImageOptions): Promise<ImageResult> {\n const endpoint = options.query\n ? \"https://api.pexels.com/v1/search\"\n : \"https://api.pexels.com/v1/curated\";\n\n // Randomizing page number (1-100) to get a \"random\" image\n const randomPage = Math.floor(Math.random() * 100) + 1;\n\n const params: any = {\n per_page: 1,\n page: randomPage,\n };\n if (options.query) params.query = options.query;\n\n const response = await axios.get(endpoint, {\n headers: {\n Authorization: this.apiKey,\n },\n params: params,\n });\n\n const data = response.data;\n\n if (!data.photos || data.photos.length === 0) {\n throw new Error(\"No images found on Pexels\");\n }\n\n const photo = data.photos[0];\n\n const baseUrl = photo.src.original;\n const sizeParams = new URLSearchParams();\n sizeParams.append(\"auto\", \"compress\");\n sizeParams.append(\"cs\", \"tinysrgb\"); // Default pexels param\n if (options.width) sizeParams.append(\"w\", options.width.toString());\n if (options.height) sizeParams.append(\"h\", options.height.toString());\n\n const finalUrl = `${baseUrl}?${sizeParams.toString()}`;\n\n return {\n url: finalUrl,\n width: options.width || photo.width,\n height: options.height || photo.height,\n author: photo.photographer,\n authorUrl: photo.photographer_url,\n originalUrl: photo.url,\n };\n }\n}\n","import axios from \"axios\";\nimport { ImageProvider, ImageOptions, ImageResult } from \"../types\";\n\nexport class PixabayProvider implements ImageProvider {\n private apiKey: string;\n\n constructor(apiKey: string) {\n this.apiKey = apiKey;\n }\n\n async fetchRandomImage(options: ImageOptions): Promise<ImageResult> {\n const params: any = {\n key: this.apiKey,\n q: options.query || \"\",\n per_page: 20, // Fetch a few to pick randomly\n };\n\n if (options.orientation) {\n params.orientation =\n options.orientation === \"portrait\" ? \"vertical\" : \"horizontal\";\n }\n\n const response = await axios.get(\"https://pixabay.com/api/\", {\n params,\n });\n\n const hits = response.data.hits;\n\n if (!hits || hits.length === 0) {\n throw new Error(\"No images found\");\n }\n\n // Pick a random hit\n const randomHit = hits[Math.floor(Math.random() * hits.length)];\n\n return {\n url: randomHit.largeImageURL || randomHit.webformatURL,\n width: randomHit.imageWidth || randomHit.webformatWidth,\n height: randomHit.imageHeight || randomHit.webformatHeight,\n author: randomHit.user,\n authorUrl: `https://pixabay.com/users/${randomHit.user}-${randomHit.user_id}/`,\n originalUrl: randomHit.pageURL,\n };\n }\n}\n","import { ImageProvider, ImageOptions, ImageResult } from './types';\n\nexport class RandomImage {\n private provider: ImageProvider;\n\n constructor(provider: ImageProvider) {\n this.provider = provider;\n }\n\n /**\n * Fetches a random image based on the provided options.\n * @param options - Configuration options for the image (width, height, query, etc.)\n * @returns A promise that resolves to an ImageResult object.\n */\n async getRandom(options: ImageOptions = {}): Promise<ImageResult> {\n return this.provider.fetchRandomImage(options);\n }\n}\n"],"mappings":"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","names":["axios","import_axios","axios","import_axios","axios"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/providers/unsplash.ts","../src/providers/pexels.ts","../src/providers/pixabay.ts","../src/image-fetcher.ts"],"sourcesContent":["export * from \"./types\";\nexport * from \"./providers/unsplash\";\nexport * from \"./providers/pexels\";\nexport * from \"./providers/pixabay\";\nexport * from \"./image-fetcher\";\n","import axios from \"axios\";\nimport { ImageProvider, ImageOptions, ImageResult } from \"../types\";\n\nexport class UnsplashProvider implements ImageProvider {\n private accessKey: string;\n\n constructor(accessKey: string) {\n this.accessKey = accessKey;\n }\n\n async fetchRandomImage(options: ImageOptions): Promise<ImageResult> {\n const response = await axios.get(\"https://api.unsplash.com/photos/random\", {\n headers: {\n Authorization: `Client-ID ${this.accessKey}`,\n },\n params: {\n query: options.query,\n orientation: options.orientation,\n },\n });\n\n const data = response.data;\n\n // Handle single random photo (array if count param used, but here generic assumes one)\n const photo = Array.isArray(data) ? data[0] : data;\n\n // Construct resized URL\n // Unsplash uses Imgix. We can append parameters.\n const baseUrl = photo.urls.raw;\n const sizeParams = new URLSearchParams();\n if (options.height || options.width) {\n sizeParams.append(\"fit\", \"crop\");\n sizeParams.append(\"crop\", \"entropy\");\n }\n if (options.width) sizeParams.append(\"w\", options.width.toString());\n if (options.height) sizeParams.append(\"h\", options.height.toString());\n if (options.quality) sizeParams.append(\"q\", options.quality.toString());\n\n const finalUrl = `${baseUrl}&${sizeParams.toString()}`;\n\n return {\n url: finalUrl,\n width: options.width || photo.width,\n height: options.height || photo.height,\n author: photo.user.name,\n authorUrl: photo.user.links.html,\n originalUrl: photo.links.html, // Link to photo page\n };\n }\n}\n","import axios from \"axios\";\nimport { ImageProvider, ImageOptions, ImageResult } from \"../types\";\n\nexport class PexelsProvider implements ImageProvider {\n private apiKey: string;\n\n constructor(apiKey: string) {\n this.apiKey = apiKey;\n }\n\n async fetchRandomImage(options: ImageOptions): Promise<ImageResult> {\n const endpoint = options.query\n ? \"https://api.pexels.com/v1/search\"\n : \"https://api.pexels.com/v1/curated\";\n\n // Randomizing page number (1-100) to get a \"random\" image\n const randomPage = Math.floor(Math.random() * 100) + 1;\n\n const params: any = {\n per_page: 1,\n page: randomPage,\n };\n if (options.query) params.query = options.query;\n\n const response = await axios.get(endpoint, {\n headers: {\n Authorization: this.apiKey,\n },\n params: params,\n });\n\n const data = response.data;\n\n if (!data.photos || data.photos.length === 0) {\n throw new Error(\"No images found on Pexels\");\n }\n\n const photo = data.photos[0];\n\n const baseUrl = photo.src.original;\n const sizeParams = new URLSearchParams();\n sizeParams.append(\"auto\", \"compress\");\n sizeParams.append(\"cs\", \"tinysrgb\"); // Default pexels param\n if (options.width) sizeParams.append(\"w\", options.width.toString());\n if (options.height) sizeParams.append(\"h\", options.height.toString());\n\n const finalUrl = `${baseUrl}?${sizeParams.toString()}`;\n\n return {\n url: finalUrl,\n width: options.width || photo.width,\n height: options.height || photo.height,\n author: photo.photographer,\n authorUrl: photo.photographer_url,\n originalUrl: photo.url,\n };\n }\n}\n","import axios from \"axios\";\nimport { ImageProvider, ImageOptions, ImageResult } from \"../types\";\n\nexport class PixabayProvider implements ImageProvider {\n private apiKey: string;\n\n constructor(apiKey: string) {\n this.apiKey = apiKey;\n }\n\n async fetchRandomImage(options: ImageOptions): Promise<ImageResult> {\n const params: any = {\n key: this.apiKey,\n q: options.query || \"\",\n per_page: 20, // Fetch a few to pick randomly\n };\n\n if (options.orientation) {\n params.orientation =\n options.orientation === \"portrait\" ? \"vertical\" : \"horizontal\";\n }\n\n const response = await axios.get(\"https://pixabay.com/api/\", {\n params,\n });\n\n const hits = response.data.hits;\n\n if (!hits || hits.length === 0) {\n throw new Error(\"No images found\");\n }\n\n // Pick a random hit\n const randomHit = hits[Math.floor(Math.random() * hits.length)];\n\n return {\n url: randomHit.largeImageURL || randomHit.webformatURL,\n width: randomHit.imageWidth || randomHit.webformatWidth,\n height: randomHit.imageHeight || randomHit.webformatHeight,\n author: randomHit.user,\n authorUrl: `https://pixabay.com/users/${randomHit.user}-${randomHit.user_id}/`,\n originalUrl: randomHit.pageURL,\n };\n }\n}\n","import { ImageProvider, ImageOptions, ImageResult, DownloadOptions } from './types';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport axios from 'axios';\nimport { v7 as uuidv4 } from 'uuid';\n\nexport class RandomImage {\n private provider: ImageProvider;\n\n constructor(provider: ImageProvider) {\n this.provider = provider;\n }\n\n /**\n * Fetches a random image based on the provided options.\n * @param options - Configuration options for the image (width, height, query, etc.)\n * @returns A promise that resolves to an ImageResult object.\n */\n async getRandom(options: ImageOptions = {}): Promise<ImageResult> {\n return this.provider.fetchRandomImage(options);\n }\n\n /**\n * Downloads an image to a specified directory.\n * @param imageUrl - The URL of the image to download (can be a string or ImageResult object)\n * @param destinationPath - The directory path where the image will be saved\n * @param options - Download options (filename, overwrite, etc.)\n * @returns A promise that resolves to the full path of the downloaded file\n */\n async download(\n imageUrl: string | ImageResult,\n destinationPath: string,\n options: DownloadOptions = {}\n ): Promise<string> {\n const url = typeof imageUrl === 'string' ? imageUrl : imageUrl.url;\n\n // Create directory if it doesn't exist\n if (!fs.existsSync(destinationPath)) {\n fs.mkdirSync(destinationPath, { recursive: true });\n }\n\n // Determine filename\n let finalFilename: string;\n if (options.filename) {\n finalFilename = options.filename;\n } else if (options.keepOriginalName) {\n // keepOriginalName === true or undefined (default behavior: try to keep original)\n const urlPath = new URL(url).pathname;\n const urlFilename = path.basename(urlPath);\n if (urlFilename && urlFilename.length > 0 && urlFilename !== '/') {\n finalFilename = urlFilename;\n } else {\n // Fallback if no filename in URL\n const ext = this.getExtensionFromUrl(url) || '.jpg';\n finalFilename = `${uuidv4()}${ext}`;\n }\n } else {\n // Generate UUID v4 filename\n const ext = this.getExtensionFromUrl(url) || '.jpg';\n finalFilename = `${uuidv4()}${ext}`;\n }\n\n // Ensure filename has an extension\n if (!path.extname(finalFilename)) {\n finalFilename += '.jpg';\n }\n\n const fullPath = path.join(destinationPath, finalFilename);\n\n // Check if file exists and overwrite option\n if (fs.existsSync(fullPath) && !options.overwrite) {\n throw new Error(`File already exists: ${fullPath}. Set overwrite: true to replace it.`);\n }\n\n try {\n const response = await axios.get(url, {\n responseType: 'stream',\n maxRedirects: 5, // Automatically handle redirects\n });\n\n const writer = fs.createWriteStream(fullPath);\n\n response.data.pipe(writer);\n\n return new Promise((resolve, reject) => {\n writer.on('finish', () => {\n resolve(fullPath);\n });\n\n writer.on('error', (err) => {\n fs.unlink(fullPath, () => { }); // Delete partial file\n reject(err);\n });\n\n response.data.on('error', (err: Error) => {\n writer.close();\n fs.unlink(fullPath, () => { }); // Delete partial file\n reject(err);\n });\n });\n } catch (error) {\n if (axios.isAxiosError(error)) {\n throw new Error(`Failed to download image: ${error.message}`);\n }\n throw error;\n }\n }\n\n /**\n * Helper method to extract file extension from URL\n * @param url - The URL to extract extension from\n * @returns The file extension (e.g., '.jpg', '.png') or null\n */\n private getExtensionFromUrl(url: string): string | null {\n try {\n const urlPath = new URL(url).pathname;\n const ext = path.extname(urlPath);\n return ext || null;\n } catch {\n return null;\n }\n }\n}\n"],"mappings":"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","names":["axios","import_axios","axios","import_axios","axios","import_axios","uuidv4","axios"]}
|