Skip to content

Commit 0e1f350

Browse files
author
AudD
committed
Initial release v1.4.0
0 parents  commit 0e1f350

81 files changed

Lines changed: 5822 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
test:
14+
name: tests (JDK ${{ matrix.java }})
15+
runs-on: ubuntu-latest
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
java: ['11', '17', '21']
20+
steps:
21+
- uses: actions/checkout@v4
22+
- uses: actions/setup-java@v4
23+
with:
24+
distribution: temurin
25+
java-version: ${{ matrix.java }}
26+
cache: maven
27+
- name: mvn verify
28+
run: mvn -B verify
29+
- name: jar --describe-module (JPMS smoke)
30+
if: matrix.java == '21'
31+
run: |
32+
JAR=$(ls target/audd-*.jar 2>/dev/null | grep -v sources | grep -v javadoc | head -1)
33+
if [ -n "$JAR" ]; then jar --describe-module --file="$JAR"; fi

.github/workflows/contract.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Contract tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
schedule:
9+
- cron: '0 6 * * *'
10+
repository_dispatch:
11+
types: [openapi-updated]
12+
13+
permissions:
14+
contents: read
15+
issues: write
16+
17+
jobs:
18+
contract:
19+
name: validate parser against latest audd-openapi fixtures
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
with:
24+
path: audd-java
25+
- name: Check out audd-openapi
26+
uses: actions/checkout@v4
27+
with:
28+
repository: AudDMusic/audd-openapi
29+
path: audd-openapi
30+
ref: main
31+
- uses: actions/setup-java@v4
32+
with:
33+
distribution: temurin
34+
java-version: '21'
35+
cache: maven
36+
- name: Run contract tests
37+
working-directory: audd-java
38+
env:
39+
AUDD_OPENAPI_FIXTURES: ${{ github.workspace }}/audd-openapi/fixtures
40+
run: mvn -B test -Dtest='*ContractTest' -DfailIfNoTests=false
41+
- name: Open issue on failure (dispatched runs only)
42+
if: failure() && github.event_name == 'repository_dispatch'
43+
env:
44+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45+
TRIGGER_SHA: ${{ github.event.client_payload.trigger_sha }}
46+
run: |
47+
gh issue create \
48+
--title "Contract drift: audd-openapi spec change broke parser" \
49+
--body "An openapi-updated dispatch (trigger SHA: $TRIGGER_SHA) caused contract tests to fail. Investigate and update parsers." \
50+
--label "contract-drift" || true

.github/workflows/release.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- 'audd-java/v*'
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
publish:
13+
name: Build and publish to Maven Central
14+
runs-on: ubuntu-latest
15+
environment: maven-central
16+
steps:
17+
- uses: actions/checkout@v4
18+
- uses: actions/setup-java@v4
19+
with:
20+
distribution: temurin
21+
java-version: '21'
22+
cache: maven
23+
server-id: central
24+
server-username: MAVEN_CENTRAL_USERNAME
25+
server-password: MAVEN_CENTRAL_PASSWORD
26+
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }}
27+
gpg-passphrase: MAVEN_GPG_PASSPHRASE
28+
- name: Verify build
29+
run: mvn -B verify
30+
- name: Deploy to Maven Central
31+
env:
32+
MAVEN_CENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
33+
MAVEN_CENTRAL_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
34+
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
35+
run: mvn -B -Prelease deploy -DskipTests

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
target/
2+
*.class
3+
*.jar
4+
*.war
5+
*.ear
6+
.idea/
7+
*.iml
8+
.vscode/
9+
.settings/
10+
.classpath
11+
.project
12+
hs_err_pid*
13+
.mvn/wrapper/maven-wrapper.jar

CHANGELOG.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6+
7+
## [1.4.0] - 2026-05-05
8+
9+
Brand polish — every public type now spells the brand name as `AudD`
10+
PascalCase across the entire public surface (clients, error classes,
11+
event, options). Java package paths (`io.audd`) and Maven coordinates
12+
(`io.audd:audd`) stay lowercase as Java conventions require.
13+
14+
### Changed
15+
16+
- Top-level clients renamed to `AudD` and `AsyncAudD`.
17+
- Event class renamed to `AudDEvent`.
18+
- Options class renamed to `AudDOptions`.
19+
- Sealed error hierarchy renamed: `AudDException`, `AudDApiError`,
20+
`AudDAuthenticationError`, `AudDSubscriptionError`,
21+
`AudDCustomCatalogAccessError`, `AudDQuotaError`,
22+
`AudDInvalidRequestError`, `AudDInvalidAudioError`,
23+
`AudDConnectionError`, `AudDSerializationError`,
24+
`AudDRateLimitError`, `AudDStreamLimitError`,
25+
`AudDBlockedError`, `AudDNotReleasedError`,
26+
`AudDNeedsUpdateError`, `AudDServerError`.
27+
28+
### Unchanged
29+
30+
- Java package name `io.audd` (lowercase per JLS convention).
31+
- Maven `groupId` / `artifactId`: `io.audd` / `audd`.
32+
- `AUDD_API_TOKEN` environment variable.
33+
- JPMS module name `io.audd`.
34+
35+
## [1.3.0] - 2026-05-05
36+
37+
Coordinated v1.3.0 stable release across the audd-sdks family. No breaking
38+
changes; the version bump signals API stability across all nine SDKs.
39+
40+
The full v0.3.0 polish — env-var auto-pickup, streaming/preview helpers
41+
with metadata fallback, `onEvent` inspection hook, thread-safe token
42+
rotation, plus per-language work (JPMS module-info, Kotlin `Flow<LongpollEvent>`,
43+
Swift `Sendable` + DocC, .NET AOT/source-gen + `IServiceCollection`, Rust
44+
TLS feature flags + `Serialize`, PHP PSR-3 logger, Python `__repr__` +
45+
`pretty_print()`, Go `slog` example) is now the v1.3.0 baseline.
46+
47+
## [0.3.0] - 2026-05-05
48+
49+
JPMS module support — `module-info.java` ships in the SDK so modular consumers can `requires io.audd;` cleanly without falling back to the automatic-module-from-filename derivation.
50+
51+
### Added
52+
53+
- **`module-info.java`** at `src/main/java/module-info.java`. Module name `io.audd`. Exports the public packages (`io.audd`, `io.audd.streams`, `io.audd.customcatalog`, `io.audd.advanced`, `io.audd.errors`, `io.audd.models`); deliberately does **not** export `io.audd.internal`. Declares `requires transitive` on `com.fasterxml.jackson.databind`, `com.fasterxml.jackson.annotation`, and `okhttp3` since their types appear on the public API surface (`JsonNode` in error classes, `OkHttpClient` / `RequestBody` in option/builder types). `requires java.logging` for the `onEvent` hook's `java.util.logging` fallback path. Maven build target stays at Java 11 — module-info compiles cleanly under `--release 11` since JPMS has been supported since Java 9.
54+
55+
## [0.2.0] - 2026-05-05
56+
57+
DX polish — env-var auto-pickup, streaming/preview helpers, on_event inspection hook, thread-safe token rotation.
58+
59+
### Added
60+
61+
- **`AUDD_API_TOKEN` env-var fallback** (§7.11): `AudD.builder().build()` and `new AudD((String) null)` now resolve the api_token via explicit-arg → `AUDD_API_TOKEN` env var → `IllegalArgumentException` with a hint pointing at <https://dashboard.audd.io>. New static factories `AudD.fromEnvironment()` / `AsyncAudD.fromEnvironment()` make the env-var path explicit.
62+
- **`RecognitionResult.streamingUrl(StreamingProvider)`** (§4.3): direct URL from the metadata block when present (`apple_music.url`, `spotify.external_urls.spotify`, `deezer.link`, `napster.href`), else `lis.tn?<provider>` redirect when `songLink` is on `lis.tn`, else `null`. `StreamingProvider` enum (`SPOTIFY`, `APPLE_MUSIC`, `DEEZER`, `NAPSTER`, `YOUTUBE`); `YOUTUBE` has only the lis.tn path.
63+
- **`RecognitionResult.streamingUrls()`** (§4.3): `Map<StreamingProvider, String>` of every provider with a resolvable URL.
64+
- **`RecognitionResult.previewUrl()`** (§4.3): first non-empty preview URL across `apple_music.previews[0].url``spotify.preview_url``deezer.preview`. Doc-noted caveat about provider TOS.
65+
- Same `streamingUrl` / `streamingUrls` helpers on `EnterpriseMatch` (lis.tn-only — enterprise responses don't carry the per-provider metadata blocks).
66+
- **`AudD.setApiToken(newToken)`** / **`AsyncAudD.setApiToken(newToken)`** (§7.10): thread-safe token rotation via `AtomicReference`. In-flight requests continue with the old token; subsequent requests use the new one. Validates non-empty. `Streams` / `AsyncStreams` now accept a `Supplier<String>` for the api_token so `deriveLongpollCategory()` reflects rotations.
67+
- **`onEvent` inspection hook** on `AudD` / `AsyncAudD` (§7.7a): a `Consumer<AudDEvent>` receiving lifecycle events (`kind` ∈ {`REQUEST`, `RESPONSE`, `EXCEPTION`}, `method`, `url`, `requestId`, `httpStatus`, `elapsedMs`, `errorCode`, `extras`). Off by default. Never carries the `api_token` or request body bytes. Hook exceptions are swallowed at FINE log level via `java.util.logging.Logger("io.audd")` so observability never breaks the request path.
68+
69+
### Internal
70+
71+
- 27 new regression tests (`src/test/java/io/audd/V02PolishTest.java`) covering all four feature areas. Total: 116 tests passing.
72+
73+
## [0.1.0] - 2026-05-04
74+
### Added
75+
- Initial release of the official AudD Java SDK.
76+
- Sync `AudD` and async `AsyncAudD` clients (CompletableFuture-based).
77+
- Tokenless `LongpollConsumer` and `AsyncLongpollConsumer` for browser/widget use cases.
78+
- Sealed exception hierarchy mapping every documented AudD error code.
79+
- Forward-compatible models via Jackson `@JsonAnySetter` / `@JsonAnyGetter`.
80+
- Cost-aware retry policy (READ / RECOGNITION / MUTATING classes).
81+
- HTTP transport injection, configurable timeouts, structured logging hooks.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 AudD
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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))

SECURITY.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Security Policy
2+
3+
## Reporting a Vulnerability
4+
5+
If you discover a security issue in the AudD Java SDK, please report it
6+
privately to **api@audd.io**. Do not open a public GitHub issue for security
7+
problems.
8+
9+
We will acknowledge receipt within 2 business days and aim to provide a fix
10+
or mitigation plan within 14 days.
11+
12+
## Scope
13+
14+
In-scope: vulnerabilities in this SDK's code (`io.audd:audd`).
15+
16+
Out-of-scope: issues in upstream dependencies (file those upstream), issues
17+
in the AudD service itself (file via api@audd.io with subject "AudD service
18+
security report").

0 commit comments

Comments
 (0)