import os
import subprocess
import time
import requests
from bs4 import BeautifulSoup
from tkinter import (
Tk, Label, Entry, Button, Text, Scrollbar, Canvas,
Frame, LabelFrame, filedialog, StringVar, END, RIGHT, Y, LEFT, BOTH
)
from PIL import Image, ImageTk
from io import BytesIO
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 FBThumbDownloader:
def __init__(self, root):
self.root = root
self.root.title("Facebook Thumbnail Downloader - DarkCorners")
self.root.resizable(False, False)
center_window(self.root, 800, 600)
self.folder_path = ""
self.folder_var = StringVar()
self.is_paused = False
self.tk_img = None
# === HEADER ===
Label(root, text="Facebook Thumbnail Downloader - DarkCorners", font=("Helvetica", 16, "bold"), fg="blue").pack(pady=(10, 0))
# === CONTROL PANEL ===
top_frame = Frame(root)
top_frame.pack(padx=10, pady=10, fill=BOTH)
top_frame.grid_columnconfigure(0, weight=1, uniform="half")
top_frame.grid_columnconfigure(1, weight=1, uniform="half")
# Left control block (folder selection)
left_panel = LabelFrame(top_frame, text="Thư Mục Lưu Ảnh", font=("Arial", 10, "bold"), bd=2, relief="groove")
left_panel.grid(row=0, column=0, sticky="nsew", padx=5)
self.folder_entry = Entry(left_panel, textvariable=self.folder_var, state='readonly')
self.folder_entry.pack(padx=10, pady=2, fill=BOTH)
folder_btn_frame = Frame(left_panel)
folder_btn_frame.pack(pady=2)
Button(folder_btn_frame, text="Chọn thư mục", command=self.choose_folder).pack(side=LEFT, padx=5)
Button(folder_btn_frame, text="Mở thư mục", command=self.open_folder).pack(side=LEFT, padx=5)
# Right control block (delay + buttons)
right_panel = LabelFrame(top_frame, text="Điều Khiển", font=("Arial", 10, "bold"), bd=2, relief="groove")
right_panel.grid(row=0, column=1, sticky="nsew", padx=5)
delay_frame = Frame(right_panel)
delay_frame.pack(pady=2, anchor='center')
Label(delay_frame, text="Delay :").pack(side=LEFT)
self.delay_entry = Entry(delay_frame, width=5)
self.delay_entry.insert(0, "3")
self.delay_entry.pack(side=LEFT, padx=5)
Button(delay_frame, text="Tải tất cả ảnh", command=self.process_all).pack(side=LEFT, padx=5)
self.pause_btn = Button(delay_frame, text="Tạm Dừng", command=self.toggle_pause)
self.pause_btn.pack(side=LEFT, padx=5)
Button(delay_frame, text="Thoát", command=self.root.quit).pack(side=LEFT, padx=5)
btn_frame = Frame(right_panel)
btn_frame.pack(pady=2)
self.counter_total = Label(btn_frame, text="Tổng Số : 0", font=("Arial", 9, "bold"), fg="red")
self.counter_total.pack(side=LEFT, padx=5)
self.counter_success = Label(btn_frame, text="Đã Tải : 0", font=("Arial", 9, "bold"), fg="green")
self.counter_success.pack(side=LEFT, padx=5)
self.counter_pending = Label(btn_frame, text="Đợi Tải : 0", font=("Arial", 9, "bold"), fg="blue")
self.counter_pending.pack(side=LEFT, padx=5)
self.counter_error = Label(btn_frame, text="Bị Lỗi : 0", font=("Arial", 9, "bold"), fg="black")
self.counter_error.pack(side=LEFT, padx=5)
# === FORM 2 ===
form2_frame = LabelFrame(root, text="Danh Sách Liên Kết Facebook Videos - FacebookURL|Filename.jpg", font=("Arial", 10, "bold"), bd=2, relief="groove")
form2_frame.pack(padx=10, pady=5, fill=BOTH)
self.textbox = Text(form2_frame, width=100, height=8)
self.textbox.pack(padx=10, pady=5, fill=BOTH)
# === FORM 3 + 4 ===
bottom_frame = Frame(root)
bottom_frame.pack(pady=10, fill=BOTH, expand=True)
# FORM 3: Canvas hiển thị ảnh
left_frame = LabelFrame(bottom_frame, text="Hình Ảnh Mới Nhất", font=("Arial", 10, "bold"), bd=2, relief="groove")
left_frame.pack(side=LEFT, padx=10, fill=BOTH, expand=True)
self.canvas = Canvas(left_frame, width=400, height=250, bg='white')
self.canvas.pack(expand=True)
self.canvas.create_text(200, 125, text="Chưa có ảnh", fill="gray")
# FORM 4: Log trạng thái
right_frame = LabelFrame(bottom_frame, text="Trạng Thái Tải Xuống", font=("Arial", 10, "bold"), bd=2, relief="groove")
right_frame.pack(side=LEFT, padx=10, fill=BOTH, expand=True)
self.logbox = Text(right_frame, width=50, height=14, fg="green", wrap='word')
self.logbox.pack(side="left", fill=BOTH, expand=True)
self.scrollbar = Scrollbar(right_frame, command=self.logbox.yview)
self.scrollbar.pack(side="right", fill="y")
self.logbox.config(yscrollcommand=self.scrollbar.set)
def log(self, message):
self.logbox.insert(END, message + '\n')
self.logbox.see(END)
def choose_folder(self):
self.folder_path = filedialog.askdirectory()
if self.folder_path:
self.folder_var.set(self.folder_path)
self.log(f"📁 Đã chọn thư mục: {self.folder_path}")
def open_folder(self):
if self.folder_path and os.path.exists(self.folder_path):
subprocess.Popen(f'explorer "{self.folder_path}"')
else:
self.log("⚠️ Chưa chọn thư mục hoặc thư mục không tồn tại!")
def toggle_pause(self):
self.is_paused = not self.is_paused
if self.is_paused:
self.pause_btn.config(text="▶️ Tiếp Tục")
self.log("⏸️ Đã tạm dừng.")
else:
self.pause_btn.config(text="⏸️ Tạm Dừng")
self.log("✅ Tiếp tục.")
def process_all(self):
lines = self.textbox.get("1.0", END).strip().split("\n")
total = len([l for l in lines if '|' in l])
success = 0
error = 0
pending = total
self.counter_total.config(text=f"Tổng Số : {total}")
self.counter_success.config(text=f"Đã Tải : {success}")
self.counter_pending.config(text=f"Đợi Tải : {pending}")
self.counter_error.config(text=f"Bị Lỗi : {error}")
try:
delay = int(self.delay_entry.get())
if delay < 0 or delay > 60:
self.log("⚠️ Thời gian chờ phải từ 1 đến 60 giây. Dùng mặc định 3s.")
delay = 3
except:
delay = 3
for i, line in enumerate(lines, start=1):
while self.is_paused:
self.log("⏸️ Đang tạm dừng... Đợi tiếp tục.")
self.root.update()
time.sleep(1)
if "|" not in line:
self.log(f"⛔ Dòng {i}: Sai định dạng (thiếu dấu |): {line}")
continue
url, filename = line.strip().split("|", 1)
success_download = self.download_and_save(url.strip(), filename.strip())
if success_download:
success += 1
else:
error += 1
pending = total - (success + error)
self.counter_success.config(text=f"Đã Tải : {success}")
self.counter_error.config(text=f"Bị Lỗi : {error}")
self.counter_pending.config(text=f"Đợi Tải : {pending}")
# Đếm ngược
for s in range(delay, 0, -1):
self.log(f"⏳ Chờ {s} giây trước khi xử lý dòng tiếp theo...")
self.root.update()
time.sleep(1)
def download_and_save(self, url, filename):
success = False
try:
headers = {'User-Agent': 'Mozilla/5.0'}
r = requests.get(url, headers=headers, timeout=10)
soup = BeautifulSoup(r.text, 'html.parser')
meta = soup.find("meta", property="og:image")
if not meta:
self.log(f"❌ Không tìm thấy thumbnail cho: {url}")
return success
thumb_url = meta["content"]
img_data = requests.get(thumb_url).content
save_path = os.path.join(self.folder_path, filename)
with open(save_path, 'wb') as f:
f.write(img_data)
self.log(f"✅ Đã lưu ảnh: {filename}")
success = True
img = Image.open(BytesIO(img_data))
img.thumbnail((400, 250))
self.tk_img = ImageTk.PhotoImage(img)
self.canvas.delete("all")
self.canvas.create_image(200, 125, anchor='center', image=self.tk_img)
except Exception as e:
self.log(f"⚠️ Lỗi khi xử lý {url}: {e}")
return success
if __name__ == "__main__":
root = Tk()
app = FBThumbDownloader(root)
root.mainloop()