Commit Graph
11 Commits
Author SHA1 Message Date
SansanandClaude Opus 5.5 f39865fffc Update the game from inside it (#13)
Build / test (push) Successful in 1m53s
Build / build (push) Successful in 1m15s
"Version X is available" in the top bar is now a button that opens an
Update popup:
- Update and restart gets the download link from the server (the redirect
  is not followed, as the Gitea link needs no token), downloads the new game
  next to the running one with a progress bar, checks it (complete, and an
  MZ or ELF file), swaps it in and restarts. The quiz is saved first and
  continues. On Windows the running .exe is renamed to .old, put back if
  the swap fails, and deleted at the next start.
- Download in browser opens the .exe, the Linux package, or the release
  page on other platforms.
- From the editor, an unwritable folder or another platform, only the
  browser download is offered, with the reason.

The message now fits between the tabs and the buttons in every theme; in
Nord and Christmas it ran into Sync. The macOS export preset is removed,
and the unused download_client_* functions are replaced by
fetch_client_download_url.

Closes #13

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 21:23:26 +02:00
SansanandClaude Opus 5.5 06421bb63e Change soundtrack covers from the app and show the cover with the answer
Build / test (push) Successful in 1m50s
Build / build (push) Successful in 1m14s
Cover menu in the soundtrack popup ("Change cover"):
- Choose picture uploads a JPEG, PNG or WebP picture (up to 10 MB).
- Pick by ID uses a game's cover on SteamGridDB or an anime's on AniList.
- Remove cover deletes it, so the server searches for a new one.
- The server's reason is shown when it refuses a change.
After a change the new cover time is fetched, and the popup, the
statistics card and the quiz show the new cover.

Quiz: once the answer is shown, the soundtrack's cover shows to the left of
the titles. Cover and titles are centered together, left and right and up
and down, and each title keeps to two lines. Cover times come from the
soundtrack list, and songs keep their soundtrack's UUID, also in saves.

Requests can now send any body with its content type, and every 2xx answer
counts as success. Cover frames are 2:3 like the server's covers. Small
copies of real covers are used by the tests and screenshots.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 23:32:09 +02:00
SansanandClaude Opus 5.5 a735c515bf Add Short songs and a choice of how random songs are picked (#30, #51)
Build / test (push) Successful in 1m40s
Build / build (push) Successful in 1m28s
Short songs (#30): a quiz settings switch and a Seconds per song row (30 by
default, 10-120 in steps of 5), saved with the quiz settings. Only part of
each quiz song plays, from a random point 10-60% into it, picked once per song
so restart and repeat play the same part. The progress bar, time and seeking
stay within the part, a hidden beginning counts inside it, and its end counts
as the song ending (repeat, stop or go to the next song as set).

Random songs (#51): the low-played switch in Settings is now a Normal / Low
played / Classic choice, stored as Settings.random_mode and not saved.
fetch_random_song(mode) picks /music/rand, /music/rand/low or
/music/rand/classic.

Closes #30
Closes #51

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:21:20 +02:00
Sansan 4a45e8516d Send the play queue ID to the server
Build / test (push) Successful in 45s
Build / build (push) Successful in 1m12s
The server keeps a play queue per client, identified by the X-Queue-Id
header. The client sends the ID it has for the selected server with every
request and keeps the one in each response, which is new after a reset or a
sync. The IDs are saved per server in the [queues] section of settings.cfg,
so the game goes on with the same queue after a restart.

HttpTransport now passes the response headers on to the client.
2026-10-07 21:23:37 +02:00
Sansan b510aa47f2 Split settings into a quiz card and an application Settings popup
Build / test (push) Successful in 44s
Build / build (push) Successful in 1m13s
- A Quiz settings button under the player bar opens a small card above
  it with the quiz settings, including Auto repeat, which moves there
  from under the player bar. The card does not dim the quiz, so it can
  be changed during a song, and grows to fit its rows.
- A Settings button in the top bar opens the application settings:
  server, fullscreen and songs to download ahead. Quick sync is gone,
  as the server no longer has it, and the server list is in Settings.
- Labels use sentence case, and seconds to hide show as whole numbers.
- Debug builds switch to the local server and a low winning score in
  the Settings autoload, before any scene asks the server for anything.
  The DEBUG label moves to the top bar, so it shows on every tab.
- Search moves from the top bar back to the quiz toolbar, as the
  Statistics tab has its own search.
2026-10-07 16:05:13 +02:00
Sansan a21b6e9b22 Add a Statistics tab with a card for every soundtrack
Build / test (push) Successful in 40s
Build / build (push) Successful in 1m16s
Part of #41. The top bar has Quiz and Statistics tabs instead of the
Statistics button. The Statistics tab replaces the quiz below the top bar
(the song keeps playing) and shows every soundtrack as a card with its
plays, last played date and most and least played songs, loaded from the
new GET /api/v1/statistics/soundtracks. The cards can be searched (with
the Search popup's matching, now in SoundtrackSearch), filtered on played
or never played, and sorted A-Z, Z-A, most or least played. Clicking a
card opens a popup with all its songs.

Screenshots of the other states only differ in the top bar, and the
layout baseline is taken again with the new statistics states.
2026-10-06 23:33:00 +02:00
Sansan f7619baf34 Use /api/v1 with token authentication and typed responses
Closes #38, #39 and #40; adds the client side of #41.

- Every request goes to /api/v1. MusicServerClient gets a token with
  Settings.api_password the first time one is needed, reuses it, and on a
  401 gets a new one and sends the request once more. Switching server gets
  a new token too. Callers never see the token.
- Sync is started with POST, as MusicServer 5.0 requires.
- The client has every endpoint in the server's OpenAPI spec, including
  statistics, soundtracks and songs by UUID, version, downloads and tokens.
- tools/generate_api_types.gd generates a typed class per schema in
  server/openapi.json into server/generated/. Song info, statistics and
  versions are passed to callers as these classes. A list the server sends
  as null is an empty list.
- FakeHttpTransport hands out tokens and answers 401 without one, so every
  test goes through the token flow. New tests cover the token handling,
  every endpoint and each generated class.
2026-10-06 22:45:28 +02:00
Sansan a0428e3475 Follow the GDScript style guide in every script
- Types on every parameter, return value and variable, and snake_case names
  (the player name limit is the constant MAX_NAME_LENGTH).
- Spelling fixes: *_triggered, character_selected, separator, caught_errors.
  The server's catched_errors key stays as the server sends it.
- Signals are connected with signal.connect() instead of by name.
- Code is ordered signals, constants, exported vars, vars, @onready vars,
  functions, with @onready on one line and two blank lines between functions.
- Every script starts with a ## description. Template comments, old LOC notes
  and commented-out code are gone, apart from the local play code.
- Lines are at most 100 characters, apart from the local play path.
- The screenshot tour turns off the blinking text cursor so pictures match.
2026-10-06 11:53:26 +02:00
Sansan bad4b3ad8b Indent every script with tabs, like the Godot editor does 2026-10-06 10:28:12 +02:00
Sansan f67dccbf52 Retry failed song downloads instead of getting stuck
MusicServerClient functions now take an optional on_failed callback,
called when a request fails or returns invalid JSON. Before, a failed
request was silently dropped, so a failure while preloading a song left
Playlist.is_song_downloading set forever and no more songs were fetched.

Song fetching retries the step that failed (download, queue or info)
every song_fetch_retry_seconds, so the server and the game keep the same
playlist.
2026-10-06 08:32:56 +02:00
Sansan c985a6ce5b Replace the broken test suite with working GdUnit4 tests
The previous tests never compiled: they used GUT's assert functions,
Godot 3 APIs and reassigned autoload functions, so no test ran.

- 90 tests: unit tests for the playlist, songs, player scoring, time
  formatting, search and the server client, plus integration tests
  that run MainWindow and SyncWindow against a fake server
- Keybind tests check that Alt+<number> and Ctrl+Alt+<number> keep
  following the player rows while players are added and removed
- run_tests.sh finds Godot and runs the suite headless (~10 seconds)
- CI runs the tests before the build job in gitea-build.yaml; the
  separate test.yml used a GitHub action that does not exist
- GdUnit4 is committed instead of ignored, since the project enables
  it, and .gitea/ is no longer ignored
- Exported builds leave out tests/ and addons/gdUnit4/
2026-10-06 08:32:56 +02:00