好友
阅读权限20
听众
最后登录1970-1-1
|
dork
发表于 2025-8-8 11:00
帮楼主转成代码模式,以方便后来人复制使用:
import tkinter as tk
from tkinter import ttk, filedialog, scrolledtext, messagebox
import os
import sys
import threading
import subprocess
import re
from datetime import datetime
class YoukuDownloader:
def __init__(self, root):
self.root = root
self.root.title("优酷视频下载工具")
self.root.geometry("800x600")
self.root.resizable(True, True)
# 设置中文字体支持
self.style = ttk.Style()
self.style.configure("TLabel", font=("SimHei", 10))
self.style.configure("TButton", font=("SimHei", 10))
self.style.configure("TEntry", font=("SimHei", 10))
self.style.configure("TCombobox", font=("SimHei", 10))
# 创建主框架
self.main_frame = ttk.Frame(root, padding="10")
self.main_frame.pack(fill=tk.BOTH, expand=True)
# URL输入区域
self.url_frame = ttk.LabelFrame(self.main_frame, text="视频URL", padding="10")
self.url_frame.pack(fill=tk.X, pady=5)
self.url_label = ttk.Label(self.url_frame, text="视频地址:")
self.url_label.pack(side=tk.LEFT, padx=5)
self.url_entry = ttk.Entry(self.url_frame)
self.url_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
self.url_entry.insert(0, "https://v.youku.com/")
# 输出路径选择区域
self.path_frame = ttk.LabelFrame(self.main_frame, text="保存设置", padding="10")
self.path_frame.pack(fill=tk.X, pady=5)
self.path_label = ttk.Label(self.path_frame, text="保存路径:")
self.path_label.pack(side=tk.LEFT, padx=5)
self.path_entry = ttk.Entry(self.path_frame)
self.path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
self.path_entry.insert(0, os.path.expanduser("~/Downloads"))
self.browse_btn = ttk.Button(self.path_frame, text="浏览...", command=self.browse_path)
self.browse_btn.pack(side=tk.LEFT, padx=5)
# 清晰度选择区域
self.quality_frame = ttk.LabelFrame(self.main_frame, text="下载设置", padding="10")
self.quality_frame.pack(fill=tk.X, pady=5)
self.quality_label = ttk.Label(self.quality_frame, text="清晰度:")
self.quality_label.pack(side=tk.LEFT, padx=5)
self.quality_var = tk.StringVar()
self.quality_combobox = ttk.Combobox(
self.quality_frame,
textvariable=self.quality_var,
state="readonly",
width=15
)
# 初始选项,后续会根据视频信息更新
self.quality_combobox['values'] = ["自动选择", "高清", "标清", "流畅"]
self.quality_combobox.current(0)
self.quality_combobox.pack(side=tk.LEFT, padx=5)
self.format_label = ttk.Label(self.quality_frame, text="输出格式:")
self.format_label.pack(side=tk.LEFT, padx=5)
self.format_var = tk.StringVar(value="mp4")
self.format_combobox = ttk.Combobox(
self.quality_frame,
textvariable=self.format_var,
state="readonly",
width=10
)
self.format_combobox['values'] = ["mp4", "flv", "webm"]
self.format_combobox.pack(side=tk.LEFT, padx=5)
# 按钮区域
self.btn_frame = ttk.Frame(self.main_frame, padding="10")
self.btn_frame.pack(fill=tk.X, pady=5)
self.fetch_btn = ttk.Button(self.btn_frame, text="获取视频信息", command=self.fetch_video_info)
self.fetch_btn.pack(side=tk.LEFT, padx=5)
self.download_btn = ttk.Button(self.btn_frame, text="开始下载", command=self.start_download)
self.download_btn.pack(side=tk.LEFT, padx=5)
self.cancel_btn = ttk.Button(self.btn_frame, text="取消下载", command=self.cancel_download)
self.cancel_btn.pack(side=tk.LEFT, padx=5)
self.cancel_btn.config(state=tk.DISABLED)
# 日志区域
self.log_frame = ttk.LabelFrame(self.main_frame, text="日志信息", padding="10")
self.log_frame.pack(fill=tk.BOTH, expand=True, pady=5)
self.log_text = scrolledtext.ScrolledText(self.log_frame, wrap=tk.WORD, font=("SimHei", 9))
self.log_text.pack(fill=tk.BOTH, expand=True)
self.log_text.config(state=tk.DISABLED)
# 下载线程和进程控制
self.download_thread = None
self.download_process = None
self.is_downloading = False
# 初始化日志
self.log("优酷视频下载工具已启动")
def browse_path(self):
"""选择保存路径"""
path = filedialog.askdirectory()
if path:
self.path_entry.delete(0, tk.END)
self.path_entry.insert(0, path)
def log(self, message):
"""添加日志信息"""
self.log_text.config(state=tk.NORMAL)
timestamp = datetime.now().strftime("%H:%M:%S")
self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
self.log_text.see(tk.END) # 滚动到最新日志
self.log_text.config(state=tk.DISABLED)
def fetch_video_info(self):
"""获取视频信息,主要是可用的清晰度"""
url = self.url_entry.get().strip()
if not url:
messagebox.showerror("错误", "请输入视频URL")
return
self.log("正在获取视频信息...")
# 在新线程中执行,避免UI卡顿
threading.Thread(target=self._do_fetch_info, args=(url,), daemon=True).start()
def _do_fetch_info(self, url):
"""实际执行获取视频信息的操作"""
try:
# 使用you-get查看视频信息
result = subprocess.run(
["you-get", "--info", url],
capture_output=True,
text=True,
encoding="utf-8",
errors="replace"
)
output = result.stdout + result.stderr
self.log("视频信息获取成功")
# 解析输出,提取可用的清晰度
qualities = []
# 简单的正则匹配,可能需要根据实际输出调整
pattern = re.compile(r'\[.*?\] (.*?) .*?(\d+x\d+)')
matches = pattern.findall(output)
if matches:
qualities = [f"{q[0]} ({q[1]})" for q in matches]
# 在UI线程中更新下拉框
self.root.after(0, self._update_quality_combobox, qualities)
else:
self.log("未识别到可用的清晰度信息,使用默认选项")
except Exception as e:
self.log(f"获取视频信息失败: {str(e)}")
def _update_quality_combobox(self, qualities):
"""更新清晰度选择下拉框"""
if qualities:
self.quality_combobox['values'] = ["自动选择"] + qualities
self.quality_combobox.current(0)
self.log(f"检测到可用清晰度: {', '.join(qualities)}")
def start_download(self):
"""开始下载视频"""
url = self.url_entry.get().strip()
output_path = self.path_entry.get().strip()
quality = self.quality_var.get()
output_format = self.format_var.get()
if not url:
messagebox.showerror("错误", "请输入视频URL")
return
if not output_path or not os.path.isdir(output_path):
messagebox.showerror("错误", "请选择有效的保存路径")
return
self.is_downloading = True
self.download_btn.config(state=tk.DISABLED)
self.cancel_btn.config(state=tk.NORMAL)
self.fetch_btn.config(state=tk.DISABLED)
self.log(f"开始下载视频: {url}")
self.log(f"保存路径: {output_path}")
self.log(f"选择清晰度: {quality}")
self.log(f"输出格式: {output_format}")
# 在新线程中执行下载,避免UI卡顿
self.download_thread = threading.Thread(
target=self._do_download,
args=(url, output_path, quality, output_format),
daemon=True
)
self.download_thread.start()
def _do_download(self, url, output_path, quality, output_format):
"""实际执行下载操作"""
try:
# 构建you-get命令
cmd = [
"you-get",
"-o", output_path,
"-O", f"video.{output_format}", # 输出文件名
]
# 如果不是自动选择,添加清晰度参数
if quality != "自动选择":
# 提取清晰度名称
quality_name = quality.split()[0]
cmd.extend(["-Q", quality_name])
cmd.append(url)
# 执行下载命令
self.download_process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
encoding="utf-8",
errors="replace"
)
# 实时输出日志
for line in self.download_process.stdout:
if not self.is_downloading:
break
self.log(line.strip())
# 等待进程结束
self.download_process.wait()
if self.is_downloading:
if self.download_process.returncode == 0:
self.log("视频下载完成!")
self.root.after(0, lambda: messagebox.showinfo("成功", "视频下载完成!"))
else:
self.log(f"下载失败,返回代码: {self.download_process.returncode}")
self.root.after(0, lambda: messagebox.showerror("失败", "视频下载失败!"))
except Exception as e:
self.log(f"下载过程中发生错误: {str(e)}")
self.root.after(0, lambda: messagebox.showerror("错误", f"下载过程中发生错误: {str(e)}"))
finally:
self.is_downloading = False
self.root.after(0, self._download_finished)
def _download_finished(self):
"""下载完成后更新UI状态"""
self.download_btn.config(state=tk.NORMAL)
self.cancel_btn.config(state=tk.DISABLED)
self.fetch_btn.config(state=tk.NORMAL)
self.download_process = None
self.download_thread = None
def cancel_download(self):
"""取消下载"""
if self.is_downloading and self.download_process:
if messagebox.askyesno("确认", "确定要取消下载吗?"):
self.log("正在取消下载...")
self.is_downloading = False
# 终止下载进程
try:
self.download_process.terminate()
self.log("下载已取消")
except Exception as e:
self.log(f"取消下载时发生错误: {str(e)}")
if __name__ == "__main__":
# 检查是否安装了you-get
try:
subprocess.run(["you-get", "--version"], capture_output=True, check=True)
except (subprocess.SubprocessError, FileNotFoundError):
print("未检测到you-get,请先安装: pip install you-get")
sys.exit(1)
root = tk.Tk()
app = YoukuDownloader(root)
root.mainloop()
[Python] 纯文本查看 复制代码 import tkinter as tk
from tkinter import ttk, filedialog, scrolledtext, messagebox
import os
import sys
import threading
import subprocess
import re
from datetime import datetime
class YoukuDownloader:
def __init__(self, root):
self.root = root
self.root.title("优酷视频下载工具")
self.root.geometry("800x600")
self.root.resizable(True, True)
# 设置中文字体支持
self.style = ttk.Style()
self.style.configure("TLabel", font=("SimHei", 10))
self.style.configure("TButton", font=("SimHei", 10))
self.style.configure("TEntry", font=("SimHei", 10))
self.style.configure("TCombobox", font=("SimHei", 10))
# 创建主框架
self.main_frame = ttk.Frame(root, padding="10")
self.main_frame.pack(fill=tk.BOTH, expand=True)
# URL输入区域
self.url_frame = ttk.LabelFrame(self.main_frame, text="视频URL", padding="10")
self.url_frame.pack(fill=tk.X, pady=5)
self.url_label = ttk.Label(self.url_frame, text="视频地址:")
self.url_label.pack(side=tk.LEFT, padx=5)
self.url_entry = ttk.Entry(self.url_frame)
self.url_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
self.url_entry.insert(0, "https://v.youku.com/")
# 输出路径选择区域
self.path_frame = ttk.LabelFrame(self.main_frame, text="保存设置", padding="10")
self.path_frame.pack(fill=tk.X, pady=5)
self.path_label = ttk.Label(self.path_frame, text="保存路径:")
self.path_label.pack(side=tk.LEFT, padx=5)
self.path_entry = ttk.Entry(self.path_frame)
self.path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
self.path_entry.insert(0, os.path.expanduser("~/Downloads"))
self.browse_btn = ttk.Button(self.path_frame, text="浏览...", command=self.browse_path)
self.browse_btn.pack(side=tk.LEFT, padx=5)
# 清晰度选择区域
self.quality_frame = ttk.LabelFrame(self.main_frame, text="下载设置", padding="10")
self.quality_frame.pack(fill=tk.X, pady=5)
self.quality_label = ttk.Label(self.quality_frame, text="清晰度:")
self.quality_label.pack(side=tk.LEFT, padx=5)
self.quality_var = tk.StringVar()
self.quality_combobox = ttk.Combobox(
self.quality_frame,
textvariable=self.quality_var,
state="readonly",
width=15
)
# 初始选项,后续会根据视频信息更新
self.quality_combobox['values'] = ["自动选择", "高清", "标清", "流畅"]
self.quality_combobox.current(0)
self.quality_combobox.pack(side=tk.LEFT, padx=5)
self.format_label = ttk.Label(self.quality_frame, text="输出格式:")
self.format_label.pack(side=tk.LEFT, padx=5)
self.format_var = tk.StringVar(value="mp4")
self.format_combobox = ttk.Combobox(
self.quality_frame,
textvariable=self.format_var,
state="readonly",
width=10
)
self.format_combobox['values'] = ["mp4", "flv", "webm"]
self.format_combobox.pack(side=tk.LEFT, padx=5)
# 按钮区域
self.btn_frame = ttk.Frame(self.main_frame, padding="10")
self.btn_frame.pack(fill=tk.X, pady=5)
self.fetch_btn = ttk.Button(self.btn_frame, text="获取视频信息", command=self.fetch_video_info)
self.fetch_btn.pack(side=tk.LEFT, padx=5)
self.download_btn = ttk.Button(self.btn_frame, text="开始下载", command=self.start_download)
self.download_btn.pack(side=tk.LEFT, padx=5)
self.cancel_btn = ttk.Button(self.btn_frame, text="取消下载", command=self.cancel_download)
self.cancel_btn.pack(side=tk.LEFT, padx=5)
self.cancel_btn.config(state=tk.DISABLED)
# 日志区域
self.log_frame = ttk.LabelFrame(self.main_frame, text="日志信息", padding="10")
self.log_frame.pack(fill=tk.BOTH, expand=True, pady=5)
self.log_text = scrolledtext.ScrolledText(self.log_frame, wrap=tk.WORD, font=("SimHei", 9))
self.log_text.pack(fill=tk.BOTH, expand=True)
self.log_text.config(state=tk.DISABLED)
# 下载线程和进程控制
self.download_thread = None
self.download_process = None
self.is_downloading = False
# 初始化日志
self.log("优酷视频下载工具已启动")
def browse_path(self):
"""选择保存路径"""
path = filedialog.askdirectory()
if path:
self.path_entry.delete(0, tk.END)
self.path_entry.insert(0, path)
def log(self, message):
"""添加日志信息"""
self.log_text.config(state=tk.NORMAL)
timestamp = datetime.now().strftime("%H:%M:%S")
self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
self.log_text.see(tk.END) # 滚动到最新日志
self.log_text.config(state=tk.DISABLED)
def fetch_video_info(self):
"""获取视频信息,主要是可用的清晰度"""
url = self.url_entry.get().strip()
if not url:
messagebox.showerror("错误", "请输入视频URL")
return
self.log("正在获取视频信息...")
# 在新线程中执行,避免UI卡顿
threading.Thread(target=self._do_fetch_info, args=(url,), daemon=True).start()
def _do_fetch_info(self, url):
"""实际执行获取视频信息的操作"""
try:
# 使用you-get查看视频信息
result = subprocess.run(
["you-get", "--info", url],
capture_output=True,
text=True,
encoding="utf-8",
errors="replace"
)
output = result.stdout + result.stderr
self.log("视频信息获取成功")
# 解析输出,提取可用的清晰度
qualities = []
# 简单的正则匹配,可能需要根据实际输出调整
pattern = re.compile(r'\[.*?\] (.*?) .*?(\d+x\d+)')
matches = pattern.findall(output)
if matches:
qualities = [f"{q[0]} ({q[1]})" for q in matches]
# 在UI线程中更新下拉框
self.root.after(0, self._update_quality_combobox, qualities)
else:
self.log("未识别到可用的清晰度信息,使用默认选项")
except Exception as e:
self.log(f"获取视频信息失败: {str(e)}")
def _update_quality_combobox(self, qualities):
"""更新清晰度选择下拉框"""
if qualities:
self.quality_combobox['values'] = ["自动选择"] + qualities
self.quality_combobox.current(0)
self.log(f"检测到可用清晰度: {', '.join(qualities)}")
def start_download(self):
"""开始下载视频"""
url = self.url_entry.get().strip()
output_path = self.path_entry.get().strip()
quality = self.quality_var.get()
output_format = self.format_var.get()
if not url:
messagebox.showerror("错误", "请输入视频URL")
return
if not output_path or not os.path.isdir(output_path):
messagebox.showerror("错误", "请选择有效的保存路径")
return
self.is_downloading = True
self.download_btn.config(state=tk.DISABLED)
self.cancel_btn.config(state=tk.NORMAL)
self.fetch_btn.config(state=tk.DISABLED)
self.log(f"开始下载视频: {url}")
self.log(f"保存路径: {output_path}")
self.log(f"选择清晰度: {quality}")
self.log(f"输出格式: {output_format}")
# 在新线程中执行下载,避免UI卡顿
self.download_thread = threading.Thread(
target=self._do_download,
args=(url, output_path, quality, output_format),
daemon=True
)
self.download_thread.start()
def _do_download(self, url, output_path, quality, output_format):
"""实际执行下载操作"""
try:
# 构建you-get命令
cmd = [
"you-get",
"-o", output_path,
"-O", f"video.{output_format}", # 输出文件名
]
# 如果不是自动选择,添加清晰度参数
if quality != "自动选择":
# 提取清晰度名称
quality_name = quality.split()[0]
cmd.extend(["-Q", quality_name])
cmd.append(url)
# 执行下载命令
self.download_process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
encoding="utf-8",
errors="replace"
)
# 实时输出日志
for line in self.download_process.stdout:
if not self.is_downloading:
break
self.log(line.strip())
# 等待进程结束
self.download_process.wait()
if self.is_downloading:
if self.download_process.returncode == 0:
self.log("视频下载完成!")
self.root.after(0, lambda: messagebox.showinfo("成功", "视频下载完成!"))
else:
self.log(f"下载失败,返回代码: {self.download_process.returncode}")
self.root.after(0, lambda: messagebox.showerror("失败", "视频下载失败!"))
except Exception as e:
self.log(f"下载过程中发生错误: {str(e)}")
self.root.after(0, lambda: messagebox.showerror("错误", f"下载过程中发生错误: {str(e)}"))
finally:
self.is_downloading = False
self.root.after(0, self._download_finished)
def _download_finished(self):
"""下载完成后更新UI状态"""
self.download_btn.config(state=tk.NORMAL)
self.cancel_btn.config(state=tk.DISABLED)
self.fetch_btn.config(state=tk.NORMAL)
self.download_process = None
self.download_thread = None
def cancel_download(self):
"""取消下载"""
if self.is_downloading and self.download_process:
if messagebox.askyesno("确认", "确定要取消下载吗?"):
self.log("正在取消下载...")
self.is_downloading = False
# 终止下载进程
try:
self.download_process.terminate()
self.log("下载已取消")
except Exception as e:
self.log(f"取消下载时发生错误: {str(e)}")
if __name__ == "__main__":
# 检查是否安装了you-get
try:
subprocess.run(["you-get", "--version"], capture_output=True, check=True)
except (subprocess.SubprocessError, FileNotFoundError):
print("未检测到you-get,请先安装: pip install you-get")
sys.exit(1)
root = tk.Tk()
app = YoukuDownloader(root)
root.mainloop()
|
|