11import PQueue from 'p-queue' ;
22import { setMaxListeners } from 'node:events' ;
3- import { CancelError } from 'got' ;
43import { DEFAULT_INTERVAL , DEFAULT_STEP } from './constanta.js' ;
54import {
65 DownloadOptions ,
@@ -43,19 +42,13 @@ export type Image = {
4342
4443export type Options = ( ImageOptions & DownloadOptions ) & {
4544 /**
46- * Do something when the image is successfully downloaded.
47- * For example, counting the number of successful downloads.
48- *
49- * Only called when downloading multiple images.
45+ * Do something when an image is successfully downloaded.
5046 *
5147 * @param image The downloaded image.
5248 */
5349 onSuccess ?: ( image : Image ) => void ;
5450 /**
55- * Do something when the image download failed.
56- * For example, counting the number of failed downloads.
57- *
58- * Only called when downloading multiple images.
51+ * Do something when an image fails to download.
5952 *
6053 * @param error The error that caused the download to fail.
6154 * @param url The URL of the image that failed to download.
@@ -79,98 +72,65 @@ export type Options = (ImageOptions & DownloadOptions) & {
7972 signal ?: AbortSignal ;
8073} ;
8174
82- async function imgdl ( url : string , options ?: Options ) : Promise < Image > ;
83- async function imgdl ( url : string [ ] , options ?: Options ) : Promise < Image [ ] > ;
84- async function imgdl (
85- url : string | string [ ] ,
86- options ?: Options ,
87- ) : Promise < Image | Image [ ] > ;
88- async function imgdl (
89- url : string | string [ ] ,
90- options ?: Options ,
91- ) : Promise < Image | Image [ ] > {
92- if ( Array . isArray ( url ) ) {
93- const queue = new PQueue ( {
94- concurrency : options ?. step ?? DEFAULT_STEP ,
95- interval : options ?. interval ?? DEFAULT_INTERVAL ,
96- intervalCap : options ?. step ?? DEFAULT_STEP ,
97- } ) ;
75+ async function imgdl ( url : string | string [ ] , options ?: Options ) : Promise < void > {
76+ const {
77+ directory,
78+ name,
79+ extension,
80+ onSuccess,
81+ onError,
82+ step,
83+ interval,
84+ ...downloadOptions
85+ } = options ?? { } ;
9886
99- // Set max listeners to infinity to prevent memory leak warning
100- if ( options ?. signal ) {
101- setMaxListeners ( Infinity , options . signal ) ;
102- }
87+ const urls = Array . isArray ( url ) ? url : [ url ] ;
88+ const queue = new PQueue ( {
89+ concurrency : step ?? DEFAULT_STEP ,
90+ interval : interval ?? DEFAULT_INTERVAL ,
91+ intervalCap : step ?? DEFAULT_STEP ,
92+ } ) ;
10393
104- return new Promise < Image [ ] > ( ( resolve , reject ) => {
105- const images : Image [ ] = [ ] ;
106- const countNames = new Map < string , number > ( ) ;
94+ // Set max listeners to infinity to prevent memory leak warning
95+ if ( downloadOptions ?. signal ) {
96+ setMaxListeners ( Infinity , downloadOptions . signal ) ;
97+ }
10798
108- url . forEach ( ( _url ) => {
109- const img = parseImageParams ( _url , options ) ;
99+ const countNames = new Map < string , number > ( ) ;
110100
111- // Make sure the name is unique
112- const nameKey = `${ img . name } .${ img . extension } ` ;
113- const count = countNames . get ( nameKey ) ;
114- if ( count ) {
115- img . name = `${ img . name } (${ count } )` ;
116- img . path = path . resolve (
117- img . directory ,
118- `${ img . name } .${ img . extension } ` ,
119- ) ;
120- }
121- countNames . set ( nameKey , ( count || 0 ) + 1 ) ;
101+ for ( const _url of urls ) {
102+ const img = parseImageParams ( _url , { directory, name, extension } ) ;
122103
123- // Add the download task to queue
124- queue
125- . add (
126- async ( { signal } ) => {
127- try {
128- return await download ( img , {
129- ...options ,
130- signal,
131- } ) ;
132- } catch ( error ) {
133- options ?. onError ?.(
134- error instanceof Error
135- ? error
136- : new Error ( 'Unknown error' , { cause : error } ) ,
137- _url ,
138- ) ;
139- return undefined ;
140- }
141- } ,
142- { signal : options ?. signal } ,
143- )
144- . then ( ( image ) => {
145- if ( image ) {
146- options ?. onSuccess ?.( image ) ;
147- images . push ( image ) ;
148- }
149- } )
150- . catch ( ( error ) => {
151- if ( ! ( error instanceof CancelError ) ) {
152- reject ( error ) ;
153- }
154- } ) ;
155- } ) ;
104+ // Make sure the name is unique
105+ const nameKey = `${ img . name } .${ img . extension } ` ;
106+ const count = countNames . get ( nameKey ) ;
107+ if ( count ) {
108+ img . name = `${ img . name } (${ count } )` ;
109+ img . path = path . resolve ( img . directory , `${ img . name } .${ img . extension } ` ) ;
110+ }
111+ countNames . set ( nameKey , ( count || 0 ) + 1 ) ;
112+
113+ // Add the download task to queue
114+ try {
115+ const image = await queue . add (
116+ ( { signal } ) => download ( img , { ...downloadOptions , signal } ) ,
117+ { signal : downloadOptions ?. signal } ,
118+ ) ;
156119
157- // Resolve/reject when all task is finished
158- queue
159- . onIdle ( )
160- . then ( ( ) => {
161- resolve ( images ) ;
162- } )
163- . catch ( ( error ) => {
164- reject ( error ) ;
165- } ) ;
166- } ) ;
120+ if ( image ) {
121+ onSuccess ?.( image ) ;
122+ }
123+ } catch ( error ) {
124+ onError ?.(
125+ error instanceof Error
126+ ? error
127+ : new Error ( 'Unknown error' , { cause : error } ) ,
128+ _url ,
129+ ) ;
130+ }
167131 }
168132
169- // TODO: implement `onSuccess` and `onError` for single download
170- return download ( parseImageParams ( url , options ) , {
171- ...options ,
172- signal : options ?. signal ,
173- } ) ;
133+ await queue . onIdle ( ) ;
174134}
175135
176136export default imgdl ;
0 commit comments