frostpv 1.0.1 → 1.0.3

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 +124 -11
  2. package/package.json +2 -2
package/index.js CHANGED
@@ -58,7 +58,10 @@ const videoPlatforms = [
58
58
  "https://twitter.com",
59
59
  "https://www.twitter.com",
60
60
  "https://vm.tiktok.com/",
61
- "https://vt.tiktok.com/"
61
+ "https://vt.tiktok.com/",
62
+ "https://www.pinterest.com",
63
+ "https://pinterest.com",
64
+ "https://pin.it"
62
65
  ];
63
66
 
64
67
  // Blacklist links
@@ -364,6 +367,7 @@ function getPlatformType(url) {
364
367
  if (lowerUrl.includes("facebook.com") || lowerUrl.includes("fb.watch") || lowerUrl.includes("fb.com")) return "facebook";
365
368
  if (lowerUrl.includes("mediafire.com")) return "mediafire";
366
369
  if (lowerUrl.includes("x.com") || lowerUrl.includes("twitter.com")) return "twitter";
370
+ if (lowerUrl.includes("pinterest.com") || lowerUrl.includes("pin.it")) return "pinterest";
367
371
  if (lowerUrl.includes("youtube.com") || lowerUrl.includes("you112t12u.be") || lowerUrl.includes("m.y11outu314be.com")) return "youtu1354be";
368
372
 
369
373
  return "unknown";
@@ -718,6 +722,32 @@ async function tryFallbackDownload(url, maxRetries = 3) {
718
722
  }
719
723
  break;
720
724
  }
725
+ case "pinterest": {
726
+ try {
727
+ const data = await pinterest(url);
728
+ let mediaUrl = null;
729
+ const pickFrom = (obj) => {
730
+ if (!obj) return null;
731
+ if (typeof obj === 'string' && obj.startsWith('http')) return obj;
732
+ if (Array.isArray(obj)) {
733
+ for (const it of obj) {
734
+ const got = pickFrom(it);
735
+ if (got) return got;
736
+ }
737
+ } else if (typeof obj === 'object') {
738
+ const candidates = ['video', 'videoUrl', 'download', 'url', 'image', 'imageUrl', 'src', 'link'];
739
+ for (const k of candidates) {
740
+ const got = pickFrom(obj[k]);
741
+ if (got) return got;
742
+ }
743
+ }
744
+ return null;
745
+ };
746
+ mediaUrl = pickFrom(data);
747
+ if (mediaUrl) return mediaUrl;
748
+ } catch (_) {}
749
+ break;
750
+ }
721
751
 
722
752
  default: {
723
753
  // Generic fallback for unknown platforms
@@ -790,12 +820,7 @@ const MediaDownloader = async (url, options = {}) => {
790
820
  if (url.includes("threads.com")) {
791
821
  throw new Error("Threads links are not supported for download. Download videos from Instagram, X, TikTok, and Facebook");
792
822
  }
793
- if (url.includes("pinterest.com")) {
794
- throw new Error("Pinterest links are not supported for download. Download videos from Instagram, X, TikTok, and Facebook");
795
- }
796
- if (url.includes("pin.it")) {
797
- throw new Error("Pinterest links are not supported for download. Download videos from Instagram, X, TikTok, and Facebook");
798
- }
823
+ // Pinterest now supported
799
824
 
800
825
  const blacklisted = blacklistLink(url);
801
826
  if (blacklisted) {
@@ -804,13 +829,13 @@ const MediaDownloader = async (url, options = {}) => {
804
829
 
805
830
  // Verificar se o link está na lista de plataformas suportadas
806
831
  if (!isVideoLink(url)) {
807
- throw new Error("This URL is not from a supported platform. Supported platforms: Instagram, X(Twitter), TikTok and Facebook");
832
+ throw new Error("This URL is not from a supported platform. Supported platforms: Instagram, X(Twitter), TikTok, Facebook, and Pinterest");
808
833
  }
809
834
 
810
835
  // Verificar se é YouTube e lançar erro customizado
811
836
  const platform = getPlatformType(url);
812
837
  if (platform === "youtube") {
813
- throw new Error("This URL is not from a supported platform. Supported platforms: Instagram, X(Twitter), TikTok and Facebook");
838
+ throw new Error("This URL is not from a supported platform. Supported platforms: Instagram, X(Twitter), TikTok, Facebook, and Pinterest");
814
839
  }
815
840
 
816
841
  await cleanupTempFiles(); // Clean up previous temp files
@@ -989,6 +1014,52 @@ async function downloadSmartVideo(url, config) {
989
1014
  }
990
1015
  break;
991
1016
  }
1017
+ case "pinterest": {
1018
+ try {
1019
+ // Supports both pin links and search queries
1020
+ const data = await pinterest(url);
1021
+ // data may be array or object depending on query or pin
1022
+ // Try to find best url (image or video)
1023
+ let directUrl = null;
1024
+ const pickFrom = (obj) => {
1025
+ if (!obj) return null;
1026
+ if (typeof obj === 'string' && obj.startsWith('http')) return obj;
1027
+ if (Array.isArray(obj)) {
1028
+ for (const it of obj) {
1029
+ const got = pickFrom(it);
1030
+ if (got) return got;
1031
+ }
1032
+ } else if (typeof obj === 'object') {
1033
+ const candidates = ['video', 'videoUrl', 'download', 'url', 'image', 'imageUrl', 'src', 'link'];
1034
+ for (const k of candidates) {
1035
+ const got = pickFrom(obj[k]);
1036
+ if (got) return got;
1037
+ }
1038
+ }
1039
+ return null;
1040
+ };
1041
+ directUrl = pickFrom(data);
1042
+ if (!directUrl) throw new Error("No media URL found in Pinterest response");
1043
+
1044
+ // HEAD validate
1045
+ const head = await axios.head(directUrl, { timeout: 10000, maxRedirects: 5 }).catch(() => null);
1046
+ if (!head || (head.status < 200 || head.status >= 400)) {
1047
+ throw new Error("Pinterest media URL not accessible");
1048
+ }
1049
+
1050
+ // If it's an image, download with generic file helper; if video, continue normal flow
1051
+ const ct = (head.headers && head.headers['content-type']) || '';
1052
+ if (ct.startsWith('image/')) {
1053
+ const filePath = await downloadGenericFile(directUrl);
1054
+ return filePath;
1055
+ } else {
1056
+ videoUrl = directUrl;
1057
+ }
1058
+ } catch (error) {
1059
+ throw new Error(`Pinterest download failed: ${error.message}`);
1060
+ }
1061
+ break;
1062
+ }
992
1063
  case "twitter": {
993
1064
  try {
994
1065
  // Validate and clean the Twitter URL
@@ -1118,6 +1189,45 @@ async function downloadDirectVideo(url, config) {
1118
1189
  }
1119
1190
  }
1120
1191
 
1192
+ // Generic file (image/video) downloader
1193
+ async function downloadGenericFile(url, preferredExt = null) {
1194
+ try {
1195
+ const response = await axios({
1196
+ url: url,
1197
+ method: "GET",
1198
+ responseType: "stream",
1199
+ timeout: 30000,
1200
+ headers: {
1201
+ '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'
1202
+ }
1203
+ });
1204
+
1205
+ // Try to infer extension from content-type
1206
+ const ct = (response.headers && response.headers['content-type']) || '';
1207
+ let ext = preferredExt;
1208
+ if (!ext) {
1209
+ if (ct.includes('image/jpeg')) ext = 'jpg';
1210
+ else if (ct.includes('image/png')) ext = 'png';
1211
+ else if (ct.includes('image/webp')) ext = 'webp';
1212
+ else if (ct.includes('image/gif')) ext = 'gif';
1213
+ else if (ct.includes('video/mp4')) ext = 'mp4';
1214
+ else if (ct.includes('video/webm')) ext = 'webm';
1215
+ else ext = 'bin';
1216
+ }
1217
+
1218
+ const fileName = path.join(OUTPUT_DIR, `v_${uuidv4().slice(0,4)}.${ext}`);
1219
+ const writer = fs.createWriteStream(fileName);
1220
+ response.data.pipe(writer);
1221
+
1222
+ return await new Promise((resolve, reject) => {
1223
+ writer.on('finish', () => resolve(fileName));
1224
+ writer.on('error', (err) => reject(new Error(`Error saving file: ${err.message}`)));
1225
+ });
1226
+ } catch (error) {
1227
+ throw new Error(`Failed to download file: ${error.message}`);
1228
+ }
1229
+ }
1230
+
1121
1231
  // Function to rotate video
1122
1232
  async function rotateVideo(fileName, rotation) {
1123
1233
  const outputPath = path.join(OUTPUT_DIR, `vr_${uuidv4().slice(0,4)}.mp4`);
@@ -1349,8 +1459,11 @@ function getFileName(url) {
1349
1459
  // Function to unshorten URLs
1350
1460
  async function unshortenUrl(url) {
1351
1461
  try {
1352
- // Special handling for Facebook URLs
1353
- if (url.includes('facebook.com') || url.includes('fb.watch') || url.includes('fb.com')) {
1462
+ // Special handling for Facebook and Pinterest short-links
1463
+ if (
1464
+ url.includes('facebook.com') || url.includes('fb.watch') || url.includes('fb.com') ||
1465
+ url.includes('pin.it') || url.includes('pinterest.com')
1466
+ ) {
1354
1467
  const response = await axios.get(url, {
1355
1468
  maxRedirects: 10,
1356
1469
  timeout: 10000,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frostpv",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
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": "4.1.27-lts",
16
+ "btch-downloader": "5.0.14",
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",