You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用yt-dlp API提供的下载进度更新PyGtk进度条?

解决PyGtk进度条不更新的问题

你的核心问题有两个:

  • yt-dlp的download()方法是阻塞式的,直接在主线程调用会卡住Gtk的UI循环,导致进度条无法刷新
  • 额外的ProgressUpdate线程没必要,反而增加了线程同步的复杂度

下面是修改后的代码,直接利用yt-dlp的进度钩子结合GLib.idle_add来安全更新UI,同时把下载操作放到子线程避免阻塞主线程:

修改后的window.py

from downloader.download import Downloader
import gi
import threading
from db.database import Data
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib


class Window(Gtk.Window):
    
    def __init__(self):
        super().__init__(title="Downloader")
        self.notebook = Gtk.Notebook()
        self.add(self.notebook)

        # Init connection with database
        self.data = Data()
        self.data.create_connection()

        # Display page of window
        self.display_download()
        self.display_history()

        self.link = ''

        # ... 保留你原来的其他代码 ...

    def _update_progress_bar(self, percent):
        """安全更新进度条的回调,必须在主线程执行"""
        self.progress_bar.set_fraction(percent)
        return False

    # callback function called when 'download' button is pressed
    def __on_button_clicked(self, widget):
        link = self.download_entry.get_text()

        if link != "":
            self.progress_bar.set_fraction(0)
            
            # 定义下载完成后的回调
            def on_download_finished():
                self.progress_bar.set_fraction(1.0)
                
            # 创建Downloader时传入进度更新回调和完成回调
            downloader = Downloader(
                progress_callback=lambda p: GLib.idle_add(self._update_progress_bar, p),
                finish_callback=lambda: GLib.idle_add(on_download_finished)
            )
            
            # 把下载操作放到子线程,避免阻塞主线程
            download_thread = threading.Thread(target=downloader.download, args=(link,))
            download_thread.start()

修改后的downloader.py

import yt_dlp as dl


class Downloader:
    def __init__(self, progress_callback=None, finish_callback=None):
        self.status = ''
        self.percent = None
        self.progress_callback = progress_callback
        self.finish_callback = finish_callback

    # Clean percent value
    def __clean_percent(self, percent_str):
        try:
            # 直接提取数字部分,比遍历更可靠
            percent = percent_str.split('%')[0].strip()
            return float(percent) / 100
        except Exception as e:
            print(e)
            return 0.0

    def my_hook(self, d):
        if d['status'] == 'downloading':
            self.percent = self.__clean_percent(d['_percent_str'])
            if self.progress_callback:
                self.progress_callback(self.percent)
        elif d['status'] == 'finished':
            self.status = 'finished'
            if self.finish_callback:
                self.finish_callback()

    def download(self, link):
        opts = {
            'progress_hooks': [self.my_hook],
        }
        with dl.YoutubeDL(opts) as ytdl:
            ytdl.download([link])


if __name__ == '__main__':
    d = Downloader()
    d.download()

关键修改点说明

  • 将下载移到子线程:用threading.Thread把downloader.download()放到后台执行,主线程可以继续处理Gtk的UI事件
  • 直接在hook里更新UI:利用yt-dlp的progress_hooks实时获取进度,通过GLib.idle_add把UI更新任务提交到主线程执行(Gtk要求所有UI操作必须在主线程)
  • 简化进度解析:替换原来的遍历字符逻辑,直接分割字符串提取百分比,更可靠
  • 移除多余的ProgressUpdate线程:不需要额外线程轮询进度,hook已经能实时触发更新

这样修改后,进度条就能实时反映下载进度,而且UI不会再被阻塞。

内容的提问来源于stack exchange,提问作者Nahuel Aguirre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 16:47:42