macOS fetch track -> push to lastfm
  • Rust 79.7%
  • Nix 20.3%
Find a file
2026-06-24 14:47:48 +03:00
nix feat: meow nya (unwheelchairing my nixfiles config and also add serde_nixos) 2026-06-24 14:47:48 +03:00
src feat: meow nya (unwheelchairing my nixfiles config and also add serde_nixos) 2026-06-24 14:47:48 +03:00
.gitignore feat: initial 2026-06-20 12:57:27 +03:00
.rustfmt.toml feat: initial 2026-06-20 12:57:27 +03:00
Cargo.lock feat: meow nya (unwheelchairing my nixfiles config and also add serde_nixos) 2026-06-24 14:47:48 +03:00
Cargo.toml feat: meow nya (unwheelchairing my nixfiles config and also add serde_nixos) 2026-06-24 14:47:48 +03:00
config.example.toml fix: readme and config example 2026-06-20 13:13:02 +03:00
flake.lock feat: initial 2026-06-20 12:57:27 +03:00
flake.nix feat: nix module and chore: fmt 2026-06-20 13:03:57 +03:00
README.md fix: readme and config example 2026-06-20 13:13:02 +03:00

Vivienne

macOS Spotify (and maybe not only Spotify) scrobbler for a self-hosted Last.fm-compatible instance

listens to Spotify playback notifications and pushes now playing + scrobble to the instances' Last.fm 2.0 API

how it even works

  • reads track metadata directly from Spotify.app playback notifications via NSDistributedNotificationCenter
  • authenticates once with auth.getMobileSession (your instance username + password), caches the session key in .vivienne_session
  • sends track.updateNowPlaying when a track starts
  • sends track.scrobble once per play, using the standard Last.fm threshold: track longer than 30s, played to half its length or 4 minutes (whichever comes first)

setup

cp config.example.toml config.toml   # then edit username/password
cargo build --release
./target/release/vivienne

config is looked up in this order: $VIVIENNE_CONFIG, ./config.toml, ~/.config/vivienne/config.toml

install

cargo install --path .

run in the background (launchd)

~/Library/LaunchAgents/rs.cuddles.vivienne.plist (pls adjust the two absolute paths):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>            <string>rs.cuddles.vivienne</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Users/USERNAME/.local/bin/vivienne</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
        <key>VIVIENNE_CONFIG</key>
        <string>/Users/USERNAME/.config/vivienne/config.toml</string>
    </dict>
    <key>RunAtLoad</key>        <true/>
    <key>KeepAlive</key>        <true/>
    <key>StandardOutPath</key>  <string>/tmp/vivienne.log</string>
    <key>StandardErrorPath</key><string>/tmp/vivienne.log</string>
</dict>
</plist>
launchctl load ~/Library/LaunchAgents/rs.cuddles.vivienne.plist
# also stop:  launchctl unload ~/Library/LaunchAgents/rs.cuddles.vivienne.plist

TODO

  • UI (objc2 maybe?? also maybe tray-icon/tao)