yoanbernabeu/music-gpt-bundle
Symfony bundle that wraps the MusicGPT API so your PHP applications can generate original music from text prompts. Configure your API key, inject the ready-made client service, and call simple methods to request tracks, returning audio you can play, stream, or store.
Read this first: it is not a Laravel package. Despite the "bundle" name in the title, yoanbernabeu/music-gpt-bundle is a Symfony bundle (Symfony\Component\HttpKernel\Bundle\Bundle), documented for Symfony ^7.0 || ^8.0. There is no Laravel service provider, no package auto-discovery, and no facade. Your first 10 minutes should be spent answering "can I even install this?" before writing any feature code.
Checklist before composer require:
composer.json. Laravel 11/12 projects commonly run 8.2/8.3 — on those, install fails at the platform check. Verify with php -v and your config.platform.php in composer.json.http-foundation, console, process, finder, error-handler) but not symfony/http-kernel. The bundle class extends it, so Composer will pull it in — and your autoloader will never register the bundle.Illuminate\Container will not resolve the bundle's services for you.Where to look in the source, in this order:
src/ for the client/service interfaces — these are what you bind or wrap.src/Dto/ and src/Enum/ — the request/response shapes. The release promises full PHP 8.4 typing here, so these are the most portable part of the package and the most likely thing you'll actually reuse.DependencyInjection/Configuration.php (or config/) to learn the real config keys — the release notes don't name them.First use case: do not start with music generation. Start with Voice Management (search/browse voice models). It is read-only, cheap, and verifies auth, HTTP wiring, config, and DTO hydration in one shot. Only after that works should you touch TTS or music generation, which consume credits and are asynchronous.
Minimal Symfony usage (for reference, even if you're porting it):
# config/packages/music_gpt.yaml
music_gpt:
api_key: '%env(MUSIC_GPT_API_KEY)%'
Laravel equivalent — you build this yourself:
// app/Providers/MusicGptServiceProvider.php
public function register(): void
{
$this->app->singleton(MusicGptClient::class, fn () => new MusicGptClient(
apiKey: config('services.music_gpt.key'),
));
}
// config/services.php
'music_gpt' => ['key' => env('MUSIC_GPT_API_KEY')],
Wrap the package behind your own gateway interface. This is the single highest-leverage pattern for a 0.1.0 dependency. Define App\Contracts\MusicGenerator, implement it with the bundle's client, and bind it in a provider. Every controller, job, and test talks to your interface. When the API surface shifts (and at 0.x it will), or when you migrate off Symfony, you change one class.
Treat every conversion as an async job. Generation, voice covers, TTS, and stem extraction are all "submit → get a conversion ID → poll" per the Conversion Tracking feature. The shape that works:
final class GenerateMusicJob implements ShouldQueue
{
public int $timeout = 600;
public int $tries = 3;
public array $backoff = [10, 60, 300];
public function __construct(public readonly MusicRequestData $request) {}
public function handle(MusicGenerator $generator): void
{
$conversion = $generator->submit($this->request); // returns your DTO
$generator->awaitCompletion($conversion->id); // polls with backoff
$generator->downloadTo($conversion->id, "music/{$conversion->id}.mp3");
}
}
Never poll inside a web request. Never poll in a tight loop — audio jobs run tens of seconds to minutes, and a naive while loop will burn worker CPU and hit rate limits.
Persist the conversion ID in your own table. Add music_conversions with (id, external_id, status, type, user_id, result_path, error, created_at). The package's Conversion Tracking is stateless; your audit trail, retry logic, and "show me my generated tracks" UI all live in your schema, not the bundle's DTOs.
Stream files, don't buffer them. Audio payloads are large. Upload via Storage::disk()->putFileAs() with a stream, and download to a temp path then move — not file_get_contents into a string. On Laravel, Http::withBody($stream) style streaming beats materialising the whole file.
Keep DTOs at the boundary. Map the package's DTOs/Enums into your own models in the gateway. Do not let YoanBernabeu\MusicGpt\Dto\* types leak into Blade views, controllers, or Eloquent casts. This is also what makes the package swappable if a Laravel-native SDK appears.
Fake it in tests. Because you bound an interface, tests use a FakeMusicGenerator that returns canned DTOs. Do not let PHPUnit call the real API — the suite becomes slow, flaky, and expensive. Reserve one opt-in integration test behind an env flag (MUSIC_GPT_INTEGRATION=1).
Config discipline: API key from env() only, never committed, and referenced as config('services.music_gpt.key') at call sites so config:cache works.
composer require and go.--ignore-platform-reqs — the codebase advertises PHP 8.4-only syntax (property hooks, asymmetric visibility are plausible given "full PHP 8.4 type safety"), and you'll get parse errors at runtime.^7.0 || ^8.0 may conflict with Laravel's pinned Symfony components. Laravel constrains specific component versions; a bundle requiring Symfony 8 can drag transitive components out of sync. Run composer why-not before assuming it resolves.0.1.0 means the API is not stable. Composer treats 0.x minors as potentially breaking. Pin narrowly ("yoanbernabeu/music-gpt-bundle": "0.1.*"), read every changelog, and lean on the gateway wrapper.MusicGenerationFailed so the rest of the app isn't coupled to the vendor's hierarchy.$this->fail()); network/5xx should back off. Blanket tries = 3 will happily retry a 401 three times and triple your log noise.last_polled_at and give up after a bounded window rather than polling an abandoned conversion forever.attempts(). Nearly every "it's stuck" report is a job that died and a conversion that kept running server-side; the ID is your only thread to pull.How can I help you explore Laravel packages today?