Apply row-gap:12px to .view#sonarr.active alongside #radarr. Extend toolbar padding/background/border/box-shadow rules to .view#sonarr>.series-toolbar. Add .series-toolbar input/select to background:rgba(42,46,54,.82) and border-color:#454d59 rules. Include .series-toolbar input:focus/select:focus in background:#303640 focus state
246 lines
118 KiB
HTML
246 lines
118 KiB
HTML
<!doctype html>
|
||
<style>
|
||
@media(max-width:700px){
|
||
html{scroll-behavior:auto!important}
|
||
header{backdrop-filter:none!important;box-shadow:0 2px 8px rgba(0,0,0,.22)!important}
|
||
.dashboard-hero,.overview-card,.insight-card,.system-panel,.service-panel,.datastore-panel,.storage-card,.download-card,.download-section,.tablewrap,.series-category,.missing-table{backdrop-filter:none!important;box-shadow:none!important}
|
||
tbody tr,.series-category>.series,.missing-table .missing-series{transform:none!important;animation:none!important}
|
||
tbody tr:hover,.series-category>.series:hover{transform:none!important}
|
||
.series table tbody tr:not(.season-divider){animation:none!important}
|
||
.tablewrap{contain:layout paint;overscroll-behavior-x:contain}
|
||
.view{contain:layout style}
|
||
}
|
||
</style>
|
||
<style>
|
||
#ioPressurePanel{max-width:none;margin:0;padding:14px}.io-pressure-bars{display:grid;gap:10px}.io-pressure-bars>div{display:grid;grid-template-columns:42px 48px 1fr;align-items:center;gap:8px;color:#9da5b2;font-size:11px}.io-pressure-bars b{color:#e8eef5;text-align:right}.io-pressure-bars .datastore-progress{height:8px}.datastore-grid{align-items:stretch}.datastore-grid>.datastore-panel>.datastore-capacity{margin:0 0 12px;padding:14px}.datastore-right-stack{display:flex;flex-direction:column;gap:12px;min-height:100%}.datastore-right-stack .raid-panel{flex:1;margin:0}.datastore-right-stack #ioPressurePanel{flex:0 0 auto}@media(max-width:900px){ #ioPressurePanel{max-width:none}.datastore-right-stack{min-height:0}}
|
||
</style>
|
||
<style>
|
||
@media(max-width:700px){.nav.legacy-mobile-nav,.nav.legacy-mobile-nav.mobile-drawer-open{display:none!important;visibility:hidden!important;pointer-events:none!important}body.mobile-nav-open .mobile-nav-backdrop{display:none!important}.mobile-drawer-clone:not(.open),.mobile-drawer-shade:not(.open){pointer-events:none!important}}
|
||
</style>
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded',()=>{const root=document.querySelector('#datastore');if(!root)return;const grid=root.querySelector('.datastore-grid');if(grid&&!document.querySelector('#ioPressurePanel')){const panel=document.createElement('section');panel.id='ioPressurePanel';panel.className='datastore-panel io-pressure-panel';panel.innerHTML='<div class="datastore-panel-head"><div><h3>I/O-Auslastung</h3><p>Festplatten-Druck aus Linux PSI</p></div><strong id="ioPressureLabel">Wird geladen …</strong></div><div class="io-pressure-bars"><div><span>Some</span><b id="ioPressureSome">—</b><div class="datastore-progress"><span id="ioPressureSomeBar"></span></div></div><div><span>Full</span><b id="ioPressureFull">—</b><div class="datastore-progress"><span id="ioPressureFullBar"></span></div></div></div>';grid.before(panel)}const pressure=async()=>{try{const data=await (await fetch('/api/datastore')).json();const io=data.io_pressure||{};const some=Number(io.some?.avg10)||0,full=Number(io.full?.avg10)||0;const level=Math.max(some,full),label=level>=20?'Hoch':level>=5?'Beobachten':'Normal';const set=(id,value)=>{const node=document.querySelector(id);if(node)node.textContent=value};set('#ioPressureLabel',io.available?`${label} · ${level.toFixed(1)}%`:'Nicht verfügbar');set('#ioPressureSome',`${some.toFixed(1)}%`);set('#ioPressureFull',`${full.toFixed(1)}%`);const a=document.querySelector('#ioPressureSomeBar'),b=document.querySelector('#ioPressureFullBar');if(a)a.style.width=`${Math.min(100,some)}%`;if(b)b.style.width=`${Math.min(100,full)}%`}catch(error){}};pressure();setInterval(pressure,5000);const disks=document.querySelector('#raidDisks');if(disks){const observer=new MutationObserver(()=>{if([...disks.querySelectorAll('.raid-disk')].some(row=>!row.querySelector('.raid-temperature')))setTimeout(pressure,0)});observer.observe(disks,{childList:true,subtree:true})}});
|
||
</script>
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded',()=>{const root=document.querySelector('#datastore'),grid=root?.querySelector('.datastore-grid'),capacity=root?.querySelector('.datastore-capacity'),pressure=root?.querySelector('#ioPressurePanel');if(!grid||!capacity||!pressure)return;const categories=grid.children[0],raid=grid.children[1];if(categories&&!categories.contains(capacity))categories.prepend(capacity);if(raid&&!raid.contains(pressure))raid.prepend(pressure)});
|
||
</script>
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded',()=>{const grid=document.querySelector('#datastore .datastore-grid'),raid=document.querySelector('#datastore .raid-panel'),pressure=document.querySelector('#ioPressurePanel');if(!grid||!raid||!pressure||grid.querySelector('.datastore-right-stack'))return;const stack=document.createElement('div');stack.className='datastore-right-stack';grid.replaceChild(stack,raid);stack.append(pressure,raid)});
|
||
</script>
|
||
<script>
|
||
if ('serviceWorker' in navigator) {
|
||
window.addEventListener('load', function () {
|
||
navigator.serviceWorker.register('/sw.js?v=5', {scope: '/'}).catch(function () {});
|
||
});
|
||
}
|
||
</script>
|
||
<style>@media(max-width:700px){header .nav,main>.nav{display:none!important}#movies td:nth-child(4) .badge:nth-child(n+2),#movies td:nth-child(5) .badge:nth-child(n+2),#sonarr td:nth-child(4) .badge:nth-child(n+2),#sonarr td:nth-child(5) .badge:nth-child(n+2),#missing td:nth-child(4) .badge:nth-child(n+2),#missing td:nth-child(5) .badge:nth-child(n+2){display:none!important}.year{display:none!important}.title{max-width:100%!important}}</style>
|
||
<script>document.addEventListener('DOMContentLoaded',()=>{const applyPreferred=()=>{const audio=localStorage.getItem('preferred-audio')||'',subs=localStorage.getItem('preferred-subs')||'';document.querySelectorAll('#movies tbody tr,#sonarr tbody tr,#missing tbody tr').forEach(row=>{[[4,audio,'audio-preferred'],[5,subs,'subtitle-preferred']].forEach(([column,preference,className])=>{const badges=[...row.querySelectorAll(`td:nth-child(${column}) .badge`)];if(!badges.length)return;badges.forEach(badge=>badge.classList.remove(className));const preferred=preference?badges.find(badge=>badge.textContent.includes(preference)):null;if(preferred){preferred.classList.add(className);preferred.parentElement.prepend(preferred)}})})};applyPreferred();setInterval(applyPreferred,1000)});</script>
|
||
<script>document.addEventListener('DOMContentLoaded',()=>{const nav=document.querySelector('.nav');if(nav)nav.classList.add('legacy-mobile-nav');const closeAll=()=>{document.body.classList.remove('mobile-nav-open');document.querySelectorAll('.mobile-drawer-clone,.mobile-drawer-shade,.mobile-nav-backdrop').forEach(node=>node.classList.remove('open'));document.querySelectorAll('.nav').forEach(node=>node.classList.remove('mobile-drawer-open'))};document.addEventListener('click',event=>{if(!window.matchMedia('(max-width:700px)').matches||!document.body.classList.contains('mobile-nav-open'))return;if(!event.target.closest('.mobile-drawer-clone,.mobile-nav,.brand'))closeAll()});document.addEventListener('keydown',event=>{if(event.key==='Escape')closeAll()});window.addEventListener('resize',()=>{if(!window.matchMedia('(max-width:700px)').matches)closeAll()})});</script>
|
||
<style>@media(max-width:700px){.legacy-mobile-nav{display:none!important}.mobile-nav-backdrop,.mobile-drawer-shade{pointer-events:auto!important}}</style>
|
||
<script>document.addEventListener('DOMContentLoaded',()=>{const style=document.createElement('style');style.textContent='@media(max-width:700px){ #movies,#sonarr table,#missing table{min-width:0!important;width:100%!important;table-layout:fixed!important}.tablewrap{overflow-x:hidden!important}.badges{max-width:100%!important;display:flex!important;flex-wrap:nowrap!important;overflow:hidden!important;max-height:22px!important}.badge{flex:0 1 auto!important;max-width:100%!important;overflow:hidden!important;text-overflow:ellipsis!important;font-size:9px!important;padding:2px 4px!important}.title{max-width:100%!important;overflow:hidden!important;text-overflow:ellipsis!important;white-space:nowrap!important}#movies th:nth-child(1),#sonarr th:nth-child(1),#missing th:nth-child(1){width:42%!important}#movies th:nth-child(2),#sonarr th:nth-child(2),#missing th:nth-child(2){width:19%!important}#movies th:nth-child(4),#movies th:nth-child(5),#sonarr th:nth-child(4),#sonarr th:nth-child(5),#missing th:nth-child(4),#missing th:nth-child(5){width:19.5%!important}#movies td,#sonarr td,#missing td{padding:7px 4px!important;font-size:10px!important}}';document.head.appendChild(style)});</script>
|
||
<script>document.addEventListener('DOMContentLoaded',()=>{const source=document.querySelector('.nav'),brand=document.querySelector('.brand');if(!source||!brand)return;const drawer=document.createElement('aside');drawer.className='mobile-drawer-clone';drawer.innerHTML=source.innerHTML;drawer.querySelector('.mobile-nav-close')?.remove();const close=document.createElement('button');close.className='mobile-drawer-close';close.type='button';close.textContent='×';close.setAttribute('aria-label','Navigation schließen');drawer.prepend(close);const shade=document.createElement('div');shade.className='mobile-drawer-shade';document.body.append(shade,drawer);const closeDrawer=()=>{drawer.classList.remove('open');shade.classList.remove('open');document.body.classList.remove('mobile-nav-open')};const openDrawer=()=>{if(window.matchMedia('(max-width:700px)').matches){drawer.classList.add('open');shade.classList.add('open');document.body.classList.add('mobile-nav-open')}};brand.addEventListener('click',openDrawer);shade.addEventListener('click',closeDrawer);close.addEventListener('click',closeDrawer);drawer.addEventListener('click',event=>{const button=event.target.closest('button[data-view]');if(!button)return;source.querySelector(`button[data-view="${button.dataset.view}"]`)?.click();closeDrawer()})});</script>
|
||
<style>.mobile-drawer-clone,.mobile-drawer-shade{display:none}@media(max-width:700px){.mobile-drawer-shade{display:none;position:fixed!important;inset:0!important;z-index:1900!important;background:rgba(0,0,0,.62)!important;backdrop-filter:blur(3px)}.mobile-drawer-shade.open{display:block!important}.mobile-drawer-clone{display:flex!important;position:fixed!important;visibility:visible!important;opacity:1!important;top:0!important;left:0!important;bottom:0!important;width:min(84vw,320px)!important;z-index:2000!important;flex-direction:column!important;gap:5px!important;padding:62px 12px 18px!important;margin:0!important;overflow-y:auto!important;overflow-x:hidden!important;background:#1c2027!important;border-right:1px solid #465160!important;box-shadow:16px 0 45px rgba(0,0,0,.5)!important;transform:translate3d(-110%,0,0)!important;transition:transform .24s ease!important}.mobile-drawer-clone.open{transform:translate3d(0,0,0)!important}.mobile-drawer-clone button{display:block!important;flex:0 0 auto!important;width:100%!important;min-height:42px!important;padding:12px 13px!important;text-align:left!important;border:0!important;border-radius:8px!important;background:transparent!important;color:#c7d0dc!important;font-size:13px!important;white-space:nowrap!important}.mobile-drawer-clone button.active{background:#303b49!important;color:#fff!important}.mobile-drawer-clone .mobile-drawer-close{position:absolute!important;top:14px!important;right:12px!important;width:34px!important;min-height:34px!important;height:34px!important;padding:0!important;border:1px solid #4a5563!important;background:#292f38!important;color:#dce8f3!important;text-align:center!important;font-size:23px!important;line-height:1!important}.mobile-drawer-clone img{width:15px!important;height:15px!important;margin-right:6px!important;vertical-align:-3px!important;object-fit:contain!important}}</style>
|
||
<script>document.addEventListener('DOMContentLoaded',()=>{const close=document.querySelector('.mobile-nav-close');if(close){close.style.fontSize='23px';close.style.padding='0'}});</script>
|
||
<script>document.addEventListener('DOMContentLoaded',()=>{const brand=document.querySelector('.brand'),nav=document.querySelector('.nav');if(!brand||!nav)return;const backdrop=document.createElement('div');backdrop.className='mobile-nav-backdrop';const close=document.createElement('button');close.className='mobile-nav-close';close.type='button';close.setAttribute('aria-label','Navigation schließen');close.textContent='×';nav.prepend(close);document.body.appendChild(backdrop);const drawerStyle=document.createElement('style');drawerStyle.textContent='@media(max-width:700px){.nav.mobile-drawer-open{position:fixed!important;display:flex!important;visibility:visible!important;opacity:1!important;top:0!important;left:0!important;bottom:0!important;width:min(82vw,310px)!important;max-width:none!important;overflow-y:auto!important;overflow-x:hidden!important;transform:translate3d(0,0,0)!important;z-index:1000!important}.nav:not(.mobile-drawer-open){transform:translate3d(-105%,0,0)!important}}';document.head.appendChild(drawerStyle);const closeNav=()=>{document.body.classList.remove('mobile-nav-open');nav.classList.remove('mobile-drawer-open')};const openNav=()=>{if(window.matchMedia('(max-width: 700px)').matches){document.body.classList.add('mobile-nav-open');nav.classList.add('mobile-drawer-open')}};brand.addEventListener('click',openNav);backdrop.addEventListener('click',closeNav);close.addEventListener('click',closeNav);nav.addEventListener('click',event=>{if(event.target.closest('button[data-view]'))closeNav()})});</script>
|
||
<style>.mobile-nav-close,.mobile-nav-backdrop{display:none}@media(max-width:700px){.brand{cursor:pointer}.mobile-nav-backdrop{position:fixed;inset:0;z-index:130;background:rgba(0,0,0,.58);backdrop-filter:blur(2px)}body.mobile-nav-open .mobile-nav-backdrop{display:block}.nav{position:fixed;top:0;bottom:0;left:0;z-index:140;width:min(82vw,310px);margin:0!important;padding:62px 12px 18px!important;display:flex;flex-direction:column;align-items:stretch;gap:5px;overflow-y:auto;border:0!important;border-right:1px solid #3c4653!important;border-radius:0!important;background:rgba(28,32,39,.98)!important;box-shadow:14px 0 40px rgba(0,0,0,.35);transform:translateX(-105%);transition:transform .24s ease}.nav button{flex:0 0 auto!important;text-align:left;padding:12px 13px!important;border-radius:8px!important;font-size:13px!important}.nav button.active{background:#303b49}.mobile-nav-close{display:block;position:absolute;top:14px;right:12px;width:34px;height:34px;padding:0;border:1px solid #4a5563;border-radius:8px;background:#292f38;color:#dce8f3;font-size:23px;line-height:1}.mobile-nav-open .nav{transform:translateX(0)}body.mobile-nav-open{overflow:hidden}#movies,#sonarr table,#missing table{min-width:0;width:100%;table-layout:fixed}#movies th:nth-child(3),#movies td:nth-child(3),#movies th:nth-child(6),#movies td:nth-child(6),#movies th:nth-child(7),#movies td:nth-child(7),#sonarr th:nth-child(3),#sonarr td:nth-child(3),#sonarr th:nth-child(6),#sonarr td:nth-child(6),#sonarr th:nth-child(7),#sonarr td:nth-child(7),#missing th:nth-child(3),#missing td:nth-child(3),#missing th:nth-child(6),#missing td:nth-child(6),#missing th:nth-child(7),#missing td:nth-child(7){display:none}#movies th:nth-child(1),#sonarr th:nth-child(1),#missing th:nth-child(1){width:46%}#movies th:nth-child(2),#sonarr th:nth-child(2),#missing th:nth-child(2){width:18%}#movies th:nth-child(4),#movies th:nth-child(5),#sonarr th:nth-child(4),#sonarr th:nth-child(5),#missing th:nth-child(4),#missing th:nth-child(5){width:18%}#movies td,#sonarr td,#missing td{padding:8px 6px;font-size:11px}.badges{gap:3px}.badge{padding:2px 5px;font-size:10px}.title{max-width:100%}.tablewrap{overflow-x:hidden}.series-category>.series table{min-width:0!important}}</style>
|
||
<script>document.addEventListener('DOMContentLoaded',()=>{const panel=document.querySelector('.system-panel'),diskList=document.querySelector('#diskList');if(!panel||!diskList)return;const extra=document.createElement('div');extra.id='systemDetails';extra.className='system-details';diskList.before(extra);const formatUptime=seconds=>{const days=Math.floor(seconds/86400),hours=Math.floor(seconds%86400/3600),minutes=Math.floor(seconds%3600/60);return `${days?days+' T ':''}${hours} Std. ${minutes} Min.`};const update=async()=>{try{const data=await (await fetch('/api/system')).json();const load=data.load||[];extra.innerHTML=`<div class="system-detail"><span class="system-detail-icon"><img src="https://api.iconify.design/mdi:chart-line.svg?color=%2360a9e6" alt="" aria-hidden="true"></span><div><small>Load Average</small><strong>${load.map(value=>Number(value).toFixed(2)).join(' · ')||'—'}</strong><em>1 / 5 / 15 Minuten</em></div></div><div class="system-detail"><span class="system-detail-icon"><img src="https://api.iconify.design/mdi:clock-outline.svg?color=%23d7adf3" alt="" aria-hidden="true"></span><div><small>Uptime</small><strong>${formatUptime(Number(data.uptime_seconds)||0)}</strong><em>Container-Laufzeit</em></div></div><div class="system-detail"><span class="system-detail-icon"><img src="https://api.iconify.design/mdi:application-cog-outline.svg?color=%23f4d267" alt="" aria-hidden="true"></span><div><small>Prozesse</small><strong>${Number(data.processes||0).toLocaleString('de-DE')}</strong><em>Aktive Prozesse</em></div></div>`}catch(error){extra.innerHTML='<div class="system-detail muted">Systemdetails momentan nicht verfügbar</div>'}};update();setInterval(update,5000)});</script>
|
||
<style>.system-details{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin:2px 0 17px}.system-detail{display:flex;align-items:center;gap:9px;padding:10px 11px;border:1px solid rgba(255,255,255,.07);border-radius:9px;background:rgba(40,43,49,.58)}.system-detail-icon{display:grid;place-items:center;flex:0 0 auto;width:28px;height:28px;border-radius:8px;background:rgba(96,169,230,.14)}.system-detail-icon img{width:17px;height:17px}.system-detail small,.system-detail em{display:block}.system-detail small{color:#9da5b2;font-size:10px}.system-detail strong{display:block;margin-top:2px;color:#f1f5fa;font-size:13px}.system-detail em{margin-top:2px;color:#7f8996;font-size:9px;font-style:normal}@media(max-width:700px){.system-details{grid-template-columns:1fr 1fr}.system-detail:last-child{grid-column:1 / -1}}</style>
|
||
<script>document.addEventListener('DOMContentLoaded',()=>{const icons={overview:['mdi:movie-open-outline','mdi:television-classic','mdi:alert-outline','mdi:download-box-outline'],insight:['mdi:movie-check-outline','mdi:playlist-check','mdi:database-check-outline','mdi:monitor-eye']};const apply=(selector,keys)=>{const colors=selector.includes('overview')?['%238bc9f2','%23d7adf3','%23ff9e9e','%236ae29a']:['%238bc9f2','%23d7adf3','%23f4d267','%236ae29a'];document.querySelectorAll(selector).forEach((node,index)=>{const key=keys[index%keys.length];if(!key||node.dataset.iconApplied===key)return;const image=document.createElement('img');image.src=`https://api.iconify.design/${key}.svg?color=${colors[index%colors.length]}`;image.alt='';image.setAttribute('aria-hidden','true');node.textContent='';node.appendChild(image);node.dataset.iconApplied=key})};const update=()=>{apply('.overview-icon',icons.overview);apply('.insight-icon',icons.insight)};update();new MutationObserver(update).observe(document.body,{childList:true,subtree:true})});</script>
|
||
<style>.overview-icon img{width:23px;height:23px;object-fit:contain}.insight-icon img{width:17px;height:17px;object-fit:contain}</style>
|
||
<script>document.addEventListener('DOMContentLoaded',()=>{const badge=document.querySelector('#raidStatusBadge');if(!badge)return;const side=document.createElement('div');side.className='raid-status-side';const countdown=document.createElement('small');countdown.id='raidRefreshCountdown';countdown.textContent='Update in 5s';badge.replaceWith(side);side.append(badge,countdown);const interval=5000;let next=Date.now()+interval;const reset=()=>{next=Date.now()+interval};window.resetRaidRefreshCountdown=reset;const update=()=>{const seconds=Math.max(0,Math.ceil((next-Date.now())/1000));countdown.textContent=`Update in ${seconds}s`;if(seconds===0)reset()};update();setInterval(update,250);const updated=document.querySelector('#datastoreUpdated');if(updated)new MutationObserver(reset).observe(updated,{childList:true,characterData:true,subtree:true})});</script>
|
||
<script>document.addEventListener('DOMContentLoaded',()=>{const icons={total:'mdi:harddisk',used:'mdi:database',free:'mdi:database-outline',percent:'mdi:chart-donut'};document.querySelectorAll('.storage-card .storage-icon').forEach((node,index)=>{const key=['total','used','free','percent'][index];if(!key)return;const image=document.createElement('img');image.src=`https://api.iconify.design/${icons[key]}.svg?color=${key==='used'?'%23f4d267':key==='free'?'%236ae29a':key==='percent'?'%23c59cff':'%2360a9e6'}`;image.alt='';image.setAttribute('aria-hidden','true');node.textContent='';node.appendChild(image)})});</script>
|
||
<script>document.addEventListener('DOMContentLoaded',()=>{if(!document.querySelector('link[rel="shortcut icon"]')){const favicon=document.createElement('link');favicon.rel='shortcut icon';favicon.type='image/svg+xml';favicon.href="{{ url_for('static', filename='media-mark.svg') }}?v=media-max";document.head.appendChild(favicon)}});</script>
|
||
<html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"><meta name="theme-color" content="#1f252d"><meta name="mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-capable" content="yes"><link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}?v=5"><link rel="apple-touch-icon" href="{{ url_for('static', filename='media-mark-192.png') }}"><link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='media-mark.svg') }}"><title>Media Max</title>
|
||
<style>
|
||
:root{color-scheme:dark;--bg:#181818;--panel:#222;--line:#3a3a3a;--text:#dedede;--muted:#929292;--yellow:#f4c430;--blue:#60a9e6}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:13px Inter,system-ui,sans-serif}header{height:58px;display:flex;align-items:center;gap:15px;padding:0 20px;background:#242424;border-bottom:1px solid #343434}.logo{color:var(--yellow);font-size:22px;font-weight:900;letter-spacing:1px}.head-title{font-weight:700}.head-sub{font-size:12px;color:var(--muted);margin-top:2px}.actions{margin-left:auto}button,input,select{font:inherit;color:var(--text);background:#292929;border:1px solid #444;border-radius:5px}button{padding:7px 10px;cursor:pointer}main{padding:14px 20px 30px}.nav{display:flex;gap:4px;margin-bottom:12px;border-bottom:1px solid var(--line)}.nav button{border:0;border-radius:5px 5px 0 0;background:transparent;color:#aaa}.nav button.active{background:#303030;color:#fff}.view{display:none}.view.active{display:block}.toolbar{display:grid;grid-template-columns:minmax(280px,1fr) 190px 190px 170px;gap:8px;margin-bottom:10px}input,select{padding:7px 10px;height:34px}.stats{display:flex;gap:8px;margin-bottom:10px;flex-wrap:wrap}.stat{background:#242424;border:1px solid #363636;padding:5px 9px;border-radius:5px;color:#bbb}.scan{color:#f4c430}.banner{padding:9px 11px;border:1px solid #693b3b;background:#372424;border-radius:5px;margin-bottom:10px;color:#ffd1d1}.tablewrap{border:1px solid #353535;border-radius:6px;overflow:hidden;background:var(--panel)}table{width:100%;border-collapse:collapse;table-layout:fixed}th{padding:8px 10px;background:#282828;border-bottom:1px solid #444;text-align:left;font-size:12px}td{padding:7px 10px;border-bottom:1px solid #343434;vertical-align:middle}.col-title{width:28%}.col-quality{width:12%}.col-state{width:10%}.col-audio{width:17%}.col-subs{width:17%}.col-video{width:8%}.col-size{width:8%}.title{color:var(--blue);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:inline-block;max-width:90%;vertical-align:bottom}.year{color:var(--muted);font-size:11px;margin-left:5px}.badges{display:flex;gap:4px;flex-wrap:wrap}.badge{display:inline-flex;gap:4px;align-items:center;padding:2px 6px;border-radius:999px;border:1px solid #484848;background:#303030;white-space:nowrap;font-size:11px}.quality{background:#0e7138;border-color:#19894b}.state{font-size:11px;font-weight:700;padding:3px 7px;border-radius:4px;display:inline-block}.state.ok{background:#126f39;color:#d9ffe8}.state.warn{background:#77561e;color:#ffebc4}.state.bad{background:#763030;color:#ffd9d9}.state.neutral{background:#3b3b3b;color:#ccc}.muted{color:var(--muted)}.hidden{display:none}.series{border-bottom:1px solid var(--line)}.series:last-child{border:0}.series summary{padding:12px;cursor:pointer;background:#282828;font-weight:700}.series summary:hover{background:#303030}.series table{border-radius:0}.series td:first-child{padding-left:28px}@media(max-width:1100px){.toolbar{grid-template-columns:1fr 1fr}.col-video,.col-size{display:none}}
|
||
.brand-mark{width:34px;height:34px;display:grid;place-items:center;filter:drop-shadow(0 0 10px rgba(96,169,230,.24))}.brand-mark svg{width:34px;height:34px}.brand-name{color:var(--yellow);font-size:16px;font-weight:900;letter-spacing:1.5px}.brand{display:flex;align-items:center;gap:9px}
|
||
.dashboard-hero{display:flex;justify-content:space-between;align-items:center;gap:20px;margin-bottom:14px;padding:26px;border:1px solid rgba(96,169,230,.2);border-radius:14px;background:radial-gradient(circle at 85% 15%,rgba(96,169,230,.2),transparent 34%),linear-gradient(135deg,rgba(39,48,61,.96),rgba(27,30,36,.96));box-shadow:0 16px 40px rgba(0,0,0,.22)}.dashboard-hero h1{margin:7px 0 4px;font-size:25px}.dashboard-hero p{margin:0;color:#9da5b2}.live-pill{display:flex;align-items:center;gap:8px;padding:9px 14px;border-radius:999px;background:rgba(21,148,71,.17);border:1px solid rgba(106,226,154,.25);color:#aaf2c5;font-weight:700}.live-dot{width:8px;height:8px;border-radius:50%;background:#6ae29a;box-shadow:0 0 12px #6ae29a}.overview-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:14px}.overview-card{display:flex;align-items:center;gap:12px;min-height:104px;padding:17px;border:1px solid rgba(255,255,255,.08);border-radius:11px;background:linear-gradient(145deg,rgba(43,47,55,.92),rgba(29,31,36,.92));box-shadow:0 8px 22px rgba(0,0,0,.16)}.overview-icon{display:grid;place-items:center;width:40px;height:40px;border-radius:11px;background:rgba(96,169,230,.17);color:#8bc9f2;font-size:23px;font-weight:700}.overview-icon.series-icon{background:rgba(146,87,194,.2);color:#d7adf3}.overview-icon.warning{background:rgba(188,70,70,.2);color:#ff9e9e}.overview-icon.download-icon{background:rgba(21,148,71,.2);color:#6ae29a}.overview-card div{min-width:0}.overview-card span:not(.overview-icon){display:block;color:#9da5b2;font-size:11px;text-transform:uppercase;letter-spacing:.06em}.overview-card strong{display:block;margin:3px 0;font-size:22px;color:#f1f5fa}.overview-card small{display:block;color:#7f8996;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.dashboard-columns{display:grid;grid-template-columns:1.45fr 1fr;gap:14px}.system-panel,.service-panel{padding:19px;border:1px solid rgba(255,255,255,.08);border-radius:12px;background:rgba(31,33,38,.84);box-shadow:0 12px 30px rgba(0,0,0,.18)}.panel-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:15px;margin-bottom:17px}.panel-heading h2{margin:0 0 4px;font-size:16px}.panel-heading p{margin:0;color:#7f8996;font-size:12px}.system-meters{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-bottom:20px}.meter-top{display:flex;justify-content:space-between;margin-bottom:7px;color:#cbd1db}.meter-top strong{color:#fff}.meter-track{height:9px;background:#363b44;border-radius:99px;overflow:hidden}.meter-track span{display:block;width:0;height:100%;border-radius:99px;background:linear-gradient(90deg,#60a9e6,#159447);transition:width .45s ease}.disk-list{display:grid;gap:12px}.disk-row{display:grid;grid-template-columns:92px 1fr auto;align-items:center;gap:10px}.disk-name{color:#cbd1db;font-weight:700}.disk-path{display:block;color:#7f8996;font-size:11px;font-weight:400}.disk-bar{height:7px;background:#363b44;border-radius:99px;overflow:hidden}.disk-bar span{display:block;height:100%;border-radius:99px;background:linear-gradient(90deg,#60a9e6,#f4c430);transition:width .45s ease}.disk-values{text-align:right;color:#cbd1db;font-size:11px}.service-row{display:flex;align-items:center;gap:11px;padding:12px 0;border-bottom:1px solid rgba(255,255,255,.07)}.service-row:last-child{border-bottom:0}.service-logo{display:grid;place-items:center;width:34px;height:34px;border-radius:9px;color:#fff;font-weight:800}.radarr-dot{background:#1d7e45}.sonarr-dot{background:#4d65b1}.sab-dot{background:#9a6830}.service-row div{flex:1}.service-row strong,.service-row small{display:block}.service-row small{margin-top:2px;color:#7f8996;font-size:11px}.service-status{padding:4px 8px;border-radius:999px;font-size:11px;font-weight:700}.service-status.online{background:#126f39;color:#d9ffe8}.service-status.offline{background:#3b3b3b;color:#aaa}
|
||
.dashboard-view{flex-direction:column}.dashboard-view .dashboard-columns{align-items:stretch}.dashboard-view .system-panel,.dashboard-view .service-panel{min-height:0}.dashboard-view .disk-list{gap:18px}.dashboard-view .disk-row{min-height:38px}.dashboard-insights{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:14px}.insight-card{display:flex;align-items:center;gap:11px;padding:13px 15px;border:1px solid rgba(255,255,255,.08);border-radius:10px;background:rgba(31,33,38,.84)}.insight-icon{display:grid;place-items:center;width:32px;height:32px;border-radius:8px;background:rgba(96,169,230,.16);color:#8bc9f2;font-size:16px}.insight-card:nth-child(2) .insight-icon{background:rgba(146,87,194,.18);color:#d7adf3}.insight-card:nth-child(3) .insight-icon{background:rgba(244,196,48,.16);color:#f4c430}.insight-card:nth-child(4) .insight-icon{background:rgba(21,148,71,.18);color:#6ae29a}.insight-card strong,.insight-card small{display:block}.insight-card strong{font-size:14px;color:#f1f5fa}.insight-card small{margin-top:3px;color:#7f8996;font-size:10px}@media(max-width:1000px){.dashboard-insights{grid-template-columns:repeat(2,1fr)}}
|
||
@media(max-width:1000px){.overview-grid{grid-template-columns:repeat(2,1fr)}.dashboard-columns{grid-template-columns:1fr}}@media(max-width:600px){.overview-grid,.system-meters{grid-template-columns:1fr}.dashboard-hero{align-items:flex-start;flex-direction:column;padding:20px}.live-pill{align-self:stretch;justify-content:center}.disk-row{grid-template-columns:78px 1fr auto}}
|
||
.settings-modal .setting-help{display:block;margin:-3px 0 12px;color:var(--muted);font-size:11px}.settings-modal .setting-select{width:100%;height:38px}.pwa-install{display:none}
|
||
@media(max-width:700px){body{font-size:12px;padding-bottom:env(safe-area-inset-bottom)}header{height:auto;min-height:62px;padding:9px 12px;gap:9px;align-items:center}.brand-mark,.brand-mark svg{width:30px;height:30px}.brand-name{font-size:14px}.head-title{font-size:12px}.head-sub{font-size:10px}.actions{gap:4px}.actions button{height:34px;padding:0 8px}.settings-button{font-size:15px}.actions #rescan{font-size:0}.actions #rescan::after{content:'↻';font-size:16px}main{padding:10px 10px 24px}.nav{margin-bottom:10px;padding:4px;overflow-x:auto;scrollbar-width:none}.nav::-webkit-scrollbar{display:none}.nav button{flex:0 0 auto;white-space:nowrap;padding:8px 10px;font-size:11px}.toolbar{grid-template-columns:1fr;gap:7px;padding:8px}.toolbar input,.toolbar select{width:100%;height:38px}.stats{gap:6px}.stat{padding:6px 8px}.tablewrap{overflow-x:auto;border-radius:8px}table{min-width:860px}.dashboard-hero{margin-bottom:10px;padding:18px;border-radius:11px}.dashboard-hero h1{font-size:21px}.dashboard-hero p{font-size:11px;line-height:1.4}.overview-grid{grid-template-columns:1fr 1fr;gap:7px}.overview-card{min-height:84px;padding:12px 10px;gap:8px}.overview-icon{width:32px;height:32px;font-size:18px}.overview-card strong{font-size:18px}.overview-card span:not(.overview-icon){font-size:9px}.overview-card small{font-size:9px}.dashboard-columns{gap:8px}.system-panel,.service-panel{padding:14px;border-radius:10px}.system-meters{gap:12px}.panel-heading{margin-bottom:13px}.panel-heading h2{font-size:14px}.panel-heading p{font-size:10px}.disk-row{grid-template-columns:82px 1fr auto;gap:7px}.disk-values{font-size:10px}.download-hero{padding:18px;margin-bottom:10px}.download-hero h2{font-size:19px}.download-hero p{font-size:11px}.download-stats{grid-template-columns:1fr 1fr;gap:7px}.download-card{min-height:78px;padding:10px;gap:8px}.card-icon{width:30px;height:30px;font-size:17px}.card-label{font-size:9px}.download-card strong{font-size:17px}.download-card small{font-size:9px}.settings-modal{max-height:calc(100dvh - 24px);overflow:auto;padding:18px}.settings-head{margin-bottom:15px}.settings-modal label{margin-top:13px}}
|
||
@media(max-width:390px){.brand-name{display:none}.head-sub{display:none}.overview-grid{grid-template-columns:1fr}.dashboard-hero{padding:15px}.download-stats{grid-template-columns:1fr}}
|
||
</style><style>.scan-control{display:flex;align-items:center;gap:5px}.scan-control select{height:36px;padding:0 8px;font-size:11px}.scan-control button{white-space:nowrap}.scan-running{position:relative;overflow:hidden;background:linear-gradient(90deg,rgba(244,196,48,.14) var(--scan-progress,0%),rgba(244,196,48,.04) var(--scan-progress,0%))!important}.scan-running::after{content:'';position:absolute;left:0;bottom:0;width:var(--scan-progress,0%);height:2px;background:#f4c430;transition:width .35s ease}@media(max-width:700px){.scan-control select{max-width:108px;font-size:10px}.scan-control button{font-size:0}.scan-control button::after{content:'↻';font-size:16px}}</style></head><body>
|
||
<header><div class="brand"><div class="brand-mark" aria-label="Media Max"><svg viewBox="0 0 48 48" role="img"><path d="M10 8h28a4 4 0 0 1 4 4v24a4 4 0 0 1-4 4H10a4 4 0 0 1-4-4V12a4 4 0 0 1 4-4Z" fill="#20252d" stroke="#60a9e6" stroke-width="2"/><path d="M14 17h20M14 24h13M14 31h20" stroke="#f4c430" stroke-width="3" stroke-linecap="round"/><circle cx="34" cy="24" r="5" fill="#159447" stroke="#c9ffe0" stroke-width="1.5"/></svg></div><div class="brand-name">MEDIA</div></div><div><div class="head-title" data-de="Media Max" data-en="Media Max">Media Max</div><div class="head-sub" data-de="Audio- und Untertitelspuren aus den Mediendateien" data-en="Audio and subtitle tracks from media files">Audio- und Untertitelspuren aus den Mediendateien</div></div><div class="actions"><button class="lang-switch" id="languageToggle" title="Sprache / Language">DE</button><button class="settings-button" id="settingsOpen" title="Einstellungen">⚙</button><div class="scan-control"><select id="scanSource" aria-label="Scan-Quelle"><option value="all">Alles scannen</option><option value="radarr">Nur Radarr</option><option value="sonarr">Nur Sonarr</option><option value="missing">Nur Ohne Deutsch</option></select><button id="rescan" data-de="↻ Neu scannen" data-en="↻ Rescan">↻ Neu scannen</button></div></div></header>
|
||
<main>
|
||
<nav class="nav"><button class="active" data-view="dashboard"><img class="nav-tab-icon" src="https://api.iconify.design/mdi:view-dashboard-outline.svg?color=%238bc9f2" alt="" aria-hidden="true">Dashboard</button><button data-view="radarr"><img class="nav-tab-icon" src="https://api.iconify.design/mdi:movie-open-outline.svg?color=%23cc7b19" alt="" aria-hidden="true">RADARR · Filme</button>{% if sonarr_enabled %}<button data-view="sonarr"><img class="nav-tab-icon" src="https://api.iconify.design/mdi:television-classic.svg?color=%2335c5f4" alt="" aria-hidden="true">SONARR · Serien</button>{% endif %}<button data-view="missing"><img class="nav-tab-icon" src="https://api.iconify.design/mdi:alert-outline.svg?color=%23ff9e9e" alt="" aria-hidden="true">Ohne Deutsch <span class="nav-count" id="missingNavCount">{{ missing_german_movies|length + (missing_german_series|map(attribute='episodes')|map('length')|sum) }}</span></button>{% if sab_enabled %}<button data-view="downloads"><img class="nav-tab-icon" src="https://api.iconify.design/mdi:download-box-outline.svg?color=%23f4c430" alt="" aria-hidden="true">SABNZBD · Downloads <span class="nav-count download-nav-count hidden" id="downloadNavCount">0</span></button>{% endif %}<button data-view="datastore"><img class="nav-tab-icon" src="https://api.iconify.design/mdi:harddisk.svg?color=%2360a9e6" alt="" aria-hidden="true">Datastore</button></nav>
|
||
<section id="dashboard" class="view active" data-source="dashboard"><div class="dashboard-hero"><div><div class="eyebrow">MEDIA MAX</div><h1>Dashboard</h1><p>Deine gesamte Medienbibliothek und Systemauslastung auf einen Blick.</p></div><div class="live-pill"><span class="live-dot"></span> Live</div></div><div class="overview-grid"><div class="overview-card"><span class="overview-icon movie">▣</span><div><span>Filme</span><strong>{{ rows|length }}</strong><small>Radarr Library</small></div></div><div class="overview-card"><span class="overview-icon series-icon">▤</span><div><span>Serien</span><strong>{{ series|length }}</strong><small>Sonarr Shows</small></div></div><div class="overview-card"><span class="overview-icon warning">⚠</span><div><span>Ohne Deutsch</span><strong id="dashboardMissing">{{ missing_german_movies|length + (missing_german_series|map(attribute='episodes')|map('length')|sum) }}</strong><small>Audio oder Untertitel prüfen</small></div></div><div class="overview-card"><span class="overview-icon download-icon">↓</span><div><span>Downloads</span><strong id="dashboardDownloads">—</strong><small id="dashboardRemaining">SABnzbd wird geladen …</small></div></div></div><div class="dashboard-columns"><section class="system-panel"><div class="panel-heading"><div><h2>Systemauslastung</h2><p>Aktuelle Werte aus dem Dashboard-Container</p></div><span id="systemUpdated" class="muted">Wird geladen …</span></div><div class="system-meters"><div class="meter"><div class="meter-top"><span>CPU</span><strong id="cpuValue">—</strong></div><div class="meter-track"><span id="cpuBar"></span></div></div><div class="meter"><div class="meter-top"><span>RAM</span><strong id="ramValue">—</strong></div><div class="meter-track"><span id="ramBar"></span></div></div></div><div id="diskList" class="disk-list"><div class="disk-row muted">Laufwerke werden geladen …</div></div></section><section class="service-panel"><div class="panel-heading"><div><h2>Dienste</h2><p>Verbindung und Bibliotheksstatus</p></div></div><div class="service-row"><span class="service-logo radarr-dot">R</span><div><strong>Radarr</strong><small>Filme & Qualität</small></div><span class="service-status online">Aktiv</span></div><div class="service-row"><span class="service-logo sonarr-dot">S</span><div><strong>Sonarr</strong><small>Serien & Episoden</small></div><span class="service-status {% if sonarr_enabled %}online{% else %}offline{% endif %}">{% if sonarr_enabled %}Aktiv{% else %}Nicht konfiguriert{% endif %}</span></div><div class="service-row"><span class="service-logo sab-dot">↓</span><div><strong>SABnzbd</strong><small>Downloads & Queue</small></div><span id="dashboardSabStatus" class="service-status {% if sab_enabled %}online{% else %}offline{% endif %}">{% if sab_enabled %}Wird geprüft …{% else %}Nicht konfiguriert{% endif %}</span></div></section></div></section>
|
||
<section id="radarr" class="view" data-source="radarr">
|
||
<div class="toolbar"><input id="search" type="search" placeholder="Film suchen …"><select id="audioFilter"><option value="">Alle Audio-Sprachen</option><option>Deutsch</option><option>Englisch</option><option>Japanisch</option><option>Koreanisch</option></select><select id="subFilter"><option value="">Alle Untertitel</option><option>Deutsch</option><option>Englisch</option><option>Japanisch</option><option>Koreanisch</option></select><select id="stateFilter"><option value="">Alle Status</option><option>DE + EN</option><option>Deutsch</option><option>English only</option></select></div>
|
||
<div class="stats"><span class="stat" id="visibleCount"></span><span class="stat scan" id="radarrStatus">Scan wird vorbereitet …</span><span class="stat">Radarr: {{ radarr_media_path }}</span></div>
|
||
<div class="tablewrap"><table id="movies"><thead><tr><th class="col-title">Film</th><th class="col-quality">Quality</th><th class="col-state">Status</th><th class="col-audio">Audio</th><th class="col-subs">Untertitel</th><th class="col-video">Video</th><th class="col-size">Größe</th></tr></thead><tbody>
|
||
{% for r in rows %}<tr data-title="{{ (r.title ~ ' ' ~ (r.year or ''))|lower }}" data-audio="{{ r.audio_languages|map(attribute='name')|join(',') }}" data-subs="{{ r.subtitle_languages|map(attribute='name')|join(',') }}" data-state="{{ r.state.label }}"><td><span class="title">{{ r.title }}</span><span class="year">{{ r.year or '' }}</span>{% if r.error %} ⚠{% endif %}</td><td><span class="badge {% if not r.missing %}quality{% endif %}">{{ r.quality }}</span></td><td><span class="state {{ r.state.class }}">{{ r.state.label }}</span></td><td><div class="badges">{% for l in r.audio_languages %}<span class="badge">{{ l.flag }} {{ l.name }}</span>{% else %}<span class="muted">—</span>{% endfor %}</div></td><td><div class="badges">{% for l in r.subtitle_languages %}<span class="badge">{{ l.flag }} {{ l.name }}</span>{% else %}<span class="muted">—</span>{% endfor %}</div></td><td>{{ r.video_codec }}</td><td>{{ r.size }}</td></tr>{% endfor %}
|
||
</tbody></table></div></section>
|
||
{% if sonarr_enabled %}<section id="sonarr" class="view" data-source="sonarr"><div class="toolbar series-toolbar"><input id="seriesSearch" type="search" placeholder="Serie suchen …"><select id="seriesAudioFilter"><option value="">Alle Audio-Sprachen</option><option>Deutsch</option><option>Englisch</option><option>Japanisch</option><option>Koreanisch</option></select><select id="seriesSubFilter"><option value="">Alle Untertitel</option><option>Deutsch</option><option>Englisch</option><option>Japanisch</option><option>Koreanisch</option></select><select id="seriesStateFilter"><option value="">Alle Status</option><option>DE + EN</option><option>Deutsch</option><option>English only</option></select></div><div class="stats"><span class="stat" id="seriesCount">{{ series|length }} Serien</span><span class="stat scan" id="sonarrStatus">Scan wird vorbereitet …</span><span class="stat">Dashboard: {{ local_series_path }}</span></div><div class="tablewrap" id="seriesList">
|
||
{% for category in series_categories %}{% if category.series %}<section class="series-category"><div class="series-category-heading"><h2>{{ category.label }}</h2><span>{{ category.series|length }} Serien</span></div>{% for group in category.series %}<details class="series" open><summary>{{ group.title }} <span class="muted">({{ group.year or '—' }}) · {{ group.episodes|length }} Episoden</span></summary><table><thead><tr><th class="col-title">Episode</th><th class="col-quality">Quality</th><th class="col-state">Status</th><th class="col-audio">Audio</th><th class="col-subs">Untertitel</th><th class="col-video">Video</th><th class="col-size">Größe</th></tr></thead><tbody>{% for r in group.episodes %}<tr data-title="{{ (r.title ~ ' ' ~ (r.year or '') ~ ' ' ~ group.title)|lower }}" data-audio="{{ r.audio_languages|map(attribute='name')|join(',') }}" data-subs="{{ r.subtitle_languages|map(attribute='name')|join(',') }}" data-state="{{ r.state.label }}"><td><span class="title">{{ r.title }}</span></td><td><span class="badge {% if not r.missing %}quality{% endif %}">{{ r.quality }}</span></td><td><span class="state {{ r.state.class }}">{{ r.state.label }}</span></td><td><div class="badges">{% for l in r.audio_languages %}<span class="badge">{{ l.flag }} {{ l.name }}</span>{% else %}<span class="muted">—</span>{% endfor %}</div></td><td><div class="badges">{% for l in r.subtitle_languages %}<span class="badge">{{ l.flag }} {{ l.name }}</span>{% else %}<span class="muted">—</span>{% endfor %}</div></td><td>{{ r.video_codec }}</td><td>{{ r.size }}</td></tr>{% endfor %}</tbody></table></details>{% endfor %}</section>{% endif %}{% endfor %}
|
||
</div></section>{% endif %}
|
||
<section id="missing" class="view" data-source="missing"><div class="stats"><span class="stat" id="missingCount">{{ missing_german_movies|length + (missing_german_series|map(attribute='episodes')|map('length')|sum) }} Einträge ohne Deutsch</span><span class="stat scan" id="missingStatus">Scan wird vorbereitet …</span><span class="stat scan" id="seasonZeroHint">Vorhanden oder wird gerade geladen · Staffel 0 ausgeblendet</span></div><div class="tablewrap missing-table">
|
||
<table id="missingMovies"><thead><tr><th class="col-title">Film</th><th class="col-quality">Qualität</th><th class="col-state">Status</th><th class="col-audio">Audio</th><th class="col-subs">Untertitel</th><th class="col-video">Video</th><th class="col-size">Größe</th></tr></thead><tbody>{% for r in missing_german_movies %}<tr><td><span class="title">{{ r.title }}</span><span class="year">{{ r.year or '' }}</span></td><td><span class="badge {% if not r.missing %}quality{% endif %}">{{ r.quality }}</span></td><td><span class="state bad">Ohne Deutsch</span></td><td><div class="badges">{% for l in r.audio_languages %}<span class="badge">{{ l.flag }} {{ l.name }}</span>{% else %}<span class="muted">—</span>{% endfor %}</div></td><td><div class="badges">{% for l in r.subtitle_languages %}<span class="badge">{{ l.flag }} {{ l.name }}</span>{% else %}<span class="muted">—</span>{% endfor %}</div></td><td>{{ r.video_codec }}</td><td>{{ r.size }}</td></tr>{% endfor %}</tbody></table>
|
||
{% for group in missing_german_series %}<details class="series missing-series"><summary>{{ group.title }} <span class="muted">({{ group.year or '—' }}) · {{ group.episodes|length }} Episoden ohne Deutsch</span></summary><table><thead><tr><th class="col-title">Episode</th><th class="col-quality">Qualität</th><th class="col-state">Status</th><th class="col-audio">Audio</th><th class="col-subs">Untertitel</th><th class="col-video">Video</th><th class="col-size">Größe</th></tr></thead><tbody>{% for r in group.episodes %}<tr><td><span class="title">{{ r.title }}</span></td><td><span class="badge {% if not r.missing %}quality{% endif %}">{{ r.quality }}</span></td><td><span class="state bad">Ohne Deutsch</span></td><td><div class="badges">{% for l in r.audio_languages %}<span class="badge">{{ l.flag }} {{ l.name }}</span>{% else %}<span class="muted">—</span>{% endfor %}</div></td><td><div class="badges">{% for l in r.subtitle_languages %}<span class="badge">{{ l.flag }} {{ l.name }}</span>{% else %}<span class="muted">—</span>{% endfor %}</div></td><td>{{ r.video_codec }}</td><td>{{ r.size }}</td></tr>{% endfor %}</tbody></table></details>{% endfor %}
|
||
{% if not missing_german_movies and not missing_german_series %}<div class="empty-state">Alle vorhandenen Filme und Episoden haben deutsches Audio oder deutsche Untertitel.</div>{% endif %}</div></section>
|
||
{% if sab_enabled %}<section id="downloads" class="view" data-source="downloads"><div class="download-hero"><div><div class="eyebrow">SABNZBD · DOWNLOAD CENTER</div><h2>Download-Übersicht</h2><p>Queue, Fortschritt und verbleibender Speicherplatz auf einen Blick.</p></div><div class="queue-pill" id="sabQueueCard"><span class="queue-dot"></span><span>Verbinde mit SABnzbd …</span></div></div><div class="download-stats"><div class="download-card remaining-card"><span class="card-icon">↓</span><div><span class="card-label">Noch zu laden</span><strong id="sabRemaining">—</strong><small id="sabRemainingHint">Wird berechnet …</small></div></div><div class="download-card"><span class="card-icon blue">◉</span><div><span class="card-label">Aktive Downloads</span><strong id="sabActive">—</strong><small id="sabHistory">Verlauf: —</small></div></div><div class="download-card"><span class="card-icon purple">↯</span><div><span class="card-label">Geschwindigkeit</span><strong id="sabSpeedCard">—</strong><small>Aktuelle Queue</small></div></div><div class="download-card"><span class="card-icon orange">✓</span><div><span class="card-label">Status</span><strong id="sabStatusCard">—</strong><small id="downloadCount">—</small></div></div></div><div id="downloadError" class="banner hidden"></div><div class="tablewrap download-table"><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="downloadsBody"><tr><td colspan="7" class="muted">Downloads werden geladen …</td></tr></tbody></table></div></section>{% endif %}
|
||
<section id="datastore" class="view" data-source="datastore"><div class="datastore-hero"><div><div class="eyebrow">MEDIA MAX · DATASTORE</div><h2>Datastore</h2><p>Speicherbelegung, Medienkategorien und RAID-Status von <code>/nesflix</code>.</p></div><div id="datastoreUpdated" class="datastore-live">Wird geladen …</div></div><div class="datastore-storage"><div class="storage-card"><span class="storage-icon">▣</span><div><small>Gesamtspeicher</small><strong id="datastoreTotal">—</strong></div></div><div class="storage-card"><span class="storage-icon used">◒</span><div><small>Belegt</small><strong id="datastoreUsed">—</strong></div></div><div class="storage-card"><span class="storage-icon free">◌</span><div><small>Frei</small><strong id="datastoreFree">—</strong></div></div><div class="storage-card"><span class="storage-icon percent">%</span><div><small>Auslastung</small><strong id="datastorePercent">—</strong></div></div></div><section class="datastore-panel datastore-capacity"><div class="datastore-panel-head"><div><h3>Gesamtauslastung</h3><p id="datastorePath">/nesflix</p></div><strong id="datastoreCapacityLabel">—</strong></div><div class="datastore-progress"><span id="datastoreCapacityBar"></span></div></section><div class="datastore-grid"><section class="datastore-panel"><div class="datastore-panel-head"><div><h3>Speicher nach Kategorie</h3><p>Anhand der tatsächlichen Verzeichnisse</p></div></div><div id="datastoreCategories" class="datastore-categories"><div class="datastore-empty">Wird geladen …</div></div></section><section class="datastore-panel raid-panel"><div class="datastore-panel-head"><div><h3>RAID-Status</h3><p id="raidDevice">/dev/md127</p></div><span id="raidStatusBadge" class="raid-status unknown">Wird geladen …</span></div><div id="raidSummary" class="raid-summary"><span>—</span><span>—</span><span>—</span></div><div id="raidRecovery" class="raid-recovery hidden"><div class="recovery-head"><strong>RAID Rebuild</strong><span id="raidRecoveryPercent">—</span></div><div class="datastore-progress recovery"><span id="raidRecoveryBar"></span></div><div class="recovery-meta"><span>Geschwindigkeit <strong id="raidRecoverySpeed">—</strong></span><span>Verbleibend <strong id="raidRecoveryFinish">—</strong></span></div></div><div id="raidDisks" class="raid-disks"><div class="datastore-empty">Wird geladen …</div></div></section></div></section>
|
||
</main>
|
||
<div class="settings-backdrop hidden" id="settingsBackdrop"><section class="settings-modal" role="dialog" aria-modal="true" aria-labelledby="settingsTitle"><div class="settings-head"><div><h2 id="settingsTitle" data-de="Einstellungen" data-en="Settings">Einstellungen</h2><p data-de="Deine Anzeige- und Sprachpräferenzen" data-en="Your display and language preferences">Deine Anzeige- und Sprachpräferenzen</p></div><button class="settings-close" id="settingsClose" aria-label="Schließen">×</button></div><label for="preferredAudio" data-de="Bevorzugte Audio-Sprache" data-en="Preferred audio language">Bevorzugte Audio-Sprache</label><select id="preferredAudio"><option value="" data-de="Keine Präferenz" data-en="No preference">Keine Präferenz</option><option value="Deutsch" data-de="Deutsch" data-en="German">Deutsch</option><option value="Englisch" data-de="Englisch" data-en="English">Englisch</option><option value="Japanisch" data-de="Japanisch" data-en="Japanese">Japanisch</option><option value="Koreanisch" data-de="Koreanisch" data-en="Korean">Koreanisch</option></select><label for="preferredSubs" data-de="Bevorzugte Untertitel-Sprache" data-en="Preferred subtitle language">Bevorzugte Untertitel-Sprache</label><select id="preferredSubs"><option value="" data-de="Keine Präferenz" data-en="No preference">Keine Präferenz</option><option value="Deutsch" data-de="Deutsch" data-en="German">Deutsch</option><option value="Englisch" data-de="Englisch" data-en="English">Englisch</option><option value="Japanisch" data-de="Japanisch" data-en="Japanese">Japanisch</option><option value="Koreanisch" data-de="Koreanisch" data-en="Korean">Koreanisch</option></select><div class="settings-actions"><button id="settingsCancel" data-de="Abbrechen" data-en="Cancel">Abbrechen</button><button class="settings-save" id="settingsSave" data-de="Speichern" data-en="Save">Speichern</button></div></section></div>
|
||
<script>
|
||
const dashboardView=document.querySelector('#dashboard');if(dashboardView){dashboardView.classList.add('dashboard-view');const stats={{ dashboard_stats|tojson }};const insights=document.createElement('div');insights.className='dashboard-insights';insights.innerHTML=`<div class="insight-card"><span class="insight-icon">▣</span><div><strong>${stats.movie_files} von ${stats.movie_total} Filmen vorhanden</strong><small>${stats.movie_missing} fehlen · ${stats.movie_downloading} werden geladen</small></div></div><div class="insight-card"><span class="insight-icon">▤</span><div><strong>${stats.episode_files} von ${stats.episode_total} Episoden vorhanden</strong><small>${stats.episode_missing} fehlen in ${stats.series_total} Serien</small></div></div><div class="insight-card"><span class="insight-icon">◌</span><div><strong>Medienbestand geprüft</strong><small>Audio, Untertitel und Qualität werden überwacht</small></div></div><div class="insight-card"><span class="insight-icon">✓</span><div><strong>Live-Überwachung aktiv</strong><small>Systemwerte und Downloads aktualisieren sich automatisch</small></div></div>`;const columns=dashboardView.querySelector('.dashboard-columns');if(columns)dashboardView.insertBefore(insights,columns)}
|
||
const servicePanel=document.querySelector('.service-panel');let serviceList=null;const serviceSummary=document.createElement('span');serviceSummary.className='service-summary';if(servicePanel){servicePanel.querySelectorAll('.service-row').forEach(row=>row.remove());serviceList=document.createElement('div');serviceList.className='service-list';servicePanel.appendChild(serviceList);const heading=servicePanel.querySelector('.panel-heading');if(heading)heading.appendChild(serviceSummary)}function renderServices(data){if(!serviceList)return;const values=data.services||[];serviceSummary.textContent=`${data.online||0} / ${data.total||values.length} online`;serviceList.innerHTML=values.map(service=>`<div class="service-row service-${service.status}"><span class="service-logo service-icon-${service.key}">${service.key==='dashboard'?'⌂':service.name.slice(0,1)}</span><div><strong>${html(service.name)}</strong><small>${html(service.detail||'Keine Antwort')}</small></div><span class="service-status ${service.status}">${html(service.label)}</span></div>`).join('')}async function fetchServices(){try{renderServices(await (await fetch('/api/services')).json())}catch(error){serviceSummary.textContent='Status nicht verfügbar'}}if(servicePanel){fetchServices();setInterval(fetchServices,30000)}
|
||
const navigation=document.querySelector('.nav');if(navigation&&!navigation.querySelector('[data-view="datastore"]')){const datastoreButton=document.createElement('button');datastoreButton.dataset.view='datastore';datastoreButton.innerHTML='<img class="nav-icon" src="https://api.iconify.design/mdi:harddisk.svg?color=%2360a9e6" alt="" aria-hidden="true"><span data-de="Datastore" data-en="Datastore">Datastore</span>';navigation.appendChild(datastoreButton)}const rows=[...document.querySelectorAll('#movies tbody tr')], search=document.querySelector('#search'), audio=document.querySelector('#audioFilter'), subs=document.querySelector('#subFilter'), state=document.querySelector('#stateFilter'), count=document.querySelector('#visibleCount');
|
||
function apply(){const q=search.value.trim().toLowerCase();let n=0;rows.forEach(r=>{const ok=(!q||r.dataset.title.includes(q))&&(!audio.value||r.dataset.audio.includes(audio.value))&&(!subs.value||r.dataset.subs.includes(subs.value))&&(!state.value||r.dataset.state===state.value);r.classList.toggle('hidden',!ok);if(ok)n++});count.textContent=`${n} Filme`};[search,audio,subs,state].forEach(x=>x.addEventListener(x===search?'input':'change',apply));apply();const seriesRows=[...document.querySelectorAll('#seriesList details.series')],seriesSearch=document.querySelector('#seriesSearch'),seriesAudio=document.querySelector('#seriesAudioFilter'),seriesSubs=document.querySelector('#seriesSubFilter'),seriesState=document.querySelector('#seriesStateFilter'),seriesCount=document.querySelector('#seriesCount');function applySeries(){if(!seriesSearch||!seriesAudio||!seriesSubs||!seriesState)return;const q=seriesSearch.value.trim().toLowerCase();let visible=0;seriesRows.forEach(detail=>{const summary=detail.querySelector('summary')?.textContent.toLowerCase()||'';let matches=0;detail.querySelectorAll('tbody tr:not(.season-divider)').forEach(row=>{const ok=(!q||summary.includes(q)||row.dataset.title.includes(q))&&(!seriesAudio.value||row.dataset.audio.includes(seriesAudio.value))&&(!seriesSubs.value||row.dataset.subs.includes(seriesSubs.value))&&(!seriesState.value||row.dataset.state===seriesState.value);row.classList.toggle('hidden',!ok);if(ok)matches++});detail.classList.toggle('hidden',matches===0);if(matches)visible++});document.querySelectorAll('#seriesList .series-category').forEach(category=>{const hasVisible=[...category.querySelectorAll('details.series')].some(detail=>!detail.classList.contains('hidden'));category.classList.toggle('hidden',!hasVisible)});if(seriesCount)seriesCount.textContent=`${visible} Serien`};[seriesSearch,seriesAudio,seriesSubs,seriesState].forEach(x=>x?.addEventListener(x===seriesSearch?'input':'change',applySeries));applySeries();
|
||
document.querySelectorAll('.nav button').forEach(b=>b.onclick=()=>{document.querySelectorAll('.nav button').forEach(x=>x.classList.remove('active'));document.querySelectorAll('.view').forEach(x=>x.classList.remove('active'));b.classList.add('active');document.querySelector('#'+b.dataset.view).classList.add('active');const rescan=document.querySelector('#rescan'),sourceSelect=document.querySelector('#scanSource'),canRescan=['radarr','sonarr','missing'].includes(b.dataset.view);if(canRescan)sourceSelect.value=b.dataset.view;rescan.disabled=false;rescan.title='Bibliothek neu scannen';sessionStorage.setItem('dashboard-active-view',b.dataset.view)});const rememberedView=sessionStorage.getItem('dashboard-active-view');if(rememberedView)document.querySelector(`.nav button[data-view="${rememberedView}"]`)?.click();
|
||
function statusText(s){if(s.state==='running'){const pct=s.total?Math.round(s.done/s.total*100):0;return `Scan läuft · ${s.done}/${s.total||'?'} Dateien · ${pct}%`}if(s.state==='done')return 'Scan abgeschlossen ✓';if(s.state==='error')return 'Scanfehler: '+s.error;return 'Scan wird vorbereitet …'}
|
||
function updateScanStatus(selector,s){const el=document.querySelector(selector);if(!el)return;const pct=s.total?Math.min(100,Math.round(s.done/s.total*100)):0;el.textContent=statusText(s);el.style.setProperty('--scan-progress',`${pct}%`);el.classList.toggle('scan-running',s.state==='running')}
|
||
const seenRunning={radarr:false,sonarr:false,missing:false};async function poll(){try{const data=await (await fetch('/api/scan-status')).json();updateScanStatus('#radarrStatus',data.radarr);const sonarr=document.querySelector('#sonarrStatus');if(sonarr)updateScanStatus('#sonarrStatus',data.sonarr);const missing=document.querySelector('#missingStatus');if(missing)updateScanStatus('#missingStatus',data.missing);['radarr','sonarr','missing'].forEach(source=>{if(data[source].state==='running')seenRunning[source]=true;if(seenRunning[source]&&['done','error'].includes(data[source].state)){seenRunning[source]=false;setTimeout(()=>{sessionStorage.setItem('dashboard-active-view',document.querySelector('.nav button.active')?.dataset.view||'dashboard');location.reload()},700)}})}catch(e){} };setInterval(poll,1200);poll();
|
||
document.querySelector('#rescan').onclick=async()=>{const b=document.querySelector('#rescan'),source=document.querySelector('#scanSource').value;sessionStorage.setItem('dashboard-active-view',document.querySelector('.nav button.active')?.dataset.view||'dashboard');b.disabled=true;b.textContent='Scan läuft …';await fetch('/api/rescan?source='+source,{method:'POST'});b.disabled=false;b.textContent='↻ Neu scannen';poll()};
|
||
</script>
|
||
<script>
|
||
const scanLabel=document.createElement('label');scanLabel.htmlFor='autoScan';scanLabel.textContent='Automatisches Scanning';const scanHelp=document.createElement('span');scanHelp.className='setting-help';scanHelp.textContent='Bibliotheken werden automatisch geprüft, solange das Dashboard geöffnet ist.';const scanSelect=document.createElement('select');scanSelect.id='autoScan';scanSelect.className='setting-select';[['0','Deaktiviert'],['1800000','Alle 30 Minuten'],['3600000','Jede Stunde'],['7200000','Alle 2 Stunden'],['10800000','Alle 3 Stunden'],['14400000','Alle 4 Stunden'],['18000000','Alle 5 Stunden']].forEach(([value,label])=>{const option=document.createElement('option');option.value=value;option.textContent=label;scanSelect.appendChild(option)});const settingsActions=document.querySelector('.settings-actions');if(settingsActions){settingsActions.before(scanLabel,scanHelp,scanSelect)}
|
||
let autoScanTimer=null;function configureAutoScan(){if(autoScanTimer)clearInterval(autoScanTimer);const interval=Number(localStorage.getItem('auto-scan-interval')||0);if(!interval)return;autoScanTimer=setInterval(()=>{['radarr','sonarr'].forEach(source=>{if(document.querySelector(`#${source}`))fetch(`/api/rescan?source=${source}`,{method:'POST'})})},interval)}function saveAutoScan(){localStorage.setItem('auto-scan-interval',scanSelect.value);configureAutoScan()}scanSelect.value=localStorage.getItem('auto-scan-interval')||'0';configureAutoScan();document.querySelector('#settingsSave').addEventListener('click',saveAutoScan);
|
||
const seasonZeroLabel=document.createElement('label');seasonZeroLabel.className='setting-toggle';seasonZeroLabel.htmlFor='showSeasonZero';seasonZeroLabel.innerHTML='<input type="checkbox" id="showSeasonZero"><span>Staffel 0 anzeigen</span>';const seasonZeroHelp=document.createElement('span');seasonZeroHelp.className='setting-help';seasonZeroHelp.textContent='Gilt für Sonarr und die Liste Ohne Deutsch.';if(settingsActions){settingsActions.before(seasonZeroLabel,seasonZeroHelp)}
|
||
function applySeasonZeroPreference(){const show=localStorage.getItem('show-season-zero')==='true';const rows=[...document.querySelectorAll('#sonarr tbody tr:not(.season-divider),#missing .missing-series tbody tr:not(.season-divider)')];rows.forEach(row=>{const title=row.querySelector('.title')?.textContent.trim()||'';row.classList.toggle('season-zero-hidden',!show&&/^S00E/i.test(title))});document.querySelectorAll('#sonarr details.series,#missing details.missing-series').forEach(detail=>{const episodeRows=[...detail.querySelectorAll('tbody tr:not(.season-divider)')];detail.classList.toggle('season-zero-empty',episodeRows.length>0&&episodeRows.every(row=>row.classList.contains('season-zero-hidden')))});const missingCount=document.querySelector('#missingCount'),missingNavCount=document.querySelector('#missingNavCount');if(missingCount||missingNavCount){const movies=document.querySelectorAll('#missingMovies tbody tr:not(.hidden)').length,episodes=document.querySelectorAll('#missing .missing-series tbody tr:not(.season-divider):not(.season-zero-hidden)').length,total=movies+episodes;if(missingCount)missingCount.textContent=`${total} Einträge ohne Deutsch`;if(missingNavCount)missingNavCount.textContent=total}const hint=document.querySelector('#seasonZeroHint');if(hint)hint.textContent=show?'Vorhanden oder wird gerade geladen · Staffel 0 eingeblendet':'Vorhanden oder wird gerade geladen · Staffel 0 ausgeblendet'}const seasonZeroToggle=document.querySelector('#showSeasonZero');if(seasonZeroToggle){seasonZeroToggle.checked=localStorage.getItem('show-season-zero')==='true';seasonZeroToggle.addEventListener('change',()=>{localStorage.setItem('show-season-zero',seasonZeroToggle.checked?'true':'false');applySeasonZeroPreference()})}applySeasonZeroPreference();
|
||
function syncDashboardMissingCounter(){const visibleMissing=document.querySelectorAll('#missingMovies tbody tr:not(.hidden),#missing .missing-series tbody tr:not(.season-zero-hidden):not(.season-divider)').length;const dashboardMissing=document.querySelector('#dashboardMissing');if(dashboardMissing)dashboardMissing.textContent=visibleMissing}syncDashboardMissingCounter();document.querySelector('#showSeasonZero')?.addEventListener('change',()=>setTimeout(syncDashboardMissingCounter,0));
|
||
function updateMissingHeading(){const heading=document.querySelector('#missingMovies thead th:first-child');if(heading)heading.textContent=document.documentElement.lang==='en'?'Media':'Medien'}updateMissingHeading();document.querySelector('#languageToggle')?.addEventListener('click',()=>setTimeout(updateMissingHeading,0));
|
||
function addMissingSections(){const view=document.querySelector('#missing'),movieTable=document.querySelector('#missingMovies'),seriesItems=[...document.querySelectorAll('#missing .missing-series')];if(!view)return;const makeHeading=(type,de,en)=>{const heading=document.createElement('div');heading.className=`missing-section-heading ${type}`;heading.dataset.de=de;heading.dataset.en=en;heading.textContent=document.documentElement.lang==='en'?en:de;return heading};if(movieTable&&movieTable.querySelector('tbody tr'))movieTable.before(makeHeading('movies','Filme ohne Deutsch','Movies without German'));if(seriesItems.length)seriesItems[0].before(makeHeading('series','Serien ohne Deutsch','Series without German'))}addMissingSections();
|
||
</script>
|
||
<style>
|
||
#dashboard.view.active{display:flex}.view.dashboard-view:not(.active){display:none}.meter:first-child .meter-top span::before{content:'◔';display:inline-grid;place-items:center;width:20px;height:20px;margin-right:7px;border-radius:6px;background:rgba(96,169,230,.18);color:#8bc9f2;font-size:13px}.meter:nth-child(2) .meter-top span::before{content:'▤';display:inline-grid;place-items:center;width:20px;height:20px;margin-right:7px;border-radius:6px;background:rgba(146,87,194,.2);color:#d7adf3;font-size:13px}.disk-name::before{content:'▰';display:grid;grid-column:1;grid-row:1 / span 2;place-items:center;width:20px;height:20px;margin:0;border-radius:6px;background:rgba(244,196,48,.16);color:#f4c430;font-size:11px}.meter-top span,.disk-name{display:flex;align-items:center}.disk-name{display:grid;grid-template-columns:20px minmax(0,1fr);grid-template-rows:auto auto;column-gap:7px;align-items:center;min-width:92px;line-height:1.1}.disk-name:not(:has(.disk-path)){grid-template-rows:auto}.disk-name .disk-path{grid-column:2;grid-row:2;white-space:nowrap}.disk-name .disk-path:empty{display:none}
|
||
</style><style>.nav-count.download-nav-count{background:#f4c430!important;border-color:#ffd95a!important;color:#17191d!important;box-shadow:0 0 10px rgba(244,196,48,.28)}.service-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:20px}.service-summary{margin-left:auto;color:#9da5b2;font-size:12px;font-weight:700}.service-row{min-width:0}.service-row strong,.service-row small{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.service-status.timeout,.service-status.error{background:#806b25;color:#fff3bd}.service-status.offline{background:#7a3030;color:#ffd9d9}.service-timeout .service-logo,.service-error .service-logo{background:#806b25!important}.service-offline .service-logo{background:#7a3030!important}@media(max-width:700px){.service-list{grid-template-columns:1fr}.service-summary{font-size:10px}}</style></body></html>
|
||
<style>
|
||
details.series{margin-bottom:8px;border:1px solid var(--line);border-radius:6px;overflow:hidden;background:#222}
|
||
details.series:last-child{margin-bottom:0}
|
||
.series-category{margin-bottom:16px}.series-category:last-child{margin-bottom:0}.series-category-heading{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:11px 14px;margin-bottom:8px;border:1px solid rgba(96,169,230,.2);border-radius:9px;background:linear-gradient(100deg,rgba(44,55,70,.9),rgba(34,34,34,.9))}.series-category-heading h2{margin:0;color:#e9f2fb;font-size:15px}.series-category-heading span{color:#9da5b2;font-size:11px}
|
||
.nav-count{display:inline-block;min-width:18px;padding:1px 5px;margin-left:4px;border-radius:999px;background:#713030;color:#ffd9d9;font-size:10px}.empty-state{padding:28px;text-align:center;color:var(--muted)}.missing-table>table{border-bottom:1px solid var(--line)}.missing-series{border-radius:0!important;border-left:0!important;border-right:0!important;border-bottom:1px solid var(--line)!important}.missing-series:last-child{border-bottom:0!important}
|
||
</style>
|
||
<style>
|
||
.view#radarr.active,.view#sonarr.active{row-gap:12px}.view#radarr>.toolbar,.view#sonarr>.series-toolbar{padding:0 10px;background:transparent;border-color:transparent;box-shadow:none}.view#radarr>.toolbar input,.view#radarr>.toolbar select,.view#sonarr>.series-toolbar input,.view#sonarr>.series-toolbar select{background:rgba(42,46,54,.82);border-color:#454d59}.view#radarr>.toolbar input:focus,.view#radarr>.toolbar select:focus,.view#sonarr>.series-toolbar input:focus,.view#sonarr>.series-toolbar select:focus{background:#303640}
|
||
.season-zero-hidden,.season-zero-empty{display:none!important}.setting-toggle{display:flex!important;align-items:center;gap:10px;margin:18px 0 6px!important;cursor:pointer}.setting-toggle input{appearance:none;width:42px;height:23px;margin:0;border:1px solid #505967;border-radius:999px;background:#30343b;position:relative;cursor:pointer;transition:.2s}.setting-toggle input::after{content:"";position:absolute;width:17px;height:17px;left:2px;top:2px;border-radius:50%;background:#9da5b2;transition:.2s}.setting-toggle input:checked{background:#176f40;border-color:#23965b}.setting-toggle input:checked::after{transform:translateX(19px);background:#e4ffef}.setting-toggle span{font-weight:700;color:#cbd1db}
|
||
.missing-table table{table-layout:fixed}.missing-table tbody tr{transform:none!important;animation:none!important}.missing-table .missing-series tbody tr:hover{transform:none!important}
|
||
.missing-table{padding:10px}.missing-table>table{width:100%;margin:0 0 8px;border:1px solid rgba(255,255,255,.09);border-radius:10px;overflow:hidden}.missing-table .missing-series{margin:8px 0 0!important}.missing-table thead th{height:38px;padding:0 12px;vertical-align:middle;background:#2d3540;border-right:1px solid rgba(255,255,255,.06)}.missing-table thead th:last-child{border-right:0}.missing-table .missing-series summary{min-height:42px;display:flex;align-items:center}
|
||
.missing-section-heading{display:flex;align-items:center;gap:10px;margin:14px 0 8px;padding:10px 13px;border-radius:9px;font-size:12px;font-weight:800;letter-spacing:.04em;text-transform:uppercase}.missing-section-heading:first-child{margin-top:0}.missing-section-heading::before{content:"";width:7px;height:7px;border-radius:50%;background:currentColor;box-shadow:0 0 10px currentColor}.missing-section-heading.movies{color:#8fc9f2;background:linear-gradient(90deg,rgba(96,169,230,.16),rgba(96,169,230,.04));border:1px solid rgba(96,169,230,.2)}.missing-section-heading.series{color:#e2a9aa;background:linear-gradient(90deg,rgba(190,83,83,.16),rgba(190,83,83,.04));border:1px solid rgba(190,83,83,.2)}
|
||
</style>
|
||
<script>document.querySelectorAll('details.series').forEach(series => { series.open = false; });</script>
|
||
<style>
|
||
header{z-index:100!important}
|
||
#seriesList{overflow:visible!important}
|
||
#seriesList details.series{overflow:visible!important}
|
||
#seriesList details.series>summary{position:sticky!important;top:64px!important;z-index:30!important}
|
||
#seriesList .season-divider td{top:110px!important;z-index:29!important}
|
||
</style>
|
||
<style>
|
||
.subtitle-de{background:#126f39!important;border-color:#19894b!important;color:#d9ffe8!important}
|
||
.audio-preferred{background:#2673a8!important;border-color:#64b5ed!important;color:#eef8ff!important;box-shadow:0 0 12px rgba(96,169,230,.3)}
|
||
.col-progress{width:16%}.col-time{width:10%}.col-source{width:10%}.col-language{width:12%}
|
||
.progress-wrap{display:flex;align-items:center;gap:8px}.progress-bar{height:7px;min-width:70px;flex:1;background:#3a3a3a;border-radius:99px;overflow:hidden}.progress-bar span{display:block;height:100%;background:#159447;border-radius:99px}.download-title{color:var(--blue);font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block}
|
||
.download-hero{display:flex;justify-content:space-between;align-items:center;gap:20px;margin-bottom:14px;padding:24px 26px;border:1px solid rgba(96,169,230,.2);border-radius:14px;background:radial-gradient(circle at 88% 20%,rgba(21,148,71,.18),transparent 32%),linear-gradient(135deg,rgba(39,48,61,.96),rgba(27,30,36,.96));box-shadow:0 16px 40px rgba(0,0,0,.22)}.eyebrow{color:#79b9e9;font-size:10px;font-weight:800;letter-spacing:.16em}.download-hero h2{margin:7px 0 4px;font-size:22px}.download-hero p{margin:0;color:#9da5b2}.queue-pill{display:flex;align-items:center;gap:9px;padding:10px 14px;border:1px solid rgba(255,255,255,.12);border-radius:999px;background:rgba(13,16,20,.35);white-space:nowrap;color:#d9dee7}.queue-dot{width:9px;height:9px;border-radius:50%;background:#f4c430;box-shadow:0 0 12px currentColor}.download-stats{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:14px}.download-card{display:flex;align-items:center;gap:12px;min-height:92px;padding:16px;border:1px solid rgba(255,255,255,.08);border-radius:11px;background:linear-gradient(145deg,rgba(43,47,55,.92),rgba(29,31,36,.92));box-shadow:0 8px 22px rgba(0,0,0,.16)}.card-icon{display:grid;place-items:center;width:38px;height:38px;border-radius:10px;background:rgba(21,148,71,.2);color:#6ae29a;font-size:21px;font-weight:700}.card-icon.blue{background:rgba(96,169,230,.18);color:#8bc9f2}.card-icon.purple{background:rgba(146,87,194,.2);color:#d7adf3}.card-icon.orange{background:rgba(244,166,48,.18);color:#ffd383}.download-card div{min-width:0}.card-label{display:block;color:#9da5b2;font-size:11px;text-transform:uppercase;letter-spacing:.06em}.download-card strong{display:block;margin:3px 0;font-size:20px;color:#f1f5fa}.download-card small{display:block;color:#7f8996;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.download-table{box-shadow:0 16px 34px rgba(0,0,0,.24)}
|
||
@media(max-width:900px){.download-stats{grid-template-columns:repeat(2,1fr)}.download-hero{align-items:flex-start;flex-direction:column}.queue-pill{align-self:stretch;justify-content:center}}@media(max-width:520px){.download-stats{grid-template-columns:1fr}.download-hero{padding:18px}.download-hero h2{font-size:19px}}
|
||
</style>
|
||
<style>
|
||
.missing-table{padding:10px}.missing-table>table{width:100%;margin:0 0 8px;border:1px solid rgba(255,255,255,.09);border-radius:10px;overflow:hidden}.missing-table .missing-series{margin:8px 0 0!important}.missing-table thead th{height:38px;padding:0 12px;vertical-align:middle;background:#2d3540;border-right:1px solid rgba(255,255,255,.06)}.missing-table thead th:last-child{border-right:0}.missing-table .missing-series summary{min-height:42px;display:flex;align-items:center}
|
||
</style>
|
||
<style>
|
||
.datastore-hero{display:flex;justify-content:space-between;align-items:center;gap:18px;margin-bottom:14px;padding:22px 26px;border:1px solid rgba(96,169,230,.2);border-radius:14px;background:radial-gradient(circle at 88% 20%,rgba(96,169,230,.14),transparent 32%),linear-gradient(135deg,rgba(39,48,61,.96),rgba(27,30,36,.96));box-shadow:0 14px 34px rgba(0,0,0,.2)}.datastore-hero h2{margin:7px 0 4px;font-size:22px}.datastore-hero p{margin:0;color:#9da5b2}.datastore-hero code{color:#b9d9f1}.datastore-live{padding:8px 12px;border:1px solid rgba(106,226,154,.22);border-radius:999px;color:#aaf2c5;background:rgba(21,148,71,.12);font-size:11px}.datastore-storage{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:12px}.storage-card{display:flex;align-items:center;gap:11px;min-height:82px;padding:14px;border:1px solid rgba(255,255,255,.08);border-radius:11px;background:linear-gradient(145deg,rgba(43,47,55,.92),rgba(29,31,36,.92));box-shadow:0 8px 22px rgba(0,0,0,.16)}.storage-icon{display:grid;place-items:center;width:35px;height:35px;border-radius:10px;background:rgba(96,169,230,.16);color:#8bc9f2;font-weight:800}.storage-icon.used{background:rgba(244,196,48,.16);color:#f4d267}.storage-icon.free{background:rgba(21,148,71,.16);color:#6ae29a}.storage-icon.percent{background:rgba(146,87,194,.18);color:#d7adf3}.storage-card small{display:block;color:#9da5b2;font-size:10px;text-transform:uppercase;letter-spacing:.06em}.storage-card strong{display:block;margin-top:3px;color:#f1f5fa;font-size:18px}.datastore-panel{padding:18px;border:1px solid rgba(255,255,255,.08);border-radius:12px;background:rgba(31,33,38,.84);box-shadow:0 12px 30px rgba(0,0,0,.18)}.datastore-capacity{margin-bottom:12px}.datastore-panel-head{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;margin-bottom:14px}.datastore-panel-head h3{margin:0 0 4px;font-size:15px}.datastore-panel-head p{margin:0;color:#7f8996;font-size:11px}.datastore-panel-head>strong{color:#dce9f4;font-size:12px}.datastore-progress{height:10px;border-radius:99px;overflow:hidden;background:#363b44}.datastore-progress span{display:block;height:100%;width:0;border-radius:99px;background:linear-gradient(90deg,#60a9e6,#f4c430);transition:width .5s ease}.datastore-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.datastore-categories{display:grid;gap:13px}.category-row-head{display:flex;justify-content:space-between;gap:10px;color:#cbd1db;font-size:12px}.category-row-head strong{color:#f1f5fa}.category-track{height:7px;margin:6px 0 4px;border-radius:99px;overflow:hidden;background:#363b44}.category-track span{display:block;height:100%;border-radius:99px;background:linear-gradient(90deg,#60a9e6,#159447);transition:width .5s ease}.category-row small{color:#7f8996;font-size:10px}.raid-status{padding:5px 9px;border-radius:999px;font-size:11px;font-weight:800}.raid-status.ok{background:#126f39;color:#d9ffe8}.raid-status.rebuilding{background:#245a91;color:#d9efff}.raid-status.degraded{background:#806b25;color:#fff3bd}.raid-status.failed{background:#7a3030;color:#ffd9d9}.raid-status.unknown{background:#3b3b3b;color:#aaa}.raid-summary{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:14px}.raid-summary span{padding:9px;border:1px solid rgba(255,255,255,.07);border-radius:8px;background:rgba(10,14,20,.2)}.raid-summary small,.raid-summary strong{display:block}.raid-summary small{color:#7f8996;font-size:10px}.raid-summary strong{margin-top:3px;color:#e8eef5;font-size:14px}.raid-disks{display:grid;gap:7px}.raid-disk{display:flex;align-items:center;gap:9px;padding:9px 10px;border:1px solid rgba(255,255,255,.07);border-radius:8px;background:rgba(40,43,49,.65)}.raid-disk>div{min-width:0;flex:1}.raid-disk strong,.raid-disk small{display:block}.raid-disk strong{color:#dce9f4;font-size:12px}.raid-disk small{margin-top:2px;color:#7f8996;font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.raid-disk b{font-size:10px;text-transform:uppercase;color:#b8c2cd}.disk-status{width:9px;height:9px;flex:0 0 auto;border-radius:50%;background:#929292}.disk-status.active{background:#6ae29a;box-shadow:0 0 9px #6ae29a}.disk-status.failed{background:#ff8585;box-shadow:0 0 9px #ff8585}.disk-status.spare{background:#b9b9b9}.disk-status.rebuilding{background:#60a9e6;box-shadow:0 0 9px #60a9e6}.disk-status.missing{background:#f4c430}.raid-recovery{margin-bottom:14px;padding:12px;border:1px solid rgba(96,169,230,.2);border-radius:9px;background:rgba(48,69,92,.22)}.recovery-head,.recovery-meta{display:flex;justify-content:space-between;gap:12px}.recovery-head{margin-bottom:8px;color:#ddecfa;font-size:12px}.recovery-meta{margin-top:8px;color:#9da5b2;font-size:10px}.recovery-meta strong{color:#e8eef5}.datastore-empty{padding:16px;text-align:center;color:#7f8996;font-size:11px}@media(max-width:900px){.datastore-storage{grid-template-columns:repeat(2,1fr)}.datastore-grid{grid-template-columns:1fr}}@media(max-width:600px){.datastore-hero{align-items:flex-start;flex-direction:column;padding:18px}.datastore-live{align-self:stretch;text-align:center}.datastore-storage{grid-template-columns:1fr 1fr;gap:7px}.storage-card{min-height:72px;padding:10px}.storage-card strong{font-size:16px}.datastore-panel{padding:14px}.raid-summary{gap:5px}.raid-summary span{padding:7px}.raid-disk small{max-width:190px}}
|
||
</style>
|
||
<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 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:[]})}};
|
||
if(downloadBody){fetchDownloads();setInterval(fetchDownloads,4000)}
|
||
const diskList=document.querySelector('#diskList');
|
||
function renderSystem(data){const cpu=Number(data.cpu_percent)||0,ram=data.memory||{};const cpuValue=document.querySelector('#cpuValue'),ramValue=document.querySelector('#ramValue'),cpuBar=document.querySelector('#cpuBar'),ramBar=document.querySelector('#ramBar'),updated=document.querySelector('#systemUpdated');if(cpuValue)cpuValue.textContent=`${cpu.toFixed(1)}%`;if(ramValue)ramValue.textContent=`${ram.used||'—'} / ${ram.total||'—'} (${Number(ram.percent||0).toFixed(1)}%)`;if(cpuBar)cpuBar.style.width=`${Math.min(100,cpu)}%`;if(ramBar)ramBar.style.width=`${Math.min(100,Number(ram.percent)||0)}%`;if(updated)updated.textContent='Gerade aktualisiert';if(!diskList)return;diskList.innerHTML=(data.disks||[]).map(d=>`<div class="disk-row"><div class="disk-name">${html(d.label)}<span class="disk-path">${html(d.path)}</span></div><div class="disk-bar"><span style="width:${Math.min(100,Number(d.percent)||0)}%"></span></div><div class="disk-values">${html(d.used)} / ${html(d.total)}<br><span class="muted">${Number(d.percent||0).toFixed(1)}% belegt</span></div></div>`).join('')||'<div class="disk-row muted">Keine Laufwerke gefunden</div>'}
|
||
async function fetchSystem(){try{const response=await fetch('/api/system');renderSystem(await response.json())}catch(error){const updated=document.querySelector('#systemUpdated');if(updated)updated.textContent='Nicht verfügbar'}}
|
||
if(document.querySelector('#dashboard')){fetchSystem();setInterval(fetchSystem,5000)}
|
||
const datastoreView=document.querySelector('#datastore');function renderDatastore(data){const storage=data.storage||{},raid=data.raid||{};const set=(id,value)=>{const node=document.querySelector(id);if(node)node.textContent=value};set('#datastoreTotal',storage.total||'—');set('#datastoreUsed',storage.used||'—');set('#datastoreFree',storage.free||'—');set('#datastorePercent',storage.percent!=null?`${storage.percent}%`:'—');set('#datastorePath',storage.path||'/nesflix');set('#datastoreCapacityLabel',storage.percent!=null?`${storage.used||'—'} / ${storage.total||'—'} · ${storage.percent}%`:storage.error||'Nicht verfügbar');set('#datastoreUpdated',storage.scanned_at?'Gerade aktualisiert':'Nicht verfügbar');const capacityBar=document.querySelector('#datastoreCapacityBar');if(capacityBar)capacityBar.style.width=`${Math.min(100,Number(storage.percent)||0)}%`;const categories=document.querySelector('#datastoreCategories');if(categories){const values=(storage.categories||[]).filter(category=>category.bytes>0||['movies','series','anime','kdrama','downloads'].includes(category.key));categories.innerHTML=values.length?values.map(category=>`<div class="category-row"><div class="category-row-head"><span>${html(category.label)}</span><strong>${html(category.size||'0 B')}</strong></div><div class="category-track"><span style="width:${Math.min(100,Number(category.percent)||0)}%"></span></div><small>${Number(category.percent||0).toFixed(1)}% vom belegten Speicher · ${Number(category.files||0).toLocaleString('de-DE')} Dateien</small></div>`).join(''):'<div class="datastore-empty">Keine Kategorien gefunden</div>'}const badge=document.querySelector('#raidStatusBadge');if(badge){badge.textContent=raid.label||'UNKNOWN';badge.className=`raid-status ${raid.status||'unknown'}`}set('#raidDevice',raid.device||'/dev/md127');const summary=document.querySelector('#raidSummary');if(summary)summary.innerHTML=`<span><small>Level</small><strong>${html(raid.level||'—')}</strong></span><span><small>Aktiv</small><strong>${html(raid.active_devices??'—')} / ${html(raid.raid_devices??'—')}</strong></span><span><small>Fehlend</small><strong>${html(raid.failed_devices??0)}</strong></span>`;const recovery=raid.recovery||{},recoveryBox=document.querySelector('#raidRecovery');if(recoveryBox){recoveryBox.classList.toggle('hidden',!recovery.active);set('#raidRecoveryPercent',`${Number(recovery.percent||0).toFixed(1)}%`);set('#raidRecoverySpeed',recovery.speed||'—');set('#raidRecoveryFinish',recovery.finish||'—');const recoveryBar=document.querySelector('#raidRecoveryBar');if(recoveryBar)recoveryBar.style.width=`${Math.min(100,Number(recovery.percent)||0)}%`}const disks=document.querySelector('#raidDisks');if(disks){disks.innerHTML=(raid.devices||[]).length?(raid.devices||[]).map(disk=>`<div class="raid-disk"><span class="disk-status ${html(disk.status||'unknown')}"></span><div><strong>${html(disk.device)}</strong><small>Slot ${html(disk.slot||'—')} · ${html(disk.model||'—')}</small><small>${html(disk.size||'—')} · Seriennummer: ${html(disk.serial||'—')}</small><small class="raid-smart">SMART: ${html(disk.smart||'Nicht aktiviert')}</small><small class="raid-temperature">Temperatur: ${html(disk.temperature||'Nicht verfügbar')}</small></div><b>${html(disk.status||'unknown')}</b></div>`).join(''):'<div class="datastore-empty">Keine RAID-Mitglieder erkannt</div>'}if(raid.error){const node=document.querySelector('#raidDisks .datastore-empty');if(node)node.textContent=raid.error}}async function fetchDatastore(){if(!datastoreView)return;try{renderDatastore(await (await fetch('/api/datastore')).json())}catch(error){renderDatastore({storage:{error:'Datastore nicht erreichbar'},raid:{status:'failed',label:'FAILED',error:'RAID-Status nicht verfügbar'}})}}if(datastoreView){fetchDatastore();setInterval(fetchDatastore,5000)}
|
||
</script>
|
||
<style>
|
||
.season-divider td{padding:6px 10px;background:#1b1b1b;border-top:1px solid #444;border-bottom:1px solid #303030;color:#aaa;font-size:11px;font-weight:700;letter-spacing:.3px}
|
||
#seriesList{overflow:visible}.series{overflow:visible!important}.series summary{position:sticky!important;top:64px!important;z-index:30!important}.season-divider td{top:110px!important;z-index:29!important}
|
||
.series[open] table{animation:seriesExpand .38s cubic-bezier(.2,.75,.25,1) both;transform-origin:top center}.series summary::after{content:'⌄';margin-left:auto;color:#929292;font-size:16px;line-height:1;transition:transform .3s ease,color .3s ease}.series[open] summary::after{transform:rotate(180deg);color:#60a9e6}@keyframes seriesExpand{from{opacity:0;transform:translateY(-8px) scaleY(.98)}to{opacity:1;transform:translateY(0) scaleY(1)}}
|
||
.series{position:relative;margin-bottom:14px;border:1px solid #353b46;border-radius:10px;box-shadow:0 8px 24px rgba(0,0,0,.14);transition:border-color .25s ease,box-shadow .25s ease,transform .25s ease}.series:hover{border-color:#4a5565;box-shadow:0 12px 30px rgba(0,0,0,.24);transform:translateY(-1px)}.series-current{border-color:#557ba5;box-shadow:0 0 0 1px rgba(96,169,230,.18),0 14px 34px rgba(0,0,0,.25)}.series summary{position:sticky;top:0;z-index:5;display:flex;align-items:center;min-height:46px;padding:12px 14px;background:rgba(40,40,40,.96);backdrop-filter:blur(12px);border-bottom:1px solid transparent;transition:background .25s ease,border-color .25s ease,color .25s ease}.series-current summary{background:rgba(47,61,78,.98)}.series summary::marker{color:#60a9e6}.series[open] summary{border-bottom-color:#3d444f;box-shadow:0 4px 14px rgba(0,0,0,.16)}.series summary:hover{background:rgba(54,60,70,.97);color:#fff}.series summary .muted{margin-left:7px}.series table tbody tr:not(.season-divider){animation:rowIn .35s ease both;transition:background .2s ease,transform .2s ease}.series table tbody tr:not(.season-divider):hover{background:#292e36;transform:translateX(2px)}.season-divider td{position:sticky;top:46px;z-index:4;background:rgba(27,27,27,.96);backdrop-filter:blur(8px)}
|
||
.state.downloading,.downloading-badge{animation:downloadPulse 2s ease-in-out infinite}.progress-bar span{position:relative;overflow:hidden}.progress-bar span::after{content:"";position:absolute;inset:0;background:linear-gradient(110deg,transparent 20%,rgba(255,255,255,.25) 48%,transparent 72%);animation:progressShimmer 1.8s linear infinite}.nav button{transition:color .2s ease,background .2s ease,transform .2s ease}.nav button:hover{transform:translateY(-1px)}.badge{transition:transform .18s ease,filter .18s ease}.badge:hover{transform:translateY(-2px);filter:brightness(1.15)}
|
||
@keyframes rowIn{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none}}@keyframes downloadPulse{0%,100%{box-shadow:0 0 0 rgba(113,60,158,0)}50%{box-shadow:0 0 14px rgba(146,87,194,.35)}}@keyframes progressShimmer{from{transform:translateX(-100%)}to{transform:translateX(100%)}}
|
||
@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-duration:.01ms!important}}
|
||
.view#sonarr{padding-bottom:4px}.view#sonarr #seriesList{border:0;border-radius:0;background:transparent;overflow:visible}.series-category{margin:0 0 18px;padding:0 0 2px;border:1px solid rgba(96,169,230,.16);border-radius:13px;background:linear-gradient(145deg,rgba(34,39,47,.82),rgba(25,27,31,.78));box-shadow:0 10px 28px rgba(0,0,0,.14);overflow:hidden}.series-category:last-child{margin-bottom:0}.series-category-heading{margin:0;padding:13px 16px;border:0;border-bottom:1px solid rgba(96,169,230,.16);border-radius:0;background:linear-gradient(100deg,rgba(54,74,98,.72),rgba(36,42,50,.7));box-shadow:inset 3px 0 0 #60a9e6}.series-category-heading h2{font-size:14px;letter-spacing:.02em}.series-category-heading span{padding:3px 8px;border:1px solid rgba(255,255,255,.1);border-radius:999px;background:rgba(10,14,20,.2)}.series-category>.series{margin:8px 10px 0;border-color:rgba(255,255,255,.08);border-radius:9px;background:rgba(31,33,38,.78);box-shadow:0 5px 15px rgba(0,0,0,.13)}.series-category>.series:last-child{margin-bottom:10px}.series-category>.series:hover{transform:none;border-color:rgba(96,169,230,.4);box-shadow:0 8px 22px rgba(0,0,0,.2)}.series-category>.series summary{position:relative!important;top:auto!important;z-index:1;min-height:42px;padding:10px 13px;background:rgba(42,45,51,.8);font-size:12px}.series-category>.series[open] summary{background:rgba(51,65,82,.88);border-bottom-color:rgba(96,169,230,.24)}.series-category>.series table{background:rgba(22,24,28,.42)}.series-category>.series th{padding:7px 10px;background:rgba(40,45,53,.86);font-size:10px;color:#9eb8ce;text-transform:uppercase;letter-spacing:.05em}.series-category>.series td{padding:8px 10px}.series-category .season-divider td{position:static!important;top:auto!important;padding:7px 13px;background:rgba(12,15,19,.72);color:#9da5b2;border-top:0}.missing-table{border:1px solid rgba(255,255,255,.1);border-radius:13px;background:rgba(28,30,35,.82);box-shadow:0 10px 30px rgba(0,0,0,.18);overflow:hidden}.missing-table>table{background:transparent}.missing-table>table th{padding:10px 12px;background:linear-gradient(100deg,#303741,#272b32);color:#b5c7d8;text-transform:uppercase;font-size:10px;letter-spacing:.06em}.missing-table>table td{padding:9px 12px}.missing-table>table tbody tr:hover{background:rgba(96,169,230,.08)}.missing-table .missing-series{margin:8px 10px!important;border:1px solid rgba(255,255,255,.09)!important;border-radius:10px!important;background:rgba(36,38,43,.78);box-shadow:0 5px 18px rgba(0,0,0,.14)}.missing-table .missing-series summary{position:relative!important;top:auto!important;z-index:1;min-height:44px;padding:11px 13px;background:rgba(47,45,45,.78);border-bottom:0}.missing-table .missing-series[open] summary{background:rgba(91,55,58,.78);border-bottom-color:rgba(255,130,130,.2)}.missing-table .missing-series table{background:rgba(22,24,28,.42)}.missing-table .missing-series th{padding:7px 10px;background:rgba(56,47,49,.84);font-size:10px;color:#d6afb2;text-transform:uppercase;letter-spacing:.05em}.missing-table .missing-series td{padding:8px 10px}.missing-table .missing-series tbody tr:hover{background:rgba(210,100,100,.08)}
|
||
</style>
|
||
<style>
|
||
:root{--accent:#f4c430;--accent-blue:#60a9e6;--surface:rgba(35,37,42,.82);--surface-strong:#282b31;--border-soft:rgba(255,255,255,.09)}
|
||
html,body{min-height:100%;background:#151619}body{min-height:100vh;background:radial-gradient(circle at 10% -15%,rgba(96,169,230,.12),transparent 33%),radial-gradient(circle at 90% 0%,rgba(244,196,48,.07),transparent 28%),#151619;background-attachment:fixed;letter-spacing:.01em}
|
||
header{height:64px;background:rgba(31,33,38,.9);backdrop-filter:blur(18px);border-bottom:1px solid var(--border-soft);box-shadow:0 8px 30px rgba(0,0,0,.2);position:sticky;top:0;z-index:20}.logo{font-size:23px;text-shadow:0 0 18px rgba(244,196,48,.24)}.head-title{font-size:14px;letter-spacing:.01em}.head-sub{color:#9da5b2}
|
||
main{max-width:1800px;margin:0 auto;padding:18px 20px 42px}.nav{gap:6px;margin:0 0 16px;padding:4px;border:1px solid var(--border-soft);border-radius:9px;background:rgba(31,33,38,.72);box-shadow:0 8px 24px rgba(0,0,0,.12)}.nav button{padding:9px 14px;border-radius:6px;color:#aeb5c1}.nav button.active{background:linear-gradient(135deg,#3a3f48,#2d3139);box-shadow:inset 0 1px rgba(255,255,255,.08),0 4px 12px rgba(0,0,0,.18);color:#fff}.nav button.active::after{content:"";display:block;height:2px;margin:6px 2px -4px;border-radius:2px;background:var(--accent-blue)}
|
||
.toolbar{padding:1px 10px;border:1px solid var(--border-soft);border-radius:8px;background:rgba(35,37,42,.72);box-shadow:0 8px 20px rgba(0,0,0,.12);gap:8px}.toolbar input,.toolbar select{height:30px;padding:5px 9px;background:#2b2e34;border-color:#474d57;transition:border-color .2s,box-shadow .2s,background .2s}.toolbar input:focus,.toolbar select:focus{outline:none;background:#30343b;border-color:var(--accent-blue);box-shadow:0 0 0 3px rgba(96,169,230,.14)}
|
||
.view#sonarr{display:none}.view#sonarr.active{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:10px;align-items:start}.view#sonarr>.series-toolbar{grid-column:1;grid-row:1;height:34px;margin-bottom:0}.view#sonarr>.stats{grid-column:2;grid-row:1;height:34px;margin:0;align-self:stretch;align-items:stretch;flex-wrap:nowrap;white-space:nowrap}.view#sonarr>.stats>.stat{height:34px;display:flex;align-items:center}.view#sonarr>#seriesList{grid-column:1/-1;grid-row:2}.view#radarr{display:none}.view#radarr.active{display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:10px;align-items:start}.view#radarr>.toolbar{grid-column:1;grid-row:1;height:34px;margin-bottom:0}.view#radarr>.stats{grid-column:2;grid-row:1;height:34px;margin:0;align-self:stretch;align-items:stretch;flex-wrap:nowrap;white-space:nowrap}.view#radarr>.stats>.stat{height:34px;display:flex;align-items:center}.view#radarr>.tablewrap{grid-column:1/-1;grid-row:2}
|
||
.stats{gap:9px;margin:12px 0}.stat{background:linear-gradient(135deg,rgba(48,52,60,.9),rgba(34,36,41,.9));border:1px solid var(--border-soft);border-radius:8px;padding:7px 11px;box-shadow:0 5px 15px rgba(0,0,0,.12)}.stat:first-child{color:#fff;font-weight:700}.scan{color:#f4c430!important}
|
||
.tablewrap{border:1px solid var(--border-soft);border-radius:10px;background:rgba(31,33,38,.84);box-shadow:0 14px 34px rgba(0,0,0,.2)}table{border-spacing:0}thead th{height:38px;background:linear-gradient(180deg,#30343b,#292c32);color:#bfc6d1;text-transform:uppercase;letter-spacing:.07em;font-size:10px}tbody td{height:42px;border-bottom-color:rgba(255,255,255,.075)}tbody tr{transition:background .2s,transform .2s}tbody tr:hover{background:rgba(96,169,230,.065);transform:translateX(1px)}.title{font-size:13px}.quality{box-shadow:0 0 12px rgba(21,148,71,.18)}
|
||
#rescan{padding:9px 13px;background:linear-gradient(135deg,#343943,#292c32);border-color:#515865;box-shadow:0 5px 16px rgba(0,0,0,.18);transition:transform .2s,box-shadow .2s,border-color .2s}#rescan:hover{transform:translateY(-2px);border-color:var(--accent-blue);box-shadow:0 8px 20px rgba(0,0,0,.28)}
|
||
.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,.view#sonarr.active{display:block}.view#radarr>.toolbar,.view#sonarr>.series-toolbar{height:auto;margin-bottom:10px}.view#radarr>.stats,.view#sonarr>.stats{height:auto;align-items:initial;flex-wrap:wrap}.view#radarr>.stats>.stat,.view#sonarr>.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)})});
|
||
document.querySelectorAll('details.series').forEach(series=>{const summary=series.querySelector('summary');if(!summary)return;summary.title='Klicken zum Auf- oder Zuklappen';const observer=new IntersectionObserver(([entry])=>series.classList.toggle('series-current',!entry.isIntersecting&&series.open),{threshold:0,rootMargin:'-58px 0px 0px 0px'});observer.observe(summary)});
|
||
</script>
|
||
<style>
|
||
.downloading-badge{background:#713c9e!important;border-color:#9257c2!important;color:#f2e5ff!important}.state.downloading{background:#713c9e;color:#f2e5ff}
|
||
header{z-index:100!important}
|
||
.downloading-state{background:#713c9e!important;color:#f2e5ff!important}
|
||
</style>
|
||
<script>
|
||
function normalizeDownloadText(value){return String(value||'').toLowerCase().replace(/[._()[\]{}'\-·]/g,' ').replace(/\s+/g,' ').trim()}
|
||
function markDownloading(items){
|
||
const downloads=(items||[]).filter(item=>!item.completed).map(item=>normalizeDownloadText(item.title));
|
||
document.querySelectorAll('#movies tbody tr,#sonarr tbody tr').forEach(row=>{
|
||
if(row.dataset.downloadMarked==='1'){
|
||
row.children[1].innerHTML='<span class="badge">Keine Datei</span>';
|
||
row.children[2].innerHTML='<span class="state bad">Fehler</span>';
|
||
row.dataset.downloadMarked='0';
|
||
}
|
||
const details=row.closest('details.series');
|
||
const seriesName=details?normalizeDownloadText(details.querySelector('summary')?.textContent.split('(')[0]):'';
|
||
const rowName=normalizeDownloadText(row.querySelector('.title')?.textContent);
|
||
const rowTokens=rowName.split(' ').filter(token=>token.length>2);
|
||
const isMatch=downloads.some(download=>download.includes(rowName)||(seriesName&&download.includes(seriesName)&&download.includes(rowName.match(/s\d{2}e\d{2}/)?.[0]||''))||(rowTokens.length>=3&&rowTokens.filter(token=>download.includes(token)).length>=Math.ceil(rowTokens.length*.75)));
|
||
if(!rowName||!isMatch)return;
|
||
const quality=row.children[1], state=row.children[2];
|
||
if(quality.textContent.includes('Keine Datei')||row.dataset.downloadMarked==='1'){
|
||
quality.innerHTML='<span class="badge downloading-badge">Downloading</span>';
|
||
state.innerHTML='<span class="state downloading-state">Downloading</span>';
|
||
row.dataset.downloadMarked='1';
|
||
}
|
||
});
|
||
}
|
||
const originalRenderDownloads=renderDownloads;
|
||
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>
|
||
const uiTranslations={de:{film:'Film',quality:'Qualität',status:'Status',audio:'Audio',subs:'Untertitel',video:'Video',size:'Größe',episode:'Episode',allAudio:'Alle Audio-Sprachen',allSubs:'Alle Untertitel',allStatus:'Alle Status',searchMovie:'Film suchen …',searchSeries:'Serie suchen …'},en:{film:'Movie',quality:'Quality',status:'Status',audio:'Audio',subs:'Subtitles',video:'Video',size:'Size',episode:'Episode',allAudio:'All audio languages',allSubs:'All subtitles',allStatus:'All statuses',searchMovie:'Search movies …',searchSeries:'Search series …'}};
|
||
let currentUiLanguage=localStorage.getItem('dashboard-language')||'de';
|
||
function translateLayout(language){
|
||
currentUiLanguage=language;localStorage.setItem('dashboard-language',language);document.documentElement.lang=language;const t=uiTranslations[language];
|
||
document.querySelectorAll('[data-de][data-en]').forEach(node=>{node.textContent=node.dataset[language]});
|
||
document.querySelector('#languageToggle').textContent=language==='de'?'EN':'DE';
|
||
const navLabels={dashboard:['▦ Dashboard','▦ Dashboard'],radarr:['RADARR · Filme','RADARR · Movies'],sonarr:['SONARR · Serien','SONARR · Series'],missing:['⚠ Ohne Deutsch','⚠ Without German'],downloads:['SABNZBD · Downloads','SABNZBD · Downloads']};document.querySelectorAll('.nav button').forEach(button=>{const labels=navLabels[button.dataset.view];if(labels)button.childNodes[0].textContent=labels[language==='de'?0:1]});
|
||
const searchInput=document.querySelector('#search');if(searchInput)searchInput.placeholder=t.searchMovie;const seriesSearchInput=document.querySelector('#seriesSearch');if(seriesSearchInput)seriesSearchInput.placeholder=t.searchSeries;
|
||
const filterOptions=[['#audioFilter',t.allAudio],['#subFilter',t.allSubs],['#stateFilter',t.allStatus]];filterOptions.forEach(([selector,label])=>{const first=document.querySelector(`${selector} option:first-child`);if(first)first.textContent=label});
|
||
document.querySelectorAll('#movies thead th').forEach((th,i)=>{th.textContent=[t.film,t.quality,t.status,t.audio,t.subs,t.video,t.size][i]||th.textContent});
|
||
document.querySelectorAll('#seriesList thead th').forEach((th,i)=>{th.textContent=[t.episode,t.quality,t.status,t.audio,t.subs,t.video,t.size][i]||th.textContent});
|
||
document.querySelectorAll('#downloadsTable thead th').forEach((th,i)=>{th.textContent=(language==='de'?['Download','Status','Fortschritt','Größe / Rest','Restzeit','Quelle','Sprache']:['Download','Status','Progress','Size / Remaining','Time left','Source','Language'])[i]||th.textContent});
|
||
}
|
||
function highlightPreferences(){const audioPreference=localStorage.getItem('preferred-audio')||'';document.querySelectorAll('#movies tbody tr td:nth-child(4) .badge,#sonarr tbody tr td:nth-child(4) .badge').forEach(badge=>badge.classList.toggle('audio-preferred',!!audioPreference&&badge.textContent.includes(audioPreference)))}
|
||
function loadPreferences(){const audioPreference=localStorage.getItem('preferred-audio')||'';const subsPreference=localStorage.getItem('preferred-subs')||'';const audioSelect=document.querySelector('#preferredAudio'),subsSelect=document.querySelector('#preferredSubs');if(audioSelect)audioSelect.value=audioPreference;if(subsSelect)subsSelect.value=subsPreference;const audioFilter=document.querySelector('#audioFilter'),subFilter=document.querySelector('#subFilter');if(audioFilter)audioFilter.value=audioPreference;if(subFilter)subFilter.value=subsPreference;if(typeof apply==='function')apply();highlightPreferences()}
|
||
translateLayout(currentUiLanguage);loadPreferences();
|
||
const serviceIconSvg={radarr:'<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="7"/><circle cx="12" cy="12" r="2" fill="currentColor"/><path d="M12 5a7 7 0 0 1 6 3M12 12l6-5"/></svg>',sonarr:'<svg viewBox="0 0 24 24"><path d="M4 12h3M17 12h3M7 8a6 6 0 0 1 10 0M7 16a6 6 0 0 0 10 0M10 12h4"/><circle cx="12" cy="12" r="2" fill="currentColor"/></svg>',sabnzbd:'<svg viewBox="0 0 24 24"><path d="M12 4v11M8 11l4 4 4-4M5 19h14"/></svg>',jellyfin:'<svg viewBox="0 0 24 24"><path d="M7 4h10l4 8-4 8H7l-4-8 4-8Z"/><path d="m10 8 6 4-6 4V8Z" fill="currentColor"/></svg>',seerr:'<svg viewBox="0 0 24 24"><circle cx="10.5" cy="10.5" r="6"/><path d="m15 15 5 5M8 10h5"/></svg>',prowlarr:'<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="8"/><path d="m12 6 2 6-2 6-2-6 2-6ZM6 12h12"/></svg>',easynews:'<svg viewBox="0 0 24 24"><path d="M6 18h12a3 3 0 0 0 .5-6A6.5 6.5 0 0 0 6 10a4 4 0 0 0 0 8Z"/></svg>',qbittorrent:'<svg viewBox="0 0 24 24"><path d="m5 7 5-3 5 3-5 3-5-3ZM9 14l5-3 5 3-5 3-5-3ZM5 17l5-3 5 3-5 3-5-3Z"/></svg>',bazarr:'<svg viewBox="0 0 24 24"><path d="M5 6h14M5 12h9M5 18h14"/><path d="m17 10 2 2-2 2"/></svg>',lingarr:'<svg viewBox="0 0 24 24"><path d="M5 6h14v10H9l-4 3V6Z"/><path d="M9 10h6M9 13h4"/></svg>',libretranslate:'<svg viewBox="0 0 24 24"><path d="M5 6h8M9 4v2M6 6c1 5 4 8 8 10M6 16c3-1 5-3 7-6M14 6h5M16 4v2M14 18h7M17 10l-3 8M18 15h4"/></svg>',sonarr_prowlarr_proxy:'<svg viewBox="0 0 24 24"><path d="M5 8h8M11 5l3 3-3 3M19 16h-8M13 13l-3 3 3 3"/><circle cx="5" cy="8" r="2"/><circle cx="19" cy="16" r="2"/></svg>',radarr_prowlarr_proxy:'<svg viewBox="0 0 24 24"><path d="M5 8h8M11 5l3 3-3 3M19 16h-8M13 13l-3 3 3 3"/><circle cx="5" cy="8" r="2"/><circle cx="19" cy="16" r="2"/></svg>',dashboard:'<svg viewBox="0 0 24 24"><path d="m4 11 8-7 8 7v8a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-8Z"/><path d="M9 20v-6h6v6"/></svg>'};function upgradeServiceIcons(){document.querySelectorAll('.service-logo[class*="service-icon-"]').forEach(icon=>{const key=[...icon.classList].find(name=>name.startsWith('service-icon-'))?.slice(13);if(serviceIconSvg[key]&&icon.dataset.iconKey!==key){icon.innerHTML=serviceIconSvg[key];icon.dataset.iconKey=key}})}const serviceIconObserver=new MutationObserver(upgradeServiceIcons);const serviceIconList=document.querySelector('.service-list');if(serviceIconList)serviceIconObserver.observe(serviceIconList,{childList:true,subtree:true});upgradeServiceIcons();
|
||
document.querySelector('#languageToggle').addEventListener('click',()=>translateLayout(currentUiLanguage==='de'?'en':'de'));
|
||
const header=document.querySelector('header'),headerActions=document.querySelector('.actions'),mainNav=document.querySelector('.nav');if(header&&headerActions&&mainNav){mainNav.classList.add('header-nav');header.insertBefore(mainNav,headerActions);const headerStyle=document.createElement('style');headerStyle.textContent='.head-title,.head-sub{display:none}.header-nav{display:flex;align-items:center;gap:4px;margin:0 0 0 22px!important;padding:3px!important;border:0!important;background:transparent!important;box-shadow:none!important;overflow:visible}.header-nav button{padding:8px 11px;font-size:12px}.header-nav .nav-count{font-size:10px}header{gap:0}.brand{flex:0 0 auto}@media(max-width:850px){.header-nav{margin-left:10px!important;overflow-x:auto;max-width:calc(100vw - 250px);scrollbar-width:none}.header-nav::-webkit-scrollbar{display:none}.header-nav button{white-space:nowrap;padding:7px 9px;font-size:11px}}@media(max-width:560px){.header-nav{max-width:calc(100vw - 135px)}.header-nav button{font-size:0;padding:8px}.header-nav button[data-view="dashboard"]{font-size:0}.header-nav button[data-view="dashboard"]::before{content:"▦";font-size:16px}.header-nav button[data-view="radarr"]::before{content:"R";font-size:14px;font-weight:800}.header-nav button[data-view="sonarr"]::before{content:"S";font-size:14px;font-weight:800}.header-nav button[data-view="missing"]::before{content:"⚠";font-size:14px}.header-nav button[data-view="downloads"]::before{content:"↓";font-size:16px}}';document.head.appendChild(headerStyle)}
|
||
const officialServiceIcons={radarr:'https://cdn.simpleicons.org/radarr/CC7B19',sonarr:'https://cdn.simpleicons.org/sonarr/35C5F4',sabnzbd:'https://api.iconify.design/mdi:download-box.svg?color=%23F5A623',jellyfin:'https://cdn.simpleicons.org/jellyfin/AA5CC3',seerr:'https://api.iconify.design/mdi:movie-search.svg?color=%2373CFF8',prowlarr:'https://api.iconify.design/mdi:radar.svg?color=%235B6BBF',easynews:'https://api.iconify.design/mdi:cloud-download.svg?color=%23F28C28',qbittorrent:'https://cdn.simpleicons.org/qbittorrent/2F81C7',bazarr:'https://api.iconify.design/mdi:subtitles.svg?color=%2344A5B5',lingarr:'https://api.iconify.design/mdi:forum.svg?color=%238B5CF6',libretranslate:'https://cdn.simpleicons.org/libretranslate/22C55E'};Object.entries(officialServiceIcons).forEach(([key,url])=>{serviceIconSvg[key]=`<img src="${url}" alt="" loading="lazy" referrerpolicy="no-referrer">`});upgradeServiceIcons();
|
||
document.querySelectorAll('.service-logo svg').forEach(svg=>{svg.style.width='19px';svg.style.height='19px';svg.style.fill='none';svg.style.stroke='currentColor';svg.style.strokeWidth='1.8';svg.style.strokeLinecap='round';svg.style.strokeLinejoin='round'});
|
||
document.querySelectorAll('.service-logo img').forEach(img=>{img.style.width='20px';img.style.height='20px';img.style.objectFit='contain'});
|
||
setInterval(()=>{document.querySelectorAll('.service-logo svg').forEach(svg=>{svg.style.width='19px';svg.style.height='19px';svg.style.fill='none';svg.style.stroke='currentColor';svg.style.strokeWidth='1.8';svg.style.strokeLinecap='round';svg.style.strokeLinejoin='round'});document.querySelectorAll('.service-logo img').forEach(img=>{img.style.width='20px';img.style.height='20px';img.style.objectFit='contain'})},500);
|
||
const settingsBackdrop=document.querySelector('#settingsBackdrop');const closeSettings=()=>settingsBackdrop.classList.add('hidden');document.querySelector('#settingsOpen').addEventListener('click',()=>{loadPreferences();settingsBackdrop.classList.remove('hidden')});document.querySelector('#settingsClose').addEventListener('click',closeSettings);document.querySelector('#settingsCancel').addEventListener('click',closeSettings);settingsBackdrop.addEventListener('click',event=>{if(event.target===settingsBackdrop)closeSettings()});document.querySelector('#settingsSave').addEventListener('click',()=>{localStorage.setItem('preferred-audio',document.querySelector('#preferredAudio').value);localStorage.setItem('preferred-subs',document.querySelector('#preferredSubs').value);loadPreferences();closeSettings()});
|
||
</script>
|
||
<style>.nav-icon{width:15px;height:15px;margin-right:5px;vertical-align:-3px;object-fit:contain}.nav-tab-icon{width:15px;height:15px;margin-right:6px;vertical-align:-3px;object-fit:contain}.storage-icon img{width:20px;height:20px;object-fit:contain}.raid-status-side{display:flex;flex-direction:column;align-items:flex-end;gap:4px}.raid-status-side small{color:#8d9aaa;font-size:10px;white-space:nowrap}.raid-disks{grid-template-columns:repeat(2,minmax(0,1fr));gap:6px}.raid-disk{padding:7px 9px}.raid-disk small{margin-top:1px}.raid-smart{opacity:.85}.series-category,.missing-table{overflow:visible!important}.series-category>.series summary,.missing-table .missing-series summary{position:sticky!important;top:64px!important;z-index:30!important}.series-category>.series[open] summary,.missing-table .missing-series[open] summary{box-shadow:0 4px 14px rgba(0,0,0,.25)}@media(max-width:700px){.raid-disks{grid-template-columns:1fr}}</style>
|