mirror of
https://github.com/gabehf/music-importer.git
synced 2026-04-22 11:31:52 -07:00
i love slop
This commit is contained in:
parent
853f08221f
commit
1d69eeb573
9 changed files with 975 additions and 85 deletions
297
index.html.tmpl
Normal file
297
index.html.tmpl
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Music Importer</title>
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
background: #111;
|
||||
color: #eee;
|
||||
text-align: center;
|
||||
padding: 60px 24px 80px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 { margin-bottom: 32px; }
|
||||
|
||||
button {
|
||||
font-size: 32px;
|
||||
padding: 20px 40px;
|
||||
border-radius: 10px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
}
|
||||
button:disabled {
|
||||
background: #555;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* ── Last run summary ── */
|
||||
.session {
|
||||
margin: 48px auto 0;
|
||||
max-width: 820px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.session-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
border-bottom: 1px solid #333;
|
||||
padding-bottom: 8px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.session-header h2 { margin: 0; font-size: 18px; color: #ccc; }
|
||||
.session-header .duration { font-size: 13px; color: #666; }
|
||||
|
||||
.album {
|
||||
background: #1a1a1a;
|
||||
border: 1px solid #2a2a2a;
|
||||
border-radius: 8px;
|
||||
padding: 16px 20px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.album-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.album-name {
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
flex: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.badge {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.badge-ok { background: #1e4d2b; color: #4CAF50; }
|
||||
.badge-warn { background: #4d3a00; color: #f0a500; }
|
||||
.badge-fatal { background: #4d1a1a; color: #e05050; }
|
||||
|
||||
/* ── Metadata row ── */
|
||||
.metadata {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 14px;
|
||||
flex-wrap: wrap;
|
||||
font-size: 12px;
|
||||
color: #777;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.metadata-title {
|
||||
color: #aaa;
|
||||
font-size: 13px;
|
||||
}
|
||||
.metadata-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
background: #222;
|
||||
border-radius: 4px;
|
||||
padding: 2px 7px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.pill-label { color: #555; }
|
||||
.pill-beets { color: #7ec8e3; }
|
||||
.pill-musicbrainz { color: #c084fc; }
|
||||
.pill-file_tags { color: #f0a500; }
|
||||
.pill-unknown { color: #888; }
|
||||
|
||||
/* ── Rich info grid ── */
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||
gap: 6px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.info-card {
|
||||
background: #222;
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.info-card-label {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: #555;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.info-card-value {
|
||||
color: #ccc;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.info-card-sub {
|
||||
margin-top: 3px;
|
||||
color: #666;
|
||||
font-size: 11px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.info-ok { color: #4CAF50; }
|
||||
.info-warn { color: #f0a500; }
|
||||
.info-dim { color: #555; }
|
||||
|
||||
/* ── Pipeline steps ── */
|
||||
.steps-label {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: #444;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.steps {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 6px;
|
||||
}
|
||||
.step {
|
||||
font-size: 12px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
background: #222;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.step-label { color: #888; }
|
||||
.step-ok { color: #4CAF50; }
|
||||
.step-warn { color: #f0a500; }
|
||||
.step-fatal { color: #e05050; }
|
||||
.step-err { font-size: 11px; color: #c0392b; margin-top: 2px; word-break: break-word; }
|
||||
|
||||
footer {
|
||||
position: fixed;
|
||||
bottom: 16px;
|
||||
width: 100%;
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
text-align: center;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Music Importer</h1>
|
||||
|
||||
<form action="/run" method="POST">
|
||||
<button type="submit" {{if .Running}}disabled{{end}}>
|
||||
{{if .Running}}Importer Running...{{else}}Run Importer{{end}}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{{with .Session}}
|
||||
<div class="session">
|
||||
<div class="session-header">
|
||||
<h2>Last Run — {{.StartedAt.Format "Jan 2, 2006 15:04:05"}}</h2>
|
||||
<span class="duration">{{duration .StartedAt .FinishedAt}}</span>
|
||||
</div>
|
||||
|
||||
{{range .Albums}}{{$album := .}}
|
||||
<div class="album">
|
||||
<div class="album-header">
|
||||
<span class="album-name" title="{{.Path}}">{{.Name}}</span>
|
||||
{{if .Succeeded}}
|
||||
{{if .HasWarnings}}
|
||||
<span class="badge badge-warn">⚠ warnings</span>
|
||||
{{else}}
|
||||
<span class="badge badge-ok">✓ ok</span>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<span class="badge badge-fatal">✗ failed at {{.FatalStep}}</span>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{with .Metadata}}
|
||||
<div class="metadata">
|
||||
<span class="metadata-title">{{.Artist}} — {{.Album}}{{if .Year}} ({{.Year}}){{end}}</span>
|
||||
{{if $album.MetadataSource}}
|
||||
<span class="metadata-pill">
|
||||
<span class="pill-label">via</span>
|
||||
{{if eq (print $album.MetadataSource) "beets"}}
|
||||
<span class="pill-beets">beets</span>
|
||||
{{else if eq (print $album.MetadataSource) "musicbrainz"}}
|
||||
<span class="pill-musicbrainz">MusicBrainz</span>
|
||||
{{else if eq (print $album.MetadataSource) "file_tags"}}
|
||||
<span class="pill-file_tags">file tags</span>
|
||||
{{else}}
|
||||
<span class="pill-unknown">unknown</span>
|
||||
{{end}}
|
||||
</span>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{/* ── Rich info cards ── */}}
|
||||
<div class="info-grid">
|
||||
{{/* Tracks */}}
|
||||
<div class="info-card">
|
||||
<div class="info-card-label">Tracks</div>
|
||||
<div class="info-card-value">{{.TrackCount}}</div>
|
||||
</div>
|
||||
|
||||
{{/* Lyrics */}}
|
||||
<div class="info-card">
|
||||
<div class="info-card-label">Lyrics</div>
|
||||
{{if eq .LyricsStats.Total 0}}
|
||||
<div class="info-card-value info-dim">n/a</div>
|
||||
{{else}}
|
||||
<div class="info-card-value {{if gt .LyricsStats.Downloaded 0}}info-ok{{else}}info-dim{{end}}">
|
||||
{{.LyricsStats.Downloaded}} / {{.LyricsStats.Total}}
|
||||
</div>
|
||||
<div class="info-card-sub">
|
||||
{{if gt .LyricsStats.Synced 0}}<span class="info-ok">{{.LyricsStats.Synced}} synced</span>{{end}}
|
||||
{{if and (gt .LyricsStats.Synced 0) (gt .LyricsStats.Plain 0)}} · {{end}}
|
||||
{{if gt .LyricsStats.Plain 0}}<span class="info-warn">{{.LyricsStats.Plain}} plain</span>{{end}}
|
||||
{{if gt .LyricsStats.AlreadyHad 0}}<span class="info-dim"> {{.LyricsStats.AlreadyHad}} existing</span>{{end}}
|
||||
{{if gt .LyricsStats.NotFound 0}}<span class="info-dim"> {{.LyricsStats.NotFound}} missing</span>{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{/* Cover art */}}
|
||||
<div class="info-card">
|
||||
<div class="info-card-label">Cover Art</div>
|
||||
{{if .CoverArtStats.Found}}
|
||||
{{if .CoverArtStats.Embedded}}
|
||||
<div class="info-card-value info-ok">Embedded</div>
|
||||
<div class="info-card-sub info-dim">{{.CoverArtStats.Source}}</div>
|
||||
{{else}}
|
||||
<div class="info-card-value info-warn">Found, not embedded</div>
|
||||
<div class="info-card-sub info-dim">{{.CoverArtStats.Source}}</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="info-card-value info-dim">Not found</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="steps-label">Pipeline</div>
|
||||
<div class="steps">
|
||||
{{stepCell "Clean Tags" .CleanTags ""}}
|
||||
{{stepCell "Metadata" .TagMetadata .FatalStep}}
|
||||
{{stepCell "Lyrics" .Lyrics ""}}
|
||||
{{stepCell "ReplayGain" .ReplayGain .FatalStep}}
|
||||
{{stepCell "Cover Art" .CoverArt .FatalStep}}
|
||||
{{stepCell "Move" .Move ""}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<footer>{{.Version}}</footer>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue