feat: album page
This commit is contained in:
parent
100d3e9d99
commit
9b5948c2a9
60
src/modules/album/album.go
Normal file
60
src/modules/album/album.go
Normal 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))
|
||||
}
|
31
src/modules/album/album.service.go
Normal file
31
src/modules/album/album.service.go
Normal 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
|
||||
}
|
27
src/modules/album/album.templ
Normal file
27
src/modules/album/album.templ
Normal 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>
|
||||
}
|
||||
}
|
125
src/modules/album/album_templ.go
Normal file
125
src/modules/album/album_templ.go
Normal 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
|
@ -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">
|
||||
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">
|
||||
for _, album := range albums {
|
||||
@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="h-30 relative">
|
||||
<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 }
|
||||
</div>
|
||||
</a>
|
||||
<div class="overflow-hidden overflow-ellipsis text-sm">
|
||||
{ album.Artist }
|
||||
</div>
|
||||
|
@ -46,7 +46,7 @@ func IndexTempl(albums []utils.Album) templ.Component {
|
||||
}()
|
||||
}
|
||||
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 {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -98,38 +98,47 @@ func albumCard(album utils.Album) templ.Component {
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("/covers/%s", album.ID))
|
||||
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))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
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 {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(album.Name)
|
||||
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))
|
||||
var templ_7745c5c3_Var5 templ.SafeURL = templ.URL(fmt.Sprintf("/album/%s", album.ID))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var5)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
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 {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
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 {
|
||||
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))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
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>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
|
31
src/modules/song/song.service.go
Normal file
31
src/modules/song/song.service.go
Normal 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
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package src
|
||||
|
||||
import (
|
||||
"acide/src/modules/album"
|
||||
"acide/src/modules/auth"
|
||||
"acide/src/modules/covers"
|
||||
"acide/src/modules/index"
|
||||
@ -29,6 +30,7 @@ func (s *Server) RegisterRoutes() http.Handler {
|
||||
index.SetupRoutes(e.Group(""))
|
||||
auth.SetupRoutes(e.Group("/auth"))
|
||||
covers.Setup(e.Group("/covers"))
|
||||
album.Setup(e.Group("/album"))
|
||||
|
||||
return e
|
||||
}
|
||||
|
@ -56,3 +56,51 @@ type Album struct {
|
||||
type NavError struct {
|
||||
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"`
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user