Show all server playlists with synced/unsynced visual separation
Server Playlists tab now displays ALL playlists from the media server, split into two sections: Synced Playlists (with mirrored/history match, full opacity, "Synced" badge, "Open Editor" action) and Other Server Playlists (everything else, dimmed at 70%, "View Tracks" action). Both sections have header with icon, title, and count. Unsynced cards fade to full opacity on hover.
This commit is contained in:
parent
a867bba18f
commit
922b350983
2 changed files with 108 additions and 12 deletions
|
|
@ -74151,20 +74151,29 @@ async function loadServerPlaylists() {
|
|||
return;
|
||||
}
|
||||
|
||||
// Only show server playlists that have a matching mirrored playlist or sync history entry
|
||||
// Separate synced vs non-synced playlists
|
||||
const mirroredNames = new Set(mirroredAll.map(p => p.name.trim().toLowerCase()));
|
||||
const syncedNames = new Set(historyNames.map(n => n.trim().toLowerCase()));
|
||||
const filtered = data.playlists.filter(pl => {
|
||||
const synced = [];
|
||||
const unsynced = [];
|
||||
for (const pl of data.playlists) {
|
||||
const key = pl.name.trim().toLowerCase();
|
||||
return mirroredNames.has(key) || syncedNames.has(key);
|
||||
});
|
||||
if (mirroredNames.has(key) || syncedNames.has(key)) {
|
||||
pl._synced = true;
|
||||
synced.push(pl);
|
||||
} else {
|
||||
pl._synced = false;
|
||||
unsynced.push(pl);
|
||||
}
|
||||
}
|
||||
|
||||
_serverPlaylists = filtered;
|
||||
_serverPlaylists = [...synced, ...unsynced];
|
||||
const title = document.getElementById('server-tab-title');
|
||||
if (title) title.textContent = `Server Playlists (${data.server_type ? data.server_type.charAt(0).toUpperCase() + data.server_type.slice(1) : ''})`;
|
||||
const serverName = data.server_type ? data.server_type.charAt(0).toUpperCase() + data.server_type.slice(1) : '';
|
||||
if (title) title.textContent = `Server Playlists (${serverName})`;
|
||||
|
||||
if (filtered.length === 0) {
|
||||
if (container) container.innerHTML = '<div class="playlist-placeholder">No synced playlists found. Only server playlists that have been synced via SoulSync are shown here.</div>';
|
||||
if (synced.length === 0 && unsynced.length === 0) {
|
||||
if (container) container.innerHTML = '<div class="playlist-placeholder">No playlists found on your media server.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -74176,11 +74185,13 @@ async function loadServerPlaylists() {
|
|||
};
|
||||
const sIcon = serverIcons[data.server_type] || serverIcons.plex;
|
||||
|
||||
container.innerHTML = `<div class="server-pl-grid">${filtered.map((pl, i) => {
|
||||
function _renderPlCard(pl, i, isSynced) {
|
||||
const hue = (i * 37 + 200) % 360;
|
||||
const safeName = _esc(pl.name).replace(/'/g, "\\'");
|
||||
const cardClass = isSynced ? 'server-pl-card' : 'server-pl-card server-pl-unsynced';
|
||||
const action = isSynced ? 'Open Editor' : 'View Tracks';
|
||||
return `
|
||||
<div class="server-pl-card" onclick="openServerPlaylistEditor('${pl.id}', '${safeName}')" style="animation-delay: ${i * 0.04}s; --card-hue: ${hue}">
|
||||
<div class="${cardClass}" onclick="openServerPlaylistEditor('${pl.id}', '${safeName}')" style="animation-delay: ${i * 0.04}s; --card-hue: ${hue}">
|
||||
<div class="server-pl-card-glow"></div>
|
||||
<div class="server-pl-card-top">
|
||||
<div class="server-pl-card-icon-wrap">
|
||||
|
|
@ -74194,16 +74205,43 @@ async function loadServerPlaylists() {
|
|||
<div class="server-pl-card-name">${_esc(pl.name)}</div>
|
||||
<div class="server-pl-card-meta">
|
||||
<span class="server-pl-track-count">${pl.track_count}</span> tracks
|
||||
${isSynced ? '<span class="server-pl-synced-badge">Synced</span>' : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div class="server-pl-card-footer">
|
||||
<span class="server-pl-card-action">Open Editor</span>
|
||||
<span class="server-pl-card-action">${action}</span>
|
||||
<span class="server-pl-card-arrow">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}).join('')}</div>`;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
|
||||
if (synced.length > 0) {
|
||||
html += `<div class="server-pl-section">
|
||||
<div class="server-pl-section-header">
|
||||
<span class="server-pl-section-icon">🔗</span>
|
||||
<span class="server-pl-section-title">Synced Playlists</span>
|
||||
<span class="server-pl-section-count">${synced.length}</span>
|
||||
</div>
|
||||
<div class="server-pl-grid">${synced.map((pl, i) => _renderPlCard(pl, i, true)).join('')}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
if (unsynced.length > 0) {
|
||||
html += `<div class="server-pl-section server-pl-section-unsynced">
|
||||
<div class="server-pl-section-header">
|
||||
<span class="server-pl-section-icon">🎵</span>
|
||||
<span class="server-pl-section-title">Other Server Playlists</span>
|
||||
<span class="server-pl-section-count">${unsynced.length}</span>
|
||||
</div>
|
||||
<div class="server-pl-grid">${unsynced.map((pl, i) => _renderPlCard(pl, i + synced.length, false)).join('')}</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
container.innerHTML = html;
|
||||
|
||||
} catch (e) {
|
||||
if (container) container.innerHTML = `<div class="playlist-placeholder">Error: ${e.message}</div>`;
|
||||
|
|
|
|||
|
|
@ -54036,6 +54036,64 @@ tr.tag-diff-same {
|
|||
SERVER PLAYLIST MANAGER — Sync Page Server Tab
|
||||
================================================================================== */
|
||||
|
||||
/* Playlist sections */
|
||||
.server-pl-section {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.server-pl-section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.server-pl-section-icon { font-size: 16px; }
|
||||
|
||||
.server-pl-section-title {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.8px;
|
||||
}
|
||||
|
||||
.server-pl-section-count {
|
||||
font-size: 10px;
|
||||
color: rgba(255, 255, 255, 0.25);
|
||||
padding: 1px 7px;
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* Unsynced cards — dimmer, different accent */
|
||||
.server-pl-unsynced {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.server-pl-unsynced:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.server-pl-unsynced .server-pl-card-glow {
|
||||
background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.03), transparent 60%) !important;
|
||||
}
|
||||
|
||||
/* Synced badge */
|
||||
.server-pl-synced-badge {
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
color: rgb(var(--accent-light-rgb));
|
||||
padding: 1px 6px;
|
||||
background: rgba(var(--accent-rgb), 0.12);
|
||||
border: 1px solid rgba(var(--accent-rgb), 0.2);
|
||||
border-radius: 6px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
/* Playlist grid layout */
|
||||
.server-pl-grid {
|
||||
display: grid;
|
||||
|
|
|
|||
Loading…
Reference in a new issue