create support for releases, artists, tracks, and credits
Signed-off-by: ari melody <ari@arimelody.me>
This commit is contained in:
parent
442889340c
commit
9329aa9f60
|
@ -34,13 +34,15 @@ func Handler() http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
type IndexData struct {
|
type IndexData struct {
|
||||||
Releases []musicModel.Release
|
Releases []*musicModel.Release
|
||||||
Artists []musicModel.Artist
|
Artists []*musicModel.Artist
|
||||||
|
Tracks []*musicModel.Track
|
||||||
}
|
}
|
||||||
|
|
||||||
serveTemplate("index.html", IndexData{
|
serveTemplate("index.html", IndexData{
|
||||||
Releases: global.Releases,
|
Releases: global.Releases,
|
||||||
Artists: global.Artists,
|
Artists: global.Artists,
|
||||||
|
Tracks: global.Tracks,
|
||||||
}).ServeHTTP(w, r)
|
}).ServeHTTP(w, r)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,7 @@ a:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.release {
|
.release {
|
||||||
|
margin-bottom: 1em;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -164,6 +165,7 @@ a:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.artist {
|
.artist {
|
||||||
|
margin-bottom: .5em;
|
||||||
padding: .5em;
|
padding: .5em;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -185,3 +187,27 @@ a:hover {
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.track {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
padding: 1em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: .5em;
|
||||||
|
|
||||||
|
border-radius: .5em;
|
||||||
|
background: #f8f8f8f8;
|
||||||
|
border: 1px solid #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2.track-title {
|
||||||
|
margin: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-description {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.track .empty {
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ func Handler() http.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
mux.Handle("/v1/music/", http.StripPrefix("/v1/music", music.ServeRelease()))
|
mux.Handle("/v1/music/", http.StripPrefix("/v1/music", music.ServeRelease()))
|
||||||
mux.Handle("/v1/music", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
mux.Handle("/v1/music", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
|
@ -39,5 +40,8 @@ func Handler() http.Handler {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
mux.Handle("/v1/musiccredit", CreateCredit())
|
||||||
|
mux.Handle("/v1/track", CreateTrack())
|
||||||
|
|
||||||
return mux
|
return mux
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,10 +98,20 @@ func CreateArtist() http.Handler {
|
||||||
var data model.Artist
|
var data model.Artist
|
||||||
err := json.NewDecoder(r.Body).Decode(&data)
|
err := json.NewDecoder(r.Body).Decode(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create artist: %s\n", err)
|
||||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if data.ID == "" {
|
||||||
|
http.Error(w, "Artist ID cannot be blank", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if data.Name == "" {
|
||||||
|
http.Error(w, "Artist name cannot be blank", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if global.GetArtist(data.ID) != nil {
|
if global.GetArtist(data.ID) != nil {
|
||||||
http.Error(w, fmt.Sprintf("Artist %s already exists", data.ID), http.StatusBadRequest)
|
http.Error(w, fmt.Sprintf("Artist %s already exists", data.ID), http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
|
@ -114,8 +124,6 @@ func CreateArtist() http.Handler {
|
||||||
Avatar: data.Avatar,
|
Avatar: data.Avatar,
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Artists = append(global.Artists, artist)
|
|
||||||
|
|
||||||
err = controller.CreateArtistDB(global.DB, &artist)
|
err = controller.CreateArtistDB(global.DB, &artist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to create artist %s: %s\n", artist.ID, err)
|
fmt.Printf("Failed to create artist %s: %s\n", artist.ID, err)
|
||||||
|
@ -123,6 +131,8 @@ func CreateArtist() http.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
global.Artists = append(global.Artists, &artist)
|
||||||
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
err = json.NewEncoder(w).Encode(artist)
|
err = json.NewEncoder(w).Encode(artist)
|
||||||
|
|
65
api/credit.go
Normal file
65
api/credit.go
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"arimelody.me/arimelody.me/global"
|
||||||
|
"arimelody.me/arimelody.me/music/model"
|
||||||
|
controller "arimelody.me/arimelody.me/music/controller"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateCredit() http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type creditJSON struct {
|
||||||
|
Release string
|
||||||
|
Artist string
|
||||||
|
Role string
|
||||||
|
Primary bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var data creditJSON
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&data)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var release = global.GetRelease(data.Release)
|
||||||
|
if release == nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Release %s does not exist", data.Release), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var artist = global.GetArtist(data.Artist)
|
||||||
|
if artist == nil {
|
||||||
|
http.Error(w, fmt.Sprintf("Artist %s does not exist", data.Artist), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var credit = model.Credit{
|
||||||
|
Artist: artist,
|
||||||
|
Role: data.Role,
|
||||||
|
Primary: data.Primary,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = controller.CreateCreditDB(global.DB, release.ID, artist.ID, &credit)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create credit: %s\n", err)
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
release.Credits = append(release.Credits, &credit)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
err = json.NewEncoder(w).Encode(credit)
|
||||||
|
})
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ import (
|
||||||
|
|
||||||
func ServeCatalog() http.Handler {
|
func ServeCatalog() http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
releases := []model.Release{}
|
releases := []*model.Release{}
|
||||||
authorised := admin.GetSession(r) != nil
|
authorised := admin.GetSession(r) != nil
|
||||||
for _, release := range global.Releases {
|
for _, release := range global.Releases {
|
||||||
if !release.IsReleased() && !authorised {
|
if !release.IsReleased() && !authorised {
|
||||||
|
@ -71,13 +71,11 @@ func CreateRelease() http.Handler {
|
||||||
Artwork: data.Artwork,
|
Artwork: data.Artwork,
|
||||||
Buyname: data.Buyname,
|
Buyname: data.Buyname,
|
||||||
Buylink: data.Buylink,
|
Buylink: data.Buylink,
|
||||||
Links: []model.Link{},
|
Links: []*model.Link{},
|
||||||
Credits: []model.Credit{},
|
Credits: []*model.Credit{},
|
||||||
Tracks: []model.Track{},
|
Tracks: []*model.Track{},
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Releases = append([]model.Release{release}, global.Releases...)
|
|
||||||
|
|
||||||
err = controller.CreateReleaseDB(global.DB, &release)
|
err = controller.CreateReleaseDB(global.DB, &release)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to create release %s: %s\n", release.ID, err)
|
fmt.Printf("Failed to create release %s: %s\n", release.ID, err)
|
||||||
|
@ -85,6 +83,8 @@ func CreateRelease() http.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
global.Releases = append([]*model.Release{&release}, global.Releases...)
|
||||||
|
|
||||||
w.Header().Add("Content-Type", "application/json")
|
w.Header().Add("Content-Type", "application/json")
|
||||||
w.WriteHeader(http.StatusCreated)
|
w.WriteHeader(http.StatusCreated)
|
||||||
err = json.NewEncoder(w).Encode(release)
|
err = json.NewEncoder(w).Encode(release)
|
46
api/track.go
Normal file
46
api/track.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"arimelody.me/arimelody.me/global"
|
||||||
|
"arimelody.me/arimelody.me/music/model"
|
||||||
|
controller "arimelody.me/arimelody.me/music/controller"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateTrack() http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
http.NotFound(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var track model.Track
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&track)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if track.Title == "" {
|
||||||
|
http.Error(w, "Track title cannot be empty", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
trackID, err := controller.CreateTrackDB(global.DB, &track)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create credit: %s\n", err)
|
||||||
|
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
track.ID = trackID
|
||||||
|
global.Tracks = append(global.Tracks, &track)
|
||||||
|
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
|
err = json.NewEncoder(w).Encode(track)
|
||||||
|
})
|
||||||
|
}
|
|
@ -17,14 +17,14 @@ var HTTP_DOMAIN = func() string {
|
||||||
|
|
||||||
var DB *sqlx.DB
|
var DB *sqlx.DB
|
||||||
|
|
||||||
var Releases []model.Release
|
var Releases []*model.Release
|
||||||
var Artists []model.Artist
|
var Artists []*model.Artist
|
||||||
var Tracks []model.Track
|
var Tracks []*model.Track
|
||||||
|
|
||||||
func GetRelease(id string) *model.Release {
|
func GetRelease(id string) *model.Release {
|
||||||
for _, release := range Releases {
|
for _, release := range Releases {
|
||||||
if release.ID == id {
|
if release.ID == id {
|
||||||
return &release
|
return release
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -33,7 +33,7 @@ func GetRelease(id string) *model.Release {
|
||||||
func GetArtist(id string) *model.Artist {
|
func GetArtist(id string) *model.Artist {
|
||||||
for _, artist := range Artists {
|
for _, artist := range Artists {
|
||||||
if artist.ID == id {
|
if artist.ID == id {
|
||||||
return &artist
|
return artist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
10
main.go
10
main.go
|
@ -15,7 +15,7 @@ import (
|
||||||
musicView "arimelody.me/arimelody.me/music/view"
|
musicView "arimelody.me/arimelody.me/music/view"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DEFAULT_PORT int = 8080
|
const DEFAULT_PORT int = 8080
|
||||||
|
@ -49,6 +49,14 @@ func main() {
|
||||||
}
|
}
|
||||||
fmt.Printf("%d releases loaded successfully.\n", len(global.Releases))
|
fmt.Printf("%d releases loaded successfully.\n", len(global.Releases))
|
||||||
|
|
||||||
|
// pull track data from DB
|
||||||
|
global.Tracks, err = musicController.PullAllTracks(global.DB)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to pull tracks from database: %v\n", err);
|
||||||
|
panic(1)
|
||||||
|
}
|
||||||
|
fmt.Printf("%d tracks loaded successfully.\n", len(global.Tracks))
|
||||||
|
|
||||||
// start the web server!
|
// start the web server!
|
||||||
mux := createServeMux()
|
mux := createServeMux()
|
||||||
port := DEFAULT_PORT
|
port := DEFAULT_PORT
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
|
|
||||||
// DATABASE
|
// DATABASE
|
||||||
|
|
||||||
func PullAllArtists(db *sqlx.DB) ([]model.Artist, error) {
|
func PullAllArtists(db *sqlx.DB) ([]*model.Artist, error) {
|
||||||
var artists = []model.Artist{}
|
var artists = []*model.Artist{}
|
||||||
|
|
||||||
err := db.Select(&artists, "SELECT * FROM artist")
|
err := db.Select(&artists, "SELECT * FROM artist")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,24 +1,39 @@
|
||||||
package music
|
package music
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"arimelody.me/arimelody.me/global"
|
||||||
"arimelody.me/arimelody.me/music/model"
|
"arimelody.me/arimelody.me/music/model"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DATABASE
|
// DATABASE
|
||||||
|
|
||||||
func PullReleaseCredits(db *sqlx.DB, releaseID string) ([]model.Credit, error) {
|
func PullReleaseCredits(db *sqlx.DB, releaseID string) ([]*model.Credit, error) {
|
||||||
var credits = []model.Credit{}
|
type creditDB struct {
|
||||||
|
Artist string
|
||||||
|
Role string
|
||||||
|
Primary bool `db:"is_primary"`
|
||||||
|
}
|
||||||
|
var credit_rows = []creditDB{}
|
||||||
|
var credits = []*model.Credit{}
|
||||||
|
|
||||||
err := db.Select(
|
err := db.Select(
|
||||||
&credits,
|
&credit_rows,
|
||||||
"SELECT * FROM musiccredit WHERE release=$1",
|
"SELECT artist, role, is_primary FROM musiccredit WHERE release=$1",
|
||||||
releaseID,
|
releaseID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, c := range credit_rows {
|
||||||
|
credits = append(credits, &model.Credit{
|
||||||
|
Artist: global.GetArtist(c.Artist),
|
||||||
|
Role: c.Role,
|
||||||
|
Primary: c.Primary,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return credits, nil
|
return credits, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
|
|
||||||
// DATABASE
|
// DATABASE
|
||||||
|
|
||||||
func PullReleaseLinks(db *sqlx.DB, releaseID string) ([]model.Link, error) {
|
func PullReleaseLinks(db *sqlx.DB, releaseID string) ([]*model.Link, error) {
|
||||||
var links = []model.Link{}
|
var links = []*model.Link{}
|
||||||
|
|
||||||
err := db.Select(
|
err := db.Select(
|
||||||
&links,
|
&links,
|
||||||
|
|
|
@ -1,20 +1,37 @@
|
||||||
package music
|
package music
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"arimelody.me/arimelody.me/global"
|
||||||
"arimelody.me/arimelody.me/music/model"
|
"arimelody.me/arimelody.me/music/model"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DATABASE
|
// DATABASE
|
||||||
|
|
||||||
func PullAllReleases(db *sqlx.DB) ([]model.Release, error) {
|
func PullAllReleases(db *sqlx.DB) ([]*model.Release, error) {
|
||||||
var releases = []model.Release{}
|
var release_rows = []*model.Release{}
|
||||||
|
var releases = []*model.Release{}
|
||||||
|
|
||||||
err := db.Select(&releases, "SELECT * FROM musicrelease ORDER BY release_date DESC")
|
err := db.Select(&release_rows, "SELECT * FROM musicrelease ORDER BY release_date DESC")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, release := range release_rows {
|
||||||
|
release.Credits, err = PullReleaseCredits(global.DB, release.ID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error pulling credits for %s: %s\n", release.ID, err)
|
||||||
|
}
|
||||||
|
release.Links, _ = PullReleaseLinks(global.DB, release.ID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error pulling links for %s: %s\n", release.ID, err)
|
||||||
|
}
|
||||||
|
release.Tracks = make([]*model.Track, 0)
|
||||||
|
releases = append(releases, release)
|
||||||
|
}
|
||||||
|
|
||||||
return releases, nil
|
return releases, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,10 @@ import (
|
||||||
|
|
||||||
// DATABASE
|
// DATABASE
|
||||||
|
|
||||||
func PullAllTracks(db *sqlx.DB) ([]model.Track, error) {
|
func PullAllTracks(db *sqlx.DB) ([]*model.Track, error) {
|
||||||
var tracks = []model.Track{}
|
var tracks = []*model.Track{}
|
||||||
|
|
||||||
err := db.Select(&tracks, "SELECT id, title, description, lyrics, preview_url FROM musictrack RETURNING id")
|
err := db.Select(&tracks, "SELECT id, title, description, lyrics, preview_url FROM musictrack")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@ type (
|
||||||
Artwork string `json:"artwork"`
|
Artwork string `json:"artwork"`
|
||||||
Buyname string `json:"buyname"`
|
Buyname string `json:"buyname"`
|
||||||
Buylink string `json:"buylink"`
|
Buylink string `json:"buylink"`
|
||||||
Links []Link `json:"links"`
|
Links []*Link `json:"links"`
|
||||||
Credits []Credit `json:"credits"`
|
Credits []*Credit `json:"credits"`
|
||||||
Tracks []Track `json:"tracks"`
|
Tracks []*Track `json:"tracks"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ func Handler() http.Handler {
|
||||||
|
|
||||||
func ServeCatalog() http.Handler {
|
func ServeCatalog() http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
releases := []model.Release{}
|
releases := []*model.Release{}
|
||||||
authorised := admin.GetSession(r) != nil
|
authorised := admin.GetSession(r) != nil
|
||||||
for _, release := range global.Releases {
|
for _, release := range global.Releases {
|
||||||
if !release.IsReleased() && !authorised {
|
if !release.IsReleased() && !authorised {
|
||||||
|
|
BIN
public/img/default-avatar.png
Normal file
BIN
public/img/default-avatar.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
|
@ -2,7 +2,7 @@
|
||||||
-- Artists (should be applicable to all art)
|
-- Artists (should be applicable to all art)
|
||||||
--
|
--
|
||||||
CREATE TABLE public.artist (
|
CREATE TABLE public.artist (
|
||||||
id character varying(64) DEFAULT gen_random_uuid(),
|
id character varying(64),
|
||||||
name text NOT NULL,
|
name text NOT NULL,
|
||||||
website text,
|
website text,
|
||||||
avatar text
|
avatar text
|
||||||
|
|
|
@ -40,8 +40,30 @@
|
||||||
<div class="card artists">
|
<div class="card artists">
|
||||||
{{range $Artist := .Artists}}
|
{{range $Artist := .Artists}}
|
||||||
<div class="artist">
|
<div class="artist">
|
||||||
<img src="https://arimelody.me/img/favicon.png" alt="" width="64" loading="lazy" class="artist-avatar">
|
<img src="{{$Artist.GetAvatar}}" alt="" width="64" loading="lazy" class="artist-avatar">
|
||||||
<a href="/admin/artists/arimelody" class="artist-name">ari melody</a>
|
<a href="/admin/artists/{{$Artist.ID}}" class="artist-name">{{$Artist.Name}}</a>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
{{if not .Artists}}
|
||||||
|
<p>There are no artists.</p>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1>Tracks</h1>
|
||||||
|
<div class="card tracks">
|
||||||
|
{{range $Track := .Tracks}}
|
||||||
|
<div class="track">
|
||||||
|
<h2 class="track-title">{{$Track.Title}}</h2>
|
||||||
|
{{if $Track.Description}}
|
||||||
|
<p class="track-description">{{$Track.Description}}</p>
|
||||||
|
{{else}}
|
||||||
|
<p class="track-description empty">No description provided.</p>
|
||||||
|
{{end}}
|
||||||
|
{{if $Track.Lyrics}}
|
||||||
|
<p class="track-lyrics">{{$Track.Lyrics}}</p>
|
||||||
|
{{else}}
|
||||||
|
<p class="track-lyrics empty">There are no lyrics.</p>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{if not .Artists}}
|
{{if not .Artists}}
|
||||||
|
|
Loading…
Reference in a new issue