pyrex-fwi/deejaypool-bundle
Symfony bundle for integrating DeejayPool into PHP applications. It provides service configuration and framework wiring to help register DeejayPool components quickly, reducing boilerplate and simplifying setup in Symfony projects.
First, reclassify what this is. pyrex-fwi/deejaypool-bundle is a Symfony bundle whose own description calls it a "test project." It has no stars, no dependents, and no stated stability guarantees. That means you should treat it as reference code you read, not a dependency you composer require into a Laravel app. Plan your first hour around reading, not installing.
Verification checklist before you invest anything:
composer.json at the repo root. Confirm the type (symfony-bundle), the autoload.psr-4 namespace, the PHP constraint, and whether it pulls symfony/framework-bundle. If it requires the framework bundle, it will not boot inside Laravel without a shim.DependencyInjection/ — that's where the bundle's config keys (Configuration::getConfigTreeBuilder) and service wiring live. This is the single most informative directory in any Symfony bundle.Command/. A "download files from a digital DJ pool" project almost always ships as one or more console commands. Reading a command's execute() tells you the whole workflow end-to-end faster than reading services.Resources/config/ (services.yaml/services.xml) to see how many services exist and how they depend on each other. If the graph is two or three classes, extraction is trivial.tests/ directory. Its absence tells you how much you trust the HTTP and auth logic.Your first use case should be the smallest possible: authenticate against the pool once, fetch a single track's metadata as a structured array or DTO, and print it. Do that in a throwaway script before you write any Laravel code. If you cannot get one track's metadata, the package is not a viable base and you should stop.
If step five succeeds, the realistic Laravel path is: clone the repo, read the downloader and auth classes, and port them into app/Services/DeejayPool/ behind a small interface. You get the pool's quirks without inheriting a Symfony bundle into a Laravel container.
Pattern A — Port, don't install (recommended). Create app/Services/DeejayPool/ with three pieces you lift directly from the source: an auth/login call, a search-or-list call, and a file-download call. Wrap them in a single DeejayPoolClient that takes a Laravel-configured Illuminate\Http\Client\Factory and a Filesystem disk. The Symfony version will use symfony/http-client or Guzzle; swapping to Laravel's HTTP client is mechanical and removes an entire dependency tree.
// app/Services/DeejayPool/DeejayPoolClient.php
final class DeejayPoolClient
{
public function __construct(
private readonly Factory $http,
private readonly Filesystem $disk,
private readonly array $config, // base_url, credentials, disk name
) {}
public function tracks(array $filters = []): array { /* ported from source */ }
public function download(string $trackId): string
{
$path = "deejaypool/{$trackId}.mp3";
$this->disk->writeStream($path, $this->http
->withBasicAuth($this->config['user'], $this->config['pass'])
->timeout(300)
->get($this->config['base_url']."/download/{$trackId}")
->toPsrResponse()
->getBody());
return $path;
}
}
Pattern B — Sidecar CLI. If the port is more work than it's worth, keep the bundle in its own Composer project on the same host and shell out to it:
Process::timeout(600)->run("php /opt/deejaypool/bin/console app:download {$trackId}");
This preserves whatever Symfony wiring the project already has, at the cost of a second PHP runtime, a second dependency tree, and no shared error handling. Acceptable for a one-off backfill; poor for anything scheduled or user-facing.
Pattern C — Scheduled batch, not request-time. DJ pool downloads are slow, large, and credentialed. Whatever path you take, put the work on a queue or the scheduler (Schedule::command(...)->hourly()), never in a web request. The job should be idempotent: check whether the track ID already exists in your tracks table or storage before re-downloading.
Integration tips that matter day-to-day:
TrackData object so a pool API change breaks exactly one file.Artist - Title (Extended Mix).mp3), and re-encoding changes hashes.Storage::disk('deejaypool') so local, S3, and test-fake behaviour all work without touching the ported code.RateLimiter::for('deejaypool', fn () => Limit::perMinute(10)) around the client. Pool accounts get throttled or banned for aggressive scraping far more often than APIs do.The Symfony/Laravel impedance mismatch is the headline gotcha. A bundle's services are registered through DependencyInjection/*Extension.php and booted by Kernel. Laravel's container knows nothing about services.yaml, #[Autowire], tagged services, or Bundle::boot(). There is no supported bridge. Anything you do beyond copying classes is unsupported and will break on the next composer update on either side.
Pin to a commit, and fork it. With zero dependents and zero stars, there is no community watching for breaking changes, no security advisories, and no maintenance cadence. If you consume it at all, consume a fork under your own namespace so the dependency can never disappear out from under you. Do not put dev-master in a composer.json you care about.
Expect no documentation. The bundle has no README worth the name. Your API documentation is the source code, and your integration test suite is the only safety net. Budget time to write characterization tests against a real account before you refactor anything.
Credential handling is on you. "Test project" code very often hardcodes endpoints, user agents, or credentials, or reads them from $_ENV with no validation. Audit every class for hardcoded strings before you let it near production. Move credentials to config/services.php and .env, and check that no secrets land in log output — pool login calls are a classic place for password-in-URL leaks.
HTTP client differences will bite. Symfony's HttpClientInterface returns lazy responses and throws TransportExceptionInterface; Guzzle throws GuzzleHttp\Exception\*; Laravel's client returns a Response and throws RequestException only when you call throw(). Don't copy-paste error handling across — rewrite it. Pay particular attention to timeouts and to max_duration for large file downloads, which is the single most common cause of truncated MP3s.
Large-file mechanics. If the original streams to a local temp path, keep streaming. Never ->body() a 50 MB track into a PHP string; you'll hit memory_limit under any concurrency. Verify final file size against the Content-Length header and treat a mismatch as failure.
Legal and terms-of-service check. These pools are paid subscription services, and their terms typically restrict automated downloading. Confirm your intended use (personal library vs. redistribution vs. automated archival) is permitted before you build a scheduler around it. This is not a technical gotcha, but it is the one most likely to end the project.
Extension point, such as it is: the HTTP/auth layer. Fork the repo, extract the transport into a single class behind an interface, and stub it in tests. Everything else in the package should become replaceable after that one extraction — and if it doesn't, the package is too tangled to build on and you should write the client from scratch. Given the project's provenance, writing a focused DeejayPoolClient yourself is likely a two-day job versus an open-ended maintenance relationship with unmaintained code.
How can I help you explore Laravel packages today?