Search code examples
python-3.xkivypytube

Kivy freezing when when downloading video


I want to make an app that downloads youtube videos with pytube and kivy. The problem is that the app freezes and then stops responding when the download starts. I know it starts to download because it creates a mp4 file. I looked into scheduling and multithreading but I don't know how those work and I'm not even sure they are a solution to my problem in the first place. Anyone that can tell me where to look?

the python file:

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

from pytube import YouTube

class MyWidget(BoxLayout):
    def download_video(self, link):
         yt = YouTube(link)
         yt.streams.first().download()

class Youtube(App):
    pass

if __name__ == "__main__":
 Youtube().run()

the kivy file:

 MyWidget:

 <MyWidget>:

 BoxLayout:
     orientation: 'horizontal'
     Button:
        text: 'press to download'
        on_press: root.download_video(url.text)
     TextInput:
        text: 'paste the youtube URL here'
        id: url

Solution

  • I was recently intimidated by threading myself, talk about GIL. The threading library itself isn't too complicated however and this problem is a great use-case for it.

    from kivy.app import App
    from kivy.uix.boxlayout import BoxLayout
    from pytube import YouTube
    
    # import Thread from threading
    from threading import Thread
    
    class MyWidget(BoxLayout):
    
         # Create a start_download method
        def start_download(self):
            # Creates a new thread that calls the download_video method
            download = Thread(target=self.download_video)
    
            # Starts the new thread
            download.start()
    
        def download_video(self):
             # I implemented the text grabbing here because I came across a weird error if I passed it from the button (something about 44 errors?) 
             yt = YouTube(self.ids.url.text)
             yt.streams.first().download()
    
    class Youtube(App):
    
        def build(self):
            return MyWidget()
    
    if __name__ == "__main__":
        Youtube().run()
    

    youtube.kv

    <MyWidget>:
        orientation: 'horizontal'
        Button:
            text: 'press to download'
            # Call the start download method to create a new thread rather than override the current.
            on_press: root.start_download()
        TextInput:
            text: 'paste the youtube URL here'
            id: url
    

    1) You enter the url

    2) You click download button

    3) This triggers a start_download method.

    4) The start_download creates a thread that runs your download method that takes the url.text at the current time as an argument. This means you can enter additional urls, while that is downloading without overwriting the url text in the previous calls because they're in different threads of execution.

    Video Example