shadowx-fbdl 1.0.0

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 (3) hide show
  1. package/README.md +86 -0
  2. package/index.js +384 -0
  3. package/package.json +49 -0
package/README.md ADDED
@@ -0,0 +1,86 @@
1
+ shadowx-fbdl
2
+
3
+ πŸ€–DEV : Mueid Mursalin Rifat
4
+
5
+ πŸš€ Simple Facebook video downloader - Supports FB, FB Watch, Reels & Shares
6
+
7
+ πŸ“¦ Install
8
+
9
+ ```bash
10
+ npm install shadowx-fbdl
11
+ ```
12
+
13
+ πŸš€ Quick Usage
14
+
15
+ ```javascript
16
+ const ShadowXFB = require('shadowx-fbdl');
17
+ const fbdl = new ShadowXFB();
18
+
19
+ // Download Facebook video
20
+ fbdl.download('https://fb.watch/abc123/')
21
+ .then(result => {
22
+ console.log('Title:', result.video.title);
23
+ console.log('Download URL:', result.download.url);
24
+ })
25
+ .catch(error => console.log(error.message));
26
+ ```
27
+
28
+ πŸ“‹ Examples
29
+
30
+ Get video info only
31
+
32
+ ```javascript
33
+ const info = await fbdl.getInfo('https://fb.watch/abc123/');
34
+ ```
35
+
36
+ Get direct download URL
37
+
38
+ ```javascript
39
+ const url = await fbdl.getDownloadUrl('https://fb.watch/abc123/');
40
+ ```
41
+
42
+ Check if URL is valid
43
+
44
+ ```javascript
45
+ if (fbdl.isValidUrl('https://fb.watch/abc123/')) {
46
+ console.log('βœ… Valid Facebook URL');
47
+ }
48
+ ```
49
+
50
+ πŸ”— Supported URLs
51
+
52
+ Β· https://www.facebook.com/watch?v=ID
53
+ Β· https://fb.watch/ID/
54
+ Β· https://www.facebook.com/reel/ID
55
+ Β· https://www.facebook.com/share/v/ID/
56
+
57
+ πŸ“€ Response Example
58
+
59
+ ```javascript
60
+ {
61
+ video: {
62
+ title: "Video Title",
63
+ duration: "2:30",
64
+ uploader: "Page Name",
65
+ views: "1.2M"
66
+ },
67
+ download: {
68
+ quality: "HD",
69
+ size: "5.2 MB",
70
+ url: "https://.../video.mp4"
71
+ }
72
+ }
73
+ ```
74
+
75
+ βš™οΈ Options
76
+
77
+ ```javascript
78
+ const fbdl = new ShadowXFB({
79
+ timeout: 30000, // Timeout in ms
80
+ apiKey: 'shadowx' // API key
81
+ });
82
+ ```
83
+
84
+ πŸ“ License
85
+
86
+ MIT Β©
package/index.js ADDED
@@ -0,0 +1,384 @@
1
+ const axios = require('axios');
2
+
3
+ class ShadowXFB {
4
+ constructor(options = {}) {
5
+ this.baseURL = options.baseURL || 'https://shadowx-downloader.vercel.app';
6
+ this.apiKey = options.apiKey || 'shadowx';
7
+ this.timeout = options.timeout || 30000;
8
+ this.userAgent = options.userAgent || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
9
+
10
+ // Create axios instance with default config
11
+ this.client = axios.create({
12
+ baseURL: this.baseURL,
13
+ timeout: this.timeout,
14
+ headers: {
15
+ 'User-Agent': this.userAgent,
16
+ 'Accept': 'application/json'
17
+ }
18
+ });
19
+ }
20
+
21
+ /**
22
+ * Get package metadata
23
+ * @returns {Object} Metadata information
24
+ */
25
+ static getMeta() {
26
+ return {
27
+ name: "shadowx-fbdl",
28
+ version: "2.0.0",
29
+ description: "Download videos from Facebook (FB, FB Watch, Reels, Shares)",
30
+ author: "Mueid Mursalin Rifat",
31
+ license: "MIT",
32
+ supported_urls: [
33
+ "https://www.facebook.com/watch?v=VIDEO_ID",
34
+ "https://fb.watch/VIDEO_ID/",
35
+ "https://www.facebook.com/reel/VIDEO_ID",
36
+ "https://www.facebook.com/share/v/VIDEO_ID/",
37
+ "https://www.facebook.com/username/videos/VIDEO_ID"
38
+ ]
39
+ };
40
+ }
41
+
42
+ /**
43
+ * Validate Facebook URL
44
+ * @param {string} url - URL to validate
45
+ * @returns {boolean} True if valid Facebook video URL
46
+ */
47
+ isValidUrl(url) {
48
+ if (!url || typeof url !== 'string') return false;
49
+
50
+ const patterns = [
51
+ /facebook\.com\/.*\/videos/i,
52
+ /facebook\.com\/watch(?:\?v=|.*?\bv=)/i,
53
+ /fb\.watch\//i,
54
+ /facebook\.com\/reel/i,
55
+ /facebook\.com\/share\/v/i,
56
+ /fb\.com\//i,
57
+ /facebook\.com\/[^\/]+\/videos/i,
58
+ /web\.facebook\.com/i
59
+ ];
60
+
61
+ return patterns.some(pattern => pattern.test(url));
62
+ }
63
+
64
+ /**
65
+ * Extract video ID from Facebook URL
66
+ * @param {string} url - Facebook URL
67
+ * @returns {string|null} Video ID or null
68
+ */
69
+ extractVideoId(url) {
70
+ const patterns = [
71
+ /videos[\/=](\d+)/i,
72
+ /watch\?v=(\d+)/i,
73
+ /reel\/(\d+)/i,
74
+ /share\/v\/(\d+)/i,
75
+ /fb\.watch\/([a-zA-Z0-9]+)/i,
76
+ /\/(\d+)\//
77
+ ];
78
+
79
+ for (const pattern of patterns) {
80
+ const match = url.match(pattern);
81
+ if (match) return match[1];
82
+ }
83
+
84
+ return null;
85
+ }
86
+
87
+ /**
88
+ * Download Facebook video
89
+ * @param {string} url - Facebook video URL
90
+ * @param {Object} options - Download options
91
+ * @returns {Promise<Object>} Video information and download links
92
+ */
93
+ async download(url, options = {}) {
94
+ // Validate input
95
+ if (!url) {
96
+ throw new Error('Facebook URL is required');
97
+ }
98
+
99
+ if (!this.isValidUrl(url)) {
100
+ throw new Error('Invalid Facebook URL format. Supported: Watch, Reels, Shares, FB Watch links');
101
+ }
102
+
103
+ const startTime = Date.now();
104
+ const videoId = this.extractVideoId(url);
105
+
106
+ try {
107
+ console.log(`πŸš€ shadowx-fbdl: Fetching video from ${url}`);
108
+
109
+ // Make request to ShadowX API
110
+ const response = await this.client.get('/dl', {
111
+ params: {
112
+ url: url,
113
+ key: options.apiKey || this.apiKey
114
+ },
115
+ timeout: options.timeout || this.timeout
116
+ });
117
+
118
+ const data = response.data;
119
+ const endTime = Date.now();
120
+ const downloadTime = ((endTime - startTime) / 1000).toFixed(2);
121
+
122
+ // Check API response
123
+ if (!data || !data.success) {
124
+ throw new Error(data?.error || 'Failed to fetch video information');
125
+ }
126
+
127
+ // Format the response
128
+ return this._formatResponse(data, url, videoId, downloadTime);
129
+
130
+ } catch (error) {
131
+ throw this._handleError(error, url);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Format API response
137
+ * @param {Object} data - Raw API data
138
+ * @param {string} originalUrl - Original URL
139
+ * @param {string} videoId - Video ID
140
+ * @param {string} downloadTime - Download time
141
+ * @returns {Object} Formatted response
142
+ * @private
143
+ */
144
+ _formatResponse(data, originalUrl, videoId, downloadTime) {
145
+ const baseURL = this.baseURL;
146
+ const downloadUrl = data.download?.url ?
147
+ (data.download.url.startsWith('http') ? data.download.url : `${baseURL}${data.download.url}`) :
148
+ null;
149
+
150
+ // Generate filename
151
+ const filename = data.download?.filename ||
152
+ `facebook_video_${videoId || Date.now()}.mp4`;
153
+
154
+ // Calculate file size in human readable format
155
+ const filesizeBytes = data.download?.filesize_bytes || 0;
156
+ const filesizeMB = filesizeBytes ? (filesizeBytes / (1024 * 1024)).toFixed(2) : 0;
157
+
158
+ return {
159
+ success: true,
160
+ message: 'Video fetched successfully',
161
+
162
+ // Package info
163
+ package: {
164
+ name: 'shadowx-fbdl',
165
+ version: '2.0.0',
166
+ author: 'Mueid Mursalin Rifat'
167
+ },
168
+
169
+ // Video metadata
170
+ video: {
171
+ id: data.id || videoId || 'N/A',
172
+ title: data.title || 'Facebook Video',
173
+ description: data.description || '',
174
+ duration: data.duration || 'Unknown',
175
+ thumbnail: data.thumbnail || null,
176
+ uploader: data.uploader || 'Unknown',
177
+ uploader_id: data.uploader_id || null,
178
+ view_count: this._formatNumber(data.view_count),
179
+ like_count: this._formatNumber(data.like_count),
180
+ comment_count: this._formatNumber(data.comment_count),
181
+ share_count: this._formatNumber(data.share_count),
182
+ was_live: data.was_live || false,
183
+ platform: data.download?.platform || 'Facebook',
184
+ url: data.url || originalUrl,
185
+ created_time: data.created_time || null
186
+ },
187
+
188
+ // Download information
189
+ download: {
190
+ quality: data.download?.quality || 'HD',
191
+ format: data.download?.format || 'mp4',
192
+ size: filesizeMB ? `${filesizeMB} MB` : 'Unknown',
193
+ size_bytes: filesizeBytes,
194
+ url: downloadUrl,
195
+ filename: filename,
196
+ direct_url: downloadUrl, // Alias for url
197
+
198
+ // Additional download info
199
+ expires_in: data.download?.expires_in || null,
200
+ download_time: `${downloadTime}s`
201
+ },
202
+
203
+ // Audio info (if available)
204
+ audio: data.audio_download ? {
205
+ url: data.audio_download.url,
206
+ size: data.audio_download.size,
207
+ format: data.audio_download.format
208
+ } : null,
209
+
210
+ // Additional metadata
211
+ metadata: {
212
+ requested_url: originalUrl,
213
+ extracted_id: videoId,
214
+ timestamp: new Date().toISOString(),
215
+ api_response_time: downloadTime
216
+ },
217
+
218
+ // Raw data for debugging
219
+ raw: data
220
+ };
221
+ }
222
+
223
+ /**
224
+ * Format large numbers with K/M/B suffixes
225
+ * @param {number|string} num - Number to format
226
+ * @returns {string} Formatted number
227
+ * @private
228
+ */
229
+ _formatNumber(num) {
230
+ if (!num && num !== 0) return 'N/A';
231
+ if (typeof num === 'string') num = parseInt(num.replace(/,/g, ''));
232
+ if (isNaN(num)) return 'N/A';
233
+
234
+ if (num >= 1000000000) return (num / 1000000000).toFixed(1) + 'B';
235
+ if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';
236
+ if (num >= 1000) return (num / 1000).toFixed(1) + 'K';
237
+ return num.toString();
238
+ }
239
+
240
+ /**
241
+ * Handle and format errors
242
+ * @param {Error} error - Error object
243
+ * @param {string} url - Original URL
244
+ * @returns {Error} Formatted error
245
+ * @private
246
+ */
247
+ _handleError(error, url) {
248
+ let errorMessage = 'Failed to download Facebook video';
249
+ let statusCode = 500;
250
+ let errorCode = 'UNKNOWN_ERROR';
251
+
252
+ if (error.response) {
253
+ // API responded with error
254
+ statusCode = error.response.status;
255
+ const apiError = error.response.data?.error || error.response.statusText;
256
+
257
+ switch (statusCode) {
258
+ case 400:
259
+ errorMessage = 'Invalid Facebook URL or video not accessible';
260
+ errorCode = 'INVALID_URL';
261
+ break;
262
+ case 401:
263
+ case 403:
264
+ errorMessage = 'Access denied or invalid API key';
265
+ errorCode = 'ACCESS_DENIED';
266
+ break;
267
+ case 404:
268
+ errorMessage = 'Video not found or has been removed';
269
+ errorCode = 'VIDEO_NOT_FOUND';
270
+ break;
271
+ case 429:
272
+ errorMessage = 'Rate limit exceeded. Please try again later';
273
+ errorCode = 'RATE_LIMITED';
274
+ break;
275
+ case 500:
276
+ case 502:
277
+ case 503:
278
+ errorMessage = 'Download service is temporarily unavailable';
279
+ errorCode = 'SERVICE_UNAVAILABLE';
280
+ break;
281
+ default:
282
+ errorMessage = `API Error: ${apiError}`;
283
+ errorCode = 'API_ERROR';
284
+ }
285
+ } else if (error.code === 'ECONNABORTED') {
286
+ errorMessage = 'Request timeout. The server might be busy';
287
+ errorCode = 'TIMEOUT';
288
+ statusCode = 408;
289
+ } else if (error.code === 'ENOTFOUND') {
290
+ errorMessage = 'Network error - cannot connect to download server';
291
+ errorCode = 'NETWORK_ERROR';
292
+ statusCode = 503;
293
+ } else if (error.message.includes('Invalid Facebook URL')) {
294
+ errorMessage = error.message;
295
+ errorCode = 'VALIDATION_ERROR';
296
+ statusCode = 400;
297
+ }
298
+
299
+ const formattedError = new Error(errorMessage);
300
+ formattedError.code = errorCode;
301
+ formattedError.statusCode = statusCode;
302
+ formattedError.url = url;
303
+ formattedError.originalError = error;
304
+ formattedError.timestamp = new Date().toISOString();
305
+ formattedError.suggestion = this._getSuggestion(errorCode);
306
+
307
+ return formattedError;
308
+ }
309
+
310
+ /**
311
+ * Get suggestion based on error code
312
+ * @param {string} errorCode - Error code
313
+ * @returns {string} Suggestion
314
+ * @private
315
+ */
316
+ _getSuggestion(errorCode) {
317
+ const suggestions = {
318
+ 'INVALID_URL': 'Make sure the URL is correct and the video is public',
319
+ 'VIDEO_NOT_FOUND': 'The video may have been deleted or made private',
320
+ 'RATE_LIMITED': 'Wait a few minutes before making more requests',
321
+ 'ACCESS_DENIED': 'Check your API key or try again later',
322
+ 'SERVICE_UNAVAILABLE': 'Try again in a few minutes',
323
+ 'TIMEOUT': 'Try again or use a shorter video',
324
+ 'NETWORK_ERROR': 'Check your internet connection',
325
+ 'VALIDATION_ERROR': 'Use a supported Facebook URL format',
326
+ 'API_ERROR': 'The download service encountered an error',
327
+ 'UNKNOWN_ERROR': 'Please report this issue on GitHub'
328
+ };
329
+
330
+ return suggestions[errorCode] || 'Please check the URL and try again';
331
+ }
332
+
333
+ /**
334
+ * Get video information without download links
335
+ * @param {string} url - Facebook video URL
336
+ * @returns {Promise<Object>} Video metadata
337
+ */
338
+ async getInfo(url) {
339
+ const result = await this.download(url);
340
+ // Remove download URLs from response
341
+ delete result.download.url;
342
+ delete result.download.direct_url;
343
+ result.download.available = true;
344
+ return result;
345
+ }
346
+
347
+ /**
348
+ * Get download URL only
349
+ * @param {string} url - Facebook video URL
350
+ * @param {string} quality - Video quality (hd, sd)
351
+ * @returns {Promise<string>} Direct download URL
352
+ */
353
+ async getDownloadUrl(url, quality = 'hd') {
354
+ const result = await this.download(url);
355
+ return result.download.url;
356
+ }
357
+
358
+ /**
359
+ * Check if URL is downloadable
360
+ * @param {string} url - Facebook URL
361
+ * @returns {Promise<boolean>} True if downloadable
362
+ */
363
+ async isDownloadable(url) {
364
+ try {
365
+ if (!this.isValidUrl(url)) return false;
366
+ const result = await this.getInfo(url);
367
+ return result.success && result.download.available;
368
+ } catch {
369
+ return false;
370
+ }
371
+ }
372
+ }
373
+
374
+ // Export for CommonJS
375
+ module.exports = ShadowXFB;
376
+
377
+ // Export factory function
378
+ module.exports.create = (options) => new ShadowXFB(options);
379
+
380
+ // Export default instance
381
+ module.exports.default = new ShadowXFB();
382
+
383
+ // Export metadata
384
+ module.exports.meta = ShadowXFB.getMeta();
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "shadowx-fbdl",
3
+ "version": "1.0.0",
4
+ "description": "Powerful Facebook video downloader - Supports FB, FB Watch, Reels, Shares using ShadowX API",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "node examples/basic-usage.js",
8
+ "start": "node index.js"
9
+ },
10
+ "keywords": [
11
+ "facebook",
12
+ "downloader",
13
+ "video",
14
+ "facebook-video",
15
+ "fb-downloader",
16
+ "fb",
17
+ "reels",
18
+ "facebook-reels",
19
+ "facebook-watch",
20
+ "shadowx",
21
+ "video-downloader",
22
+ "social-media-downloader",
23
+ "facebook-downloader",
24
+ "fb-video",
25
+ "reels-downloader"
26
+ ],
27
+ "author": "Mueid Mursalin Rifat",
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "axios": "^1.6.2"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/mueidmursalin/shadowx-fbdl.git"
35
+ },
36
+ "bugs": {
37
+ "url": "https://github.com/mueidmursalin/shadowx-fbdl/issues"
38
+ },
39
+ "homepage": "https://github.com/mueidmursalin/shadowx-fbdl#readme",
40
+ "engines": {
41
+ "node": ">=12.0.0"
42
+ },
43
+ "files": [
44
+ "index.js",
45
+ "README.md",
46
+ "LICENSE",
47
+ "examples/"
48
+ ]
49
+ }