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.
- package/index.js +61 -96
- 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
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
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
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
1115
|
-
|
|
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
|
|
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)}
|
|
1131
|
+
let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.mp4`);
|
|
1155
1132
|
|
|
1156
|
-
const
|
|
1157
|
-
response.data.pipe(
|
|
1133
|
+
const videoWriter = fs.createWriteStream(fileName);
|
|
1134
|
+
response.data.pipe(videoWriter);
|
|
1158
1135
|
|
|
1159
1136
|
return new Promise((resolve, reject) => {
|
|
1160
|
-
|
|
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
|
|
1142
|
+
reject(new Error(`Error processing downloaded video: ${error.message}`));
|
|
1166
1143
|
}
|
|
1167
1144
|
});
|
|
1168
|
-
|
|
1169
|
-
reject(new Error(`Error saving
|
|
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)}
|
|
1168
|
+
let fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0, 4)}.mp4`);
|
|
1204
1169
|
|
|
1205
|
-
const
|
|
1206
|
-
response.data.pipe(
|
|
1170
|
+
const videoWriter = fs.createWriteStream(fileName);
|
|
1171
|
+
response.data.pipe(videoWriter);
|
|
1207
1172
|
|
|
1208
1173
|
return new Promise((resolve, reject) => {
|
|
1209
|
-
|
|
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
|
|
1179
|
+
reject(new Error(`Error processing downloaded video: ${error.message}`));
|
|
1215
1180
|
}
|
|
1216
1181
|
});
|
|
1217
|
-
|
|
1218
|
-
reject(new Error(`Error saving
|
|
1182
|
+
videoWriter.on("error", (error) => {
|
|
1183
|
+
reject(new Error(`Error saving video: ${error.message}`));
|
|
1219
1184
|
});
|
|
1220
1185
|
});
|
|
1221
1186
|
} catch (error) {
|