Show soundtrack covers and edit alternative names (#49)
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:
1 parent
a735c515bf
commit
ef6ac8f1fd
37 files changed
+8029
-1763
No files matched your search
@@ -4,6 +4,9 @@ extends PanelContainer
|
||||
## played, and its most and least played songs. Songs never played are not listed, and
|
||||
## the least played are only shown when there are enough played songs to not repeat
|
||||
## any. Clicking it asks for all its songs.
|
||||
##
|
||||
## Its cover shows on the left once [method load_cover] has fetched it; until then, and
|
||||
## when the soundtrack has no cover, the soundtrack's initials stand in for it.
|
||||
|
||||
## Emitted when the card is clicked.
|
||||
signal pressed(soundtrack: SoundtrackWithSongs)
|
||||
@@ -15,6 +18,11 @@ const SONGS_SHOWN: int = 3
|
||||
## The soundtrack the card shows.
|
||||
var soundtrack: SoundtrackWithSongs
|
||||
|
||||
# The cover time asked for, so a cover is only asked for again when it changed
|
||||
var _cover_asked: int = 0
|
||||
|
||||
@onready var cover_picture: TextureRect = %CoverPicture
|
||||
@onready var cover_placeholder: Label = %CoverPlaceholder
|
||||
@onready var name_label: Label = %NameLabel
|
||||
@onready var played_label: Label = %PlayedLabel
|
||||
@onready var last_played_label: Label = %LastPlayedLabel
|
||||
@@ -32,7 +40,14 @@ func _ready() -> void:
|
||||
|
||||
## Shows [param data] on the card.
|
||||
func show_soundtrack(data: SoundtrackWithSongs) -> void:
|
||||
var cover_changed: bool = soundtrack == null \
|
||||
|| soundtrack.soundtrack_uuid != data.soundtrack_uuid \
|
||||
|| soundtrack.cover_modified != data.cover_modified
|
||||
soundtrack = data
|
||||
if cover_changed:
|
||||
show_cover(null)
|
||||
_cover_asked = 0
|
||||
cover_placeholder.text = initials(data.soundtrack_name)
|
||||
name_label.text = data.soundtrack_name
|
||||
tooltip_text = data.soundtrack_name
|
||||
played_label.text = played_text(data.soundtrack_played)
|
||||
@@ -55,6 +70,34 @@ func show_soundtrack(data: SoundtrackWithSongs) -> void:
|
||||
_fill_song_list(least_played_list, least_played)
|
||||
|
||||
|
||||
## Asks [param covers] for the soundtrack's cover, unless it already has, and shows it
|
||||
## when it comes. Soundtracks without a cover keep their initials.
|
||||
func load_cover(covers: CoverCache) -> void:
|
||||
if soundtrack == null || soundtrack.cover_modified <= 0 \
|
||||
|| _cover_asked == soundtrack.cover_modified:
|
||||
return
|
||||
_cover_asked = soundtrack.cover_modified
|
||||
covers.fetch_cover(soundtrack.soundtrack_uuid, soundtrack.cover_modified, show_cover)
|
||||
|
||||
|
||||
## Shows [param cover], or the initials when it is null.
|
||||
func show_cover(cover: Image) -> void:
|
||||
cover_picture.texture = ImageTexture.create_from_image(cover) if cover != null else null
|
||||
cover_placeholder.visible = cover == null
|
||||
|
||||
|
||||
## Up to two initials of [param soundtrack_name], e.g. "FF" for "Final Fantasy VI".
|
||||
static func initials(soundtrack_name: String) -> String:
|
||||
var letters: String = ""
|
||||
for word: String in soundtrack_name.split(" ", false):
|
||||
var first: String = word.left(1)
|
||||
if first.to_upper() != first.to_lower():
|
||||
letters += first.to_upper()
|
||||
if letters.length() == 2:
|
||||
break
|
||||
return letters if !letters.is_empty() else soundtrack_name.left(1)
|
||||
|
||||
|
||||
## "Played 3 times", or "Never played" for [code]0[/code].
|
||||
static func played_text(times_played: int) -> String:
|
||||
if times_played == 0:
|
||||
|
||||
Reference in new issue
Block a user