bandcamp-plex-sync
bandcamp-plex-sync helps keep a purchased Bandcamp collection in sync with a
local Plex music library.
It can:
- Fetch your Bandcamp fan collection.
- Incrementally scan a Plex-style local music tree such as
/nas/music. - Cache scanned tags in SQLite so unchanged audio files are not reopened each run.
- Report albums/tracks that appear to be missing locally.
- Download missing purchased FLAC files from Bandcamp.
- Extract Bandcamp album ZIPs into Plex-friendly
Artist/Album/directories.
The tool is intentionally conservative: it never deletes existing music and it
dry-runs downloads unless you pass --yes.
Requirements
- Linux/macOS with Python 3.11+
uvinstalled- A browser where you are logged in to Bandcamp
- A local Plex music directory, defaulting to
/nas/music
Bandcamp purchased FLAC downloads require authentication. The tool reads
Bandcamp cookies from your browser with browser-cookie3; no Bandcamp password
is stored by this tool.
Installation
Install the published command with uv:
uv tool install bandcamp-plex-sync
bandcamp-plex-sync --help
To run from a source checkout:
git clone git@github.com:feoh/bandcamp-plex-sync.git
cd bandcamp-plex-sync
uv sync
uv run bandcamp-plex-sync --help
The checkout also retains a development wrapper with inline uv dependency
metadata:
./bin/bandcamp-plex-sync --help
Quick start
Run an authenticated audit, then download missing FLAC albums/tracks:
bandcamp-plex-sync audit YOUR_BANDCAMP_USERNAME --cookies-from-browser
bandcamp-plex-sync download-missing --yes
For the original use case:
bandcamp-plex-sync audit feoh --cookies-from-browser
bandcamp-plex-sync download-missing --yes
By default, files are written under /nas/music.
Commands
audit
Fetches your Bandcamp collection, scans the local music directory, compares both, and writes reports.
bandcamp-plex-sync audit USER --cookies-from-browser
Useful options:
bandcamp-plex-sync audit USER \
--music-root /nas/music \
--output-dir ~/.cache/bandcamp-plex-sync \
--cookies-from-browser \
--include-hidden
Outputs:
~/.cache/bandcamp-plex-sync/
bandcamp-collection.json # fetched Bandcamp collection metadata
local-scan.json # scanned local audio metadata and incremental scan stats
music-scan.sqlite3 # persistent file signatures and cached tag metadata
sync-report.json # machine-readable comparison
sync-report.md # human-readable report
missing-urls.txt # Bandcamp item pages for missing items
missing.csv # spreadsheet-friendly missing item list
download-missing
Downloads missing purchased items from sync-report.json.
bandcamp-plex-sync download-missing --yes
FLAC is the default:
bandcamp-plex-sync download-missing --download-format flac --yes
Safety/testing options:
# Dry run; shows what would download
bandcamp-plex-sync download-missing
# Test with one item
bandcamp-plex-sync download-missing --limit 1 --yes
# Write somewhere other than /nas/music
bandcamp-plex-sync download-missing --destination ./downloads --yes
# Keep Bandcamp ZIP archives after extraction
bandcamp-plex-sync download-missing --keep-archives --yes
# Replace existing files
bandcamp-plex-sync download-missing --overwrite --yes
Downloaded album ZIPs are extracted into:
/nas/music/Artist/Album/
Single-track FLAC downloads are written similarly:
/nas/music/Artist/Track Title/Artist - Track Title.flac
auth-check
Verify that one of the supported browser profiles is logged in to the account that owns the requested collection and that Bandcamp exposes protected download URLs:
bandcamp-plex-sync auth-check USER
This is a diagnostic check only. It does not create a persistent login or store browser cookies.
fetch
Only fetch Bandcamp metadata:
bandcamp-plex-sync fetch USER --cookies-from-browser
scan
Incrementally scan local music metadata:
bandcamp-plex-sync scan --music-root /nas/music
The first run reads tags from every audio file and records the file size,
nanosecond modification/change times, device, and inode in
~/.cache/bandcamp-plex-sync/music-scan.sqlite3. Later runs still walk the
filesystem to detect additions, modifications, renames, and deletions, but they
reuse cached tags for unchanged files instead of opening and parsing every audio
file. This makes repeat scans much faster while retaining change detection.
The scan and audit commands both support:
# Put the checkpoint database somewhere else
bandcamp-plex-sync scan --checkpoint-db /path/to/music-checkpoints.sqlite3
# Ignore cached signatures and refresh every track's metadata
bandcamp-plex-sync scan --rescan-all
When --output or --output-dir is changed without an explicit
--checkpoint-db, the database is placed alongside the JSON output.
compare
Compare previously fetched/scanned JSON files:
bandcamp-plex-sync compare
Authentication and browser cookies
Use --cookies-from-browser with fetch or audit when you want purchased
download URLs. download-missing uses browser authentication by default because
opening Bandcamp's protected download pages requires it. Authentication is
command-scoped; no login session is persisted between invocations.
Use auth-check USER to diagnose authentication independently before running an
audit or download.
Every discovered profile in each supported browser is checked, rather than only the browser's default profile. The tool verifies that the selected session owns the requested Bandcamp collection before accepting its cookies; this prevents an anonymous or wrong-profile session from silently producing empty download URLs.
Supported browser cookie stores are tried in this order:
- Firefox
- LibreWolf
- Chrome
- Chromium
- Brave
- Vivaldi
- Edge
- Opera
If cookies fail to load:
- Make sure you are logged in to Bandcamp in one of those browsers.
- Close the browser if its cookie database is locked.
- Try running the command from the same desktop user account as the browser.
- Confirm the username passed to
auditis the collection owned by the logged-in Bandcamp account.
Bandcamp subscription collection entries are not individually downloadable. Their album and track releases appear as separate downloadable collection items.
Matching behavior
The audit compares normalized artist, album, and track metadata. It uses local audio tags when present and falls back to Plex-style paths:
Artist/Album/Track.ext
The report distinguishes:
matched: confident local matchpossible: fuzzy match worth reviewingmissing: no good local match found
You can tune fuzzy matching with:
bandcamp-plex-sync audit USER --threshold 0.90
Higher thresholds produce fewer possible matches.
Privacy and safety
- The tool does not store your Bandcamp password.
- Authenticated Bandcamp redownload URLs are saved in
bandcamp-collection.jsonandsync-report.json; treat these files as private. - Existing music is skipped by default.
- The local SQLite checkpoint database contains file paths and music tags, but no Bandcamp credentials or browser cookies.
- Nothing is downloaded unless
download-missing --yesis used. - Nothing is deleted from your Plex library.
Development
uv sync
uv run python -m compileall -q src tests
uv run pre-commit run --all-files
uv run mypy src/bandcamp_plex_sync
uv run python -m unittest discover -s tests -v
uv build
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bandcamp_plex_sync-0.2.0.tar.gz.
File metadata
- Download URL: bandcamp_plex_sync-0.2.0.tar.gz
- Upload date:
- Size: 65.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c7ea78db177e6957b1f14f583b560c60e87db576086b9260c889369908abbb5
|
|
| MD5 |
88ff5c1b1b2ba52ddbcc974d5ed3439f
|
|
| BLAKE2b-256 |
898e9a9b595eca71bfdcbe0ac4974a5d4afc1eb27edf4a5f048e00565d8a3088
|
Provenance
The following attestation bundles were made for bandcamp_plex_sync-0.2.0.tar.gz:
Publisher:
release.yml on feoh/bandcamp-plex-sync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bandcamp_plex_sync-0.2.0.tar.gz -
Subject digest:
9c7ea78db177e6957b1f14f583b560c60e87db576086b9260c889369908abbb5 - Sigstore transparency entry: 2304706403
- Sigstore integration time:
-
Permalink:
feoh/bandcamp-plex-sync@7eb923c98c00a2a96fbfd6fbe2113b58545e2a61 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/feoh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7eb923c98c00a2a96fbfd6fbe2113b58545e2a61 -
Trigger Event:
release
-
Statement type:
File details
Details for the file bandcamp_plex_sync-0.2.0-py3-none-any.whl.
File metadata
- Download URL: bandcamp_plex_sync-0.2.0-py3-none-any.whl
- Upload date:
- Size: 18.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f33d8fc2a6a9d95b3faceb7f353cb216baf041ed96f0ff77e6d10157764074a4
|
|
| MD5 |
52a5306dc6caebb07d2372212e69c195
|
|
| BLAKE2b-256 |
4f79d3bd99c3517ba4b104f275e590f281cba3cec419be775a35014a33ed18ce
|
Provenance
The following attestation bundles were made for bandcamp_plex_sync-0.2.0-py3-none-any.whl:
Publisher:
release.yml on feoh/bandcamp-plex-sync
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bandcamp_plex_sync-0.2.0-py3-none-any.whl -
Subject digest:
f33d8fc2a6a9d95b3faceb7f353cb216baf041ed96f0ff77e6d10157764074a4 - Sigstore transparency entry: 2304706435
- Sigstore integration time:
-
Permalink:
feoh/bandcamp-plex-sync@7eb923c98c00a2a96fbfd6fbe2113b58545e2a61 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/feoh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7eb923c98c00a2a96fbfd6fbe2113b58545e2a61 -
Trigger Event:
release
-
Statement type: