From 23247576bb83f97f9998f49a85d39180e14c1504 Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Thu, 3 Apr 2025 10:22:56 +0200 Subject: [PATCH] feat: caching covers images --- public/sw.js | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/public/sw.js b/public/sw.js index d41385b..17c8f78 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,5 +1,6 @@ const CACHE_NAME = "stripstream-cache-v1"; const BOOKS_CACHE_NAME = "stripstream-books"; +const COVERS_CACHE_NAME = "stripstream-covers"; const OFFLINE_PAGE = "/offline.html"; const STATIC_ASSETS = [ @@ -16,6 +17,7 @@ self.addEventListener("install", (event) => { Promise.all([ caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS)), caches.open(BOOKS_CACHE_NAME), + caches.open(COVERS_CACHE_NAME), ]) ); }); @@ -26,7 +28,9 @@ self.addEventListener("activate", (event) => { caches.keys().then((cacheNames) => { return Promise.all( cacheNames - .filter((name) => name !== CACHE_NAME && name !== BOOKS_CACHE_NAME) + .filter( + (name) => name !== CACHE_NAME && name !== BOOKS_CACHE_NAME && name !== COVERS_CACHE_NAME + ) .map((name) => caches.delete(name)) ); }) @@ -54,6 +58,69 @@ const isBookResource = (url) => { return url.includes("/api/v1/books/") && (url.includes("/pages") || url.includes("/thumbnail")); }; +// Fonction pour vérifier si c'est une image de couverture +const isCoverImage = (url) => { + const urlParams = new URLSearchParams(url.split("?")[1]); + const originalUrl = urlParams.get("url") || url; + + return ( + originalUrl.includes("/api/komga/images/") && + (originalUrl.includes("/series/") || originalUrl.includes("/books/")) && + originalUrl.includes("/thumbnail") + ); +}; + +// Stratégie de cache pour les images de couverture +const coverCacheStrategy = async (request) => { + const urlParams = new URLSearchParams(request.url.split("?")[1]); + const originalUrl = urlParams.get("url") || request.url; + const originalRequest = new Request(originalUrl, request); + + const cache = await caches.open(COVERS_CACHE_NAME); + const cachedResponse = await cache.match(originalRequest); + + if (cachedResponse) { + return cachedResponse; + } + + try { + const response = await fetch(originalRequest); + if (response.ok) { + await cache.put(originalRequest, response.clone()); + return response; + } + throw new Error("Network response was not ok"); + } catch (error) { + console.error("Error fetching cover image", error); + return new Response("", { + status: 404, + statusText: "Not Found", + headers: { + "Content-Type": "text/plain", + }, + }); + } +}; + +// Écouteur pour les messages du client +self.addEventListener("message", (event) => { + if (event.data && event.data.type === "CACHE_COVER") { + const { url } = event.data; + if (url && isCoverImage(url)) { + fetch(url) + .then(async (response) => { + if (response.ok) { + const cache = await caches.open(COVERS_CACHE_NAME); + await cache.put(url, response.clone()); + } + }) + .catch(() => { + // Ignorer les erreurs de mise en cache + }); + } + } +}); + self.addEventListener("fetch", (event) => { // Ignorer les requêtes non GET if (event.request.method !== "GET") return; @@ -61,6 +128,40 @@ self.addEventListener("fetch", (event) => { // Ignorer les ressources webpack if (isWebpackResource(event.request.url)) return; + // Gérer les requêtes d'images de couverture + if (event.request.url.includes("/api/v1/books/") && event.request.url.includes("/cover")) { + event.respondWith( + caches.match(event.request).then((response) => { + if (response) { + return response; + } + + return fetch(event.request) + .then((response) => { + if (!response.ok) { + return new Response(null, { status: 404 }); + } + + const responseToCache = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseToCache); + }); + + return response; + }) + .catch(() => { + return new Response(null, { status: 404 }); + }); + }) + ); + } + + // Pour les images de couverture + if (isCoverImage(event.request.url)) { + event.respondWith(coverCacheStrategy(event.request)); + return; + } + // Pour les ressources de livre if (isBookResource(event.request.url)) { event.respondWith(