Add Douyin video downloader with auto-cookie feature

This commit is contained in:
2026-03-02 21:42:52 +08:00
commit 0f3dd8f287
50 changed files with 6471 additions and 0 deletions

382
douyin_ui.py Normal file
View File

@@ -0,0 +1,382 @@
import tkinter as tk
from tkinter import ttk, filedialog, scrolledtext, messagebox
import subprocess
import threading
import os
import re
PYTHON_PATH = r"C:\Program Files\Python311\python.exe"
COOKIE_FILE = r"C:\Users\Administrator\Desktop\TikTokDownload-main\douyin_cookie.txt"
class DouyinDownloaderUI:
def __init__(self, root):
self.root = root
self.root.title("抖音去水印下载器 v3.0 (自动Cookie)")
self.root.geometry("800x700")
self.root.resizable(True, True)
self.download_dir = os.path.join(os.path.expanduser("~"), "Downloads", "DouyinDownloads")
os.makedirs(self.download_dir, exist_ok=True)
self.is_downloading = False
self.cookie = None
self.setup_ui()
self.auto_load_cookie()
def auto_load_cookie(self):
"""启动时自动加载Cookie"""
if os.path.exists(COOKIE_FILE):
try:
with open(COOKIE_FILE, 'r', encoding='utf-8') as f:
self.cookie = f.read().strip()
if self.cookie and len(self.cookie) > 100:
self.update_cookie_status(True)
self.log("✓ 已自动加载Cookie", "SUCCESS")
return
except:
pass
self.update_cookie_status(False)
self.log(" 未检测到Cookie首次下载时将自动获取", "INFO")
def update_cookie_status(self, has_cookie):
"""更新Cookie状态指示"""
if has_cookie:
self.cookie_status.config(text="● Cookie: 有效", foreground="green")
self.cookie_entry.delete(0, tk.END)
self.cookie_entry.insert(0, "[已自动获取Cookie]")
else:
self.cookie_status.config(text="● Cookie: 未获取", foreground="gray")
def setup_ui(self):
style = ttk.Style()
style.theme_use('clam')
main_frame = ttk.Frame(self.root, padding="15")
main_frame.pack(fill=tk.BOTH, expand=True)
# Title
title_label = ttk.Label(main_frame, text="抖音/TikTok 去水印下载器", font=('Microsoft YaHei', 18, 'bold'))
title_label.pack(pady=(0, 5))
subtitle = ttk.Label(main_frame, text="自动Cookie | 无需登录 | 一键下载", font=('Microsoft YaHei', 10), foreground="gray")
subtitle.pack(pady=(0, 10))
# Cookie Status Frame
cookie_frame = ttk.LabelFrame(main_frame, text="Cookie状态", padding="10")
cookie_frame.pack(fill=tk.X, pady=(0, 10))
self.cookie_status = ttk.Label(cookie_frame, text="● Cookie: 检测中...", font=('Microsoft YaHei', 10, 'bold'))
self.cookie_status.pack(side=tk.LEFT, padx=5)
ttk.Button(cookie_frame, text="自动获取Cookie", command=self.auto_get_cookie_ui, width=15).pack(side=tk.LEFT, padx=10)
ttk.Label(cookie_frame, text="(首次下载时会自动获取,无需手动操作)",
font=('Microsoft YaHei', 8), foreground="gray").pack(side=tk.LEFT, padx=5)
# Hidden Cookie Entry (for compatibility)
self.cookie_entry = ttk.Entry(cookie_frame, width=20, state='disabled')
# URL Frame
input_frame = ttk.LabelFrame(main_frame, text="下载设置", padding="10")
input_frame.pack(fill=tk.X, pady=(0, 10))
ttk.Label(input_frame, text="分享链接:", font=('Microsoft YaHei', 10)).grid(row=0, column=0, sticky=tk.W, pady=5)
self.url_entry = ttk.Entry(input_frame, width=80, font=('Microsoft YaHei', 10))
self.url_entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.EW)
self.url_entry.bind('<Control-v>', self.paste_clipboard)
ttk.Label(input_frame, text="下载模式:", font=('Microsoft YaHei', 10)).grid(row=1, column=0, sticky=tk.W, pady=5)
self.mode_var = tk.StringVar(value="one")
mode_frame = ttk.Frame(input_frame)
mode_frame.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5)
modes = [
("单个作品", "one"),
("用户主页", "post"),
("用户喜欢", "like"),
("用户收藏", "collects"),
("直播", "live"),
]
for i, (text, value) in enumerate(modes):
rb = ttk.Radiobutton(mode_frame, text=text, value=value, variable=self.mode_var)
rb.pack(side=tk.LEFT, padx=8)
ttk.Label(input_frame, text="保存目录:", font=('Microsoft YaHei', 10)).grid(row=2, column=0, sticky=tk.W, pady=5)
dir_frame = ttk.Frame(input_frame)
dir_frame.grid(row=2, column=1, sticky=tk.EW, padx=5, pady=5)
self.dir_entry = ttk.Entry(dir_frame, textvariable=tk.StringVar(value=self.download_dir), font=('Microsoft YaHei', 10))
self.dir_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
ttk.Button(dir_frame, text="浏览", command=self.browse_dir, width=8).pack(side=tk.LEFT, padx=(5, 0))
input_frame.columnconfigure(1, weight=1)
# Options Frame
options_frame = ttk.LabelFrame(main_frame, text="选项", padding="10")
options_frame.pack(fill=tk.X, pady=(0, 10))
self.download_music = tk.BooleanVar(value=False)
self.download_cover = tk.BooleanVar(value=False)
ttk.Checkbutton(options_frame, text="下载原声", variable=self.download_music).pack(side=tk.LEFT, padx=10)
ttk.Checkbutton(options_frame, text="下载封面", variable=self.download_cover).pack(side=tk.LEFT, padx=10)
ttk.Label(options_frame, text="作品数量:").pack(side=tk.LEFT, padx=(20, 5))
self.limit_entry = ttk.Entry(options_frame, width=8)
self.limit_entry.insert(0, "0")
self.limit_entry.pack(side=tk.LEFT)
ttk.Label(options_frame, text="(0=全部)").pack(side=tk.LEFT, padx=5)
# Buttons
btn_frame = ttk.Frame(main_frame)
btn_frame.pack(fill=tk.X, pady=10)
self.download_btn = ttk.Button(btn_frame, text="开始下载", command=self.start_download)
self.download_btn.pack(side=tk.LEFT, padx=5, ipadx=20, ipady=5)
self.stop_btn = ttk.Button(btn_frame, text="停止", command=self.stop_download, state=tk.DISABLED)
self.stop_btn.pack(side=tk.LEFT, padx=5, ipadx=20, ipady=5)
ttk.Button(btn_frame, text="打开目录", command=self.open_download_dir).pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="清空日志", command=self.clear_log).pack(side=tk.RIGHT, padx=5)
# Log Frame
log_frame = ttk.LabelFrame(main_frame, text="下载日志", padding="5")
log_frame.pack(fill=tk.BOTH, expand=True)
self.log_text = scrolledtext.ScrolledText(log_frame, height=15, font=('Consolas', 9),
bg='#1e1e1e', fg='#00ff00', insertbackground='white')
self.log_text.pack(fill=tk.BOTH, expand=True)
# Progress
self.progress_var = tk.DoubleVar()
self.progress_bar = ttk.Progressbar(main_frame, variable=self.progress_var, maximum=100)
self.progress_bar.pack(fill=tk.X, pady=(10, 0))
self.status_label = ttk.Label(main_frame, text="就绪", font=('Microsoft YaHei', 9))
self.status_label.pack(pady=5)
def auto_get_cookie_ui(self):
"""UI线程自动获取Cookie"""
self.log("正在自动获取Cookie...", "INFO")
self.status_label.config(text="正在获取Cookie...")
self.download_btn.config(state=tk.DISABLED)
thread = threading.Thread(target=self.auto_get_cookie_thread)
thread.daemon = True
thread.start()
def auto_get_cookie_thread(self):
"""后台线程自动获取Cookie"""
try:
from auto_cookie import get_douyin_cookie_auto
# 强制刷新,使用有界面模式(更稳定)
cookie = get_douyin_cookie_auto(force_refresh=True, headless=False)
if cookie and len(cookie) > 100:
self.cookie = cookie
self.root.after(0, lambda: self.update_cookie_status(True))
self.root.after(0, lambda: self.log("✓ Cookie获取成功", "SUCCESS"))
self.root.after(0, lambda: self.status_label.config(text="Cookie已就绪"))
else:
self.root.after(0, lambda: self.log("✗ Cookie获取失败", "ERROR"))
self.root.after(0, lambda: self.status_label.config(text="Cookie获取失败"))
except Exception as e:
self.root.after(0, lambda: self.log(f"✗ Cookie获取失败: {str(e)}", "ERROR"))
self.root.after(0, lambda: self.status_label.config(text="Cookie获取失败"))
finally:
self.root.after(0, lambda: self.download_btn.config(state=tk.NORMAL))
def paste_clipboard(self, event=None):
try:
clipboard = self.root.clipboard_get()
self.url_entry.delete(0, tk.END)
self.url_entry.insert(0, clipboard)
except:
pass
return "break"
def browse_dir(self):
dir_path = filedialog.askdirectory(initialdir=self.download_dir)
if dir_path:
self.download_dir = dir_path
self.dir_entry.delete(0, tk.END)
self.dir_entry.insert(0, dir_path)
def open_download_dir(self):
os.startfile(self.download_dir)
def clear_log(self):
self.log_text.delete(1.0, tk.END)
def log(self, message, level="INFO"):
self.log_text.insert(tk.END, f"[{level}] {message}\n")
self.log_text.see(tk.END)
self.root.update_idletasks()
def extract_url(self, text):
match = re.search(r'https?://[^\s<>"{}|\\^`\[\]]+', text)
if match:
return match.group(0)
return text
def ensure_cookie(self):
"""确保有Cookie如果没有则自动获取"""
if self.cookie and len(self.cookie) > 100:
return True
self.log("检测到无Cookie正在自动获取...", "INFO")
try:
from auto_cookie import get_douyin_cookie_auto
# 自动获取,使用无头模式(不打扰用户)
cookie = get_douyin_cookie_auto(force_refresh=False, headless=True)
if cookie and len(cookie) > 100:
self.cookie = cookie
self.root.after(0, lambda: self.update_cookie_status(True))
self.log("✓ Cookie自动获取成功", "SUCCESS")
return True
else:
self.log("✗ 自动获取Cookie失败", "ERROR")
return False
except Exception as e:
self.log(f"✗ 自动获取Cookie失败: {str(e)}", "ERROR")
return False
def start_download(self):
url = self.url_entry.get().strip()
if not url:
messagebox.showwarning("提示", "请输入分享链接!")
return
# 确保有Cookie
if not self.ensure_cookie():
messagebox.showerror("错误", "无法获取Cookie请检查网络或浏览器是否正常")
return
url = self.extract_url(url)
self.log(f"解析链接: {url}", "INFO")
self.is_downloading = True
self.download_btn.config(state=tk.DISABLED)
self.stop_btn.config(state=tk.NORMAL)
self.progress_var.set(0)
thread = threading.Thread(target=self.download_thread, args=(url,))
thread.daemon = True
thread.start()
def download_thread(self, url):
try:
self.log("开始下载任务...", "INFO")
self.status_label.config(text="正在下载...")
cmd = [PYTHON_PATH, "-m", "f2", "dy"]
mode = self.mode_var.get()
cmd.extend(["-M", mode])
cmd.extend(["-u", url])
download_dir = self.dir_entry.get().strip()
cmd.extend(["-p", download_dir])
# 使用自动获取的Cookie
if self.cookie:
cmd.extend(["-k", self.cookie])
if self.download_music.get():
cmd.extend(["-m", "true"])
if self.download_cover.get():
cmd.extend(["-v", "true"])
limit = self.limit_entry.get().strip()
if limit and limit != "0":
cmd.extend(["-o", limit])
self.log("执行下载命令...", "INFO")
env = os.environ.copy()
env["PYTHONIOENCODING"] = "utf-8"
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
encoding='utf-8',
errors='replace',
env=env,
bufsize=1
)
download_failed = False
while self.is_downloading:
if process.stdout is None:
break
line = process.stdout.readline()
if not line and process.poll() is not None:
break
if line:
line = line.strip()
if line:
self.log(line, "INFO")
if "下载" in line or "成功" in line or "保存" in line:
self.progress_var.set(min(self.progress_var.get() + 10, 90))
# 检测Cookie相关的错误
if "cookie" in line.lower() or "登录" in line or "失败" in line:
download_failed = True
if not self.is_downloading:
process.terminate()
self.log("下载已停止", "WARNING")
elif process.returncode == 0:
self.progress_var.set(100)
self.log("✓ 下载完成!", "SUCCESS")
self.status_label.config(text="下载完成!")
self.root.after(0, lambda: messagebox.showinfo("完成", f"下载完成!\n\n保存位置: {download_dir}"))
elif download_failed:
self.log("检测到Cookie可能过期尝试刷新...", "WARNING")
# 重新获取Cookie
from auto_cookie import get_douyin_cookie_auto
new_cookie = get_douyin_cookie_auto(force_refresh=True, headless=True)
if new_cookie:
self.cookie = new_cookie
self.log("Cookie已刷新请重试下载", "INFO")
self.status_label.config(text="Cookie已刷新请重试")
else:
self.log("下载过程中出现错误", "WARNING")
self.status_label.config(text="下载失败")
except Exception as e:
self.log(f"错误: {str(e)}", "ERROR")
self.status_label.config(text="下载失败")
self.root.after(0, lambda: messagebox.showerror("错误", f"下载失败:\n{str(e)}"))
finally:
self.is_downloading = False
self.download_btn.config(state=tk.NORMAL)
self.stop_btn.config(state=tk.DISABLED)
def stop_download(self):
self.is_downloading = False
def main():
root = tk.Tk()
app = DouyinDownloaderUI(root)
root.mainloop()
if __name__ == "__main__":
main()