@@ -67,9 +70,10 @@
{{if $Track.Release}}
{{$Track.Release.Title}}
{{else}}
- (no album)
+ (no release)
{{end}}
+
{{$Track.ID}}
{{if $Track.Description}}
{{$Track.Description}}
{{else}}
diff --git a/views/admin/layout.html b/admin/views/layout.html
similarity index 68%
rename from views/admin/layout.html
rename to admin/views/layout.html
index 06c15c9..961dfc6 100644
--- a/views/admin/layout.html
+++ b/admin/views/layout.html
@@ -13,6 +13,13 @@
+
+
{{block "content" .}}
{{end}}
diff --git a/admin/views/login.html b/admin/views/login.html
new file mode 100644
index 0000000..0edb29e
--- /dev/null
+++ b/admin/views/login.html
@@ -0,0 +1,35 @@
+{{define "head"}}
+
login - ari melody 💫
+
+
+
+{{end}}
+
+{{define "content"}}
+
+
+ {{if .Token}}
+
+
+
+ Logged in successfully.
+ You should be redirected to /admin in 5 seconds.
+
+
+ {{else}}
+ Log in with Discord.
+ {{end}}
+
+
+{{end}}
diff --git a/admin/views/logout.html b/admin/views/logout.html
new file mode 100644
index 0000000..8f8f801
--- /dev/null
+++ b/admin/views/logout.html
@@ -0,0 +1,25 @@
+{{define "head"}}
+
admin - ari melody 💫
+
+
+
+{{end}}
+
+{{define "content"}}
+
+
+
+
+ Logged out successfully.
+ You should be redirected to / in 5 seconds.
+
+
+
+
+{{end}}
diff --git a/api/artist.go b/api/artist.go
index e0e984b..03c69f7 100644
--- a/api/artist.go
+++ b/api/artist.go
@@ -10,6 +10,13 @@ import (
controller "arimelody.me/arimelody.me/music/controller"
)
+type artistJSON struct {
+ ID string `json:"id"`
+ Name *string `json:"name"`
+ Website *string `json:"website"`
+ Avatar *string `json:"avatar"`
+}
+
func ServeAllArtists() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
@@ -78,7 +85,7 @@ func CreateArtist() http.Handler {
return
}
- var data model.Artist
+ var data artistJSON
err := json.NewDecoder(r.Body).Decode(&data)
if err != nil {
fmt.Printf("Failed to create artist: %s\n", err)
@@ -90,7 +97,7 @@ func CreateArtist() http.Handler {
http.Error(w, "Artist ID cannot be blank\n", http.StatusBadRequest)
return
}
- if data.Name == "" {
+ if data.Name == nil || *data.Name == "" {
http.Error(w, "Artist name cannot be blank\n", http.StatusBadRequest)
return
}
@@ -102,9 +109,9 @@ func CreateArtist() http.Handler {
var artist = model.Artist{
ID: data.ID,
- Name: data.Name,
- Website: data.Website,
- Avatar: data.Avatar,
+ Name: *data.Name,
+ Website: *data.Website,
+ Avatar: *data.Avatar,
}
err = controller.CreateArtistDB(global.DB, &artist)
@@ -138,7 +145,7 @@ func UpdateArtist() http.Handler {
return
}
- var data model.Artist
+ var data artistJSON
err := json.NewDecoder(r.Body).Decode(&data)
if err != nil {
fmt.Printf("Failed to update artist: %s\n", err)
@@ -153,24 +160,24 @@ func UpdateArtist() http.Handler {
return
}
- if data.ID == "" { data.ID = artist.ID }
+ var update = *artist
- if data.Name == "" {
- http.Error(w, "Artist name cannot be blank\n", http.StatusBadRequest)
- return
- }
+ if data.ID != "" { update.ID = data.ID }
+ if data.Name != nil { update.Name = *data.Name }
+ if data.Website != nil { update.Website = *data.Website }
+ if data.Avatar != nil { update.Avatar = *data.Avatar }
- err = controller.UpdateArtistDB(global.DB, &data)
+ err = controller.UpdateArtistDB(global.DB, &update)
if err != nil {
fmt.Printf("Failed to update artist %s: %s\n", artist.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- artist.ID = data.ID
- artist.Name = data.Name
- artist.Website = data.Website
- artist.Avatar = data.Avatar
+ artist.ID = update.ID
+ artist.Name = update.Name
+ artist.Website = update.Website
+ artist.Avatar = update.Avatar
w.Header().Add("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(artist)
diff --git a/api/release.go b/api/release.go
index 3011c9f..6d02733 100644
--- a/api/release.go
+++ b/api/release.go
@@ -15,14 +15,14 @@ import (
type releaseBodyJSON struct {
ID string `json:"id"`
- Visible bool `json:"visible"`
- Title string `json:"title"`
- Description string `json:"description"`
- ReleaseType model.ReleaseType `json:"type"`
- ReleaseDate time.Time `json:"releaseDate"`
- Artwork string `json:"artwork"`
- Buyname string `json:"buyname"`
- Buylink string `json:"buylink"`
+ Visible *bool `json:"visible"`
+ Title *string `json:"title"`
+ Description *string `json:"description"`
+ ReleaseType *model.ReleaseType `json:"type"`
+ ReleaseDate *string `json:"releaseDate"`
+ Artwork *string `json:"artwork"`
+ Buyname *string `json:"buyname"`
+ Buylink *string `json:"buylink"`
}
func ServeCatalog() http.Handler {
@@ -36,7 +36,7 @@ func ServeCatalog() http.Handler {
Artwork string `json:"artwork"`
Buyname string `json:"buyname"`
Buylink string `json:"buylink"`
- Links []*model.Link `json:"links"`
+ Links []*model.Link `json:"links"`
}
catalog := []CatalogItem{}
@@ -85,26 +85,40 @@ func CreateRelease() http.Handler {
http.Error(w, "Release ID cannot be empty\n", http.StatusBadRequest)
return
}
- if data.Title == "" {
+ if *data.Title == "" {
http.Error(w, "Release title cannot be empty\n", http.StatusBadRequest)
return
}
+ if data.Buyname == nil || *data.Buyname == "" { *data.Buyname = "buy" }
+ if data.Buylink == nil || *data.Buylink == "" { *data.Buylink = "https://arimelody.me" }
if global.GetRelease(data.ID) != nil {
http.Error(w, fmt.Sprintf("Release %s already exists\n", data.ID), http.StatusBadRequest)
return
}
+ releaseDate := time.Time{}
+ if *data.ReleaseDate == "" {
+ http.Error(w, "Release date cannot be empty\n", http.StatusBadRequest)
+ return
+ } else if data.ReleaseDate != nil {
+ releaseDate, err = time.Parse("2006-01-02T15:04", *data.ReleaseDate)
+ if err != nil {
+ http.Error(w, "Invalid release date", http.StatusBadRequest)
+ return
+ }
+ }
+
var release = model.Release{
ID: data.ID,
- Visible: data.Visible,
- Title: data.Title,
- Description: data.Description,
- ReleaseType: data.ReleaseType,
- ReleaseDate: data.ReleaseDate,
- Artwork: data.Artwork,
- Buyname: data.Buyname,
- Buylink: data.Buylink,
+ Visible: *data.Visible,
+ Title: *data.Title,
+ Description: *data.Description,
+ ReleaseType: *data.ReleaseType,
+ ReleaseDate: releaseDate,
+ Artwork: *data.Artwork,
+ Buyname: *data.Buyname,
+ Buylink: *data.Buylink,
Links: []*model.Link{},
Credits: []*model.Credit{},
Tracks: []*model.Track{},
@@ -153,41 +167,46 @@ func UpdateRelease() http.Handler {
return
}
- if data.ID == "" { data.ID = release.ID }
-
- if data.Title == "" {
- http.Error(w, "Release title cannot be blank\n", http.StatusBadRequest)
- return
+ var update = *release
+ if data.ID != "" { update.ID = data.ID }
+ if data.Visible != nil { update.Visible = *data.Visible }
+ if data.Title != nil { update.Title = *data.Title }
+ if data.Description != nil { update.Description = *data.Description }
+ if data.ReleaseType != nil { update.ReleaseType = *data.ReleaseType }
+ if data.ReleaseDate != nil {
+ newDate, err := time.Parse("2006-01-02T15:04", *data.ReleaseDate)
+ if err != nil {
+ http.Error(w, "Invalid release date", http.StatusBadRequest)
+ return
+ }
+ update.ReleaseDate = newDate
}
-
- var new_release = model.Release{
- ID: data.ID,
- Visible: data.Visible,
- Title: data.Title,
- Description: data.Description,
- ReleaseType: data.ReleaseType,
- ReleaseDate: data.ReleaseDate,
- Artwork: data.Artwork,
- Buyname: data.Buyname,
- Buylink: data.Buylink,
+ if data.Artwork != nil { update.Artwork = *data.Artwork }
+ if data.Buyname != nil {
+ if *data.Buyname == "" {
+ http.Error(w, "Release buy name cannot be empty", http.StatusBadRequest)
+ return
+ }
+ update.Buyname = *data.Buyname
}
+ if data.Buylink != nil { update.Buylink = *data.Buylink }
- err = controller.UpdateReleaseDB(global.DB, release)
+ err = controller.UpdateReleaseDB(global.DB, &update)
if err != nil {
fmt.Printf("Failed to update release %s: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
- release.ID = new_release.ID
- release.Visible = new_release.Visible
- release.Title = new_release.Title
- release.Description = new_release.Description
- release.ReleaseType = new_release.ReleaseType
- release.ReleaseDate = new_release.ReleaseDate
- release.Artwork = new_release.Artwork
- release.Buyname = new_release.Buyname
- release.Buylink = new_release.Buylink
+ release.ID = update.ID
+ release.Visible = update.Visible
+ release.Title = update.Title
+ release.Description = update.Description
+ release.ReleaseType = update.ReleaseType
+ release.ReleaseDate = update.ReleaseDate
+ release.Artwork = update.Artwork
+ release.Buyname = update.Buyname
+ release.Buylink = update.Buylink
w.Header().Add("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(release)
@@ -195,6 +214,7 @@ func UpdateRelease() http.Handler {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return
}
+ return
}
if len(segments) == 2 {
diff --git a/api/track.go b/api/track.go
index cf0cab3..a5fc03d 100644
--- a/api/track.go
+++ b/api/track.go
@@ -122,10 +122,7 @@ func UpdateTrack() http.Handler {
data.ID = trackID
- if data.Title == "" {
- http.Error(w, "Track title cannot be blank\n", http.StatusBadRequest)
- return
- }
+ if data.Title == "" { data.Title = track.Title }
err = controller.UpdateTrackDB(global.DB, &data)
if err != nil {
diff --git a/discord/discord.go b/discord/discord.go
index 5190b09..46ca7f7 100644
--- a/discord/discord.go
+++ b/discord/discord.go
@@ -57,7 +57,7 @@ type (
AuthInfoResponse struct {
Application struct {
- Id string `json:"id"`
+ ID string `json:"id"`
Name string `json:"name"`
Icon string `json:"icon"`
Description string `json:"description"`
@@ -72,7 +72,7 @@ type (
}
DiscordUser struct {
- Id string `json:"id"`
+ ID string `json:"id"`
Username string `json:"username"`
Avatar string `json:"avatar"`
Discriminator string `json:"discriminator"`
diff --git a/music/controller/release.go b/music/controller/release.go
index 55eb51a..9e70aa7 100644
--- a/music/controller/release.go
+++ b/music/controller/release.go
@@ -76,7 +76,7 @@ func CreateReleaseDB(db *sqlx.DB, release *model.Release) error {
release.Title,
release.Description,
release.ReleaseType,
- release.ReleaseDate.Format("2-Jan-2006"),
+ release.ReleaseDate.Format("2006-01-02 15:04:05"),
release.Artwork,
release.Buyname,
release.Buylink,
@@ -91,15 +91,14 @@ func CreateReleaseDB(db *sqlx.DB, release *model.Release) error {
func UpdateReleaseDB(db *sqlx.DB, release *model.Release) error {
_, err := db.Exec(
"UPDATE musicrelease SET "+
- "visible=$2, title=$3, description=$4, type=$5, release_date=$6, artwork=$7, buyname=$8, buylink=$9) "+
- "VALUES ($2, $3, $4, $5, $6, $7, $8, $9) "+
+ "visible=$2, title=$3, description=$4, type=$5, release_date=$6, artwork=$7, buyname=$8, buylink=$9 "+
"WHERE id=$1",
release.ID,
release.Visible,
release.Title,
release.Description,
release.ReleaseType,
- release.ReleaseDate.Format("2-Jan-2006"),
+ release.ReleaseDate.Format("2006-01-02 15:04:05"),
release.Artwork,
release.Buyname,
release.Buylink,
diff --git a/music/model/artist.go b/music/model/artist.go
index 1956e3a..64e52d9 100644
--- a/music/model/artist.go
+++ b/music/model/artist.go
@@ -2,13 +2,17 @@ package model
type (
Artist struct {
- ID string `json:"id"`
+ ID string `json:"id"`
Name string `json:"name"`
Website string `json:"website"`
Avatar string `json:"avatar"`
}
)
+func (artist Artist) GetWebsite() string {
+ return artist.Website
+}
+
func (artist Artist) GetAvatar() string {
if artist.Avatar == "" {
return "/img/default-avatar.png"
diff --git a/music/model/release.go b/music/model/release.go
index a23166d..47dce6e 100644
--- a/music/model/release.go
+++ b/music/model/release.go
@@ -8,18 +8,18 @@ import (
type (
ReleaseType string
Release struct {
- ID string `json:"id"`
- Visible bool `json:"visible"`
- Title string `json:"title"`
- Description string `json:"description"`
- ReleaseType ReleaseType `json:"type" db:"type"`
- ReleaseDate time.Time `json:"releaseDate" db:"release_date"`
- Artwork string `json:"artwork"`
- Buyname string `json:"buyname"`
- Buylink string `json:"buylink"`
- Links []*Link `json:"links"`
- Credits []*Credit `json:"credits"`
- Tracks []*Track `json:"tracks"`
+ ID string `json:"id"`
+ Visible bool `json:"visible"`
+ Title string `json:"title"`
+ Description string `json:"description"`
+ ReleaseType ReleaseType `json:"type" db:"type"`
+ ReleaseDate time.Time `json:"releaseDate" db:"release_date"`
+ Artwork string `json:"artwork"`
+ Buyname string `json:"buyname"`
+ Buylink string `json:"buylink"`
+ Links []*Link `json:"links"`
+ Credits []*Credit `json:"credits"`
+ Tracks []*Track `json:"tracks"`
}
)
@@ -32,11 +32,8 @@ const (
// GETTERS
-func (release Release) GetArtwork() string {
- if release.Artwork == "" {
- return "/img/default-cover-art.png"
- }
- return release.Artwork
+func (release Release) TextReleaseDate() string {
+ return release.ReleaseDate.Format("2006-01-02T15:04")
}
func (release Release) PrintReleaseDate() string {
@@ -47,6 +44,13 @@ func (release Release) GetReleaseYear() int {
return release.ReleaseDate.Year()
}
+func (release Release) GetArtwork() string {
+ if release.Artwork == "" {
+ return "/img/default-cover-art.png"
+ }
+ return release.Artwork
+}
+
func (release Release) IsSingle() bool {
return len(release.Tracks) == 1;
}
diff --git a/music/view/release.go b/music/view/release.go
index 65ddc3a..11b18e8 100644
--- a/music/view/release.go
+++ b/music/view/release.go
@@ -12,6 +12,19 @@ import (
"arimelody.me/arimelody.me/music/model"
)
+type (
+ gatewayTrack struct {
+ *model.Track
+ Lyrics template.HTML
+ Number int
+ }
+
+ gatewayRelease struct {
+ *model.Release
+ Tracks []gatewayTrack
+ }
+)
+
// HTTP HANDLERS
func ServeRelease() http.Handler {
@@ -51,19 +64,6 @@ func ServeGateway() http.Handler {
return
}
- type (
- GatewayTrack struct {
- *model.Track
- Lyrics template.HTML
- Number int
- }
-
- GatewayRelease struct {
- *model.Release
- Tracks []GatewayTrack
- }
- )
-
id := r.URL.Path[1:]
release := global.GetRelease(id)
if release == nil {
@@ -71,9 +71,9 @@ func ServeGateway() http.Handler {
return
}
- tracks := []GatewayTrack{}
+ tracks := []gatewayTrack{}
for i, track := range release.Tracks {
- tracks = append([]GatewayTrack{GatewayTrack{
+ tracks = append([]gatewayTrack{{
Track: track,
Lyrics: template.HTML(strings.Replace(track.Lyrics, "\n", "
", -1)),
Number: len(release.Tracks) - i,
@@ -87,9 +87,9 @@ func ServeGateway() http.Handler {
return
}
- lrw := global.LoggingResponseWriter{w, http.StatusOK}
+ lrw := global.LoggingResponseWriter{ResponseWriter: w, Code: http.StatusOK}
- global.ServeTemplate("music-gateway.html", GatewayRelease{release, tracks}).ServeHTTP(&lrw, r)
+ global.ServeTemplate("music-gateway.html", gatewayRelease{release, tracks}).ServeHTTP(&lrw, r)
if lrw.Code != http.StatusOK {
fmt.Printf("Error rendering music gateway for %s\n", id)
diff --git a/public/script/silver.min.js b/public/script/silver.min.js
new file mode 100644
index 0000000..47d4cbb
--- /dev/null
+++ b/public/script/silver.min.js
@@ -0,0 +1 @@
+export default class Stateful{#e;#t=[];constructor(e){this.#e=e}get(){return this.#e}set(e){let t=this.#e;this.#e=e;for(let s in this.#t)this.#t[s](e,t)}update(e){this.set(e(this.#e))}onUpdate(e){return this.#t.push(e),e}removeListener(e){this.#t=this.#t.filter((t=>t!==e))}}
diff --git a/schema.sql b/schema.sql
index a8dd65e..1a7533c 100644
--- a/schema.sql
+++ b/schema.sql
@@ -18,7 +18,7 @@ CREATE TABLE public.musicrelease (
title text NOT NULL,
description text,
type text,
- release_date DATE NOT NULL,
+ release_date TIMESTAMP NOT NULL,
artwork text,
buyname text,
buylink text
diff --git a/views/admin/login.html b/views/admin/login.html
deleted file mode 100644
index af35e2e..0000000
--- a/views/admin/login.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{{define "head"}}
-
admin - ari melody 💫
-
-
-
-{{end}}
-
-{{define "content"}}
-
-
- Log in with Discord.
-
-
-
-
-{{end}}
diff --git a/views/music-gateway.html b/views/music-gateway.html
index 821a552..92e3b21 100644
--- a/views/music-gateway.html
+++ b/views/music-gateway.html
@@ -88,8 +88,8 @@
{{range .Credits}}
{{$Artist := .Artist}}
- {{if $Artist.Website}}
- - {{$Artist.Name}}: {{.Role}}
+ {{if $Artist.GetWebsite}}
+ - {{$Artist.Name}}: {{.Role}}
{{else}}
- {{$Artist.Name}}: {{.Role}}
{{end}}