feat: split downloads view into active and queued sections with separate tables and dynamic row distribution
Replace single downloads table with two-section layout: "Aktive Downloads" for downloading items and "Weitere Queue-Einträge" for queued/paused items. Inject dual-table HTML structure with section headers showing item counts. Update renderDownloads() to distribute rows by status into activeDownloadsBody/otherDownloadsBody with fallback empty states. Add .download-table grid layout, .download-section styling
This commit is contained in:
@@ -89,7 +89,7 @@ header{z-index:100!important}
|
||||
<script>
|
||||
document.querySelectorAll('#movies tbody tr td:nth-child(5) .badge,#sonarr tbody tr td:nth-child(5) .badge').forEach(badge=>{if(badge.textContent.includes('Deutsch'))badge.classList.add('subtitle-de')});
|
||||
document.querySelectorAll('#movies tbody tr,#sonarr tbody tr').forEach(row=>{if(row.querySelector('.state.downloading'))row.children[1].querySelector('.badge')?.classList.add('downloading-badge')});
|
||||
const downloadBody=document.querySelector('#downloadsBody');
|
||||
const downloadTable=document.querySelector('.download-table');if(downloadTable){downloadTable.innerHTML='<section class="download-section"><div class="download-section-head"><h3>Aktive Downloads</h3><span id="activeDownloadSectionCount">0</span></div><table id="downloadsTable"><thead><tr><th class="col-title">Download</th><th class="col-state">Status</th><th class="col-progress">Fortschritt</th><th class="col-size">Größe / Rest</th><th class="col-time">Restzeit</th><th class="col-source">Quelle</th><th class="col-language">Sprache</th></tr></thead><tbody id="activeDownloadsBody"></tbody></table></section><section class="download-section download-secondary"><div class="download-section-head"><h3>Weitere Queue-Einträge</h3><span id="otherDownloadSectionCount">0</span></div><table id="otherDownloadsTable"><thead><tr><th class="col-title">Download</th><th class="col-state">Status</th><th class="col-progress">Fortschritt</th><th class="col-size">Größe / Rest</th><th class="col-time">Restzeit</th><th class="col-source">Quelle</th><th class="col-language">Sprache</th></tr></thead><tbody id="otherDownloadsBody"></tbody></table></section>'}const downloadBody=document.querySelector('#activeDownloadsBody'),otherDownloadBody=document.querySelector('#otherDownloadsBody');
|
||||
function html(value){return String(value??'—').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]))}
|
||||
function renderDownloads(data){if(!downloadBody)return;const items=data.items||[], active=Number(data.active_count)||0, history=Number(data.history_count)||0, paused=!!data.paused, status=data.error?'Fehler':(paused?'Pausiert':(data.queue_status||'Idle'));document.querySelector('#downloadCount').textContent=`${items.length} Einträge`;const dashboardDownloads=document.querySelector('#dashboardDownloads'),dashboardRemaining=document.querySelector('#dashboardRemaining'),dashboardSabStatus=document.querySelector('#dashboardSabStatus');if(dashboardDownloads)dashboardDownloads.textContent=active;if(dashboardRemaining)dashboardRemaining.textContent=data.remaining?`Noch ${data.remaining}`:'Queue ist leer';if(dashboardSabStatus)dashboardSabStatus.textContent=data.error?'Fehler':(paused?'Pausiert':'Aktiv');document.querySelector('#sabRemaining').textContent=data.remaining||'—';document.querySelector('#sabRemainingHint').textContent=active?`${active} Einträge in der Queue`:'Queue ist leer';document.querySelector('#sabActive').textContent=active;document.querySelector('#sabHistory').textContent=`Verlauf: ${history} Einträge`;document.querySelector('#sabSpeedCard').textContent=data.speed||'—';document.querySelector('#sabStatusCard').textContent=status;document.querySelector('#sabQueueCard').innerHTML=`<span class="queue-dot ${paused?'paused':''}"></span><span>${html(paused?'Pausiert':(data.error?'Nicht erreichbar':'SABnzbd '+status))}</span>`;const error=document.querySelector('#downloadError');error.classList.toggle('hidden',!data.error);error.innerHTML=data.error?`<strong>SABnzbd:</strong> ${html(data.error)}`:'';downloadBody.innerHTML=items.length?items.map(item=>{const pct=Math.max(0,Math.min(100,Number(item.percentage)||0));const languages=(item.languages||[]).map(l=>`<span class="badge">${html(l.flag)} ${html(l.name)}</span>`).join('')||'<span class="muted">Nicht erkannt</span>';return `<tr><td><span class="download-title" title="${html(item.title)}">${html(item.title)}</span><span class="muted">${html(item.category)}</span></td><td><span class="state ${item.completed?'ok':'neutral'}">${html(item.status)}</span></td><td><div class="progress-wrap"><div class="progress-bar"><span style="width:${pct}%"></span></div><span>${pct.toFixed(1)}%</span></div></td><td>${html(item.size)}<br><span class="muted">Rest: ${html(item.remaining)}</span></td><td>${html(item.completed?'—':item.timeleft)}</td><td>${html(item.source)}</td><td><div class="badges">${languages}</div></td></tr>`}).join(''):'<tr><td colspan="7" class="muted">Keine aktuellen Downloads</td></tr>'}
|
||||
async function fetchDownloads(){if(!downloadBody)return;try{const response=await fetch('/api/downloads');const data=await response.json();renderDownloads(data);if(typeof markDownloading==='function')markDownloading(data.items)}catch(error){renderDownloads({error:'Dashboard konnte SABnzbd nicht erreichen.',items:[]})}};
|
||||
@@ -122,6 +122,7 @@ main{max-width:1800px;margin:0 auto;padding:18px 20px 42px}.nav{gap:6px;margin:0
|
||||
.actions{display:flex;align-items:center;gap:7px}.lang-switch,.settings-button{height:36px;min-width:36px;padding:0 9px;background:rgba(48,52,60,.9);border-color:#515865;color:#d9dee7;font-weight:700}.settings-button{font-size:17px;padding:0}.lang-switch:hover,.settings-button:hover{border-color:var(--accent-blue);background:#363c47}.settings-backdrop{position:fixed;inset:0;z-index:200;display:grid;place-items:center;background:rgba(5,7,10,.68);backdrop-filter:blur(8px);animation:fadeIn .2s ease}.settings-backdrop.hidden{display:none}.settings-modal{width:min(440px,calc(100vw - 28px));padding:22px;border:1px solid #4a5362;border-radius:14px;background:linear-gradient(145deg,#292e36,#1f2228);box-shadow:0 24px 80px rgba(0,0,0,.55);animation:modalIn .28s cubic-bezier(.2,.8,.2,1)}.settings-head{display:flex;justify-content:space-between;gap:20px;margin-bottom:22px}.settings-head h2{margin:0 0 5px;font-size:19px}.settings-head p{margin:0;color:var(--muted);font-size:12px}.settings-close{padding:0;width:30px;height:30px;border:0;background:transparent;font-size:25px;color:#aeb5c1}.settings-modal label{display:block;margin:15px 0 7px;color:#cbd1db;font-size:12px;font-weight:700}.settings-modal select{width:100%;height:38px;background:#20242a;border-color:#505967}.settings-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:24px}.settings-actions button{padding:9px 14px}.settings-save{background:#176f40;border-color:#23965b;color:#e4ffef}.settings-save:hover{background:#1c8850}@keyframes fadeIn{from{opacity:0}to{opacity:1}}@keyframes modalIn{from{opacity:0;transform:translateY(12px) scale(.97)}to{opacity:1;transform:none}}
|
||||
@media(max-width:760px){header{padding:0 12px}.head-sub{display:none}main{padding:12px}.view#radarr.active{display:block}.view#radarr>.toolbar{height:auto;margin-bottom:10px}.view#radarr>.stats{height:auto;align-items:initial;flex-wrap:wrap}.view#radarr>.stats>.stat{height:auto}.toolbar{grid-template-columns:1fr}.actions{margin-left:auto}.nav{overflow-x:auto}.nav button{white-space:nowrap}.tablewrap{overflow-x:auto}table{min-width:900px}}
|
||||
main{min-height:calc(100vh - 64px)}
|
||||
.download-table{display:grid;gap:14px;border:0;background:transparent;box-shadow:none;overflow:visible}.download-section{border:1px solid rgba(255,255,255,.09);border-radius:12px;overflow:hidden;background:rgba(31,33,38,.84);box-shadow:0 12px 28px rgba(0,0,0,.18)}.download-section-head{display:flex;align-items:center;justify-content:space-between;padding:13px 15px;border-bottom:1px solid rgba(255,255,255,.08);background:linear-gradient(100deg,rgba(46,56,69,.9),rgba(36,39,45,.9))}.download-section-head h3{margin:0;color:#e8eef5;font-size:14px}.download-section-head span{min-width:24px;padding:3px 8px;border-radius:999px;background:rgba(96,169,230,.16);color:#9ed2f5;font-size:11px;text-align:center}.download-secondary .download-section-head{background:linear-gradient(100deg,rgba(53,47,47,.9),rgba(38,37,41,.9))}.download-secondary .download-section-head span{background:rgba(244,196,48,.15);color:#f4d267}.download-section table{background:transparent}.download-section tbody tr:hover{background:rgba(96,169,230,.07)}
|
||||
</style>
|
||||
<script>
|
||||
document.querySelectorAll('details.series tbody').forEach(tbody=>{let currentSeason=null;[...tbody.querySelectorAll('tr')].forEach(row=>{if(row.classList.contains('season-divider'))return;const match=row.querySelector('.title')?.textContent.match(/^S(\d{2})E\d{2}/i);if(!match||match[1]===currentSeason)return;currentSeason=match[1];const divider=document.createElement('tr');divider.className='season-divider';divider.innerHTML=`<td colspan="7">Staffel ${Number(currentSeason)}</td>`;tbody.insertBefore(divider,row)})});
|
||||
@@ -157,7 +158,7 @@ function markDownloading(items){
|
||||
});
|
||||
}
|
||||
const originalRenderDownloads=renderDownloads;
|
||||
renderDownloads=function(data){originalRenderDownloads(data);markDownloading(data.items);const navCount=document.querySelector('#downloadNavCount'),active=Number(data.active_count)||0;if(navCount){navCount.textContent=active;navCount.classList.toggle('hidden',active===0)}};
|
||||
renderDownloads=function(data){originalRenderDownloads(data);const rows=[...downloadBody.querySelectorAll('tr')],activeRows=[],otherRows=[];rows.forEach(row=>{const status=(row.querySelector('.state')?.textContent||'').trim().toLowerCase();if(status==='downloading')activeRows.push(row);else if(row.querySelector('.download-title'))otherRows.push(row)});downloadBody.innerHTML='';otherDownloadBody.innerHTML='';activeRows.forEach(row=>downloadBody.appendChild(row));otherRows.forEach(row=>otherDownloadBody.appendChild(row));if(!activeRows.length)downloadBody.innerHTML='<tr><td colspan="7" class="muted">Keine aktiven Downloads</td></tr>';if(!otherRows.length)otherDownloadBody.innerHTML='<tr><td colspan="7" class="muted">Keine weiteren Queue-Einträge</td></tr>';document.querySelector('#activeDownloadSectionCount').textContent=activeRows.length;document.querySelector('#otherDownloadSectionCount').textContent=otherRows.length;markDownloading(data.items);const navCount=document.querySelector('#downloadNavCount'),active=Number(data.active_count)||0;if(navCount){navCount.textContent=active;navCount.classList.toggle('hidden',active===0)}};
|
||||
if(downloadBody)fetchDownloads();
|
||||
</script>
|
||||
<script>
|
||||
|
||||
Reference in New Issue
Block a user