2026年1月19日 星期一

YouTube 下載器

  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()
  • 沒有留言:

    張貼留言

    經由MQTT協定的2個WOKWI ESP32 雙向通訊 (ESP32 to ESP32 MQTT Communication )

     經由MQTT協定的2個WOKWI ESP32 雙向通訊  (ESP32  to ESP32 MQTT Communication ) 使用兩個 ESP32 建立一個遠端控制系統。 MQTT Broker: mqtt-dashboard.com Topic (主題): ale...