mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Add manual track mappings display to web UI
- Shows all manual mappings in Active Playlists tab - Displays summary counts (total, jellyfin, external) - Table shows playlist, Spotify ID, type, target, and creation date - Color-coded badges for jellyfin vs external mappings - Auto-refreshes every 30 seconds - Helps review mappings before phasing out local ones
This commit is contained in:
@@ -676,6 +676,51 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Manual Track Mappings Section -->
|
||||
<div class="card">
|
||||
<h2>
|
||||
Manual Track Mappings
|
||||
<div class="actions">
|
||||
<button onclick="fetchTrackMappings()">Refresh</button>
|
||||
</div>
|
||||
</h2>
|
||||
<p style="color: var(--text-secondary); margin-bottom: 12px;">
|
||||
Manual mappings override automatic matching. <strong>Local (Jellyfin)</strong> mappings will be phased out in favor of the Spotify Import plugin.
|
||||
</p>
|
||||
<div id="mappings-summary" style="display: flex; gap: 20px; margin-bottom: 16px; padding: 12px; background: var(--bg-tertiary); border-radius: 6px;">
|
||||
<div>
|
||||
<span style="color: var(--text-secondary);">Total:</span>
|
||||
<span style="font-weight: 600; margin-left: 8px;" id="mappings-total">0</span>
|
||||
</div>
|
||||
<div>
|
||||
<span style="color: var(--text-secondary);">Jellyfin (Local):</span>
|
||||
<span style="font-weight: 600; margin-left: 8px; color: var(--accent);" id="mappings-jellyfin">0</span>
|
||||
</div>
|
||||
<div>
|
||||
<span style="color: var(--text-secondary);">External:</span>
|
||||
<span style="font-weight: 600; margin-left: 8px; color: var(--success);" id="mappings-external">0</span>
|
||||
</div>
|
||||
</div>
|
||||
<table class="playlist-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Playlist</th>
|
||||
<th>Spotify ID</th>
|
||||
<th>Type</th>
|
||||
<th>Target</th>
|
||||
<th>Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="mappings-table-body">
|
||||
<tr>
|
||||
<td colspan="5" class="loading">
|
||||
<span class="spinner"></span> Loading mappings...
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Configuration Tab -->
|
||||
@@ -1337,6 +1382,52 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchTrackMappings() {
|
||||
try {
|
||||
const res = await fetch('/api/admin/mappings/tracks');
|
||||
const data = await res.json();
|
||||
|
||||
// Update summary
|
||||
document.getElementById('mappings-total').textContent = data.totalCount || 0;
|
||||
document.getElementById('mappings-jellyfin').textContent = data.jellyfinCount || 0;
|
||||
document.getElementById('mappings-external').textContent = data.externalCount || 0;
|
||||
|
||||
const tbody = document.getElementById('mappings-table-body');
|
||||
|
||||
if (data.mappings.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="5" style="text-align:center;color:var(--text-secondary);padding:40px;">No manual mappings found.</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
tbody.innerHTML = data.mappings.map(m => {
|
||||
const typeColor = m.type === 'jellyfin' ? 'var(--accent)' : 'var(--success)';
|
||||
const typeBadge = `<span style="display:inline-block;padding:2px 8px;border-radius:4px;font-size:0.8rem;background:${typeColor}20;color:${typeColor};font-weight:500;">${m.type}</span>`;
|
||||
|
||||
let targetDisplay = '';
|
||||
if (m.type === 'jellyfin') {
|
||||
targetDisplay = `<span style="font-family:monospace;font-size:0.85rem;">${m.jellyfinId}</span>`;
|
||||
} else {
|
||||
targetDisplay = `<span style="font-family:monospace;font-size:0.85rem;color:var(--success);">${m.externalProvider}/${m.externalId}</span>`;
|
||||
}
|
||||
|
||||
const createdDate = m.createdAt ? new Date(m.createdAt).toLocaleString() : '-';
|
||||
|
||||
return `
|
||||
<tr>
|
||||
<td><strong>${escapeHtml(m.playlist)}</strong></td>
|
||||
<td style="font-family:monospace;font-size:0.85rem;color:var(--text-secondary);">${m.spotifyId}</td>
|
||||
<td>${typeBadge}</td>
|
||||
<td>${targetDisplay}</td>
|
||||
<td style="color:var(--text-secondary);font-size:0.85rem;">${createdDate}</td>
|
||||
</tr>
|
||||
`;
|
||||
}).join('');
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch track mappings:', error);
|
||||
showToast('Failed to fetch track mappings', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchConfig() {
|
||||
try {
|
||||
const res = await fetch('/api/admin/config');
|
||||
@@ -2500,6 +2591,7 @@
|
||||
// Initial load
|
||||
fetchStatus();
|
||||
fetchPlaylists();
|
||||
fetchTrackMappings();
|
||||
fetchJellyfinUsers();
|
||||
fetchJellyfinPlaylists();
|
||||
fetchConfig();
|
||||
@@ -2508,6 +2600,7 @@
|
||||
setInterval(() => {
|
||||
fetchStatus();
|
||||
fetchPlaylists();
|
||||
fetchTrackMappings();
|
||||
}, 30000);
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user