I have added several long conversational youtube videos into a playlist, hoping I’ll come back to the them some day, but in reality I never do that. Recently I found out that Spotify has released a new save-to-spotify CLI tool that lets you upload any audio file into your Spotify account as a private podcast episode. This is perfect for me, as I can download those youtube videos as audio files, upload them to Spotify, and listen to them while I’m driving.
Here’s how you can do that:
Download the Audio from Youtube
Install yt-dlp and ffmpeg:
brew install yt-dlp ffmpeg
Download and extract the audio:
yt-dlp --extract-audio --audio-format mp3 --audio-quality 0 -o "%(title)s.%(ext)s" "https://www.youtube.com/watch?v=YOUR_VIDEO_ID"
Save to Spotify
Install save-to-spotify:
curl -fsSL https://saveto.spotify.com/install.sh | bash -s -- --no-skills
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc && source ~/.zshrc
Then authenticate with your Spotify account:
save-to-spotify auth login
Now you can:
- Add a new episode and create a new show:
save-to-spotify upload file.mp3 --title "TITLE" --new-show "SHOW"`
- Add a new episode to an existing show:
save-to-spotify upload file.mp3 --title "TITLE" --show-id spotify:show:abcd1234
If --show-id is not specified, it will use the most recently created show.
Then visit your Spotify account and find the show and episode in your library (it takes a few minutes to show up there).
One Command
To combine yt-dlp and save-to-spotify in one command, the following bash script gets the audio, description, and thumbnail from Youtube and saves them to Spotify:
#!/usr/bin/env bash
set -euo pipefail
usage() {
echo "Usage: $0 <video-id> [--new-show NAME | --show-id ID]"
exit 1
}
[[ $# -lt 1 ]] && usage
URL="https://www.youtube.com/watch?v=$1"
shift
SHOW_ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--new-show) SHOW_ARGS+=(--new-show "$2"); shift 2 ;;
--show-id) SHOW_ARGS+=(--show-id "$2"); shift 2 ;;
*) usage ;;
esac
done
TMPDIR="$(mktemp -d)"
trap 'rm -rf "$TMPDIR"' EXIT
echo "Downloading audio, thumbnail, and metadata..."
yt-dlp \
--extract-audio --audio-format mp3 --audio-quality 0 \
--write-thumbnail --convert-thumbnails jpg \
--write-info-json \
-o "$TMPDIR/%(id)s.%(ext)s" \
"$URL"
AUDIO="$(ls "$TMPDIR"/*.mp3)"
INFO="$(ls "$TMPDIR"/*.info.json)"
THUMB="$(ls "$TMPDIR"/*.jpg 2>/dev/null || true)"
TITLE="$(python3 -c "import json,sys; print(json.load(open(sys.argv[1]))['title'])" "$INFO")"
DESC="$(python3 -c "import json,sys; print(json.load(open(sys.argv[1])).get('description','')[:4000])" "$INFO")"
UPLOAD_ARGS=(--title "$TITLE")
[[ -n "$DESC" ]] && UPLOAD_ARGS+=(--summary "$DESC")
if [[ -n "$THUMB" ]]; then
THUMB_SIZE="$(stat -f%z "$THUMB" 2>/dev/null || stat -c%s "$THUMB" 2>/dev/null)"
if [[ "$THUMB_SIZE" -le 1048576 ]]; then
UPLOAD_ARGS+=(--image "$THUMB")
else
echo "Thumbnail exceeds 1 MB, skipping."
fi
fi
echo "Uploading to Spotify..."
save-to-spotify upload "$AUDIO" "${UPLOAD_ARGS[@]}" ${SHOW_ARGS[@]+"${SHOW_ARGS[@]}"}
echo "Done! '$TITLE' saved to Spotify."
Save it as yt2spotify.sh and make it executable:
chmod +x yt2spotify.sh
Then run:
yt2spotify.sh VIDEO_ID
yt2spotify.sh VIDEO_ID --new-show "YouTube Listens"
yt2spotify.sh VIDEO_ID --show-id spotify:show:abc123