diff --git a/static/manifest.json b/static/manifest.json new file mode 100644 index 0000000..4b8f2ac --- /dev/null +++ b/static/manifest.json @@ -0,0 +1,14 @@ +{ + "name": "Media Language Dashboard", + "short_name": "Media Dashboard", + "description": "Radarr, Sonarr und SABnzbd im Überblick", + "start_url": "/", + "scope": "/", + "display": "standalone", + "background_color": "#151619", + "theme_color": "#1f252d", + "orientation": "any", + "icons": [ + {"src": "/static/media-mark.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any maskable"} + ] +} diff --git a/static/sw.js b/static/sw.js new file mode 100644 index 0000000..c42edf5 --- /dev/null +++ b/static/sw.js @@ -0,0 +1,20 @@ +const CACHE_NAME = 'media-dashboard-shell-v1'; +const SHELL = ['/static/media-mark.svg', '/static/manifest.json']; + +self.addEventListener('install', event => { + event.waitUntil(caches.open(CACHE_NAME).then(cache => cache.addAll(SHELL))); + self.skipWaiting(); +}); + +self.addEventListener('activate', event => { + event.waitUntil(caches.keys().then(keys => Promise.all(keys.filter(key => key !== CACHE_NAME).map(key => caches.delete(key))))); + self.clients.claim(); +}); + +self.addEventListener('fetch', event => { + const request = event.request; + if (request.method !== 'GET') return; + const url = new URL(request.url); + if (url.pathname.startsWith('/api/')) return; + event.respondWith(fetch(request).catch(() => caches.match(request).then(response => response || caches.match('/static/manifest.json')))); +}); diff --git a/templates/index.html b/templates/index.html index 729fc69..7a0a657 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,11 +1,14 @@ -Language Dashboard +Language Dashboard
MEDIA
Language Dashboard
Audio- und Untertitelspuren aus den Mediendateien
@@ -37,6 +40,11 @@ function statusText(s){if(s.state==='running')return `Scan: ${s.done}/${s.total| 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()}; +