Listen to music offline for free

Anh-Thi Dinh
I want to download all my favorite music to my local machine for completely offline listening. My goal is to have the same music library on both my iPhone (iOS) and MacBook (macOS).
This is for personal use only. I don't encourage illegal activities.

Download music

⚠️
Be careful: If you use --cookies-from-browser chrome to bypass the 403 error, you'll need to grant yt-dlp your OS user password. This gives the program access to your Chrome cookies and potentially stored passwords. Use at your own risk and make sure to download the official version from GitHub.

Download a single file

1yt-dlp --cookies-from-browser chrome \
2	-x --audio-format mp3 \
3	--embed-metadata --embed-thumbnail	\
4	--parse-metadata ".*:(?P<album>tinh_tao)"	\
5	--parse-metadata "uploader:(?P<artist>.*)" \
6	--replace-in-metadata "title" " \[.*?\]" "" \
7	-o "%(title)s.%(ext)s" \
8	"PLAYLIST_URL"

Download a playlist

Download all songs in .mp3 format from a YouTube playlist using yt-dlp
1yt-dlp --cookies-from-browser chrome \
2	-x --audio-format mp3 \
3	--embed-metadata --embed-thumbnail \
4	--parse-metadata ".*:(?P<album>tinh_tao)" \
5	--parse-metadata "uploader:(?P<artist>.*)" \
6	--replace-in-metadata "title" " \[.*?\]" "" -o "%(title)s.%(ext)s" \
7	--download-archive downloaded.txt \
8	--no-overwrite \
9	"PLAYLIST_URL"
This command downloads and converts all songs from a YouTube playlist to .mp3 format. It saves the downloaded video IDs in downloaded.txt to prevent downloading the same song twice (using --no-overwrite). The command also captures all metadata (title, artist, album, cover) and removes [youtube_id] from the title.
The --cookies-from-browser option helps bypass the 403 (Forbidden) error.
Other options are available in the yt-dlp repository. Here are some useful ones:
  • Start downloading from a specific playlist index: --playlist-start 4
  • Download only up to a specific playlist index: --playlist-end 10
If you only want to generate the downloaded.txt file without downloading anything:
1yt-dlp --flat-playlist --no-overwrite \
2	--get-id "PLAYLIST_URL" | sed 's/^/youtube /' > downloaded.txt
To play a notification sound when your download completes:
1yt-dlp .... && afplay /System/Library/Sounds/Glass.aiff

Using scripts to quickly update your music library

You can create aliases (in zsh or bash) to efficiently download newly added songs from your playlists. For example, I have three playlists—"choices," "mood," and "rock"—with corresponding folders of the same names in ~/Download/musics/
1download_music_choices() {
2  cd ~/Downloads/musics/choices && yt-dlp --cookies-from-browser chrome -x --audio-format mp3 --embed-metadata --embed-thumbnail --parse-metadata ".*:(?P<album>choices)" --parse-metadata "uploader:(?P<artist>.*)" --replace-in-metadata "title" " \[.*?\]" "" -o "%(title)s.%(ext)s" --download-archive downloaded.txt --no-overwrite "<PLAY_LIST_URL>" && afplay /System/Library/Sounds/Glass.aiff
3}
4
5download_music_tinh_tao() {
6  cd ~/Downloads/musics/tinh_tao && yt-dlp --cookies-from-browser chrome -x --audio-format mp3 --embed-metadata --embed-thumbnail --parse-metadata ".*:(?P<album>rock)" --parse-metadata "uploader:(?P<artist>.*)" --replace-in-metadata "title" " \[.*?\]" "" -o "%(title)s.%(ext)s" --download-archive downloaded.txt --no-overwrite "<PLAY_LIST_URL>" && afplay /System/Library/Sounds/Glass.aiff
7}
8
9download_music_mood() {
10  yt-dlp --cookies-from-browser chrome -x --audio-format mp3 --embed-metadata --embed-thumbnail --parse-metadata ".*:(?P<album>mood)" --parse-metadata "uploader:(?P<artist>.*)" --replace-in-metadata "title" " \[.*?\]" "" -o "%(title)s.%(ext)s" --download-archive downloaded.txt --no-overwrite "<PLAY_LIST_URL>" && afplay /System/Library/Sounds/Glass.aiff
11}
12
13download_music_all() {
14  download_music_choices
15  download_music_rock
16  download_music_mood
17}
18
19download_mp3() {
20  yt-dlp --cookies-from-browser chrome -x --audio-format mp3 --embed-metadata --embed-thumbnail	--parse-metadata ".*:(?P<album>tinh_tao)"	--parse-metadata "uploader:(?P<artist>.*)" --replace-in-metadata "title" " \[.*?\]" "" -o "%(title)s.%(ext)s" --download-archive downloaded.txt --no-overwrite "$1"
21}
Now, I can simply run download_music_all to update all albums with the latest songs. Alternatively, I can use download_mp3 <url> to download either a single song or an entire playlist.

Modify music tags

For editing music metadata on macOS, I recommend MusicBrainz Picard. I use it to set the "album" attribute for all songs downloaded from the same playlist, making it easier to group them in the Apple Music app.

Music players

I use the built-in Apple Music app on both macOS and iOS for playing music.
To sync music between devices, I connect my iPhone to my MacBook with a cable and sync them through Finder → iPhone. macOS automatically identifies differences between the two devices and updates accordingly.
A key benefit of Apple Music is the ability to transfer music to Apple Watch for offline listening directly from the watch.

Using third-party tools

If Apple Music isn't your preference, consider Evermusic. It's affordable and feature-rich, though their customer service can be slow to respond. I personally stopped using it due to purchase-related issues.
Evermusic allows you to upload songs to cloud services like Google Drive, Mega, Dropbox, or iCloud, and stream directly from there.
The app offers offline downloading and device syncing over Wi-Fi.