frostpv 1.0.10 → 1.0.12

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 +65 -161
  2. package/package.json +3 -3
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, youtube } = require("btch-downloader");
7
+ const { igdl, ttdl, fbdown, mediafire, capcut, gdrive, pinterest } = 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");
@@ -58,13 +58,7 @@ const videoPlatforms = [
58
58
  "https://twitter.com",
59
59
  "https://www.twitter.com",
60
60
  "https://vm.tiktok.com/",
61
- "https://vt.tiktok.com/",
62
- "https://www.youtube.com",
63
- "https://youtube.com",
64
- "https://youtu.be",
65
- "https://m.youtube.com",
66
- "https://www.youtube.com/watch?"
67
-
61
+ "https://vt.tiktok.com/"
68
62
  ];
69
63
 
70
64
  // Blacklist links
@@ -375,39 +369,11 @@ function getPlatformType(url) {
375
369
  if (lowerUrl.includes("facebook.com") || lowerUrl.includes("fb.watch") || lowerUrl.includes("fb.com")) return "facebook";
376
370
  if (lowerUrl.includes("mediafire.com")) return "mediafire";
377
371
  if (lowerUrl.includes("x.com") || lowerUrl.includes("twitter.com")) return "twitter";
378
- if (lowerUrl.includes("youtube.com") || lowerUrl.includes("youtu.be") || lowerUrl.includes("m.youtube.com")) return "youtube";
372
+ if (lowerUrl.includes("youtube.com") || lowerUrl.includes("you112t12u.be") || lowerUrl.includes("m.y11outu314be.com")) return "youtu1354be";
379
373
 
380
374
  return "unknown";
381
375
  }
382
376
 
383
- // Function to get direct video URL from Twitter using yt-dlp
384
- async function downloadTwitterWithYtDlp(url) {
385
- return new Promise(async (resolve, reject) => {
386
- try {
387
- const ytdlp = new YtDlp({ ffmpegPath });
388
- // Use -g to get URL, -f "best[ext=mp4]/best" to get best quality single file if possible
389
- const args = [
390
- url,
391
- '-g',
392
- '-f', 'best[ext=mp4]/best'
393
- ];
394
-
395
- const result = await ytdlp.execAsync(args);
396
- // yt-dlp might return multiple lines if it finds separate audio/video streams
397
- // We take the first one, but for Twitter it is usually a single mp4 file
398
- const videoUrl = result.trim().split('\n')[0];
399
-
400
- if (videoUrl && videoUrl.startsWith('http')) {
401
- resolve(videoUrl);
402
- } else {
403
- reject(new Error('yt-dlp did not return a valid URL'));
404
- }
405
- } catch (error) {
406
- reject(error);
407
- }
408
- });
409
- }
410
-
411
377
  // Enhanced fallback download function with platform-specific methods
412
378
  async function tryFallbackDownload(url, maxRetries = 3) {
413
379
  const platform = getPlatformType(url);
@@ -555,12 +521,8 @@ async function tryFallbackDownload(url, maxRetries = 3) {
555
521
  }
556
522
 
557
523
  case "twitter": {
558
- // Try using yt-dlp first (Most reliable), then fallback to existing methods
524
+ // Try multiple Twitter methods with better response handling
559
525
  const methods = [
560
- async () => {
561
- // Priority 1: yt-dlp
562
- return await downloadTwitterWithYtDlp(url);
563
- },
564
526
  async () => {
565
527
  const cleanUrl = validateTwitterUrl(url);
566
528
  const data = await TwitterDL(cleanUrl, {});
@@ -873,8 +835,9 @@ const MediaDownloader = async (url, options = {}) => {
873
835
 
874
836
  // Verificar se é YouTube e lançar erro customizado
875
837
  const platform = getPlatformType(url);
876
- // YouTube is now supported
877
-
838
+ if (platform === "youtube") {
839
+ throw new Error("This URL is not from a supported platform. Supported platforms: Instagram, X(Twitter), TikTok, Facebook");
840
+ }
878
841
 
879
842
  await cleanupTempFiles(); // Clean up previous temp files
880
843
 
@@ -887,7 +850,7 @@ const MediaDownloader = async (url, options = {}) => {
887
850
  } catch (error) {
888
851
  const platform = getPlatformType(url);
889
852
 
890
- // YouTube doesn't use fallback method - it has its own specific implementation in downloadSmartVideo
853
+ // YouTube doesn't use fallback method - it has its own specific implementation
891
854
  if (platform === "youtube") {
892
855
  throw new Error(`YouTube download failed: ${error.message}`);
893
856
  }
@@ -923,16 +886,25 @@ const MediaDownloader = async (url, options = {}) => {
923
886
  // Function to process downloaded file
924
887
  async function processDownloadedFile(fileName, config, platform = null) {
925
888
  let processedFile = path.resolve(fileName);
926
- if (config.rotation) {
927
- processedFile = await rotateVideo(processedFile, config.rotation);
928
- }
929
- if (config.autocrop) {
930
- processedFile = await autoCrop(processedFile);
931
- }
932
- processedFile = await checkAndCompressVideo(processedFile, config.limitSizeMB, platform);
933
- if (config.outputFormat) {
934
- processedFile = await convertVideoFormat(processedFile, String(config.outputFormat).toLowerCase());
889
+
890
+ // Check if file is video or image based on extension
891
+ const ext = path.extname(processedFile).toLowerCase();
892
+ const isVideo = ['.mp4', '.mov', '.webm', '.mkv'].includes(ext);
893
+
894
+ // Only apply video processing if it is a video
895
+ if (isVideo) {
896
+ if (config.rotation) {
897
+ processedFile = await rotateVideo(processedFile, config.rotation);
898
+ }
899
+ if (config.autocrop) {
900
+ processedFile = await autoCrop(processedFile);
901
+ }
902
+ processedFile = await checkAndCompressVideo(processedFile, config.limitSizeMB, platform);
903
+ if (config.outputFormat) {
904
+ processedFile = await convertVideoFormat(processedFile, String(config.outputFormat).toLowerCase());
905
+ }
935
906
  }
907
+
936
908
  return processedFile;
937
909
  }
938
910
 
@@ -1147,98 +1119,6 @@ async function downloadSmartVideo(url, config) {
1147
1119
  }
1148
1120
  break;
1149
1121
  }
1150
- case "youtube": {
1151
- try {
1152
- const data = await youtube(url);
1153
- // Try to find the video URL in the response
1154
-
1155
- if (!data) throw new Error("No data returned from youtube downloader");
1156
-
1157
- // Helper to check if a value looks like a video URL
1158
- const isVideoUrl = (val) => typeof val === 'string' && val.startsWith('http') && !val.includes('.mp3');
1159
-
1160
- let bestUrl = null;
1161
- let bestScore = -1;
1162
-
1163
- // Function to score keys based on quality
1164
- const getScore = (key) => {
1165
- const k = key.toLowerCase();
1166
- if (k.includes('1080')) return 100;
1167
- if (k.includes('720')) return 80;
1168
- if (k.includes('480')) return 60;
1169
- if (k.includes('hd')) return 90;
1170
- if (k.includes('sd')) return 50;
1171
- if (k.includes('360')) return 40;
1172
- if (k.includes('mp4')) return 30;
1173
- if (k.includes('video')) return 20;
1174
- return 10;
1175
- };
1176
-
1177
- // Recursive function to gather all candidates
1178
- const gatherCandidates = (obj) => {
1179
- if (!obj) return;
1180
-
1181
- if (typeof obj === 'object') {
1182
- for (const key in obj) {
1183
- const val = obj[key];
1184
- if (isVideoUrl(val)) {
1185
- const score = getScore(key);
1186
- if (score > bestScore) {
1187
- bestScore = score;
1188
- bestUrl = val;
1189
- }
1190
- } else if (typeof val === 'object') {
1191
- // Check if this object represents a format (e.g. { quality: '720p', url: '...' })
1192
- if (val.url && isVideoUrl(val.url)) {
1193
- let score = getScore(key); // Score from key name
1194
- if (val.quality || val.resolution) {
1195
- score = Math.max(score, getScore(String(val.quality || val.resolution)));
1196
- }
1197
- if (score > bestScore) {
1198
- bestScore = score;
1199
- bestUrl = val.url;
1200
- }
1201
- }
1202
- // Recurse
1203
- gatherCandidates(val);
1204
- }
1205
- }
1206
- }
1207
- };
1208
-
1209
- gatherCandidates(data);
1210
-
1211
- // If no scored candidate found, try direct simple extraction with priority keys
1212
- if (!bestUrl) {
1213
- const priorities = ['mp4', 'url', 'link', 'download', 'video'];
1214
- const findSimple = (obj) => {
1215
- if (!obj) return null;
1216
- for (const key of priorities) {
1217
- if (obj[key] && isVideoUrl(obj[key])) return obj[key];
1218
- }
1219
- for (const key in obj) {
1220
- if (typeof obj[key] === 'object') {
1221
- const found = findSimple(obj[key]);
1222
- if (found) return found;
1223
- }
1224
- }
1225
- return null;
1226
- };
1227
- bestUrl = findSimple(data);
1228
- }
1229
-
1230
- videoUrl = bestUrl;
1231
-
1232
- if (!videoUrl) {
1233
- console.log('YouTube data dump:', JSON.stringify(data, null, 2)); // Debug log since we can't test
1234
- throw new Error("Could not extract video URL from YouTube response");
1235
- }
1236
-
1237
- } catch (error) {
1238
- throw new Error(`YouTube download failed: ${error.message}`);
1239
- }
1240
- break;
1241
- }
1242
1122
  default:
1243
1123
  throw new Error("Platform not supported or invalid link.");
1244
1124
  }
@@ -1247,7 +1127,7 @@ async function downloadSmartVideo(url, config) {
1247
1127
  throw new Error("Returned video URL is invalid or unavailable.");
1248
1128
  }
1249
1129
 
1250
- // Download the video with better error handling
1130
+ // Download the video/image with better error handling
1251
1131
  const response = await axios({
1252
1132
  url: videoUrl,
1253
1133
  method: "GET",
@@ -1258,23 +1138,35 @@ async function downloadSmartVideo(url, config) {
1258
1138
  }
1259
1139
  });
1260
1140
 
1141
+ // Detect file type from content-type
1142
+ const contentType = response.headers['content-type'] || 'video/mp4';
1143
+ let ext = 'mp4'; // default
1144
+
1145
+ if (contentType.includes('image/jpeg')) ext = 'jpg';
1146
+ else if (contentType.includes('image/png')) ext = 'png';
1147
+ else if (contentType.includes('image/webp')) ext = 'webp';
1148
+ else if (contentType.includes('image/gif')) ext = 'gif';
1149
+ else if (contentType.includes('video/webm')) ext = 'webm';
1150
+ else if (contentType.includes('video/quicktime')) ext = 'mov';
1151
+ else if (contentType.includes('video/x-matroska')) ext = 'mkv';
1152
+
1261
1153
  // Create minimal unique file name in output dir
1262
- let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.mp4`);
1154
+ let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.${ext}`);
1263
1155
 
1264
- const videoWriter = fs.createWriteStream(fileName);
1265
- response.data.pipe(videoWriter);
1156
+ const writer = fs.createWriteStream(fileName);
1157
+ response.data.pipe(writer);
1266
1158
 
1267
1159
  return new Promise((resolve, reject) => {
1268
- videoWriter.on("finish", async () => {
1160
+ writer.on("finish", async () => {
1269
1161
  try {
1270
1162
  const finalFilePath = await processDownloadedFile(fileName, config, platform);
1271
1163
  resolve(finalFilePath);
1272
1164
  } catch (error) {
1273
- reject(new Error(`Error processing downloaded video: ${error.message}`));
1165
+ reject(new Error(`Error processing downloaded file: ${error.message}`));
1274
1166
  }
1275
1167
  });
1276
- videoWriter.on("error", (error) => {
1277
- reject(new Error(`Error saving video: ${error.message}`));
1168
+ writer.on("error", (error) => {
1169
+ reject(new Error(`Error saving file: ${error.message}`));
1278
1170
  });
1279
1171
  });
1280
1172
  } catch (error) {
@@ -1295,23 +1187,35 @@ async function downloadDirectVideo(url, config) {
1295
1187
  }
1296
1188
  });
1297
1189
 
1190
+ // Detect file type from content-type
1191
+ const contentType = response.headers['content-type'] || 'video/mp4';
1192
+ let ext = 'mp4'; // default
1193
+
1194
+ if (contentType.includes('image/jpeg')) ext = 'jpg';
1195
+ else if (contentType.includes('image/png')) ext = 'png';
1196
+ else if (contentType.includes('image/webp')) ext = 'webp';
1197
+ else if (contentType.includes('image/gif')) ext = 'gif';
1198
+ else if (contentType.includes('video/webm')) ext = 'webm';
1199
+ else if (contentType.includes('video/quicktime')) ext = 'mov';
1200
+ else if (contentType.includes('video/x-matroska')) ext = 'mkv';
1201
+
1298
1202
  // Create minimal unique file name in output dir
1299
- let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.mp4`);
1203
+ let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.${ext}`);
1300
1204
 
1301
- const videoWriter = fs.createWriteStream(fileName);
1302
- response.data.pipe(videoWriter);
1205
+ const writer = fs.createWriteStream(fileName);
1206
+ response.data.pipe(writer);
1303
1207
 
1304
1208
  return new Promise((resolve, reject) => {
1305
- videoWriter.on("finish", async () => {
1209
+ writer.on("finish", async () => {
1306
1210
  try {
1307
1211
  const finalFilePath = await processDownloadedFile(fileName, config, "unknown");
1308
1212
  resolve(finalFilePath);
1309
1213
  } catch (error) {
1310
- reject(new Error(`Error processing downloaded video: ${error.message}`));
1214
+ reject(new Error(`Error processing downloaded file: ${error.message}`));
1311
1215
  }
1312
1216
  });
1313
- videoWriter.on("error", (error) => {
1314
- reject(new Error(`Error saving video: ${error.message}`));
1217
+ writer.on("error", (error) => {
1218
+ reject(new Error(`Error saving file: ${error.message}`));
1315
1219
  });
1316
1220
  });
1317
1221
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frostpv",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "downloads",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -13,7 +13,7 @@
13
13
  "license": "ISC",
14
14
  "dependencies": {
15
15
  "axios": "1.12.0",
16
- "btch-downloader": "6.0.23",
16
+ "btch-downloader": "6.0.25",
17
17
  "btch-downloader-old": "npm:btch-downloader@4.0.15",
18
18
  "express": "^5.1.0",
19
19
  "ffmpeg-ffprobe-static": "^6.1.1-rc.5",
@@ -22,7 +22,7 @@
22
22
  "path": "^0.12.7",
23
23
  "twitter-downloader": "^1.1.8",
24
24
  "uuid": "^11.1.0",
25
- "@tobyg74/tiktok-api-dl": "^1.3.2",
25
+ "@tobyg74/tiktok-api-dl": "^1.3.7",
26
26
  "ytdlp-nodejs": "2.3.4"
27
27
  }
28
28
  }