Files
language-dashboard/templates/index.html
T
nessi a276911ca7 feat: add refined visual design with gradient backgrounds, improved borders, and enhanced hover effects
Add radial gradient background with accent colors, refined header with backdrop blur and shadow, improved navigation with active state indicator line, enhanced toolbar with focus states and transitions, refined table styling with gradient headers and hover animations, and add rescan button hover lift effect. Include responsive breakpoints for mobile layout adjustments.
2026-08-13 11:46:02 +02:00

103 lines
23 KiB
HTML

<!doctype html>
<html lang="de"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Language Dashboard</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}}
</style></head><body>
<header><div class="logo">MEDIA</div><div><div class="head-title">Language Dashboard</div><div class="head-sub">Audio- und Untertitelspuren aus den Mediendateien</div></div><div class="actions"><button id="rescan">↻ Neu scannen</button></div></header>
<main>
{% if error %}<div class="banner"><strong>Fehler:</strong> {{ error }}</div>{% endif %}
<nav class="nav"><button class="active" data-view="radarr">RADARR · Filme</button>{% if sonarr_enabled %}<button data-view="sonarr">SONARR · Serien</button>{% endif %}{% if sab_enabled %}<button data-view="downloads">SABNZBD · Downloads</button>{% endif %}</nav>
<section id="radarr" class="view active" 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="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 group in 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><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 %}
</div></section>{% endif %}
{% if sab_enabled %}<section id="downloads" class="view" data-source="downloads"><div class="stats"><span class="stat" id="downloadCount">Downloads werden geladen …</span><span class="stat" id="sabQueueStatus">SABnzbd …</span><span class="stat" id="sabSpeed">Geschwindigkeit: —</span></div><div id="downloadError" class="banner hidden"></div><div class="tablewrap"><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 %}
</main>
<script>
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();
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');document.querySelector('#rescan').dataset.source=b.dataset.view});
function statusText(s){if(s.state==='running')return `Scan: ${s.done}/${s.total||'?'} Dateien`;if(s.state==='done')return 'Scan abgeschlossen';if(s.state==='error')return 'Scanfehler: '+s.error;return 'Scan wird vorbereitet …'}
const seenRunning={radarr:false,sonarr:false};async function poll(){try{const data=await (await fetch('/api/scan-status')).json();document.querySelector('#radarrStatus').textContent=statusText(data.radarr);const sonarr=document.querySelector('#sonarrStatus');if(sonarr)sonarr.textContent=statusText(data.sonarr);['radarr','sonarr'].forEach(source=>{if(data[source].state==='running')seenRunning[source]=true;if(seenRunning[source]&&['done','error'].includes(data[source].state)){seenRunning[source]=false;setTimeout(()=>location.reload(),700)}})}catch(e){} };setInterval(poll,1200);poll();
document.querySelector('#rescan').onclick=async()=>{const b=document.querySelector('#rescan');b.disabled=true;b.textContent='Scanne …';await fetch('/api/rescan?source='+(b.dataset.source||'radarr'),{method:'POST'});b.disabled=false;b.textContent='↻ Neu scannen';poll()};
</script></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}
</style>
<script>document.querySelectorAll('details.series').forEach(series => { series.open = false; });</script>
<style>
.subtitle-de{background:#126f39!important;border-color:#19894b!important;color:#d9ffe8!important}
.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}
</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 downloadBody=document.querySelector('#downloadsBody');
function html(value){return String(value??'—').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]))}
function renderDownloads(data){if(!downloadBody)return;const items=data.items||[];document.querySelector('#downloadCount').textContent=`${items.length} Downloads`;document.querySelector('#sabQueueStatus').textContent=`SABnzbd: ${data.paused?'Pausiert':data.queue_status||'Idle'}`;document.querySelector('#sabSpeed').textContent=`Geschwindigkeit: ${data.speed||'—'}`;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)}
</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:0!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}}
</style>
<style>
:root{--accent:#f4c430;--accent-blue:#60a9e6;--surface:rgba(35,37,42,.82);--surface-strong:#282b31;--border-soft:rgba(255,255,255,.09)}
body{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;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:10px;border:1px solid var(--border-soft);border-radius:10px;background:rgba(35,37,42,.72);box-shadow:0 10px 28px rgba(0,0,0,.14);gap:9px}.toolbar input,.toolbar select{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)}
.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)}
@media(max-width:760px){header{padding:0 12px}.head-sub{display:none}main{padding:12px}.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}}
</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}
.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);markDownloading(data.items)};
</script>