Turn an Apple Music playlist into a YouTube-ready video — with a Textual TUI for setup, track picking, and live progress.
Scrapes the tracklist, downloads audio via yt-dlp (duration-checked against Apple Music when possible), renders album art + scrolling title cards, then stitches clips with ffmpeg. Optimized for macOS / Apple Silicon (VideoToolbox HEVC), with NVENC and libx264 fallbacks.
brew install ffmpeg deno
uv sync
uv run playwright install chromium # required — Chromium scrape is the defaultPython 3.10+ via uv.
uv run app.py
# or after sync:
uv run playlist-gen- Paste an Apple Music playlist URL → Load tracks (
L) - Toggle tracks with space (or
A/Nfor all / none) - Set resolution, backgrounds, workers, etc.
- Generate (
G) — watch overall + per-slot download/render bars - Cancel (
C) stops downloads and kills in-flight ffmpeg encodes
Output: output/playlist_video.mp4 + output/playlist_video_description.txt (YouTube chapter format, ready to paste).
uv run apple_music_playlist_video.py "https://music.apple.com/…/playlist/…" \
--output output/playlist_video.mp4 \
--resolution 1920x1080 \
--background-image assets/backgrounds/bg1.jpeg \
--limit 5
# or:
uv run playlist-gen-cli "https://music.apple.com/…/playlist/…" --limit 2Or edit the defaults in playlist_video/config.py and run with no args.
uv run apple_music_playlist_video.py --helpplaylist-gen/
├── app.py # TUI entry (uv run app.py)
├── apple_music_playlist_video.py # headless CLI entry
├── pyproject.toml # deps + playlist-gen / playlist-gen-cli scripts
├── playlist_video/ # pipeline package
│ ├── config.py # user-editable defaults
│ ├── models.py # Track / PipelineConfig / progress / cancel
│ ├── scraper.py # Apple Music scraping (amp-api + Chromium)
│ ├── downloader.py # yt-dlp audio + artwork + duration match
│ ├── fonts.py / backgrounds.py / text.py / frames.py / transitions.py
│ ├── encoding.py # VideoToolbox / NVENC / libx264 + raw ffmpeg pipe
│ ├── progress.py / render.py / pipeline.py / cli.py
│ └── tui/ # Textual TUI
├── tests/ # pytest (no network)
├── assets/backgrounds/
├── output/
└── README.md
- Audio comes from YouTube search matches via yt-dlp. When Apple Music provides a duration, candidates more than 20% off are rejected and the next search hit is tried.
- Description file uses YouTube chapter format (
0:00 Title - Artist). - Defaults: 1080p, no shuffle, full playlist (
LIMIT = None). Use--limit/ TUI selection while iterating. - Scraping defaults to headless Chromium. Opt into the old HTML path with
--fast-scrape. - Generated media under
output/is gitignored.
Use / modify as you like. You are responsible for copyright compliance on any audio or artwork you publish.