nucleos/lastfm-bundle
A Symfony bundle that integrates the Last.fm API, providing services and configuration to fetch music data such as artists, albums, and tracks and add Last.fm-powered features to your application.
nucleos/lastfm-bundle is a Symfony bundle wrapping the Last.fm API (auth, scrobbling, artist/track queries). As of 1.5.0 it requires PHP 8.3+ (8.2 support was dropped) — check your runtime and CI images before upgrading.
composer require nucleos/lastfm-bundlebundles.php) and configure credentials:# config/packages/nucleos_last_fm.yaml
nucleos_last_fm:
api:
key: '%env(LASTFM_API_KEY)%'
secret: '%env(LASTFM_API_SECRET)%'
Client (arbitrary Last.fm methods), the Scrobbler (now-playing/scrobble helpers) and the Session service (token → session key exchange).In Laravel (no bundle autoloading): there is no service provider, facade, or config publishing. In practice you either (a) register the client classes manually in a ServiceProvider with a singleton binding and a config/lastfm.php, or (b) skip the bundle and depend on the underlying client library directly, wiring auth yourself. Option (b) is usually less friction.
First use case: the auth handshake, because nothing else works per-user without a session key — getToken() → redirect the user to the Last.fm authorize URL → handle the callback → getSession($token) → persist the returned session key against your user. Then a trivial read (user.getRecentTracks) to confirm credentials before touching scrobbling.
Where to look first: the Client class for the full method surface, Scrobbler for the scrobble/now-playing shortcuts, and SessionInterface for token and session-key handling.
Per-user session keys. The API key/secret identify your app; the session key identifies the user. Store the session key on your users table (encrypted cast), and resolve a per-user client/session rather than a global singleton. A common pattern is a small factory:
final class LastFmFactory
{
public function __construct(private Client $client) {}
public function forUser(User $user): Scrobbler
{
return new Scrobbler($this->client, $user->lastfm_session_key);
}
}
Scrobble from queued jobs, never inline in a request. Scrobbling is a fire-and-forget side effect with a third-party dependency; dispatch a ScrobbleTrack job with $tries = 3, backoff(), and ShouldBeUnique keyed on track+timestamp to avoid double-scrobbling on retry. Send updateNowPlaying synchronously on playback start (it's cheap and user-visible), and the actual scrobble on completion.
Batch where possible. Last.fm accepts multiple scrobbles per request (up to 50). If you buffer offline/sync scrobbles, group them into one call rather than N calls — this is the single biggest win against rate limits.
Cache reads aggressively. artist.getInfo, album.getInfo, track.getInfo are stable-ish metadata; cache them for hours/days with a tagged cache key. Do not cache user-specific reads like recent tracks unless the TTL is very short.
Contract-first for testing. Type-hint the package's ClientInterface/SessionInterface in your own code and bind fakes in tests — this keeps your suite from hitting the network and lets you assert the exact method + params sent (signature correctness is easy to get wrong and hard to eyeball).
Laravel wiring sketch:
$this->app->singleton(Client::class, fn () => new Client(
config('lastfm.api.key'),
config('lastfm.api.secret'),
new Psr18Client(...),
));
Extension points: override the client/session services in your DI config to inject a custom HTTP client (proxy, retry middleware, logging), or decorate the Client to add metrics/circuit-breaking around Last.fm outages.
config/lastfm.php to publish, no facade. Budget for a service provider and be aware that bundle-internal config resolution (Symfony Configuration tree) won't run for you — you're passing raw values.error code (e.g. 9 = invalid session key, 11/16 = service/temporary error, 26 = suspended API key). Status-code-only error handling will silently swallow every failure. Inspect the error code and map 9 → re-auth prompt, 11/16/29 → retry with backoff, others → surface.error field. Last.fm support is unresponsive; your own request log is the only reliable diagnostic.auth.getMobileSession with username/password vs the token flow). Don't mix them; pick one per user journey.How can I help you explore Laravel packages today?