承接 audd/audd 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

audd/audd

Composer 安装命令:

composer require audd/audd

包简介

Official PHP SDK for the AudD music recognition API.

README 文档

README

Powered by AudD CI Contract Packagist PHP versions

Official PHP SDK for music recognition API: identify music from a short audio clip, a long audio file, or a live stream.

The API itself is so simple that it can easily be used even without an SDK: docs.audd.io.

Quickstart

composer require audd/audd:^1.5.15

Get your API token at dashboard.audd.io.

Recognize from a URL:

use AudD\AudD;

$audd = new AudD('your-api-token');
$result = $audd->recognize('https://audd.tech/example.mp3');
if ($result !== null) {
    echo $result->artist . '' . $result->title;
}

Recognize from a local file:

use AudD\AudD;

$audd = new AudD('your-api-token');
$result = $audd->recognize('/path/to/clip.mp3');
if ($result !== null) {
    echo $result->artist . '' . $result->title;
}

recognize() accepts a URL, a filesystem path, a PSR-7 StreamInterface, a resource handle, or raw bytes wrapped via AudD::bytes($buf) — auto-detected. It returns a RecognitionResult on a match, or null when the clip isn't recognized.

For longer audio files (full-length songs, short-form videos, podcasts, broadcasts, DJ sets), use recognizeEnterprise($source, limit: ...) — it returns list<EnterpriseMatch>, one per song detected across the file.

Authentication

Pass the token as a named argument:

$audd = new AudD('your-token');

Or omit it and set AUDD_API_TOKEN in the environment — the SDK reads it on construction:

putenv('AUDD_API_TOKEN=your-token');
$audd = AudD::fromEnvironment();

AudD::fromEnvironment() is the explicit factory; plain new AudD() does the same env-var lookup but reads less obviously at the call site.

For long-running services that rotate tokens (from a secrets manager, Vault, AWS Parameter Store), call $audd->setApiToken($newToken). Subsequent requests use the new value.

What you get back

By default recognize() returns the core tags plus AudD's universal song link — no metadata-block opt-in needed:

use AudD\AudD;
use AudD\StreamingProvider;

$audd = new AudD();
$result = $audd->recognize('https://audd.tech/example.mp3');
if ($result === null) {
    exit("no match\n");
}

// Core tags
echo $result->artist, '', $result->title, "\n";
echo $result->album, ' / ', $result->release_date, ' / ', $result->label, "\n";

// AudD's universal song page (works in any browser, links into all providers)
echo $result->song_link, "\n";

// Helpers — driven off song_link, work without any return_metadata opt-in
echo $result->thumbnailUrl(), "\n";                                   // cover-art URL, or null
echo $result->streamingUrl(StreamingProvider::SPOTIFY), "\n";         // direct or lis.tn redirect
print_r($result->streamingUrls());                                    // ["spotify" => "...", ...]

If you need provider-specific metadata blocks, opt in per call. Request only what you need — each provider you ask for adds latency:

$result = $audd->recognize(
    'https://audd.tech/example.mp3',
    return_metadata: ['apple_music', 'spotify'],
);
echo $result->apple_music->url, "\n";  // direct Apple Music link
echo $result->spotify->uri, "\n";      // spotify:track:...
echo $result->previewUrl(), "\n";      // first preview across requested providers, or null

Valid return_metadata values: apple_music, spotify, deezer, napster, musicbrainz. The corresponding properties ($result->apple_music, $result->spotify, …) are null when not requested.

EnterpriseMatch (returned by recognizeEnterprise) carries the same core tags plus score, isrc, upc. It also tells you where the song plays in the file: start_seconds and end_seconds are the match's position in your file, in seconds. These are precise because recognizeEnterprise requests accurate offsets by default (pass accurateOffsets: false to turn that off). Behind them, start_offset and end_offset are the raw fragment-relative offsets in milliseconds. Access to isrc, upc, and score requires a Startup plan or higher — contact us for enterprise features.

Reading additional metadata

Every typed model exposes extras carrying any fields the SDK doesn't surface as a typed property. This is the supported way to read additional fields the SDK doesn't surface as typed properties:

$result = $audd->recognize('https://example.mp3', return_metadata: ['apple_music']);

// Top-level extras
$genre = $result->extras['genre'] ?? null;

// Nested extras inside a typed metadata block
$artwork = $result->apple_music->extras['artwork'] ?? null;

Magic property access falls through to extras too — $result->genre returns the same value as $result->extras['genre']. Per-account custom fields and beta API responses surface here.

For sending arbitrary form fields the typed parameters don't cover, pass extra_parameters:

$result = $audd->recognize(
    '/tmp/snippet.wav',
    return_metadata: ['apple_music'],
    extra_parameters: ['my_custom_flag' => '1'],
);

Typed parameters win on collision.

Errors

Every server-side error becomes a typed exception. The hierarchy lets you handle whole families with one catch:

AudDException                          (base)
├── AudDConnectionException             network / TLS / timeout
├── AudDSerializationException          malformed JSON
├── AudDConfigurationException          missing or empty token
└── AudDApiException                    status=error from server
    ├── AudDAuthenticationException     900 / 901 / 903
    ├── AudDQuotaException              902
    ├── AudDSubscriptionException       904 / 905
    │   └── AudDCustomCatalogAccessException  904 from customCatalog
    ├── AudDInvalidRequestException     50 / 51 / 600 / 601 / 602 / 700–702 / 906
    ├── AudDInvalidAudioException       300 / 400 / 500
    ├── AudDStreamLimitException        610
    ├── AudDRateLimitException          611
    ├── AudDNotReleasedException        907
    ├── AudDBlockedException            19 / 31337
    ├── AudDNeedsUpdateException        20
    └── AudDServerException              100 / 1000 / unknown

Idiomatic catch:

use AudD\AudD;
use AudD\Errors\AudDApiException;
use AudD\Errors\AudDAuthenticationException;
use AudD\Errors\AudDInvalidAudioException;

try {
    $result = (new AudD())->recognize('https://example.mp3');
} catch (AudDAuthenticationException $e) {
    exit("check your token: [#{$e->errorCode}] {$e->apiMessage}\n");
} catch (AudDInvalidAudioException $e) {
    echo "audio rejected: {$e->apiMessage}\n";
} catch (AudDApiException $e) {
    // catch-all for anything the server reported
    echo "AudD #{$e->errorCode}: {$e->apiMessage} (request_id={$e->requestId})\n";
}

match works equally well for typed dispatch on the exception class:

catch (AudDApiException $e) {
    $action = match (true) {
        $e instanceof AudDAuthenticationException => 'reload-token',
        $e instanceof AudDRateLimitException      => 'back-off',
        $e instanceof AudDInvalidAudioException   => 'skip',
        default                                   => 'log-and-rethrow',
    };
}

Every AudDApiException carries errorCode, apiMessage, httpStatus, requestId, requestedParams, requestMethod, brandedMessage, and rawResponse — enough to log a full incident or open a support ticket.

Logging (PSR-3)

The client accepts an optional Psr\Log\LoggerInterface. Pass any PSR-3 implementation — Monolog, Symfony's logger, Laravel's Log channel, or a custom one — and the SDK routes diagnostics through it. The default is NullLogger (silent), and the api_token is never written to a record.

use AudD\AudD;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;

$logger = new Logger('audd');
$logger->pushHandler(new StreamHandler('php://stderr', Logger::DEBUG));

$audd = new AudD(apiToken: 'your-token', logger: $logger);

Records emitted today: debug for onEvent hook failures (with the exception in context), warning for server-side deprecated-parameter notices (server code 51).

Configuration

use AudD\AudD;
use GuzzleHttp\Client;

$audd = new AudD(
    apiToken: 'your-token',
    maxRetries: 3,                                        // per-call retry budget
    backoffFactor: 0.5,                                   // initial backoff seconds (jittered)
    httpClient: new Client(['proxy' => 'http://corp:8080']),
    onEvent: fn ($e) => error_log((string) $e->method),
    logger: $logger,
);

Custom HTTP client. httpClient accepts any Psr\Http\Client\ClientInterface. Inject a configured Guzzle client, a Symfony PSR-18 adapter, or your own transport to add proxies, mTLS, custom CA bundles, or shared connection pools.

If the client you inject is a Guzzle client, the SDK passes its timeouts as per-request options, so the timeout: argument and the built-in enterprise defaults still apply. Any other PSR-18 client has no channel for per-request timeouts — configure connect/read timeouts on that client directly, and for long enterprise uploads give it a generous read timeout, up to an hour, when you build it.

Retries. Calls are classified by cost and retried accordingly:

Class Endpoints Retried on
RECOGNITION recognize, recognizeEnterprise, advanced->* failures to establish the connection, and 5xx responses
READ streams->list, streams->getCallbackUrl, longpoll any network error, plus 408, 429, and 5xx
MUTATING streams->setCallbackUrl, streams->add, streams->delete failures to establish the connection — never 5xx (the change may already have been applied)
NONE customCatalog->add never retried — the upload is metered, and re-sending it could double-bill

RECOGNITION will not double-bill your account: any failure after the connection is up and the upload is in flight is surfaced rather than retried, because the server may already be processing the audio.

Inspection. Pass an onEvent closure to receive an AudDEvent for every request / response / exception — useful for metrics, distributed tracing, or attaching requestId to your application logs. Events never carry the api_token or request bytes; exceptions raised from the hook are swallowed and routed through the PSR-3 logger at debug level so observability can't break the request path.

use AudD\AudDEvent;
use AudD\AudDEventKind;

$audd = new AudD(
    apiToken: 'your-token',
    onEvent: function (AudDEvent $e): void {
        if ($e->kind === AudDEventKind::Response) {
            error_log("audd {$e->method} -> {$e->httpStatus} ({$e->elapsedMs}ms)");
        }
    },
);

Timeouts. Defaults are 30s connect / 60s read for standard endpoints, and 30s connect / 1 hour read for the enterprise endpoint (which can legitimately process multi-hour files). Override per call with timeout: (seconds).

Streams

Real-time recognition off radio streams, broadcast feeds, and any other long-running URL. Configure once, then either receive callbacks on your server or poll for events.

$audd->streams()->setCallbackUrl('https://your.server/audd-callback');
$audd->streams()->add('https://your.stream.url/listen.m3u8', radioId: 42);

foreach ($audd->streams()->list() as $stream) {
    echo $stream->radio_id, ' ', $stream->url, ' ', ($stream->stream_running ? 'on' : 'off'), "\n";
}

Inside your webhook handler, parse the POST body into a typed result. handleCallback() accepts a PSR-7 ServerRequestInterface, raw JSON bytes, or an already-decoded array — pick whichever your framework gives you:

use AudD\Streams;

// PSR-7 request from your framework:
$result = Streams::handleCallback($request);

// or raw bytes:
$result = Streams::handleCallback(file_get_contents('php://input'));

if ($result->isMatch()) {
    $m = $result->match;
    echo $m->song->artist, '', $m->song->title, "\n";
    foreach ($m->alternatives as $alt) {
        echo "  alt: ", $alt->artist, '', $alt->title, "\n";
    }
} elseif ($result->isNotification()) {
    echo 'notification: ', $result->notification->notification_message, "\n";
}

Use Streams::parseCallback($array) if you already have the decoded JSON. Both methods are static.

add() accepts direct stream URLs (DASH, Icecast, HLS, m3u/m3u8) and the shortcuts twitch:<channel>, youtube:<video_id>, youtube-ch:<channel_id>.

Receiving events without a callback URL (longpoll)

If you can't expose a public callback URL, longpoll instead. AudD still requires a callback URL to be configured for the account (https://audd.tech/empty/ works as a no-op receiver), and the SDK preflights this for you — pass skipCallbackCheck: true to skip if you've already verified.

use AudD\Models\StreamCallbackMatch;
use AudD\Models\StreamCallbackNotification;

$radioId = 1; // any integer you choose — your handle for this stream

$poll = $audd->streams()->longpoll(radioId: $radioId, timeout: 30);
$poll->onMatch(function (StreamCallbackMatch $m): void {
    echo $m->song->artist, '', $m->song->title, "\n";
});
$poll->onNotification(function (StreamCallbackNotification $n): void {
    echo 'notification: ', $n->notification_message, "\n";
});
$poll->onError(function (\Throwable $e) use ($poll): void {
    fwrite(STDERR, $e->getMessage() . "\n");
    $poll->close();
});
$poll->run();  // blocks until close() or a terminal error

Keepalive responses ({"timeout":"no events before timeout"}) are silently absorbed — your onMatch/onNotification only fire on real events.

deriveLongpollCategory is a local computation: MD5(MD5(api_token) + radio_id) truncated to 9 hex chars. The category alone is sufficient to subscribe — the api_token is never sent over the wire for longpolls.

Tokenless consumers

For browser widgets, embedded extensions, or any context where shipping the api_token would leak it: derive the category server-side, ship only the category to the consumer, and have the consumer use LongpollConsumer — same callback API, no api_token required:

use AudD\LongpollConsumer;

// $category was derived on your server and shared with this process.
$consumer = new LongpollConsumer(category: 'abc123def');
$poll = $consumer->iterate(timeout: 30);
$poll->onMatch(fn ($m) => print_r($m));
$poll->onError(fn ($e) => fwrite(STDERR, $e->getMessage()));
$poll->run();

Custom catalog (advanced)

The custom-catalog endpoint is NOT how you submit audio for music recognition. For recognition, use recognize() (or recognizeEnterprise() for longer audio files). The custom-catalog endpoint adds songs to your private fingerprint database so future recognize() calls on your account can identify your own tracks. Requires special access — contact api@audd.io.

$audd->customCatalog()->add(audioId: 42, source: 'https://my.song.mp3');

License

MIT — see LICENSE.

Support

audd/audd 适用场景与选型建议

audd/audd 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 19 次下载、GitHub Stars 达 1, 最近一次更新时间为 2026 年 05 月 06 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「audio」 「music」 「recognition」 「fingerprinting」 「shazam」 「audd」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 audd/audd 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 audd/audd 我们能提供哪些服务?
定制开发 / 二次开发

基于 audd/audd 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 19
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 1
  • 点击次数: 42
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-05-06