feat(library): add hand-ordered backlog with next-up picker source
Build, Test & Push Core Service / quality-check (push) Successful in 1m25s
Build, Test & Push Core Service / docker (push) Successful in 1m29s

This commit is contained in:
lobo committed 2026-10-06 09:06:51 +02:00
1 parent 7b538b7f30
commit 7747bc7b8b
22 files changed
+754 -20

No files matched your search

+13 -3
View File
@@ -21,13 +21,20 @@ To Be Played is split into three repositories:
can be taken over; its own finish then changes nothing.
- **Backlog**: status (`BACKLOG`, `PLAYING`, `FINISHED`, `COMPLETED` = 100 %, `DROPPED`), rating 1–5, notes, priority,
filters and sorting. "Pile of Shame" summary with a random suggestion of what to play.
- **Picker**: up to 10 backlog games for a time budget (IGDB normal time to beat) and a mood, mixed from three
- **Picker**: up to 10 backlog games for a time budget (IGDB normal time to beat) and a mood, mixed from four
sources, one per slot in turn: a game sharing moods with a random one of the user's 5 most played games (the
most shared moods first), the game waiting longest (oldest `firstSeenAt`), and a random one (HIGH priority first,
then MEDIUM, then the rest). A source with nothing left gives its slot to a random game; no game appears twice.
then MEDIUM, then the rest), and a random one of the first 5 games of the user's own backlog order that pass the
budget, mood and exclude filters (`NEXT_UP`). A source with nothing left gives its slot to a random game; no game appears twice.
Every game carries a `reason` (`source`, plus the anchor game for `SIMILAR` or `firstSeenAt` for `LONGEST`).
Games already shown can be excluded for a reroll. Moods `STORY`, `COOP`,
`CHILL`, `ACTION`, `SCARY` come from IGDB genres, themes and game modes; `SPEEDRUN` from speedrun.com.
- **Backlog order**: every listed `BACKLOG` game (not removed, not hidden) has a row in `backlog_order (user_id,
appid, position)`, kept by a database trigger on `user_game` like the status history: a game entering the backlog
goes to the bottom, a game leaving it loses its row. V34 seeded the first order (priority HIGH, MEDIUM, rest, then
date added). `PUT /api/library/backlog` replaces the whole order under a row lock; ids not in the backlog are
ignored and games not sent go after the sent ones. A merge keeps the better position of the two games. Coming
games are in the list; `released=true` leaves them out.
- **Coming games**: a game with a release date after today (Berlin date) or Steam "coming soon", and no playtime or
achievement of any user, is not picked and not on the pile (live count, new snapshots, pile stats, time to beat
sum). It keeps its status and shows `coming` / `comingOn`. Date: IGDB `first_release_date`, else Steam's store
@@ -37,9 +44,10 @@ To Be Played is split into three repositories:
```mermaid
flowchart LR
A["anchor: random of top 5 played"] --> S[similar]
S --> M["PickMix: rotate, dedupe, random fallback"]
S --> M["PickMix: rotate 4 sources, dedupe, random fallback"]
L[longest waiting] --> M
R[random] --> M
N["next up: random of top 5 in order"] --> M
M --> O["games + reason"]
```
- **Speedrun games**: a backlog game counts as a speedrun game when speedrun.com has a game with the same name (and
@@ -186,6 +194,8 @@ ArchUnit role rules (`RoleAnnotationRules`) and `src/test/resources/contract/aut
| `GET`, `DELETE` | `/api/library/imports/{id}/undo-check` (`{games, changed}`), `/api/library/imports/{id}/games` (undo; `204`, `409 IMPORT_NOT_SAVED`) | JWT |
| `POST` | `/api/library/sync` | JWT |
| `POST` | `/api/library/broken-images` (`{appid, kind}`, kind `COVER` or `ACHIEVEMENT_ICONS`; `204`) | JWT |
| `GET` | `/api/library/backlog?limit=&released=` (`{games}` in the user's order; `limit` 1-20, none = all) | JWT |
| `PUT` | `/api/library/backlog` (`{appids}` = the whole order; `204`) | JWT |
| `GET` | `/api/library/picker?maxMinutes=&mood=&count=&exclude=` (each game has a `reason` object) | JWT |
| `GET` | `/api/library/stats?groups=` (`OVERVIEW`, `PILE`, `TOP_GAMES`, `TASTE`, `HABITS`, `FINISHED`, `TREND`, `PLATFORMS`; none = all) | JWT |
| `GET` | `/api/library/stats/games?key=&arg=&page=` (the games behind a stats number; `400 BAD_ARG`) | JWT |