Shimmer Image
python

Creating a GUI Application to Download YouTube Media Using yt-dlp, Python, and FFmpeg

Creating a GUI Application to Download YouTube Media Using yt-dlp, Python, and FFmpeg
0 views
12 min read
#python
Image

Disclaimer

This tutorial is provided for entertainment and educational purposes only. I do not promote or condone the unauthorized downloading, distribution, or use of copyrighted content. Users of this application are responsible for ensuring that their use of the software complies with all applicable local, national, and international laws and regulations. Please respect the rights of content creators and adhere to the terms of service of the platforms from which you are downloading media. Use this tool responsibly.

Creating a GUI Application to Download YouTube Media Using yt-dlp, Python, and FFmpeg

In this detailed guide, we'll create a Python GUI application that allows users to download media from YouTube using yt-dlp and convert it to either MP3 or MP4 format. The application will be built using the tkinter library for the GUI, and we'll use yt-dlp and ffmpeg for downloading and converting the videos. Finally, we'll use PyInstaller to package the application into an executable that can be easily shared with others.

yt-dlp is a fork of the popular youtube-dl project, created to address issues and add new features more rapidly. It has become a preferred choice for many due to its active development and support for a wide range of sites.

Prerequisities

  1. Python: Ensure you have Python installed on your system. You can download it from the official Python website (https://www.python.org/).

  2. yt-dlp: Install yt-dlp using pip:

pip install yt-dlp
  1. FFmpeg: Download and install FFmpeg from the official FFmpeg website (https://ffmpeg.org/download.html). Make sure to add FFmpeg to your system PATH.

    a. Open System Properties:

    • Press Win + R to open the Run dialog.

    • Type sysdm.cpl and press Enter. This opens the System Properties window.

    b. Access Environment Variables:

    • In the System Properties window, click on the "Advanced" tab.

    • Click on the "Environment Variables..." button at the bottom.

    c. Edit System Variables:

    • In the Environment Variables window, under "System Variables", find and select the Path variable.

    • Click on the "Edit..." button.

    d. Add FFmpeg Path:

    • In the Edit Environment Variable window, click on 'New' to add a new entry.

    • Enter the path to the directory where you extracted FFmpeg. For example, if you extracted FFmpeg to C:\ffmpeg, add C:\ffmpeg\bin to the list.

    • Click "OK" to close each window.

    • You may or may not need to perform a system restart. If the next step fails to return a version for ffmpeg, you can restart your machine and pick up where you left off (Step e below).

    e. Verify FFmpeg Installation:

    • Open a new Command Prompt window (Win + R, type cmd, then Enter).

    • Type ffmpeg -version and press Enter.

    • You should see the FFmpeg version and configuration details printed in the Command Prompt if FFmpeg is correctly installed and added to the PATH. If you get an error, refer to step d.4 above.

  2. tkinter: This comes pre-installed with Python. If not, you can install it using:

pip install tk
  1. PyInstaller: Install PyInstaller using pip:
pip install pyinstaller

Step-by-Step Guide

1. Setting Up the Project

Create a new directory for your project and navigate into it:

mkdir yt_downloader
cd yt_downloader

yt-dlp supports downloading from over 1000 websites, including YouTube, Vimeo, Dailymotion, and many more, making it highly versatile for various content sources.

2. Writing the Python Script

Create a new Python file called main.py and open it in your favorite text editor. Place the following code into the file:

# main.py

import tkinter as tk
from settings import SettingsPage
from tkinter import filedialog, messagebox
from pathlib import Path
import yt_dlp
from tkinter import ttk
import configparser
import subprocess

class YoutubeDownloader:
    def __init__(self, root):
        self.root = root
        self.root.title("FM 5.0")

        # Load settings from the configuration file
        self.load_settings()

        # Default download folder
        self.download_folder = Path(self.default_folder)

        # Create the download folder if it doesn't exist
        self.download_folder.mkdir(exist_ok=True)

        # GUI elements
        self.directions_label = tk.Label(root, text="Enter a valid YouTube URL:")
        self.directions_label.pack(pady=(10, 0))

        self.url_entry = tk.Entry(root, width=40)
        self.url_entry.pack(pady=(5, 10))

        self.download_button_mp4 = tk.Button(root, text="Download Video (MP4)", command=lambda: self.download_media('mp4'))
        self.download_button_mp4.pack(pady=5)

        self.download_button_mp3 = tk.Button(root, text="Download Audio (MP3)", command=lambda: self.download_media('mp3'))
        self.download_button_mp3.pack(pady=5)

        # Progress bar
        self.progress = ttk.Progressbar(root, orient=tk.HORIZONTAL, length=100, mode='determinate')
        self.progress.pack(pady=5)

        # Listbox to display downloaded files
        self.download_listbox = tk.Listbox(root, selectmode=tk.SINGLE, height=5, width=50)
        self.download_listbox.pack(pady=10, padx=10)

        # Bind the <<ListboxSelect>> event to a callback function
        self.download_listbox.bind("<<ListboxSelect>>", self.on_listbox_select)

        # Button for deleting songs
        self.delete_button = tk.Button(root, text="Delete Song", command=self.delete_selected)
        self.delete_button.pack(pady=5)

        # Button for fetching contents
        self.fetch_button = tk.Button(root, text="Fetch Contents", command=self.fetch_contents)
        self.fetch_button.pack(pady=5)

        # Settings button
        self.settings_button = tk.Button(root, text="Settings", command=self.open_settings)
        self.settings_button.pack(pady=10)

        # Check if dark mode is enabled
        if self.dark_mode:
            self.apply_dark_mode()

        # Load the list of downloads
        self.load_downloads()

    def apply_dark_mode(self):
        # Apply dark mode color scheme to your widgets
        self.root.configure(bg='#2C2C2C')
        self.directions_label.configure(bg='#2C2C2C', fg='white')
        self.url_entry.configure(bg='#4D4D4D', fg='white')
        self.download_button_mp4.configure(bg='#007BFF', fg='white')
        self.download_button_mp3.configure(bg='#007BFF', fg='white')
        self.progress.configure(style='dark.Horizontal.TProgressbar')
        self.download_listbox.configure(bg='#4D4D4D', fg='white')
        self.delete_button.configure(bg='#DC3545', fg='white')
        self.fetch_button.configure(bg='#28A745', fg='white')
        self.settings_button.configure(bg='#6C757D', fg='white')

    def load_settings(self):
        # Load settings from the configuration file
        config = configparser.ConfigParser()
        config.read("settings.ini")

        # Update settings in the YoutubeDownloader instance
        self.default_folder = config.get('Settings', 'DefaultFolder', fallback='FM5')
        self.dark_mode = config.getboolean('Settings', 'DarkMode', fallback=False)

        # Update the download folder based on the new settings
        self.download_folder = Path(self.default_folder)
        self.download_folder.mkdir(exist_ok=True)

    def open_settings(self):
        # Create an instance of the SettingsPage when the settings button is clicked
        settings_root = tk.Toplevel(self.root)
        settings_page = SettingsPage(settings_root, self)

    def download_media(self, format):
        url = self.url_entry.get()
        if not url:
            messagebox.showerror("Error", "Please enter a YouTube URL.")
            return

        ydl_opts = {
            'format': 'bestvideo+bestaudio/best',
            'outtmpl': f'{self.download_folder}/%(title)s.%(ext)s',
            'progress_hooks': [self.update_progress],
        }

        if format == 'mp3':
            ydl_opts['format'] = 'bestaudio/best'
            ydl_opts['postprocessors'] = [{
                'key': 'FFmpegExtractAudio',
                'preferredcodec': 'mp3',
                'preferredquality': '192',
            }]

        with yt_dlp.YoutubeDL(ydl_opts) as ydl:
            try:
                info_dict = ydl.extract_info(url, download=True)
                title = info_dict.get('title', 'media')
                messagebox.showinfo("Success", f"{format.upper()} download completed successfully.")
                # Update the list of downloads
                self.load_downloads()
            except yt_dlp.DownloadError as e:
                messagebox.showerror("Error", f"Download failed: {e}")

    def update_progress(self, d):
        if d['status'] == 'downloading':
            # Update progress bar
            progress_str = d['_percent_str'].strip('%')
            try:
                progress_value = float(progress_str)
                self.progress['value'] = progress_value
                self.progress.update_idletasks()
            except ValueError:
                pass  # Ignore if the conversion to float fails

    def load_downloads(self):
        # Clear existing items
        self.download_listbox.delete(0, tk.END)
        # List all files in the download folder
        for file in self.download_folder.iterdir():
            if file.is_file():
                self.download_listbox.insert(tk.END, file.name)

    def delete_selected(self):
        selected_index = self.download_listbox.curselection()
        if selected_index:
            selected_file = self.download_listbox.get(selected_index)
            selected_path = self.download_folder / selected_file
            # Ask for confirmation before deleting
            confirm = messagebox.askyesno("Confirm Deletion", f"Do you want to delete {selected_file}?")
            if confirm:
                selected_path.unlink()
                # Update the list of downloads
                self.load_downloads()

    def fetch_contents(self):
        # Fetch contents of the download folder
        self.load_downloads()

    def on_listbox_select(self, event):
        selected_index = self.download_listbox.curselection()
        if selected_index:
            selected_file = self.download_listbox.get(selected_index)
            selected_path = self.download_folder / selected_file

            # Open the file with the default media player
            try:
                subprocess.run([selected_path], shell=True)
            except Exception as e:
                messagebox.showerror("Error", f"Failed to open file: {e}")

if __name__ == "__main__":
    root = tk.Tk()
    app = YoutubeDownloader(root)
    root.mainloop()

Then, in the same directory, create another Python file called settings.py and place this code inside:

# settings.py

import tkinter as tk
from tkinter import ttk
from tkinter import filedialog
import configparser

class SettingsPage:
    def __init__(self, root, youtube_downloader):
        self.root = root
        self.root.title("Settings")
        self.root.geometry("400x200")

        # Reference to the YoutubeDownloader instance
        self.youtube_downloader = youtube_downloader

        # Config file path
        self.config_file = 'settings.ini'

        # Dark mode checkbox
        self.dark_mode_var = tk.BooleanVar()
        self.dark_mode_var.set(self.youtube_downloader.dark_mode)
        self.dark_mode_checkbox = tk.Checkbutton(root, text="Dark Mode", variable=self.dark_mode_var)
        self.dark_mode_checkbox.pack(pady=10)

        # Default download folder entry
        self.default_folder_label = tk.Label(root, text="Default Download Folder:")
        self.default_folder_label.pack(pady=(10, 0))

        self.default_folder_entry = tk.Entry(root, width=30)
        self.default_folder_entry.insert(0, self.youtube_downloader.default_folder)
        self.default_folder_entry.pack(pady=5)

        # Save button
        self.save_button = tk.Button(root, text="Save", command=self.save_settings)
        self.save_button.pack(pady=10)

        # Load settings
        self.load_settings()

    def load_settings(self):
        # Load settings from the configuration file
        config = configparser.ConfigParser()
        config.read(self.config_file)

        # Update GUI elements with the loaded settings
        self.dark_mode_var.set(config.getboolean('Settings', 'DarkMode', fallback=False))
        self.default_folder_entry.delete(0, tk.END)
        self.default_folder_entry.insert(0, config.get('Settings', 'DefaultFolder', fallback='FM5'))

    def save_settings(self):
        # Save settings to the configuration file
        config = configparser.ConfigParser()
        config['Settings'] = {
            'DarkMode': str(self.dark_mode_var.get()),
            'DefaultFolder': self.default_folder_entry.get(),
        }

        with open(self.config_file, 'w') as configfile:
            config.write(configfile)

        # Update the settings in the YoutubeDownloader instance
        self.youtube_downloader.load_settings()

        # Close the settings window
        self.root.destroy()

if __name__ == "__main__":
    root = tk.Tk()
    app = SettingsPage(root, None)  # Pass None since we don't have a YoutubeDownloader instance in this context
    root.mainloop()

This will allow the user to select a theme (dark/light), as well as customize where the downloaded files are saved

3. Explanation of the Code

  • Dependencies: The script uses tkinter for the GUI, yt-dlp for downloading videos, and ffmpeg for converting videos to audio.

  • Functions:

    • download_video: Downloads the video in MP4 format using yt-dlp.

    • download_audio: Downloads the video in the best audio format and then converts it to MP3 using ffmpeg.

  • GUI Setup:

    • An input field for the YouTube URL.

    • Status bar indicating download progress.

    • Buttons to select between MP4 and MP3.

    • A button to start the download process.

    • A settings button for changing the app theme from light to dark, and well as customize where files are saved.

4. Creating an Executable with PyInstaller

To package the script into an executable, use PyInstaller:

pyinstaller --onefile --noconsole main.py

This command will generate a dist folder containing the yt_downloader.exe file.

  • --onefile:

    This flag tells PyInstaller to package your entire application into a single executable file. Without this flag, PyInstaller creates a bundle consisting of multiple files (e.g., an executable and several support files). Purpose: Simplifies distribution by providing a single executable file that contains all necessary dependencies.

  • --noconsole:

    This flag instructs PyInstaller to create the executable without opening a console window when the program runs. It's particularly useful for GUI applications where a console window is unnecessary. Purpose: Improves user experience by hiding the console window, making the application appear more like a typical GUI application.

PyInstaller can handle complex applications, including those with multiple modules, dynamic imports, and non-Python files, ensuring that all necessary components are bundled together.

5. Distributing the Executable

Once you have created the yt_downloader.exe file using PyInstaller, you can share it with others who use Windows. Here are some important points to consider:

  • Stand-Alone Execution: The yt_downloader.exe file is a stand-alone executable that can be run on Windows systems without requiring Python, yt-dlp, or FFmpeg to be separately installed. This makes it easy for anyone to use the application without additional dependencies.

  • Limitations: While the application can download videos and save them in MP3 format, it does not support converting already downloaded MP4 videos to MP3 directly within the application. Users will need to use separate tools or online services to convert MP4 videos to MP3 format if needed.

  • Converting MP4 to MP3: There are several free tools and online converters available for converting MP4 videos to MP3 format. Here are a few popular resources:

These resources provide user-friendly interfaces for converting videos to various audio formats, including MP3.

Caveats and Recommendations

  • Dependency on FFmpeg: Although the yt_downloader.exe does not require FFmpeg to be installed separately on the user's system, the ability to extract MP3 directly from MP4 videos is limited to downloading directly as MP4 using yt-dlp.

  • Sharing the Application: When sharing yt_downloader.exe, ensure that recipients are aware of the limitations regarding downloading media. This will help manage expectations and guide users towards appropriate tools for any additional conversions they may need.

FAQ

Do I need to install FFmpeg separately?

Yes, FFmpeg needs to be installed and added to your system PATH.

What if I encounter errors during the download process?

Ensure that the URL is correct and that you have a stable internet connection. Also, check that yt-dlp and ffmpeg are properly installed.

Can I download videos in other formats?

Yes, you can customize the ydl_opts in the script to specify different formats.

How can I add more features to this application?

You can enhance the GUI to include more options, such as selecting video quality, downloading subtitles, etc., by modifying the ydl_opts in the script.

FFmpeg's libraries, such as libavcodec, libavformat, and libavutil, are used by many software applications to handle multimedia content, including VLC media player, HandBrake, and many others.

How to Use:

Double click the .exe to open the GUI. Enter the url where the media you intend to download is located. Choose whether to download the 'MP4' or 'MP3'. Wait for results.

Image

Resources:

Feel free to download the completed code by clicking the button below. After download, extract the contents from the .zip file.

You can also download a pre-packaged .exe ready for use on Windows machines:

  • yt-dlp: For more information about yt-dlp, visit the official yt-dlp GitHub repository (https://github.com/yt-dlp/yt-dlp). This repository contains comprehensive documentation, installation instructions, and usage examples.

  • Supported Sites: A full list of supported sites whose URLs can be used with yt-dlp for downloading can be found here (https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md). This list includes a wide variety of video hosting and streaming platforms.

Disclaimer

This tutorial is provided for entertainment and educational purposes only. I do not promote or condone the unauthorized downloading, distribution, or use of copyrighted content. Users of this application are responsible for ensuring that their use of the software complies with all applicable local, national, and international laws and regulations. Please respect the rights of content creators and adhere to the terms of service of the platforms from which you are downloading media. Use this tool responsibly.

If you enjoyed this article, please consider making a donation. Your support means a lot to me.

  • Cashapp: $hookerhillstudios
  • Paypal: Paypal

Conclusion

In this guide, we've walked through the steps to create a YouTube downloader with a GUI using Python, yt-dlp, and ffmpeg. We also covered how to package the application into an executable using PyInstaller. With this knowledge, you can further customize and expand the application to fit your needs.

Comments

to join the conversation

Loading comments...

About the Author

Jared Hooker

Hi, I'm Jared Hooker, and I have been passionate about coding since I was 13 years old. My journey began with creating mods for iconic games like Morrowind and Rise of Nations, where I discovered the thrill of bringing my ideas to life through programming.

Over the years, my love for coding evolved, and I pursued a career in software development. Today, I am the founder of Hooker Hill Studios Blog, where I specialize in web and mobile development. My goal is to help businesses and individuals transform their ideas into innovative digital products.

Thank you for visiting my blog! I hope you find the content valuable and inspiring.

Recent Posts

Recent Articles

View All