jordanpartridge/conduit-spotify
A Laravel package that connects your application to the Spotify Web API through Conduit, providing a clean, consistent interface for authentication and Spotify data access.
This release is additive: existing conduit spotify:* invocations behave exactly as before, so upgrade with a normal composer update and no migration steps. The new surface area is entirely opt-in via --format and --non-interactive.
Minimal setup:
composer require jordanpartridge/conduit-spotify (or bump to ^2.1 if already installed).conduit spotify:current
conduit spotify:current --format=json | jq '.data.track.name'
Where to look first:
src/Commands/ — spotify:current, spotify:play, spotify:pause now all share the same option contract.src/Concerns/HandlesSpotifyOutput.php (the HandlesSpotifyOutput trait) — this is the canonical place to read how JSON envelopes, error responses, and interactive-mode detection are actually implemented. Any custom Spotify command you add should use this trait rather than hand-rolling output.config/conduit-spotify.php — credentials/scopes; unchanged in this release but still the first stop for auth failures.First real use case: swap a shell one-liner like osascript/playerctl for a portable status check that also works in CI or an AI agent loop:
conduit spotify:current --format=json --non-interactive
Interactive default, JSON on demand. --format=interactive remains the default, so no flags means the pretty status bars and notifications you already rely on. Reach for --format=json only at the boundary where a machine consumes the result — a script, a scheduled task, an HTTP endpoint. Don't sprinkle it into commands a human reads; you lose the device/progress visualization for no gain.
Treat the JSON envelope as a stable contract. Every command returns the same shape (timestamp, command, success, data), which means callers can branch on success once and handle data per command. A small helper keeps this DRY:
private function currentTrack(): ?array
{
$result = Process::run(['conduit', 'spotify:current', '--format=json', '--non-interactive']);
$payload = json_decode($result->output(), true);
return ($payload['success'] ?? false) ? $payload['data']['track'] : null;
}
Automation and scheduling. Use --non-interactive on anything invoked from a scheduler, queue worker, webhook handler, or agent. The flag suppresses prompts and status bars, which is what keeps stdout parseable. Pair it with --format=json almost every time:
Schedule::command('spotify:play', ['Focus Playlist', '--non-interactive', '--format=json'])
->weekdays()
->at('09:00');
Resume/focus workflows. spotify:pause --format=json now returns track context alongside the confirmation, which is handy for "pause and log what was playing" flows without a second round-trip to spotify:current.
Event dispatching as the integration seam. The release notes mention enhanced event dispatching alongside the new interface. If you're building app-level reactions (Slack notifications, timers, activity logging), prefer listening to the dispatched events over shelling out and parsing JSON — it keeps you inside the framework and avoids process-spawn overhead. Reserve CLI+JSON for cases where the command is genuinely external to your app.
Error handling by exit code, not string matching. The release standardizes exit codes and structured error responses. In JSON mode, check success and the error payload; don't grep stderr for "authentication" text, which is explicitly called out as having changed wording.
--format=json does not imply --non-interactive. They're separate options with separate jobs: one shapes output, the other suppresses prompts. A command that would normally prompt (e.g. spotify:play with an ambiguous track name) can still block waiting for input even with JSON output selected. Always pass both when scripting.$this->info() calls in your own wrappers, or third-party output on stdout will break json_decode. If you're piping to jq, send your own diagnostics to stderr.timestamp is UTC ISO-8601 with microseconds. Convert at the boundary if you're storing or displaying it; don't assume local time. Note it reflects when the command ran, not when playback state was sampled — for accurate progress_ms semantics, record your own receive-time.progress_ms is a snapshot. Between the API call and your consumer reading the JSON, playback has moved on. Don't drive a smooth progress bar from a single poll; poll on an interval or extrapolate client-side.spotify:* command, use HandlesSpotifyOutput so you inherit mode detection and the error envelope. Reimplementing the JSON shape by hand is the fastest way to drift from the contract Conduit's tooling expects.--format=json --non-interactive to confirm the automation path. Two runs, two failure domains.How can I help you explore Laravel packages today?