YouTube 下載器
使用 Python 的 Tkinter 搭配 yt-dlp 程式庫是目前最穩定且推薦的方法。由於 YouTube 經常更新演算法,傳統的 pytube 庫有時會失效,而 yt-dlp 維護頻率更高、速度更快。
安裝 FFmpeg (重要):
YouTube 的高畫質影片(1080p 以上)是影像與聲音分離的。
如果你下載後發現沒有聲音或無法合併,請下載
ffmpeg.exe並放在此 Python 程式碼的同一個資料夾下。
JS Runtime 警告:
如果執行時終端機出現
No supported JavaScript runtime,建議到 安裝穩定版,安裝後不需設定,重啟程式即可。Node.js 官網
打包成 EXE:
如果您要使用 PyInstaller 打包,記得要在安裝了
yt-dlp的環境下執行:pyinstaller --noconsole --onefile 您的檔名.py
import tkinter as tk
from tkinter import messagebox, filedialog, ttk
import yt_dlp
import threading
import os
class YTDownloader:
def __init__(self, root):
self.root = root
self.root.title("YouTube 高階下載器 (yt-dlp 版)")
self.root.geometry("600x350")
# 設定介面樣式
self.setup_ui()
def setup_ui(self):
# 標題
tk.Label(self.root, text="YouTube 影片下載工具", font=("Arial", 16, "bold")).pack(pady=10)
# 網址輸入區
input_frame = tk.Frame(self.root)
input_frame.pack(pady=10, padx=20, fill="x")
tk.Label(input_frame, text="影片網址:").pack(side="left")
self.url_entry = tk.Entry(input_frame, font=("Arial", 10))
self.url_entry.pack(side="left", fill="x", expand=True, padx=5)
# 下載選項 (畫質選擇)
self.format_var = tk.StringVar(value="best")
option_frame = tk.Frame(self.root)
option_frame.pack(pady=5)
tk.Radiobutton(option_frame, text="最佳畫質 (需 FFmpeg)", variable=self.format_var, value="best").pack(side="left")
tk.Radiobutton(option_frame, text="標準 MP4 (最相容)", variable=self.format_var, value="mp4").pack(side="left")
# 進度條
self.progress_label = tk.Label(self.root, text="準備就緒", fg="blue")
self.progress_label.pack(pady=5)
self.progress_bar = ttk.Progressbar(self.root, orient="horizontal", length=400, mode="determinate")
self.progress_bar.pack(pady=5)
# 按鈕
self.download_btn = tk.Button(self.root, text="開始下載", command=self.start_download_thread,
bg="#4CAF50", fg="white", font=("Arial", 11, "bold"), width=15)
self.download_btn.pack(pady=20)
def progress_hook(self, d):
"""下載進度回調函數"""
if d['status'] == 'downloading':
p = d.get('_percent_str', '0%').replace('%','')
try:
self.progress_bar['value'] = float(p)
self.progress_label.config(text=f"正在下載: {d.get('_percent_str')} | 速度: {d.get('_speed_str')}")
except:
pass
elif d['status'] == 'finished':
self.progress_label.config(text="下載完成,正在合併檔案...")
def start_download_thread(self):
url = self.url_entry.get().strip()
if not url:
messagebox.showwarning("警告", "請先貼入 YouTube 網址!")
return
# 選擇存檔路徑
save_dir = filedialog.askdirectory()
if not save_dir:
return
# 停用按鈕避免重複點擊
self.download_btn.config(state=tk.DISABLED)
# 開啟執行緒執行下載
thread = threading.Thread(target=self.download_video, args=(url, save_dir))
thread.daemon = True
thread.start()
def download_video(self, url, save_path):
# 根據選擇設定格式
fmt = 'bestvideo+bestaudio/best' if self.format_var.get() == "best" else 'best[ext=mp4]'
ydl_opts = {
'format': fmt,
'outtmpl': f'{save_path}/%(title)s.%(ext)s',
'progress_hooks': [self.progress_hook],
# 關鍵偽裝參數:避免 Fragment not found 或 Empty file 錯誤
'user_agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'extractor_args': {'youtube': {'player_client': ['android', 'web']}},
'nocheckcertificate': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
ydl.download([url])
self.progress_label.config(text="下載成功!", fg="green")
messagebox.showinfo("完成", "影片已成功儲存至指定資料夾")
except Exception as e:
self.progress_label.config(text="發生錯誤", fg="red")
messagebox.showerror("錯誤", f"下載失敗:\n{str(e)}")
finally:
self.download_btn.config(state=tk.NORMAL)
self.progress_bar['value'] = 0
if __name__ == "__main__":
root = tk.Tk()
app = YTDownloader(root)
root.mainloop()

沒有留言:
張貼留言