A decentralized music tracking and discovery platform built on AT Protocol 🎵 rocksky.app
spotify atproto lastfm musicbrainz scrobbling listenbrainz
at main 161 lines 5.1 kB view raw
1#!/usr/bin/env node 2 3import chalk from "chalk"; 4import { albums } from "cmd/albums"; 5import { artists } from "cmd/artists"; 6import { createApiKey } from "cmd/create"; 7import { mcp } from "cmd/mcp"; 8import { nowplaying } from "cmd/nowplaying"; 9import { scrobble } from "cmd/scrobble"; 10import { scrobbles } from "cmd/scrobbles"; 11import { search } from "cmd/search"; 12import { stats } from "cmd/stats"; 13import { tracks } from "cmd/tracks"; 14import { whoami } from "cmd/whoami"; 15import { Command } from "commander"; 16import { version } from "../package.json" assert { type: "json" }; 17import { login } from "./cmd/login"; 18import { sync } from "cmd/sync"; 19import { initializeDatabase } from "./drizzle"; 20import { scrobbleApi } from "cmd/scrobble-api"; 21 22await initializeDatabase(); 23 24const program = new Command(); 25 26program 27 .name("rocksky") 28 .description( 29 ` 30 ___ __ __ _______ ____ 31 / _ \\___ ____/ /__ ___ / /____ __ / ___/ / / _/ 32 / , _/ _ \\/ __/ '_/(_-</ '_/ // / / /__/ /___/ / 33 /_/|_|\\___/\\__/_/\\_\\/___/_/\\_\\\\_, / \\___/____/___/ 34 /___/ 35 Command-line interface for Rocksky ${chalk.magentaBright( 36 "https://rocksky.app", 37 )} – scrobble tracks, view stats, and manage your listening history.`, 38 ) 39 .version(version); 40 41program.configureHelp({ 42 styleTitle: (str) => chalk.bold.cyan(str), 43 styleCommandText: (str) => chalk.yellow(str), 44 styleDescriptionText: (str) => chalk.white(str), 45 styleOptionText: (str) => chalk.green(str), 46 styleArgumentText: (str) => chalk.magenta(str), 47 styleSubcommandText: (str) => chalk.blue(str), 48}); 49 50program.addHelpText( 51 "after", 52 ` 53${chalk.bold("\nLearn more about Rocksky:")} https://docs.rocksky.app 54${chalk.bold("Join our Discord community:")} ${chalk.blueBright("https://discord.gg/EVcBy2fVa3")} 55`, 56); 57 58program 59 .command("login") 60 .argument("<handle>", "your AT Proto handle (e.g., <username>.bsky.social)") 61 .description("login with your AT Proto account and get a session token") 62 .action(login); 63 64program 65 .command("whoami") 66 .description("get the current logged-in user") 67 .action(whoami); 68 69program 70 .command("nowplaying") 71 .argument( 72 "[did]", 73 "the DID or handle of the user to get the now playing track for", 74 ) 75 .description("get the currently playing track") 76 .action(nowplaying); 77 78program 79 .command("scrobbles") 80 .option("-s, --skip <number>", "number of scrobbles to skip") 81 .option("-l, --limit <number>", "number of scrobbles to limit") 82 .argument("[did]", "the DID or handle of the user to get the scrobbles for") 83 .description("display recently played tracks") 84 .action(scrobbles); 85 86program 87 .command("search") 88 .option("-a, --albums", "search for albums") 89 .option("-t, --tracks", "search for tracks") 90 .option("-u, --users", "search for users") 91 .option("-l, --limit <number>", "number of results to limit") 92 .argument( 93 "<query>", 94 "the search query, e.g., artist, album, title or account", 95 ) 96 .description("search for tracks, albums, or accounts") 97 .action(search); 98 99program 100 .command("stats") 101 .option("-l, --limit <number>", "number of results to limit") 102 .argument("[did]", "the DID or handle of the user to get stats for") 103 .description("get the user's listening stats") 104 .action(stats); 105 106program 107 .command("artists") 108 .option("-l, --limit <number>", "number of results to limit") 109 .argument("[did]", "the DID or handle of the user to get artists for") 110 .description("get the user's top artists") 111 .action(artists); 112 113program 114 .command("albums") 115 .option("-l, --limit <number>", "number of results to limit") 116 .argument("[did]", "the DID or handle of the user to get albums for") 117 .description("get the user's top albums") 118 .action(albums); 119 120program 121 .command("tracks") 122 .option("-l, --limit <number>", "number of results to limit") 123 .argument("[did]", "the DID or handle of the user to get tracks for") 124 .description("get the user's top tracks") 125 .action(tracks); 126 127program 128 .command("scrobble") 129 .argument("<track>", "the title of the track") 130 .argument("<artist>", "the artist of the track") 131 .option("-t, --timestamp <timestamp>", "the timestamp of the scrobble") 132 .option("-d, --dry-run", "simulate the scrobble without actually sending it") 133 .description("scrobble a track to your profile") 134 .action(scrobble); 135 136program 137 .command("create") 138 .description("create a new API key") 139 .command("apikey") 140 .argument("<name>", "the name of the API key") 141 .option("-d, --description <description>", "the description of the API key") 142 .description("create a new API key") 143 .action(createApiKey); 144 145program 146 .command("mcp") 147 .description("starts an MCP server to use with Claude or other LLMs") 148 .action(mcp); 149 150program 151 .command("sync") 152 .description("sync your local Rocksky data from AT Protocol") 153 .action(sync); 154 155program 156 .command("scrobble-api") 157 .description("start a local listenbrainz/lastfm compatibility server") 158 .option("-p, --port <port>", "the port to listen on", "8778") 159 .action(scrobbleApi); 160 161program.parse(process.argv);