feat: album page

This commit is contained in:
Araozu 2024-10-08 09:17:50 -05:00
parent 100d3e9d99
commit 9b5948c2a9
9 changed files with 353 additions and 14 deletions

View File

@ -0,0 +1,60 @@
package album
import (
"acide/src/modules/song"
"acide/src/utils"
"net/http"
"sync"
"github.com/labstack/echo"
"github.com/labstack/gommon/log"
)
func Setup(g *echo.Group) {
log.Print("Setting up the album module")
g.Use(utils.Authed)
g.GET("/:id", albumPage)
}
func albumPage(c echo.Context) error {
token, server := utils.Credentials(c)
albumId := c.Param("id")
// load album info and song list on the background
var wg sync.WaitGroup
var album *utils.Album
var songs []utils.Song
var routineErr error = nil
wg.Add(2)
go func() {
defer wg.Done()
res, err := loadAlbum(token, server, albumId)
if err != nil {
routineErr = err
return
}
album = res
}()
go func() {
defer wg.Done()
res, err := song.LoadSongs(token, server, albumId)
if err != nil {
routineErr = err
return
}
songs = res
}()
wg.Wait()
if routineErr != nil {
return routineErr
}
return utils.RenderTempl(c, http.StatusOK, albumTempl(albumId, album, songs))
}

View File

@ -0,0 +1,31 @@
package album
import (
"acide/src/utils"
"errors"
"fmt"
"github.com/go-resty/resty/v2"
)
func loadAlbum(token, server, albumId string) (*utils.Album, error) {
var album utils.Album
var error utils.NavError
client := resty.New()
response, err := client.R().
SetHeader("x-nd-authorization", fmt.Sprintf("Bearer %s", token)).
SetResult(&album).
SetError(&error).
Get(fmt.Sprintf("%s/api/album/%s", server, albumId))
if err != nil {
return nil, err
}
if !response.IsSuccess() {
return nil, errors.New(error.Error)
}
return &album, nil
}

View File

@ -0,0 +1,27 @@
package album
import (
"acide/src/utils"
"fmt"
)
templ albumTempl(albumId string, album *utils.Album, songs []utils.Song) {
@utils.SkeletonTempl() {
<div class="text-center">
<img class="inline-block mt-2 rounded shadow" src={ fmt.Sprintf("/covers/%s", albumId) }/>
<h1 class="font-bold pt-4 pb-2 text-2xl">
{ album.Name }
</h1>
<p>
{ album.Artist }
</p>
<div class="text-left">
for _, song := range songs {
<p class="py-2 px-4">
{ song.Title }
</p>
}
</div>
</div>
}
}

View File

@ -0,0 +1,125 @@
// Code generated by templ - DO NOT EDIT.
// templ: version: v0.2.778
package album
//lint:file-ignore SA4006 This context is only used if a nested component is present.
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"acide/src/utils"
"fmt"
)
func albumTempl(albumId string, album *utils.Album, songs []utils.Song) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
return templ_7745c5c3_CtxErr
}
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
if templ_7745c5c3_Var1 == nil {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
if !templ_7745c5c3_IsBuffer {
defer func() {
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
if templ_7745c5c3_Err == nil {
templ_7745c5c3_Err = templ_7745c5c3_BufErr
}
}()
}
ctx = templ.InitializeContext(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"text-center\"><img class=\"inline-block mt-2 rounded shadow\" src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/covers/%s", albumId))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/modules/album/album.templ`, Line: 11, Col: 89}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><h1 class=\"font-bold pt-4 pb-2 text-2xl\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(album.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/modules/album/album.templ`, Line: 13, Col: 16}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h1><p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(album.Artist)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/modules/album/album.templ`, Line: 16, Col: 18}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p><div class=\"text-left\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, song := range songs {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<p class=\"py-2 px-4\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(song.Title)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/modules/album/album.templ`, Line: 21, Col: 18}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
templ_7745c5c3_Err = utils.SkeletonTempl().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
return templ_7745c5c3_Err
})
}
var _ = templruntime.GeneratedTemplate

View File

@ -11,6 +11,9 @@ templ IndexTempl(albums []utils.Album) {
<nav class="text-blue-500 font-bold text-xl py-2 border-b border-blue-500"> <nav class="text-blue-500 font-bold text-xl py-2 border-b border-blue-500">
music to go music to go
</nav> </nav>
<h2 class="font-bold text-xl px-2 pt-4">
Random Albums
</h2>
<div class="overflow-x-scroll whitespace-nowrap py-2"> <div class="overflow-x-scroll whitespace-nowrap py-2">
for _, album := range albums { for _, album := range albums {
@albumCard(album) @albumCard(album)
@ -24,9 +27,12 @@ templ albumCard(album utils.Album) {
<div class="inline-block p-1 mx-1 rounded bg-zinc-200 w-32"> <div class="inline-block p-1 mx-1 rounded bg-zinc-200 w-32">
<div class="h-30 relative"> <div class="h-30 relative">
<img src={ fmt.Sprintf("/covers/%s", album.ID) }/> <img src={ fmt.Sprintf("/covers/%s", album.ID) }/>
<div class="overflow-hidden overflow-ellipsis"> <a
href={ templ.URL(fmt.Sprintf("/album/%s", album.ID)) }
class="inline-block w-full overflow-hidden overflow-ellipsis hover:underline"
>
{ album.Name } { album.Name }
</div> </a>
<div class="overflow-hidden overflow-ellipsis text-sm"> <div class="overflow-hidden overflow-ellipsis text-sm">
{ album.Artist } { album.Artist }
</div> </div>

View File

@ -46,7 +46,7 @@ func IndexTempl(albums []utils.Album) templ.Component {
}() }()
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><nav class=\"text-blue-500 font-bold text-xl py-2 border-b border-blue-500\">music to go</nav><div class=\"overflow-x-scroll whitespace-nowrap py-2\">") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div><nav class=\"text-blue-500 font-bold text-xl py-2 border-b border-blue-500\">music to go</nav><h2 class=\"font-bold text-xl px-2 pt-4\">Random Albums</h2><div class=\"overflow-x-scroll whitespace-nowrap py-2\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -98,38 +98,47 @@ func albumCard(album utils.Album) templ.Component {
var templ_7745c5c3_Var4 string var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/covers/%s", album.ID)) templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/covers/%s", album.ID))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/modules/index/index.templ`, Line: 26, Col: 49} return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/modules/index/index.templ`, Line: 29, Col: 49}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><div class=\"overflow-hidden overflow-ellipsis\">") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"> <a href=\"")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var5 string var templ_7745c5c3_Var5 templ.SafeURL = templ.URL(fmt.Sprintf("/album/%s", album.ID))
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(album.Name) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var5)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/modules/index/index.templ`, Line: 28, Col: 16}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div><div class=\"overflow-hidden overflow-ellipsis text-sm\">") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\" class=\"inline-block w-full overflow-hidden overflow-ellipsis hover:underline\">")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var6 string var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(album.Artist) templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(album.Name)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/modules/index/index.templ`, Line: 31, Col: 18} return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/modules/index/index.templ`, Line: 34, Col: 16}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a><div class=\"overflow-hidden overflow-ellipsis text-sm\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(album.Artist)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `src/modules/index/index.templ`, Line: 37, Col: 18}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div></div>") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div></div>")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err

View File

@ -0,0 +1,31 @@
package song
import (
"acide/src/utils"
"errors"
"fmt"
"github.com/go-resty/resty/v2"
)
func LoadSongs(token, server, albumId string) ([]utils.Song, error) {
var songs []utils.Song
var error utils.NavError
client := resty.New()
response, err := client.R().
SetHeader("x-nd-authorization", fmt.Sprintf("Bearer %s", token)).
SetResult(&songs).
SetError(&error).
Get(fmt.Sprintf("%s/api/song?_end=0&_order=ASC&_sort=album&_start=0&album_id=%s", server, albumId))
if err != nil {
return nil, err
}
if !response.IsSuccess() {
return nil, errors.New(error.Error)
}
return songs, nil
}

View File

@ -1,6 +1,7 @@
package src package src
import ( import (
"acide/src/modules/album"
"acide/src/modules/auth" "acide/src/modules/auth"
"acide/src/modules/covers" "acide/src/modules/covers"
"acide/src/modules/index" "acide/src/modules/index"
@ -29,6 +30,7 @@ func (s *Server) RegisterRoutes() http.Handler {
index.SetupRoutes(e.Group("")) index.SetupRoutes(e.Group(""))
auth.SetupRoutes(e.Group("/auth")) auth.SetupRoutes(e.Group("/auth"))
covers.Setup(e.Group("/covers")) covers.Setup(e.Group("/covers"))
album.Setup(e.Group("/album"))
return e return e
} }

View File

@ -56,3 +56,51 @@ type Album struct {
type NavError struct { type NavError struct {
Error string `json:"error"` Error string `json:"error"`
} }
type Song struct {
PlayCount int `json:"playCount"`
PlayDate time.Time `json:"playDate"`
Rating int `json:"rating"`
Starred bool `json:"starred"`
StarredAt interface{} `json:"starredAt"`
BookmarkPosition int `json:"bookmarkPosition"`
ID string `json:"id"`
LibraryID int `json:"libraryId"`
Path string `json:"path"`
Title string `json:"title"`
Album string `json:"album"`
ArtistID string `json:"artistId"`
Artist string `json:"artist"`
AlbumArtistID string `json:"albumArtistId"`
AlbumArtist string `json:"albumArtist"`
AlbumID string `json:"albumId"`
HasCoverArt bool `json:"hasCoverArt"`
TrackNumber int `json:"trackNumber"`
DiscNumber int `json:"discNumber"`
Year int `json:"year"`
OriginalYear int `json:"originalYear"`
ReleaseYear int `json:"releaseYear"`
Size int `json:"size"`
Suffix string `json:"suffix"`
Duration float64 `json:"duration"`
BitRate int `json:"bitRate"`
SampleRate int `json:"sampleRate"`
Channels int `json:"channels"`
Genre string `json:"genre"`
Genres []struct {
ID string `json:"id"`
Name string `json:"name"`
} `json:"genres"`
OrderTitle string `json:"orderTitle"`
OrderAlbumName string `json:"orderAlbumName"`
OrderArtistName string `json:"orderArtistName"`
OrderAlbumArtistName string `json:"orderAlbumArtistName"`
Compilation bool `json:"compilation"`
Lyrics string `json:"lyrics"`
RgAlbumGain int `json:"rgAlbumGain"`
RgAlbumPeak int `json:"rgAlbumPeak"`
RgTrackGain int `json:"rgTrackGain"`
RgTrackPeak int `json:"rgTrackPeak"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}