Introduction
As an electronic music producer, I occasionally enjoy sampling old songs. The easiest way to obtain audio of acceptable quality is through downloading YouTube videos and extracting the audio with a web app. I was tired of having to google for one each time, since these services come and go, and the one you used last week may not be functional this week. I wanted something simple, efficient, and that wouldn’t require me to waste time, so I decided to build my own YouTube downloader. Without even searching, I know that there must already be a myriad of available solutions that wouldn’t require me to code, but I already knew what I needed and wanted to create a tool that fits my needs perfectly.
This blog post documents how I created a Python script using yt-dlp and turned it into a portable .exe application using PyInstaller. The process taught me a lot about dependencies, error handling, and packaging Python applications. It also shows how a bit of curiosity and persistence can turn a small idea into a practical tool. Full disclosure: I used chatgpt to help me, so the code would probably make a developer cry.
1. The Idea: A Functional, Minimalist YouTube Downloader
My goal was to create a portable application that:
- Downloads audio from YouTube on the highest available quality.
- By pasting one or more YouTube links
- By actively listening to the clipboard and recognizing if a YouTube link has been copied to start downloading immediately
- Quick access to the downloads folder
When looking for packages to build the backend, I found yt-dlp, an active fork of youtube-dl. It offers extensive format support, excellent documentation, and frequent updates.
2. Writing the Core Script in Python
I wrote a simple Python script that accepts a YouTube URL as input and downloads the best audio.
To make the script more robust, I added:
- Error handling for invalid URLs.
- Logging to track download progress.
The script worked well from the terminal, but I am not a terminal kinda guy, so I designed a simple GUI with tkinter to be able to use the app easily
3. Testing the Script
Before packaging the script, I conducted iterative testing to ensure everything worked smoothly. This step was essential for catching bugs early and improving usability. I didn’t necessarily fix everything or made it super robust, but concentrated on the use case I would be giving it.
- Downloading links individually or multiple at a time: I tested the script with multiple YouTube URLs, including music videos, playlists, and long-form content. This helped me identify edge cases, and see if my use case was completely supported
- Catching exceptions: I simulated invalid input, to make sure the script failed gracefully. I added exception handling to display clear error messages without crashing the script.
This phase also helped refine the user experience. For instance, I adjusted default download settings and added meaningful print statements to guide the user.
4. Packaging with PyInstaller
The next challenge was converting my script into a Windows .exe file. I chose PyInstaller, which bundles Python scripts along with their dependencies into a standalone executable.
Installation was straightforward:
pip install pyinstaller
Then I used the command:
pyinstaller --onefile --noconsole yt_downloader.py
This created a dist/yt_downloader.exe file. However, when I ran the .exe, it immediately crashed.
5. Solving the ‘ModuleNotFoundError’
To debug the issue, I ran the .exe from the command prompt and saw the following error:
ModuleNotFoundError: No module named 'yt_dlp'
Even though the script worked in Python, PyInstaller didn’t automatically detect yt_dlp as a dependency. Apparently this is a common issue with some third-party packages.
I solved it by reinstalling it in the env I was working on.
pip install yt-dlp
After rebuilding the .exe, it worked perfectly. A lingering problem is that as it is an unsigned .exe, it gets flagged as a potential virus by Microsoft. Since I will not be distributing this, I am not interested in fixing it, and simply adding it to Microsoft Defender’s white list is enough for me.
6. Final Outcome: A Portable Tool
The final product is a compact .exe file that can:
- Run on any Windows machine without requiring the use of Python
- Download YouTube videos to mp3s with one command.
It’s exactly what I wanted: fast, reliable, and minimal.
7. Lessons Learned
This project helped me sharpen several valuable skills:
- Packaging Python apps for end users.
- Debugging
- Vibe coding kinda works, if you know a bit about what you’re doing and the project is not too complex
It also reinforced my ability to be resourceful, looking up things, and trying solutions until they work.
Download
Python script and .exe file