Download your Bandcamp collection using this python script.
It requires you to have a browser with a logged in session of bandcamp open. Cookies from the browser will be used to authenticate with Bandcamp.
Supported browsers are the same as in browser_cookie3: Chrome, Chromium, Firefox, Brave, Opera, and Edge
Alternatively, you can use a Netscape format cookies file.
Albums will be downloaded into their zip files and singles will just be plain files. Downloads are organized by Artist name. Already existing files of the same name will have their file sizes checked against what it should be, and if they are the same, the download will be skipped, otherwise it will be over-written. You can use the --force flag to always overwrite existing files.
By default the download only includes unhidden items in your collection. To include hidden items as well, use the --include-hidden flag.
Downloads will happen in parallel, by default using a pool of 5 threads. You can configure how many threads to use with the --parallel-downloads/-p flag. After each download a thread will wait 1 second before trying the next download. This is to try and not overwhelm (and be rejected by) the bandcamp servers. This can be configured with the --wait-after-download flag.
If a download should fail because of an HTTP/network error, it will be retried again after a short wait. By default a file download will be attempted at most 5 times. This can be configured with the --max-download-attempts flag. By default, a failed download will wait 5 seconds before trying again. This can be configured by the --retry-wait flag.
By default, files are downloaded in mp3-320 format, but that can be changed with the --format/-f flag.
--no-progress: Disable tqdm progress bars and emit one-line structured events (JSON) to stdout per album. Progress and warnings continue to stderr for PowerShell-friendly output.--log PATH: Write a log file to PATH. If PATH is a directory (or ends with a path separator), a timestamped file is created inside. Logs include album start/finish, retries, and HTTP errors.- Resume: A small state file
.bandcamp-state.jsonis stored in the target download directory. Completed albums are skipped on re-run. If a run is interrupted, re-running resumes from where it left off. - Exit codes:
0: Success or nothing to do1: Unexpected/non-recoverable error2: Auth/cookies error (invalid or expired cookies)3: Partial success with failures (failed albums are listed on stderr and in the log)
Filename safety: Windows-incompatible characters are sanitized, and filename collisions are handled by suffixing (1), (2), etc.
This is seems to be a WSL issue. The browser_cookie3 module tries to get a secret from your keyring via dbus, but WSL may not have dbus installed, or may not have it set up as expected. As such, you may see the following error:
secretstorage.exceptions.SecretServiceNotAvailableException: Environment variable DBUS_SESSION_BUS_ADDRESS is unset
Please either check your WSL dbus installation/configuration, or run the script nativity on windows.
There is currently an issue with browser_cookie3. This has been reported within this repo here and you can see the status of it upstream here.
Sometimes a browser does not put its files in the expected location. This is especially true if the browser is installed as a flatpack or snap. As such, browser_cookie3 doesn't know where to look for the cookie store.
You can fix this by using the --cookies flag and giving it the path to your browser's cookie store, usually a file named something like Cookies or cookies.sqlite. Note: You still need to give the correct --browser flag.
Another option is to symlink the directory to the correct place. For example, the package chromium-bin often installs to the directory ~/.chromium-bin but it is expected to be at ~.chromium. You can run:
symlink -s ~/.chromium-bin ~/.chromium
and then browser_cookie3 will be able to find the cookies as expected and you will not need to use the --cookies flag.
Install the script dependencies by running:
pip install .
Run the program:
bandcamp-downloader.py [arguments]
If you run into errors or dependency issues, you can try installing exact dependency versions by running:
pip install -r requirements.txt
Install requirements using Python Poetry. Installation instructions here.
poetry install
Run the script within the poetry shell:
poetry shell
python bandcamp-downloader.py [arguments]
or directly through poetry run:
poetry run python bandcamp-downloader.py [arguments]
usage: bandcamp-downloader.py [-h]
[--browser {firefox,chrome,chromium,brave,opera,edge}]
[--cookies COOKIES]
[--directory DIRECTORY]
[--filename-format FILENAME_FORMAT]
[--format {aac-hi,aiff-lossless,alac,flac,mp3-320,mp3-v0,vorbis,wav}]
[--parallel-downloads PARALLEL_DOWNLOADS]
[--force]
[--wait-after-download WAIT_AFTER_DOWNLOAD]
[--max-download-attempts MAX_DOWNLOAD_ATTEMPTS]
[--retry-wait RETRY_WAIT]
[--include-hidden]
[--download-since DOWNLOAD_SINCE]
[--download-until DOWNLOAD_UNTIL]
[--extract]
[--summary]
[--dry-run]
[--verbose] [-v]
username
Download your collection from bandcamp. Requires a logged in session in a
supported browser so that the browser cookies can be used to authenticate with
bandcamp. Albums are saved into directories named after their artist. Already
existing albums will have their file size compared to what is expected and re-
downloaded if the sizes differ. Otherwise already existing albums will not be
re-downloaded.
positional arguments:
username Your bandcamp username, as it appears at the end of
your bandcamp collection url, I.E. bandcamp.com/user_name
optional arguments:
-h, --help show this help message and exit
--browser {firefox,chrome,chromium,brave,opera,edge}, -b {firefox,chrome,chromium,brave,opera,edge}
The browser whose cookies to use for accessing
bandcamp. Defaults to "firefox"
--cookies COOKIES, -c COOKIES
Path to a cookie file. First, we will try to use
it as a mozilla cookie jar. If that fails, it'll
be used as the path for your given browser's cookie store.
--directory DIRECTORY, -d DIRECTORY
The directory to download albums to. Defaults to the
current directory.
--filename-format FILENAME_FORMAT
The filename format for downloaded tracks. Default is
'{artist}/{artist} - {title}'.
All placeholders: item_id, artist, title
--format {aac-hi,aiff-lossless,alac,flac,mp3-320,mp3-v0,vorbis,wav}, -f {aac-hi,aiff-lossless,alac,flac,mp3-320,mp3-v0,vorbis,wav}
What format to download the songs in. Default is
'mp3-320'.
--parallel-downloads PARALLEL_DOWNLOADS, -p PARALLEL_DOWNLOADS
How many threads to use for parallel downloads. Set to
'1' to disable parallelism. Default is 5. Must be
between 1 and 32
--force Always re-download existing albums, even if they
already exist.
--wait-after-download WAIT_AFTER_DOWNLOAD
How long, in seconds, to wait after successfully
completing a download before downloading the next
file. Defaults to '1'.
--max-download-attempts MAX_DOWNLOAD_ATTEMPTS
How many times to try downloading any individual files
before giving up on it. Defaults to '5'.
--retry-wait RETRY_WAIT
How long, in seconds, to wait before trying to
download a file again after a failure. Defaults to
'5'.
--include-hidden Download items in your collection that have been marked as hidden.
--download-since DOWNLOAD_SINCE
Only download items purchased on or after the given date (lower inclusive).
YYYY-MM-DD format, defaults to all items.
--download-until DOWNLOAD_UNTIL
Only download items purchased before (upper exclusive) the given date.
YYYY-MM-DD format, defaults to all items.
--extract, -x Extracts downloaded albums, organised in {ARTIST}/{ALBUM} subdirectories.
Songs are extracted to the path specified in the `--directory`/`-d` flag,
otherwise to the current directory if not specified. Upon completion,
original .zip file is deleted.
--sevenzip SEVENZIP Optional path to 7-Zip executable (7z.exe). If provided or found on PATH,
extraction uses 7-Zip; otherwise Python's zipfile is used.
--dry-run Don't actually download files, just process all the web data
and report what would have been done.
--no-progress Disable tqdm progress bars and print one line per album instead.
--log LOG Path to a log file (or directory). If a directory, a timestamped file is created inside.
--summary Display a summary of the status of every item at the end.
Deletes the zip file on completion of command.
--verbose, -v
## Windows/PowerShell Examples
Dry run (enumerate only):
python -u bandcamp-downloader.py watchthelight
--format flac
--directory "C:\Users\BASH\Documents\Bandcamp"
--cookies "C:\Users\BASH\Downloads\bandcamp.com_cookies.txt"
--dry-run --no-progress
Deterministic single-threaded run with retries and summary:
python -u bandcamp-downloader.py watchthelight
--format flac
--directory "C:\Users\BASH\Documents\Bandcamp"
--cookies "C:\Users\BASH\Downloads\bandcamp.com_cookies.txt"
--parallel-downloads 1 --max-download-attempts 3 --retry-wait 10 --summary
Invalid cookies (expected exit code 2):
python -u bandcamp-downloader.py watchthelight
--format flac
--directory "C:\Users\BASH\Documents\Bandcamp"
--cookies "C:\Users\BASH\Downloads\not_real.txt"
PowerShell wrapper (creates separate stdout/stderr logs and prevents sleep; uses tqdm-friendly env;
extraction uses 7-Zip if available):
./RunBandcamp.ps1
When modifying required packages, please:
- Add to Poetry (
poetry add) - Then update the
requirements.txt(poetry run pip freeze > requirements.txt) and the dependencies insetup.py. - Commit all updated files
If you have a logged in session in the browser, have used the --browser/-b flag correctly, and still are being told that the script isn't finding any albums, check out the page for browser_cookie3, you might need to do some configuring in your browser to make the cookies available to the script.
If you are downloading your collection in multiple formats, the script can't tell if an already downloaded zip file is the same format or not, and will happily overwrite it. So make sure to use different directories for different formats, either by running the script somewhere else or by supplying directories to the --directory/-d flag.
If you are running windows and having issues getting things running (that is not related to WSL crashes, DBUS errors, or Visual C++ errors) you might have some luck with some of the information in this issue report.