Files
tiktok/douyin_ui.py

383 lines
16 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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()