Files
owenandClaude Sonnet 5 9a3f1e4a24 Make the now-playing bar's title/artist clickable and add a cover art lightbox
The song title links to its album, the artist name links to the artist
page, and clicking the cover art opens a larger version in a modal.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014r98AtcBbevpscWLsfwVvv
2026-08-23 22:04:36 -05:00

117 lines
2.7 KiB
Go

package web
import (
"database/sql"
"git.noroutes.net/NoRoutes/vibetunes/internal/database"
)
// View-model structs shape database rows for templates, keeping SQL
// scanning details (sql.Null*) out of the .html files.
type ArtistView struct {
ID string
Name string
Bio string
CoverArtID string
}
type AlbumView struct {
ID string
Title string
ArtistName string
Year int64
CoverArtID string
}
type SongRow struct {
ID string
Title string
ArtistName string
ArtistID string
AlbumTitle string
AlbumID string
CoverArtID string
TrackNumber int64
DurationSeconds float64
Starred bool
IsAdmin bool
}
func toArtistView(a *database.Artist) ArtistView {
v := ArtistView{ID: a.ID, Name: a.Name, Bio: a.Bio}
if a.CoverArtID.Valid {
v.CoverArtID = a.CoverArtID.String
}
return v
}
func toAlbumView(db *sql.DB, a *database.Album) AlbumView {
v := AlbumView{ID: a.ID, Title: a.Title}
if a.Year.Valid {
v.Year = a.Year.Int64
}
if a.CoverArtID.Valid {
v.CoverArtID = a.CoverArtID.String
}
if a.PrimaryAlbumArtistID.Valid {
if artist, err := database.GetArtist(db, a.PrimaryAlbumArtistID.String); err == nil {
v.ArtistName = artist.Name
}
}
return v
}
// toSongRow builds a display row for a song, given its owning album (may
// be nil for albumless tracks) and the current user (for the starred
// flag; pass "" userID to skip that lookup, and isAdmin=false to hide
// the row's "Edit" link).
func toSongRow(db *sql.DB, s *database.Song, album *database.Album, userID string, isAdmin bool) SongRow {
row := SongRow{
ID: s.ID,
Title: s.Title,
DurationSeconds: 0,
IsAdmin: isAdmin,
}
if s.TrackNumber.Valid {
row.TrackNumber = s.TrackNumber.Int64
}
if names, err := database.ListSongArtistNames(db, s.ID); err == nil && len(names) > 0 {
row.ArtistName = names[0]
for _, n := range names[1:] {
row.ArtistName += ", " + n
}
}
if id, err := database.PrimarySongArtistID(db, s.ID); err == nil {
row.ArtistID = id
}
if album != nil {
row.AlbumTitle = album.Title
row.AlbumID = album.ID
if album.CoverArtID.Valid {
row.CoverArtID = album.CoverArtID.String
}
}
if mf, err := database.GetMediaFile(db, s.MediaFileID); err == nil {
row.DurationSeconds = mf.DurationSeconds
}
if userID != "" {
if starred, err := database.IsStarred(db, userID, "song", s.ID); err == nil {
row.Starred = starred
}
}
return row
}
// albumOf loads a song's album, tolerating albumless songs.
func albumOf(db *sql.DB, s *database.Song) *database.Album {
if !s.AlbumID.Valid {
return nil
}
album, err := database.GetAlbum(db, s.AlbumID.String)
if err != nil {
return nil
}
return album
}