frostpv 1.0.11 → 1.0.13

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/index.js +139 -141
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -4,7 +4,7 @@ const path = require("path");
4
4
  const os = require("os");
5
5
  const FormData = require("form-data");
6
6
  const crypto = require("crypto");
7
- const { igdl, ttdl, fbdown, mediafire, capcut, gdrive, pinterest } = require("btch-downloader");
7
+ const { igdl, ttdl, fbdown, mediafire, capcut, gdrive, pinterest, twitter } = require("btch-downloader");
8
8
  const { TwitterDL } = require("twitter-downloader");
9
9
  const btch = require("btch-downloader");
10
10
  const btchOld = require("btch-downloader-old");
@@ -21,10 +21,10 @@ ffmpeg.setFfmpegPath(pathToFfmpeg.ffmpegPath);
21
21
 
22
22
  // Output directory is the caller's working directory (keeps files next to the running app)
23
23
  const OUTPUT_DIR = process.cwd();
24
- try { fs.mkdirSync(OUTPUT_DIR, { recursive: true }); } catch (_) {}
24
+ try { fs.mkdirSync(OUTPUT_DIR, { recursive: true }); } catch (_) { }
25
25
  // Keep an OS temp directory for any future transient needs
26
26
  const TEMP_DIR = path.join(os.tmpdir(), "downloader-dl-bot");
27
- try { fs.mkdirSync(TEMP_DIR, { recursive: true }); } catch (_) {}
27
+ try { fs.mkdirSync(TEMP_DIR, { recursive: true }); } catch (_) { }
28
28
  // TTL (minutes) for files in OUTPUT_DIR (audio/video) before they are pruned
29
29
  const OUTPUT_RETENTION_MIN = Number(process.env.OUTPUT_RETENTION_MIN || 5);
30
30
 
@@ -73,7 +73,7 @@ const isVideoLink = (link) => {
73
73
  const u = new URL(link);
74
74
  const host = (u.hostname || '').toLowerCase();
75
75
  if (host.endsWith('pintere21313213st.com')) return true;
76
- } catch (_) {}
76
+ } catch (_) { }
77
77
  return videoPlatforms.some(platform => link.startsWith(platform));
78
78
  };
79
79
 
@@ -146,14 +146,14 @@ async function safeUnlinkWithRetry(filePath, maxRetries = 3) {
146
146
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
147
147
  try {
148
148
  const resolvedPath = path.resolve(filePath);
149
-
149
+
150
150
  if (fs.existsSync(resolvedPath)) {
151
151
  // Check if file is in use (Windows)
152
152
  if (process.platform === 'win32' && isFileInUse(resolvedPath)) {
153
153
  await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
154
154
  continue;
155
155
  }
156
-
156
+
157
157
  fs.rmSync(resolvedPath, { force: true });
158
158
  return true;
159
159
  } else {
@@ -225,7 +225,7 @@ async function validateVideoUrl(url, platform) {
225
225
  return status >= 200 && status < 400; // Accept redirects
226
226
  }
227
227
  });
228
-
228
+
229
229
  return true;
230
230
  } catch (error) {
231
231
  return false;
@@ -237,18 +237,18 @@ function validateTwitterUrl(url) {
237
237
  try {
238
238
  // Remove query parameters that might cause issues
239
239
  const cleanUrl = url.split('?')[0];
240
-
240
+
241
241
  // Ensure it's a valid Twitter/X URL format
242
242
  if (!cleanUrl.includes('x.com') && !cleanUrl.includes('twitter.com')) {
243
243
  throw new Error("Not a valid Twitter/X URL");
244
244
  }
245
-
245
+
246
246
  // Check if it has the required structure
247
247
  const urlParts = cleanUrl.split('/');
248
248
  if (urlParts.length < 5) {
249
249
  throw new Error("Invalid Twitter/X URL structure");
250
250
  }
251
-
251
+
252
252
  return cleanUrl;
253
253
  } catch (error) {
254
254
  throw new Error(`Twitter URL validation failed: ${error.message}`);
@@ -287,10 +287,10 @@ async function downloadYoutubeAudio(url, outputPath) {
287
287
  try {
288
288
  const ytdlp = new YtDlp({ ffmpegPath });
289
289
  const cookiesPath = getYoutubeCookiesPath();
290
-
290
+
291
291
  // Garantir que o nome do arquivo tenha a extensão correta
292
292
  const baseName = outputPath.replace(/\.[^/.]+$/, ""); // Remove extensão se existir
293
-
293
+
294
294
  // Primeiro, tentar com a API de opções
295
295
  try {
296
296
  const options = {
@@ -301,11 +301,11 @@ async function downloadYoutubeAudio(url, outputPath) {
301
301
  },
302
302
  output: outputPath
303
303
  };
304
-
304
+
305
305
  if (cookiesPath) {
306
306
  options.cookies = cookiesPath;
307
307
  }
308
-
308
+
309
309
  // Remover logs detalhados para download de áudio do YouTube
310
310
  const result = await ytdlp.downloadAsync(url, options);
311
311
  // Verificar se o arquivo foi criado
@@ -314,7 +314,7 @@ async function downloadYoutubeAudio(url, outputPath) {
314
314
  resolve(actualFileName);
315
315
  return;
316
316
  }
317
-
317
+
318
318
  // Tentar encontrar o arquivo com extensão diferente
319
319
  const files = fs.readdirSync('./');
320
320
  const downloadedFile = files.find(file => file.startsWith(baseName.split('/').pop()));
@@ -325,18 +325,18 @@ async function downloadYoutubeAudio(url, outputPath) {
325
325
  } catch (apiError) {
326
326
  console.log('API method failed, trying direct args method:', apiError.message);
327
327
  }
328
-
328
+
329
329
  // Fallback: usar argumentos diretos para máxima qualidade de áudio
330
330
  const args = [
331
331
  url,
332
332
  '-f', 'bestaudio[ext=mp3]/bestaudio',
333
333
  '-o', outputPath
334
334
  ];
335
-
335
+
336
336
  if (cookiesPath) {
337
337
  args.push('--cookies', cookiesPath);
338
338
  }
339
-
339
+
340
340
  // Remover logs detalhados para download de áudio do YouTube
341
341
  const result = await ytdlp.execAsync(args);
342
342
  // Verificar se o arquivo foi criado
@@ -363,25 +363,25 @@ async function downloadYoutubeAudio(url, outputPath) {
363
363
  // Enhanced function to get platform type from URL
364
364
  function getPlatformType(url) {
365
365
  const lowerUrl = url.toLowerCase();
366
-
366
+
367
367
  if (lowerUrl.includes("instagram.com") || lowerUrl.includes("instagr.am")) return "instagram";
368
368
  if (lowerUrl.includes("tiktok.com") || lowerUrl.includes("vm.tiktok.com") || lowerUrl.includes("vt.tiktok.com")) return "tiktok";
369
369
  if (lowerUrl.includes("facebook.com") || lowerUrl.includes("fb.watch") || lowerUrl.includes("fb.com")) return "facebook";
370
370
  if (lowerUrl.includes("mediafire.com")) return "mediafire";
371
371
  if (lowerUrl.includes("x.com") || lowerUrl.includes("twitter.com")) return "twitter";
372
372
  if (lowerUrl.includes("youtube.com") || lowerUrl.includes("you112t12u.be") || lowerUrl.includes("m.y11outu314be.com")) return "youtu1354be";
373
-
373
+
374
374
  return "unknown";
375
375
  }
376
376
 
377
377
  // Enhanced fallback download function with platform-specific methods
378
378
  async function tryFallbackDownload(url, maxRetries = 3) {
379
379
  const platform = getPlatformType(url);
380
-
380
+
381
381
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
382
382
  try {
383
383
  let videoUrl = null;
384
-
384
+
385
385
  // Platform-specific fallback methods
386
386
  switch (platform) {
387
387
  case "instagram": {
@@ -427,7 +427,7 @@ async function tryFallbackDownload(url, maxRetries = 3) {
427
427
  }
428
428
  }
429
429
  ];
430
-
430
+
431
431
  for (const method of methods) {
432
432
  try {
433
433
  videoUrl = await method();
@@ -438,7 +438,7 @@ async function tryFallbackDownload(url, maxRetries = 3) {
438
438
  }
439
439
  break;
440
440
  }
441
-
441
+
442
442
  case "tiktok": {
443
443
  // Try multiple TikTok methods
444
444
  const methods = [
@@ -473,7 +473,7 @@ async function tryFallbackDownload(url, maxRetries = 3) {
473
473
  throw new Error("No valid video in Tiktok.Downloader v2 response");
474
474
  }
475
475
  ];
476
-
476
+
477
477
  for (const method of methods) {
478
478
  try {
479
479
  videoUrl = await method();
@@ -484,7 +484,7 @@ async function tryFallbackDownload(url, maxRetries = 3) {
484
484
  }
485
485
  break;
486
486
  }
487
-
487
+
488
488
  case "facebook": {
489
489
  // Try multiple Facebook methods
490
490
  const methods = [
@@ -508,7 +508,7 @@ async function tryFallbackDownload(url, maxRetries = 3) {
508
508
  throw new Error("No valid video in fbdown response with custom headers");
509
509
  }
510
510
  ];
511
-
511
+
512
512
  for (const method of methods) {
513
513
  try {
514
514
  videoUrl = await method();
@@ -519,42 +519,42 @@ async function tryFallbackDownload(url, maxRetries = 3) {
519
519
  }
520
520
  break;
521
521
  }
522
-
522
+
523
523
  case "twitter": {
524
524
  // Try multiple Twitter methods with better response handling
525
525
  const methods = [
526
526
  async () => {
527
527
  const cleanUrl = validateTwitterUrl(url);
528
528
  const data = await TwitterDL(cleanUrl, {});
529
-
529
+
530
530
  // Check multiple possible response structures
531
531
  if (data && data.result) {
532
532
  // Structure 1: data.result.media[0].videos[0].url
533
- if (data.result.media && data.result.media[0] &&
534
- data.result.media[0].videos && data.result.media[0].videos[0] &&
535
- data.result.media[0].videos[0].url) {
533
+ if (data.result.media && data.result.media[0] &&
534
+ data.result.media[0].videos && data.result.media[0].videos[0] &&
535
+ data.result.media[0].videos[0].url) {
536
536
  return data.result.media[0].videos[0].url;
537
537
  }
538
-
538
+
539
539
  // Structure 2: data.result.video[0].url
540
540
  if (data.result.video && data.result.video[0] && data.result.video[0].url) {
541
541
  return data.result.video[0].url;
542
542
  }
543
-
543
+
544
544
  // Structure 3: data.result.url (direct URL)
545
545
  if (data.result.url) {
546
546
  return data.result.url;
547
547
  }
548
-
548
+
549
549
  // Structure 4: data.result.media[0].url
550
550
  if (data.result.media && data.result.media[0] && data.result.media[0].url) {
551
551
  return data.result.media[0].url;
552
552
  }
553
-
553
+
554
554
  // Structure 5: Check for any video URL in the entire response
555
555
  const findVideoUrl = (obj) => {
556
- if (typeof obj === 'string' && obj.includes('http') &&
557
- (obj.includes('.mp4') || obj.includes('video') || obj.includes('media'))) {
556
+ if (typeof obj === 'string' && obj.includes('http') &&
557
+ (obj.includes('.mp4') || obj.includes('video') || obj.includes('media'))) {
558
558
  return obj;
559
559
  }
560
560
  if (typeof obj === 'object' && obj !== null) {
@@ -565,13 +565,13 @@ async function tryFallbackDownload(url, maxRetries = 3) {
565
565
  }
566
566
  return null;
567
567
  };
568
-
568
+
569
569
  const foundUrl = findVideoUrl(data.result);
570
570
  if (foundUrl) {
571
571
  return foundUrl;
572
572
  }
573
573
  }
574
-
574
+
575
575
  throw new Error("No valid video URL found in TwitterDL response structure");
576
576
  },
577
577
  async () => {
@@ -587,30 +587,30 @@ async function tryFallbackDownload(url, maxRetries = 3) {
587
587
  'Upgrade-Insecure-Requests': '1'
588
588
  }
589
589
  });
590
-
590
+
591
591
  // Same structure checking as above
592
592
  if (data && data.result) {
593
- if (data.result.media && data.result.media[0] &&
594
- data.result.media[0].videos && data.result.media[0].videos[0] &&
595
- data.result.media[0].videos[0].url) {
593
+ if (data.result.media && data.result.media[0] &&
594
+ data.result.media[0].videos && data.result.media[0].videos[0] &&
595
+ data.result.media[0].videos[0].url) {
596
596
  return data.result.media[0].videos[0].url;
597
597
  }
598
-
598
+
599
599
  if (data.result.video && data.result.video[0] && data.result.video[0].url) {
600
600
  return data.result.video[0].url;
601
601
  }
602
-
602
+
603
603
  if (data.result.url) {
604
604
  return data.result.url;
605
605
  }
606
-
606
+
607
607
  if (data.result.media && data.result.media[0] && data.result.media[0].url) {
608
608
  return data.result.media[0].url;
609
609
  }
610
-
610
+
611
611
  const findVideoUrl = (obj) => {
612
- if (typeof obj === 'string' && obj.includes('http') &&
613
- (obj.includes('.mp4') || obj.includes('video') || obj.includes('media'))) {
612
+ if (typeof obj === 'string' && obj.includes('http') &&
613
+ (obj.includes('.mp4') || obj.includes('video') || obj.includes('media'))) {
614
614
  return obj;
615
615
  }
616
616
  if (typeof obj === 'object' && obj !== null) {
@@ -621,13 +621,13 @@ async function tryFallbackDownload(url, maxRetries = 3) {
621
621
  }
622
622
  return null;
623
623
  };
624
-
624
+
625
625
  const foundUrl = findVideoUrl(data.result);
626
626
  if (foundUrl) {
627
627
  return foundUrl;
628
628
  }
629
629
  }
630
-
630
+
631
631
  throw new Error("No valid video URL found in TwitterDL response with custom headers");
632
632
  },
633
633
  async () => {
@@ -663,9 +663,9 @@ async function tryFallbackDownload(url, maxRetries = 3) {
663
663
  const data = await btchOld[funcName](cleanUrl);
664
664
  // Check multiple possible response structures
665
665
  if (data && data.result) {
666
- if (data.result.media && data.result.media[0] &&
667
- data.result.media[0].videos && data.result.media[0].videos[0] &&
668
- data.result.media[0].videos[0].url) {
666
+ if (data.result.media && data.result.media[0] &&
667
+ data.result.media[0].videos && data.result.media[0].videos[0] &&
668
+ data.result.media[0].videos[0].url) {
669
669
  return data.result.media[0].videos[0].url;
670
670
  }
671
671
  if (data.result.video && data.result.video[0] && data.result.video[0].url) {
@@ -692,7 +692,7 @@ async function tryFallbackDownload(url, maxRetries = 3) {
692
692
  }
693
693
  }
694
694
  ];
695
-
695
+
696
696
  for (const method of methods) {
697
697
  try {
698
698
  videoUrl = await method();
@@ -703,12 +703,12 @@ async function tryFallbackDownload(url, maxRetries = 3) {
703
703
  }
704
704
  break;
705
705
  }
706
-
706
+
707
707
  case "youtube": {
708
708
  // YouTube doesn't need fallback - it has its own specific method
709
709
  throw new Error("YouTube downloads should use the primary method, not fallback");
710
710
  }
711
-
711
+
712
712
  case "mediafire": {
713
713
  // Try MediaFire method
714
714
  try {
@@ -746,10 +746,10 @@ async function tryFallbackDownload(url, maxRetries = 3) {
746
746
  };
747
747
  mediaUrl = pickFrom(data);
748
748
  if (mediaUrl) return mediaUrl;
749
- } catch (_) {}
749
+ } catch (_) { }
750
750
  break;
751
751
  }
752
-
752
+
753
753
  default: {
754
754
  // Generic fallback for unknown platforms
755
755
  try {
@@ -765,18 +765,18 @@ async function tryFallbackDownload(url, maxRetries = 3) {
765
765
  break;
766
766
  }
767
767
  }
768
-
768
+
769
769
  // If we got a video URL, validate it
770
770
  if (videoUrl && videoUrl.includes("http")) {
771
771
  // Validate the URL is accessible
772
772
  try {
773
- const response = await axios.head(videoUrl, {
773
+ const response = await axios.head(videoUrl, {
774
774
  timeout: 10000,
775
775
  headers: {
776
776
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
777
777
  }
778
778
  });
779
-
779
+
780
780
  if (response.status === 200) {
781
781
  return videoUrl;
782
782
  } else {
@@ -788,7 +788,7 @@ async function tryFallbackDownload(url, maxRetries = 3) {
788
788
  } else {
789
789
  throw new Error("No valid video URL obtained from any fallback method");
790
790
  }
791
-
791
+
792
792
  } catch (error) {
793
793
  if (attempt === maxRetries) {
794
794
  throw new Error(`Enhanced fallback method failed after ${maxRetries} attempts for ${platform}: ${error.message}`);
@@ -849,21 +849,21 @@ const MediaDownloader = async (url, options = {}) => {
849
849
  downloadedFilePath = await downloadSmartVideo(url, config);
850
850
  } catch (error) {
851
851
  const platform = getPlatformType(url);
852
-
852
+
853
853
  // YouTube doesn't use fallback method - it has its own specific implementation
854
854
  if (platform === "youtube") {
855
855
  throw new Error(`YouTube download failed: ${error.message}`);
856
856
  }
857
-
857
+
858
858
  try {
859
859
  const fallbackUrl = await tryFallbackDownload(url);
860
-
860
+
861
861
  // Validate fallback URL
862
862
  const isValid = await validateVideoUrl(fallbackUrl, platform);
863
863
  if (!isValid) {
864
864
  throw new Error("Fallback URL validation failed");
865
865
  }
866
-
866
+
867
867
  downloadedFilePath = await downloadDirectVideo(fallbackUrl, config);
868
868
  } catch (fallbackError) {
869
869
  throw new Error(`Failed to download video with both methods: ${fallbackError.message}`);
@@ -986,15 +986,15 @@ async function downloadSmartVideo(url, config) {
986
986
  throw new Error("No video URLs found in Facebook response");
987
987
  }
988
988
  videoUrl = data.Normal_video || data.HD;
989
-
989
+
990
990
  // Validate the URL is accessible
991
- const response = await axios.head(videoUrl, {
991
+ const response = await axios.head(videoUrl, {
992
992
  timeout: 10000,
993
993
  headers: {
994
994
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
995
995
  }
996
996
  }).catch(() => null);
997
-
997
+
998
998
  if (!response || response.status !== 200) {
999
999
  throw new Error("Facebook video URL is not accessible");
1000
1000
  }
@@ -1063,47 +1063,45 @@ async function downloadSmartVideo(url, config) {
1063
1063
  }
1064
1064
  case "twitter": {
1065
1065
  try {
1066
- // Validate and clean the Twitter URL
1067
- const cleanUrl = validateTwitterUrl(url);
1068
- const data = await TwitterDL(cleanUrl, {});
1069
-
1070
- if (data && data.result) {
1071
- // Try multiple possible response structures
1072
- if (data.result.media && data.result.media[0] &&
1073
- data.result.media[0].videos && data.result.media[0].videos[0] &&
1074
- data.result.media[0].videos[0].url) {
1075
- videoUrl = data.result.media[0].videos[0].url;
1076
- } else if (data.result.video && data.result.video[0] && data.result.video[0].url) {
1077
- videoUrl = data.result.video[0].url;
1078
- } else if (data.result.url) {
1079
- videoUrl = data.result.url;
1080
- } else if (data.result.media && data.result.media[0] && data.result.media[0].url) {
1081
- videoUrl = data.result.media[0].url;
1082
- } else {
1083
- // Search for any video URL in the response
1084
- const findVideoUrl = (obj) => {
1085
- if (typeof obj === 'string' && obj.includes('http') &&
1086
- (obj.includes('.mp4') || obj.includes('video') || obj.includes('media'))) {
1087
- return obj;
1088
- }
1089
- if (typeof obj === 'object' && obj !== null) {
1090
- for (const key in obj) {
1091
- const result = findVideoUrl(obj[key]);
1092
- if (result) return result;
1093
- }
1094
- }
1095
- return null;
1096
- };
1097
-
1098
- const foundUrl = findVideoUrl(data.result);
1099
- if (foundUrl) {
1100
- videoUrl = foundUrl;
1066
+ const data = await twitter(url);
1067
+ if (data && data.url) {
1068
+ // Se data.url for um array, procuramos por qualidade HD
1069
+ if (Array.isArray(data.url)) {
1070
+ // Tentar encontrar HD especificamente
1071
+ const hdVideo = data.url.find(v => v.hd);
1072
+ if (hdVideo) {
1073
+ videoUrl = hdVideo.hd;
1101
1074
  } else {
1102
- throw new Error("No video URL found in Twitter response structure");
1075
+ // Tentar encontrar por label ou qualidade
1076
+ const highQuality = data.url.find(v =>
1077
+ (v.quality && typeof v.quality === 'string' && v.quality.toUpperCase().includes('HD')) ||
1078
+ (v.label && typeof v.label === 'string' && v.label.toUpperCase().includes('HD'))
1079
+ );
1080
+
1081
+ if (highQuality) {
1082
+ videoUrl = highQuality.url;
1083
+ } else {
1084
+ // Fallback: pega o primeiro que tiver url
1085
+ const anyVideo = data.url.find(v => v.url);
1086
+ videoUrl = anyVideo ? anyVideo.url : data.url[0];
1087
+ }
1103
1088
  }
1089
+ } else if (typeof data.url === 'string') {
1090
+ videoUrl = data.url;
1104
1091
  }
1105
- } else {
1106
- throw new Error("Invalid Twitter response structure");
1092
+ }
1093
+
1094
+ if (!videoUrl) {
1095
+ // Fallback para estrutura antiga ou diferente
1096
+ if (Array.isArray(data) && data[0] && data[0].url) {
1097
+ videoUrl = data[0].url;
1098
+ } else if (data && data.HD) {
1099
+ videoUrl = data.HD;
1100
+ }
1101
+ }
1102
+
1103
+ if (!videoUrl) {
1104
+ throw new Error("No video URL found in btch-downloader twitter response");
1107
1105
  }
1108
1106
  } catch (error) {
1109
1107
  throw new Error(`Twitter download failed: ${error.message}`);
@@ -1117,21 +1115,21 @@ async function downloadSmartVideo(url, config) {
1117
1115
  if (!videoUrl || !videoUrl.includes("http")) {
1118
1116
  throw new Error("Returned video URL is invalid or unavailable.");
1119
1117
  }
1120
-
1118
+
1121
1119
  // Download the video with better error handling
1122
- const response = await axios({
1123
- url: videoUrl,
1124
- method: "GET",
1120
+ const response = await axios({
1121
+ url: videoUrl,
1122
+ method: "GET",
1125
1123
  responseType: "stream",
1126
1124
  timeout: 30000,
1127
1125
  headers: {
1128
1126
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
1129
1127
  }
1130
1128
  });
1131
-
1129
+
1132
1130
  // Create minimal unique file name in output dir
1133
- let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0,4)}.mp4`);
1134
-
1131
+ let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.mp4`);
1132
+
1135
1133
  const videoWriter = fs.createWriteStream(fileName);
1136
1134
  response.data.pipe(videoWriter);
1137
1135
 
@@ -1156,19 +1154,19 @@ async function downloadSmartVideo(url, config) {
1156
1154
  // Function for direct video download
1157
1155
  async function downloadDirectVideo(url, config) {
1158
1156
  try {
1159
- const response = await axios({
1160
- url: url,
1161
- method: "GET",
1157
+ const response = await axios({
1158
+ url: url,
1159
+ method: "GET",
1162
1160
  responseType: "stream",
1163
1161
  timeout: 30000,
1164
1162
  headers: {
1165
1163
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
1166
1164
  }
1167
1165
  });
1168
-
1166
+
1169
1167
  // Create minimal unique file name in output dir
1170
- let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0,4)}.mp4`);
1171
-
1168
+ let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.mp4`);
1169
+
1172
1170
  const videoWriter = fs.createWriteStream(fileName);
1173
1171
  response.data.pipe(videoWriter);
1174
1172
 
@@ -1216,7 +1214,7 @@ async function downloadGenericFile(url, preferredExt = null) {
1216
1214
  else ext = 'bin';
1217
1215
  }
1218
1216
 
1219
- const fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0,4)}.${ext}`);
1217
+ const fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.${ext}`);
1220
1218
  const writer = fs.createWriteStream(fileName);
1221
1219
  response.data.pipe(writer);
1222
1220
 
@@ -1231,7 +1229,7 @@ async function downloadGenericFile(url, preferredExt = null) {
1231
1229
 
1232
1230
  // Function to rotate video
1233
1231
  async function rotateVideo(fileName, rotation) {
1234
- const outputPath = path.join(OUTPUT_DIR, `vr_${uuidv4().slice(0,4)}.mp4`);
1232
+ const outputPath = path.join(OUTPUT_DIR, `vr_${uuidv4().slice(0, 4)}.mp4`);
1235
1233
  let angle;
1236
1234
  switch (rotation.toLowerCase()) {
1237
1235
  case "left": angle = "transpose=2"; break;
@@ -1282,8 +1280,8 @@ async function cleanupTempFiles() {
1282
1280
  try {
1283
1281
  if (!fs.existsSync(TEMP_DIR)) return;
1284
1282
  const files = fs.readdirSync(TEMP_DIR);
1285
- const tempFiles = files.filter(file =>
1286
- /^temp_video.*\.mp4$/.test(file) ||
1283
+ const tempFiles = files.filter(file =>
1284
+ /^temp_video.*\.mp4$/.test(file) ||
1287
1285
  /_audio\.mp3$/.test(file) ||
1288
1286
  /_rotated\.mp4$/.test(file) ||
1289
1287
  /_cropped\.mp4$/.test(file) ||
@@ -1315,9 +1313,9 @@ async function cleanupOutputFiles() {
1315
1313
  if (now - st.mtimeMs > maxAgeMs) {
1316
1314
  await safeUnlinkWithRetry(full);
1317
1315
  }
1318
- } catch (_) {}
1316
+ } catch (_) { }
1319
1317
  }
1320
- } catch (_) {}
1318
+ } catch (_) { }
1321
1319
  }
1322
1320
 
1323
1321
  // Schedule periodic cleanup (every 10 minutes)
@@ -1331,7 +1329,7 @@ cleanupOutputFiles();
1331
1329
  // Function to auto-crop video
1332
1330
  async function autoCrop(fileName) {
1333
1331
  const inputPath = fileName;
1334
- const outputPath = path.join(OUTPUT_DIR, `vc_${uuidv4().slice(0,4)}.mp4`);
1332
+ const outputPath = path.join(OUTPUT_DIR, `vc_${uuidv4().slice(0, 4)}.mp4`);
1335
1333
 
1336
1334
  return new Promise((resolve, reject) => {
1337
1335
  let cropValues = null;
@@ -1339,13 +1337,13 @@ async function autoCrop(fileName) {
1339
1337
  .outputOptions('-vf', 'cropdetect=24:16:0')
1340
1338
  .outputFormat('null')
1341
1339
  .output('-')
1342
- .on('stderr', function(stderrLine) {
1340
+ .on('stderr', function (stderrLine) {
1343
1341
  const cropMatch = stderrLine.match(/crop=([0-9]+):([0-9]+):([0-9]+):([0-9]+)/);
1344
1342
  if (cropMatch) {
1345
1343
  cropValues = `crop=${cropMatch[1]}:${cropMatch[2]}:${cropMatch[3]}:${cropMatch[4]}`;
1346
1344
  }
1347
1345
  })
1348
- .on('end', function() {
1346
+ .on('end', function () {
1349
1347
  if (!cropValues) {
1350
1348
  resolve(inputPath);
1351
1349
  return;
@@ -1378,7 +1376,7 @@ async function checkAndCompressVideo(filePath, limitSizeMB, platform = null) {
1378
1376
  return filePath;
1379
1377
  }
1380
1378
 
1381
- const outputPath = path.join(OUTPUT_DIR, `vx_${uuidv4().slice(0,4)}.mp4`);
1379
+ const outputPath = path.join(OUTPUT_DIR, `vx_${uuidv4().slice(0, 4)}.mp4`);
1382
1380
 
1383
1381
  return new Promise((resolve, reject) => {
1384
1382
  ffmpeg(filePath)
@@ -1413,17 +1411,17 @@ async function convertVideoFormat(inputPath, targetFormat) {
1413
1411
  const supported = ["mp4", "mov", "webm", "mkv"];
1414
1412
  if (!supported.includes(fmt)) return inputPath;
1415
1413
 
1416
- const outputPath = path.join(OUTPUT_DIR, `vf_${uuidv4().slice(0,4)}.${fmt}`);
1414
+ const outputPath = path.join(OUTPUT_DIR, `vf_${uuidv4().slice(0, 4)}.${fmt}`);
1417
1415
 
1418
1416
  const ff = ffmpeg(inputPath);
1419
1417
  switch (fmt) {
1420
1418
  case "mp4":
1421
1419
  case "mov":
1422
1420
  case "mkv":
1423
- ff.outputOptions("-c:v","libx264","-pix_fmt","yuv420p","-c:a","aac","-b:a","192k");
1421
+ ff.outputOptions("-c:v", "libx264", "-pix_fmt", "yuv420p", "-c:a", "aac", "-b:a", "192k");
1424
1422
  break;
1425
1423
  case "webm":
1426
- ff.outputOptions("-c:v","libvpx-vp9","-b:v","0","-crf","30","-c:a","libopus","-b:a","128k");
1424
+ ff.outputOptions("-c:v", "libvpx-vp9", "-b:v", "0", "-crf", "30", "-c:a", "libopus", "-b:a", "128k");
1427
1425
  break;
1428
1426
  default:
1429
1427
  return inputPath;
@@ -1480,14 +1478,14 @@ async function unshortenUrl(url) {
1480
1478
  return status >= 200 && status < 400; // Accept redirects
1481
1479
  }
1482
1480
  });
1483
-
1481
+
1484
1482
  // Get the final URL after all redirects
1485
1483
  const finalUrl = response.request.res.responseUrl || response.config.url;
1486
1484
  return finalUrl;
1487
1485
  }
1488
-
1486
+
1489
1487
  // For other URLs, use the original method
1490
- const response = await axios.head(url, {
1488
+ const response = await axios.head(url, {
1491
1489
  maxRedirects: 10,
1492
1490
  timeout: 10000,
1493
1491
  headers: {
@@ -1556,10 +1554,10 @@ const AudioDownloader = async (url, options = {}) => {
1556
1554
  if (downloadedFilePath) {
1557
1555
  // Extrair áudio em mp3
1558
1556
  audioFilePath = await extractAudioMp3(downloadedFilePath);
1559
-
1557
+
1560
1558
  // Remove o arquivo de vídeo temporário após extrair o áudio
1561
1559
  await safeUnlinkWithRetry(downloadedFilePath);
1562
-
1560
+
1563
1561
  const result = await uploadToGoFileIfNeeded(audioFilePath);
1564
1562
  return result;
1565
1563
  } else {
@@ -1575,7 +1573,7 @@ const AudioDownloader = async (url, options = {}) => {
1575
1573
  // Função para extrair áudio em mp3 usando ffmpeg
1576
1574
  async function extractAudioMp3(videoPath) {
1577
1575
  return new Promise((resolve, reject) => {
1578
- const audioPath = path.join(OUTPUT_DIR, `a_${uuidv4().slice(0,4)}.mp3`);
1576
+ const audioPath = path.join(OUTPUT_DIR, `a_${uuidv4().slice(0, 4)}.mp3`);
1579
1577
  ffmpeg(videoPath)
1580
1578
  .noVideo()
1581
1579
  .audioCodec('libmp3lame')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frostpv",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "downloads",
5
5
  "main": "index.js",
6
6
  "scripts": {