Lyrics are suggested at submission and never imposed. The conversion worker makes one LRCLIB lookup with whatever metadata exists, and the waiting page has a Hae sanoitukset button that re-queries with whatever title and artist are currently typed — which is the case that matters, since our metadata comes from ID3 tags and YouTube uploaders. Neither path overwrites typed text. - lyrics text on both submissions and songs, copied across at publish. Nothing has launched, so the column goes into 001_init.sql rather than a migration - The lock does not cover lyrics: it freezes what the song claims to be, and nobody reviewed the lyrics. So the submitter can still fix them afterwards, or paste them for an old song a year later - The review strip gained a second pane: lyrics on the left, review on the right, so following the words costs no scrolling. No lyrics means no pane, not an empty one. Below 1024px the panes stack - LRC timestamps are stored but stripped for reading — they belong to the player, not the reader - The lyrics box is capped and scrolls inside itself, so a long song cannot stretch the strip past the screen Fixes a real bug found on the way: saveMetadata cleared any field the request did not carry, so publishing wiped the lyrics the worker had just fetched. Fields absent from a request now keep their stored value. The client identifies itself to LRCLIB as "levyraati" and nothing more. Tests cover cleanLyrics keeping line breaks, and fetchLyrics against a local server: synced beats plain, instrumentals and wrong-length takes are skipped, and a miss is empty with no error.
93 lines
4.0 KiB
HTML
93 lines
4.0 KiB
HTML
{{define "submission-status"}}
|
|
<div class="status{{if .Failed}} failed{{end}}" {{if not .Done}}hx-get="/submit/{{.ID}}/status" hx-trigger="every 2s" hx-swap="outerHTML"{{end}}>
|
|
<ol class="segments" aria-label="Lähetyksen tila">
|
|
<li class="{{if .Done}}done{{else}}now{{end}}">Lähetetty</li>
|
|
<li class="{{if .Done}}done{{else if eq .Status "converting"}}now{{else if eq .Status "downloading"}}now{{end}}">
|
|
{{if eq .Status "downloading"}}Ladataan{{else}}Muunnetaan{{end}}</li>
|
|
<li class="{{if .Ready}}done{{else if .Failed}}failed{{end}}">{{if .Failed}}Epäonnistui{{else}}Valmis{{end}}</li>
|
|
</ol>
|
|
<p class="{{if .Failed}}error{{end}}"><strong>{{.Label}}</strong></p>
|
|
{{if .Failed}}
|
|
{{with .StatusMsg}}<p class="muted small">{{.}}</p>{{end}}
|
|
<div class="actions">
|
|
{{if .CanRetry}}
|
|
<form method="post" action="/submit/{{.ID}}/retry">
|
|
<button type="submit">Yritä uudelleen</button>
|
|
</form>
|
|
{{end}}
|
|
<form method="post" action="/submit/{{.ID}}/discard">
|
|
<button type="submit" class="danger">Poista lähetys</button>
|
|
</form>
|
|
</div>
|
|
{{if not .CanRetry}}<p class="muted small">Lataa tiedosto uudelleen, jos haluat yrittää toisen kerran.</p>{{end}}
|
|
{{else}}
|
|
<!-- Outside the form and attached to it with form=, so one button both submits the metadata
|
|
and publishes. Disabled until the audio has finished converting. -->
|
|
<button type="submit" form="meta" {{if not .Ready}}disabled{{end}}>Julkaise</button>
|
|
{{if not .Ready}}<p class="muted small">Julkaise aukeaa kun muunnos on valmis.</p>{{end}}
|
|
{{end}}
|
|
</div>
|
|
{{end}}
|
|
|
|
{{define "saved"}}<span id="saved" class="saved">{{if .}}Tallennettu {{.}}{{end}}</span>{{end}}
|
|
|
|
<!-- Included by the page and returned alone by the suggestion button, so the markup exists once.
|
|
hx-include sends the current title and artist, which is the whole point: the lookup uses what
|
|
the submitter just fixed, not what the tags claimed. -->
|
|
{{define "lyricsfield"}}
|
|
<div id="lyricsfield">
|
|
<label>Sanoitukset <span class="muted small">(vapaaehtoinen)</span>
|
|
<textarea name="lyrics" rows="8" maxlength="20000"
|
|
placeholder="Liitä sanoitukset tähän tai hae ne alta.">{{.Lyrics}}</textarea>
|
|
</label>
|
|
<div class="lyricsbar">
|
|
<button type="button" class="ghost"
|
|
hx-post="/submit/{{.ID}}/lyrics"
|
|
hx-include="[name='title'], [name='artist']"
|
|
hx-target="#lyricsfield" hx-swap="outerHTML">Hae sanoitukset</button>
|
|
{{if .Found}}<span class="muted small">Löytyi — tarkista ja muokkaa tarvittaessa.</span>{{end}}
|
|
{{if .Searched}}{{if not .Found}}
|
|
<span class="muted small">Ei löytynyt. Tarkista nimi ja esittäjä tai liitä sanoitukset itse.</span>
|
|
{{end}}{{end}}
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
|
|
{{define "content"}}
|
|
<h1>Lähetys</h1>
|
|
|
|
{{if .Data.Failed}}{{template "submission-status" .Data}}{{end}}
|
|
|
|
{{if not .Data.Failed}}
|
|
<form id="meta" method="post" action="/submit/{{.Data.ID}}/publish" class="stack"
|
|
hx-post="/submit/{{.Data.ID}}" hx-trigger="input changed delay:1.2s, change"
|
|
hx-target="#saved" hx-swap="outerHTML">
|
|
<label>Kappaleen nimi
|
|
<input name="title" value="{{.Data.Title}}" maxlength="100" required>
|
|
</label>
|
|
<label>Esittäjä
|
|
<input name="artist" value="{{.Data.Artist}}" maxlength="100" required>
|
|
</label>
|
|
<label>Genre
|
|
<select name="genre" required>
|
|
<option value="">— valitse —</option>
|
|
{{$current := .Data.Genre}}
|
|
{{range .Data.Genres}}
|
|
<option value="{{.Code}}" {{if eq .Code $current}}selected{{end}}>{{.Label}}</option>
|
|
{{end}}
|
|
</select>
|
|
</label>
|
|
<label>Esittely <span class="muted small">(vapaaehtoinen)</span>
|
|
<textarea name="description" rows="5" maxlength="2000">{{.Data.Description}}</textarea>
|
|
</label>
|
|
{{template "lyricsfield" dict "ID" .Data.ID "Lyrics" .Data.Lyrics}}
|
|
{{template "saved" ""}}
|
|
</form>
|
|
|
|
<p class="muted">Tiedot tallentuvat itsestään kirjoittaessasi. Nimi, esittäjä ja genre tarvitaan
|
|
ennen julkaisua.</p>
|
|
|
|
{{template "submission-status" .Data}}
|
|
{{end}}
|
|
{{end}}
|