|
| 1 | +# audd-java — Official Java SDK for AudD |
| 2 | + |
| 3 | +The official Java SDK for the [AudD music recognition API](https://audd.io). |
| 4 | +Java 11+, OkHttp transport, sync `AudD` + async `AsyncAudD` (CompletableFuture-based), |
| 5 | +forward-compatible types, cost-aware retries. |
| 6 | + |
| 7 | +## Hello, AudD |
| 8 | + |
| 9 | +```java |
| 10 | +import io.audd.AudD; |
| 11 | + |
| 12 | +public class Hello { |
| 13 | + public static void main(String[] args) throws Exception { |
| 14 | + try (AudD audd = AudD.builder().apiToken("test").build()) { |
| 15 | + var result = audd.recognize("https://audd.tech/example.mp3"); |
| 16 | + if (result != null) { |
| 17 | + System.out.println(result.artist() + " — " + result.title()); |
| 18 | + } |
| 19 | + } |
| 20 | + } |
| 21 | +} |
| 22 | +``` |
| 23 | + |
| 24 | +The public `"test"` token is capped at 10 requests; get a real one at |
| 25 | +[dashboard.audd.io](https://dashboard.audd.io/). |
| 26 | + |
| 27 | +## Install |
| 28 | + |
| 29 | +Maven: |
| 30 | + |
| 31 | +```xml |
| 32 | +<dependency> |
| 33 | + <groupId>io.audd</groupId> |
| 34 | + <artifactId>audd</artifactId> |
| 35 | + <version>1.4.0</version> |
| 36 | +</dependency> |
| 37 | +``` |
| 38 | + |
| 39 | +Gradle: |
| 40 | + |
| 41 | +```kotlin |
| 42 | +implementation("io.audd:audd:1.4.0") |
| 43 | +``` |
| 44 | + |
| 45 | +Java 11+. |
| 46 | + |
| 47 | +## Capabilities |
| 48 | + |
| 49 | +| Capability | SDK | |
| 50 | +|---|---| |
| 51 | +| Recognize a song (URL / file / bytes / InputStream) | `audd.recognize(source, opts)` | |
| 52 | +| Recognize a long file (enterprise) | `audd.recognizeEnterprise(source, opts)` | |
| 53 | +| Configure callback URL | `audd.streams().setCallbackUrl(url, opts)` | |
| 54 | +| Add a stream | `audd.streams().add(req)` | |
| 55 | +| List/manage streams | `audd.streams().list() / delete(...) / setUrl(...)` | |
| 56 | +| Streams (callback POST or longpoll subscription) | `audd.streams().longpoll(...)` / `Streams.parseCallback(body)` | |
| 57 | +| Tokenless longpoll (browser/widget) | `new LongpollConsumer(category)` | |
| 58 | +| Derive a longpoll category locally | `Streams.deriveLongpollCategory(token, radioId)` | |
| 59 | + |
| 60 | +Sample mains will be added under [`examples/`](./examples). For now, treat the |
| 61 | +[`Hello, AudD`](#hello-audd) snippet above as the canonical starting point. |
| 62 | + |
| 63 | +## Configuration |
| 64 | + |
| 65 | +```java |
| 66 | +AudD audd = AudD.builder() |
| 67 | + .apiToken("your-token") |
| 68 | + .maxRetries(5) |
| 69 | + .backoffFactorMs(1000) |
| 70 | + .standardTimeoutSeconds(120) |
| 71 | + .enterpriseTimeoutSeconds(7200) |
| 72 | + .httpClient(new OkHttpClient.Builder() /* corporate proxy etc. */ .build()) |
| 73 | + .onDeprecation(msg -> log.warn("audd-deprecation: {}", msg)) |
| 74 | + .build(); |
| 75 | +``` |
| 76 | + |
| 77 | +`AudD` and `AsyncAudD` are safe for concurrent use across threads. Token |
| 78 | +rotation via `setApiToken(...)` uses an `AtomicReference`; in-flight requests |
| 79 | +continue with the prior token, subsequent ones use the new one. |
| 80 | + |
| 81 | +Retries are cost-aware: |
| 82 | + |
| 83 | +- **READ** endpoints (`streams.list`, `streams.getCallbackUrl`): retry on |
| 84 | + 408/429/5xx and connection errors. |
| 85 | +- **RECOGNITION** endpoints (`recognize`, `recognizeEnterprise`, |
| 86 | + `advanced.findLyrics`, `advanced.rawRequest`): retry on pre-upload |
| 87 | + connection failures and on 5xx. Do **not** retry on read-timeout |
| 88 | + after upload completed (cost protection). |
| 89 | +- **MUTATING** endpoints (`streams.add`, `streams.delete`, etc.): retry only |
| 90 | + on pre-upload connection failures. |
| 91 | + |
| 92 | +## Error handling |
| 93 | + |
| 94 | +Sealed hierarchy under `AudDException`: |
| 95 | + |
| 96 | +``` |
| 97 | +AudDException |
| 98 | +├── AudDApiError |
| 99 | +│ ├── AudDAuthenticationError (900, 901, 903) |
| 100 | +│ ├── AudDQuotaError (902) |
| 101 | +│ ├── AudDSubscriptionError (904, 905) |
| 102 | +│ │ └── AudDCustomCatalogAccessError |
| 103 | +│ ├── AudDInvalidRequestError (50, 51, 600/601/602, 700/701/702, 906) |
| 104 | +│ ├── AudDInvalidAudioError (300, 400, 500) |
| 105 | +│ ├── AudDRateLimitError (611) |
| 106 | +│ ├── AudDStreamLimitError (610) |
| 107 | +│ ├── AudDNotReleasedError (907) |
| 108 | +│ ├── AudDBlockedError (19, 31337) |
| 109 | +│ ├── AudDNeedsUpdateError (20) |
| 110 | +│ └── AudDServerError (100, 1000, generic 5xx) |
| 111 | +├── AudDConnectionError # network / TLS / timeout |
| 112 | +└── AudDSerializationError # 2xx response with malformed JSON |
| 113 | +``` |
| 114 | + |
| 115 | +## Custom catalog (advanced — read first) |
| 116 | + |
| 117 | +> **This is NOT how you submit audio for music recognition.** For that, use |
| 118 | +> `audd.recognize(...)` (or `audd.recognizeEnterprise(...)` for files longer |
| 119 | +> than 25 seconds). The custom-catalog endpoint manipulates your **private |
| 120 | +> fingerprint catalog** so AudD's recognition can later identify *your own* |
| 121 | +> tracks for *your account only*. Requires special access — contact |
| 122 | +> api@audd.io if you need it enabled. |
| 123 | +
|
| 124 | +```java |
| 125 | +audd.customCatalog().add(146, Path.of("track.mp3")); |
| 126 | +``` |
| 127 | + |
| 128 | +## Advanced |
| 129 | + |
| 130 | +`audd.advanced().findLyrics("query")` and `audd.advanced().rawRequest(method, params)`. |
| 131 | +`rawRequest` is the escape hatch for any AudD endpoint not yet wrapped here. |
| 132 | + |
| 133 | +## Contributing / security / license |
| 134 | + |
| 135 | +- Issues: <https://github.com/AudDMusic/audd-java/issues> |
| 136 | +- Security: see [SECURITY.md](./SECURITY.md) |
| 137 | +- License: MIT (see [LICENSE](./LICENSE)) |
0 commit comments