Import your Last.fm and Spotify listening history to the AT Protocol network using the fm.teal.alpha.feed.play lexicon.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: rename project from lastfm-importer to malachite

Update project name across codebase including package.json, client agent strings,
state directory path, and documentation references.

+11 -11
+4 -4
README.md
··· 1 - # Last.fm to ATProto Importer 1 + # Malachite 2 2 3 3 Import your Last.fm and Spotify listening history to the AT Protocol network using the `fm.teal.alpha.feed.play` lexicon. 4 4 ··· 256 256 - **trackName**: The name of the track 257 257 - **artists**: Array of artist objects (requires `artistName`, optional `artistMbId` for Last.fm) 258 258 - **playedTime**: ISO 8601 timestamp of when you listened 259 - - **submissionClientAgent**: Identifies this importer (`lastfm-importer/v0.6.0`) 259 + - **submissionClientAgent**: Identifies this importer (`malachite/v0.6.0`) 260 260 - **musicServiceBaseDomain**: Set to `last.fm` or `spotify.com` 261 261 262 262 ### Optional Fields ··· 283 283 "recordingMbId": "3a390ad3-fe56-45f2-a073-bebc45d6bde1", 284 284 "playedTime": "2025-11-13T23:49:36Z", 285 285 "originUrl": "https://www.last.fm/music/Cjbeards/_/Paint+My+Masterpiece", 286 - "submissionClientAgent": "lastfm-importer/v0.6.0", 286 + "submissionClientAgent": "malachite/v0.6.0", 287 287 "musicServiceBaseDomain": "last.fm" 288 288 } 289 289 ``` ··· 301 301 "releaseName": "Twenty", 302 302 "playedTime": "2021-09-09T10:34:08Z", 303 303 "originUrl": "https://open.spotify.com/track/3gZqDJkMZipOYCRjlHWgOV", 304 - "submissionClientAgent": "lastfm-importer/v0.6.0", 304 + "submissionClientAgent": "malachite/v0.6.0", 305 305 "musicServiceBaseDomain": "spotify.com" 306 306 } 307 307 ```
+2 -2
package-lock.json
··· 1 1 { 2 - "name": "lastfm-importer", 2 + "name": "malachite", 3 3 "version": "0.6.0", 4 4 "lockfileVersion": 3, 5 5 "requires": true, 6 6 "packages": { 7 7 "": { 8 - "name": "lastfm-importer", 8 + "name": "malachite", 9 9 "version": "0.6.0", 10 10 "license": "AGPL-3.0-only", 11 11 "dependencies": {
+1 -1
package.json
··· 1 1 { 2 - "name": "lastfm-importer", 2 + "name": "malachite", 3 3 "version": "0.6.0", 4 4 "description": "Import Last.fm scrobbles to ATProto with rate limiting", 5 5 "type": "module",
+1 -1
src/config.ts
··· 30 30 const platform = 31 31 PLATFORM_LABELS[process.platform] ?? process.platform; 32 32 33 - return `lastfm-importer/v0.6.0 (${platform}; Node/${process.version})`; 33 + return `malachite/v0.6.0 (${platform}; Node/${process.version})`; 34 34 } 35 35 36 36 const CLIENT_AGENT = buildClientAgent();
+2 -2
src/lib/cli.ts
··· 91 91 • Sync mode requires authentication even with --dry-run 92 92 93 93 ${'\x1b[1m'}MORE INFO:${'\x1b[0m'} 94 - Repository: https://github.com/yourusername/atproto-lastfm-importer 95 - Issues: https://github.com/yourusername/atproto-lastfm-importer/issues 94 + Repository: https://github.com/ewanc26/atproto-lastfm-importer 95 + Issues: https://github.com/ewanc26/atproto-lastfm-importer/issues 96 96 `); 97 97 } 98 98
+1 -1
src/utils/import-state.ts
··· 27 27 * Get the state file path for an import 28 28 */ 29 29 export function getStateFilePath(inputFile: string, mode: string): string { 30 - const stateDir = path.join(os.homedir(), '.lastfm-importer', 'state'); 30 + const stateDir = path.join(os.homedir(), '.malachite', 'state'); 31 31 32 32 // Create state directory if it doesn't exist 33 33 if (!fs.existsSync(stateDir)) {