Just a simple script to store useful yt-dlp options. Like what one would do with a Bash script, but platform-agnostic.
Last active
February 10, 2026 13:14
-
-
Save scivision/c81e43fe0f26c4fec70938b14d236e6b to your computer and use it in GitHub Desktop.
YouTube channel download with yt-dlp from Python script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Download all videos from a YouTube channel using yt-dlp. | |
| Comments, thumbnails, and metadata are included. | |
| """ | |
| import argparse | |
| import subprocess | |
| from pathlib import Path | |
| from ytdlp_command import find_yt_dlp | |
| def run_yt_dlp(channel_handle: str, output_dir: Path | str) -> None: | |
| """ | |
| Execute yt-dlp with the recommended settings for full channel download. | |
| """ | |
| base_dir = Path(output_dir).resolve() | |
| base_dir.mkdir(parents=True, exist_ok=True) | |
| # Archive file prevents re-downloading already saved videos | |
| archive_path = base_dir / "archive.txt" | |
| # Output template: channel_name / date - title [id].mp4 | |
| output_template = str( | |
| base_dir / "%(uploader)s/%(upload_date)s - %(title)s [%(id)s].%(ext)s" | |
| ) | |
| cmd = [ | |
| find_yt_dlp(), | |
| "--no-warnings", # cleaner output | |
| "-f", | |
| "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best", | |
| "--merge-output-format", | |
| "mp4", | |
| "-o", | |
| output_template, | |
| "--download-archive", | |
| str(archive_path), | |
| "--sleep-requests", | |
| "2", # polite rate limiting | |
| "--write-thumbnail", | |
| "--embed-thumbnail", | |
| "--write-info-json", | |
| "--write-comments", | |
| "--embed-metadata", # bonus: embeds title/description/etc. | |
| f"https://www.youtube.com/{channel_handle}", | |
| ] | |
| print(f"\nStarting download for YouTube channel: {channel_handle}") | |
| print(f"Output will be saved to: {base_dir}") | |
| print(f"Archive file: {archive_path}") | |
| try: | |
| # Run yt-dlp and stream output live to terminal | |
| process = subprocess.Popen( | |
| cmd, | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.STDOUT, | |
| text=True, | |
| bufsize=1, # line buffered | |
| ) | |
| # Print output in real time | |
| if process.stdout is not None: | |
| for line in process.stdout: | |
| print(line, end="", flush=True) | |
| return_code = process.wait() | |
| if return_code == 0: | |
| print("\n\nDownload completed successfully ✓") | |
| else: | |
| raise SystemExit(f"\nyt-dlp exited with code {return_code}") | |
| except KeyboardInterrupt: | |
| raise SystemExit("\n\nRun the script again to resume.") | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description="Download all videos from a YouTube channel using yt-dlp", | |
| ) | |
| parser.add_argument("channel", help="YouTube channel handle") | |
| parser.add_argument( | |
| "-o", | |
| "--output-dir", | |
| help="Base folder where channel subfolder will be created", | |
| default=Path.cwd(), | |
| ) | |
| args = parser.parse_args() | |
| # Normalize: make sure it starts with @ | |
| channel = args.channel | |
| if not channel.startswith("@"): | |
| channel = "@" + channel | |
| run_yt_dlp(channel, args.output_dir) | |
| if __name__ == "__main__": | |
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Download a single video from YouTube at best quality using yt-dlp. | |
| Comments, thumbnails, and metadata are included. | |
| """ | |
| import argparse | |
| import subprocess | |
| from pathlib import Path | |
| from ytdlp_common import find_yt_dlp | |
| def run_yt_dlp(video_url: str, output_dir: Path | str) -> None: | |
| """ | |
| Execute yt-dlp with the recommended settings for single video download. | |
| """ | |
| output_dir = Path(output_dir).resolve() | |
| # Output template: date - title [id].mp4 | |
| output_template = str(output_dir / "%(upload_date)s - %(title)s [%(id)s].%(ext)s") | |
| cmd = [ | |
| find_yt_dlp(), | |
| "--no-warnings", # cleaner output | |
| "-f", | |
| "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best", | |
| "--merge-output-format", | |
| "mp4", | |
| "-o", | |
| output_template, | |
| "--sleep-requests", | |
| "2", # polite rate limiting | |
| "--write-thumbnail", | |
| "--embed-thumbnail", | |
| "--write-info-json", | |
| "--write-comments", | |
| "--embed-metadata", # bonus: embeds title/description/etc. | |
| video_url, | |
| ] | |
| print(f"\nStarting download for YouTube video: {video_url}") | |
| print(f"Output will be saved to: {output_template}*") | |
| try: | |
| # Run yt-dlp and stream output live to terminal | |
| process = subprocess.Popen( | |
| cmd, | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.STDOUT, | |
| text=True, | |
| bufsize=1, # line buffered | |
| ) | |
| # Stream output live to terminal | |
| for line in process.stdout: | |
| print(line, end="") | |
| process.wait() | |
| except KeyboardInterrupt: | |
| print("\nDownload interrupted by user.") | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description="Download a single YouTube video at best quality using yt-dlp." | |
| ) | |
| parser.add_argument( | |
| "video_url", | |
| type=str, | |
| help="The full URL of the YouTube video to download.", | |
| ) | |
| parser.add_argument( | |
| "-o", | |
| "--output-dir", | |
| type=str, | |
| default=".", | |
| help="Directory to save the downloaded video. Defaults to current directory.", | |
| ) | |
| args = parser.parse_args() | |
| run_yt_dlp(args.video_url, args.output_dir) | |
| if __name__ == "__main__": | |
| main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import functools | |
| from shutil import which | |
| @functools.cache | |
| def find_yt_dlp() -> str: | |
| """ | |
| Find the yt-dlp executable in the system PATH. | |
| Caches the result for future calls. | |
| """ | |
| yt_dlp_path = which("yt-dlp") | |
| if yt_dlp_path is None: | |
| raise FileNotFoundError( | |
| "yt-dlp executable not found in system PATH. Please install yt-dlp and ensure it's accessible." | |
| ) | |
| return yt_dlp_path |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment