š
Online Notepad
+
ADD NEW TEXT
Untitled Note
Last saved: May 17, 10:54 AM
Untitled Note
Last saved: Apr 19, 8:09 AM
Untitled Note
Last saved: Apr 19, 7:24 AM
Untitled Note
Last saved: Apr 19, 7:21 AM
Untitled Note
Last saved: Apr 19, 7:19 AM
Untitled Note
Last saved: Apr 19, 6:19 AM
Untitled Note
Last saved: Apr 19, 5:48 AM
Untitled Note
Last saved: Apr 19, 5:44 AM
Untitled Note
Last saved: Apr 11, 10:20 AM
Untitled Note
Last saved: Apr 11, 8:34 AM
Untitled Note
Last saved: Apr 11, 8:23 AM
Untitled Note
Last saved: Apr 11, 8:14 AM
Untitled Note
Last saved: Apr 11, 7:44 AM
Untitled Note
Last saved: Apr 11, 7:43 AM
Untitled Note
Last saved: Apr 11, 7:35 AM
Untitled Note
Last saved: Apr 11, 7:32 AM
š¾ Save to Server
šļø Delete
import tkinter as tk from tkinter import scrolledtext, messagebox import threading import queue import os import re import shutil from selenium import webdriver from selenium.webdriver.common.by import By from time import sleep import subprocess import csv import http.cookiejar class ReelsDownloaderGUI: def __init__(self, root): self.root = root self.root.title("Facebook Reels Downloader - STABLE v8.3 (Improved Scrolling)") self.root.geometry("950x720") self.root.configure(bg="#f0f0f0") self.log_queue = queue.Queue() # === PERSISTENT CHROME PROFILE === self.profile_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "chrome_profile") os.makedirs(self.profile_dir, exist_ok=True) tk.Label(root, text="Facebook Reels Bulk Downloader", font=("Arial", 16, "bold"), bg="#f0f0f0").pack(pady=10) tk.Label(root, text="Folder Name:", bg="#f0f0f0").pack(anchor="w", padx=20) self.folder_var = tk.StringVar(value="tanweer_reels") tk.Entry(root, textvariable=self.folder_var, width=85).pack(padx=20, pady=5) tk.Label(root, text="Reels Tab URL:", bg="#f0f0f0").pack(anchor="w", padx=20) self.url_var = tk.StringVar(value="https://www.facebook.com/profile.php?id=61573238759323&sk=reels_tab") tk.Entry(root, textvariable=self.url_var, width=85).pack(padx=20, pady=5) tk.Label(root, text="Facebook Cookies (Netscape format) - ONE-TIME ONLY:", bg="#f0f0f0").pack(anchor="w", padx=20) self.cookies_text = scrolledtext.ScrolledText(root, height=10, width=105) self.cookies_text.pack(padx=20, pady=5) # Auto message saved_cookies_path = "saved_cookies.txt" profile_has_cookies = os.path.exists(os.path.join(self.profile_dir, "Default", "Cookies")) if os.path.exists(saved_cookies_path) or profile_has_cookies: self.cookies_text.insert(tk.INSERT, "=== COOKIES ALREADY SAVED IN PERSISTENT CHROME PROFILE ===\n\n" "ā One-time import completed!\n" "ā Leave this box BLANK for all future downloads.\n" "ā Browser will automatically use the saved login session.\n\n" "Only paste new cookies here if you want to UPDATE your login.") else: self.cookies_text.insert(tk.INSERT, "Paste your Facebook Cookies (Netscape format) here\n" "ā This is ONLY needed the FIRST time.\n" "ā After the first successful download, leave this box blank forever.") btn_frame = tk.Frame(root, bg="#f0f0f0") btn_frame.pack(pady=12) tk.Button(btn_frame, text="š Start Download - STABLE v8.3 (Improved Scrolling)", font=("Arial", 13, "bold"), bg="#1877f2", fg="white", height=2, command=self.start_download_thread).pack(side="left", padx=15) tk.Button(btn_frame, text="Clear Log", command=self.clear_log).pack(side="left", padx=10) tk.Button(btn_frame, text="Exit", command=root.quit).pack(side="left", padx=10) tk.Label(root, text="Live Log:", bg="#f0f0f0").pack(anchor="w", padx=20) self.log_text = scrolledtext.ScrolledText(root, height=19, bg="black", fg="#00ff00", font=("Consolas", 10)) self.log_text.pack(padx=20, pady=5, fill="both", expand=True) self.update_log() def log(self, message): self.log_queue.put(message) def update_log(self): while not self.log_queue.empty(): msg = self.log_queue.get_nowait() self.log_text.insert(tk.END, msg + "\n") self.log_text.see(tk.END) self.root.after(100, self.update_log) def clear_log(self): self.log_text.delete("1.0", tk.END) def start_download_thread(self): thread = threading.Thread(target=self.download_process, daemon=True) thread.start() def download_process(self): folder_name = self.folder_var.get().strip() or "facebook_reels" url = self.url_var.get().strip() cookies_content = self.cookies_text.get("1.0", tk.END).strip() if not url: messagebox.showerror("Error", "Please enter Reels URL") return self.log("=== STABLE v8.3 - Persistent Chrome Profile + IMPROVED SCROLLING ===") self.log("ā Now catches ALL reels (even when Facebook lazy-loads slowly)") self.log(f"Target: {url}") self.log(f"Save folder: output/{folder_name}\n") saved_cookies_path = "saved_cookies.txt" apply_cookies = False if cookies_content and not cookies_content.startswith("=== COOKIES ALREADY SAVED"): apply_cookies = True with open(saved_cookies_path, "w", encoding="utf-8") as f: f.write(cookies_content) self.log("ā New cookies saved permanently (saved_cookies.txt)") elif os.path.exists(saved_cookies_path): self.log("ā Using previously saved cookies from persistent profile") # === Chrome Options with PERSISTENT PROFILE === options = webdriver.ChromeOptions() options.add_argument("--disable-notifications") options.add_argument("--disable-popup-blocking") options.add_argument("--start-maximized") options.add_argument(f"--user-data-dir={self.profile_dir}") driver = webdriver.Chrome(options=options) # Apply cookies ONLY the first time if apply_cookies: self.log("š Applying cookies to persistent Chrome profile...") driver.get("https://www.facebook.com") sleep(4) cj = http.cookiejar.MozillaCookieJar() cj.load(saved_cookies_path, ignore_discard=True, ignore_expires=True) for cookie in cj: if 'facebook.com' in cookie.domain: try: driver.add_cookie({ 'name': cookie.name, 'value': cookie.value, 'domain': cookie.domain, 'path': cookie.path or '/' }) except: pass driver.refresh() sleep(6) self.log("ā Logged in with cookies ā Profile now saved forever!") driver.get(url) self.log("š Reels tab opened...") sleep(8) # Close any popup try: driver.find_element(By.XPATH, "//div[@aria-label='Close' or @role='button']").click() self.log("Closed popup") sleep(3) except: pass # ====================== IMPROVED REELS SCRAPING (2026-proof) ====================== self.log("š Loading ALL reels (aggressive infinite scroll + height check)...") reel_links = [] seen = set() scroll_count = 0 max_scrolls = 5 no_progress_count = 0 previous_count = 0 last_height = driver.execute_script("return document.body.scrollHeight") while scroll_count < max_scrolls and no_progress_count < 10: scroll_count += 1 self.log(f" Scroll #{scroll_count} - loading more reels...") # 1. Scroll all the way to the bottom driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") sleep(3) # 2. Extra micro-scrolls to trigger lazy loading for _ in range(6): driver.execute_script("window.scrollBy(0, 900);") sleep(0.7) sleep(4) # give Facebook time to render new reels # 3. Check current height current_height = driver.execute_script("return document.body.scrollHeight") # 4. Extract EVERY reel link new_added = 0 for el in driver.find_elements(By.XPATH, "//a[contains(@href, '/reel/')]"): href = el.get_attribute('href') if href: clean = re.sub(r'\?.*$', '', href).rstrip('/') if re.search(r'/reel/[0-9a-zA-Z]{10,}', clean) and clean not in seen: seen.add(clean) reel_links.append(clean) new_added += 1 current_total = len(reel_links) self.log(f" Scroll #{scroll_count} ā {current_total} reels (added {new_added})") # Stop only when BOTH no new links AND page height stopped growing if current_total == previous_count and current_height == last_height: no_progress_count += 1 else: no_progress_count = 0 previous_count = current_total last_height = current_height self.log(f"ā Final total reels found = {len(reel_links)}") driver.quit() # ====================== DOWNLOAD ====================== if not reel_links: self.log("ā ļø No reels found!") return if shutil.which("yt-dlp") is None: self.log("ā ERROR: yt-dlp not found in PATH!") self.log("Please run: pip install -U yt-dlp") messagebox.showerror("Error", "yt-dlp not found. Install it first.") return os.makedirs("output", exist_ok=True) output_dir = os.path.join("output", folder_name) os.makedirs(output_dir, exist_ok=True) csv_path = os.path.join("output", f"{folder_name}.csv") with open(csv_path, "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) for link in reel_links: writer.writerow([link]) self.log(f"\nš„ Downloading {len(reel_links)} reels as SINGLE MP4 with sound...") args = ["yt-dlp"] if os.path.exists(saved_cookies_path): args.extend(["--cookies", saved_cookies_path]) args.extend([ "-f", "best[ext=mp4]/bestvideo[ext=mp4]+bestaudio[ext=m4a]/bestvideo+bestaudio/best", "--merge-output-format", "mp4", "--postprocessor-args", "ffmpeg:-c:v copy -c:a aac -movflags +faststart", "--force-ipv4", "--geo-bypass", "--retries", "30", "--fragment-retries", "30", "--sleep-interval", "2", "-a", csv_path, "--output", os.path.join(output_dir, "%(id)s.%(ext)s"), "--no-keep-video", "--no-keep-fragments", ]) process = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding="utf-8", errors="replace") for line in process.stdout: if line.strip(): self.log(line.strip()) process.wait() self.log("\nš ALL DONE! Reels saved as single MP4 with sound.") self.log(f"ā Check folder: output\\{folder_name}\\") messagebox.showinfo("Success", f"Done!\n{len(reel_links)} reels downloaded successfully") if __name__ == "__main__": root = tk.Tk() app = ReelsDownloaderGUI(root) root.mainloop()
ā Saved online