9a116f3da4
Credentials page now shows a fact-checked one-line description per service and marks Spotify/Soulseek/Navidrome as Required (no toggle -- they're load-bearing for every playlist sync). The four optional integrations (Bandcamp, AzuraCast, Qobuz, Telegram) get a real on/off switch: Bandcamp pauses its scheduled job (same mechanism as the Jobs page), the other three just remove their rendered credential file, which the scripts that read them already treat as "not configured, skip" -- no script changes needed. Disabled optional scopes also drop off the dashboard's credential status row. New Settings > Artist Casing page to view/add/remove entries in artist-canonical.list without shelling in -- adding a name that already matches case-insensitively replaces its casing in place instead of duplicating. Playlist detail page's status section is now two side-by-side columns: the original Spotify tracklist on the left, and a tabbed Downloaded / Waiting-to-download view on the right (client-side tabs via Alpine), both with an internal scroll area so long playlists don't pull the columns out of sync. Uses the existing status_service reconciliation (sldl index + beets + quarantine) -- no backend changes needed there.
52 lines
2.3 KiB
HTML
52 lines
2.3 KiB
HTML
{% extends "settings/_layout.html" %}
|
|
{% block settings_title %}Artist Casing{% endblock %}
|
|
{% block settings_content %}
|
|
<h2 style="margin-top:0;">Artist casing</h2>
|
|
<p class="muted" style="margin-top:-0.75rem; max-width:60ch;">
|
|
Canonical spelling/casing for artist names, exactly as they're stylized on Spotify.
|
|
The <span class="mono">Normalize artist casing</span> job (Settings → <a href="/settings/jobs">Jobs</a>)
|
|
rewrites any album artist tag that matches a name here case-insensitively but differs
|
|
in casing — e.g. <span class="mono">MALL GRAB</span> → <span class="mono">Mall Grab</span>.
|
|
This prevents Linux and Windows from disagreeing about whether two folder names are
|
|
the same, which breaks Syncthing.
|
|
</p>
|
|
|
|
<div class="panel" style="max-width:560px;">
|
|
<form method="post" action="/settings/artist-casing/add" style="display:flex; gap:0.6rem; align-items:flex-end;">
|
|
<div class="field wide" style="margin-bottom:0; flex:1;">
|
|
<label for="name">Artist name</label>
|
|
<input type="text" id="name" name="name" placeholder="exactly as Spotify displays it" required>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary">Add</button>
|
|
</form>
|
|
</div>
|
|
|
|
<h2>Canonical list <span class="muted" style="font-weight:400;">({{ entries | length }})</span></h2>
|
|
<div class="table-wrap scroll">
|
|
<table>
|
|
<thead><tr><th>Artist</th><th></th></tr></thead>
|
|
<tbody>
|
|
{% for name in entries | sort(case_sensitive=False) %}
|
|
<tr>
|
|
<td>{{ name }}</td>
|
|
<td class="actions-cell">
|
|
<form method="post" action="/settings/artist-casing/remove" class="inline"
|
|
onsubmit="return confirm('Remove {{ name }} from the canonical list?')">
|
|
<input type="hidden" name="name" value="{{ name }}">
|
|
<button type="submit" class="btn btn-sm btn-ghost">Remove</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
<tr class="empty-row"><td colspan="2">
|
|
<div class="empty-state">
|
|
<span class="sparkle" style="position:relative; display:inline-block; margin-bottom:0.5rem;"></span>
|
|
<p class="muted" style="margin:0;">No canonical names yet. Add one above.</p>
|
|
</div>
|
|
</td></tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endblock %}
|