grab-url 0.9.139 โ†’ 0.9.141

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/grab-url-cli.js +61 -43
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "grab-url",
3
3
  "description": "๐Ÿ“ฅ Generate Request to API from Browser",
4
4
  "type": "module",
5
- "version": "0.9.139",
5
+ "version": "0.9.141",
6
6
  "author": "vtempest",
7
7
  "license": "rights.institute/prosper",
8
8
  "repository": {
@@ -141,7 +141,7 @@ ensureStateDirectoryExists() {
141
141
  fs.mkdirSync(this.stateDir, { recursive: true });
142
142
  }
143
143
  } catch (error) {
144
- console.log(this.colors.warning('โš ๏ธ Could not create state directory, using current directory'));
144
+ console.log(this.colors.warning('Could not create state directory, using current directory'));
145
145
  this.stateDir = process.cwd();
146
146
  }
147
147
  }
@@ -166,7 +166,7 @@ cleanupStateFile(stateFilePath) {
166
166
  fs.unlinkSync(stateFilePath);
167
167
  }
168
168
  } catch (error) {
169
- console.log(this.colors.warning('โš ๏ธ Could not clean up state file'));
169
+ console.log(this.colors.warning('Could not clean up state file'));
170
170
  }
171
171
  }
172
172
 
@@ -291,11 +291,17 @@ calculateBarSize(spinnerFrame, baseBarSize = 20) {
291
291
  */
292
292
  async checkServerSupport(url) {
293
293
  try {
294
+ // Create a timeout controller
295
+ const timeoutController = new AbortController();
296
+ const timeoutId = setTimeout(() => timeoutController.abort(), 10000); // 10 second timeout
297
+
294
298
  const response = await fetch(url, {
295
299
  method: 'HEAD',
296
- signal: this.abortController?.signal
300
+ signal: timeoutController.signal
297
301
  });
298
302
 
303
+ clearTimeout(timeoutId);
304
+
299
305
  if (!response.ok) {
300
306
  throw new Error(`HTTP error! status: ${response.status}`);
301
307
  }
@@ -313,7 +319,11 @@ async checkServerSupport(url) {
313
319
  headers: response.headers
314
320
  };
315
321
  } catch (error) {
316
- console.log(this.colors.warning('โš ๏ธ Could not check server resume support, proceeding with regular download'));
322
+ if (error.name === 'AbortError') {
323
+ console.log(this.colors.warning('Server check timed out, proceeding with regular download'));
324
+ } else {
325
+ console.log(this.colors.warning('Could not check server resume support, proceeding with regular download'));
326
+ }
317
327
  return {
318
328
  supportsResume: false,
319
329
  totalSize: 0,
@@ -336,7 +346,7 @@ loadDownloadState(stateFilePath) {
336
346
  return JSON.parse(stateData);
337
347
  }
338
348
  } catch (error) {
339
- console.log(this.colors.warning('โš ๏ธ Could not load download state, starting fresh'));
349
+ console.log(this.colors.warning('Could not load download state, starting fresh'));
340
350
  }
341
351
  return null;
342
352
  }
@@ -350,7 +360,7 @@ saveDownloadState(stateFilePath, state) {
350
360
  try {
351
361
  fs.writeFileSync(stateFilePath, JSON.stringify(state, null, 2));
352
362
  } catch (error) {
353
- console.log(this.colors.warning('โš ๏ธ Could not save download state'));
363
+ console.log(this.colors.warning('Could not save download state'));
354
364
  }
355
365
  }
356
366
 
@@ -366,7 +376,7 @@ getPartialFileSize(filePath) {
366
376
  return stats.size;
367
377
  }
368
378
  } catch (error) {
369
- console.log(this.colors.warning('โš ๏ธ Could not read partial file size'));
379
+ console.log(this.colors.warning('Could not read partial file size'));
370
380
  }
371
381
  return 0;
372
382
  }
@@ -960,12 +970,20 @@ async downloadSingleFileWithBar(fileBar, masterBar, totalFiles, totalTracking) {
960
970
  headers['Range'] = `bytes=${startByte}-`;
961
971
  }
962
972
 
963
- // Make the fetch request
973
+ // Make the fetch request with timeout
974
+ const timeoutController = new AbortController();
975
+ const timeoutId = setTimeout(() => timeoutController.abort(), 30000); // 30 second timeout
976
+
977
+ // Combine abort and timeout signals
978
+ const combinedSignal = abortController.signal;
979
+
964
980
  const response = await fetch(url, {
965
981
  headers,
966
- signal: abortController.signal
982
+ signal: combinedSignal
967
983
  });
968
984
 
985
+ clearTimeout(timeoutId);
986
+
969
987
  if (!response.ok) {
970
988
  throw new Error(`HTTP error! status: ${response.status}`);
971
989
  }
@@ -1135,7 +1153,7 @@ async downloadSingleFileWithBar(fileBar, masterBar, totalFiles, totalTracking) {
1135
1153
  });
1136
1154
 
1137
1155
  // Don't clean up partial file on error - allow resume
1138
- console.log(this.colors.info(`๐Ÿ’พ Partial download saved for ${filename}. Restart to resume.`));
1156
+ console.log(this.colors.info(`Partial download saved for ${filename}. Restart to resume.`));
1139
1157
  throw error;
1140
1158
  }
1141
1159
  }
@@ -1156,7 +1174,7 @@ async downloadFile(url, outputPath) {
1156
1174
  // Start with a random ora spinner animation
1157
1175
  const randomOraSpinner = this.getRandomOraSpinner();
1158
1176
  this.loadingSpinner = ora({
1159
- text: this.colors.primary('๐ŸŒ Checking server capabilities...'),
1177
+ text: this.colors.primary('Checking server capabilities...'),
1160
1178
  spinner: randomOraSpinner,
1161
1179
  color: 'cyan'
1162
1180
  }).start();
@@ -1183,10 +1201,10 @@ async downloadFile(url, outputPath) {
1183
1201
  if (fileUnchanged && partialSize < serverInfo.totalSize) {
1184
1202
  startByte = partialSize;
1185
1203
  resuming = true;
1186
- this.loadingSpinner.succeed(this.colors.success(`โœ… Found partial download: ${this.formatBytes(partialSize)} of ${this.formatTotal(serverInfo.totalSize)}`));
1187
- console.log(this.colors.info(`๐Ÿ”„ Resuming download from ${this.formatBytes(startByte)}`));
1204
+ this.loadingSpinner.succeed(this.colors.success(`Found partial download: ${this.formatBytes(partialSize)} of ${this.formatTotal(serverInfo.totalSize)}`));
1205
+ console.log(this.colors.info(`Resuming download from ${this.formatBytes(startByte)}`));
1188
1206
  } else {
1189
- this.loadingSpinner.warn(this.colors.warning('โš ๏ธ File changed on server, starting fresh download'));
1207
+ this.loadingSpinner.warn(this.colors.warning('File changed on server, starting fresh download'));
1190
1208
  // Clean up partial file and state
1191
1209
  if (fs.existsSync(tempFilePath)) {
1192
1210
  fs.unlinkSync(tempFilePath);
@@ -1196,7 +1214,7 @@ async downloadFile(url, outputPath) {
1196
1214
  } else {
1197
1215
  this.loadingSpinner.stop();
1198
1216
  if (partialSize > 0) {
1199
- console.log(this.colors.warning('โš ๏ธ Server does not support resumable downloads, starting fresh'));
1217
+ console.log(this.colors.warning('Server does not support resumable downloads, starting fresh'));
1200
1218
  // Clean up partial file
1201
1219
  if (fs.existsSync(tempFilePath)) {
1202
1220
  fs.unlinkSync(tempFilePath);
@@ -1210,12 +1228,17 @@ async downloadFile(url, outputPath) {
1210
1228
  headers['Range'] = `bytes=${startByte}-`;
1211
1229
  }
1212
1230
 
1213
- // Make the fetch request
1231
+ // Make the fetch request with timeout
1232
+ const timeoutController = new AbortController();
1233
+ const timeoutId = setTimeout(() => timeoutController.abort(), 30000); // 30 second timeout
1234
+
1214
1235
  const response = await fetch(url, {
1215
1236
  headers,
1216
1237
  signal: this.abortController.signal
1217
1238
  });
1218
1239
 
1240
+ clearTimeout(timeoutId);
1241
+
1219
1242
  if (!response.ok) {
1220
1243
  throw new Error(`HTTP error! status: ${response.status}`);
1221
1244
  }
@@ -1227,9 +1250,9 @@ async downloadFile(url, outputPath) {
1227
1250
 
1228
1251
  if (!resuming) {
1229
1252
  if (totalSize === 0) {
1230
- console.log(this.colors.warning('โš ๏ธ Warning: Content-Length not provided, progress will be estimated'));
1253
+ console.log(this.colors.warning('Warning: Content-Length not provided, progress will be estimated'));
1231
1254
  } else {
1232
- console.log(this.colors.info(`๐Ÿ“ฆ File size: ${this.formatTotal(totalSize)}`));
1255
+ console.log(this.colors.info(`File size: ${this.formatTotal(totalSize)}`));
1233
1256
  }
1234
1257
  }
1235
1258
 
@@ -1416,36 +1439,31 @@ async downloadFile(url, outputPath) {
1416
1439
  // Clean up state file
1417
1440
  this.cleanupStateFile(stateFilePath);
1418
1441
 
1419
- // Success celebration
1420
- console.log(this.colors.success('โœ… Download completed!'));
1421
- console.log(this.colors.primary('๐Ÿ“ File saved to: ') + chalk.underline(outputPath));
1422
- console.log(this.colors.purple('๐Ÿ“Š Total size: ') + this.formatBytes(downloaded));
1442
+ // Success message
1443
+ console.log(this.colors.success('Download completed!'));
1444
+ console.log(this.colors.primary('File saved to: ') + chalk.underline(outputPath));
1445
+ console.log(this.colors.purple('Total size: ') + this.formatBytes(downloaded));
1423
1446
 
1424
1447
  if (resuming) {
1425
- console.log(this.colors.info('๐Ÿ”„ Resumed from: ') + this.formatBytes(startByte));
1426
- console.log(this.colors.info('๐Ÿ“ฅ Downloaded this session: ') + this.formatBytes(sessionDownloaded));
1448
+ console.log(this.colors.info('Resumed from: ') + this.formatBytes(startByte));
1449
+ console.log(this.colors.info('Downloaded this session: ') + this.formatBytes(sessionDownloaded));
1427
1450
  }
1428
-
1429
- // Random success emoji
1430
- const celebrationEmojis = ['๐Ÿฅณ', '๐ŸŽŠ', '๐ŸŽˆ', '๐ŸŒŸ', '๐Ÿ’ฏ', '๐Ÿš€', 'โœจ', '๐Ÿ”ฅ'];
1431
- const randomEmoji = celebrationEmojis[Math.floor(Math.random() * celebrationEmojis.length)];
1432
- console.log(this.colors.success(`${randomEmoji} Successfully downloaded! ${randomEmoji}`));
1433
1451
 
1434
1452
  } catch (error) {
1435
1453
  if (this.loadingSpinner && this.loadingSpinner.isSpinning) {
1436
- this.loadingSpinner.fail(this.colors.error('โŒ Connection failed'));
1454
+ this.loadingSpinner.fail(this.colors.error('Connection failed'));
1437
1455
  }
1438
1456
  if (this.progressBar) {
1439
1457
  this.progressBar.stop();
1440
1458
  }
1441
1459
 
1442
1460
  // Don't clean up partial file on error - allow resume
1443
- console.error(this.colors.error.bold('๐Ÿ’ฅ Download failed: ') + this.colors.warning(error.message));
1461
+ console.error(this.colors.error.bold('Download failed: ') + this.colors.warning(error.message));
1444
1462
 
1445
1463
  if (error.name === 'AbortError') {
1446
- console.log(this.colors.info('๐Ÿ’พ Download state saved. You can resume later by running the same command.'));
1464
+ console.log(this.colors.info('Download state saved. You can resume later by running the same command.'));
1447
1465
  } else {
1448
- console.log(this.colors.info('๐Ÿ’พ Partial download saved. Restart to resume from where it left off.'));
1466
+ console.log(this.colors.info('Partial download saved. Restart to resume from where it left off.'));
1449
1467
  }
1450
1468
 
1451
1469
  throw error;
@@ -1891,7 +1909,7 @@ for (let i = 0; i < urlIndices.length; i++) {
1891
1909
  downloads.push({ url, outputPath });
1892
1910
  }
1893
1911
 
1894
- console.log(chalk.cyan.bold(`๐Ÿ” Detected ${downloads.length} download(s):`));
1912
+ console.log(chalk.cyan.bold(`Detected ${downloads.length} download(s):`));
1895
1913
  downloads.forEach((download, index) => {
1896
1914
  console.log(` ${index + 1}. ${chalk.yellow(download.url)} (URL)`);
1897
1915
  });
@@ -1899,7 +1917,7 @@ console.log('');
1899
1917
 
1900
1918
  // Handle multiple downloads
1901
1919
  if (downloads.length > 1) {
1902
- console.log(chalk.blue.bold(`๐Ÿš€ Multiple downloads detected: ${downloads.length} files\n`));
1920
+ console.log(chalk.blue.bold(`Multiple downloads detected: ${downloads.length} files\n`));
1903
1921
 
1904
1922
  // Prepare download objects
1905
1923
  const downloadObjects = downloads.map((download, index) => {
@@ -1932,7 +1950,7 @@ if (downloads.length > 1) {
1932
1950
  });
1933
1951
 
1934
1952
  // Show download queue
1935
- console.log(chalk.cyan.bold('\n๐Ÿ“‹ Download Queue:'));
1953
+ console.log(chalk.cyan.bold('\nDownload Queue:'));
1936
1954
  downloadObjects.forEach((downloadObj, index) => {
1937
1955
  console.log(` ${chalk.yellow((index + 1).toString().padStart(2))}. ${chalk.green(downloadObj.filename)} ${chalk.gray('โ†’')} ${downloadObj.outputPath}`);
1938
1956
  });
@@ -1942,13 +1960,13 @@ if (downloads.length > 1) {
1942
1960
  await downloader.downloadMultipleFiles(downloadObjects);
1943
1961
 
1944
1962
  // Display individual file stats
1945
- console.log(chalk.cyan.bold('\n๐Ÿ“Š File Details:'));
1963
+ console.log(chalk.cyan.bold('\nFile Details:'));
1946
1964
  downloadObjects.forEach((downloadObj) => {
1947
1965
  displayFileStats(downloadObj.outputPath, downloader);
1948
1966
  });
1949
1967
 
1950
1968
  } catch (error) {
1951
- console.error(chalk.red.bold('๐Ÿ’ฅ Failed to download files: ') + chalk.yellow(error.message));
1969
+ console.error(chalk.red.bold('Failed to download files: ') + chalk.yellow(error.message));
1952
1970
  process.exit(1);
1953
1971
  }
1954
1972
 
@@ -1975,26 +1993,26 @@ if (downloads.length > 1) {
1975
1993
 
1976
1994
  // Check if file already exists
1977
1995
  if (fs.existsSync(outputPath)) {
1978
- console.log(chalk.yellow('โš ๏ธ File already exists: ') + outputPath);
1996
+ console.log(chalk.yellow('File already exists: ') + outputPath);
1979
1997
  console.log(chalk.gray(' Continuing will overwrite the existing file...'));
1980
1998
  }
1981
1999
 
1982
- console.log(chalk.green('\n๐ŸŽฏ Target: ') + chalk.bold(outputPath));
2000
+ console.log(chalk.green('\nTarget: ') + chalk.bold(outputPath));
1983
2001
 
1984
2002
  try {
1985
2003
  await downloader.downloadFile(url, outputPath);
1986
2004
  displayFileStats(outputPath, downloader);
1987
2005
 
1988
2006
  } catch (error) {
1989
- console.error(chalk.red.bold('๐Ÿ’ฅ Failed to download file: ') + chalk.yellow(error.message));
2007
+ console.error(chalk.red.bold('Failed to download file: ') + chalk.yellow(error.message));
1990
2008
 
1991
2009
  // Clean up partial file if it exists
1992
2010
  if (fs.existsSync(outputPath)) {
1993
2011
  try {
1994
2012
  fs.unlinkSync(outputPath);
1995
- console.log(chalk.gray('๐Ÿงน Cleaned up partial download'));
2013
+ console.log(chalk.gray('Cleaned up partial download'));
1996
2014
  } catch (cleanupError) {
1997
- console.log(chalk.yellow('โš ๏ธ Could not clean up partial file: ') + cleanupError.message);
2015
+ console.log(chalk.yellow('Could not clean up partial file: ') + cleanupError.message);
1998
2016
  }
1999
2017
  }
2000
2018