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>
110 lines
3.5 KiB
GDScript
110 lines
3.5 KiB
GDScript
extends RefCounted
|
|
|
|
class_name SongObject
|
|
|
|
var song_number: int
|
|
var song_title: String
|
|
var soundtrack_title: String
|
|
## The UUID of the song's soundtrack on the server, empty for songs from a music folder.
|
|
var soundtrack_uuid: String = ""
|
|
|
|
## Ids of the players who got a point for this song.
|
|
var players_given_point: Array[String] = []
|
|
|
|
var is_answered: bool = false
|
|
var has_played: bool = false
|
|
var is_playing: bool = false
|
|
## The song's file: a download in [method Playlist.songs_folder], or a song in the music
|
|
## folder. Read the song with [method Playlist.get_song].
|
|
var song_path: String = ""
|
|
## The song itself, only when it could not be saved to a file.
|
|
var song: AudioStream
|
|
## Where the part played with Short songs starts, in seconds, or -1 before it is picked.
|
|
## Picked once, so restarting and repeating play the same part.
|
|
var short_start: float = -1.0
|
|
|
|
|
|
func get_soundtrack_title() -> String:
|
|
if is_answered:
|
|
return soundtrack_title
|
|
else:
|
|
return "????????"
|
|
|
|
|
|
func get_song_title() -> String:
|
|
if is_answered:
|
|
return song_title
|
|
else:
|
|
return "??????"
|
|
|
|
|
|
## The soundtrack's UUID once the answer is shown, otherwise empty.
|
|
func get_soundtrack_uuid() -> String:
|
|
return soundtrack_uuid if is_answered else ""
|
|
|
|
|
|
func get_song_info() -> String:
|
|
if Settings.is_debug:
|
|
return "%d. %s | played: %s | answered: %s" % [(song_number + 1), _full_title(),
|
|
has_played, is_answered]
|
|
elif is_answered:
|
|
return "%d. %s" % [(song_number + 1), _full_title()]
|
|
else:
|
|
return "??? - ???"
|
|
|
|
|
|
# "Soundtrack - Song", or only the song in a prepared quiz, which has no soundtracks
|
|
func _full_title() -> String:
|
|
if soundtrack_title.is_empty():
|
|
return song_title
|
|
return "%s - %s" % [soundtrack_title, song_title]
|
|
|
|
|
|
func add_point(player_id: String) -> void:
|
|
GameLog.debug("add_point")
|
|
if !players_given_point.has(player_id):
|
|
players_given_point.append(player_id)
|
|
|
|
|
|
func remove_point(player_id: String) -> void:
|
|
GameLog.debug("remove_point")
|
|
players_given_point.erase(player_id)
|
|
|
|
|
|
## The song as a Dictionary that can be written as JSON, for [QuizSave]. A song kept only
|
|
## in memory is saved without its file.
|
|
func to_dict() -> Dictionary:
|
|
return {
|
|
"number": song_number, "soundtrack": soundtrack_title, "song": song_title,
|
|
"points": players_given_point.duplicate(), "answered": is_answered,
|
|
"played": has_played, "playing": is_playing, "path": song_path,
|
|
"soundtrack_uuid": soundtrack_uuid,
|
|
}
|
|
|
|
|
|
## A song made from [param data], as given by [method to_dict], or null when [param data]
|
|
## is not a song.
|
|
static func from_dict(data: Variant) -> SongObject:
|
|
if !(data is Dictionary) || !(data.get("number") is float || data.get("number") is int):
|
|
return null
|
|
var song_object: SongObject = SongObject.new()
|
|
song_object.song_number = int(data.number)
|
|
song_object.soundtrack_title = str(data.get("soundtrack", ""))
|
|
song_object.song_title = str(data.get("song", ""))
|
|
song_object.soundtrack_uuid = str(data.get("soundtrack_uuid", ""))
|
|
if data.get("points") is Array:
|
|
for player_id: Variant in data.points:
|
|
song_object.add_point(str(player_id))
|
|
song_object.is_answered = bool(data.get("answered", false))
|
|
song_object.has_played = bool(data.get("played", false))
|
|
song_object.is_playing = bool(data.get("playing", false))
|
|
song_object.song_path = str(data.get("path", ""))
|
|
return song_object
|
|
|
|
|
|
func _to_string() -> String:
|
|
var format_string: String = "song_number: %d | soundtrack_title: %s | song_title: %s" \
|
|
+ " | is_answered: %s | has_played: %s | is_playing: %s"
|
|
return format_string % [song_number, soundtrack_title, song_title, is_answered, has_played,
|
|
is_playing]
|