# Lazuli Import Last.fm and Spotify listening history to teal.fm. ## Overview Lazuli is a command-line tool that parses listening history exports from Last.fm and Spotify, merges them to remove duplicates, and publishes them to teal.fm as `fm.teal.alpha.feed.play` records. Re-written from . ## Usage ### Authentication Set your Bluesky credentials via environment variables or flags: ```sh export LAZULI_HANDLE="your-handle.bsky.social" export LAZULI_PASSWORD="your-app-password" ``` ### Commands | Command | Usage | | :------- | :------------------------------------------------------- | | `export` | Parse and merge Last.fm/Spotify exports into a JSON file | | `import` | Import new records to Bluesky (auto-skips existing) | | `sync` | Refresh the local cache with records from Bluesky | | `stats` | Show database status and daily rate limit consumption | | `failed` | List records that failed to import | | `retry` | Attempt to re-import failed records | | `dedupe` | Remove duplicate records from your Bluesky profile | | `debug` | Dump raw records from Bluesky for troubleshooting | ### Advanced Options - **Rate Limiting**: Lazuli automatically respects Bluesky/ATProto rate limits (default 9,000 writes/day). Use `lazuli stats` to see your remaining quota. - **Automatic Resume**: The local cache tracks which records were successfully imported. If a process is interrupted, re-running the same command will skip already-published entries. - **Output Formats**: Most commands support `--output-format=json` for machine-readable output. - **Fresh Sync**: Use `--fresh` to bypass the local cache and fetch everything directly from the server. - **CAR Export**: Use `--car` with `sync` and `dedupe` commands to fetch the entire repository via `com.atproto.sync.getRepo` (much faster for large repos, uses a single API call). ## Environment Variables | Variable | Description | | ----------------- | ----------------------------------------- | | `LAZULI_HANDLE` | Bluesky handle (e.g., `user.bsky.social`) | | `LAZULI_PASSWORD` | Bluesky app password | | `LAZULI_LASTFM` | Path to Last.fm CSV file | | `LAZULI_SPOTIFY` | Path to Spotify JSON file/directory/zip | | `LAZULI_DRY_RUN` | Preview without publishing | | `LAZULI_VERBOSE` | Enable verbose logging | | `LAZULI_REVERSE` | Process records in reverse order | | `LAZULI_USE_CAR` | Use CAR export for sync/dedupe (1/true) | ## Input Formats ### Last.fm Export your listening history from Last.fm. The CSV file should have columns: - UTC timestamp - Artist name - Album name - Track name - MusicBrainz IDs (optional) ### Spotify Lazuli is designed to work with your **Extended Streaming History** from Spotify. You can request this from your [Spotify Privacy Settings](https://www.spotify.com/account/privacy/). The recommended way to use Spotify data is by passing the **ZIP archive** you receive from Spotify directly. Lazuli will automatically find and parse all streaming history files within it. Lazuli accepts: - **ZIP archives** containing extended history (Recommended) - Directories containing `Streaming_History_Audio_*.json` files - Single `Streaming_History_Audio_*.json` files > [!IMPORTANT] > Make sure to request "Extended streaming history", as the standard "Account data" export does not contain your full listening history. ## Features - **Cross-source deduplication**: Merges Last.fm and Spotify data, removing duplicates within a configurable time tolerance - **Rate limiting**: Respects ATProto rate limits with configurable batch sizes and delays - **Automatic resume**: Cache tracks imported records - re-running skips already-imported entries - **Dry-run mode**: Preview imports without publishing - **Cache management**: Caches teal records for faster subsequent operations