Show soundtrack covers and edit alternative names (#49)
Build / test (push) Successful in 1m43s
Build / build (push) Successful in 1m14s

Covers: statistics cards show the soundtrack's cover on the left, and the
statistics popup shows it large. Until it arrives, and without a cover, the
soundtrack's initials stand in. Covers load only for cards on or near the
screen, three at a time, and are cached in covers/ next to the settings
(CoverCache), downloaded again only when cover_modified changes. The index and
file handling are shared with CharacterCache in a new FileCache.

Alternative names: the popup shows them and has an editor (add, remove, save)
that sends PUT /soundtracks/{uuid}/alternative-names. The statistics search and
the Search popup find soundtracks by them; Search shows the matching name after
the real one. Search now loads /soundtracks, falling back to the plain list.

The API types are regenerated from the server's spec, with a SoundtrackCover
theme style in every theme.

Closes #49

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
SansanandClaude Opus 5.5 committed 2026-10-14 22:06:30 +02:00
1 parent a735c515bf
commit ef6ac8f1fd
37 files changed
+8029 -1763

No files matched your search

@@ -5,6 +5,10 @@ extends RefCounted
## The [code]SoundtrackWithSongList[/code] schema of the MusicServer API.
var added: String
## Other names the soundtrack is known by
var alternative_names: Array[String]
## When the cover last changed, in Unix seconds. Missing when there is no cover.
var cover_modified: int
var last_played: String
var name: String
var songs: Array[Song]
@@ -16,6 +20,8 @@ var uuid: String
static func from_dict(data: Dictionary) -> SoundtrackWithSongList:
var result: SoundtrackWithSongList = SoundtrackWithSongList.new()
result.added = ApiJson.get_string(data, "added", "")
result.alternative_names.assign(ApiJson.get_array(data, "alternative_names"))
result.cover_modified = ApiJson.get_int(data, "cover_modified", 0)
result.last_played = ApiJson.get_string(data, "last_played", "")
result.name = ApiJson.get_string(data, "name", "")
for item: Variant in ApiJson.get_array(data, "songs"):
@@ -30,6 +36,8 @@ static func from_dict(data: Dictionary) -> SoundtrackWithSongList:
func to_dict() -> Dictionary:
return {
"added": added,
"alternative_names": alternative_names,
"cover_modified": cover_modified,
"last_played": last_played,
"name": name,
"songs": songs.map(func(item: Song) -> Dictionary: return item.to_dict()),