@nghiavuive/random-image 1.0.0 → 1.1.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 +115 -15
- package/dist/index.d.mts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +152 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +141 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
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.
|
|
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
11
|
## Usage
|
|
@@ -14,33 +14,91 @@ You need to obtain API keys from the respective providers:
|
|
|
14
14
|
- [Unsplash Developers](https://unsplash.com/developers)
|
|
15
15
|
- [Pexels API](https://www.pexels.com/api/)
|
|
16
16
|
|
|
17
|
+
### Basic Usage - Fetching Random Images
|
|
18
|
+
|
|
17
19
|
```typescript
|
|
18
|
-
import { RandomImage, UnsplashProvider, PexelsProvider } from 'random-image';
|
|
20
|
+
import { RandomImage, UnsplashProvider, PexelsProvider } from '@nghiavuive/random-image';
|
|
19
21
|
|
|
20
22
|
// Using Unsplash
|
|
21
23
|
const unsplash = new UnsplashProvider('YOUR_UNSPLASH_ACCESS_KEY');
|
|
22
24
|
const fetcher = new RandomImage(unsplash);
|
|
23
25
|
|
|
24
|
-
fetcher.getRandom({
|
|
26
|
+
const image = await fetcher.getRandom({
|
|
25
27
|
width: 1920,
|
|
26
28
|
height: 1080,
|
|
27
29
|
query: 'nature'
|
|
28
|
-
}).then(image => {
|
|
29
|
-
console.log(image.url); // URL to the image
|
|
30
|
-
console.log(image.author); // Photographer's name
|
|
31
30
|
});
|
|
32
31
|
|
|
32
|
+
console.log(image.url); // URL to the image
|
|
33
|
+
console.log(image.author); // Photographer's name
|
|
34
|
+
|
|
33
35
|
// Using Pexels
|
|
34
36
|
const pexels = new PexelsProvider('YOUR_PEXELS_API_KEY');
|
|
35
37
|
const pexelsFetcher = new RandomImage(pexels);
|
|
36
38
|
|
|
37
|
-
pexelsFetcher.getRandom({
|
|
39
|
+
const pexelsImage = await pexelsFetcher.getRandom({
|
|
38
40
|
width: 800,
|
|
39
41
|
height: 600,
|
|
40
42
|
query: 'cats'
|
|
41
|
-
}).then(image => {
|
|
42
|
-
console.log(image.url);
|
|
43
43
|
});
|
|
44
|
+
|
|
45
|
+
console.log(pexelsImage.url);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Downloading Images
|
|
49
|
+
|
|
50
|
+
The library now supports downloading images directly to your local filesystem:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { RandomImage, UnsplashProvider } from '@nghiavuive/random-image';
|
|
54
|
+
|
|
55
|
+
const provider = new UnsplashProvider('YOUR_API_KEY');
|
|
56
|
+
const fetcher = new RandomImage(provider);
|
|
57
|
+
|
|
58
|
+
// Fetch and download a random image
|
|
59
|
+
const image = await fetcher.getRandom({ query: 'mountains' });
|
|
60
|
+
|
|
61
|
+
// Download with auto-generated filename
|
|
62
|
+
const filePath = await fetcher.download(image, './downloads');
|
|
63
|
+
console.log(`Image saved to: ${filePath}`);
|
|
64
|
+
|
|
65
|
+
// Download with custom filename
|
|
66
|
+
const customPath = await fetcher.download(
|
|
67
|
+
image,
|
|
68
|
+
'./downloads',
|
|
69
|
+
{ filename: 'my-mountain.jpg' }
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// Download with overwrite option
|
|
73
|
+
const overwritePath = await fetcher.download(
|
|
74
|
+
image,
|
|
75
|
+
'./downloads',
|
|
76
|
+
{
|
|
77
|
+
filename: 'mountain.jpg',
|
|
78
|
+
overwrite: true // Will replace existing file
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Download directly from URL
|
|
83
|
+
await fetcher.download(
|
|
84
|
+
'https://example.com/image.jpg',
|
|
85
|
+
'./downloads',
|
|
86
|
+
{ filename: 'direct-download.jpg' }
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Download with UUID-based random filename
|
|
90
|
+
await fetcher.download(
|
|
91
|
+
image,
|
|
92
|
+
'./downloads',
|
|
93
|
+
{ keepOriginalName: false }
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// Download keeping original filename (default behavior)
|
|
97
|
+
await fetcher.download(
|
|
98
|
+
image,
|
|
99
|
+
'./downloads',
|
|
100
|
+
{ keepOriginalName: true }
|
|
101
|
+
);
|
|
44
102
|
```
|
|
45
103
|
|
|
46
104
|
## API
|
|
@@ -51,24 +109,66 @@ The main class to interact with.
|
|
|
51
109
|
```typescript
|
|
52
110
|
class RandomImage {
|
|
53
111
|
constructor(provider: ImageProvider);
|
|
112
|
+
|
|
113
|
+
// Fetch a random image
|
|
54
114
|
getRandom(options: ImageOptions): Promise<ImageResult>;
|
|
115
|
+
|
|
116
|
+
// Download an image to local filesystem
|
|
117
|
+
download(
|
|
118
|
+
imageUrl: string | ImageResult,
|
|
119
|
+
destinationPath: string,
|
|
120
|
+
options?: DownloadOptions
|
|
121
|
+
): Promise<string>;
|
|
55
122
|
}
|
|
56
123
|
```
|
|
57
124
|
|
|
58
125
|
### `ImageOptions`
|
|
59
|
-
|
|
60
|
-
- `
|
|
61
|
-
- `
|
|
62
|
-
- `
|
|
126
|
+
Configuration for fetching random images:
|
|
127
|
+
- `width` (number, optional): Desired width of the image.
|
|
128
|
+
- `height` (number, optional): Desired height of the image.
|
|
129
|
+
- `quality` (number, optional): Quality (0-100) if supported by provider.
|
|
130
|
+
- `query` (string, optional): Search query (e.g. "nature", "city").
|
|
131
|
+
- `orientation` ("landscape" | "portrait", optional): Image orientation.
|
|
132
|
+
|
|
133
|
+
### `DownloadOptions`
|
|
134
|
+
Configuration for downloading images:
|
|
135
|
+
- `filename` (string, optional): Custom filename for the downloaded image. If provided, this takes precedence over `keepOriginalName`.
|
|
136
|
+
- `overwrite` (boolean, optional): Whether to overwrite existing files. Default is `false`. If `false` and file exists, an error will be thrown.
|
|
137
|
+
- `keepOriginalName` (boolean, optional): Controls filename generation when `filename` is not provided:
|
|
138
|
+
- `true` or `undefined` (default): Extract and use the original filename from the URL
|
|
139
|
+
- `false`: Generate a random UUID-based filename
|
|
63
140
|
|
|
64
141
|
### `ImageResult`
|
|
142
|
+
Result object containing image information:
|
|
65
143
|
- `url` (string): Direct URL to the image.
|
|
66
144
|
- `width` (number): Width of the image.
|
|
67
145
|
- `height` (number): Height of the image.
|
|
68
146
|
- `author` (string): Name of the photographer.
|
|
69
|
-
- `authorUrl` (string): URL to photographer's profile.
|
|
147
|
+
- `authorUrl` (string, optional): URL to photographer's profile.
|
|
70
148
|
- `originalUrl` (string): URL to the original photo page.
|
|
71
149
|
|
|
150
|
+
## Features
|
|
151
|
+
|
|
152
|
+
### Download Functionality
|
|
153
|
+
- ✅ Automatic directory creation
|
|
154
|
+
- ✅ Flexible filename options:
|
|
155
|
+
- Custom filename
|
|
156
|
+
- Keep original filename from URL (default)
|
|
157
|
+
- Generate random UUID-based filename
|
|
158
|
+
- ✅ Overwrite protection
|
|
159
|
+
- ✅ Automatic file extension detection
|
|
160
|
+
- ✅ Support for both HTTP and HTTPS
|
|
161
|
+
- ✅ Automatic redirect handling (up to 5 redirects)
|
|
162
|
+
- ✅ Stream-based downloading for memory efficiency
|
|
163
|
+
- ✅ Error handling with cleanup of partial downloads
|
|
164
|
+
|
|
165
|
+
## Supported Providers
|
|
166
|
+
|
|
167
|
+
- **Unsplash**: High-quality photos with attribution
|
|
168
|
+
- **Pexels**: Free stock photos and videos
|
|
169
|
+
- **Pixabay**: Free images and videos
|
|
170
|
+
|
|
72
171
|
## License
|
|
73
172
|
|
|
74
173
|
ISC
|
|
174
|
+
|
package/dist/index.d.mts
CHANGED
|
@@ -3,6 +3,7 @@ interface ImageOptions {
|
|
|
3
3
|
height?: number;
|
|
4
4
|
quality?: number;
|
|
5
5
|
query?: string;
|
|
6
|
+
orientation?: "landscape" | "portrait";
|
|
6
7
|
}
|
|
7
8
|
interface ImageResult {
|
|
8
9
|
url: string;
|
|
@@ -15,6 +16,11 @@ interface ImageResult {
|
|
|
15
16
|
interface ImageProvider {
|
|
16
17
|
fetchRandomImage(options: ImageOptions): Promise<ImageResult>;
|
|
17
18
|
}
|
|
19
|
+
interface DownloadOptions {
|
|
20
|
+
filename?: string;
|
|
21
|
+
overwrite?: boolean;
|
|
22
|
+
keepOriginalName?: boolean;
|
|
23
|
+
}
|
|
18
24
|
|
|
19
25
|
declare class UnsplashProvider implements ImageProvider {
|
|
20
26
|
private accessKey;
|
|
@@ -28,6 +34,12 @@ declare class PexelsProvider implements ImageProvider {
|
|
|
28
34
|
fetchRandomImage(options: ImageOptions): Promise<ImageResult>;
|
|
29
35
|
}
|
|
30
36
|
|
|
37
|
+
declare class PixabayProvider implements ImageProvider {
|
|
38
|
+
private apiKey;
|
|
39
|
+
constructor(apiKey: string);
|
|
40
|
+
fetchRandomImage(options: ImageOptions): Promise<ImageResult>;
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
declare class RandomImage {
|
|
32
44
|
private provider;
|
|
33
45
|
constructor(provider: ImageProvider);
|
|
@@ -37,6 +49,20 @@ declare class RandomImage {
|
|
|
37
49
|
* @returns A promise that resolves to an ImageResult object.
|
|
38
50
|
*/
|
|
39
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;
|
|
40
66
|
}
|
|
41
67
|
|
|
42
|
-
export { type ImageOptions, type ImageProvider, type ImageResult, PexelsProvider, RandomImage, UnsplashProvider };
|
|
68
|
+
export { type DownloadOptions, type ImageOptions, type ImageProvider, type ImageResult, PexelsProvider, PixabayProvider, RandomImage, UnsplashProvider };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ interface ImageOptions {
|
|
|
3
3
|
height?: number;
|
|
4
4
|
quality?: number;
|
|
5
5
|
query?: string;
|
|
6
|
+
orientation?: "landscape" | "portrait";
|
|
6
7
|
}
|
|
7
8
|
interface ImageResult {
|
|
8
9
|
url: string;
|
|
@@ -15,6 +16,11 @@ interface ImageResult {
|
|
|
15
16
|
interface ImageProvider {
|
|
16
17
|
fetchRandomImage(options: ImageOptions): Promise<ImageResult>;
|
|
17
18
|
}
|
|
19
|
+
interface DownloadOptions {
|
|
20
|
+
filename?: string;
|
|
21
|
+
overwrite?: boolean;
|
|
22
|
+
keepOriginalName?: boolean;
|
|
23
|
+
}
|
|
18
24
|
|
|
19
25
|
declare class UnsplashProvider implements ImageProvider {
|
|
20
26
|
private accessKey;
|
|
@@ -28,6 +34,12 @@ declare class PexelsProvider implements ImageProvider {
|
|
|
28
34
|
fetchRandomImage(options: ImageOptions): Promise<ImageResult>;
|
|
29
35
|
}
|
|
30
36
|
|
|
37
|
+
declare class PixabayProvider implements ImageProvider {
|
|
38
|
+
private apiKey;
|
|
39
|
+
constructor(apiKey: string);
|
|
40
|
+
fetchRandomImage(options: ImageOptions): Promise<ImageResult>;
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
declare class RandomImage {
|
|
32
44
|
private provider;
|
|
33
45
|
constructor(provider: ImageProvider);
|
|
@@ -37,6 +49,20 @@ declare class RandomImage {
|
|
|
37
49
|
* @returns A promise that resolves to an ImageResult object.
|
|
38
50
|
*/
|
|
39
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;
|
|
40
66
|
}
|
|
41
67
|
|
|
42
|
-
export { type ImageOptions, type ImageProvider, type ImageResult, PexelsProvider, RandomImage, UnsplashProvider };
|
|
68
|
+
export { type DownloadOptions, type ImageOptions, type ImageProvider, type ImageResult, PexelsProvider, PixabayProvider, RandomImage, UnsplashProvider };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,38 +17,50 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
21
31
|
var index_exports = {};
|
|
22
32
|
__export(index_exports, {
|
|
23
33
|
PexelsProvider: () => PexelsProvider,
|
|
34
|
+
PixabayProvider: () => PixabayProvider,
|
|
24
35
|
RandomImage: () => RandomImage,
|
|
25
36
|
UnsplashProvider: () => UnsplashProvider
|
|
26
37
|
});
|
|
27
38
|
module.exports = __toCommonJS(index_exports);
|
|
28
39
|
|
|
29
40
|
// src/providers/unsplash.ts
|
|
41
|
+
var import_axios = __toESM(require("axios"));
|
|
30
42
|
var UnsplashProvider = class {
|
|
31
43
|
constructor(accessKey) {
|
|
32
44
|
this.accessKey = accessKey;
|
|
33
45
|
}
|
|
34
46
|
async fetchRandomImage(options) {
|
|
35
|
-
const
|
|
36
|
-
if (options.query) params.append("query", options.query);
|
|
37
|
-
const url = `https://api.unsplash.com/photos/random?${params.toString()}`;
|
|
38
|
-
const response = await fetch(url, {
|
|
47
|
+
const response = await import_axios.default.get("https://api.unsplash.com/photos/random", {
|
|
39
48
|
headers: {
|
|
40
49
|
Authorization: `Client-ID ${this.accessKey}`
|
|
50
|
+
},
|
|
51
|
+
params: {
|
|
52
|
+
query: options.query,
|
|
53
|
+
orientation: options.orientation
|
|
41
54
|
}
|
|
42
55
|
});
|
|
43
|
-
|
|
44
|
-
throw new Error(`Unsplash API error: ${response.statusText}`);
|
|
45
|
-
}
|
|
46
|
-
const data = await response.json();
|
|
56
|
+
const data = response.data;
|
|
47
57
|
const photo = Array.isArray(data) ? data[0] : data;
|
|
48
58
|
const baseUrl = photo.urls.raw;
|
|
49
59
|
const sizeParams = new URLSearchParams();
|
|
60
|
+
if (options.height || options.width) {
|
|
61
|
+
sizeParams.append("fit", "crop");
|
|
62
|
+
sizeParams.append("crop", "entropy");
|
|
63
|
+
}
|
|
50
64
|
if (options.width) sizeParams.append("w", options.width.toString());
|
|
51
65
|
if (options.height) sizeParams.append("h", options.height.toString());
|
|
52
66
|
if (options.quality) sizeParams.append("q", options.quality.toString());
|
|
@@ -64,6 +78,7 @@ var UnsplashProvider = class {
|
|
|
64
78
|
};
|
|
65
79
|
|
|
66
80
|
// src/providers/pexels.ts
|
|
81
|
+
var import_axios2 = __toESM(require("axios"));
|
|
67
82
|
var PexelsProvider = class {
|
|
68
83
|
constructor(apiKey) {
|
|
69
84
|
this.apiKey = apiKey;
|
|
@@ -71,19 +86,18 @@ var PexelsProvider = class {
|
|
|
71
86
|
async fetchRandomImage(options) {
|
|
72
87
|
const endpoint = options.query ? "https://api.pexels.com/v1/search" : "https://api.pexels.com/v1/curated";
|
|
73
88
|
const randomPage = Math.floor(Math.random() * 100) + 1;
|
|
74
|
-
const params =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
const params = {
|
|
90
|
+
per_page: 1,
|
|
91
|
+
page: randomPage
|
|
92
|
+
};
|
|
93
|
+
if (options.query) params.query = options.query;
|
|
94
|
+
const response = await import_axios2.default.get(endpoint, {
|
|
79
95
|
headers: {
|
|
80
96
|
Authorization: this.apiKey
|
|
81
|
-
}
|
|
97
|
+
},
|
|
98
|
+
params
|
|
82
99
|
});
|
|
83
|
-
|
|
84
|
-
throw new Error(`Pexels API error: ${response.statusText}`);
|
|
85
|
-
}
|
|
86
|
-
const data = await response.json();
|
|
100
|
+
const data = response.data;
|
|
87
101
|
if (!data.photos || data.photos.length === 0) {
|
|
88
102
|
throw new Error("No images found on Pexels");
|
|
89
103
|
}
|
|
@@ -106,7 +120,46 @@ var PexelsProvider = class {
|
|
|
106
120
|
}
|
|
107
121
|
};
|
|
108
122
|
|
|
123
|
+
// src/providers/pixabay.ts
|
|
124
|
+
var import_axios3 = __toESM(require("axios"));
|
|
125
|
+
var PixabayProvider = class {
|
|
126
|
+
constructor(apiKey) {
|
|
127
|
+
this.apiKey = apiKey;
|
|
128
|
+
}
|
|
129
|
+
async fetchRandomImage(options) {
|
|
130
|
+
const params = {
|
|
131
|
+
key: this.apiKey,
|
|
132
|
+
q: options.query || "",
|
|
133
|
+
per_page: 20
|
|
134
|
+
// Fetch a few to pick randomly
|
|
135
|
+
};
|
|
136
|
+
if (options.orientation) {
|
|
137
|
+
params.orientation = options.orientation === "portrait" ? "vertical" : "horizontal";
|
|
138
|
+
}
|
|
139
|
+
const response = await import_axios3.default.get("https://pixabay.com/api/", {
|
|
140
|
+
params
|
|
141
|
+
});
|
|
142
|
+
const hits = response.data.hits;
|
|
143
|
+
if (!hits || hits.length === 0) {
|
|
144
|
+
throw new Error("No images found");
|
|
145
|
+
}
|
|
146
|
+
const randomHit = hits[Math.floor(Math.random() * hits.length)];
|
|
147
|
+
return {
|
|
148
|
+
url: randomHit.largeImageURL || randomHit.webformatURL,
|
|
149
|
+
width: randomHit.imageWidth || randomHit.webformatWidth,
|
|
150
|
+
height: randomHit.imageHeight || randomHit.webformatHeight,
|
|
151
|
+
author: randomHit.user,
|
|
152
|
+
authorUrl: `https://pixabay.com/users/${randomHit.user}-${randomHit.user_id}/`,
|
|
153
|
+
originalUrl: randomHit.pageURL
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
109
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");
|
|
110
163
|
var RandomImage = class {
|
|
111
164
|
constructor(provider) {
|
|
112
165
|
this.provider = provider;
|
|
@@ -119,10 +172,91 @@ var RandomImage = class {
|
|
|
119
172
|
async getRandom(options = {}) {
|
|
120
173
|
return this.provider.fetchRandomImage(options);
|
|
121
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
|
+
}
|
|
122
255
|
};
|
|
123
256
|
// Annotate the CommonJS export names for ESM import in node:
|
|
124
257
|
0 && (module.exports = {
|
|
125
258
|
PexelsProvider,
|
|
259
|
+
PixabayProvider,
|
|
126
260
|
RandomImage,
|
|
127
261
|
UnsplashProvider
|
|
128
262
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/providers/unsplash.ts","../src/providers/pexels.ts","../src/image-fetcher.ts"],"sourcesContent":["export * from './types';\nexport * from './providers/unsplash';\nexport * from './providers/pexels';\nexport * from './image-fetcher';\n","import { 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 params = new URLSearchParams();\n if (options.query) params.append('query', options.query);\n // Unsplash doesn't strictly support width/height for random photo selection in the same way for resizing via API query params on the /random endpoint directly for *fetching* the image content, \n // but we can request specific dimensions roughly or rely on the returned structure to pick a size.\n // However, the /photos/random endpoint returns a JSON with urls.raw, urls.full, etc.\n // We can append parameters to the returned URL for resizing.\n \n // Using fetching logic:\n const url = `https://api.unsplash.com/photos/random?${params.toString()}`;\n \n const response = await fetch(url, {\n headers: {\n Authorization: `Client-ID ${this.accessKey}`\n }\n });\n\n if (!response.ok) {\n throw new Error(`Unsplash API error: ${response.statusText}`);\n }\n\n const data = await response.json();\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.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 { 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 // Pexels 'curated' or 'search'. \n // If query is present, use search. If not, use curated.\n // We need to randomize the result because Pexels search/curated returns a list.\n // We can use 'page' and 'per_page=1' with a random page number to simulate random? \n // Or just fetch one page and pick random?\n // Pexels API doesn't have a direct /random endpoint like Unsplash.\n // Efficient strategy: Search with 1 result per page, but randomize the page number.\n // Max page is tricky, maybe limit to top 100 results?\n \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 = new URLSearchParams();\n if (options.query) params.append('query', options.query);\n params.append('per_page', '1');\n params.append('page', randomPage.toString());\n\n const response = await fetch(`${endpoint}?${params.toString()}`, {\n headers: {\n Authorization: this.apiKey\n }\n });\n\n if (!response.ok) {\n throw new Error(`Pexels API error: ${response.statusText}`);\n }\n\n const data = await response.json();\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 // Pexels supports resizing via query params on the image url?\n // Pexels returns src object with original, large2x, large, medium, small, portrait, landscape, tiny.\n // Or we can modify the 'original' url.\n // Pexels docs say: https://images.pexels.com/photos/2014422/pexels-photo-2014422.jpeg?auto=compress&cs=tinysrgb&h=350\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 // Note: Quality param is not standard in Pexels URL manipulation documented publicly as 'q', \n // but 'auto=compress' handles it. We can try adding it if needed or ignore. \n // I'll skip explicit q param mapping for now as it's not strictly 'quality=X'.\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 { 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":[]}
|
|
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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAkB;AAGX,IAAM,mBAAN,MAAgD;AAAA,EAGrD,YAAY,WAAmB;AAC7B,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,MAAM,iBAAiB,SAA6C;AAClE,UAAM,WAAW,MAAM,aAAAA,QAAM,IAAI,0CAA0C;AAAA,MACzE,SAAS;AAAA,QACP,eAAe,aAAa,KAAK,SAAS;AAAA,MAC5C;AAAA,MACA,QAAQ;AAAA,QACN,OAAO,QAAQ;AAAA,QACf,aAAa,QAAQ;AAAA,MACvB;AAAA,IACF,CAAC;AAED,UAAM,OAAO,SAAS;AAGtB,UAAM,QAAQ,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI;AAI9C,UAAM,UAAU,MAAM,KAAK;AAC3B,UAAM,aAAa,IAAI,gBAAgB;AACvC,QAAI,QAAQ,UAAU,QAAQ,OAAO;AACnC,iBAAW,OAAO,OAAO,MAAM;AAC/B,iBAAW,OAAO,QAAQ,SAAS;AAAA,IACrC;AACA,QAAI,QAAQ,MAAO,YAAW,OAAO,KAAK,QAAQ,MAAM,SAAS,CAAC;AAClE,QAAI,QAAQ,OAAQ,YAAW,OAAO,KAAK,QAAQ,OAAO,SAAS,CAAC;AACpE,QAAI,QAAQ,QAAS,YAAW,OAAO,KAAK,QAAQ,QAAQ,SAAS,CAAC;AAEtE,UAAM,WAAW,GAAG,OAAO,IAAI,WAAW,SAAS,CAAC;AAEpD,WAAO;AAAA,MACL,KAAK;AAAA,MACL,OAAO,QAAQ,SAAS,MAAM;AAAA,MAC9B,QAAQ,QAAQ,UAAU,MAAM;AAAA,MAChC,QAAQ,MAAM,KAAK;AAAA,MACnB,WAAW,MAAM,KAAK,MAAM;AAAA,MAC5B,aAAa,MAAM,MAAM;AAAA;AAAA,IAC3B;AAAA,EACF;AACF;;;ACjDA,IAAAC,gBAAkB;AAGX,IAAM,iBAAN,MAA8C;AAAA,EAGnD,YAAY,QAAgB;AAC1B,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,iBAAiB,SAA6C;AAClE,UAAM,WAAW,QAAQ,QACrB,qCACA;AAGJ,UAAM,aAAa,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG,IAAI;AAErD,UAAM,SAAc;AAAA,MAClB,UAAU;AAAA,MACV,MAAM;AAAA,IACR;AACA,QAAI,QAAQ,MAAO,QAAO,QAAQ,QAAQ;AAE1C,UAAM,WAAW,MAAM,cAAAC,QAAM,IAAI,UAAU;AAAA,MACzC,SAAS;AAAA,QACP,eAAe,KAAK;AAAA,MACtB;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,OAAO,SAAS;AAEtB,QAAI,CAAC,KAAK,UAAU,KAAK,OAAO,WAAW,GAAG;AAC5C,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AAEA,UAAM,QAAQ,KAAK,OAAO,CAAC;AAE3B,UAAM,UAAU,MAAM,IAAI;AAC1B,UAAM,aAAa,IAAI,gBAAgB;AACvC,eAAW,OAAO,QAAQ,UAAU;AACpC,eAAW,OAAO,MAAM,UAAU;AAClC,QAAI,QAAQ,MAAO,YAAW,OAAO,KAAK,QAAQ,MAAM,SAAS,CAAC;AAClE,QAAI,QAAQ,OAAQ,YAAW,OAAO,KAAK,QAAQ,OAAO,SAAS,CAAC;AAEpE,UAAM,WAAW,GAAG,OAAO,IAAI,WAAW,SAAS,CAAC;AAEpD,WAAO;AAAA,MACL,KAAK;AAAA,MACL,OAAO,QAAQ,SAAS,MAAM;AAAA,MAC9B,QAAQ,QAAQ,UAAU,MAAM;AAAA,MAChC,QAAQ,MAAM;AAAA,MACd,WAAW,MAAM;AAAA,MACjB,aAAa,MAAM;AAAA,IACrB;AAAA,EACF;AACF;;;ACzDA,IAAAC,gBAAkB;AAGX,IAAM,kBAAN,MAA+C;AAAA,EAGpD,YAAY,QAAgB;AAC1B,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,iBAAiB,SAA6C;AAClE,UAAM,SAAc;AAAA,MAClB,KAAK,KAAK;AAAA,MACV,GAAG,QAAQ,SAAS;AAAA,MACpB,UAAU;AAAA;AAAA,IACZ;AAEA,QAAI,QAAQ,aAAa;AACvB,aAAO,cACL,QAAQ,gBAAgB,aAAa,aAAa;AAAA,IACtD;AAEA,UAAM,WAAW,MAAM,cAAAC,QAAM,IAAI,4BAA4B;AAAA,MAC3D;AAAA,IACF,CAAC;AAED,UAAM,OAAO,SAAS,KAAK;AAE3B,QAAI,CAAC,QAAQ,KAAK,WAAW,GAAG;AAC9B,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AAGA,UAAM,YAAY,KAAK,KAAK,MAAM,KAAK,OAAO,IAAI,KAAK,MAAM,CAAC;AAE9D,WAAO;AAAA,MACL,KAAK,UAAU,iBAAiB,UAAU;AAAA,MAC1C,OAAO,UAAU,cAAc,UAAU;AAAA,MACzC,QAAQ,UAAU,eAAe,UAAU;AAAA,MAC3C,QAAQ,UAAU;AAAA,MAClB,WAAW,6BAA6B,UAAU,IAAI,IAAI,UAAU,OAAO;AAAA,MAC3E,aAAa,UAAU;AAAA,IACzB;AAAA,EACF;AACF;;;AC3CA,SAAoB;AACpB,WAAsB;AACtB,IAAAC,gBAAkB;AAClB,kBAA6B;AAEtB,IAAM,cAAN,MAAkB;AAAA,EAGvB,YAAY,UAAyB;AACnC,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UAAU,UAAwB,CAAC,GAAyB;AAChE,WAAO,KAAK,SAAS,iBAAiB,OAAO;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,SACJ,UACA,iBACA,UAA2B,CAAC,GACX;AACjB,UAAM,MAAM,OAAO,aAAa,WAAW,WAAW,SAAS;AAG/D,QAAI,CAAI,cAAW,eAAe,GAAG;AACnC,MAAG,aAAU,iBAAiB,EAAE,WAAW,KAAK,CAAC;AAAA,IACnD;AAGA,QAAI;AACJ,QAAI,QAAQ,UAAU;AACpB,sBAAgB,QAAQ;AAAA,IAC1B,WAAW,QAAQ,kBAAkB;AAEnC,YAAM,UAAU,IAAI,IAAI,GAAG,EAAE;AAC7B,YAAM,cAAmB,cAAS,OAAO;AACzC,UAAI,eAAe,YAAY,SAAS,KAAK,gBAAgB,KAAK;AAChE,wBAAgB;AAAA,MAClB,OAAO;AAEL,cAAM,MAAM,KAAK,oBAAoB,GAAG,KAAK;AAC7C,wBAAgB,OAAG,YAAAC,IAAO,CAAC,GAAG,GAAG;AAAA,MACnC;AAAA,IACF,OAAO;AAEL,YAAM,MAAM,KAAK,oBAAoB,GAAG,KAAK;AAC7C,sBAAgB,OAAG,YAAAA,IAAO,CAAC,GAAG,GAAG;AAAA,IACnC;AAGA,QAAI,CAAM,aAAQ,aAAa,GAAG;AAChC,uBAAiB;AAAA,IACnB;AAEA,UAAM,WAAgB,UAAK,iBAAiB,aAAa;AAGzD,QAAO,cAAW,QAAQ,KAAK,CAAC,QAAQ,WAAW;AACjD,YAAM,IAAI,MAAM,wBAAwB,QAAQ,sCAAsC;AAAA,IACxF;AAEA,QAAI;AACF,YAAM,WAAW,MAAM,cAAAC,QAAM,IAAI,KAAK;AAAA,QACpC,cAAc;AAAA,QACd,cAAc;AAAA;AAAA,MAChB,CAAC;AAED,YAAM,SAAY,qBAAkB,QAAQ;AAE5C,eAAS,KAAK,KAAK,MAAM;AAEzB,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,eAAO,GAAG,UAAU,MAAM;AACxB,kBAAQ,QAAQ;AAAA,QAClB,CAAC;AAED,eAAO,GAAG,SAAS,CAAC,QAAQ;AAC1B,UAAG,UAAO,UAAU,MAAM;AAAA,UAAE,CAAC;AAC7B,iBAAO,GAAG;AAAA,QACZ,CAAC;AAED,iBAAS,KAAK,GAAG,SAAS,CAAC,QAAe;AACxC,iBAAO,MAAM;AACb,UAAG,UAAO,UAAU,MAAM;AAAA,UAAE,CAAC;AAC7B,iBAAO,GAAG;AAAA,QACZ,CAAC;AAAA,MACH,CAAC;AAAA,IACH,SAAS,OAAO;AACd,UAAI,cAAAA,QAAM,aAAa,KAAK,GAAG;AAC7B,cAAM,IAAI,MAAM,6BAA6B,MAAM,OAAO,EAAE;AAAA,MAC9D;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,oBAAoB,KAA4B;AACtD,QAAI;AACF,YAAM,UAAU,IAAI,IAAI,GAAG,EAAE;AAC7B,YAAM,MAAW,aAAQ,OAAO;AAChC,aAAO,OAAO;AAAA,IAChB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;","names":["axios","import_axios","axios","import_axios","axios","import_axios","uuidv4","axios"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
// src/providers/unsplash.ts
|
|
2
|
+
import axios from "axios";
|
|
2
3
|
var UnsplashProvider = class {
|
|
3
4
|
constructor(accessKey) {
|
|
4
5
|
this.accessKey = accessKey;
|
|
5
6
|
}
|
|
6
7
|
async fetchRandomImage(options) {
|
|
7
|
-
const
|
|
8
|
-
if (options.query) params.append("query", options.query);
|
|
9
|
-
const url = `https://api.unsplash.com/photos/random?${params.toString()}`;
|
|
10
|
-
const response = await fetch(url, {
|
|
8
|
+
const response = await axios.get("https://api.unsplash.com/photos/random", {
|
|
11
9
|
headers: {
|
|
12
10
|
Authorization: `Client-ID ${this.accessKey}`
|
|
11
|
+
},
|
|
12
|
+
params: {
|
|
13
|
+
query: options.query,
|
|
14
|
+
orientation: options.orientation
|
|
13
15
|
}
|
|
14
16
|
});
|
|
15
|
-
|
|
16
|
-
throw new Error(`Unsplash API error: ${response.statusText}`);
|
|
17
|
-
}
|
|
18
|
-
const data = await response.json();
|
|
17
|
+
const data = response.data;
|
|
19
18
|
const photo = Array.isArray(data) ? data[0] : data;
|
|
20
19
|
const baseUrl = photo.urls.raw;
|
|
21
20
|
const sizeParams = new URLSearchParams();
|
|
21
|
+
if (options.height || options.width) {
|
|
22
|
+
sizeParams.append("fit", "crop");
|
|
23
|
+
sizeParams.append("crop", "entropy");
|
|
24
|
+
}
|
|
22
25
|
if (options.width) sizeParams.append("w", options.width.toString());
|
|
23
26
|
if (options.height) sizeParams.append("h", options.height.toString());
|
|
24
27
|
if (options.quality) sizeParams.append("q", options.quality.toString());
|
|
@@ -36,6 +39,7 @@ var UnsplashProvider = class {
|
|
|
36
39
|
};
|
|
37
40
|
|
|
38
41
|
// src/providers/pexels.ts
|
|
42
|
+
import axios2 from "axios";
|
|
39
43
|
var PexelsProvider = class {
|
|
40
44
|
constructor(apiKey) {
|
|
41
45
|
this.apiKey = apiKey;
|
|
@@ -43,19 +47,18 @@ var PexelsProvider = class {
|
|
|
43
47
|
async fetchRandomImage(options) {
|
|
44
48
|
const endpoint = options.query ? "https://api.pexels.com/v1/search" : "https://api.pexels.com/v1/curated";
|
|
45
49
|
const randomPage = Math.floor(Math.random() * 100) + 1;
|
|
46
|
-
const params =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
const params = {
|
|
51
|
+
per_page: 1,
|
|
52
|
+
page: randomPage
|
|
53
|
+
};
|
|
54
|
+
if (options.query) params.query = options.query;
|
|
55
|
+
const response = await axios2.get(endpoint, {
|
|
51
56
|
headers: {
|
|
52
57
|
Authorization: this.apiKey
|
|
53
|
-
}
|
|
58
|
+
},
|
|
59
|
+
params
|
|
54
60
|
});
|
|
55
|
-
|
|
56
|
-
throw new Error(`Pexels API error: ${response.statusText}`);
|
|
57
|
-
}
|
|
58
|
-
const data = await response.json();
|
|
61
|
+
const data = response.data;
|
|
59
62
|
if (!data.photos || data.photos.length === 0) {
|
|
60
63
|
throw new Error("No images found on Pexels");
|
|
61
64
|
}
|
|
@@ -78,7 +81,46 @@ var PexelsProvider = class {
|
|
|
78
81
|
}
|
|
79
82
|
};
|
|
80
83
|
|
|
84
|
+
// src/providers/pixabay.ts
|
|
85
|
+
import axios3 from "axios";
|
|
86
|
+
var PixabayProvider = class {
|
|
87
|
+
constructor(apiKey) {
|
|
88
|
+
this.apiKey = apiKey;
|
|
89
|
+
}
|
|
90
|
+
async fetchRandomImage(options) {
|
|
91
|
+
const params = {
|
|
92
|
+
key: this.apiKey,
|
|
93
|
+
q: options.query || "",
|
|
94
|
+
per_page: 20
|
|
95
|
+
// Fetch a few to pick randomly
|
|
96
|
+
};
|
|
97
|
+
if (options.orientation) {
|
|
98
|
+
params.orientation = options.orientation === "portrait" ? "vertical" : "horizontal";
|
|
99
|
+
}
|
|
100
|
+
const response = await axios3.get("https://pixabay.com/api/", {
|
|
101
|
+
params
|
|
102
|
+
});
|
|
103
|
+
const hits = response.data.hits;
|
|
104
|
+
if (!hits || hits.length === 0) {
|
|
105
|
+
throw new Error("No images found");
|
|
106
|
+
}
|
|
107
|
+
const randomHit = hits[Math.floor(Math.random() * hits.length)];
|
|
108
|
+
return {
|
|
109
|
+
url: randomHit.largeImageURL || randomHit.webformatURL,
|
|
110
|
+
width: randomHit.imageWidth || randomHit.webformatWidth,
|
|
111
|
+
height: randomHit.imageHeight || randomHit.webformatHeight,
|
|
112
|
+
author: randomHit.user,
|
|
113
|
+
authorUrl: `https://pixabay.com/users/${randomHit.user}-${randomHit.user_id}/`,
|
|
114
|
+
originalUrl: randomHit.pageURL
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
81
119
|
// src/image-fetcher.ts
|
|
120
|
+
import * as fs from "fs";
|
|
121
|
+
import * as path from "path";
|
|
122
|
+
import axios4 from "axios";
|
|
123
|
+
import { v7 as uuidv4 } from "uuid";
|
|
82
124
|
var RandomImage = class {
|
|
83
125
|
constructor(provider) {
|
|
84
126
|
this.provider = provider;
|
|
@@ -91,9 +133,90 @@ var RandomImage = class {
|
|
|
91
133
|
async getRandom(options = {}) {
|
|
92
134
|
return this.provider.fetchRandomImage(options);
|
|
93
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Downloads an image to a specified directory.
|
|
138
|
+
* @param imageUrl - The URL of the image to download (can be a string or ImageResult object)
|
|
139
|
+
* @param destinationPath - The directory path where the image will be saved
|
|
140
|
+
* @param options - Download options (filename, overwrite, etc.)
|
|
141
|
+
* @returns A promise that resolves to the full path of the downloaded file
|
|
142
|
+
*/
|
|
143
|
+
async download(imageUrl, destinationPath, options = {}) {
|
|
144
|
+
const url = typeof imageUrl === "string" ? imageUrl : imageUrl.url;
|
|
145
|
+
if (!fs.existsSync(destinationPath)) {
|
|
146
|
+
fs.mkdirSync(destinationPath, { recursive: true });
|
|
147
|
+
}
|
|
148
|
+
let finalFilename;
|
|
149
|
+
if (options.filename) {
|
|
150
|
+
finalFilename = options.filename;
|
|
151
|
+
} else if (options.keepOriginalName) {
|
|
152
|
+
const urlPath = new URL(url).pathname;
|
|
153
|
+
const urlFilename = path.basename(urlPath);
|
|
154
|
+
if (urlFilename && urlFilename.length > 0 && urlFilename !== "/") {
|
|
155
|
+
finalFilename = urlFilename;
|
|
156
|
+
} else {
|
|
157
|
+
const ext = this.getExtensionFromUrl(url) || ".jpg";
|
|
158
|
+
finalFilename = `${uuidv4()}${ext}`;
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
const ext = this.getExtensionFromUrl(url) || ".jpg";
|
|
162
|
+
finalFilename = `${uuidv4()}${ext}`;
|
|
163
|
+
}
|
|
164
|
+
if (!path.extname(finalFilename)) {
|
|
165
|
+
finalFilename += ".jpg";
|
|
166
|
+
}
|
|
167
|
+
const fullPath = path.join(destinationPath, finalFilename);
|
|
168
|
+
if (fs.existsSync(fullPath) && !options.overwrite) {
|
|
169
|
+
throw new Error(`File already exists: ${fullPath}. Set overwrite: true to replace it.`);
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
const response = await axios4.get(url, {
|
|
173
|
+
responseType: "stream",
|
|
174
|
+
maxRedirects: 5
|
|
175
|
+
// Automatically handle redirects
|
|
176
|
+
});
|
|
177
|
+
const writer = fs.createWriteStream(fullPath);
|
|
178
|
+
response.data.pipe(writer);
|
|
179
|
+
return new Promise((resolve, reject) => {
|
|
180
|
+
writer.on("finish", () => {
|
|
181
|
+
resolve(fullPath);
|
|
182
|
+
});
|
|
183
|
+
writer.on("error", (err) => {
|
|
184
|
+
fs.unlink(fullPath, () => {
|
|
185
|
+
});
|
|
186
|
+
reject(err);
|
|
187
|
+
});
|
|
188
|
+
response.data.on("error", (err) => {
|
|
189
|
+
writer.close();
|
|
190
|
+
fs.unlink(fullPath, () => {
|
|
191
|
+
});
|
|
192
|
+
reject(err);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (axios4.isAxiosError(error)) {
|
|
197
|
+
throw new Error(`Failed to download image: ${error.message}`);
|
|
198
|
+
}
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Helper method to extract file extension from URL
|
|
204
|
+
* @param url - The URL to extract extension from
|
|
205
|
+
* @returns The file extension (e.g., '.jpg', '.png') or null
|
|
206
|
+
*/
|
|
207
|
+
getExtensionFromUrl(url) {
|
|
208
|
+
try {
|
|
209
|
+
const urlPath = new URL(url).pathname;
|
|
210
|
+
const ext = path.extname(urlPath);
|
|
211
|
+
return ext || null;
|
|
212
|
+
} catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
94
216
|
};
|
|
95
217
|
export {
|
|
96
218
|
PexelsProvider,
|
|
219
|
+
PixabayProvider,
|
|
97
220
|
RandomImage,
|
|
98
221
|
UnsplashProvider
|
|
99
222
|
};
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/unsplash.ts","../src/providers/pexels.ts","../src/image-fetcher.ts"],"sourcesContent":["import { 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 params = new URLSearchParams();\n if (options.query) params.append('query', options.query);\n // Unsplash doesn't strictly support width/height for random photo selection in the same way for resizing via API query params on the /random endpoint directly for *fetching* the image content, \n // but we can request specific dimensions roughly or rely on the returned structure to pick a size.\n // However, the /photos/random endpoint returns a JSON with urls.raw, urls.full, etc.\n // We can append parameters to the returned URL for resizing.\n \n // Using fetching logic:\n const url = `https://api.unsplash.com/photos/random?${params.toString()}`;\n \n const response = await fetch(url, {\n headers: {\n Authorization: `Client-ID ${this.accessKey}`\n }\n });\n\n if (!response.ok) {\n throw new Error(`Unsplash API error: ${response.statusText}`);\n }\n\n const data = await response.json();\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.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 { 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 // Pexels 'curated' or 'search'. \n // If query is present, use search. If not, use curated.\n // We need to randomize the result because Pexels search/curated returns a list.\n // We can use 'page' and 'per_page=1' with a random page number to simulate random? \n // Or just fetch one page and pick random?\n // Pexels API doesn't have a direct /random endpoint like Unsplash.\n // Efficient strategy: Search with 1 result per page, but randomize the page number.\n // Max page is tricky, maybe limit to top 100 results?\n \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 = new URLSearchParams();\n if (options.query) params.append('query', options.query);\n params.append('per_page', '1');\n params.append('page', randomPage.toString());\n\n const response = await fetch(`${endpoint}?${params.toString()}`, {\n headers: {\n Authorization: this.apiKey\n }\n });\n\n if (!response.ok) {\n throw new Error(`Pexels API error: ${response.statusText}`);\n }\n\n const data = await response.json();\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 // Pexels supports resizing via query params on the image url?\n // Pexels returns src object with original, large2x, large, medium, small, portrait, landscape, tiny.\n // Or we can modify the 'original' url.\n // Pexels docs say: https://images.pexels.com/photos/2014422/pexels-photo-2014422.jpeg?auto=compress&cs=tinysrgb&h=350\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 // Note: Quality param is not standard in Pexels URL manipulation documented publicly as 'q', \n // but 'auto=compress' handles it. We can try adding it if needed or ignore. \n // I'll skip explicit q param mapping for now as it's not strictly 'quality=X'.\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 { 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":[]}
|
|
1
|
+
{"version":3,"sources":["../src/providers/unsplash.ts","../src/providers/pexels.ts","../src/providers/pixabay.ts","../src/image-fetcher.ts"],"sourcesContent":["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","axios","axios"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nghiavuive/random-image",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -31,8 +31,14 @@
|
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^25.2.0",
|
|
34
|
+
"@types/uuid": "^10.0.0",
|
|
35
|
+
"dotenv": "^17.2.3",
|
|
34
36
|
"tsup": "^8.5.1",
|
|
35
37
|
"typescript": "^5.9.3",
|
|
36
38
|
"vitest": "^4.0.18"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"axios": "^1.13.4",
|
|
42
|
+
"uuid": "^13.0.0"
|
|
37
43
|
}
|
|
38
44
|
}
|