diff --git a/.gitignore b/.gitignore index c43fb26..3980cdb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ config.json -.DS_Store -GridTV Beta.sublime-project \ No newline at end of file +.DS_Store \ No newline at end of file diff --git a/assets/apple-touch-icon.png b/assets/apple-touch-icon.png new file mode 100644 index 0000000..e17f1e0 Binary files /dev/null and b/assets/apple-touch-icon.png differ diff --git a/assets/favicon.png b/assets/favicon.png new file mode 100644 index 0000000..59c89d8 Binary files /dev/null and b/assets/favicon.png differ diff --git a/assets/icon-192.png b/assets/icon-192.png new file mode 100644 index 0000000..c2197a2 Binary files /dev/null and b/assets/icon-192.png differ diff --git a/assets/icon-512.png b/assets/icon-512.png new file mode 100644 index 0000000..5dc7085 Binary files /dev/null and b/assets/icon-512.png differ diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000..27d7178 Binary files /dev/null and b/assets/icon.png differ diff --git a/favicon.ico b/favicon.ico new file mode 100644 index 0000000..95ff786 Binary files /dev/null and b/favicon.ico differ diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..d1563ec --- /dev/null +++ b/manifest.json @@ -0,0 +1,24 @@ +{ + "name": "GridTV", + "short_name": "GridTV", + "description": "Self-hosted IPTV TV guide", + "start_url": "/", + "display": "standalone", + "background_color": "#0a0b0d", + "theme_color": "#0a0b0d", + "orientation": "any", + "icons": [ + { + "src": "assets/icon-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "assets/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + } + ] +} diff --git a/src/css/mobile.css b/src/css/mobile.css index d36bdfb..5391a63 100644 --- a/src/css/mobile.css +++ b/src/css/mobile.css @@ -189,3 +189,22 @@ padding: 8px 18px; cursor: pointer; } + +@media (max-width: 600px) { + html, body { height: 100%; overflow: hidden; } + body { overflow: hidden; } + + .topbar { + height: auto; + flex-wrap: wrap; + padding: 8px 12px; + gap: 8px; + } + .logo { font-size: 11px; } + .clock { font-size: 14px; } + + .grid-wrapper { display: none; } + .mobile-view { display: block; } + .tooltip { display: none !important; } + #navBtns { display: none !important; } + } diff --git a/src/css/topbar.css b/src/css/topbar.css index a314444..e33eff1 100644 --- a/src/css/topbar.css +++ b/src/css/topbar.css @@ -148,9 +148,7 @@ .theme-select:hover { border-color: var(--accent2); color: var(--accent2); } .theme-select option { background: #111; color: #eee; font-family: sans-serif; } - @media (max-width: 600px) { - html, body { height: 100%; overflow: hidden; } - body { overflow: hidden; } + body { overflow: hidden; } .topbar { height: auto; diff --git a/src/tpl/footer.php b/src/tpl/footer.php index 3962be4..f7d5605 100644 --- a/src/tpl/footer.php +++ b/src/tpl/footer.php @@ -9,5 +9,13 @@ foreach ($js_modules as $mod) { } ?> + + diff --git a/src/tpl/head.php b/src/tpl/head.php index 6ca2685..6e1ea29 100644 --- a/src/tpl/head.php +++ b/src/tpl/head.php @@ -4,10 +4,18 @@ GridTV — <?= $group_name ?> + + + + + + + + \n"; foreach ($css_files as $file) { echo file_get_contents($css_dir . $file . '.css') . "\n"; diff --git a/sw.js b/sw.js new file mode 100644 index 0000000..f5ea5fc --- /dev/null +++ b/sw.js @@ -0,0 +1,76 @@ +const CACHE_NAME = 'gridtv-v1'; + +// Ressources statiques à mettre en cache immédiatement +const STATIC_ASSETS = [ + '/', + '/index.php', + '/manifest.json', + '/assets/icon-192.png', + '/assets/icon-512.png', + 'https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Barlow+Condensed:wght@300;400;600;700&display=swap', + 'https://cdnjs.cloudflare.com/ajax/libs/hls.js/1.4.12/hls.min.js' +]; + +// Install : mise en cache des assets statiques +self.addEventListener('install', e => { + e.waitUntil( + caches.open(CACHE_NAME) + .then(cache => cache.addAll(STATIC_ASSETS)) + .then(() => self.skipWaiting()) + ); +}); + +// Activate : purger les anciens caches +self.addEventListener('activate', e => { + e.waitUntil( + caches.keys().then(keys => + Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k))) + ).then(() => self.clients.claim()) + ); +}); + +// Fetch : stratégie selon le type de ressource +self.addEventListener('fetch', e => { + const url = new URL(e.request.url); + + // Flux EPG/M3U et API GitHub : network only (pas de cache) + if ( + url.pathname.includes('xmltv') || + url.pathname.includes('.m3u') || + url.hostname === 'api.github.com' || + url.pathname.includes('proxy.php') + ) { + return; + } + + // Assets statiques : cache first + if ( + e.request.destination === 'style' || + e.request.destination === 'script' || + e.request.destination === 'font' || + e.request.destination === 'image' || + url.pathname.endsWith('.json') || + url.pathname.endsWith('.css') || + url.pathname.endsWith('.js') + ) { + e.respondWith( + caches.match(e.request).then(cached => cached || fetch(e.request).then(res => { + const clone = res.clone(); + caches.open(CACHE_NAME).then(cache => cache.put(e.request, clone)); + return res; + })) + ); + return; + } + + // Pages PHP (index.php) : network first, fallback cache + e.respondWith( + fetch(e.request) + .then(res => { + const clone = res.clone(); + caches.open(CACHE_NAME).then(cache => cache.put(e.request, clone)); + return res; + }) + .catch(() => caches.match(e.request)) + ); +});