frostpv 1.0.12 → 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 +61 -96
  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");
@@ -886,25 +886,16 @@ const MediaDownloader = async (url, options = {}) => {
886
886
  // Function to process downloaded file
887
887
  async function processDownloadedFile(fileName, config, platform = null) {
888
888
  let processedFile = path.resolve(fileName);
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
- }
889
+ if (config.rotation) {
890
+ processedFile = await rotateVideo(processedFile, config.rotation);
891
+ }
892
+ if (config.autocrop) {
893
+ processedFile = await autoCrop(processedFile);
894
+ }
895
+ processedFile = await checkAndCompressVideo(processedFile, config.limitSizeMB, platform);
896
+ if (config.outputFormat) {
897
+ processedFile = await convertVideoFormat(processedFile, String(config.outputFormat).toLowerCase());
906
898
  }
907
-
908
899
  return processedFile;
909
900
  }
910
901
 
@@ -1072,47 +1063,45 @@ async function downloadSmartVideo(url, config) {
1072
1063
  }
1073
1064
  case "twitter": {
1074
1065
  try {
1075
- // Validate and clean the Twitter URL
1076
- const cleanUrl = validateTwitterUrl(url);
1077
- const data = await TwitterDL(cleanUrl, {});
1078
-
1079
- if (data && data.result) {
1080
- // Try multiple possible response structures
1081
- if (data.result.media && data.result.media[0] &&
1082
- data.result.media[0].videos && data.result.media[0].videos[0] &&
1083
- data.result.media[0].videos[0].url) {
1084
- videoUrl = data.result.media[0].videos[0].url;
1085
- } else if (data.result.video && data.result.video[0] && data.result.video[0].url) {
1086
- videoUrl = data.result.video[0].url;
1087
- } else if (data.result.url) {
1088
- videoUrl = data.result.url;
1089
- } else if (data.result.media && data.result.media[0] && data.result.media[0].url) {
1090
- videoUrl = data.result.media[0].url;
1091
- } else {
1092
- // Search for any video URL in the response
1093
- const findVideoUrl = (obj) => {
1094
- if (typeof obj === 'string' && obj.includes('http') &&
1095
- (obj.includes('.mp4') || obj.includes('video') || obj.includes('media'))) {
1096
- return obj;
1097
- }
1098
- if (typeof obj === 'object' && obj !== null) {
1099
- for (const key in obj) {
1100
- const result = findVideoUrl(obj[key]);
1101
- if (result) return result;
1102
- }
1103
- }
1104
- return null;
1105
- };
1106
-
1107
- const foundUrl = findVideoUrl(data.result);
1108
- if (foundUrl) {
1109
- 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;
1110
1074
  } else {
1111
- 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
+ }
1112
1088
  }
1089
+ } else if (typeof data.url === 'string') {
1090
+ videoUrl = data.url;
1113
1091
  }
1114
- } else {
1115
- 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");
1116
1105
  }
1117
1106
  } catch (error) {
1118
1107
  throw new Error(`Twitter download failed: ${error.message}`);
@@ -1127,7 +1116,7 @@ async function downloadSmartVideo(url, config) {
1127
1116
  throw new Error("Returned video URL is invalid or unavailable.");
1128
1117
  }
1129
1118
 
1130
- // Download the video/image with better error handling
1119
+ // Download the video with better error handling
1131
1120
  const response = await axios({
1132
1121
  url: videoUrl,
1133
1122
  method: "GET",
@@ -1138,35 +1127,23 @@ async function downloadSmartVideo(url, config) {
1138
1127
  }
1139
1128
  });
1140
1129
 
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
-
1153
1130
  // Create minimal unique file name in output dir
1154
- let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.${ext}`);
1131
+ let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.mp4`);
1155
1132
 
1156
- const writer = fs.createWriteStream(fileName);
1157
- response.data.pipe(writer);
1133
+ const videoWriter = fs.createWriteStream(fileName);
1134
+ response.data.pipe(videoWriter);
1158
1135
 
1159
1136
  return new Promise((resolve, reject) => {
1160
- writer.on("finish", async () => {
1137
+ videoWriter.on("finish", async () => {
1161
1138
  try {
1162
1139
  const finalFilePath = await processDownloadedFile(fileName, config, platform);
1163
1140
  resolve(finalFilePath);
1164
1141
  } catch (error) {
1165
- reject(new Error(`Error processing downloaded file: ${error.message}`));
1142
+ reject(new Error(`Error processing downloaded video: ${error.message}`));
1166
1143
  }
1167
1144
  });
1168
- writer.on("error", (error) => {
1169
- reject(new Error(`Error saving file: ${error.message}`));
1145
+ videoWriter.on("error", (error) => {
1146
+ reject(new Error(`Error saving video: ${error.message}`));
1170
1147
  });
1171
1148
  });
1172
1149
  } catch (error) {
@@ -1187,35 +1164,23 @@ async function downloadDirectVideo(url, config) {
1187
1164
  }
1188
1165
  });
1189
1166
 
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
-
1202
1167
  // Create minimal unique file name in output dir
1203
- let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.${ext}`);
1168
+ let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.mp4`);
1204
1169
 
1205
- const writer = fs.createWriteStream(fileName);
1206
- response.data.pipe(writer);
1170
+ const videoWriter = fs.createWriteStream(fileName);
1171
+ response.data.pipe(videoWriter);
1207
1172
 
1208
1173
  return new Promise((resolve, reject) => {
1209
- writer.on("finish", async () => {
1174
+ videoWriter.on("finish", async () => {
1210
1175
  try {
1211
1176
  const finalFilePath = await processDownloadedFile(fileName, config, "unknown");
1212
1177
  resolve(finalFilePath);
1213
1178
  } catch (error) {
1214
- reject(new Error(`Error processing downloaded file: ${error.message}`));
1179
+ reject(new Error(`Error processing downloaded video: ${error.message}`));
1215
1180
  }
1216
1181
  });
1217
- writer.on("error", (error) => {
1218
- reject(new Error(`Error saving file: ${error.message}`));
1182
+ videoWriter.on("error", (error) => {
1183
+ reject(new Error(`Error saving video: ${error.message}`));
1219
1184
  });
1220
1185
  });
1221
1186
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frostpv",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "downloads",
5
5
  "main": "index.js",
6
6
  "scripts": {