Vinyl scrobbler for Raspberry Pi
Listens to your turntable, identifies tracks via audio fingerprinting, and scrobbles to Last.fm with Spotify-accurate metadata.
Turntable → RCA Y-splitter → USB audio adapter → Raspberry Pi → Last.fm
↓
Speakers
Spindle records audio from a USB adapter in short chunks, fingerprints each chunk to identify the track, then scrobbles to Last.fm. It uses Spotify's API for canonical artist/title formatting so vinyl scrobbles stack perfectly with your Spotify plays.
- Dual fingerprinting — AcoustID (Chromaprint) primary, ShazamIO fallback. Catches everything from mainstream to niche.
- Sliding window capture — Records in 2-second steps with 10-second overlapping windows. New fingerprint attempt every 2 seconds for fast identification without sacrificing audio quality.
- Album-lock — Identify one track, scrobble the whole side. Uses Spotify's tracklist + track durations to predict and scrobble tracks even when fingerprinting misses. Retroactively backfills tracks that played before the first identification.
- Spotify-canonical names — Scrobbles use Spotify's formatting (e.g.,
Playboi Carti — Poke It Out (with Nicki Minaj)) so they stack with Spotify plays on Last.fm. - Offline queue — WiFi drops? Scrobbles are saved locally and flushed when the connection is back. Survives reboots.
- Telegram bot — Live notifications when album-lock activates, tracks advance, and sides finish. Interactive commands:
/status,/history,/stats. - Vinyl-aware timing — Accounts for ±3% speed drift, inter-track gaps, and needle-drop imprecision.
- 30-second rule — Respects Last.fm's scrobble spec: tracks must play ≥30s to count.
- systemd service — Starts on boot, restarts on crash. Set it and forget it.
| Component | Example | Notes |
|---|---|---|
| Raspberry Pi | Pi 4 / Pi 5 | Any model with USB + network |
| USB audio adapter | Plugable USB-AUDIO | Stereo input, line level |
| RCA Y-splitters | Any | Split turntable output to speakers + Pi |
| RCA → 3.5mm cable | Any | Connect splitter to USB adapter |
| (Optional) LCD | Waveshare 3.5" IPS | For album art display (coming soon) |
Turntable (phono out) → Preamp/Receiver (line out)
↓
RCA Y-splitter
├── Speakers / Powered monitors
└── RCA → 3.5mm → USB audio adapter → Pi
Important: The Pi needs a line-level signal, not phono. If your turntable has a built-in preamp or you're using a receiver's line/tape out, you're good.
sudo apt install -y python3-pip python3-venv ffmpeg libchromaprint-tools portaudio19-dev alsa-utilsgit clone https://github.com/LuKresXD/spindle.git
cd spindle
python3 -m venv venv
source venv/bin/activate
pip install -e .cp config.example.yaml config.yamlEdit config.yaml with your API keys:
| Service | Get your key | Required |
|---|---|---|
| AcoustID | Register an application | ✅ |
| Last.fm | Create API account | ✅ |
| Spotify | Create an app | Recommended |
Spotify enables album-lock and canonical naming. Without it, Spindle still works but falls back to basic fingerprint → scrobble mode.
Find your audio device:
arecord -l
# Look for your USB adapter, e.g.: card 1: Device [USB Audio Device], device 0
# → device string: plughw:CARD=Device,DEV=0# Dry run — identify tracks without scrobbling
spindle --dry-run -v
# With Last.fm canonicalization preview
spindle --dry-run --canonicalize-preview -vspindle -v# Edit spindle.service if your paths differ
sudo cp spindle.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now spindleNow Spindle starts on boot and restarts on crash. Check logs with:
journalctl -u spindle -fSpindle's signature feature. When a track is identified:
- Lock — Fetches the album tracklist from Spotify
- Backfill — Calculates how long music has been playing. Walks backwards through the tracklist to scrobble tracks that played before identification
- Predict — Tracks forward using durations. Auto-advances and scrobbles when each track's time elapses
- Confirm — Subsequent fingerprint hits confirm or correct the prediction
- End — Silence (needle lift, side flip, end of record) cleanly ends the session
This means: drop the needle, walk away, come back to a fully scrobbled side — even if fingerprinting only caught 1 out of 6 tracks.
- Needle dropped mid-album → only backfills tracks that fit in elapsed time
- 20-second interludes → advance buffer scales (3s for short tracks, 15s for long)
- 13-minute epics → same system, just longer between advances
- Side flip → silence breaks the lock, Side B starts a fresh session
- Different record → old session finalized, new album locked
- Vinyl speed drift → ±3% tolerance on all timing calculations
See config.example.yaml for all options. Key settings:
audio:
device: "plughw:CARD=Device,DEV=0"
chunk_duration: 10 # fingerprint window size (seconds)
# Capture step is 2s — new window every 2s for fast identification
silence:
threshold_db: -22 # vinyl surface noise is ~-25 dB
min_silence_seconds: 3
scrobble:
min_play_seconds: 30 # Last.fm 30-second rule
dedup_window: 300 # ignore same track within 5 min
telegram:
bot_token: "" # from @BotFather
chat_id: "" # your Telegram chat ID
silent: false # silent notifications
verbose: false # notify on every scrobble
errors: true # notify on errors
logging:
file: "" # optional log file path
max_bytes: 5000000 # 5 MB rotation
backup_count: 3 # keep 3 rotated filesspindle/
├── cli.py # Main loop: capture → identify → scrobble
├── capture.py # Sliding window audio capture via arecord
├── fingerprint.py # AcoustID + ShazamIO identification
├── spotify.py # Spotify API: canonical names + album tracklists
├── albumlock.py # Album-lock v2: anchor-based track prediction
├── scrobbler.py # Last.fm scrobbling + offline queue
├── history.py # Scrobble history (JSONL log for stats)
├── notify.py # Telegram notifications
├── bot.py # Telegram bot command handler
├── config.py # YAML config loader
└── display.py # LCD display (stub, coming soon)
- Audio capture + dual fingerprinting (AcoustID + ShazamIO)
- Last.fm scrobbling with dedup + canonicalization
- Spotify lookup for canonical artist/title format
- Album-lock scrobbling with retroactive backfill
- Offline scrobble queue (survives WiFi drops + reboots)
- systemd service (autostart, auto-restart)
- Sliding window capture (2s step, 10s window)
- Telegram bot (notifications +
/status,/history,/stats) - Scrobble history + statistics
- Rotating log files
- Waveshare 3.5" LCD display (album art, track info, progress)
Built with pyacoustid, ShazamIO, pylast, and the Spotify Web API.
