devusman commited on
Commit
a2e646a
·
1 Parent(s): a1a36a1
Files changed (4) hide show
  1. Dockerfile +12 -17
  2. app.py +4 -3
  3. templates/index.html +271 -0
  4. test.html +322 -0
Dockerfile CHANGED
@@ -1,30 +1,25 @@
1
- # Use an official Python runtime as a parent image
2
- FROM python:3.9-slim
3
 
4
- # Install ffmpeg and other system dependencies
5
- RUN apt-get update && apt-get install -y --no-install-recommends \
6
  ffmpeg \
7
  && rm -rf /var/lib/apt/lists/*
8
 
9
- # Set the working directory in the container
10
  WORKDIR /app
11
 
12
- # Copy the dependencies file to the working directory
13
  COPY requirements.txt .
14
 
15
- # Install any needed packages specified in requirements.txt
16
  RUN pip install --no-cache-dir -r requirements.txt
17
 
18
- # Copy the rest of the application code to the working directory
19
  COPY . .
20
 
21
- # Make port 7860 available to the world outside this container
22
- # Hugging Face Spaces use 7860 by default
23
- EXPOSE 7860
24
 
25
- # Define environment variables
26
- ENV FLASK_APP=app.py
27
-
28
- # Run the app using gunicorn for production
29
- # Use 0.0.0.0 to make it accessible from outside the container
30
- CMD ["gunicorn", "--bind", "0.0.0.0:7860", "--workers", "1", "--threads", "4", "app:app"]
 
1
+ # Use lightweight Python image
2
+ FROM python:3.11-slim
3
 
4
+ # Install ffmpeg (needed by yt-dlp)
5
+ RUN apt-get update && apt-get install -y \
6
  ffmpeg \
7
  && rm -rf /var/lib/apt/lists/*
8
 
9
+ # Set working directory
10
  WORKDIR /app
11
 
12
+ # Copy requirements first
13
  COPY requirements.txt .
14
 
15
+ # Install dependencies
16
  RUN pip install --no-cache-dir -r requirements.txt
17
 
18
+ # Copy project files
19
  COPY . .
20
 
21
+ # Expose port for Render
22
+ EXPOSE 8080
 
23
 
24
+ # Start Flask app directly
25
+ CMD ["python", "app.py"]
 
 
 
 
app.py CHANGED
@@ -25,9 +25,9 @@ def sanitize_facebook_url(url):
25
  print(f"Could not sanitize URL, using original. Error: {e}", flush=True)
26
  return url
27
 
28
- @app.route('/')
29
  def index():
30
- """Serves the main HTML page."""
31
  return render_template('index.html')
32
 
33
  @app.route('/download', methods=['POST'])
@@ -127,5 +127,6 @@ def download():
127
  return jsonify({'error': 'This content is private or requires login. Please check `cookies.txt`.'}), 403
128
  return jsonify({'error': 'An unknown error occurred. The link may be invalid or private.'}), 500
129
 
 
130
  if __name__ == '__main__':
131
- app.run(debug=True, host='0.0.0.0', port=7860)
 
25
  print(f"Could not sanitize URL, using original. Error: {e}", flush=True)
26
  return url
27
 
28
+ @app.route('/', methods=['GET'])
29
  def index():
30
+ """Renders the main page."""
31
  return render_template('index.html')
32
 
33
  @app.route('/download', methods=['POST'])
 
127
  return jsonify({'error': 'This content is private or requires login. Please check `cookies.txt`.'}), 403
128
  return jsonify({'error': 'An unknown error occurred. The link may be invalid or private.'}), 500
129
 
130
+ # For local testing
131
  if __name__ == '__main__':
132
+ app.run(host='0.0.0.0', port=8080, debug=True)
templates/index.html CHANGED
@@ -194,6 +194,78 @@
194
  #pause-btn.paused:hover {
195
  background-color: #218838;
196
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  </style>
198
  </head>
199
  <body>
@@ -207,6 +279,9 @@
207
  <button class="tab-button" onclick="openTab(event, 'audio')">
208
  Audio
209
  </button>
 
 
 
210
  </div>
211
 
212
  <div id="video" class="tab-content active">
@@ -250,6 +325,30 @@
250
  </p>
251
  </div>
252
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  <div id="status-container">
254
  <div id="spinner" class="spinner"></div>
255
  <div id="progress-container">
@@ -274,6 +373,8 @@
274
  let currentReceived = 0;
275
  let currentTotal = 0;
276
  let currentResponse = null;
 
 
277
 
278
  function openTab(evt, tabName) {
279
  document
@@ -284,6 +385,9 @@
284
  .forEach((tb) => tb.classList.remove("active"));
285
  document.getElementById(tabName).classList.add("active");
286
  evt.currentTarget.classList.add("active");
 
 
 
287
  }
288
 
289
  async function handleSubmit(e) {
@@ -365,6 +469,170 @@
365
  }
366
  }
367
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  async function readChunk() {
369
  if (!currentReader || !isDownloading) return;
370
 
@@ -495,6 +763,9 @@
495
  document
496
  .getElementById("audioForm")
497
  .addEventListener("submit", handleSubmit);
 
 
 
498
  </script>
499
  </body>
500
  </html>
 
194
  #pause-btn.paused:hover {
195
  background-color: #218838;
196
  }
197
+ #stories-results {
198
+ display: none;
199
+ margin-top: 1.5rem;
200
+ }
201
+ .story {
202
+ margin-bottom: 1.5rem;
203
+ border: 1px solid var(--border-color);
204
+ padding: 1rem;
205
+ border-radius: 8px;
206
+ background: #fafbfc;
207
+ }
208
+ .story h3 {
209
+ margin: 0 0 0.5rem 0;
210
+ color: var(--primary-color);
211
+ font-size: 1.1rem;
212
+ }
213
+ .preview-container {
214
+ text-align: center;
215
+ margin-bottom: 1rem;
216
+ }
217
+ .preview-container video,
218
+ .preview-container img,
219
+ .preview-container audio {
220
+ width: 150px;
221
+ height: 150px;
222
+ object-fit: cover;
223
+ border-radius: 6px;
224
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
225
+ display: block;
226
+ margin: 0 auto;
227
+ }
228
+ .preview-container audio {
229
+ height: auto;
230
+ width: 150px;
231
+ }
232
+ .story ul {
233
+ list-style: none;
234
+ padding: 0;
235
+ display: flex;
236
+ flex-wrap: wrap;
237
+ gap: 0.5rem;
238
+ }
239
+ .story li {
240
+ flex: 1;
241
+ min-width: 120px;
242
+ }
243
+ .story a {
244
+ display: block;
245
+ padding: 0.5rem;
246
+ background: var(--primary-color);
247
+ color: white;
248
+ text-decoration: none;
249
+ border-radius: 4px;
250
+ text-align: center;
251
+ font-size: 0.9rem;
252
+ transition: background-color 0.2s;
253
+ }
254
+ .story a:hover {
255
+ background-color: var(--primary-hover);
256
+ }
257
+ #back-to-form {
258
+ background-color: var(--subtle-text);
259
+ margin-top: 1rem;
260
+ }
261
+ #back-to-form:hover {
262
+ background-color: #525b69;
263
+ }
264
+ #stories-loader {
265
+ display: none;
266
+ text-align: center;
267
+ margin: 1rem 0;
268
+ }
269
  </style>
270
  </head>
271
  <body>
 
279
  <button class="tab-button" onclick="openTab(event, 'audio')">
280
  Audio
281
  </button>
282
+ <button class="tab-button" onclick="openTab(event, 'stories')">
283
+ Stories
284
+ </button>
285
  </div>
286
 
287
  <div id="video" class="tab-content active">
 
325
  </p>
326
  </div>
327
 
328
+ <div id="stories" class="tab-content">
329
+ <form id="storiesForm">
330
+ <input
331
+ type="text"
332
+ name="url"
333
+ placeholder="Enter Facebook Stories URL"
334
+ required
335
+ />
336
+ <button type="submit">Fetch Stories</button>
337
+ </form>
338
+ <div id="stories-loader" class="spinner"></div>
339
+ <p class="note">
340
+ Fetch and download Facebook stories. Previews and multiple format
341
+ options will be displayed.
342
+ </p>
343
+ <div id="stories-results">
344
+ <h2 style="text-align: center; margin-bottom: 1rem">Stories</h2>
345
+ <div id="stories-container"></div>
346
+ <button id="back-to-form" onclick="backToStoriesForm()">
347
+ Back to Form
348
+ </button>
349
+ </div>
350
+ </div>
351
+
352
  <div id="status-container">
353
  <div id="spinner" class="spinner"></div>
354
  <div id="progress-container">
 
373
  let currentReceived = 0;
374
  let currentTotal = 0;
375
  let currentResponse = null;
376
+ const proxy = "https://corsproxy.io/?";
377
+ const targetUrl = "https://getvidfb.com/";
378
 
379
  function openTab(evt, tabName) {
380
  document
 
385
  .forEach((tb) => tb.classList.remove("active"));
386
  document.getElementById(tabName).classList.add("active");
387
  evt.currentTarget.classList.add("active");
388
+ if (tabName === "stories") {
389
+ document.getElementById("stories-results").style.display = "none";
390
+ }
391
  }
392
 
393
  async function handleSubmit(e) {
 
469
  }
470
  }
471
 
472
+ async function handleStoriesSubmit(e) {
473
+ e.preventDefault();
474
+ const form = e.target;
475
+ const urlInput = form.querySelector('input[name="url"]');
476
+ const fbUrl = urlInput.value;
477
+ const loader = document.getElementById("stories-loader");
478
+ const results = document.getElementById("stories-results");
479
+ const container = document.getElementById("stories-container");
480
+ const status = document.getElementById("status");
481
+
482
+ if (!fbUrl) return;
483
+
484
+ form.querySelector('button[type="submit"]').disabled = true;
485
+ loader.style.display = "block";
486
+ status.textContent = "Fetching stories...";
487
+ status.classList.remove("error");
488
+ results.style.display = "none";
489
+ container.innerHTML = "";
490
+
491
+ try {
492
+ const proxiedUrl = proxy + encodeURIComponent(targetUrl);
493
+ const formData = new URLSearchParams();
494
+ formData.append("url", fbUrl);
495
+ formData.append("lang", "en");
496
+ formData.append("type", "redirect");
497
+
498
+ const response = await fetch(proxiedUrl, {
499
+ method: "POST",
500
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
501
+ body: formData,
502
+ });
503
+
504
+ if (!response.ok) throw new Error("Failed to fetch stories");
505
+
506
+ const html = await response.text();
507
+ const parser = new DOMParser();
508
+ const doc = parser.parseFromString(html, "text/html");
509
+ const storyBlocks = doc.querySelectorAll(".snaptikvid");
510
+
511
+ if (storyBlocks.length === 0) {
512
+ // Fallback for single content
513
+ const links = doc.querySelectorAll("#snaptik-video a.abutton");
514
+ const storyDiv = document.createElement("div");
515
+ storyDiv.className = "story";
516
+ storyDiv.innerHTML = "<h3>Story #0</h3>";
517
+ const previewContainer = document.createElement("div");
518
+ previewContainer.className = "preview-container";
519
+ let previewAdded = false;
520
+ const ul = document.createElement("ul");
521
+ links.forEach((link) => {
522
+ const title =
523
+ link.querySelector("span")?.textContent.trim() || "Download";
524
+ const href = link.getAttribute("href");
525
+ const li = document.createElement("li");
526
+ let preview;
527
+ if (!previewAdded && title.includes("Video/Mp4")) {
528
+ preview = document.createElement("video");
529
+ preview.controls = true;
530
+ preview.src = href;
531
+ previewContainer.appendChild(preview);
532
+ previewAdded = true;
533
+ } else if (!previewAdded && title.includes("Photo/Jpg")) {
534
+ preview = document.createElement("img");
535
+ preview.src = href;
536
+ previewContainer.appendChild(preview);
537
+ previewAdded = true;
538
+ } else if (!previewAdded && title.includes("Audio/Mp3")) {
539
+ preview = document.createElement("audio");
540
+ preview.controls = true;
541
+ preview.src = href;
542
+ previewContainer.appendChild(preview);
543
+ previewAdded = true;
544
+ }
545
+ const a = document.createElement("a");
546
+ a.href = href;
547
+ a.textContent = title;
548
+ a.download = "";
549
+ li.appendChild(a);
550
+ ul.appendChild(li);
551
+ });
552
+ if (!previewAdded && links.length > 0) {
553
+ const firstHref = links[0].getAttribute("href");
554
+ if (firstHref) {
555
+ const preview = document.createElement("img");
556
+ preview.src = firstHref;
557
+ previewContainer.appendChild(preview);
558
+ }
559
+ }
560
+ storyDiv.appendChild(previewContainer);
561
+ storyDiv.appendChild(ul);
562
+ container.appendChild(storyDiv);
563
+ } else {
564
+ storyBlocks.forEach((storyBlock, index) => {
565
+ const storyDiv = document.createElement("div");
566
+ storyDiv.className = "story";
567
+ storyDiv.innerHTML = `<h3>Story #${index}</h3>`;
568
+ const previewContainer = document.createElement("div");
569
+ previewContainer.className = "preview-container";
570
+ const links = storyBlock.querySelectorAll("a.abutton");
571
+ let previewAdded = false;
572
+ const ul = document.createElement("ul");
573
+ links.forEach((link) => {
574
+ const title =
575
+ link.querySelector("span")?.textContent.trim() || "Download";
576
+ const href = link.getAttribute("href");
577
+ const li = document.createElement("li");
578
+ let preview;
579
+ if (!previewAdded && title.includes("Video/Mp4")) {
580
+ preview = document.createElement("video");
581
+ preview.controls = true;
582
+ preview.src = href;
583
+ previewContainer.appendChild(preview);
584
+ previewAdded = true;
585
+ } else if (!previewAdded && title.includes("Photo/Jpg")) {
586
+ preview = document.createElement("img");
587
+ preview.src = href;
588
+ previewContainer.appendChild(preview);
589
+ previewAdded = true;
590
+ } else if (!previewAdded && title.includes("Audio/Mp3")) {
591
+ preview = document.createElement("audio");
592
+ preview.controls = true;
593
+ preview.src = href;
594
+ previewContainer.appendChild(preview);
595
+ previewAdded = true;
596
+ }
597
+ const a = document.createElement("a");
598
+ a.href = href;
599
+ a.textContent = title;
600
+ a.download = "";
601
+ li.appendChild(a);
602
+ ul.appendChild(li);
603
+ });
604
+ if (!previewAdded) {
605
+ const thumbImg = storyBlock.querySelector(".snaptik-left img");
606
+ if (thumbImg) {
607
+ const preview = document.createElement("img");
608
+ preview.src = thumbImg.src;
609
+ previewContainer.appendChild(preview);
610
+ }
611
+ }
612
+ storyDiv.appendChild(previewContainer);
613
+ storyDiv.appendChild(ul);
614
+ container.appendChild(storyDiv);
615
+ });
616
+ }
617
+
618
+ results.style.display = "block";
619
+ status.textContent = "Stories fetched successfully!";
620
+ urlInput.value = "";
621
+ } catch (error) {
622
+ status.textContent = `Error: ${error.message}`;
623
+ status.classList.add("error");
624
+ } finally {
625
+ loader.style.display = "none";
626
+ form.querySelector('button[type="submit"]').disabled = false;
627
+ }
628
+ }
629
+
630
+ function backToStoriesForm() {
631
+ document.getElementById("stories-results").style.display = "none";
632
+ document.getElementById("storiesForm").reset();
633
+ document.getElementById("status").textContent = "";
634
+ }
635
+
636
  async function readChunk() {
637
  if (!currentReader || !isDownloading) return;
638
 
 
763
  document
764
  .getElementById("audioForm")
765
  .addEventListener("submit", handleSubmit);
766
+ document
767
+ .getElementById("storiesForm")
768
+ .addEventListener("submit", handleStoriesSubmit);
769
  </script>
770
  </body>
771
  </html>
test.html ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>FB Video Downloader</title>
8
+ <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600&display=swap" rel="stylesheet">
9
+ <style>
10
+ * {
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: 'Poppins', sans-serif;
16
+ max-width: 700px;
17
+ margin: 0 auto;
18
+ padding: 20px;
19
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
20
+ color: #333;
21
+ min-height: 100vh;
22
+ }
23
+
24
+ h1 {
25
+ text-align: center;
26
+ color: white;
27
+ margin-bottom: 30px;
28
+ font-weight: 600;
29
+ font-size: 2.5em;
30
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
31
+ }
32
+
33
+ form {
34
+ display: flex;
35
+ margin-bottom: 30px;
36
+ background: white;
37
+ border-radius: 50px;
38
+ overflow: hidden;
39
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
40
+ }
41
+
42
+ input {
43
+ flex: 1;
44
+ padding: 15px 20px;
45
+ border: none;
46
+ font-size: 16px;
47
+ outline: none;
48
+ }
49
+
50
+ button {
51
+ padding: 15px 30px;
52
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
53
+ color: white;
54
+ border: none;
55
+ cursor: pointer;
56
+ font-weight: 600;
57
+ transition: transform 0.2s;
58
+ }
59
+
60
+ button:hover {
61
+ transform: translateY(-2px);
62
+ }
63
+
64
+ #results {
65
+ display: none;
66
+ background: white;
67
+ border-radius: 20px;
68
+ padding: 30px;
69
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
70
+ }
71
+
72
+ #loader {
73
+ display: none;
74
+ text-align: center;
75
+ margin: 40px;
76
+ color: white;
77
+ font-size: 18px;
78
+ }
79
+
80
+ .story {
81
+ margin-bottom: 30px;
82
+ border: 1px solid #e0e0e0;
83
+ padding: 20px;
84
+ border-radius: 15px;
85
+ background: #fafafa;
86
+ transition: box-shadow 0.3s;
87
+ }
88
+
89
+ .story:hover {
90
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
91
+ }
92
+
93
+ .story h3 {
94
+ margin: 0 0 15px 0;
95
+ color: #667eea;
96
+ font-size: 1.2em;
97
+ font-weight: 600;
98
+ }
99
+
100
+ .preview-container {
101
+ text-align: center;
102
+ margin-bottom: 20px;
103
+ }
104
+
105
+ video,
106
+ img,
107
+ audio {
108
+ width: 200px;
109
+ height: 200px;
110
+ object-fit: cover;
111
+ border-radius: 10px;
112
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
113
+ display: block;
114
+ margin: 0 auto;
115
+ }
116
+
117
+ audio {
118
+ height: auto;
119
+ width: 200px;
120
+ }
121
+
122
+ ul {
123
+ list-style: none;
124
+ padding: 0;
125
+ display: flex;
126
+ flex-wrap: wrap;
127
+ gap: 10px;
128
+ }
129
+
130
+ li {
131
+ flex: 1;
132
+ min-width: 150px;
133
+ }
134
+
135
+ a {
136
+ display: block;
137
+ padding: 12px;
138
+ background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
139
+ color: white;
140
+ text-decoration: none;
141
+ border-radius: 8px;
142
+ text-align: center;
143
+ font-weight: 500;
144
+ transition: transform 0.2s, box-shadow 0.2s;
145
+ }
146
+
147
+ a:hover {
148
+ transform: translateY(-2px);
149
+ box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3);
150
+ }
151
+
152
+ #back {
153
+ background: linear-gradient(135deg, #6c757d 0%, #495057 100%);
154
+ border: none;
155
+ padding: 12px 24px;
156
+ color: white;
157
+ border-radius: 8px;
158
+ cursor: pointer;
159
+ font-weight: 500;
160
+ transition: transform 0.2s;
161
+ margin-top: 20px;
162
+ }
163
+
164
+ #back:hover {
165
+ transform: translateY(-2px);
166
+ }
167
+ </style>
168
+ <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
169
+ </head>
170
+
171
+ <body>
172
+ <h1>Facebook Video Downloader</h1>
173
+ <form id="form">
174
+ <input type="text" id="url" placeholder="Enter Facebook URL" required>
175
+ <button type="submit">Get Downloads</button>
176
+ </form>
177
+ <div id="loader">Loading...</div>
178
+ <div id="results">
179
+ <h2 style="text-align: center; color: #333; margin-bottom: 20px;">Download Links</h2>
180
+ <div id="stories-container"></div>
181
+ <button id="back">Back</button>
182
+ </div>
183
+
184
+ <script>
185
+ const form = document.getElementById('form');
186
+ const urlInput = document.getElementById('url');
187
+ const loader = document.getElementById('loader');
188
+ const results = document.getElementById('results');
189
+ const storiesContainer = document.getElementById('stories-container');
190
+ const back = document.getElementById('back');
191
+ const proxy = 'https://corsproxy.io/?';
192
+
193
+ form.addEventListener('submit', async (e) => {
194
+ e.preventDefault();
195
+ const fbUrl = urlInput.value;
196
+ loader.style.display = 'block';
197
+ try {
198
+ const targetUrl = 'https://getvidfb.com/';
199
+ const proxiedUrl = proxy + encodeURIComponent(targetUrl);
200
+ const response = await axios.post(proxiedUrl, {
201
+ url: fbUrl,
202
+ lang: 'en',
203
+ type: 'redirect'
204
+ }, {
205
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
206
+ });
207
+ const parser = new DOMParser();
208
+ const doc = parser.parseFromString(response.data, 'text/html');
209
+ const storyBlocks = doc.querySelectorAll('.snaptikvid');
210
+ storiesContainer.innerHTML = '';
211
+ if (storyBlocks.length === 0) {
212
+ const links = doc.querySelectorAll('#snaptik-video a.abutton');
213
+ const storyDiv = document.createElement('div');
214
+ storyDiv.className = 'story';
215
+ storyDiv.innerHTML = '<h3>Story #0</h3>';
216
+ const previewContainer = document.createElement('div');
217
+ previewContainer.className = 'preview-container';
218
+ let previewAdded = false;
219
+ const ul = document.createElement('ul');
220
+ links.forEach(link => {
221
+ const title = link.querySelector('span').textContent.trim();
222
+ const href = link.getAttribute('href');
223
+ const li = document.createElement('li');
224
+ let preview;
225
+ if (!previewAdded && title.includes('Video/Mp4')) {
226
+ preview = document.createElement('video');
227
+ preview.controls = true;
228
+ preview.src = href;
229
+ previewContainer.appendChild(preview);
230
+ previewAdded = true;
231
+ } else if (!previewAdded && title.includes('Photo/Jpg')) {
232
+ preview = document.createElement('img');
233
+ preview.src = href;
234
+ previewContainer.appendChild(preview);
235
+ previewAdded = true;
236
+ } else if (!previewAdded && title.includes('Audio/Mp3')) {
237
+ preview = document.createElement('audio');
238
+ preview.controls = true;
239
+ preview.src = href;
240
+ previewContainer.appendChild(preview);
241
+ previewAdded = true;
242
+ }
243
+ const a = document.createElement('a');
244
+ a.href = href;
245
+ a.textContent = title;
246
+ a.download = '';
247
+ li.appendChild(a);
248
+ ul.appendChild(li);
249
+ });
250
+ storyDiv.appendChild(previewContainer);
251
+ storyDiv.appendChild(ul);
252
+ storiesContainer.appendChild(storyDiv);
253
+ } else {
254
+ storyBlocks.forEach((storyBlock, index) => {
255
+ const storyDiv = document.createElement('div');
256
+ storyDiv.className = 'story';
257
+ storyDiv.innerHTML = `<h3>Story #${index}</h3>`;
258
+ const previewContainer = document.createElement('div');
259
+ previewContainer.className = 'preview-container';
260
+ const links = storyBlock.querySelectorAll('a.abutton');
261
+ let previewAdded = false;
262
+ const ul = document.createElement('ul');
263
+ links.forEach(link => {
264
+ const title = link.querySelector('span').textContent.trim();
265
+ const href = link.getAttribute('href');
266
+ const li = document.createElement('li');
267
+ let preview;
268
+ if (!previewAdded && title.includes('Video/Mp4')) {
269
+ preview = document.createElement('video');
270
+ preview.controls = true;
271
+ preview.src = href;
272
+ previewContainer.appendChild(preview);
273
+ previewAdded = true;
274
+ } else if (!previewAdded && title.includes('Photo/Jpg')) {
275
+ preview = document.createElement('img');
276
+ preview.src = href;
277
+ previewContainer.appendChild(preview);
278
+ previewAdded = true;
279
+ } else if (!previewAdded && title.includes('Audio/Mp3')) {
280
+ preview = document.createElement('audio');
281
+ preview.controls = true;
282
+ preview.src = href;
283
+ previewContainer.appendChild(preview);
284
+ previewAdded = true;
285
+ }
286
+ const a = document.createElement('a');
287
+ a.href = href;
288
+ a.textContent = title;
289
+ a.download = '';
290
+ li.appendChild(a);
291
+ ul.appendChild(li);
292
+ });
293
+ if (!previewAdded) {
294
+ const thumbImg = storyBlock.querySelector('.snaptik-left img');
295
+ if (thumbImg) {
296
+ const preview = document.createElement('img');
297
+ preview.src = thumbImg.src;
298
+ previewContainer.appendChild(preview);
299
+ }
300
+ }
301
+ storyDiv.appendChild(previewContainer);
302
+ storyDiv.appendChild(ul);
303
+ storiesContainer.appendChild(storyDiv);
304
+ });
305
+ }
306
+ form.style.display = 'none';
307
+ results.style.display = 'block';
308
+ } catch (error) {
309
+ alert('Error fetching links: ' + error.message);
310
+ }
311
+ loader.style.display = 'none';
312
+ });
313
+
314
+ back.addEventListener('click', () => {
315
+ form.style.display = 'flex';
316
+ results.style.display = 'none';
317
+ urlInput.value = '';
318
+ });
319
+ </script>
320
+ </body>
321
+
322
+ </html>