turns out rewriting all of your database code takes a while
This commit is contained in:
parent
1998a36d6d
commit
965d6f5c3e
|
@ -51,9 +51,8 @@
|
|||
|
||||
makeMagicList(creditList, ".credit");
|
||||
|
||||
creditList.addEventListener("htmx:afterSwap", e => {
|
||||
const el = creditList.children[creditList.children.length - 1];
|
||||
|
||||
function rigCredit(el) {
|
||||
console.log(el);
|
||||
const artistID = el.dataset.artist;
|
||||
const deleteBtn = el.querySelector("a.delete");
|
||||
|
||||
|
@ -64,6 +63,12 @@
|
|||
|
||||
el.addEventListener("dragstart", () => { el.classList.add("moving") });
|
||||
el.addEventListener("dragend", () => { el.classList.remove("moving") });
|
||||
}
|
||||
|
||||
[...creditList.querySelectorAll(".credit")].map(rigCredit);
|
||||
|
||||
creditList.addEventListener("htmx:afterSwap", () => {
|
||||
rigCredit(creditList.children[creditList.children.length - 1]);
|
||||
});
|
||||
|
||||
container.showModal();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<li class="track" data-track="{{.ID}}" data-title="{{.Title}}" data-number="{{.Number}}" draggable="true">
|
||||
<li class="track" data-track="{{.ID}}" data-title="{{.Title}}" data-number="0" draggable="true">
|
||||
<div>
|
||||
<p class="track-name">
|
||||
<span class="track-number">{{.Number}}</span>
|
||||
<span class="track-number">0</span>
|
||||
{{.Title}}
|
||||
</p>
|
||||
<a class="delete">Delete</a>
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
|
||||
"arimelody.me/arimelody.me/discord"
|
||||
"arimelody.me/arimelody.me/global"
|
||||
musicController "arimelody.me/arimelody.me/music/controller"
|
||||
musicModel "arimelody.me/arimelody.me/music/model"
|
||||
musicDB "arimelody.me/arimelody.me/music/controller"
|
||||
)
|
||||
|
||||
type loginData struct {
|
||||
|
@ -29,18 +29,6 @@ func Handler() http.Handler {
|
|||
mux.Handle("/static/", http.StripPrefix("/static", staticHandler()))
|
||||
mux.Handle("/release/", MustAuthorise(http.StripPrefix("/release", serveRelease())))
|
||||
mux.Handle("/track/", MustAuthorise(http.StripPrefix("/track", serveTrack())))
|
||||
mux.Handle("/createtrack", MustAuthorise(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
track := musicModel.Track{ Title: "Untitled Track" }
|
||||
trackID, err := musicController.CreateTrackDB(global.DB, &track)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create track: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
track.ID = trackID
|
||||
global.Tracks = append(global.Tracks, &track)
|
||||
http.Redirect(w, r, fmt.Sprintf("/admin/track/%s", trackID), http.StatusTemporaryRedirect)
|
||||
})))
|
||||
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/" {
|
||||
http.NotFound(w, r)
|
||||
|
@ -54,30 +42,55 @@ func Handler() http.Handler {
|
|||
}
|
||||
|
||||
type (
|
||||
Track struct {
|
||||
*musicModel.Track
|
||||
Lyrics template.HTML
|
||||
// Number int
|
||||
}
|
||||
IndexData struct {
|
||||
Releases []*musicModel.Release
|
||||
Releases []musicModel.FullRelease
|
||||
Artists []*musicModel.Artist
|
||||
Tracks []Track
|
||||
Tracks []musicModel.DisplayTrack
|
||||
}
|
||||
)
|
||||
|
||||
var tracks = []Track{}
|
||||
for _, track := range global.Tracks {
|
||||
if track.Release != nil { continue }
|
||||
tracks = append(tracks, Track{
|
||||
dbReleases, err := musicDB.GetAllReleases(global.DB)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull releases: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
releases := []musicModel.FullRelease{}
|
||||
for _, release := range dbReleases {
|
||||
fullRelease, err := musicDB.GetFullRelease(global.DB, release)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull full release data for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
releases = append(releases, *fullRelease)
|
||||
}
|
||||
|
||||
artists, err := musicDB.GetAllArtists(global.DB)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull artists: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
dbTracks, err := musicDB.GetOrphanTracks(global.DB)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull orphan tracks: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var tracks = []musicModel.DisplayTrack{}
|
||||
for _, track := range dbTracks {
|
||||
tracks = append(tracks, musicModel.DisplayTrack{
|
||||
Track: track,
|
||||
Lyrics: template.HTML(strings.Replace(track.Lyrics, "\n", "<br>", -1)),
|
||||
})
|
||||
}
|
||||
|
||||
err := pages["index"].Execute(w, IndexData{
|
||||
Releases: global.Releases,
|
||||
Artists: global.Artists,
|
||||
err = pages["index"].Execute(w, IndexData{
|
||||
Releases: releases,
|
||||
Artists: artists,
|
||||
Tracks: tracks,
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -2,83 +2,79 @@ package admin
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"arimelody.me/arimelody.me/global"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
)
|
||||
|
||||
type (
|
||||
gatewayTrack struct {
|
||||
*model.Track
|
||||
Lyrics template.HTML
|
||||
Number int
|
||||
}
|
||||
|
||||
gatewayRelease struct {
|
||||
*model.Release
|
||||
Tracks []gatewayTrack
|
||||
}
|
||||
controller "arimelody.me/arimelody.me/music/controller"
|
||||
)
|
||||
|
||||
func serveRelease() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
slices := strings.Split(r.URL.Path[1:], "/")
|
||||
id := slices[0]
|
||||
release := global.GetRelease(id)
|
||||
releaseID := slices[0]
|
||||
release, err := controller.GetRelease(global.DB, releaseID)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull release %s: %s\n", releaseID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if release == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
authorised := GetSession(r) != nil
|
||||
if !authorised && !release.Visible {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
fullRelease, err := controller.GetFullRelease(global.DB, release)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull full release data for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if len(slices) > 1 {
|
||||
switch slices[1] {
|
||||
case "editcredits":
|
||||
serveEditCredits(release).ServeHTTP(w, r)
|
||||
serveEditCredits(fullRelease).ServeHTTP(w, r)
|
||||
return
|
||||
case "addcredit":
|
||||
serveAddCredit(release).ServeHTTP(w, r)
|
||||
serveAddCredit(fullRelease).ServeHTTP(w, r)
|
||||
return
|
||||
case "newcredit":
|
||||
serveNewCredit().ServeHTTP(w, r)
|
||||
return
|
||||
case "editlinks":
|
||||
serveEditLinks(release).ServeHTTP(w, r)
|
||||
serveEditLinks(fullRelease).ServeHTTP(w, r)
|
||||
return
|
||||
case "edittracks":
|
||||
serveEditTracks(release).ServeHTTP(w, r)
|
||||
serveEditTracks(fullRelease).ServeHTTP(w, r)
|
||||
return
|
||||
case "addtrack":
|
||||
serveAddTrack(release).ServeHTTP(w, r)
|
||||
serveAddTrack(fullRelease).ServeHTTP(w, r)
|
||||
return
|
||||
case "newtrack":
|
||||
serveNewTrack(release).ServeHTTP(w, r)
|
||||
serveNewTrack().ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
tracks := []gatewayTrack{}
|
||||
for i, track := range release.Tracks {
|
||||
tracks = append(tracks, gatewayTrack{
|
||||
Track: track,
|
||||
Lyrics: template.HTML(strings.Replace(track.Lyrics, "\n", "<br>", -1)),
|
||||
Number: i + 1,
|
||||
})
|
||||
}
|
||||
|
||||
err := pages["release"].Execute(w, gatewayRelease{release, tracks})
|
||||
err = pages["release"].Execute(w, fullRelease)
|
||||
if err != nil {
|
||||
fmt.Printf("Error rendering admin release page for %s: %s\n", id, err)
|
||||
fmt.Printf("Error rendering admin release page for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func serveEditCredits(release *model.Release) http.Handler {
|
||||
func serveEditCredits(release *model.FullRelease) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
err := components["editcredits"].Execute(w, release)
|
||||
|
@ -89,20 +85,13 @@ func serveEditCredits(release *model.Release) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func serveAddCredit(release *model.Release) http.Handler {
|
||||
func serveAddCredit(release *model.FullRelease) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var artists = []*model.Artist{}
|
||||
for _, artist := range global.Artists {
|
||||
var exists = false
|
||||
for _, credit := range release.Credits {
|
||||
if credit.Artist == artist {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
artists = append(artists, artist)
|
||||
}
|
||||
artists, err := controller.GetArtistsNotOnRelease(global.DB, release.Release)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull artists not on %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
type response struct {
|
||||
|
@ -111,7 +100,7 @@ func serveAddCredit(release *model.Release) http.Handler {
|
|||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
err := components["addcredit"].Execute(w, response{
|
||||
err = components["addcredit"].Execute(w, response{
|
||||
ReleaseID: release.ID,
|
||||
Artists: artists,
|
||||
})
|
||||
|
@ -124,23 +113,28 @@ func serveAddCredit(release *model.Release) http.Handler {
|
|||
|
||||
func serveNewCredit() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
artist := global.GetArtist(strings.Split(r.URL.Path, "/")[3])
|
||||
artistID := strings.Split(r.URL.Path, "/")[3]
|
||||
artist, err := controller.GetArtist(global.DB, artistID)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull artists %s: %s\n", artistID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if artist == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
err := components["newcredit"].Execute(w, artist)
|
||||
err = components["newcredit"].Execute(w, artist)
|
||||
if err != nil {
|
||||
fmt.Printf("Error rendering new credit component for %s: %s\n", artist.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func serveEditLinks(release *model.Release) http.Handler {
|
||||
func serveEditLinks(release *model.FullRelease) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
err := components["editlinks"].Execute(w, release)
|
||||
|
@ -148,49 +142,27 @@ func serveEditLinks(release *model.Release) http.Handler {
|
|||
fmt.Printf("Error rendering edit links component for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func serveEditTracks(release *model.Release) http.Handler {
|
||||
func serveEditTracks(release *model.FullRelease) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
type Track struct {
|
||||
*model.Track
|
||||
Number int
|
||||
}
|
||||
type Release struct {
|
||||
*model.Release
|
||||
Tracks []Track
|
||||
}
|
||||
var data = Release{ release, []Track{} }
|
||||
for i, track := range release.Tracks {
|
||||
data.Tracks = append(data.Tracks, Track{track, i + 1})
|
||||
}
|
||||
|
||||
err := components["edittracks"].Execute(w, data)
|
||||
err := components["edittracks"].Execute(w, release)
|
||||
if err != nil {
|
||||
fmt.Printf("Error rendering edit tracks component for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
func serveAddTrack(release *model.Release) http.Handler {
|
||||
func serveAddTrack(release *model.FullRelease) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var tracks = []*model.Track{}
|
||||
for _, track := range global.Tracks {
|
||||
var exists = false
|
||||
for _, t := range release.Tracks {
|
||||
if t == track {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
tracks = append(tracks, track)
|
||||
}
|
||||
tracks, err := controller.GetTracksNotOnRelease(global.DB, release.Release)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull tracks not on %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
type response struct {
|
||||
|
@ -199,7 +171,7 @@ func serveAddTrack(release *model.Release) http.Handler {
|
|||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
err := components["addtrack"].Execute(w, response{
|
||||
err = components["addtrack"].Execute(w, response{
|
||||
ReleaseID: release.ID,
|
||||
Tracks: tracks,
|
||||
})
|
||||
|
@ -211,24 +183,22 @@ func serveAddTrack(release *model.Release) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func serveNewTrack(release *model.Release) http.Handler {
|
||||
func serveNewTrack() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
track := global.GetTrack(strings.Split(r.URL.Path, "/")[3])
|
||||
trackID := strings.Split(r.URL.Path, "/")[3]
|
||||
track, err := controller.GetTrack(global.DB, trackID)
|
||||
if err != nil {
|
||||
fmt.Printf("Error rendering new track component for %s: %s\n", trackID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if track == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
type Track struct {
|
||||
*model.Track
|
||||
Number int
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
err := components["newtrack"].Execute(w, Track{
|
||||
track,
|
||||
len(release.Tracks) + 1,
|
||||
})
|
||||
err = components["newtrack"].Execute(w, track)
|
||||
if err != nil {
|
||||
fmt.Printf("Error rendering new track component for %s: %s\n", track.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const newReleaseBtn = document.getElementById("create-release");
|
||||
const newTrackBtn = document.getElementById("create-track");
|
||||
|
||||
newReleaseBtn.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
|
@ -22,3 +23,27 @@ newReleaseBtn.addEventListener("click", event => {
|
|||
console.error(err);
|
||||
});
|
||||
});
|
||||
|
||||
newTrackBtn.addEventListener("click", event => {
|
||||
event.preventDefault();
|
||||
const title = prompt("Enter an title for this track:");
|
||||
if (title == null || title == "") return;
|
||||
|
||||
fetch("/api/v1/track", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({title})
|
||||
}).then(res => {
|
||||
res.text().then(text => {
|
||||
if (res.ok) {
|
||||
location = "/admin/track/" + text;
|
||||
} else {
|
||||
alert("Request failed: " + text);
|
||||
console.error(text);
|
||||
}
|
||||
})
|
||||
}).catch(err => {
|
||||
alert("Failed to create release. Check the console for details.");
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,19 +6,48 @@ import (
|
|||
"strings"
|
||||
|
||||
"arimelody.me/arimelody.me/global"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
"arimelody.me/arimelody.me/music/controller"
|
||||
)
|
||||
|
||||
func serveTrack() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
slices := strings.Split(r.URL.Path[1:], "/")
|
||||
id := slices[0]
|
||||
track := global.GetTrack(id)
|
||||
track, err := music.GetTrack(global.DB, id)
|
||||
if err != nil {
|
||||
fmt.Printf("Error rendering admin track page for %s: %s\n", id, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if track == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
err := pages["track"].Execute(w, track)
|
||||
dbReleases, err := music.GetTrackReleases(global.DB, track)
|
||||
if err != nil {
|
||||
fmt.Printf("Error rendering admin track page for %s: %s\n", id, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
releases := []model.FullRelease{}
|
||||
for _, release := range dbReleases {
|
||||
fullRelease, err := music.GetFullRelease(global.DB, release)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull full release data for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
releases = append(releases, *fullRelease)
|
||||
}
|
||||
|
||||
type Track struct {
|
||||
*model.Track
|
||||
Releases []model.FullRelease
|
||||
}
|
||||
|
||||
err = pages["track"].Execute(w, Track{ Track: track, Releases: releases })
|
||||
if err != nil {
|
||||
fmt.Printf("Error rendering admin track page for %s: %s\n", id, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
|
|
|
@ -43,8 +43,10 @@
|
|||
<h2>Featured in</h2>
|
||||
</div>
|
||||
<div class="card releases">
|
||||
{{if .Release}}
|
||||
{{block "release" .Release}}{{end}}
|
||||
{{if .Releases}}
|
||||
{{range .Releases}}
|
||||
{{block "release" .}}{{end}}
|
||||
{{end}}
|
||||
{{else}}
|
||||
<p>This track isn't bound to a release.</p>
|
||||
{{end}}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<div class="card-title">
|
||||
<h1>Releases</h1>
|
||||
<a href="/admin/createrelease" class="create-btn" id="create-release">Create New</a>
|
||||
<a class="create-btn" id="create-release">Create New</a>
|
||||
</div>
|
||||
<div class="card releases">
|
||||
{{range .Releases}}
|
||||
|
@ -22,7 +22,7 @@
|
|||
|
||||
<div class="card-title">
|
||||
<h1>Artists</h1>
|
||||
<a href="/admin/createartist" class="create-btn">Create New</a>
|
||||
<a class="create-btn">Create New</a>
|
||||
</div>
|
||||
<div class="card artists">
|
||||
{{range $Artist := .Artists}}
|
||||
|
@ -38,7 +38,7 @@
|
|||
|
||||
<div class="card-title">
|
||||
<h1>Tracks</h1>
|
||||
<a href="/admin/createtrack" class="create-btn">Create New</a>
|
||||
<a class="create-btn" id="create-track">Create New</a>
|
||||
</div>
|
||||
<div class="card tracks">
|
||||
<p><em>"Orphaned" tracks that have not yet been bound to a release.</em></p>
|
||||
|
@ -47,11 +47,6 @@
|
|||
<div class="track">
|
||||
<h2 class="track-title">
|
||||
<a href="/admin/track/{{$Track.ID}}">{{$Track.Title}}</a>
|
||||
{{if $Track.Release}}
|
||||
<small class="track-album">{{$Track.Release.Title}}</small>
|
||||
{{else}}
|
||||
<small class="track-album empty">(no release)</small>
|
||||
{{end}}
|
||||
</h2>
|
||||
{{if $Track.Description}}
|
||||
<p class="track-description">{{$Track.Description}}</p>
|
||||
|
|
49
api/api.go
49
api/api.go
|
@ -1,9 +1,13 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"arimelody.me/arimelody.me/admin"
|
||||
"arimelody.me/arimelody.me/global"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
music "arimelody.me/arimelody.me/music/view"
|
||||
)
|
||||
|
||||
|
@ -13,16 +17,25 @@ func Handler() http.Handler {
|
|||
// ARTIST ENDPOINTS
|
||||
|
||||
mux.Handle("/v1/artist/", http.StripPrefix("/v1/artist", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var artistID = strings.Split(r.URL.Path[1:], "/")[0]
|
||||
var artist model.Artist
|
||||
err := global.DB.Get(&artist, "SELECT * FROM artist WHERE id=$1", artistID)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Error while retrieving artist %s: %s\n", artistID, err)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
// GET /api/v1/artist/{id}
|
||||
ServeArtist().ServeHTTP(w, r)
|
||||
ServeArtist(artist).ServeHTTP(w, r)
|
||||
case http.MethodPut:
|
||||
// PUT /api/v1/artist/{id} (admin)
|
||||
admin.MustAuthorise(UpdateArtist()).ServeHTTP(w, r)
|
||||
admin.MustAuthorise(UpdateArtist(artist)).ServeHTTP(w, r)
|
||||
case http.MethodDelete:
|
||||
// DELETE /api/v1/artist/{id} (admin)
|
||||
admin.MustAuthorise(DeleteArtist()).ServeHTTP(w, r)
|
||||
admin.MustAuthorise(DeleteArtist(artist)).ServeHTTP(w, r)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
@ -43,16 +56,25 @@ func Handler() http.Handler {
|
|||
// RELEASE ENDPOINTS
|
||||
|
||||
mux.Handle("/v1/music/", http.StripPrefix("/v1/music", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var releaseID = strings.Split(r.URL.Path[1:], "/")[0]
|
||||
var release model.Release
|
||||
err := global.DB.Get(&release, "SELECT * FROM musicrelease WHERE id=$1", releaseID)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Error while retrieving release %s: %s\n", releaseID, err)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
// GET /api/v1/music/{id}
|
||||
music.ServeRelease().ServeHTTP(w, r)
|
||||
music.ServeRelease(release).ServeHTTP(w, r)
|
||||
case http.MethodPut:
|
||||
// PUT /api/v1/music/{id} (admin)
|
||||
admin.MustAuthorise(UpdateRelease()).ServeHTTP(w, r)
|
||||
admin.MustAuthorise(UpdateRelease(release)).ServeHTTP(w, r)
|
||||
case http.MethodDelete:
|
||||
// DELETE /api/v1/music/{id} (admin)
|
||||
admin.MustAuthorise(DeleteRelease()).ServeHTTP(w, r)
|
||||
admin.MustAuthorise(DeleteRelease(release)).ServeHTTP(w, r)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
@ -73,16 +95,25 @@ func Handler() http.Handler {
|
|||
// TRACK ENDPOINTS
|
||||
|
||||
mux.Handle("/v1/track/", http.StripPrefix("/v1/track", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var trackID = strings.Split(r.URL.Path[1:], "/")[0]
|
||||
var track model.Track
|
||||
err := global.DB.Get(&track, "SELECT * FROM musictrack WHERE id=$1", trackID)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Error while retrieving track %s: %s\n", trackID, err)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
// GET /api/v1/track/{id} (admin)
|
||||
admin.MustAuthorise(ServeTrack()).ServeHTTP(w, r)
|
||||
admin.MustAuthorise(ServeTrack(track)).ServeHTTP(w, r)
|
||||
case http.MethodPut:
|
||||
// PUT /api/v1/track/{id} (admin)
|
||||
admin.MustAuthorise(UpdateTrack()).ServeHTTP(w, r)
|
||||
admin.MustAuthorise(UpdateTrack(track)).ServeHTTP(w, r)
|
||||
case http.MethodDelete:
|
||||
// DELETE /api/v1/track/{id} (admin)
|
||||
admin.MustAuthorise(DeleteTrack()).ServeHTTP(w, r)
|
||||
admin.MustAuthorise(DeleteTrack(track)).ServeHTTP(w, r)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
|
183
api/artist.go
183
api/artist.go
|
@ -4,10 +4,10 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"arimelody.me/arimelody.me/global"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
controller "arimelody.me/arimelody.me/music/controller"
|
||||
)
|
||||
|
||||
type artistJSON struct {
|
||||
|
@ -19,76 +19,60 @@ type artistJSON struct {
|
|||
|
||||
func ServeAllArtists() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
err := json.NewEncoder(w).Encode(global.Artists)
|
||||
var artists = []*model.Artist{}
|
||||
err := global.DB.Select(&artists, "SELECT * FROM artist")
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to serve all artists: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
err = json.NewEncoder(w).Encode(artists)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func ServeArtist() http.Handler {
|
||||
func ServeArtist(artist model.Artist) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
ServeAllArtists().ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
type (
|
||||
creditJSON struct {
|
||||
Role string `json:"role"`
|
||||
Primary bool `json:"primary"`
|
||||
Release string `json:"release"`
|
||||
Role string `json:"role"`
|
||||
Primary bool `json:"primary"`
|
||||
}
|
||||
artistJSON struct {
|
||||
model.Artist
|
||||
Credits map[string]creditJSON `json:"credits"`
|
||||
}
|
||||
)
|
||||
var artist = artistJSON{}
|
||||
|
||||
artist.ID = r.URL.Path[1:]
|
||||
var a = global.GetArtist(artist.ID)
|
||||
if a == nil {
|
||||
http.NotFound(w, r)
|
||||
var credits = map[string]creditJSON{}
|
||||
err := global.DB.Select(&credits, "SELECT release,role,is_primary FROM musiccredit WHERE id=$1", artist.ID)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to retrieve artist credits for %s: %s\n", artist.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
artist.Name = a.Name
|
||||
artist.Website = a.Website
|
||||
artist.Credits = make(map[string]creditJSON)
|
||||
|
||||
for _, release := range global.Releases {
|
||||
for _, credit := range release.Credits {
|
||||
if credit.Artist.ID != artist.ID {
|
||||
continue
|
||||
}
|
||||
artist.Credits[release.ID] = creditJSON{
|
||||
Role: credit.Role,
|
||||
Primary: credit.Primary,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
err := json.NewEncoder(w).Encode(artist)
|
||||
err = json.NewEncoder(w).Encode(artistJSON{
|
||||
Artist: artist,
|
||||
Credits: credits,
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func CreateArtist() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
var data artistJSON
|
||||
err := json.NewDecoder(r.Body).Decode(&data)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create artist: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
@ -102,11 +86,6 @@ func CreateArtist() http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
if global.GetArtist(data.ID) != nil {
|
||||
http.Error(w, fmt.Sprintf("Artist %s already exists\n", data.ID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var artist = model.Artist{
|
||||
ID: data.ID,
|
||||
Name: *data.Name,
|
||||
|
@ -114,116 +93,66 @@ func CreateArtist() http.Handler {
|
|||
Avatar: *data.Avatar,
|
||||
}
|
||||
|
||||
err = controller.CreateArtistDB(global.DB, &artist)
|
||||
_, err = global.DB.Exec(
|
||||
"INSERT INTO artist (id, name, website, avatar) "+
|
||||
"VALUES ($1, $2, $3, $4)",
|
||||
artist.ID,
|
||||
artist.Name,
|
||||
artist.Website,
|
||||
artist.Avatar)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create artist %s: %s\n", artist.ID, err)
|
||||
if strings.Contains(err.Error(), "duplicate key") {
|
||||
http.Error(w, fmt.Sprintf("Artist %s already exists\n", data.ID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fmt.Printf("FATAL: Failed to create artist %s: %s\n", artist.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
global.Artists = append(global.Artists, &artist)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
err = json.NewEncoder(w).Encode(artist)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateArtist() http.Handler {
|
||||
func UpdateArtist(artist model.Artist) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPut {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if r.URL.Path == "/" {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var data artistJSON
|
||||
err := json.NewDecoder(r.Body).Decode(&data)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update artist: %s\n", err)
|
||||
fmt.Printf("FATAL: Failed to update artist: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var artistID = r.URL.Path[1:]
|
||||
var artist = global.GetArtist(artistID)
|
||||
if artist == nil {
|
||||
http.Error(w, fmt.Sprintf("Artist %s does not exist\n", artistID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if data.ID != "" { artist.ID = data.ID }
|
||||
if data.Name != nil { artist.Name = *data.Name }
|
||||
if data.Website != nil { artist.Website = *data.Website }
|
||||
if data.Avatar != nil { artist.Avatar = *data.Avatar }
|
||||
|
||||
var update = *artist
|
||||
|
||||
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, &update)
|
||||
_, err = global.DB.Exec(
|
||||
"UPDATE artist "+
|
||||
"SET name=$2, website=$3, avatar=$4 "+
|
||||
"WHERE id=$1",
|
||||
artist.ID,
|
||||
artist.Name,
|
||||
artist.Website,
|
||||
artist.Avatar)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update artist %s: %s\n", artist.ID, err)
|
||||
fmt.Printf("FATAL: Failed to update artist %s: %s\n", artist.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteArtist() http.Handler {
|
||||
func DeleteArtist(artist model.Artist) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if r.URL.Path == "/" {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var artistID = r.URL.Path[1:]
|
||||
var artist = global.GetArtist(artistID)
|
||||
if artist == nil {
|
||||
http.Error(w, fmt.Sprintf("Artist %s does not exist\n", artistID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err := controller.DeleteArtistDB(global.DB, artist)
|
||||
_, err := global.DB.Exec(
|
||||
"DELETE FROM artist "+
|
||||
"WHERE id=$1",
|
||||
artist.ID)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to delete artist %s: %s\n", artist.ID, err)
|
||||
fmt.Printf("FATAL: Failed to delete artist %s: %s\n", artist.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
global.Artists = func () []*model.Artist {
|
||||
var artists = []*model.Artist{}
|
||||
for _, a := range global.Artists {
|
||||
if a.ID == artist.ID { continue }
|
||||
artists = append(artists, a)
|
||||
}
|
||||
return artists
|
||||
}()
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(fmt.Sprintf("Artist %s has been deleted\n", artist.ID)))
|
||||
})
|
||||
}
|
||||
|
|
449
api/release.go
449
api/release.go
|
@ -1,8 +1,6 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
|
@ -14,7 +12,6 @@ import (
|
|||
|
||||
"arimelody.me/arimelody.me/admin"
|
||||
"arimelody.me/arimelody.me/global"
|
||||
controller "arimelody.me/arimelody.me/music/controller"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
)
|
||||
|
||||
|
@ -32,39 +29,40 @@ type releaseBodyJSON struct {
|
|||
|
||||
func ServeCatalog() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
type CatalogItem struct {
|
||||
type catalogItem struct {
|
||||
ID string `json:"id"`
|
||||
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"`
|
||||
Links []*model.Link `json:"links"`
|
||||
}
|
||||
|
||||
catalog := []CatalogItem{}
|
||||
releases := []*model.Release{}
|
||||
err := global.DB.Select(&releases, "SELECT * FROM musicrelease ORDER BY release_date DESC")
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
catalog := []catalogItem{}
|
||||
authorised := admin.GetSession(r) != nil
|
||||
for _, release := range global.Releases {
|
||||
for _, release := range releases {
|
||||
if !release.Visible && !authorised {
|
||||
continue
|
||||
}
|
||||
catalog = append(catalog, CatalogItem{
|
||||
catalog = append(catalog, catalogItem{
|
||||
ID: release.ID,
|
||||
Title: release.Title,
|
||||
Description: release.Description,
|
||||
ReleaseType: release.ReleaseType,
|
||||
ReleaseDate: release.ReleaseDate,
|
||||
Artwork: release.Artwork,
|
||||
Buyname: release.Buyname,
|
||||
Buylink: release.Buylink,
|
||||
Links: release.Links,
|
||||
})
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
err := json.NewEncoder(w).Encode(catalog)
|
||||
err = json.NewEncoder(w).Encode(catalog)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -122,11 +120,6 @@ func CreateRelease() http.Handler {
|
|||
buylink := ""
|
||||
if data.Buylink != nil && *data.Buylink != "" { buylink = *data.Buylink }
|
||||
|
||||
if global.GetRelease(data.ID) != nil {
|
||||
http.Error(w, fmt.Sprintf("Release %s already exists\n", data.ID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var release = model.Release{
|
||||
ID: data.ID,
|
||||
Visible: false,
|
||||
|
@ -137,31 +130,42 @@ func CreateRelease() http.Handler {
|
|||
Artwork: artwork,
|
||||
Buyname: buyname,
|
||||
Buylink: buylink,
|
||||
Links: []*model.Link{},
|
||||
Credits: []*model.Credit{},
|
||||
Tracks: []*model.Track{},
|
||||
}
|
||||
|
||||
err = controller.CreateReleaseDB(global.DB, &release)
|
||||
_, err = global.DB.Exec(
|
||||
"INSERT INTO musicrelease "+
|
||||
"(id, visible, title, description, type, release_date, artwork, buyname, buylink) "+
|
||||
"VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
release.ID,
|
||||
release.Visible,
|
||||
release.Title,
|
||||
release.Description,
|
||||
release.ReleaseType,
|
||||
release.ReleaseDate.Format("2006-01-02 15:04:05"),
|
||||
release.Artwork,
|
||||
release.Buyname,
|
||||
release.Buylink)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "duplicate key") {
|
||||
http.Error(w, fmt.Sprintf("Release %s already exists\n", data.ID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fmt.Printf("Failed to create release %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
global.Releases = append([]*model.Release{&release}, global.Releases...)
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
err = json.NewEncoder(w).Encode(release)
|
||||
if err != nil {
|
||||
fmt.Printf("WARN: Release %s created, but failed to send JSON response: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateRelease() http.Handler {
|
||||
func UpdateRelease(release model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
http.NotFound(w, r)
|
||||
|
@ -170,124 +174,11 @@ func UpdateRelease() http.Handler {
|
|||
|
||||
segments := strings.Split(r.URL.Path[1:], "/")
|
||||
var releaseID = segments[0]
|
||||
var release = global.GetRelease(releaseID)
|
||||
if release == nil {
|
||||
http.Error(w, fmt.Sprintf("Release %s does not exist\n", releaseID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if len(segments) == 1 {
|
||||
var data releaseBodyJSON
|
||||
err := json.NewDecoder(r.Body).Decode(&data)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update release: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), 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
|
||||
}
|
||||
if data.Artwork != nil {
|
||||
if strings.Contains(*data.Artwork, ";base64,") {
|
||||
split := strings.Split(*data.Artwork, ";base64,")
|
||||
header := split[0]
|
||||
imageData, err := base64.StdEncoding.DecodeString(split[1])
|
||||
ext, _ := strings.CutPrefix(header, "data:image/")
|
||||
|
||||
switch ext {
|
||||
case "png":
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
default:
|
||||
http.Error(w, "Invalid image type. Allowed: .png, .jpg, .jpeg", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
artworkDirectory := filepath.Join("uploads", "musicart")
|
||||
// ensure directory exists
|
||||
os.MkdirAll(artworkDirectory, os.ModePerm)
|
||||
|
||||
imagePath := filepath.Join(artworkDirectory, fmt.Sprintf("%s.%s", update.ID, ext))
|
||||
file, err := os.Create(imagePath)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to create file %s: %s\n", imagePath, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
buffer := bufio.NewWriter(file)
|
||||
_, err = buffer.Write(imageData)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to write to file %s: %s\n", imagePath, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err := buffer.Flush(); err != nil {
|
||||
fmt.Printf("FATAL: Failed to flush data to file %s: %s\n", imagePath, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// clean up files with this ID and different extensions
|
||||
err = filepath.Walk(artworkDirectory, func(path string, info fs.FileInfo, err error) error {
|
||||
if path == imagePath { return nil }
|
||||
|
||||
withoutExt := strings.TrimSuffix(path, filepath.Ext(path))
|
||||
if withoutExt != filepath.Join(artworkDirectory, update.ID) { return nil }
|
||||
|
||||
return os.Remove(path)
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("WARN: Error while cleaning up artwork files: %s\n", err)
|
||||
}
|
||||
|
||||
update.Artwork = fmt.Sprintf("/uploads/musicart/%s.%s", update.ID, ext)
|
||||
} else {
|
||||
update.Artwork = *data.Artwork
|
||||
}
|
||||
}
|
||||
|
||||
if data.Buyname != nil { update.Buyname = *data.Buyname }
|
||||
if data.Buylink != nil { update.Buylink = *data.Buylink }
|
||||
|
||||
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 = 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)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
var exists int
|
||||
err := global.DB.Get(&exists, "SELECT count(*) FROM musicrelease WHERE id=$1", releaseID)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update release: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -302,18 +193,81 @@ func UpdateRelease() http.Handler {
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
http.NotFound(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateReleaseTracks(release *model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" || r.Method != http.MethodPut {
|
||||
|
||||
if len(segments) > 2 {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
var data releaseBodyJSON
|
||||
err = json.NewDecoder(r.Body).Decode(&data)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if data.ID != "" { release.ID = data.ID }
|
||||
if data.Visible != nil { release.Visible = *data.Visible }
|
||||
if data.Title != nil { release.Title = *data.Title }
|
||||
if data.Description != nil { release.Description = *data.Description }
|
||||
if data.ReleaseType != nil { release.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
|
||||
}
|
||||
release.ReleaseDate = newDate
|
||||
}
|
||||
if data.Artwork != nil {
|
||||
if strings.Contains(*data.Artwork, ";base64,") {
|
||||
var artworkDirectory = filepath.Join("uploads", "musicart")
|
||||
filename, err := HandleImageUpload(data.Artwork, artworkDirectory, data.ID)
|
||||
|
||||
// clean up files with this ID and different extensions
|
||||
err = filepath.Walk(artworkDirectory, func(path string, info fs.FileInfo, err error) error {
|
||||
if path == filepath.Join(artworkDirectory, filename) { return nil }
|
||||
|
||||
withoutExt := strings.TrimSuffix(path, filepath.Ext(path))
|
||||
if withoutExt != filepath.Join(artworkDirectory, release.ID) { return nil }
|
||||
|
||||
return os.Remove(path)
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("WARN: Error while cleaning up artwork files: %s\n", err)
|
||||
}
|
||||
|
||||
release.Artwork = fmt.Sprintf("/uploads/musicart/%s", filename)
|
||||
} else {
|
||||
release.Artwork = *data.Artwork
|
||||
}
|
||||
}
|
||||
|
||||
if data.Buyname != nil { release.Buyname = *data.Buyname }
|
||||
if data.Buylink != nil { release.Buylink = *data.Buylink }
|
||||
|
||||
_, err = global.DB.Exec(
|
||||
"UPDATE musicrelease SET "+
|
||||
"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("2006-01-02 15:04:05"),
|
||||
release.Artwork,
|
||||
release.Buyname,
|
||||
release.Buylink)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to update release %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateReleaseTracks(release model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var trackIDs = []string{}
|
||||
err := json.NewDecoder(r.Body).Decode(&trackIDs)
|
||||
if err != nil {
|
||||
|
@ -321,123 +275,85 @@ func UpdateReleaseTracks(release *model.Release) http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
var old_tracks = (*release).Tracks
|
||||
var new_tracks = []*model.Track{}
|
||||
for _, trackID := range trackIDs {
|
||||
var track = global.GetTrack(trackID)
|
||||
if track == nil {
|
||||
http.Error(w, fmt.Sprintf("Track %s does not exist\n", trackID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
new_tracks = append(new_tracks, track)
|
||||
track.Release = release
|
||||
tx := global.DB.MustBegin()
|
||||
tx.MustExec("DELETE FROM musicreleasetrack WHERE release=$1", release.ID)
|
||||
for i, trackID := range trackIDs {
|
||||
tx.MustExec(
|
||||
"INSERT INTO musicreleasetrack "+
|
||||
"(release, track, number) "+
|
||||
"VALUES ($1, $2, $3)",
|
||||
release.ID,
|
||||
trackID,
|
||||
i)
|
||||
}
|
||||
|
||||
err = controller.UpdateReleaseTracksDB(global.DB, release, new_tracks)
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update tracks for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
release.Tracks = new_tracks
|
||||
|
||||
// remove release from orphaned tracks
|
||||
for _, old_track := range old_tracks {
|
||||
var exists = false
|
||||
for _, track := range new_tracks {
|
||||
if track.ID == old_track.ID {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
old_track.Release = nil
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
err = json.NewEncoder(w).Encode(release)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateReleaseCredits(release *model.Release) http.Handler {
|
||||
func UpdateReleaseCredits(release model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPut {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
type creditJSON struct {
|
||||
Artist string
|
||||
Role string
|
||||
Primary bool
|
||||
}
|
||||
|
||||
var list []creditJSON
|
||||
err := json.NewDecoder(r.Body).Decode(&list)
|
||||
var data []creditJSON
|
||||
err := json.NewDecoder(r.Body).Decode(&data)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var credits = []*model.Credit{}
|
||||
for i, data := range list {
|
||||
if data.Artist == "" {
|
||||
http.Error(w, fmt.Sprintf("Artist ID cannot be blank (%d)", i), http.StatusBadRequest)
|
||||
return
|
||||
// clear duplicates
|
||||
type Credit struct {
|
||||
Role string
|
||||
Primary bool
|
||||
}
|
||||
var credits = map[string]Credit{}
|
||||
for _, credit := range data {
|
||||
credits[credit.Artist] = Credit{
|
||||
Role: credit.Role,
|
||||
Primary: credit.Primary,
|
||||
}
|
||||
|
||||
for _, credit := range credits {
|
||||
if data.Artist == credit.Artist.ID {
|
||||
http.Error(w, fmt.Sprintf("Artist %s credited more than once", data.Artist), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if data.Role == "" {
|
||||
http.Error(w, fmt.Sprintf("Artist role cannot be blank (%d)", i), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var artist = global.GetArtist(data.Artist)
|
||||
if artist == nil {
|
||||
http.Error(w, fmt.Sprintf("Artist %s does not exist\n", data.Artist), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
credits = append(credits, &model.Credit{
|
||||
Artist: artist,
|
||||
Role: data.Role,
|
||||
Primary: data.Primary,
|
||||
})
|
||||
}
|
||||
|
||||
err = controller.UpdateReleaseCreditsDB(global.DB, release, credits)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update links %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
tx := global.DB.MustBegin()
|
||||
tx.MustExec("DELETE FROM musiccredit WHERE release=$1", release.ID)
|
||||
for artistID := range credits {
|
||||
if credits[artistID].Role == "" {
|
||||
http.Error(w, fmt.Sprintf("Artist role cannot be blank (%s)", artistID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
release.Credits = credits
|
||||
var exists int
|
||||
_ = global.DB.Get(&exists, "SELECT count(*) FROM artist WHERE id=$1", artistID)
|
||||
if exists == 0 {
|
||||
http.Error(w, fmt.Sprintf("Artist %s does not exist\n", artistID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
err = json.NewEncoder(w).Encode(release)
|
||||
tx.MustExec(
|
||||
"INSERT INTO musiccredit "+
|
||||
"(release, artist, role, is_primary) "+
|
||||
"VALUES ($1, $2, $3, $4)",
|
||||
release.ID,
|
||||
artistID,
|
||||
credits[artistID].Role,
|
||||
credits[artistID].Primary)
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update links for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateReleaseLinks(release *model.Release) http.Handler {
|
||||
func UpdateReleaseLinks(release model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPut {
|
||||
http.NotFound(w, r)
|
||||
|
@ -451,56 +367,31 @@ func UpdateReleaseLinks(release *model.Release) http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
err = controller.UpdateReleaseLinksDB(global.DB, release, links)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update links %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
tx := global.DB.MustBegin()
|
||||
tx.MustExec("DELETE FROM musiclink WHERE release=$1", release.ID)
|
||||
for _, link := range links {
|
||||
tx.MustExec(
|
||||
"INSERT INTO musiclink "+
|
||||
"(release, name, url) "+
|
||||
"VALUES ($1, $2, $3)",
|
||||
release.ID,
|
||||
link.Name,
|
||||
link.URL)
|
||||
}
|
||||
|
||||
release.Links = links
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
err = json.NewEncoder(w).Encode(release)
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update links for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteRelease() http.Handler {
|
||||
func DeleteRelease(release model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
var releaseID = r.URL.Path[1:]
|
||||
var release = global.GetRelease(releaseID)
|
||||
if release == nil {
|
||||
http.Error(w, fmt.Sprintf("Release %s does not exist\n", releaseID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err := controller.DeleteReleaseDB(global.DB, release)
|
||||
_, err := global.DB.Exec("DELETE FROM musicrelease WHERE id=$1", release.ID)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to delete release %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
global.Releases = func () []*model.Release {
|
||||
var releases = []*model.Release{}
|
||||
for _, r := range global.Releases {
|
||||
if r.ID == release.ID { continue }
|
||||
releases = append(releases, r)
|
||||
}
|
||||
return releases
|
||||
}()
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(fmt.Sprintf("Release %s has been deleted\n", release.ID)))
|
||||
})
|
||||
}
|
||||
|
|
173
api/track.go
173
api/track.go
|
@ -7,35 +7,32 @@ import (
|
|||
|
||||
"arimelody.me/arimelody.me/global"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
controller "arimelody.me/arimelody.me/music/controller"
|
||||
)
|
||||
|
||||
func ServeAllTracks() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// type trackJSON struct {
|
||||
// model.Track
|
||||
// Release string `json:"release"`
|
||||
// }
|
||||
// var tracks = []trackJSON{}
|
||||
//
|
||||
// for _, track := range global.Tracks {
|
||||
// for _, release := range global. {
|
||||
// tracks = append(tracks, {
|
||||
// track,
|
||||
// Release
|
||||
// })
|
||||
// }
|
||||
type track struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
}
|
||||
var tracks = []track{}
|
||||
|
||||
err := global.DB.Select(&tracks, "SELECT id, title FROM musictrack")
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull tracks from DB: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
err := json.NewEncoder(w).Encode(global.Tracks)
|
||||
err = json.NewEncoder(w).Encode(tracks)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to serve all tracks: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func ServeTrack() http.Handler {
|
||||
func ServeTrack(track model.Track) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
ServeAllTracks().ServeHTTP(w, r)
|
||||
|
@ -43,17 +40,35 @@ func ServeTrack() http.Handler {
|
|||
}
|
||||
|
||||
var trackID = r.URL.Path[1:]
|
||||
var track = global.GetTrack(trackID)
|
||||
if track == nil {
|
||||
var track = model.Track{}
|
||||
err := global.DB.Get(&track, "SELECT * from musictrack WHERE id=$1", trackID)
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
err := json.NewEncoder(w).Encode(track)
|
||||
if err != nil {
|
||||
var releases = []*model.Release{}
|
||||
err = global.DB.Select(&releases,
|
||||
"SELECT * FROM musicrelease JOIN musicreleasetrack AS mrt "+
|
||||
"WHERE mrt.track=$1 "+
|
||||
"ORDER BY release_date",
|
||||
track.ID,
|
||||
)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull track releases for %s from DB: %s\n", trackID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
type response struct {
|
||||
model.Track
|
||||
Releases []*model.Release
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
err = json.NewEncoder(w).Encode(response{ track, releases })
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to serve track %s: %s\n", trackID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -77,122 +92,92 @@ func CreateTrack() http.Handler {
|
|||
return
|
||||
}
|
||||
|
||||
trackID, err := controller.CreateTrackDB(global.DB, &track)
|
||||
var trackID string
|
||||
err = global.DB.Get(&trackID,
|
||||
"INSERT INTO musictrack (title, description, lyrics, preview_url) "+
|
||||
"VALUES ($1, $2, $3, $4) "+
|
||||
"RETURNING id",
|
||||
track.Title,
|
||||
track.Description,
|
||||
track.Lyrics,
|
||||
track.PreviewURL)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to create track: %s\n", err)
|
||||
fmt.Printf("FATAL: Failed to create track: %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.Header().Add("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
err = json.NewEncoder(w).Encode(track)
|
||||
w.Write([]byte(trackID))
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateTrack() http.Handler {
|
||||
func UpdateTrack(track model.Track) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPut {
|
||||
if r.Method != http.MethodPut || r.URL.Path == "/" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if r.URL.Path == "/" {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var data model.Track
|
||||
err := json.NewDecoder(r.Body).Decode(&data)
|
||||
var update model.Track
|
||||
err := json.NewDecoder(r.Body).Decode(&update)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update track: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var trackID = r.URL.Path[1:]
|
||||
var track = global.GetTrack(trackID)
|
||||
if track == nil {
|
||||
http.Error(w, fmt.Sprintf("Track %s does not exist\n", trackID), http.StatusBadRequest)
|
||||
if update.Title == "" {
|
||||
http.Error(w, "Track title cannot be empty\n", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
data.ID = trackID
|
||||
var trackID = r.URL.Path[1:]
|
||||
var track = model.Track{}
|
||||
err = global.DB.Get(&track, "SELECT * from musictrack WHERE id=$1", trackID)
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if data.Title == "" { data.Title = track.Title }
|
||||
|
||||
err = controller.UpdateTrackDB(global.DB, &data)
|
||||
_, err = global.DB.Exec(
|
||||
"UPDATE musictrack "+
|
||||
"SET title=$2, description=$3, lyrics=$4, preview_url=$5 "+
|
||||
"WHERE id=$1",
|
||||
track.ID,
|
||||
track.Title,
|
||||
track.Description,
|
||||
track.Lyrics,
|
||||
track.PreviewURL)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to update track %s: %s\n", track.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
track.Title = data.Title
|
||||
track.Description = data.Description
|
||||
track.Lyrics = data.Lyrics
|
||||
track.PreviewURL = data.PreviewURL
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
err = json.NewEncoder(w).Encode(track)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func DeleteTrack() http.Handler {
|
||||
func DeleteTrack(track model.Track) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
if r.Method != http.MethodDelete || r.URL.Path == "/" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if r.URL.Path == "/" {
|
||||
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var trackID = r.URL.Path[1:]
|
||||
var track = global.GetTrack(trackID)
|
||||
if track == nil {
|
||||
http.Error(w, fmt.Sprintf("Track %s does not exist\n", trackID), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err := controller.DeleteTrackDB(global.DB, track)
|
||||
_, err := global.DB.Exec(
|
||||
"DELETE FROM musictrack "+
|
||||
"WHERE id=$1",
|
||||
trackID)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to delete track %s: %s\n", track.ID, err)
|
||||
fmt.Printf("Failed to delete track %s: %s\n", trackID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// clear track from releases
|
||||
for _, release := range global.Releases {
|
||||
release.Tracks = func () []*model.Track {
|
||||
var tracks = []*model.Track{}
|
||||
for _, t := range release.Tracks {
|
||||
if t.ID == track.ID { continue }
|
||||
tracks = append(tracks, t)
|
||||
}
|
||||
return tracks
|
||||
}()
|
||||
}
|
||||
|
||||
global.Tracks = func () []*model.Track {
|
||||
var tracks = []*model.Track{}
|
||||
for _, t := range global.Tracks {
|
||||
if t.ID == track.ID { continue }
|
||||
tracks = append(tracks, t)
|
||||
}
|
||||
return tracks
|
||||
}()
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(fmt.Sprintf("Track %s has been deleted\n", track.ID)))
|
||||
})
|
||||
}
|
||||
|
|
49
api/uploads.go
Normal file
49
api/uploads.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func HandleImageUpload(data *string, directory string, filename string) (string, error) {
|
||||
split := strings.Split(*data, ";base64,")
|
||||
header := split[0]
|
||||
imageData, err := base64.StdEncoding.DecodeString(split[1])
|
||||
ext, _ := strings.CutPrefix(header, "data:image/")
|
||||
|
||||
switch ext {
|
||||
case "png":
|
||||
case "jpg":
|
||||
case "jpeg":
|
||||
default:
|
||||
return "", errors.New("Invalid image type. Allowed: .png, .jpg, .jpeg")
|
||||
}
|
||||
filename = fmt.Sprintf("%s.%s", filename, ext)
|
||||
|
||||
// ensure directory exists
|
||||
os.MkdirAll(directory, os.ModePerm)
|
||||
|
||||
imagePath := filepath.Join(directory, filename)
|
||||
file, err := os.Create(imagePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
buffer := bufio.NewWriter(file)
|
||||
_, err = buffer.Write(imageData)
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if err := buffer.Flush(); err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return filename, nil
|
||||
}
|
|
@ -5,7 +5,6 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
|
@ -44,34 +43,3 @@ var HTTP_DOMAIN = func() string {
|
|||
}()
|
||||
|
||||
var DB *sqlx.DB
|
||||
|
||||
var Releases []*model.Release
|
||||
var Artists []*model.Artist
|
||||
var Tracks []*model.Track
|
||||
|
||||
func GetRelease(id string) *model.Release {
|
||||
for _, release := range Releases {
|
||||
if release.ID == id {
|
||||
return release
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetArtist(id string) *model.Artist {
|
||||
for _, artist := range Artists {
|
||||
if artist.ID == id {
|
||||
return artist
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetTrack(id string) *model.Track {
|
||||
for _, track := range Tracks {
|
||||
if track.ID == id {
|
||||
return track
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,9 +3,6 @@ package global
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"html/template"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
@ -64,39 +61,3 @@ func HTTPLog(next http.Handler) http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func ServeTemplate(page string, data any) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
lp_layout := filepath.Join("views", "layout.html")
|
||||
lp_header := filepath.Join("views", "header.html")
|
||||
lp_footer := filepath.Join("views", "footer.html")
|
||||
lp_prideflag := filepath.Join("views", "prideflag.html")
|
||||
fp := filepath.Join("views", filepath.Clean(page))
|
||||
|
||||
info, err := os.Stat(fp)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
template, err := template.ParseFiles(lp_layout, lp_header, lp_footer, lp_prideflag, fp)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing template files: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
err = template.ExecuteTemplate(w, "layout.html", data)
|
||||
if err != nil {
|
||||
fmt.Printf("Error executing template: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
35
main.go
35
main.go
|
@ -11,8 +11,8 @@ import (
|
|||
"arimelody.me/arimelody.me/admin"
|
||||
"arimelody.me/arimelody.me/api"
|
||||
"arimelody.me/arimelody.me/global"
|
||||
musicController "arimelody.me/arimelody.me/music/controller"
|
||||
musicView "arimelody.me/arimelody.me/music/view"
|
||||
"arimelody.me/arimelody.me/templates"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq"
|
||||
|
@ -25,42 +25,18 @@ func main() {
|
|||
var err error
|
||||
global.DB, err = sqlx.Connect("postgres", "user=arimelody dbname=arimelody password=fuckingpassword sslmode=disable")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unable to create database connection pool: %v\n", err)
|
||||
fmt.Fprintf(os.Stderr, "FATAL: Unable to create database connection pool: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
global.DB.SetConnMaxLifetime(time.Minute * 3)
|
||||
global.DB.SetMaxOpenConns(10)
|
||||
global.DB.SetMaxIdleConns(10)
|
||||
defer global.DB.Close()
|
||||
|
||||
// pull artist data from DB
|
||||
global.Artists, err = musicController.PullAllArtists(global.DB)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to pull artists from database: %v\n", err);
|
||||
panic(1)
|
||||
}
|
||||
fmt.Printf("%d artists loaded successfully.\n", len(global.Artists))
|
||||
|
||||
// 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))
|
||||
|
||||
// pull release data from DB
|
||||
global.Releases, err = musicController.PullAllReleases(global.DB)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to pull releases from database: %v\n", err);
|
||||
panic(1)
|
||||
}
|
||||
fmt.Printf("%d releases loaded successfully.\n", len(global.Releases))
|
||||
|
||||
// start the web server!
|
||||
mux := createServeMux()
|
||||
port := DEFAULT_PORT
|
||||
fmt.Printf("now serving at http://127.0.0.1:%d\n", port)
|
||||
fmt.Printf("Now serving at http://127.0.0.1:%d\n", port)
|
||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), global.HTTPLog(mux)))
|
||||
}
|
||||
|
||||
|
@ -73,7 +49,10 @@ func createServeMux() *http.ServeMux {
|
|||
mux.Handle("/uploads/", http.StripPrefix("/uploads", staticHandler("uploads")))
|
||||
mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" || r.URL.Path == "/index.html" {
|
||||
global.ServeTemplate("index.html", nil).ServeHTTP(w, r)
|
||||
err := templates.Pages["index"].Execute(w, nil)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
staticHandler("public").ServeHTTP(w, r)
|
||||
|
|
|
@ -7,7 +7,18 @@ import (
|
|||
|
||||
// DATABASE
|
||||
|
||||
func PullAllArtists(db *sqlx.DB) ([]*model.Artist, error) {
|
||||
func GetArtist(db *sqlx.DB, id string) (*model.Artist, error) {
|
||||
var artist = model.Artist{}
|
||||
|
||||
err := db.Get(&artist, "SELECT * FROM artist WHERE id=$1", id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &artist, nil
|
||||
}
|
||||
|
||||
func GetAllArtists(db *sqlx.DB) ([]*model.Artist, error) {
|
||||
var artists = []*model.Artist{}
|
||||
|
||||
err := db.Select(&artists, "SELECT * FROM artist")
|
||||
|
@ -18,7 +29,22 @@ func PullAllArtists(db *sqlx.DB) ([]*model.Artist, error) {
|
|||
return artists, nil
|
||||
}
|
||||
|
||||
func CreateArtistDB(db *sqlx.DB, artist *model.Artist) error {
|
||||
func GetArtistsNotOnRelease(db *sqlx.DB, release *model.Release) ([]*model.Artist, error) {
|
||||
var artists = []*model.Artist{}
|
||||
|
||||
err := db.Select(&artists,
|
||||
"SELECT * FROM artist "+
|
||||
"WHERE id NOT IN "+
|
||||
"(SELECT artist FROM musiccredit WHERE release=$1)",
|
||||
release.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return artists, nil
|
||||
}
|
||||
|
||||
func CreateArtist(db *sqlx.DB, artist *model.Artist) error {
|
||||
_, err := db.Exec(
|
||||
"INSERT INTO artist (id, name, website, avatar) "+
|
||||
"VALUES ($1, $2, $3, $4)",
|
||||
|
@ -34,7 +60,7 @@ func CreateArtistDB(db *sqlx.DB, artist *model.Artist) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func UpdateArtistDB(db *sqlx.DB, artist *model.Artist) error {
|
||||
func UpdateArtist(db *sqlx.DB, artist *model.Artist) error {
|
||||
_, err := db.Exec(
|
||||
"UPDATE artist "+
|
||||
"SET name=$2, website=$3, avatar=$4 "+
|
||||
|
@ -51,7 +77,7 @@ func UpdateArtistDB(db *sqlx.DB, artist *model.Artist) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func DeleteArtistDB(db *sqlx.DB, artist *model.Artist) error {
|
||||
func DeleteArtist(db *sqlx.DB, artist *model.Artist) error {
|
||||
_, err := db.Exec(
|
||||
"DELETE FROM artist "+
|
||||
"WHERE id=$1",
|
||||
|
|
|
@ -1,43 +1,29 @@
|
|||
package music
|
||||
|
||||
import (
|
||||
"arimelody.me/arimelody.me/global"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
// DATABASE
|
||||
|
||||
func PullReleaseCredits(db *sqlx.DB, releaseID string) ([]*model.Credit, error) {
|
||||
type creditDB struct {
|
||||
Artist string
|
||||
Role string
|
||||
Primary bool `db:"is_primary"`
|
||||
}
|
||||
var credit_rows = []creditDB{}
|
||||
var credits = []*model.Credit{}
|
||||
func GetReleaseCredits(db *sqlx.DB, release *model.Release) ([]model.Credit, error) {
|
||||
var credits = []model.Credit{}
|
||||
|
||||
err := db.Select(
|
||||
&credit_rows,
|
||||
"SELECT artist, role, is_primary FROM musiccredit WHERE release=$1",
|
||||
releaseID,
|
||||
err := db.Select(&credits,
|
||||
"SELECT artist.*,role,is_primary FROM musiccredit "+
|
||||
"JOIN artist ON artist=id "+
|
||||
"WHERE release=$1",
|
||||
release.ID,
|
||||
)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
func CreateCreditDB(db *sqlx.DB, releaseID string, artistID string, credit *model.Credit) (error) {
|
||||
func CreateCredit(db *sqlx.DB, releaseID string, artistID string, credit *model.Credit) (error) {
|
||||
_, err := db.Exec(
|
||||
"INSERT INTO musiccredit (release, artist, role, is_primary) "+
|
||||
"VALUES ($1, $2, $3, $4)",
|
||||
|
@ -53,7 +39,7 @@ func CreateCreditDB(db *sqlx.DB, releaseID string, artistID string, credit *mode
|
|||
return nil
|
||||
}
|
||||
|
||||
func UpdateCreditDB(db *sqlx.DB, releaseID string, artistID string, credit *model.Credit) (error) {
|
||||
func UpdateCredit(db *sqlx.DB, releaseID string, artistID string, credit *model.Credit) (error) {
|
||||
_, err := db.Exec(
|
||||
"UPDATE musiccredit SET "+
|
||||
"role=$3, is_primary=$4 "+
|
||||
|
@ -70,7 +56,7 @@ func UpdateCreditDB(db *sqlx.DB, releaseID string, artistID string, credit *mode
|
|||
return nil
|
||||
}
|
||||
|
||||
func DeleteCreditDB(db *sqlx.DB, releaseID string, artistID string) (error) {
|
||||
func DeleteCredit(db *sqlx.DB, releaseID string, artistID string) (error) {
|
||||
_, err := db.Exec(
|
||||
"DELETE FROM musiccredit "+
|
||||
"WHERE release=$1, artist=$2",
|
||||
|
|
|
@ -7,14 +7,10 @@ import (
|
|||
|
||||
// DATABASE
|
||||
|
||||
func PullReleaseLinks(db *sqlx.DB, releaseID string) ([]*model.Link, error) {
|
||||
var links = []*model.Link{}
|
||||
func GetReleaseLinks(db *sqlx.DB, release *model.Release) ([]model.Link, error) {
|
||||
var links = []model.Link{}
|
||||
|
||||
err := db.Select(
|
||||
&links,
|
||||
"SELECT name, url FROM musiclink WHERE release=$1",
|
||||
releaseID,
|
||||
)
|
||||
err := db.Select(&links, "SELECT name,url FROM musiclink WHERE release=$1", release.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -22,7 +18,7 @@ func PullReleaseLinks(db *sqlx.DB, releaseID string) ([]*model.Link, error) {
|
|||
return links, nil
|
||||
}
|
||||
|
||||
func CreateLinkDB(db *sqlx.DB, releaseID string, link *model.Link) (error) {
|
||||
func CreateLink(db *sqlx.DB, releaseID string, link *model.Link) (error) {
|
||||
_, err := db.Exec(
|
||||
"INSERT INTO musiclink (release, name, url) "+
|
||||
"VALUES ($1, $2, $3)",
|
||||
|
@ -37,7 +33,7 @@ func CreateLinkDB(db *sqlx.DB, releaseID string, link *model.Link) (error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func UpdateLinkDB(db *sqlx.DB, releaseID string, link *model.Link) (error) {
|
||||
func UpdateLink(db *sqlx.DB, releaseID string, link *model.Link) (error) {
|
||||
_, err := db.Exec(
|
||||
"UPDATE musiclink SET "+
|
||||
"name=$2, url=$3 "+
|
||||
|
@ -53,7 +49,7 @@ func UpdateLinkDB(db *sqlx.DB, releaseID string, link *model.Link) (error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func DeleteLinkDB(db *sqlx.DB, releaseID string, link *model.Link) (error) {
|
||||
func DeleteLink(db *sqlx.DB, releaseID string, link *model.Link) (error) {
|
||||
_, err := db.Exec(
|
||||
"DELETE FROM musiclink "+
|
||||
"WHERE release=$1, name=$2",
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
package music
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"arimelody.me/arimelody.me/global"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
// DATABASE
|
||||
func GetRelease(db *sqlx.DB, id string) (*model.Release, error) {
|
||||
var releases = model.Release{}
|
||||
|
||||
func PullAllReleases(db *sqlx.DB) ([]*model.Release, error) {
|
||||
err := db.Get(&releases, "SELECT * FROM musicrelease WHERE id=$1", id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &releases, nil
|
||||
}
|
||||
|
||||
func GetAllReleases(db *sqlx.DB) ([]*model.Release, error) {
|
||||
var releases = []*model.Release{}
|
||||
|
||||
err := db.Select(&releases, "SELECT * FROM musicrelease ORDER BY release_date DESC")
|
||||
|
@ -19,33 +24,15 @@ func PullAllReleases(db *sqlx.DB) ([]*model.Release, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
for _, release := range releases {
|
||||
release.Credits, err = PullReleaseCredits(global.DB, release.ID)
|
||||
if err != nil {
|
||||
fmt.Printf("Error pulling credits for %s: %s\n", release.ID, err)
|
||||
release.Credits = []*model.Credit{}
|
||||
}
|
||||
release.Links, err = PullReleaseLinks(global.DB, release.ID)
|
||||
if err != nil {
|
||||
fmt.Printf("Error pulling links for %s: %s\n", release.ID, err)
|
||||
release.Links = []*model.Link{}
|
||||
}
|
||||
release.Tracks, err = PullReleaseTracksDB(global.DB, release)
|
||||
if err != nil {
|
||||
fmt.Printf("Error pulling tracks for %s: %s\n", release.ID, err)
|
||||
release.Tracks = []*model.Track{}
|
||||
}
|
||||
}
|
||||
|
||||
return releases, nil
|
||||
}
|
||||
|
||||
func PullReleaseTracksDB(db *sqlx.DB, release *model.Release) ([]*model.Track, error) {
|
||||
var track_rows = []string{}
|
||||
func GetReleaseTracks(db *sqlx.DB, release *model.Release) ([]*model.Track, error) {
|
||||
var tracks = []*model.Track{}
|
||||
|
||||
err := db.Select(&track_rows,
|
||||
"SELECT track FROM musicreleasetrack "+
|
||||
err := db.Select(&tracks,
|
||||
"SELECT musictrack.* FROM musictrack "+
|
||||
"JOIN musicreleasetrack ON track=id "+
|
||||
"WHERE release=$1 "+
|
||||
"ORDER BY number ASC",
|
||||
release.ID,
|
||||
|
@ -54,19 +41,10 @@ func PullReleaseTracksDB(db *sqlx.DB, release *model.Release) ([]*model.Track, e
|
|||
return nil, err
|
||||
}
|
||||
|
||||
for _, trackID := range track_rows {
|
||||
var track = global.GetTrack(trackID)
|
||||
if track == nil {
|
||||
return nil, errors.New("Recieved a track from the DB that does not exist in memory")
|
||||
}
|
||||
track.Release = release
|
||||
tracks = append(tracks, track)
|
||||
}
|
||||
|
||||
return tracks, nil
|
||||
}
|
||||
|
||||
func CreateReleaseDB(db *sqlx.DB, release *model.Release) error {
|
||||
func CreateRelease(db *sqlx.DB, release *model.Release) error {
|
||||
_, err := db.Exec(
|
||||
"INSERT INTO musicrelease "+
|
||||
"(id, visible, title, description, type, release_date, artwork, buyname, buylink) "+
|
||||
|
@ -88,7 +66,7 @@ func CreateReleaseDB(db *sqlx.DB, release *model.Release) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func UpdateReleaseDB(db *sqlx.DB, release *model.Release) error {
|
||||
func UpdateRelease(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 "+
|
||||
|
@ -110,7 +88,7 @@ func UpdateReleaseDB(db *sqlx.DB, release *model.Release) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func UpdateReleaseTracksDB(db *sqlx.DB, release *model.Release, new_tracks []*model.Track) error {
|
||||
func UpdateReleaseTracks(db *sqlx.DB, release *model.Release, new_tracks []*model.Track) error {
|
||||
_, err := db.Exec(
|
||||
"DELETE FROM musicreleasetrack "+
|
||||
"WHERE release=$1",
|
||||
|
@ -137,7 +115,7 @@ func UpdateReleaseTracksDB(db *sqlx.DB, release *model.Release, new_tracks []*mo
|
|||
return nil
|
||||
}
|
||||
|
||||
func UpdateReleaseCreditsDB(db *sqlx.DB, release *model.Release, new_credits []*model.Credit) error {
|
||||
func UpdateReleaseCredits(db *sqlx.DB, release *model.Release, new_credits []*model.Credit) error {
|
||||
_, err := db.Exec(
|
||||
"DELETE FROM musiccredit "+
|
||||
"WHERE release=$1",
|
||||
|
@ -165,7 +143,7 @@ func UpdateReleaseCreditsDB(db *sqlx.DB, release *model.Release, new_credits []*
|
|||
return nil
|
||||
}
|
||||
|
||||
func UpdateReleaseLinksDB(db *sqlx.DB, release *model.Release, new_links []*model.Link) error {
|
||||
func UpdateReleaseLinks(db *sqlx.DB, release *model.Release, new_links []*model.Link) error {
|
||||
_, err := db.Exec(
|
||||
"DELETE FROM musiclink "+
|
||||
"WHERE release=$1",
|
||||
|
@ -192,7 +170,7 @@ func UpdateReleaseLinksDB(db *sqlx.DB, release *model.Release, new_links []*mode
|
|||
return nil
|
||||
}
|
||||
|
||||
func DeleteReleaseDB(db *sqlx.DB, release *model.Release) error {
|
||||
func DeleteRelease(db *sqlx.DB, release *model.Release) error {
|
||||
_, err := db.Exec(
|
||||
"DELETE FROM musicrelease "+
|
||||
"WHERE id=$1",
|
||||
|
@ -204,3 +182,34 @@ func DeleteReleaseDB(db *sqlx.DB, release *model.Release) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetFullRelease(db *sqlx.DB, release *model.Release) (*model.FullRelease, error) {
|
||||
// get credits
|
||||
credits, err := GetReleaseCredits(db, release)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get tracks
|
||||
dbTracks, err := GetReleaseTracks(db, release)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tracks := []model.DisplayTrack{}
|
||||
for i, track := range dbTracks {
|
||||
tracks = append(tracks, track.MakeDisplay(i + 1))
|
||||
}
|
||||
|
||||
// get links
|
||||
links, err := GetReleaseLinks(db, release)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &model.FullRelease{
|
||||
Release: release,
|
||||
Tracks: tracks,
|
||||
Credits: credits,
|
||||
Links: links,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -7,10 +7,21 @@ import (
|
|||
|
||||
// DATABASE
|
||||
|
||||
func PullAllTracks(db *sqlx.DB) ([]*model.Track, error) {
|
||||
func GetTrack(db *sqlx.DB, id string) (*model.Track, error) {
|
||||
var track = model.Track{}
|
||||
|
||||
stmt, _ := db.Preparex("SELECT * FROM musictrack WHERE id=$1")
|
||||
err := stmt.Get(&track, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &track, nil
|
||||
}
|
||||
|
||||
func GetAllTracks(db *sqlx.DB) ([]*model.Track, error) {
|
||||
var tracks = []*model.Track{}
|
||||
|
||||
err := db.Select(&tracks, "SELECT id, title, description, lyrics, preview_url FROM musictrack")
|
||||
err := db.Select(&tracks, "SELECT * FROM musictrack")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -18,6 +29,49 @@ func PullAllTracks(db *sqlx.DB) ([]*model.Track, error) {
|
|||
return tracks, nil
|
||||
}
|
||||
|
||||
func GetOrphanTracks(db *sqlx.DB) ([]*model.Track, error) {
|
||||
var tracks = []*model.Track{}
|
||||
|
||||
err := db.Select(&tracks, "SELECT * FROM musictrack WHERE id NOT IN (SELECT track FROM musicreleasetrack)")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tracks, nil
|
||||
}
|
||||
|
||||
func GetTracksNotOnRelease(db *sqlx.DB, release *model.Release) ([]*model.Track, error) {
|
||||
var tracks = []*model.Track{}
|
||||
|
||||
err := db.Select(&tracks,
|
||||
"SELECT * FROM musictrack "+
|
||||
"WHERE id NOT IN "+
|
||||
"(SELECT track FROM musicreleasetrack WHERE release=$1)",
|
||||
release.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tracks, nil
|
||||
}
|
||||
|
||||
func GetTrackReleases(db *sqlx.DB, track *model.Track) ([]*model.Release, error) {
|
||||
var releases = []*model.Release{}
|
||||
|
||||
err := db.Select(&releases,
|
||||
"SELECT musicrelease.* FROM musicrelease "+
|
||||
"JOIN musicreleasetrack ON release=id "+
|
||||
"WHERE track=$1 "+
|
||||
"ORDER BY release_date",
|
||||
track.ID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return releases, nil
|
||||
}
|
||||
|
||||
func PullOrphanTracks(db *sqlx.DB) ([]*model.Track, error) {
|
||||
var tracks = []*model.Track{}
|
||||
|
||||
|
@ -33,7 +87,7 @@ func PullOrphanTracks(db *sqlx.DB) ([]*model.Track, error) {
|
|||
return tracks, nil
|
||||
}
|
||||
|
||||
func CreateTrackDB(db *sqlx.DB, track *model.Track) (string, error) {
|
||||
func CreateTrack(db *sqlx.DB, track *model.Track) (string, error) {
|
||||
var trackID string
|
||||
err := db.QueryRow(
|
||||
"INSERT INTO musictrack (title, description, lyrics, preview_url) "+
|
||||
|
@ -51,7 +105,7 @@ func CreateTrackDB(db *sqlx.DB, track *model.Track) (string, error) {
|
|||
return trackID, nil
|
||||
}
|
||||
|
||||
func UpdateTrackDB(db *sqlx.DB, track *model.Track) error {
|
||||
func UpdateTrack(db *sqlx.DB, track *model.Track) error {
|
||||
_, err := db.Exec(
|
||||
"UPDATE musictrack "+
|
||||
"SET title=$2, description=$3, lyrics=$4, preview_url=$5 "+
|
||||
|
@ -69,7 +123,7 @@ func UpdateTrackDB(db *sqlx.DB, track *model.Track) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func DeleteTrackDB(db *sqlx.DB, track *model.Track) error {
|
||||
func DeleteTrack(db *sqlx.DB, track *model.Track) error {
|
||||
_, err := db.Exec(
|
||||
"DELETE FROM musictrack "+
|
||||
"WHERE id=$1",
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package model
|
||||
|
||||
import "strings"
|
||||
|
||||
type (
|
||||
Artist struct {
|
||||
ID string `json:"id"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Website string `json:"website"`
|
||||
Avatar string `json:"avatar"`
|
||||
|
@ -14,8 +16,61 @@ func (artist Artist) GetWebsite() string {
|
|||
}
|
||||
|
||||
func (artist Artist) GetAvatar() string {
|
||||
if artist.Avatar == "" {
|
||||
return "/img/default-avatar.png"
|
||||
}
|
||||
if artist.Avatar == "" {
|
||||
return "/img/default-avatar.png"
|
||||
}
|
||||
return artist.Avatar
|
||||
}
|
||||
|
||||
func (release FullRelease) GetUniqueArtists(only_primary bool) []*Artist {
|
||||
var artists = []*Artist{}
|
||||
|
||||
for _, credit := range release.Credits {
|
||||
if only_primary && !credit.Primary {
|
||||
continue
|
||||
}
|
||||
|
||||
exists := false
|
||||
for _, a := range artists {
|
||||
if a.ID == credit.Artist.ID {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
|
||||
artists = append(artists, &credit.Artist)
|
||||
}
|
||||
|
||||
return artists
|
||||
}
|
||||
|
||||
func (release FullRelease) GetUniqueArtistNames(only_primary bool) []string {
|
||||
var names = []string{}
|
||||
for _, artist := range release.GetUniqueArtists(only_primary) {
|
||||
names = append(names, artist.Name)
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
func (release FullRelease) PrintArtists(only_primary bool, ampersand bool) string {
|
||||
names := release.GetUniqueArtistNames(only_primary)
|
||||
|
||||
if len(names) == 0 {
|
||||
return "Unknown Artist"
|
||||
} else if len(names) == 1 {
|
||||
return names[0]
|
||||
}
|
||||
|
||||
if ampersand {
|
||||
res := strings.Join(names[:len(names)-1], ", ")
|
||||
res += " & " + names[len(names)-1]
|
||||
return res
|
||||
} else {
|
||||
return strings.Join(names[:], ", ")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package model
|
||||
|
||||
type Credit struct {
|
||||
Artist *Artist `json:"artist"`
|
||||
Artist `json:"artist"`
|
||||
Role string `json:"role"`
|
||||
Primary bool `json:"primary" db:"is_primary"`
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
)
|
||||
|
||||
type Link struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
func (link Link) NormaliseName() string {
|
||||
|
|
|
@ -1,25 +1,31 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
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"`
|
||||
|
||||
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"`
|
||||
Copyright string `json:"copyright" db:"copyright"`
|
||||
CopyrightURL string `json:"copyrightURL" db:"copyrighturl"`
|
||||
}
|
||||
|
||||
FullRelease struct {
|
||||
*Release
|
||||
Tracks []DisplayTrack
|
||||
Credits []Credit
|
||||
Links []Link
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -52,63 +58,10 @@ func (release Release) GetArtwork() string {
|
|||
return release.Artwork
|
||||
}
|
||||
|
||||
func (release Release) IsSingle() bool {
|
||||
func (release FullRelease) IsSingle() bool {
|
||||
return len(release.Tracks) == 1;
|
||||
}
|
||||
|
||||
func (release Release) IsReleased() bool {
|
||||
return release.ReleaseDate.Before(time.Now())
|
||||
}
|
||||
|
||||
func (release Release) GetUniqueArtists(only_primary bool) []Artist {
|
||||
var artists = []Artist{}
|
||||
|
||||
for _, credit := range release.Credits {
|
||||
if only_primary && !credit.Primary {
|
||||
continue
|
||||
}
|
||||
|
||||
exists := false
|
||||
for _, a := range artists {
|
||||
if a.ID == credit.Artist.ID {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
|
||||
artists = append(artists, *credit.Artist)
|
||||
}
|
||||
|
||||
return artists
|
||||
}
|
||||
|
||||
func (release Release) GetUniqueArtistNames(only_primary bool) []string {
|
||||
var names = []string{}
|
||||
for _, artist := range release.GetUniqueArtists(only_primary) {
|
||||
names = append(names, artist.Name)
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
func (release Release) PrintArtists(only_primary bool, ampersand bool) string {
|
||||
names := release.GetUniqueArtistNames(only_primary)
|
||||
|
||||
if len(names) == 0 {
|
||||
return "Unknown Artist"
|
||||
} else if len(names) == 1 {
|
||||
return names[0]
|
||||
}
|
||||
|
||||
if ampersand {
|
||||
res := strings.Join(names[:len(names)-1], ", ")
|
||||
res += " & " + names[len(names)-1]
|
||||
return res
|
||||
} else {
|
||||
return strings.Join(names[:], ", ")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,30 @@
|
|||
package model
|
||||
|
||||
type Track struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Lyrics string `json:"lyrics"`
|
||||
PreviewURL string `json:"previewURL" db:"preview_url"`
|
||||
Release *Release `json:"-" db:"-"`
|
||||
import (
|
||||
"html/template"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
Track struct {
|
||||
ID string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Lyrics string `json:"lyrics"`
|
||||
PreviewURL string `json:"previewURL" db:"preview_url"`
|
||||
}
|
||||
|
||||
DisplayTrack struct {
|
||||
*Track
|
||||
Lyrics template.HTML
|
||||
Number int
|
||||
}
|
||||
)
|
||||
|
||||
func (track Track) MakeDisplay(number int) DisplayTrack {
|
||||
return DisplayTrack{
|
||||
Track: &track,
|
||||
Lyrics: template.HTML(strings.Replace(track.Lyrics, "\n", "<br>", -1)),
|
||||
Number: number,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package view
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"arimelody.me/arimelody.me/global"
|
||||
music "arimelody.me/arimelody.me/music/controller"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
"arimelody.me/arimelody.me/templates"
|
||||
)
|
||||
|
||||
// HTTP HANDLER METHODS
|
||||
|
@ -17,7 +20,15 @@ func Handler() http.Handler {
|
|||
ServeCatalog().ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
ServeGateway().ServeHTTP(w, r)
|
||||
|
||||
var release model.Release
|
||||
err := global.DB.Get(&release, "SELECT * FROM musicrelease WHERE id=$1", r.URL.Path[1:])
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
ServeGateway(release).ServeHTTP(w, r)
|
||||
}))
|
||||
|
||||
return mux
|
||||
|
@ -25,71 +36,30 @@ func Handler() http.Handler {
|
|||
|
||||
func ServeCatalog() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
releases := []model.Release{}
|
||||
for _, r := range global.Releases {
|
||||
if r.Visible {
|
||||
release := *r
|
||||
if !release.IsReleased() {
|
||||
release.ReleaseType = model.Upcoming
|
||||
}
|
||||
releases = append(releases, release)
|
||||
}
|
||||
}
|
||||
|
||||
global.ServeTemplate("music.html", releases).ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
func ServeArtwork() http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(r.URL.Path, ".png") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
releaseID := r.URL.Path[1:len(r.URL.Path) - 4]
|
||||
var release = GetRelease(releaseID)
|
||||
if release == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// only allow authorised users to view unreleased releases
|
||||
authorised := r.Context().Value("role") != nil && r.Context().Value("role") == "admin"
|
||||
if !release.IsReleased() && !authorised {
|
||||
admin.MustAuthorise(ServeArtwork()).ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
fp := filepath.Join("data", "music-artwork", releaseID + ".png")
|
||||
info, err := os.Stat(fp)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
length := info.Size()
|
||||
|
||||
file, err := os.Open(fp)
|
||||
dbReleases, err := music.GetAllReleases(global.DB)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull releases for catalog: %s\n", err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
releases := []*model.FullRelease{}
|
||||
for _, dbRelease := range dbReleases {
|
||||
if !dbRelease.Visible { continue }
|
||||
if !dbRelease.IsReleased() {
|
||||
dbRelease.ReleaseType = model.Upcoming
|
||||
}
|
||||
release, err := music.GetFullRelease(global.DB, dbRelease)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull full release for %s: %s\n", dbRelease.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
releases = append(releases, release)
|
||||
}
|
||||
|
||||
var bytes = make([]byte, length)
|
||||
file.Read(bytes)
|
||||
|
||||
w.Header().Add("Content-Type", "image/png")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(bytes)
|
||||
err = templates.Pages["music"].Execute(w, releases)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -3,59 +3,42 @@ package view
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"arimelody.me/arimelody.me/admin"
|
||||
"arimelody.me/arimelody.me/global"
|
||||
"arimelody.me/arimelody.me/music/model"
|
||||
)
|
||||
|
||||
type (
|
||||
gatewayTrack struct {
|
||||
*model.Track
|
||||
Lyrics template.HTML
|
||||
Number int
|
||||
}
|
||||
|
||||
gatewayRelease struct {
|
||||
*model.Release
|
||||
Tracks []gatewayTrack
|
||||
Authorised bool
|
||||
}
|
||||
db "arimelody.me/arimelody.me/music/controller"
|
||||
"arimelody.me/arimelody.me/templates"
|
||||
)
|
||||
|
||||
// HTTP HANDLERS
|
||||
|
||||
func ServeRelease() http.Handler {
|
||||
func ServeRelease(release model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
releaseID := r.URL.Path[1:]
|
||||
var releaseRef = global.GetRelease(releaseID)
|
||||
if releaseRef == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
var release = *releaseRef
|
||||
|
||||
// only allow authorised users to view hidden releases
|
||||
authorised := admin.GetSession(r) != nil
|
||||
if !authorised && !release.Visible {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if !authorised && !release.IsReleased() {
|
||||
release.Tracks = nil
|
||||
release.Credits = nil
|
||||
|
||||
fullRelease := &model.FullRelease{
|
||||
Release: &release,
|
||||
}
|
||||
|
||||
if authorised || release.IsReleased() {
|
||||
fullerRelease, err := db.GetFullRelease(global.DB, &release)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull full release data for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
fullRelease = fullerRelease
|
||||
}
|
||||
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
err := json.NewEncoder(w).Encode(release)
|
||||
err := json.NewEncoder(w).Encode(fullRelease)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -63,42 +46,34 @@ func ServeRelease() http.Handler {
|
|||
})
|
||||
}
|
||||
|
||||
func ServeGateway() http.Handler {
|
||||
func ServeGateway(release model.Release) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/" {
|
||||
http.Redirect(w, r, "/music", http.StatusPermanentRedirect)
|
||||
return
|
||||
}
|
||||
|
||||
id := r.URL.Path[1:]
|
||||
release := global.GetRelease(id)
|
||||
if release == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// only allow authorised users to view hidden releases
|
||||
authorised := admin.GetSession(r) != nil
|
||||
if !release.Visible && !authorised {
|
||||
if !authorised && !release.Visible {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
tracks := []gatewayTrack{}
|
||||
for i, track := range release.Tracks {
|
||||
tracks = append(tracks, gatewayTrack{
|
||||
Track: track,
|
||||
Lyrics: template.HTML(strings.Replace(track.Lyrics, "\n", "<br>", -1)),
|
||||
Number: i + 1,
|
||||
})
|
||||
fullRelease := &model.FullRelease{
|
||||
Release: &release,
|
||||
}
|
||||
|
||||
lrw := global.LoggingResponseWriter{ResponseWriter: w, Code: http.StatusOK}
|
||||
if authorised || release.IsReleased() {
|
||||
fullerRelease, err := db.GetFullRelease(global.DB, &release)
|
||||
if err != nil {
|
||||
fmt.Printf("FATAL: Failed to pull full release data for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
fullRelease = fullerRelease
|
||||
}
|
||||
|
||||
global.ServeTemplate("music-gateway.html", gatewayRelease{release, tracks, authorised}).ServeHTTP(&lrw, r)
|
||||
err := templates.Pages["music-gateway"].Execute(w, fullRelease)
|
||||
|
||||
if lrw.Code != http.StatusOK {
|
||||
fmt.Printf("Error rendering music gateway for %s\n", id)
|
||||
if err != nil {
|
||||
fmt.Printf("Error rendering music gateway for %s: %s\n", release.ID, err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
|
|
@ -21,7 +21,9 @@ CREATE TABLE public.musicrelease (
|
|||
release_date TIMESTAMP NOT NULL,
|
||||
artwork text,
|
||||
buyname text,
|
||||
buylink text
|
||||
buylink text,
|
||||
copyright text,
|
||||
copyrightURL text
|
||||
);
|
||||
ALTER TABLE public.musicrelease ADD CONSTRAINT musicrelease_pk PRIMARY KEY (id);
|
||||
|
||||
|
|
33
templates/templates.go
Normal file
33
templates/templates.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package templates
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var Pages = map[string]*template.Template{
|
||||
"index": template.Must(template.ParseFiles(
|
||||
filepath.Join("views", "layout.html"),
|
||||
filepath.Join("views", "header.html"),
|
||||
filepath.Join("views", "footer.html"),
|
||||
filepath.Join("views", "prideflag.html"),
|
||||
filepath.Join("views", "index.html"),
|
||||
)),
|
||||
"music": template.Must(template.ParseFiles(
|
||||
filepath.Join("views", "layout.html"),
|
||||
filepath.Join("views", "header.html"),
|
||||
filepath.Join("views", "footer.html"),
|
||||
filepath.Join("views", "prideflag.html"),
|
||||
filepath.Join("views", "music.html"),
|
||||
)),
|
||||
"music-gateway": template.Must(template.ParseFiles(
|
||||
filepath.Join("views", "layout.html"),
|
||||
filepath.Join("views", "header.html"),
|
||||
filepath.Join("views", "footer.html"),
|
||||
filepath.Join("views", "prideflag.html"),
|
||||
filepath.Join("views", "music-gateway.html"),
|
||||
)),
|
||||
}
|
||||
|
||||
var Components = map[string]*template.Template{
|
||||
}
|
Loading…
Reference in a new issue