import os
import threading
import tkinter as tk
from tkinter import filedialog, messagebox, ttk, scrolledtext
from yt_dlp import YoutubeDL
def center_window(root, width=800, height=600):
# Lấy kích thước màn hình
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
# Tính vị trí bắt đầu trục ngang và trục đứng
aaaxxx = (screen_width // 2) - (width // 2)
bbbyyy = (screen_height // 2) - (height // 2) - (40)
# Đặt geometry
root.geometry(f"{width}x{height}+{aaaxxx}+{bbbyyy}")
class FacebookDownloaderApp:
def __init__(self, root):
self.root = root
self.root.title("Facebook Video Downloader - DarkCorners")
self.root.resizable(False, False)
center_window(self.root, 800, 600)
self.save_folder = ""
self.pause_flag = False
self.downloading = False
self.total_videos = 0
self.completed_videos = 0
self.pending_videos = 0
self.failed_videos = 0
self.setup_ui()
def setup_ui(self):
tk.Label(self.root, text="Facebook Video Downloader - DarkCorners", font=("Arial", 16, "bold")).pack(pady=10)
container = tk.Frame(self.root)
container.pack(fill=tk.X, padx=10, pady=5)
config_frame = tk.LabelFrame(container, text="Cấu Hình", font=("Arial", 10, "bold"), padx=10, pady=10)
config_frame.grid(row=0, column=0, sticky="nsew", padx=5)
container.grid_columnconfigure(0, weight=1, uniform="group1")
folder_frame = tk.Frame(config_frame)
folder_frame.pack(fill=tk.X, pady=2)
self.folder_path_entry = tk.Entry(folder_frame, width=45)
self.folder_path_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=False)
tk.Button(folder_frame, text="Chọn", command=self.select_folder, width=5).pack(side=tk.LEFT, padx=5)
tk.Label(config_frame, text="Chọn thư mục chứa videos sẽ tải xuống !", font=("Arial", 9, "bold"), fg="red").pack(anchor="w")
tk.Label(config_frame, text="Danh sách link cần tải xuống theo định dạng : link|filename", font=("Arial", 9, "bold"), fg="red").pack(anchor="w")
control_frame = tk.LabelFrame(container, text="Điều Khiển", font=("Arial", 10, "bold"), padx=10, pady=10)
control_frame.grid(row=0, column=1, sticky="nsew", padx=5)
container.grid_columnconfigure(1, weight=1, uniform="group1")
quality_frame = tk.Frame(control_frame)
quality_frame.pack(fill=tk.X, pady=2)
tk.Label(quality_frame, text="Chọn chất lượng video : ").pack(side=tk.LEFT, padx=5)
self.quality_var = tk.StringVar(value="720p")
ttk.Combobox(quality_frame, textvariable=self.quality_var, values=["480p", "720p", "1080p"], width=10).pack(side=tk.LEFT, padx=5)
control_buttons = tk.Frame(control_frame)
control_buttons.pack(pady=5)
tk.Button(control_buttons, text="Tải Xuống", command=self.start_download_thread, width=15).pack(side=tk.LEFT, padx=5)
tk.Button(control_buttons, text="Tạm Dừng", command=self.pause_download, width=15).pack(side=tk.LEFT, padx=5)
tk.Button(control_buttons, text="Thoát", command=self.root.quit, width=15).pack(side=tk.LEFT, padx=5)
links_frame = tk.LabelFrame(self.root, text="Link Videos Cần Tải Xuống", font=("Arial", 10, "bold"), padx=10, pady=5)
links_frame.pack(fill=tk.X, padx=10, pady=5)
self.link_text = scrolledtext.ScrolledText(links_frame, height=10)
self.link_text.pack(fill=tk.BOTH, expand=True)
section4 = tk.Frame(self.root)
section4.pack(fill=tk.X, padx=10, pady=5)
logs_frame = tk.LabelFrame(section4, text="Logs", font=("Arial", 10, "bold"), padx=10, pady=5)
logs_frame.grid(row=0, column=0, sticky="nsew", padx=5)
self.log_text = scrolledtext.ScrolledText(logs_frame, width=45, height=10)
self.log_text.pack()
status_frame = tk.LabelFrame(section4, text="Tiến Trình Xử Lý", font=("Arial", 10, "bold"), padx=10, pady=5)
status_frame.grid(row=0, column=1, sticky="nsew", padx=5)
section4.grid_columnconfigure(0, weight=1, uniform="group4")
section4.grid_columnconfigure(1, weight=1, uniform="group4")
line1 = tk.Frame(status_frame)
line1.pack(fill=tk.X, pady=2)
tk.Label(line1, text="Video Hiện Tại:").pack(side=tk.LEFT)
self.progress_current = ttk.Progressbar(line1)
self.progress_current.pack(side=tk.LEFT, padx=5, fill='x', expand=True)
line2 = tk.Frame(status_frame)
line2.pack(fill=tk.X, pady=2)
tk.Label(line2, text="Tổng Thể:").pack(side=tk.LEFT)
self.progress_total = ttk.Progressbar(line2)
self.progress_total.pack(side=tk.LEFT, padx=5, fill='x', expand=True)
ttk.Separator(status_frame, orient='horizontal').pack(fill='x', pady=5)
self.label_total = tk.Label(status_frame, text="Tổng Số Videos : 0", font=("Arial", 9, "bold"), fg="red")
self.label_total.pack(anchor="w")
self.label_done = tk.Label(status_frame, text="Videos Đã Tải : 0", font=("Arial", 9, "bold"), fg="green")
self.label_done.pack(anchor="w")
self.label_pending = tk.Label(status_frame, text="Videos Đợi Tải : 0", font=("Arial", 9, "bold"), fg="blue")
self.label_pending.pack(anchor="w")
self.label_failed = tk.Label(status_frame, text="Videos Bị Lỗi : 0", font=("Arial", 9, "bold"), fg="black")
self.label_failed.pack(anchor="w")
def log(self, message):
self.log_text.insert(tk.END, message + "\n")
self.log_text.see(tk.END)
def update_counters(self):
self.label_total.config(text=f"Tổng Số Videos : {self.total_videos}")
self.label_done.config(text=f"Videos Đã Tải : {self.completed_videos}")
self.label_pending.config(text=f"Videos Đợi Tải : {self.pending_videos}")
self.label_failed.config(text=f"Videos Bị Lỗi : {self.failed_videos}")
def select_folder(self):
folder = filedialog.askdirectory()
if folder:
self.save_folder = folder
self.folder_path_entry.delete(0, tk.END)
self.folder_path_entry.insert(0, folder)
def pause_download(self):
self.pause_flag = True
self.log("⏸ Đã tạm dừng tải xuống.")
def start_download_thread(self):
if not self.save_folder:
messagebox.showwarning("Chưa chọn thư mục", "Vui lòng chọn thư mục lưu video.")
return
raw_lines = self.link_text.get("1.0", tk.END).strip().splitlines()
links = []
for line in raw_lines:
parts = line.strip().split("|", 1)
url = parts[0].strip()
filename = parts[1].strip() if len(parts) > 1 else None
links.append((url, filename))
if not links:
messagebox.showwarning("Chưa nhập link", "Vui lòng nhập ít nhất 1 link video Facebook.")
return
self.total_videos = len(links)
self.completed_videos = 0
self.pending_videos = self.total_videos
self.failed_videos = 0
self.update_counters()
self.downloading = True
self.pause_flag = False
threading.Thread(target=self.download_videos, args=(links,)).start()
def get_format_selector(self, preferred_quality):
if preferred_quality == "1080p":
return "bestvideo[height<=1080]+bestaudio/best"
elif preferred_quality == "720p":
return "bestvideo[height<=720]+bestaudio/best"
elif preferred_quality == "480p":
return "bestvideo[height<=480]+bestaudio/best"
return "best"
def download_videos(self, links):
self.progress_total["maximum"] = len(links)
self.progress_total["value"] = 0
for index, (url, custom_name) in enumerate(links):
if self.pause_flag:
break
self.log(f"🚀 Đang tải video {index + 1}/{len(links)}: {url}")
self.progress_current["value"] = 0
outtmpl = os.path.join(self.save_folder, custom_name + ".%(ext)s") if custom_name else os.path.join(self.save_folder, '%(title).100s.%(ext)s')
ydl_opts = {
'format': self.get_format_selector(self.quality_var.get()),
'outtmpl': outtmpl,
'quiet': True,
'noplaylist': True,
'progress_hooks': [self.hook],
'merge_output_format': 'mp4',
'windowsfilenames': True,
}
try:
with YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
self.completed_videos += 1
self.log(f"✅ Đã tải xong: {url}")
except Exception as e:
self.failed_videos += 1
self.log(f"❌ Lỗi tải video: {e}")
self.pending_videos -= 1
self.update_counters()
self.progress_total["value"] = index + 1
self.progress_current["value"] = 0
if not self.pause_flag:
self.log("🎉 Hoàn tất tất cả video.")
else:
self.log("⏸ Tải xuống đã bị tạm dừng.")
def hook(self, d):
if d['status'] == 'downloading':
total = d.get('total_bytes') or d.get('total_bytes_estimate')
downloaded = d.get('downloaded_bytes')
if total and downloaded:
try:
percent = (downloaded / total) * 100
self.progress_current["value"] = percent
except:
pass
if __name__ == "__main__":
root = tk.Tk()
app = FacebookDownloaderApp(root)
root.mainloop()