Skip to content

Commit

Permalink
chore(song): show monthly stats
Browse files Browse the repository at this point in the history
  • Loading branch information
Topvennie committed Dec 19, 2024
1 parent 4b61b31 commit 2e95a78
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 36 deletions.
2 changes: 1 addition & 1 deletion db/queries/song.sql
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ FROM (
) aggregated
JOIN song s ON aggregated.song_id = s.id
ORDER BY aggregated.created_at DESC
LIMIT 20;
LIMIT 50;

-- name: GetTopSongs :many
SELECT s.id AS song_id, s.title, COUNT(sh.id) AS play_count
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/db/dto/song.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type Song struct {
Album string `json:"album"`
SpotifyID string `json:"spotify_id" validate:"required"`
DurationMS int32 `json:"duration_ms"`
LyricsType string `json:"lyrics_type"` // Either 'synced' or 'plain'
LyricsType string `json:"lyrics_type"` // Either 'synced' ,'plain' or 'instrumental'
Lyrics string `json:"lyrics"`
CreatedAt time.Time `json:"created_at"`
Artists []SongArtist `json:"artists"`
Expand Down
2 changes: 1 addition & 1 deletion internal/pkg/db/sqlc/song.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion internal/pkg/song/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ func (s *Song) getArtist(artist *dto.SongArtist) error {
}

type lyricsResponse struct {
Instrumental bool `json:"instrumental"`
PlainLyrics string `json:"plainLyrics"`
SyncedLyrics string `json:"SyncedLyrics"`
}
Expand Down Expand Up @@ -126,18 +127,31 @@ func (s *Song) getLyrics(track *dto.Song) error {
return errors.Join(append([]error{errors.New("Song: Lyrics request failed")}, errs...)...)
}
if status != fiber.StatusOK {
if status == fiber.StatusNotFound {
// Lyrics not found
return nil
}

return fmt.Errorf("Song: Lyrics request wrong status code %d", status)
}
if (res == &lyricsResponse{}) {
return errors.New("Song: Lyrics request returned empty struct")
}

zap.S().Info(res)

if res.SyncedLyrics != "" {
// Synced lyrics ?
track.LyricsType = "synced"
track.Lyrics = res.SyncedLyrics
} else {
} else if res.PlainLyrics != "" {
// Plain lyrics ?
track.LyricsType = "plain"
track.Lyrics = res.PlainLyrics
} else if res.Instrumental {
// Instrumental ?
track.LyricsType = "instrumental"
track.Lyrics = ""
}

return nil
Expand Down
5 changes: 0 additions & 5 deletions tui/components/stopwatch/stopwatch.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,6 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
case StartStopMsg:
if msg.running {
// Start
if m.running {
// Already running
return m, nil
}

m.id = nextID()
m.duration = msg.startDuration
m.running = true
Expand Down
5 changes: 1 addition & 4 deletions tui/view/song/song.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func (m *Model) Update(msg tea.Msg) (view.View, tea.Cmd) {
lyric, ok := m.current.lyrics.Current()
if !ok {
// Shouldn't happen
zap.S().Error("song: unable to get current lyric in initialization phase: ", m.current.song.Title)
zap.S().Error("song: Unable to get current lyric in initialization phase: ", m.current.song.Title)
m.current.playing = false
return m, nil
}
Expand Down Expand Up @@ -326,9 +326,6 @@ func updateMonthlyStats(view view.View) (tea.Msg, error) {
return nil, err
}

// Don't bother checking if anything has changed
// A single extra refresh won't matter

msg := msgStats{monthly: true, stats: []stat{}}

// Songs
Expand Down
9 changes: 8 additions & 1 deletion tui/view/song/style.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ var (
sStatEnum = base.Foreground(cSpotify).Width(wStatEnum).Align(lipgloss.Left)
sStatEntry = base.Align(lipgloss.Left)
sStatAmount = base.Foreground(cZeus).Width(wStatAmount).Align(lipgloss.Right)

// Specific styles for when no song is playing
sStatCategory = base.Align(lipgloss.Center)
sStatCategoryTitle = base.Foreground(cZeus).Align(lipgloss.Center).Border(lipgloss.NormalBorder(), true, false).BorderForeground(cBorder)
sStatHistory = base.MarginRight(1).PaddingRight(2).Border(lipgloss.ThickBorder(), false, true, false, false).BorderForeground(cBorder)
)

// Styles for the lyrics
Expand All @@ -53,7 +58,7 @@ var (

// Styles for the status
var (
sStatus = base
sStatus = base.MarginTop(1)
sStatusSong = base.Align(lipgloss.Center)
sStatusStopwatch = base.Faint(true)
sStatusBar = base.Foreground(cZeus).Align(lipgloss.Left)
Expand All @@ -77,6 +82,8 @@ func (m *Model) updateStyles() {
// We're full screen
sStatOne = sStatOne.Margin(0, 3)
}
sStatCategory = sStatCategory.Width(2 * (sStatOne.GetWidth() + view.GetOuterWidth(sStatOne)))
sStatCategoryTitle = sStatCategoryTitle.Width(2*sStatOne.GetWidth() + view.GetOuterWidth(sStatOne))

// Adjust lyrics styles
sLyric = sLyric.Width(m.width)
Expand Down
81 changes: 59 additions & 22 deletions tui/view/song/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,48 +78,85 @@ func (m *Model) viewPlayingLyrics() string {
func (m *Model) viewPlayingStats() string {
columns := make([]string, 0, 4)

columns = append(columns, m.viewStat(m.history))
columns = append(columns, m.viewStat(m.stats[0]))
columns = append(columns, m.viewStat(m.stats[1]))
columns = append(columns, m.viewStat(m.stats[2]))
columns = append(columns, m.viewStatPlaying(m.history))
columns = append(columns, m.viewStatPlaying(m.statsMonthly[0]))
columns = append(columns, m.viewStatPlaying(m.statsMonthly[1]))
columns = append(columns, m.viewStatPlaying(m.statsMonthly[2]))

return lipgloss.JoinHorizontal(lipgloss.Top, columns...)
}

func (m *Model) viewNotPlaying() string {
rows := make([][]string, 0, 2)
for i := 0; i < 2; i++ {
// Render stats
rows := make([][]string, 0, 3)
for i := 0; i < 3; i++ {
rows = append(rows, make([]string, 0, 2))
}

rows[0] = append(rows[0], m.viewStat(m.history))
rows[0] = append(rows[0], m.viewStat(m.stats[0]))
rows[1] = append(rows[1], m.viewStat(m.stats[1]))
rows[1] = append(rows[1], m.viewStat(m.stats[2]))
rows[0] = append(rows[0], m.viewStatPlaying(m.statsMonthly[0], "Monthly"))
rows[0] = append(rows[0], m.viewStatPlaying(m.stats[0], "All Time"))
rows[1] = append(rows[1], m.viewStatPlaying(m.statsMonthly[1], "Monthly"))
rows[1] = append(rows[1], m.viewStatPlaying(m.stats[1], "All Time"))
rows[2] = append(rows[2], m.viewStatPlaying(m.statsMonthly[2], "Monthly"))
rows[2] = append(rows[2], m.viewStatPlaying(m.stats[2], "All Time"))

renderedRows := make([]string, 0, 3)
var title string
for i, row := range rows {
r := lipgloss.JoinHorizontal(lipgloss.Top, row...)
title = sStatCategory.Render(sStatCategoryTitle.Render(m.stats[i].title)) // HACK: Make border same size as 2 stats next to each other
renderedRows = append(renderedRows, lipgloss.JoinVertical(lipgloss.Left, title, r))
}

v := lipgloss.JoinVertical(lipgloss.Left, renderedRows...)

// Render history
items := make([]string, 0, len(m.history.entries))

renderedRows := make([]string, 0, 2)
for _, row := range rows {
renderedRows = append(renderedRows, lipgloss.JoinHorizontal(lipgloss.Top, row...))
// Push it down
for range lipgloss.Height(title) {
items = append(items, "")
}
items = append(items, sStatTitle.Render(m.history.title))

for i, entry := range m.history.entries {
enum := sStatEnum.Render(fmt.Sprintf("%d.", i+1))
body := sStatEntry.Render(entry.name)
amount := sStatAmount.Render(fmt.Sprintf("%d", entry.amount))
items = append(items, lipgloss.JoinHorizontal(lipgloss.Top, enum, body, amount))
}
items = append(items, "") // HACK: Avoid the last item shifting to the right
list := lipgloss.JoinVertical(lipgloss.Left, items...)
// title := sStatTitle.Render(m.history.title)
history := sStatHistory.Height(lipgloss.Height(v) - 1).MaxHeight(lipgloss.Height(v) - 1).Render(list) // - 1 to compensate for the hack newline at the end

view := lipgloss.JoinVertical(lipgloss.Left, renderedRows...)
v = lipgloss.JoinHorizontal(lipgloss.Top, history, v)

return sAll.Render(view)
return sAll.Render(v)
}

func (m *Model) viewStat(stat stat) string {
func (m *Model) viewStatPlaying(stat stat, titleOpt ...string) string {
title := stat.title
if len(titleOpt) > 0 {
title = titleOpt[0]
}

items := make([]string, 0, len(stat.entries))
for i, stat := range stat.entries {
for i := range stat.entries {
if i >= 10 {
break
}

enum := sStatEnum.Render(fmt.Sprintf("%d.", i+1))
entry := sStatEntry.Render(stat.name)
amount := sStatAmount.Render(fmt.Sprintf("%d", stat.amount))
body := sStatEntry.Render(stat.entries[i].name)
amount := sStatAmount.Render(fmt.Sprintf("%d", stat.entries[i].amount))

items = append(items, lipgloss.JoinHorizontal(lipgloss.Top, enum, entry, amount))
items = append(items, lipgloss.JoinHorizontal(lipgloss.Top, enum, body, amount))
}
items = append(items, "") // HACK: Avoid the last item shifting to the right
l := lipgloss.JoinVertical(lipgloss.Left, items...)

title := sStatTitle.Render(stat.title)
t := sStatTitle.Render(title)

return sStatOne.Render(lipgloss.JoinVertical(lipgloss.Left, title, l))
return sStatOne.Render(lipgloss.JoinVertical(lipgloss.Left, t, l))
}

0 comments on commit 2e95a78

Please sign in to comment.