Comparing version 0.5.1 to 0.6.1
#!/usr/bin/env node | ||
import{a as o,b,c as w}from"./chunk-TDJJNDZB.js";import x from"cli-progress";import y from"meow";import $ from"fs";import v from"path";import d from"chalk";var m=y(` | ||
import{a as o,b,c as w}from"./chunk-FMOY4ZR7.js";import x from"cli-progress";import y from"meow";import $ from"fs";import v from"path";import d from"chalk";var m=y(` | ||
USAGE | ||
@@ -34,3 +34,3 @@ $ imgdl <url> ... [OPTIONS] | ||
$ imgdl https://example.com/image.jpg --header="User-Agent: Mozilla/5.0" --header="Cookie: foo=bar" | ||
`,{importMeta:import.meta,booleanDefault:void 0,flags:{dir:{shortFlag:"d",type:"string"},ext:{shortFlag:"e",type:"string"},header:{shortFlag:"H",type:"string",isMultiple:!0},increment:{shortFlag:"i",type:"boolean"},interval:{type:"number"},start:{type:"number"},end:{type:"number"},name:{shortFlag:"n",type:"string"},maxRetry:{type:"number"},silent:{type:"boolean"},step:{type:"number"},timeout:{shortFlag:"t",type:"number"},version:{shortFlag:"v",type:"boolean"}}}),S=d.bold.green,c=d.bold.red,T=d.yellow,s=d.dim;async function D(){let t=m.input,{flags:e}=m;if(e.version&&m.showVersion(),t.length||m.showHelp(),e.increment){if(t.length>1)throw new o("Only one URL is allowed in increment mode");let n=t[0];if(!n.includes("{i}"))throw new o("The URL must contain {i} placeholder for the index");if(!e.end)throw new o("The end index is required in increment mode");if(e.start&&e.start>e.end)throw new o("The start index cannot be greater than the end index");let{start:i=0,end:l}=e;t=[];for(let g=i;g<=l;g+=1)t.push(n.replace("{i}",g.toString()))}e.silent||console.log(` | ||
`,{importMeta:import.meta,booleanDefault:void 0,flags:{dir:{shortFlag:"d",type:"string"},ext:{shortFlag:"e",type:"string"},header:{shortFlag:"H",type:"string",isMultiple:!0},increment:{shortFlag:"i",type:"boolean"},interval:{type:"number"},start:{type:"number"},end:{type:"number"},name:{shortFlag:"n",type:"string"},maxRetry:{type:"number"},silent:{type:"boolean"},step:{type:"number"},timeout:{shortFlag:"t",type:"number"},version:{shortFlag:"v",type:"boolean"}}}),S=d.bold.green,c=d.bold.red,T=d.yellow,s=d.dim;async function D(){let t=m.input,{flags:e}=m;if(e.version&&m.showVersion(),t.length||m.showHelp(0),e.increment){if(t.length>1)throw new o("Only one URL is allowed in increment mode");let n=t[0];if(!n.includes("{i}"))throw new o("The URL must contain {i} placeholder for the index");if(!e.end)throw new o("The end index is required in increment mode");if(e.start&&e.start>e.end)throw new o("The start index cannot be greater than the end index");let{start:i=0,end:l}=e;t=[];for(let g=i;g<=l;g+=1)t.push(n.replace("{i}",g.toString()))}e.silent||console.log(` | ||
${s("Downloading...")} | ||
@@ -37,0 +37,0 @@ ${T("Press Ctrl+C to abort")}`);let h=s("|"),a=new x.SingleBar({format:`{percentage}% [{bar}] {value}/{total} ${h} ${S("\u2705 {success}")} ${h} ${c("\u274C {errorCount}")} ${h} ETA: {eta_formatted} ${s("/ {duration_formatted}")}`,hideCursor:null,barsize:24}),p=0,r=0;!e.silent&&t.length>1&&a.start(t.length,0,{success:p,errorCount:r});let u={};e.header&&e.header.forEach(n=>{let[i,l]=n.split(":");if(!i||!l)throw new o("Invalid header format");u[i.trim()]=l.trim()});let f=new AbortController;process.on("SIGINT",()=>{a.stop(),console.log(s(` |
@@ -1,2 +0,2 @@ | ||
type DownloadOptions = { | ||
type ImageOptions = { | ||
/** | ||
@@ -9,23 +9,11 @@ * The directory to save the image to. | ||
/** | ||
* The headers to send with the request. | ||
*/ | ||
headers?: Record<string, string | string[] | undefined>; | ||
/** | ||
* The name of the image file. | ||
* | ||
* You also can provide a function that returns the name. | ||
* The function will be called with the original name, if it exists in the URL. | ||
* If not provided, the default value will be the **original name** if it exists in the URL. | ||
* | ||
* The default value will be used if this value (or the function) returns an empty string. | ||
* | ||
* The default value will be the **original name** if it exists in the URL. | ||
* Otherwise, it will be **'image'**. | ||
*/ | ||
name?: string | ((original?: string) => string); | ||
/** | ||
* Set the maximum number of times to retry the request if it fails. | ||
* | ||
* @default 2 | ||
* If a name with same extension already exists, ` (1)`, ` (2)`, etc. will be added to the end of the name. | ||
*/ | ||
maxRetry?: number; | ||
name?: string; | ||
/** | ||
@@ -39,3 +27,15 @@ * The extension of the image. | ||
extension?: string; | ||
}; | ||
type DownloadOptions = { | ||
/** | ||
* The headers to send with the request. | ||
*/ | ||
headers?: Record<string, string | string[] | undefined>; | ||
/** | ||
* Set the maximum number of times to retry the request if it fails. | ||
* | ||
* @default 2 | ||
*/ | ||
maxRetry?: number; | ||
/** | ||
* Set timeout for each request in milliseconds. | ||
@@ -54,3 +54,3 @@ */ | ||
*/ | ||
url: string; | ||
url: URL; | ||
/** | ||
@@ -81,24 +81,6 @@ * The name of the image file, without the extension. | ||
}; | ||
type Options = Omit<DownloadOptions, 'name'> & { | ||
type Options = (ImageOptions & DownloadOptions) & { | ||
/** | ||
* The name of the image file. | ||
* Do something when an image is successfully downloaded. | ||
* | ||
* You also can provide a function that returns the name. | ||
* The function will be called with the original name, if it exists in the URL. | ||
* | ||
* The default value will be used if this value (or the function) returns an empty string. | ||
* | ||
* The default value will be the **original name** if it exists in the URL. | ||
* Otherwise, it will be **'image'**. | ||
* | ||
* When downloading multiple images, `-index` will be appended to the end of the name (suffix). | ||
* `index` will start from 1. | ||
*/ | ||
name?: string; | ||
/** | ||
* Do something when the image is successfully downloaded. | ||
* For example, counting the number of successful downloads. | ||
* | ||
* Only called when downloading multiple images. | ||
* | ||
* @param image The downloaded image. | ||
@@ -108,7 +90,4 @@ */ | ||
/** | ||
* Do something when the image download failed. | ||
* For example, counting the number of failed downloads. | ||
* Do something when an image fails to download. | ||
* | ||
* Only called when downloading multiple images. | ||
* | ||
* @param error The error that caused the download to fail. | ||
@@ -135,6 +114,4 @@ * @param url The URL of the image that failed to download. | ||
}; | ||
declare function imgdl(url: string, options?: Options): Promise<Image>; | ||
declare function imgdl(url: string[], options?: Options): Promise<Image[]>; | ||
declare function imgdl(url: string | string[], options?: Options): Promise<Image | Image[]>; | ||
declare function imgdl(url: string | string[], options?: Options): Promise<void>; | ||
export { type Image, type Options, imgdl as default }; |
@@ -1,2 +0,2 @@ | ||
import{c as a}from"./chunk-TDJJNDZB.js";export{a as default}; | ||
import{c as a}from"./chunk-FMOY4ZR7.js";export{a as default}; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "img-dl", | ||
"version": "0.5.1", | ||
"version": "0.6.1", | ||
"description": "Download image(s), by command or programmatically", | ||
@@ -40,3 +40,3 @@ "type": "module", | ||
"engines": { | ||
"node": ">=18" | ||
"node": ">=20.9" | ||
}, | ||
@@ -47,11 +47,12 @@ "devDependencies": { | ||
"@types/node": "^20.12.8", | ||
"@vitest/coverage-v8": "^1.6.0", | ||
"@vitest/coverage-v8": "^2.0.2", | ||
"eslint": "^8.57.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"execa": "^8.0.1", | ||
"prettier": "^3.2.5", | ||
"tsup": "^8.0.2", | ||
"typescript": "^5.4.5", | ||
"execa": "^9.3.0", | ||
"msw": "^2.3.1", | ||
"prettier": "^3.3.2", | ||
"tsup": "^8.1.0", | ||
"typescript": "^5.5.2", | ||
"typescript-eslint": "^7.8.0", | ||
"vitest": "^1.6.0" | ||
"vitest": "^2.0.2" | ||
}, | ||
@@ -61,3 +62,3 @@ "dependencies": { | ||
"cli-progress": "^3.12.0", | ||
"got": "^13.0.0", | ||
"got": "^14.4.1", | ||
"meow": "^13.2.0", | ||
@@ -64,0 +65,0 @@ "p-queue": "^8.0.1", |
111
README.md
# img-dl | ||
Downloade image(s), by command or programmatically. The alternative for `image-downloader` package (see the [comparison](#comparison)). | ||
Downloade image(s), by command or programmatically. The alternative for `image-downloader` package (see the [features](#features)). | ||
@@ -10,6 +10,35 @@ [![MIT license](https://img.shields.io/github/license/fityannugroho/img-dl.svg)](https://github.com/fityannugroho/img-dl/blob/main/LICENSE) | ||
## Features | ||
| Features | **img-dl** | [image-downloader][p1] | | ||
| --------------------------- | :--------: | :--------------------: | | ||
| Single download | ✅ | ✅ | | ||
| Bulk download | ✅ | ❌ | | ||
| CLI | ✅ | ❌ | | ||
| Custom filename | ✅ | ✅ | | ||
| Custom extension | ✅ | ❌ | | ||
| Request timeout | ✅ | ✅ | | ||
| Retry failed request | ✅ | ❌ | | ||
| Abort request | ✅ | ❌ | | ||
| **Increment mode (in CLI)** | ✅ | ❌ | | ||
| **Overwrite prevention** | ✅ | ❌ | | ||
### Increment mode | ||
Download images with an url that contains `{i}` placeholder for the index, and specify the start and end index. | ||
### Overwrite prevention | ||
To prevent overwriting, ` (n)` will be appended to the name of the new file if the file with the same name already exists. | ||
The number will be incremented until the file name is unique in the directory, starting from 1 (e.g. `image (1).jpg`, `image (2).jpg`, etc.). | ||
Image with different extension will be considered as **different** file, so it will not be appended with ` (n)`. For example, `image.jpg` and `image.png` will not be considered as the same file. | ||
> This feature will work for both single and bulk download. | ||
## Prerequisites | ||
- Node.js 18 or later | ||
- npm 9 or later | ||
- Node.js 20.9 or later | ||
- npm 10 or later | ||
@@ -47,3 +76,3 @@ ## Installation | ||
In increment mode, the URL must contain {i} placeholder for the index, | ||
only one URL is allowed, and the 'end' flag is required. | ||
only one URL is allowed, and the '--end' is required. | ||
@@ -58,3 +87,3 @@ OPTIONS | ||
--interval=<number> The interval between each batch of requests in milliseconds | ||
-n, --name=<filename> The filename. Default: original filename or timestamp | ||
-n, --name=<filename> The filename. If not specified, the original filename will be used. Default: 'image' | ||
--max-retry=<number> Set the maximum number of times to retry the request if it fails | ||
@@ -82,3 +111,3 @@ --silent Disable logging | ||
#### Download multiple images | ||
#### Bulk download | ||
@@ -89,3 +118,3 @@ ```bash | ||
#### Download multiple images with increment mode | ||
#### Bulk download with increment mode | ||
@@ -103,7 +132,18 @@ ```bash | ||
const image = await imgdl('https://example.com/image.jpg'); | ||
const image = await new Promise((resolve, reject) => { | ||
imgdl('https://example.com/image.jpg', { | ||
onSuccess: resolve, | ||
onError: reject, | ||
}); | ||
}); | ||
console.log(image); | ||
/* | ||
{ | ||
url: 'https://example.com/image.jpg', | ||
url: { | ||
href: 'https://example.com/image.jpg', | ||
origin: 'https://example.com', | ||
protocol: 'https:', | ||
pathname: '/image.jpg', | ||
// ... | ||
}, | ||
name: 'image', | ||
@@ -119,3 +159,3 @@ extension: 'jpg', | ||
#### Download multiple images | ||
#### Bulk download | ||
@@ -125,6 +165,19 @@ ```js | ||
const images = await imgdl([ | ||
const urls = [ | ||
'https://example.com/image.jpg', | ||
'https://example.com/image2.jpg', | ||
]); | ||
]; | ||
await imgdl(urls, { | ||
onSuccess: (image) => { | ||
// Do something with the downloaded image | ||
console.log(image); | ||
}, | ||
onError: (error, url) => { | ||
// Do something when the image fails to download | ||
console.error(`Failed to download ${url}: ${error.message}`); | ||
}, | ||
}); | ||
console.log('Download completed'); | ||
``` | ||
@@ -136,2 +189,4 @@ | ||
Returns: `Promise<void>` | ||
Download image(s) from the given URL(s). | ||
@@ -184,3 +239,3 @@ | ||
The filename. If not specified, the original filename will be used. If the original filename is not available, 'image' will be used. <br>When downloading multiple images, `-index` will be appended to the end of the name (suffix). `index` will start from 1. For example: 'image-1' | ||
The filename. If not specified, the original filename will be used. If the original filename is not available, 'image' will be used. | ||
@@ -199,4 +254,14 @@ ##### `maxRetry` | ||
The callback function to be called when the image is successfully downloaded. Only available when downloading multiple images. | ||
The callback function to be called when the image is successfully downloaded. | ||
`Image` is an object containing the information of the downloaded image. `Image` has the following properties: | ||
- `url`: The instance of `URL` class of the image. See [`URL` Class](https://nodejs.org/api/url.html#class-url). | ||
- `originalName`: The original name of the image if available. Default: `undefined`. | ||
- `originalExtension`: The original extension of the image if available. Default: `undefined`. | ||
- `name`: The user-defined name of the image. If not specified and the original name is available, the original name will be used. If the original name is not available, `'image'` will be used. | ||
- `extension`: The user-defined extension of the image. If not specified and the original extension is available, the original extension will be used. If the original extension is not available, `'jpg'` will be used. | ||
- `directory`: The output directory of the image. | ||
- `path`: The path of the downloaded image. | ||
##### `onError` | ||
@@ -207,3 +272,3 @@ | ||
The callback function to be called when the image fails to download. Only available when downloading multiple images. | ||
The callback function to be called when the image fails to download. | ||
@@ -231,16 +296,2 @@ ##### `signal` | ||
## Comparison | ||
| Features | **img-dl** | [image-downloader][p1] | | ||
| ------------------------ | :--------: | :--------------------: | | ||
| Download single image | ✅ | ✅ | | ||
| Download multiple images | ✅ | ❌ | | ||
| CLI | ✅ | ❌ | | ||
| Increment download | ✅ | ❌ | | ||
| Custom filename | ✅ | ✅ | | ||
| Custom extension | ✅ | ❌ | | ||
| Request timeout | ✅ | ✅ | | ||
| Retry failed request | ✅ | ❌ | | ||
| Abort request | ✅ | ❌ | | ||
<!-- Project links --> | ||
@@ -254,2 +305,2 @@ | ||
You can support this project by donating via [GitHub Sponsors](https://github.com/sponsors/fityannugroho), [Trakteer](https://trakteer.id/fityannugroho/tip), or [Saweria](https://saweria.co/fityannugroho). | ||
Also please consider supporting this project with a **donation**. Your donation will help us maintain and develop this project and provide you with better support. |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
49653
293
13
167
+ Added@sec-ant/readable-stream@0.4.1(transitive)
+ Added@sindresorhus/is@7.0.1(transitive)
+ Addedcacheable-request@12.0.1(transitive)
+ Addedform-data-encoder@4.0.2(transitive)
+ Addedget-stream@9.0.1(transitive)
+ Addedgot@14.4.4(transitive)
+ Addedis-stream@4.0.1(transitive)
+ Addedp-cancelable@4.0.1(transitive)
+ Addedtype-fest@4.27.0(transitive)
- Removed@sindresorhus/is@5.6.0(transitive)
- Removedcacheable-request@10.2.14(transitive)
- Removedform-data-encoder@2.1.4(transitive)
- Removedget-stream@6.0.1(transitive)
- Removedgot@13.0.0(transitive)
- Removedp-cancelable@3.0.0(transitive)
Updatedgot@^14.4.1