Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b53c03f
Refactor single-score.html layout for improved responsiveness and tex…
ZayrexDev Sep 17, 2026
237a32b
Reformat code
ZayrexDev Sep 18, 2026
4e15195
Implement caching for beatmap and score JSON responses and refactor s…
ZayrexDev Sep 19, 2026
b3e5480
Implement caching for beatmap and score JSON responses and refactor s…
ZayrexDev Sep 19, 2026
d145082
Add more fields for ScoreFilter
ZayrexDev Sep 19, 2026
82effd8
Add more fields for ScoreFilter
ZayrexDev Sep 19, 2026
63296f0
Implement automatic caching for beatmap and score data with new conso…
ZayrexDev Sep 19, 2026
e49b56b
Refactor multiplayer room and match handling to use updated model cla…
ZayrexDev Sep 20, 2026
56b051a
Refactor controllers to use ImageResponse for rendering and enhance J…
ZayrexDev Sep 20, 2026
067557c
Refactor authorization header handling to use Headers for consistency
ZayrexDev Sep 20, 2026
fad6dde
Update console command documentation for clarity and formatting consi…
ZayrexDev Sep 20, 2026
3ffa23f
Bump version to 1.12.3 in pom.xml
ZayrexDev Sep 20, 2026
3728033
Enhance error handling by adding HTTP status codes to ErrorCode and u…
ZayrexDev Sep 20, 2026
eb5043c
Add hidden class and update rank history handling in score-list.html
ZayrexDev Sep 21, 2026
c982724
Fix score retrieval logic in MultiplayerController and enhance error …
ZayrexDev Sep 22, 2026
07a7e2a
Refactor MissVisualizationData structure and enhance response data fo…
ZayrexDev Sep 22, 2026
e7d53dc
Add win/lose indicators and enhance score display in multiplayer results
ZayrexDev Sep 22, 2026
3079467
Add custom BO handling and enhance multiplayer result indicators
ZayrexDev Sep 23, 2026
9e79818
Update osuModel dependency version to v1.0.9 in pom.xml
ZayrexDev Sep 23, 2026
2963ee4
Enhance multiplayer result indicators and adjust score calculation logic
ZayrexDev Sep 23, 2026
9c2c43b
Enhance MissVisualizationData to include nearby hit events and key fr…
ZayrexDev Sep 23, 2026
5e05709
Update JSON response structure to wrap array results in `data.result`…
ZayrexDev Sep 27, 2026
555ca57
Bump version to 1.12.7; change probeWorkers and WorkerStatus methods …
ZayrexDev Sep 28, 2026
7a747e4
Reduce timeout duration for worker status requests from 10 seconds to…
ZayrexDev Sep 28, 2026
a51794b
Bump osuParser version to v1.6.1
ZayrexDev Sep 28, 2026
7b37943
Add getBeatmapsets endpoint to fetch multiple beatmapsets asynchronou…
ZayrexDev Sep 28, 2026
390edcd
Change beatmapsets endpoint from GET to POST for improved data handling
ZayrexDev Sep 28, 2026
e845821
Bump version to 1.13.0
ZayrexDev Sep 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 23 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ multiplayer info, and rendered osu! images.
It is the backend for [Seira](https://github.com/BotSeira/SeiraCore) bot,
and also provides a standalone API for other clients to consume.

Image endpoints also return JSON when requested with `Accept: application/json`.
See [response formats and supported endpoints](docs/image-responses.md).

## What You Get

- PNG score panels for best and recent scores, beatmap, beatmapset, and so on!
Expand Down Expand Up @@ -100,9 +103,15 @@ curl "http://localhost:8721/bp?u=12345678&n=20" --output best_of_20.png

Base URL: `http://localhost:<OSTELLA_PORT>`

Most JSON endpoints return: `{"success": boolean, "message": string, "data": any}`.
Most JSON endpoints return: `{"success": boolean, "message": string, "data": object}`.
Array results are wrapped as `{"data":{"result":[...]}}` instead of being placed directly in `data`.
Image endpoints return PNG bytes. Replay download returns `video/mp4`.

When `ostella.token` is configured, every request must include
`Authorization: Bearer <ostellaToken>`. Endpoints that need a player's osu! OAuth
credential use the separate `X-Osu-Authorization: Bearer <osuAccessToken>` header;
the player credential must not replace the oStella service header.

### Beatmaps

| Method | Path | Purpose | Params / POST Body | Response |
Expand Down Expand Up @@ -136,22 +145,22 @@ Image endpoints return PNG bytes. Replay download returns `video/mp4`.

### Multiplayer Rooms

| Method | Path | Purpose | Params / POST Body | Response |
|--------|-----------------------------------|----------------------------|-------------------------------|----------|
| GET | `/multiplayer/rooms/current` | Current multiplayer room | Requires Authorization Header | JSON |
| GET | `/multiplayer/rooms/current/item` | Current room playlist item | Requires Authorization Header | JSON |
| Method | Path | Purpose | Params / POST Body | Response |
|--------|-----------------------------------|----------------------------|---------------------------------------------------------|----------|
| GET | `/multiplayer/rooms/current` | Current multiplayer room | Requires `X-Osu-Authorization: Bearer <osuAccessToken>` | JSON |
| GET | `/multiplayer/rooms/current/item` | Current room playlist item | Requires `X-Osu-Authorization: Bearer <osuAccessToken>` | JSON |

### Users

| Method | Path | Purpose | Params / POST Body | Response |
|--------|-------------------------------------|-------------------------------|--------------------------------------------------|----------|
| POST | `/users` | Get multiple user data | POST Body `{"ids":[user ids]}` | JSON |
| GET | `/users/me` | User data | Requires Authorization Header | JSON |
| GET | `/users/me/friends` | Friends list for user | Requires Authorization Header | JSON |
| POST | `/users/leaderboards` | User PP leaderboard image | `{"uids":[user ids]}` | PNG |
| GET | `/users/{userId}/scores/bestof` | Best-of-N scores image | path `userId`, query `n` (count) | PNG |
| GET | `/users/{userId}/scores/recent` | Recent scores image | path `userId`, query `n` (count) | PNG |
| GET | `/users/{userId}/scores/today-best` | Best scores achieved recently | path `userId`, optional query `days` (default 1) | PNG |
| Method | Path | Purpose | Params / POST Body | Response |
|--------|-------------------------------------|-------------------------------|---------------------------------------------------------|----------|
| POST | `/users` | Get multiple user data | POST Body `{"ids":[user ids]}` | JSON |
| GET | `/users/me` | User data | Requires `X-Osu-Authorization: Bearer <osuAccessToken>` | JSON |
| GET | `/users/me/friends` | Friends list for user | Requires `X-Osu-Authorization: Bearer <osuAccessToken>` | JSON |
| POST | `/users/leaderboards` | User PP leaderboard image | `{"uids":[user ids]}` | PNG |
| GET | `/users/{userId}/scores/bestof` | Best-of-N scores image | path `userId`, query `n` (count) | PNG |
| GET | `/users/{userId}/scores/recent` | Recent scores image | path `userId`, query `n` (count) | PNG |
| GET | `/users/{userId}/scores/today-best` | Best scores achieved recently | path `userId`, optional query `days` (default 1) | PNG |

### Replays (enabled when `replayRender.enabled` is true)

Expand Down
53 changes: 35 additions & 18 deletions docs/console.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,42 @@

oStella uses Log4J2 for service output and JLine for interactive input, history, completion, and prompt-safe log redraw. Console text is English to avoid terminal encoding problems.

| Command | Purpose |
| --- | --- |
| `status` | Web, token, HTTP, async, renderer, replay-worker, cache, and uptime health |
| `metrics` | HTTP and asynchronous work counters |
| `token status` | Show osu! API token health |
| `token renew` | Queue an immediate token renewal |
| `replay status` | Probe configured osuRenderer queues |
| `replay job <uuid>` | Find a remote render job |
| `replay delete <uuid> confirm` | Delete a remote render job |
| `cache status` | Show file count and size for every cache area |
| `cache <query/delete/get/fetch> <score/beatmap/beatmapset/replay> <id>` | Operate on local cache and aggregate every configured osuRenderer worker |
| `cache clear <area> confirm` | Clear `beatmaps`, `images`, `replays`, `score-json`, `beatmapsets`, or `all` |
| `config show` | Show effective configuration with credentials redacted |
| `config check` | Validate `config.yml` without applying it |
| `log show` | Show the current Log4J2 root level |
| `log level <level>` | Set `trace`, `debug`, `info`, `warn`, or `error` until restart |
| `system` | Show version, JVM, OS, threads, memory, and uptime |
| `stop confirm` | Gracefully stop the console and every owned service |
| Command | Purpose |
|-------------------------------------------------------------------------|------------------------------------------------------------------------------|
| `status` | Web, token, HTTP, async, renderer, replay-worker, cache, and uptime health |
| `metrics` | HTTP and asynchronous work counters |
| `token status` | Show osu! API token health |
| `token renew` | Queue an immediate token renewal |
| `replay status` | Probe configured osuRenderer queues |
| `replay job <uuid>` | Find a remote render job |
| `replay delete <uuid> confirm` | Delete a remote render job |
| `cache status` | Show file count and size for every cache area |
| `cache <query/delete/get/fetch> <score/beatmap/beatmapset/replay> <id>` | Operate on local cache and aggregate every configured osuRenderer worker |
| `cache clear <area> confirm` | Clear `beatmaps`, `images`, `replays`, `score-json`, `beatmapsets`, or `all` |
| `config show` | Show effective configuration with credentials redacted |
| `config check` | Validate `config.yml` without applying it |
| `log show` | Show the current Log4J2 root level |
| `log level <level>` | Set `trace`, `debug`, `info`, `warn`, or `error` until restart |
| `system` | Show version, JVM, OS, threads, memory, and uptime |
| `stop confirm` | Gracefully stop the console and every owned service |

Use `help [command]` and Tab completion in the running console. Bulk cache clearing and service shutdown require the literal `confirm` argument; unified single-ID cache deletion uses the four-part command directly.

For the unified cache command, `query` returns the chain presence matrix, `get` adds path/size/time metadata, `delete` removes all reachable copies, and `fetch` downloads into oStella before pushing beatmapsets or replays to every worker. Score and beatmap caches are oStella-only and therefore appear as `N/A` on workers.

## Automatic caching

`autocache <beatmapset/beatmapset-json/beatmap/beatmap-json> <on/off>` toggles each type independently. All four default to off, and settings last until restart. For example:

```text
autocache beatmapset-json on
autocache beatmap-json on
autocache beatmap on
autocache beatmapset off
```

The worker discovers distinct user IDs from currently cached score JSON files and fetches their best 200 osu!standard scores in two pages of at most 100. It prefetches missing `.osz` archives, full beatmapset JSON, `.osu` files, or full beatmap JSON according to the enabled types. Shared maps/sets reuse the existing local cache; it does not push files to rendering workers.

Work starts only after five seconds without foreground API activity, including queued requests and replay requests. One page or cache item is processed per second, subject to the existing API rate limit. Direct API calls, including token renewal, also count as activity. Foreground calls never wait for the background download; an already-started download may finish, but subsequent requests pause until idle again. Turning a type off prevents further downloads of that type; shutdown interrupts the worker.

Cached-score users are rediscovered every five minutes when the current queue drains. Each user's best scores are refreshed at most hourly; missing or failed resources are retried on a later refresh. Changing a toggle restarts discovery. Malformed cached score files are skipped. Downloaded beatmapsets can use substantial disk space; there is no automatic eviction.
65 changes: 65 additions & 0 deletions docs/image-responses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Image and JSON responses

Image endpoints support an explicit JSON opt-in using `Accept: application/json`.
URLs, query parameters, authentication, filtering, and ordering remain the same.

```sh
curl -H 'Accept: application/json' 'http://localhost:8080/users/123'
curl -H 'Accept: application/json' 'http://localhost:8080/beatmapsets/456'
curl -H 'Accept: application/json' 'http://localhost:8080/users/123/scores/bestof?n=5'
```

Use your configured server port. JSON responses use the existing envelope:

```json
{"success":true,"message":"Success","data":{}}
```

When an endpoint returns an array, it is exposed as `data.result`, for example
`{"success":true,"message":"Success","data":{"result":[]}}`.

The response has `Content-Type: application/json`. Rendered images have
`Content-Type: image/png`; background downloads retain their image format.
Negotiated responses include `Vary: Accept` so caches distinguish representations.

An absent `Accept`, `*/*`, or an image media type keeps the image response.
Media types are case-insensitive; parameters and comma-separated media ranges are
supported. An explicit `application/json` with a valid positive `q` value opts in,
even when images are also listed. `application/json;q=0` does not opt in.
Wildcard media types do not opt in to JSON.

## Supported endpoints and `data`

| Endpoint | JSON data |
|--------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------|
| `GET /users/{userId}` | User object; no best-score request is made |
| `GET /beatmapsets/{beatmapsetId}` | Beatmapset object with difficulties ordered by stars |
| `GET /beatmaps/{beatmapId}` | Beatmap object with beatmapset metadata |
| `GET /scores/{scoreId}` | Score object |
| `GET /users/{userId}/scores/recent` | `user`, filtered `scores`, `type`, `filters`, and original `positions` |
| `GET /users/{userId}/scores/bestof` | Same score-list structure |
| `GET /users/{userId}/scores/today-best` | Same structure, plus `title` describing the time window |
| `POST /users/leaderboards` | `result` array containing users sorted by osu! pp |
| `POST /beatmaps/{beatmapId}/leaderboards` | `beatmap` and sorted `placements` |
| `GET /beatmaps/{beatmapId}/analysis` | `beatmap`, `diff`, `mods`, `performance`, and `patterns` |
| `GET /scores/{scoreId}/analysis` | Score, difficulty, hit/miss positions, timing errors, unstable rate, performance graphs, PP+, and simulation results |
| `GET /scores/{scoreId}/misses/{missIndex}/visualize` | Miss index, beatmap ID, object index, time, type, nearby keyframes, difficulty, and PP loss estimates |
| `GET /multiplayer/rooms/{roomId}/playlist/{playlistItemId}/result` | Multiplayer result data including players, teams, and series scores |
| `POST /templates/{templateName}/render` | Resolved template variables, including `@score`, `@user`, `@beatmap`, and `@beatmapset` references |
| `GET /beatmapsets/{beatmapsetId}/background` | `beatmapset_id` and cover `url`; no image download |
| `GET /beatmaps/{beatmapId}/background` | `beatmapId`, `beatmapsetId`, and background `fileName`; no archive extraction |

osu! model fields retain their existing JSON names. New composite data uses the
field names listed above; optional null fields may be omitted. The nested
beatmapset in a beatmap response omits `beatmaps` to avoid circular references.
Score analysis excludes the parser's complete replay/beatmap objects.

JSON requests skip HTML, screenshots, and the browser render queue. Basic score,
beatmap, and map leaderboard JSON requests also skip image-only difficulty
calculations. Analysis endpoints still perform the analysis and require the same
replay/PP+ dependencies as their image counterparts. The basic beatmap JSON is
the API model; the `mod` parameter affects rendered difficulty, while the analysis
endpoint returns calculated modded data.

Existing JSON-only endpoints, beatmapset archive downloads, and replay video
endpoints keep their existing response formats.
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.github.BotSeira</groupId>
<artifactId>oStella</artifactId>
<version>1.12.0</version>
<version>1.13.0</version>

<properties>
<maven.compiler.source>25</maven.compiler.source>
Expand Down Expand Up @@ -112,13 +112,13 @@
<dependency>
<groupId>com.github.BotSeira</groupId>
<artifactId>osuModel</artifactId>
<version>v1.0.3</version>
<version>v1.0.9</version>
</dependency>

<dependency>
<groupId>com.github.BotSeira</groupId>
<artifactId>osuParser</artifactId>
<version>v1.5.6</version>
<version>v1.6.4</version>
</dependency>

<!-- Source: https://mvnrepository.com/artifact/org.tukaani/xz -->
Expand Down
1 change: 1 addition & 0 deletions src/main/java/xyz/zcraft/ostella/config/OstellaConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package xyz.zcraft.ostella.config;

public record OstellaConfig(
String token,
int requestPerSecond,
int replayRequestIntervalMillis,
int replayMaxConcurrent,
Expand Down
Loading
Loading