more admin dashboard polish, some code cleanup

This commit is contained in:
ari melody 2025-11-06 22:08:06 +00:00
parent 65f277b3f2
commit c547fca0d7
Signed by: ari
GPG key ID: CF99829C92678188
27 changed files with 187 additions and 270 deletions

View file

@ -103,9 +103,7 @@ func serveRelease(app *model.AppState, releaseID string, action string) http.Han
Release *model.Release
}
for i, track := range release.Tracks {
track.Number = i + 1
}
for i, track := range release.Tracks { track.Number = i + 1 }
err = templates.EditReleaseTemplate.Execute(w, ReleaseResponse{
adminPageData: adminPageData{ Path: r.URL.Path, Session: session },
@ -157,7 +155,8 @@ func serveAddCredit(app *model.AppState, release *model.Release) http.Handler {
func serveNewCredit(app *model.AppState) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
artistID := strings.Split(r.URL.Path, "/")[3]
split := strings.Split(r.URL.Path, "/")
artistID := split[len(split) - 1]
artist, err := controller.GetArtist(app.DB, artistID)
if err != nil {
fmt.Printf("WARN: Failed to fetch artist %s: %s\n", artistID, err)
@ -195,6 +194,8 @@ func serveEditTracks(release *model.Release) http.Handler {
type editTracksData struct { Release *model.Release }
for i, track := range release.Tracks { track.Number = i + 1 }
err := templates.EditTracksTemplate.Execute(w, editTracksData{ Release: release })
if err != nil {
fmt.Printf("WARN: Failed to serve edit tracks component for %s: %s\n", release.ID, err)
@ -231,7 +232,8 @@ func serveAddTrack(app *model.AppState, release *model.Release) http.Handler {
func serveNewTrack(app *model.AppState) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
trackID := strings.Split(r.URL.Path, "/")[3]
split := strings.Split(r.URL.Path, "/")
trackID := split[len(split) - 1]
track, err := controller.GetTrack(app.DB, trackID)
if err != nil {
fmt.Printf("WARN: Failed to fetch track %s: %s\n", trackID, err)

View file

@ -111,7 +111,7 @@ body {
font-family: "Inter", sans-serif;
font-size: 16px;
color: var(--fg-0);
background: var(--bg-0);
background-color: var(--bg-0);
transition: background .1s ease-out, color .1s ease-out;
}
@ -252,12 +252,6 @@ a {
transition: color .1s ease-out, background-color .1s ease-out;
}
/*
a:hover {
text-decoration: underline;
}
*/
img.icon {
height: .8em;
transition: filter .1s ease-out;
@ -283,7 +277,7 @@ code {
.card {
flex-basis: 40em;
padding: 1em;
background: var(--bg-1);
background-color: var(--bg-1);
border-radius: 16px;
box-shadow: var(--shadow-lg);
@ -361,7 +355,7 @@ a.delete:not(.button) {
font-size: inherit;
color: inherit;
background: var(--bg-2);
background-color: var(--bg-2);
border: none;
border-radius: 10em;
box-shadow: var(--shadow-sm);
@ -380,27 +374,27 @@ button:active, .button:active {
.button.new, button.new {
color: var(--col-on-new);
background: var(--col-new);
background-color: var(--col-new);
}
.button.save, button.save {
color: var(--col-on-save);
background: var(--col-save);
background-color: var(--col-save);
}
.button.delete, button.delete {
color: var(--col-on-delete);
background: var(--col-delete);
background-color: var(--col-delete);
}
.button:hover, button:hover {
color: var(--bg-3);
background: var(--fg-3);
background-color: var(--fg-3);
}
.button:active, button:active {
color: var(--bg-2);
background: var(--fg-0);
background-color: var(--fg-0);
}
.button[disabled], button[disabled] {
color: var(--fg-0) !important;
background: var(--bg-3) !important;
background-color: var(--bg-3) !important;
opacity: .5;
cursor: default !important;
}

View file

@ -2,7 +2,7 @@
padding: .5em;
color: var(--fg-3);
background: var(--bg-2);
background-color: var(--bg-2);
box-shadow: var(--shadow-md);
border-radius: 16px;
text-align: center;
@ -12,7 +12,7 @@
}
.artist:hover {
background: var(--bg-1);
background-color: var(--bg-1);
text-decoration: hover;
}

View file

@ -4,4 +4,29 @@ document.addEventListener("readystatechange", () => {
document.querySelectorAll(".artists-group .artist").forEach(el => {
hijackClickEvent(el, el.querySelector("a.artist-name"))
});
const newArtistBtn = document.getElementById("create-artist");
if (newArtistBtn) newArtistBtn.addEventListener("click", event => {
event.preventDefault();
const id = prompt("Enter an ID for this artist:");
if (id == null || id == "") return;
fetch("/api/v1/artist", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({id})
}).then(res => {
res.text().then(text => {
if (res.ok) {
location = "/admin/artists/" + id;
} else {
alert(text);
console.error(text);
}
})
}).catch(err => {
alert("Failed to create artist. Check the console for details.");
console.error(err);
});
});
});

View file

@ -33,7 +33,7 @@ form#delete-account input {
justify-content: space-between;
color: var(--fg-3);
background: var(--bg-2);
background-color: var(--bg-2);
box-shadow: var(--shadow-md);
border-radius: 16px;
}

View file

@ -6,7 +6,7 @@
gap: 1.2em;
border-radius: 16px;
background: var(--bg-2);
background-color: var(--bg-2);
box-shadow: var(--shadow-md);
}
@ -50,18 +50,11 @@ input[type="text"] {
font-family: inherit;
font-weight: inherit;
color: inherit;
background: var(--bg-0);
background-color: var(--bg-0);
border: none;
border-radius: 4px;
outline: none;
}
input[type="text"]:hover {
border-color: #80808080;
}
input[type="text"]:active,
input[type="text"]:focus {
border-color: #808080;
}
.artist-actions {
margin-top: auto;
@ -84,7 +77,7 @@ input[type="text"]:focus {
align-items: center;
border-radius: 16px;
background: var(--bg-2);
background-color: var(--bg-2);
box-shadow: var(--shadow-md);
cursor: pointer;
@ -92,7 +85,7 @@ input[type="text"]:focus {
}
.credit:hover {
background: var(--bg-1);
background-color: var(--bg-1);
}
.release-artwork {

View file

@ -12,7 +12,7 @@ input[type="text"] {
gap: 1.2em;
border-radius: 8px;
background: var(--bg-2);
background-color: var(--bg-2);
box-shadow: var(--shadow-md);
transition: background .1s ease-out, color .1s ease-out;
@ -33,7 +33,7 @@ input[type="text"] {
.release-artwork #remove-artwork {
margin-top: .5em;
padding: .3em .6em;
background: var(--bg-3);
background-color: var(--bg-3);
}
.release-info {
@ -62,13 +62,13 @@ input[type="text"] {
}
#title:hover {
background: var(--bg-3);
background-color: var(--bg-3);
border-color: var(--fg-0);
}
#title:active,
#title:focus {
background: var(--bg-3);
background-color: var(--bg-3);
}
.release-title small {
@ -93,7 +93,7 @@ input[type="text"] {
.release-info table tr td:not(:first-child) select:hover,
.release-info table tr td:not(:first-child) input:hover,
.release-info table tr td:not(:first-child) textarea:hover {
background: var(--bg-3);
background-color: var(--bg-3);
cursor: pointer;
}
.release-info table td select,
@ -127,7 +127,7 @@ input[type="text"] {
.release-actions button,
.release-actions .button {
color: var(--fg-2);
background: var(--bg-3);
background-color: var(--bg-3);
}
dialog {
@ -234,7 +234,7 @@ dialog div.dialog-actions {
gap: 1em;
border-radius: 8px;
background: var(--bg-2);
background-color: var(--bg-2);
box-shadow: var(--shadow-md);
}
@ -280,7 +280,7 @@ dialog div.dialog-actions {
border: none;
border-radius: 4px;
color: var(--fg-2);
background: var(--bg-0);
background-color: var(--bg-0);
}
#editcredits .credit .credit-info .credit-attribute input[type="checkbox"] {
margin: 0 .3em;
@ -299,6 +299,7 @@ dialog div.dialog-actions {
#editcredits .credit .delete {
margin-right: .5em;
cursor: pointer;
overflow: visible;
}
#editcredits .credit .delete:hover {
text-decoration: underline;
@ -315,14 +316,17 @@ dialog div.dialog-actions {
display: flex;
gap: .5em;
cursor: pointer;
background-color: var(--bg-2);
}
#addcredit ul li.new-artist:nth-child(even) {
background: #f0f0f0;
background-color: var(--bg-1);
}
#addcredit ul li.new-artist:hover {
background: #e0e0e0;
background-color: var(--bg-2);
}
#addcredit .new-artist .artist-id {
@ -375,6 +379,8 @@ dialog div.dialog-actions {
#editlinks tr {
display: flex;
background-color: var(--bg-1);
transition: background-color .1s ease-out;
}
#editlinks th {
@ -385,7 +391,7 @@ dialog div.dialog-actions {
}
#editlinks tr:nth-child(odd) {
background: #f8f8f8;
background-color: var(--bg-2);
}
#editlinks tr th,
@ -416,6 +422,11 @@ dialog div.dialog-actions {
width: 1em;
pointer-events: none;
}
@media (prefers-color-scheme: dark) {
#editlinks tr .grabber img {
filter: invert();
}
}
#editlinks tr .link-name {
width: 8em;
}
@ -454,6 +465,7 @@ dialog div.dialog-actions {
}
#edittracks .track {
background-color: var(--bg-2);
transition: transform .2s ease-out, opacity .2s;
}
@ -476,7 +488,7 @@ dialog div.dialog-actions {
}
#edittracks .track:nth-child(even) {
background: #f0f0f0;
background-color: var(--bg-1);
}
#edittracks .track-number {
@ -492,7 +504,6 @@ dialog div.dialog-actions {
#addtrack ul {
padding: 0;
list-style: none;
background: #f8f8f8;
}
#addtrack ul li.new-track {

View file

@ -8,7 +8,7 @@
gap: 1.2em;
border-radius: 16px;
background: var(--bg-2);
background-color: var(--bg-2);
box-shadow: var(--shadow-md);
}
@ -45,25 +45,13 @@
font-weight: inherit;
font-family: inherit;
font-size: inherit;
background: var(--bg-0);
background-color: var(--bg-0);
border: none;
border-radius: 4px;
outline: none;
color: inherit;
}
.track-info input[type="text"]:hover,
.track-info textarea:hover {
border-color: #80808080;
}
.track-info input[type="text"]:active,
.track-info textarea:active,
.track-info input[type="text"]:focus,
.track-info textarea:focus {
border-color: #808080;
}
.track-actions {
margin-top: 1em;
display: flex;

View file

@ -1,74 +0,0 @@
const newReleaseBtn = document.getElementById("create-release");
const newArtistBtn = document.getElementById("create-artist");
const newTrackBtn = document.getElementById("create-track");
newReleaseBtn.addEventListener("click", event => {
event.preventDefault();
const id = prompt("Enter an ID for this release:");
if (id == null || id == "") return;
fetch("/api/v1/music", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({id})
}).then(res => {
if (res.ok) location = "/admin/releases/" + id;
else {
res.text().then(err => {
alert("Request failed: " + err);
console.error(err);
});
}
}).catch(err => {
alert("Failed to create release. Check the console for details.");
console.error(err);
});
});
newArtistBtn.addEventListener("click", event => {
event.preventDefault();
const id = prompt("Enter an ID for this artist:");
if (id == null || id == "") return;
fetch("/api/v1/artist", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({id})
}).then(res => {
res.text().then(text => {
if (res.ok) {
location = "/admin/artists/" + id;
} else {
alert("Request failed: " + text);
console.error(text);
}
})
}).catch(err => {
alert("Failed to create artist. Check the console for details.");
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/tracks/" + text;
} else {
alert("Request failed: " + text);
console.error(text);
}
})
}).catch(err => {
alert("Failed to create track. Check the console for details.");
console.error(err);
});
});

View file

@ -8,7 +8,7 @@ form#search-form {
padding: 1em;
border-radius: 16px;
color: var(--fg-0);
background: var(--bg-2);
background-color: var(--bg-2);
box-shadow: var(--shadow-md);
}
@ -23,7 +23,7 @@ div#search {
border: none;
border-radius: 16px;
color: var(--fg-1);
background: var(--bg-0);
background-color: var(--bg-0);
box-shadow: var(--shadow-sm);
}
@ -100,8 +100,8 @@ td.log-content {
#logs .log.warn {
color: var(--col-on-warn);
background: var(--col-warn);
background-color: var(--col-warn);
}
#logs .log.warn:hover {
background: var(--col-warn-hover);
background-color: var(--col-warn-hover);
}

View file

@ -6,7 +6,7 @@
gap: 1em;
border-radius: 16px;
background: var(--bg-2);
background-color: var(--bg-2);
box-shadow: var(--shadow-md);
transition: background .1s ease-out, color .1s ease-out;
@ -67,14 +67,14 @@
display: inline-block;
border-radius: 4px;
background: var(--bg-3);
background-color: var(--bg-3);
box-shadow: var(--shadow-sm);
transition: color .1s ease-out, background .1s ease-out;
}
.release .release-actions a:hover {
background: var(--bg-0);
background-color: var(--bg-0);
color: var(--fg-3);
text-decoration: none;
}

25
admin/static/releases.js Normal file
View file

@ -0,0 +1,25 @@
document.addEventListener('readystatechange', () => {
const newReleaseBtn = document.getElementById("create-release");
if (newReleaseBtn) newReleaseBtn.addEventListener("click", event => {
event.preventDefault();
const id = prompt("Enter an ID for this release:");
if (id == null || id == "") return;
fetch("/api/v1/music", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({id})
}).then(res => {
if (res.ok) location = "/admin/releases/" + id;
else {
res.text().then(err => {
alert(err);
console.error(err);
});
}
}).catch(err => {
alert("Failed to create release. Check the console for details.");
console.error(err);
});
});
});

View file

@ -12,7 +12,7 @@
gap: .5em;
border-radius: 16px;
background: var(--bg-2);
background-color: var(--bg-2);
box-shadow: var(--shadow-md);
transition: background .1s ease-out, color .1s ease-out;
@ -44,11 +44,6 @@
opacity: .5;
}
#tracks .track-album.empty {
color: #ff2020;
opacity: 1;
}
#tracks .track-description {
font-style: italic;
}
@ -67,61 +62,4 @@
margin: 0;
display: flex;
flex-direction: row;
/*
justify-content: space-between;
*/
}
/*
.track {
margin-bottom: 1em;
padding: 1em;
display: flex;
flex-direction: column;
gap: .5em;
border-radius: 8px;
background-color: var(--bg-2);
box-shadow: var(--shadow-md);
transition: color .1s ease-out, background-color .1s ease-out;
}
.track p {
margin: 0;
}
.track-id {
width: fit-content;
font-family: "Monaspace Argon", monospace;
font-size: .8em;
font-style: italic;
line-height: 1em;
user-select: all;
}
.track-album {
margin-left: auto;
font-style: italic;
font-size: .75em;
opacity: .5;
}
.track-album.empty {
color: #ff2020;
opacity: 1;
}
.track-description {
font-style: italic;
}
.track-lyrics {
max-height: 10em;
overflow-y: scroll;
}
.track .empty {
opacity: 0.75;
}
*/

24
admin/static/tracks.js Normal file
View file

@ -0,0 +1,24 @@
const newTrackBtn = document.getElementById("create-track");
if (newTrackBtn) 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/tracks/" + text;
} else {
alert(text);
console.error(text);
}
})
}).catch(err => {
alert("Failed to create track. Check the console for details.");
console.error(err);
});
});

View file

@ -12,12 +12,12 @@
<form action="/api/v1/music/{{.Release.ID}}/tracks">
<ul>
{{range $i, $track := .Release.Tracks}}
<li class="track" data-track="{{$track.ID}}" data-title="{{$track.Title}}" data-number="{{$track.Add $i 1}}" draggable="true">
{{range .Release.Tracks}}
<li class="track" data-track="{{.ID}}" data-title="{{.Title}}" data-number="{{.Number}}" draggable="true">
<div>
<p class="track-name">
<span class="track-number">{{.Add $i 1}}</span>
{{$track.Title}}
<span class="track-number">{{.Number}}</span>
{{.Title}}
</p>
<a class="delete">Delete</a>
</div>
@ -49,7 +49,6 @@
deleteBtn.addEventListener("click", e => {
e.preventDefault();
if (!confirm("Are you sure you want to remove " + trackTitle + "?")) return;
trackItem.remove();
refreshTrackNumbers();
});

View file

@ -100,7 +100,7 @@
</div>
</div>
<div class="card" id="credits">
<div id="credits" class="card">
<div class="card-header">
<h2>Credits <small>({{len .Release.Credits}} total)</small></h2>
<a class="button edit"
@ -110,6 +110,7 @@
hx-swap="beforeend"
>Edit</a>
</div>
{{range .Release.Credits}}
<div class="credit">
<img src="{{.Artist.GetAvatar}}" alt="" width="64" loading="lazy" class="artist-avatar">
@ -125,13 +126,13 @@
</div>
{{end}}
{{if not .Release.Credits}}
<p>There are no credits.</p>
<p>This release has no credits.</p>
{{end}}
</div>
<div class="card" id="links">
<div id="links" class="card">
<div class="card-header">
<h2>Links ({{len .Release.Links}})</h2>
<h2>Links <small>({{len .Release.Links}} total)</small></h2>
<a class="button edit"
href="/admin/releases/{{.Release.ID}}/editlinks"
hx-get="/admin/releases/{{.Release.ID}}/editlinks"
@ -139,16 +140,21 @@
hx-swap="beforeend"
>Edit</a>
</div>
{{if .Release.Links}}
<ul>
{{range .Release.Links}}
<a href="{{.URL}}" target="_blank" class="button" data-name="{{.Name}}">{{.Name}} <img class="icon" src="/img/external-link.svg"/></a>
{{end}}
</ul>
{{else}}
<p>This release has no links.</p>
{{end}}
</div>
<div class="card" id="tracks">
<div class="card-header" id="tracks">
<h2>Tracklist ({{len .Release.Tracks}})</h2>
<div id="tracks" class="card">
<div class="card-header">
<h2>Tracks <small>({{len .Release.Tracks}} total)</small></h2>
<a class="button edit"
href="/admin/releases/{{.Release.ID}}/edittracks"
hx-get="/admin/releases/{{.Release.ID}}/edittracks"
@ -156,9 +162,13 @@
hx-swap="beforeend"
>Edit</a>
</div>
{{range $i, $track := .Release.Tracks}}
{{range .Release.Tracks}}
{{block "track" .}}{{end}}
{{end}}
{{if not .Release.Tracks}}
<p>This release has no tracks.</p>
{{end}}
</div>
<div class="card" id="danger">

View file

@ -56,6 +56,7 @@
</main>
<script type="module" src="/admin/static/releases.js"></script>
<script type="module" src="/admin/static/artists.js"></script>
<script type="module" src="/admin/static/index.js"></script>
<script type="module" src="/admin/static/tracks.js"></script>
{{end}}

View file

@ -21,4 +21,6 @@
<p>There are no releases.</p>
{{end}}
</main>
<script type="module" src="/admin/static/releases.js"></script>
{{end}}

View file

@ -12,22 +12,8 @@
</header>
<div id="tracks">
{{range $Track := .Tracks}}
<div class="track">
<h2 class="track-title">
<a href="/admin/tracks/{{$Track.ID}}">{{$Track.Title}}</a>
</h2>
{{if $Track.Description}}
<p class="track-description">{{$Track.GetDescriptionHTML}}</p>
{{else}}
<p class="track-description empty">No description provided.</p>
{{end}}
{{if $Track.Lyrics}}
<p class="track-lyrics">{{$Track.GetLyricsHTML}}</p>
{{else}}
<p class="track-lyrics empty">There are no lyrics.</p>
{{end}}
</div>
{{range .Tracks}}
{{block "track" .}}{{end}}
{{end}}
</div>
</main>

View file

@ -324,6 +324,10 @@ func UpdateReleaseTracks(app *model.AppState, release *model.Release) http.Handl
err = controller.UpdateReleaseTracks(app.DB, release.ID, trackIDs)
if err != nil {
if strings.Contains(err.Error(), "duplicate key") {
http.Error(w, "Release cannot have duplicate tracks", http.StatusBadRequest)
return
}
if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
@ -366,14 +370,14 @@ func UpdateReleaseCredits(app *model.AppState, release *model.Release) http.Hand
err = controller.UpdateReleaseCredits(app.DB, release.ID, credits)
if err != nil {
if strings.Contains(err.Error(), "duplicate key") {
http.Error(w, "Artists may only be credited once\n", http.StatusBadRequest)
http.Error(w, "Artists may only be credited once", http.StatusBadRequest)
return
}
if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return
}
fmt.Printf("WARN: Failed to update links for %s: %s\n", release.ID, err)
fmt.Printf("WARN: Failed to update credits for %s: %s\n", release.ID, err)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
@ -394,6 +398,10 @@ func UpdateReleaseLinks(app *model.AppState, release *model.Release) http.Handle
err = controller.UpdateReleaseLinks(app.DB, release.ID, links)
if err != nil {
if strings.Contains(err.Error(), "duplicate key") {
http.Error(w, "Release cannot have duplicate link names", http.StatusBadRequest)
return
}
if strings.Contains(err.Error(), "no rows") {
http.NotFound(w, r)
return

View file

@ -24,8 +24,3 @@ func (track Track) GetDescriptionHTML() template.HTML {
func (track Track) GetLyricsHTML() template.HTML {
return template.HTML(strings.ReplaceAll(track.Lyrics, "\n", "<br>"))
}
// this function is stupid and i hate that i need it
func (track Track) Add(a int, b int) int {
return a + b
}

View file

@ -31,13 +31,3 @@ func Test_Track_LyricsHTML(t *testing.T) {
t.Errorf(`track lyrics incorrectly formatted (want "%s", got "%s")`, want, got)
}
}
func Test_Track_Add(t *testing.T) {
track := Track{}
want := 4
got := track.Add(2, 2)
if want != got {
t.Errorf(`somehow, we screwed up addition. (want %d, got %d)`, want, got)
}
}

View file

@ -153,7 +153,7 @@ header ul li a:hover {
flex-direction: column;
gap: 1rem;
border-bottom: 1px solid #888;
background: var(--background);
background-color: var(--background);
display: none;
}

View file

@ -16,7 +16,7 @@
body {
margin: 0;
padding: 0;
background: var(--background);
background-color: var(--background);
color: var(--on-background);
font-family: "Monaspace Argon", monospace;
font-size: 18px;
@ -150,7 +150,7 @@ a#backtotop:hover {
@keyframes list-item-fadein {
from {
opacity: 1;
background: var(--links);
background-color: var(--links);
}
to {

View file

@ -30,7 +30,7 @@ header {
background-size: cover;
background-position: center;
filter: blur(25px) saturate(25%) brightness(0.5);
-webkit-filter: blur(25px) saturate(25%) brightness(0.5);;
-webkit-filter: blur(25px) saturate(25%) brightness(0.5);
animation: background-init .5s forwards,background-loop 30s ease-in-out infinite
}

View file

@ -83,9 +83,9 @@
{{else if .IsSingle}}
{{$Track := index .Tracks 0}}
{{if $Track.Description}}
<p id="description">{{$Track.Description}}</p>
{{index .Tracks 0}}
{{if .Description}}
<p id="description">{{.Description}}</p>
{{end}}
{{end}}
@ -132,18 +132,18 @@
{{else if .Tracks}}
<div id="tracks">
<h2>TRACKS</h2>
{{range $i, $track := .Tracks}}
{{range .Tracks}}
<details>
<summary class="album-track-title">{{$track.Add $i 1}}. {{$track.Title}}</summary>
<summary class="album-track-title">{{.Number}}. {{.Title}}</summary>
{{if $track.Description}}
{{if .Description}}
<p class="album-track-subheading">DESCRIPTION</p>
{{$track.Description}}
{{.Description}}
{{end}}
<p class="album-track-subheading">LYRICS</p>
{{if $track.Lyrics}}
{{$track.GetLyricsHTML}}
{{if .Lyrics}}
{{.GetLyricsHTML}}
{{else}}
<span class="empty">No lyrics.</span>
{{end}}

View file

@ -78,15 +78,15 @@ func ServeGateway(app *model.AppState, release *model.Release) http.Handler {
}
}
response := *release
if release.IsReleased() || privileged {
response.Tracks = release.Tracks
response.Credits = release.Credits
response.Links = release.Links
if !release.IsReleased() && !privileged {
release.Tracks = nil
release.Credits = nil
release.Links = nil
}
err := templates.MusicGatewayTemplate.Execute(w, response)
for i, track := range release.Tracks { track.Number = i + 1 }
err := templates.MusicGatewayTemplate.Execute(w, release)
if err != nil {
fmt.Printf("Error rendering music gateway for %s: %s\n", release.ID, err)