feat: add download_playlists.py for downloading playlists/videos with yt-dlp
Some checks failed
Code Check - Quality and Syntax / syntax-lint (3.11) (push) Failing after 11s
Code Check - Quality and Syntax / syntax-lint (3.13) (push) Failing after 9s
Code Check - Quality and Syntax / syntax-lint (3.14) (push) Failing after 11s

This commit is contained in:
Space-Banane
2026-04-07 00:31:48 +02:00
parent d1043a992f
commit 5e82ae99d1
3 changed files with 112 additions and 0 deletions

View File

@@ -48,6 +48,13 @@ jobs:
for file in $files; do
echo "Testing execution of $file..."
# Compile-only: do not execute this script in CI smoke tests
if [ "$file" = "download_playlists.py" ]; then
echo "Skipping execution for $file; running compile-only check"
python -m py_compile "$file"
continue
fi
# Special-case: run `batch.py` and watch its output for the marker
if [ "$file" = "batch.py" ]; then
tmp=$(mktemp)

View File

@@ -15,6 +15,7 @@ Lightweight helpers for using `yt-dlp` to download media from Patreon posts you
## Requirements
- Python 3.8+ and `pip`.
- `yt-dlp` installed and available on PATH (or configure via environment variables).
- `ffmpeg` installed and available on PATH (required by `yt-dlp` for merge/remux operations).
## Installation
1. Clone the repository:

104
download_playlists.py Normal file
View File

@@ -0,0 +1,104 @@
import argparse
import subprocess
import sys
DEFAULT_FORMAT = (
"bestvideo[height=1080][ext=mp4]+bestaudio[ext=m4a]/"
"bestvideo[height=1080]+bestaudio"
)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Download a playlist/video with yt-dlp using configurable options."
)
parser.add_argument(
"url",
help="Playlist or video URL to download.",
)
parser.add_argument(
"--yt-dlp",
dest="ytdlp",
default="yt-dlp",
help="Path to yt-dlp executable (default: yt-dlp from PATH).",
)
parser.add_argument(
"--format",
default=DEFAULT_FORMAT,
help="yt-dlp format selector.",
)
parser.add_argument(
"--output",
default="tip2tip/%(title)s/%(title)s.%(ext)s",
help="Output template path used by yt-dlp.",
)
parser.add_argument(
"--merge-output-format",
default="mp4",
help="Container to merge media into (default: mp4).",
)
parser.add_argument(
"--no-embed-thumbnail",
action="store_true",
help="Disable embedding thumbnail in output media.",
)
parser.add_argument(
"--no-embed-metadata",
action="store_true",
help="Disable embedding metadata in output media.",
)
parser.add_argument(
"--no-write-thumbnail",
action="store_true",
help="Disable writing thumbnail sidecar file.",
)
parser.add_argument(
"--print-cmd",
action="store_true",
help="Print the final yt-dlp command before running.",
)
return parser.parse_args()
def build_command(args: argparse.Namespace) -> list[str]:
cmd = [
args.ytdlp,
"-f",
args.format,
"--merge-output-format",
args.merge_output_format,
"-o",
args.output,
]
if not args.no_embed_thumbnail:
cmd.append("--embed-thumbnail")
if not args.no_embed_metadata:
cmd.append("--embed-metadata")
if not args.no_write_thumbnail:
cmd.append("--write-thumbnail")
cmd.append(args.url)
return cmd
def main() -> int:
args = parse_args()
cmd = build_command(args)
print(f"Downloading with output template: {args.output}")
if args.print_cmd:
print("Command:", " ".join(cmd))
result = subprocess.run(cmd, check=False)
if result.returncode != 0:
print(f"\n[!] yt-dlp exited with code {result.returncode}")
return result.returncode
print("\n[+] All done!")
return 0
if __name__ == "__main__":
sys.exit(main())