A Python CLI tool that automates downloading tracks from SoundCloud and importing them into Apple Music on macOS.
SC2AM provides a small, repeatable workflow for importing SoundCloud tracks into your macOS Music library:
- Validate a SoundCloud track URL
- Download the audio using yt-dlp and convert/normalize to MP3
- Embed metadata (title, artist, album, genre, date) and cover artwork
- Open Music.app and import the tagged MP3
- Optionally add the track to a specified playlist
The downloaded MP3 files are automatically enriched with SoundCloud metadata (title, artist, album, genre, date) and cover artwork, with improved title and artist mapping so Apple Music shows the correct track information after import.
- macOS (Apple Music integration requires macOS)
- Python 3.10+
- yt-dlp (will be installed as dependency)
Apple Music automation relies on macOS permissions and Music.app being available locally. Before using SC2AM, make sure:
- Music.app is installed and can be opened manually on this Mac.
- Music.app has been launched at least once so the library is initialized.
- Automation permission is allowed for the app you run SC2AM from, such as Terminal, iTerm, VS Code, or PyCharm.
- System Settings > Privacy & Security > Automation allows that app to control Music.
- The Music library is accessible on the current macOS account you are using.
For a step-by-step checklist, see docs/macos-setup.md.
- Clone or download the project:
git clone https://github.com/zfl4wless/sc2am.git
cd sc2am- Create a virtual environment (recommended):
python3 -m venv venv
source venv/bin/activate- Install dependencies:
pip install -r requirements.txt- Install from source (optional) If you want to work on the project or install it in editable mode:
pip install -e .- Initialize configuration (optional):
python main.py config initThis creates a default config at ~/.sc2am/config.yaml.
SoundCloud-Links must point to a single track, for example:
https://soundcloud.com/artist/track
or https://www.soundcloud.com/artist/track.
Download a single track:
python main.py download "https://soundcloud.com/artist/track"Download multiple tracks in one run:
python main.py download \
"https://soundcloud.com/artist/track1" \
"https://soundcloud.com/artist/track2"If you want to keep processing after one URL fails, use:
python main.py download \
"https://soundcloud.com/artist/track1" \
"https://soundcloud.com/artist/track2" \
--continue-on-errorDownload and add to playlist:
python main.py download "https://soundcloud.com/artist/track" --playlist "My Playlist"If you do not pass --playlist, SC2AM uses the configured default_playlist when one is set. Playlist names are matched against the playlists currently available in Apple Music, and the app will tell you clearly if the playlist is missing or if the name is duplicated.
Don't automatically open Music app:
python main.py download "https://soundcloud.com/artist/track" --no-openCreate a file urls.txt with one URL per line:
https://soundcloud.com/artist/track1
https://soundcloud.com/artist/track2
# This is a comment
https://soundcloud.com/artist/track3
Then process all URLs:
python main.py batch urls.txtBatch options:
# Add all tracks to a playlist
python main.py batch urls.txt --playlist "My Playlist"
# Continue processing even if a URL fails
python main.py batch urls.txt --continue-on-errorSee the CLI and configuration contract for the complete interface. Precedence is explicit CLI options > environment > selected YAML file > defaults. A custom YAML file replaces the default file. Invalid YAML, unknown keys, and invalid values are reported instead of silently ignored.
View current configuration:
python main.py config showConfiguration File (~/.sc2am/config.yaml):
download_dir: ~/Downloads/sc2am
music_library_path: null
default_playlist: null
keep_downloads: true
open_music_app: true
continue_on_error: false
normalize_metadata: true
skip_existing_tracks: false
log_level: INFO
log_file: nullEnvironment variables:
Use exported variables or prefix the command with assignments. .env files are not
loaded automatically.
SC2AM_DOWNLOAD_DIR=~/Music/Downloads SC2AM_PLAYLIST="My Playlist" SC2AM_LOG_LEVEL=DEBUG \
python main.py download "https://soundcloud.com/artist/track"Active settings:
| Option | Type | Default | Description |
|---|---|---|---|
download_dir |
Path | ~/Downloads/sc2am |
MP3 destination |
default_playlist |
String or null | null |
Default playlist |
open_music_app |
Bool | true |
Automatically opens MP3s in Music |
continue_on_error |
Bool | false |
Continues after an input/download failure |
log_level |
String | INFO |
DEBUG, INFO, WARNING, ERROR, or CRITICAL |
log_file |
Path or null | null |
Optional detailed log file |
music_library_path, keep_downloads, normalize_metadata, and
skip_existing_tracks are accepted compatibility settings and currently have no
workflow effect. Music uses its active library, MP3s are retained, metadata tagging
is always attempted, and Music-library duplicate detection is not implemented.
See the contract for all environment variable names and accepted values.
Both download and batch support --no-open / --open,
--continue-on-error / --stop-on-error, --playlist NAME, and --dry-run.
Omitted options inherit configuration. --playlist "" disables the configured
playlist. --no-open leaves playlist actions enabled; use both to avoid Music
interaction. Dry runs validate and preview without downloads, file writes, or
Music actions.
# Use custom config file
python main.py --config /path/to/config.yaml download "..."
# Set log level
python main.py --log-level DEBUG download "..."Batch download with logging:
python main.py --log-level DEBUG batch urls.txt --continue-on-errorDownload to custom directory:
SC2AM_DOWNLOAD_DIR=~/Music python main.py download "..."- Validate - Checks if the provided URL is from a supported platform
- Download - Uses yt-dlp to download audio as MP3 (192kbps)
- Tag - Embeds title, artist, album/genre/date and cover artwork into the MP3
- Open - Launches Apple Music with the tagged MP3 file
- Add - (Optional) Adds track to specified playlist via AppleScript
SC2AM automatically retries transient download and Apple Music import failures a few times before surfacing an error, so brief network hiccups or a busy Music.app are less likely to interrupt a run.
SC2AM now returns stable exit codes for scripting:
0- command completed without input/download failures1- runtime/download failure or a run that continued past invalid entries2- usage/configuration error or an input-validation abort
Music open/playlist failures remain warnings and do not change exit status. Summary success counts describe downloads or dry-run previews, not confirmed imports.
CLI progress output is written as clean status lines. Python logger output on the console is limited to warnings and errors so informational log lines do not duplicate the CLI status messages. To retain detailed logs, configure log_file.
When running batch operations with multiple links or URLs from a file, SC2AM provides improved logging and summary output to help you debug and understand the results:
- Grouped logs - Each track is clearly separated with visual dividers for easy scanning
- Real-time status - Per-track status updates show what SC2AM is doing (downloading, opening, adding to playlist)
- Success rate - Final summary includes percentage of successful downloads or previews
- Failed track details - If any tracks fail, the summary lists each failed URL with its specific error message
Example output:
──────────────────────────────────────────────
Track 1/3: Processing https://soundcloud.com/artist/track1
Track 1/3: Validating SoundCloud URL...
Track 1/3: OK: Valid SoundCloud URL
Track 1/3: Downloading track...
Track 1/3: OK: Downloaded: track1.mp3
Track 1/3: Done!
──────────────────────────────────────────────
Track 2/3: Processing https://soundcloud.com/artist/track2
Track 2/3: Validating SoundCloud URL...
Track 2/3: OK: Valid SoundCloud URL
Track 2/3: Downloading track...
Track 2/3: ERROR: Track not found
──────────────────────────────────────────────
Track 3/3: Processing https://soundcloud.com/artist/track3
Track 3/3: Validating SoundCloud URL...
Track 3/3: OK: Valid SoundCloud URL
Track 3/3: Downloading track...
Track 3/3: OK: Downloaded: track3.mp3
Track 3/3: Done!
Summary: 2 succeeded, 1 failed (67% success rate)
Failed tracks:
1. https://soundcloud.com/artist/track2
Error: Track not found
pip install yt-dlp --upgrade- Ensure Music.app is installed (comes with macOS)
- Check your
open_music_appsetting in config - Confirm the app you use to run SC2AM has Automation permission for Music in System Settings
- Open Music.app once manually and confirm it launches without errors
- Go to System Settings > Privacy & Security > Automation and allow the app you use to run SC2AM to control Music
- If you previously denied access, re-run SC2AM after re-enabling the permission so macOS can prompt again if needed
- See
docs/macos-setup.mdfor the full checklist
- Playlist name must exactly match your Music.app playlists
- Ensure the Music.app is not currently playing (can interfere with AppleScript)
- Check that download directory exists and is writable:
mkdir -p ~/Downloads/sc2am
chmod 755 ~/Downloads/sc2amContributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Make your changes
- Submit a pull request
# Install with dev dependencies
pip install -r requirements-dev.txt
# Code formatting
black sc2am/
# Linting
flake8 sc2am/Dependency source of truth:
pyproject.tomlis the canonical dependency definition.requirements.txtandrequirements-dev.txtare thin compatibility wrappers that install from project metadata.
MIT License - see LICENSE file for details
- This tool is for personal use to manage legally acquired music
- Respect copyright laws in your jurisdiction
- SoundCloud's terms of service should be respected
For issues, feature requests, or questions:
- Open an issue on GitHub
- Check existing issues first