A powerful, open-source YouTube downloader built with Kivy and yt-dlp
Download videos and audio directly to your Android device — even in the background.
- Screenshots
- Features
- Installation
- Building from Source
- Usage
- Project Structure
- Technical Details
- Permissions
- Troubleshooting
- Logging
| Splash Screen | Home Screen | Audio Mode |
|---|---|---|
![]() |
![]() |
![]() |
| Welcom Page | Paste a YouTube link and choose quality | Toggle Audio Only for MP3/M4A downloads |
| Downloading (Paused) | Cancel Confirmation |
|---|---|
![]() |
![]() |
| Pause mid-download and resume anytime | Confirm before cancelling — incomplete files are auto-deleted |
- Video download — up to Max / 1080p / 720p / 480p quality
- Audio download — M4A (Android native) or MP3 (Desktop via ffmpeg)
- Playlist support — automatically detects and downloads all videos in a playlist
- Resume interrupted downloads — picks up where it left off if cut short
- Smart format selection — picks the best available format for your chosen quality
- Pause / Continue — freeze a download mid-way and resume from exactly where it stopped
- Cancel with confirmation — prompts before cancelling; automatically deletes leftover
.partfiles so no storage is wasted - Live progress display — shows percentage, downloaded size vs total size, filename
- Speed & ETA — real-time download speed (MB/s) and estimated time remaining (Android)
- Runs as a Foreground Service — download continues even when you switch apps or lock your screen
- Live notification bar — shows progress percentage, file size, and speed in the Android notification shade
- Tap notification to return — brings the app back to foreground
- Sticky notification — cannot be accidentally dismissed while a download is active
- Saves to system Downloads folder — files visible in any file manager app
- Organised subfolders —
Downloads/YouTubeDownloader/Video/and.../Audio/ - Files survive app uninstall — stored in public Downloads, not app-private storage
| Feature | Android | Desktop / WSL |
|---|---|---|
| Video download | ✅ | ✅ |
| Audio download (M4A) | ✅ | — |
| Audio download (MP3) | — | ✅ |
| Background service | ✅ | — |
| Notification bar | ✅ | — |
| Auto ffmpeg detection | via p4a recipe | ✅ multi-location search |
| In-app log viewer | ✅ | ✅ |
- Download the latest
.apkfrom the Releases page - On your Android device: Settings → Security → Allow unknown sources
- Open the downloaded APK and tap Install
- Grant the requested permissions when prompted
| Permission | Why it's needed |
|---|---|
INTERNET |
To fetch video info and download from YouTube |
READ_MEDIA_VIDEO |
To save video files to Downloads (Android 13+) |
READ_MEDIA_AUDIO |
To save audio files to Downloads (Android 13+) |
WRITE_EXTERNAL_STORAGE |
To save files on older Android versions |
FOREGROUND_SERVICE |
To keep download running in background |
POST_NOTIFICATIONS |
To show progress in notification bar |
WAKE_LOCK |
To prevent CPU sleep during active download |
- Python 3.10+
- Buildozer 1.5+
- Ubuntu / Debian (recommended for building)
- Android SDK / NDK (auto-downloaded by Buildozer)
# Clone the repo
git clone https://github.com/yourusername/youtube-downloader.git
cd youtube-downloader
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install buildozer kivy yt-dlp
# Install Buildozer system dependencies
sudo apt install -y \
git zip unzip openjdk-17-jdk python3-pip \
autoconf libtool pkg-config zlib1g-dev \
libncurses5-dev libncursesw5-dev libtinfo5 \
cmake libffi-dev libssl-dev# Debug build (for testing)
buildozer android debug
# Deploy and run on connected device
buildozer android debug deploy run
# Deploy and stream logs
buildozer android debug deploy run logcat 2>&1 | grep pythonThe APK will be in ./bin/ after a successful build.
# Install desktop dependencies
pip install kivy yt-dlp
# Install ffmpeg
sudo apt install ffmpeg # Ubuntu/WSL
brew install ffmpeg # macOS
# Run
python3 main.py- Copy a YouTube video or playlist URL
- Open the app and tap Paste (or paste manually into the URL field)
- Select quality — Max Available, 1080p, 720p, or 480p
- Tap Download
- File saves to
Downloads/YouTubeDownloader/Video/
- Paste a YouTube URL
- Toggle Audio Only switch to ON
- Tap Download
- File saves to
Downloads/YouTubeDownloader/Audio/as M4A (Android) or MP3 (Desktop)
- During a download, tap
|| Pauseto freeze the download instantly - The progress bar turns amber and shows
|| Paused - Tap
|> Continueto resume from exactly where it stopped - You can minimize the app while paused — the service holds the state
- Tap
X Cancelduring a download - A confirmation dialog appears — tap Yes, Cancel to stop
- The app automatically deletes any incomplete
.partfiles - Storage is fully reclaimed — no orphaned temp files
- Tap the "YouTube Downloader" title text 5 times quickly
- The in-app log viewer opens as an overlay
- Shows the last 100 lines of timestamped logs
- Tap X Close to return to the main screen
youtube-downloader/
├── main.py # Main UI app — Kivy widget, download logic (Desktop)
├── design.kv # Kivy layout — all UI defined here
├── buildozer.spec # Android build configuration
├── recipes/ # Custom python-for-android build recipes
│ └── ...
├── presplash.png # Splash screen shown on app startup
├── icon.png # App icon
└── images/ # Screenshots for README
├── splash.jpg
├── home.jpg
├── audio_mode.jpg
├── paused.jpg
└── cancel.jpg
main.py
The Kivy application. On Android it starts the background service and polls dl_state.json for progress updates. On Desktop it runs yt-dlp directly in a background thread. Handles all UI events, pause/resume/cancel controls, permissions, storage setup, and the in-app log viewer.
design.kv
All UI layout and styling. Uses a FloatLayout root so the cancel confirmation overlay and log viewer can float on top of the main content without affecting layout.
Pause is implemented using a threading.Event:
- Running state — event is
set(), download proceeds normally - Paused state — event is
clear(), the yt-dlp progress hook blocks atevent.wait() - Resume — event is
set()again, hook unblocks and download continues - The pause checkpoint polls every 200ms so cancel requests are also noticed while paused
On Desktop/WSL, the app searches for ffmpeg in this order:
- System
PATHviashutil.which() - Common Windows paths (
C:\ffmpeg\bin, Chocolatey, Scoop, Conda) - WSL mounts (
/mnt/c/,/mnt/d/) - Common Linux/macOS paths (
/usr/bin,/opt/homebrew/bin, etc.)
If not found, a clear install instruction is shown instead of a raw error.
When a download is cancelled:
_cancel_flagis set — the progress hook raisesDownloadCancelled- The download thread exits cleanly
- After a 2-second delay (to ensure the thread has stopped), all
*.part,*.ytdl, and*.part-Frag*files in the output directory are deleted - UI resets to idle state
On first launch (Android 11+), the app will:
- Request
READ_MEDIA_VIDEO,READ_MEDIA_AUDIO,WRITE_EXTERNAL_STORAGE - On Android 11+ (API 30+), redirect to All Files Access settings if needed
- Set up storage paths only after permissions are granted (via callback)
This ensures the Downloads/YouTubeDownloader/ folder is always created successfully.
Download fails immediately
- Check your internet connection
- Make sure the video is not age-restricted or private
"ffmpeg not found" error (Desktop)
# Ubuntu / Debian / WSL
sudo apt install ffmpeg
# macOS
brew install ffmpeg
# Windows
winget install ffmpegFiles not appearing in Downloads folder
- Open a file manager and navigate to
Downloads/YouTubeDownloader/ - On Android 11+, make sure All Files Access was granted in Settings
- Check the in-app logs for
[Storage]entries to see where files are being saved
Pull requests are welcome. For major changes, please open an issue first to discuss what you'd like to change.
- Fork the repo
- Create a feature branch:
git checkout -b feature/my-feature - Commit your changes:
git commit -m 'Add my feature' - Push:
git push origin feature/my-feature - Open a Pull Request
This project is licensed under the MIT License — see the LICENSE file for details.
use adb to install and debug program
buildozer android debug
# install app
adb install bin/ytdlapp-1.6-arm64-v8a-debug.apk# see logs
adb logcat | grep python # single command
adb logcat -c
adb install -r -g bin\ytdlapp-1.6-arm64-v8a-debug.apk
adb shell monkey -p org.ytdl.ytdlapp -c android.intent.category.LAUNCHER 1
adb logcat | Select-String python



