frostpv 1.0.14 → 1.0.15

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 +41 -127
  2. package/package.json +5 -5
package/index.js CHANGED
@@ -9,9 +9,9 @@ const { TwitterDL } = require("twitter-downloader");
9
9
  const btch = require("btch-downloader");
10
10
  const btchOld = require("btch-downloader-old");
11
11
 
12
- // Fallback libs
12
+
13
13
  const Tiktok = require("@tobyg74/tiktok-api-dl");
14
- const { YtDlp } = require('ytdlp-nodejs');
14
+ const ab = require("ab-downloader");
15
15
  const ffmpegPath = require('ffmpeg-ffprobe-static').ffmpegPath;
16
16
  const { v4: uuidv4 } = require('uuid');
17
17
 
@@ -19,13 +19,10 @@ const pathToFfmpeg = require("ffmpeg-ffprobe-static");
19
19
  const ffmpeg = require("fluent-ffmpeg");
20
20
  ffmpeg.setFfmpegPath(pathToFfmpeg.ffmpegPath);
21
21
 
22
- // Output directory is the caller's working directory (keeps files next to the running app)
23
22
  const OUTPUT_DIR = process.cwd();
24
23
  try { fs.mkdirSync(OUTPUT_DIR, { recursive: true }); } catch (_) { }
25
- // Keep an OS temp directory for any future transient needs
26
24
  const TEMP_DIR = path.join(os.tmpdir(), "downloader-dl-bot");
27
25
  try { fs.mkdirSync(TEMP_DIR, { recursive: true }); } catch (_) { }
28
- // TTL (minutes) for files in OUTPUT_DIR (audio/video) before they are pruned
29
26
  const OUTPUT_RETENTION_MIN = Number(process.env.OUTPUT_RETENTION_MIN || 5);
30
27
 
31
28
  const GOFILE_API = "Vs4e2PL8n65ExPz6wgDlqSY8kcEBRrzN";
@@ -85,7 +82,7 @@ const blacklistLink = (link) => {
85
82
 
86
83
  const defaultConfig = {
87
84
  autocrop: false,
88
- limitSizeMB: null, // For compression, not for GoFile limit
85
+ limitSizeMB: null,
89
86
  rotation: null,
90
87
  outputFormat: null,
91
88
  };
@@ -265,102 +262,6 @@ function getYoutubeCookiesPath() {
265
262
  return null;
266
263
  }
267
264
 
268
- // Função utilitária para obter duração do vídeo/áudio em segundos
269
- async function getYoutubeDurationSeconds(url) {
270
- try {
271
- const ytdlp = new YtDlp({ ffmpegPath });
272
- const info = await ytdlp.getInfoAsync(url);
273
- if (info && info.duration) return info.duration;
274
- return null;
275
- } catch (e) {
276
- return null;
277
- }
278
- }
279
-
280
- // Função para baixar áudio do YouTube
281
- async function downloadYoutubeAudio(url, outputPath) {
282
- // Verificar duração máxima de 15 minutos
283
- const duration = await getYoutubeDurationSeconds(url);
284
- if (duration && duration > 900) {
285
- throw new Error('The audio is longer than 15 minutes. Test limit: 15 minutes. This feature is still in beta.');
286
- }
287
- return new Promise(async (resolve, reject) => {
288
- try {
289
- const ytdlp = new YtDlp({ ffmpegPath });
290
- const cookiesPath = getYoutubeCookiesPath();
291
-
292
- // Garantir que o nome do arquivo tenha a extensão correta
293
- const baseName = outputPath.replace(/\.[^/.]+$/, ""); // Remove extensão se existir
294
-
295
- // Primeiro, tentar com a API de opções
296
- try {
297
- const options = {
298
- format: {
299
- filter: 'audioonly',
300
- type: 'mp3',
301
- quality: 'highestaudio'
302
- },
303
- output: outputPath
304
- };
305
-
306
- if (cookiesPath) {
307
- options.cookies = cookiesPath;
308
- }
309
-
310
- // Remover logs detalhados para download de áudio do YouTube
311
- const result = await ytdlp.downloadAsync(url, options);
312
- // Verificar se o arquivo foi criado
313
- const actualFileName = baseName + '.mp3';
314
- if (fs.existsSync(actualFileName)) {
315
- resolve(actualFileName);
316
- return;
317
- }
318
-
319
- // Tentar encontrar o arquivo com extensão diferente
320
- const files = fs.readdirSync('./');
321
- const downloadedFile = files.find(file => file.startsWith(baseName.split('/').pop()));
322
- if (downloadedFile) {
323
- resolve(downloadedFile);
324
- return;
325
- }
326
- } catch (apiError) {
327
- console.log('API method failed, trying direct args method:', apiError.message);
328
- }
329
-
330
- // Fallback: usar argumentos diretos para máxima qualidade de áudio
331
- const args = [
332
- url,
333
- '-f', 'bestaudio[ext=mp3]/bestaudio',
334
- '-o', outputPath
335
- ];
336
-
337
- if (cookiesPath) {
338
- args.push('--cookies', cookiesPath);
339
- }
340
-
341
- // Remover logs detalhados para download de áudio do YouTube
342
- const result = await ytdlp.execAsync(args);
343
- // Verificar se o arquivo foi criado
344
- const actualFileName = baseName + '.mp3';
345
- if (fs.existsSync(actualFileName)) {
346
- resolve(actualFileName);
347
- } else {
348
- // Tentar encontrar o arquivo com extensão diferente
349
- const files = fs.readdirSync('./');
350
- const downloadedFile = files.find(file => file.startsWith(baseName.split('/').pop()));
351
- if (downloadedFile) {
352
- resolve(downloadedFile);
353
- } else {
354
- reject(new Error('YouTube audio download completed but file not found'));
355
- }
356
- }
357
- } catch (error) {
358
- console.error('YouTube audio download error:', error);
359
- reject(new Error('This URL is not from a supported platform. Supported platforms: Instagram, X(Twitter), TikTok and Facebook'));
360
- }
361
- });
362
- }
363
-
364
265
  // Enhanced function to get platform type from URL
365
266
  function getPlatformType(url) {
366
267
  const lowerUrl = url.toLowerCase();
@@ -375,7 +276,6 @@ function getPlatformType(url) {
375
276
  return "unknown";
376
277
  }
377
278
 
378
- // Enhanced fallback download function with platform-specific methods
379
279
  async function tryFallbackDownload(url, maxRetries = 3) {
380
280
  const platform = getPlatformType(url);
381
281
 
@@ -383,10 +283,8 @@ async function tryFallbackDownload(url, maxRetries = 3) {
383
283
  try {
384
284
  let videoUrl = null;
385
285
 
386
- // Platform-specific fallback methods
387
286
  switch (platform) {
388
287
  case "instagram": {
389
- // Try multiple Instagram methods
390
288
  const methods = [
391
289
  async () => {
392
290
  const data = await igdl(url);
@@ -413,7 +311,6 @@ async function tryFallbackDownload(url, maxRetries = 3) {
413
311
  throw new Error("No valid URL in igdl response with custom headers");
414
312
  },
415
313
  async () => {
416
- // Fallback: try using the old version of btch-downloader
417
314
  try {
418
315
  const { igdl: igdlOld } = btchOld;
419
316
  if (typeof igdlOld === 'function') {
@@ -422,10 +319,17 @@ async function tryFallbackDownload(url, maxRetries = 3) {
422
319
  return data[0].url;
423
320
  }
424
321
  }
425
- throw new Error("Old btch-downloader igdl not available or failed");
322
+ throw new Error("Old downloader not available or failed");
426
323
  } catch (oldError) {
427
- throw new Error(`Old btch-downloader fallback failed: ${oldError.message}`);
324
+ throw new Error(`Old downloader fallback failed: ${oldError.message}`);
325
+ }
326
+ },
327
+ async () => {
328
+ const data = await ab.igdl(url);
329
+ if (data && Array.isArray(data) && data[0] && data[0].url) {
330
+ return data[0].url;
428
331
  }
332
+ throw new Error("No valid URL");
429
333
  }
430
334
  ];
431
335
 
@@ -441,7 +345,7 @@ async function tryFallbackDownload(url, maxRetries = 3) {
441
345
  }
442
346
 
443
347
  case "tiktok": {
444
- // Try multiple TikTok methods
348
+
445
349
  const methods = [
446
350
  async () => {
447
351
  const data = await ttdl(url);
@@ -472,6 +376,13 @@ async function tryFallbackDownload(url, maxRetries = 3) {
472
376
  }
473
377
  }
474
378
  throw new Error("No valid video in Tiktok.Downloader v2 response");
379
+ },
380
+ async () => {
381
+ const data = await ab.ttdl(url);
382
+ if (data && data.video && data.video[0]) {
383
+ return data.video[0];
384
+ }
385
+ throw new Error("No valid video");
475
386
  }
476
387
  ];
477
388
 
@@ -507,6 +418,13 @@ async function tryFallbackDownload(url, maxRetries = 3) {
507
418
  return data.Normal_video || data.HD;
508
419
  }
509
420
  throw new Error("No valid video in fbdown response with custom headers");
421
+ },
422
+ async () => {
423
+ const data = await ab.fbdown(url);
424
+ if (data && (data.Normal_video || data.HD)) {
425
+ return data.Normal_video || data.HD;
426
+ }
427
+ throw new Error("No valid video");
510
428
  }
511
429
  ];
512
430
 
@@ -653,7 +571,6 @@ async function tryFallbackDownload(url, maxRetries = 3) {
653
571
  }
654
572
  },
655
573
  async () => {
656
- // Fallback: try using the old version of btch-downloader for Twitter/X
657
574
  try {
658
575
  const cleanUrl = validateTwitterUrl(url);
659
576
  // Try to find any Twitter-related function in the old version
@@ -687,10 +604,20 @@ async function tryFallbackDownload(url, maxRetries = 3) {
687
604
  }
688
605
  }
689
606
  }
690
- throw new Error("No Twitter functions found in old btch-downloader");
607
+ throw new Error("No Twitter functions found");
691
608
  } catch (oldError) {
692
- throw new Error(`Old btch-downloader Twitter fallback failed: ${oldError.message}`);
609
+ throw new Error(`Twitter fallback failed: ${oldError.message}`);
693
610
  }
611
+ },
612
+ async () => {
613
+ const data = await ab.twitter(url);
614
+ if (data && data.result && data.result.url) {
615
+ return data.result.url;
616
+ }
617
+ if (data && Array.isArray(data) && data[0] && data[0].url) {
618
+ return data[0].url;
619
+ }
620
+ throw new Error("No valid video");
694
621
  }
695
622
  ];
696
623
 
@@ -706,12 +633,10 @@ async function tryFallbackDownload(url, maxRetries = 3) {
706
633
  }
707
634
 
708
635
  case "youtube": {
709
- // YouTube doesn't need fallback - it has its own specific method
710
636
  throw new Error("YouTube downloads should use the primary method, not fallback");
711
637
  }
712
638
 
713
639
  case "mediafire": {
714
- // Try MediaFire method
715
640
  try {
716
641
  const data = await mediafire(url);
717
642
  if (data && data.url) {
@@ -720,7 +645,6 @@ async function tryFallbackDownload(url, maxRetries = 3) {
720
645
  throw new Error("No valid URL in mediafire response");
721
646
  }
722
647
  } catch (methodError) {
723
- // Continue to next attempt
724
648
  }
725
649
  break;
726
650
  }
@@ -752,7 +676,6 @@ async function tryFallbackDownload(url, maxRetries = 3) {
752
676
  }
753
677
 
754
678
  default: {
755
- // Generic fallback for unknown platforms
756
679
  try {
757
680
  const data = await igdl(url);
758
681
  if (data && Array.isArray(data) && data[0] && data[0].url) {
@@ -761,15 +684,12 @@ async function tryFallbackDownload(url, maxRetries = 3) {
761
684
  throw new Error("No valid URL in generic igdl response");
762
685
  }
763
686
  } catch (methodError) {
764
- // Continue to next attempt
765
687
  }
766
688
  break;
767
689
  }
768
690
  }
769
691
 
770
- // If we got a video URL, validate it
771
692
  if (videoUrl && videoUrl.includes("http")) {
772
- // Validate the URL is accessible
773
693
  try {
774
694
  const response = await axios.head(videoUrl, {
775
695
  timeout: 10000,
@@ -943,7 +863,6 @@ async function downloadSmartVideo(url, config) {
943
863
  }
944
864
  videoUrl = data.video[0];
945
865
  } catch (error) {
946
- // Fallback: @tobyg74/tiktok-api-dl
947
866
  try {
948
867
  const result = await Tiktok.Downloader(url, { version: "v1" });
949
868
  if (result.status === "success" && result.result) {
@@ -1018,10 +937,7 @@ async function downloadSmartVideo(url, config) {
1018
937
  }
1019
938
  case "pinterest": {
1020
939
  try {
1021
- // Supports both pin links and search queries
1022
940
  const data = await pinterest(url);
1023
- // data may be array or object depending on query or pin
1024
- // Try to find best url (image or video)
1025
941
  let directUrl = null;
1026
942
  const pickFrom = (obj) => {
1027
943
  if (!obj) return null;
@@ -1082,7 +998,6 @@ async function downloadSmartVideo(url, config) {
1082
998
  if (highQuality) {
1083
999
  videoUrl = highQuality.url;
1084
1000
  } else {
1085
- // Fallback: pega o primeiro que tiver url
1086
1001
  const anyVideo = data.url.find(v => v.url);
1087
1002
  videoUrl = anyVideo ? anyVideo.url : data.url[0];
1088
1003
  }
@@ -1093,7 +1008,6 @@ async function downloadSmartVideo(url, config) {
1093
1008
  }
1094
1009
 
1095
1010
  if (!videoUrl) {
1096
- // Fallback para estrutura antiga ou diferente
1097
1011
  if (Array.isArray(data) && data[0] && data[0].url) {
1098
1012
  videoUrl = data[0].url;
1099
1013
  } else if (data && data.HD) {
@@ -1102,7 +1016,7 @@ async function downloadSmartVideo(url, config) {
1102
1016
  }
1103
1017
 
1104
1018
  if (!videoUrl) {
1105
- throw new Error("No video URL found in btch-downloader twitter response");
1019
+ throw new Error("No video URL found in downloader twitter response");
1106
1020
  }
1107
1021
  } catch (error) {
1108
1022
  throw new Error(`Twitter download failed: ${error.message}`);
@@ -1528,7 +1442,7 @@ const AudioDownloader = async (url, options = {}) => {
1528
1442
  try {
1529
1443
  let platform = getPlatformType(url);
1530
1444
  if (platform === "y124outube") {
1531
- // Baixar áudio do YouTube usando ytdlp-nodejs
1445
+
1532
1446
  let fileName = "temp_audio.mp3";
1533
1447
  let count = 1;
1534
1448
  while (fs.existsSync(fileName)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frostpv",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "description": "downloads",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -12,6 +12,8 @@
12
12
  "author": "Delta",
13
13
  "license": "ISC",
14
14
  "dependencies": {
15
+ "@tobyg74/tiktok-api-dl": "^1.3.7",
16
+ "ab-downloader": "^1.0.1",
15
17
  "axios": "1.12.0",
16
18
  "btch-downloader": "6.0.25",
17
19
  "btch-downloader-old": "npm:btch-downloader@4.0.15",
@@ -21,8 +23,6 @@
21
23
  "fs": "^0.0.1-security",
22
24
  "path": "^0.12.7",
23
25
  "twitter-downloader": "^1.1.8",
24
- "uuid": "^11.1.0",
25
- "@tobyg74/tiktok-api-dl": "^1.3.7",
26
- "ytdlp-nodejs": "2.3.4"
26
+ "uuid": "^11.1.0"
27
27
  }
28
- }
28
+ }