self.addEventListener('install', e => {
    // init
});

self.addEventListener('activate', event => {
    event.waitUntil(self.clients.claim());
});

const CACHE_NAME = "v1";

self.addEventListener('fetch', function (event) {
	// SKIP cachecheck if it's the streaming path
	if (event.request.url.indexOf('/listen') !== -1 || event.request.url.indexOf('file://') !== -1) {
		// nothing to see here, carry on
	} else {
		event.respondWith(
			caches.match(event.request).then(function (response) {
				// Cache hit - return response
				if (response) {
					return response;
				}

				// IMPORTANT: Clone the request. A request is a stream and
				// can only be consumed once. Since we are consuming this
				// once by cache and once by the browser for fetch, we need
				// to clone the response.
				var fetchRequest = event.request.clone();

				return fetch(fetchRequest).then(
					function (response) {
						// Check if we received a valid response
						if (!response) {
							return response;
						}
						if (fetchRequest.url.indexOf('/data/') === -1 && fetchRequest.url.indexOf('lastfm-img2') === -1) {
							return response;
						}

						// IMPORTANT: Clone the response. A response is a stream
						// and because we want the browser to consume the response
						// as well as the cache consuming the response, we need
						// to clone it so we have two streams.
						var responseToCache = response.clone();

						caches.open(CACHE_NAME)
							.then(function (cache) {
								cache.put(event.request, responseToCache);
							});

						return response;
					}
				);
			})
		)
	}
});

self.addEventListener('install', e => {
    // init
});

self.addEventListener('activate', event => {
    event.waitUntil(self.clients.claim());
});

const CACHE_NAME = "v1";

self.addEventListener('fetch', function (event) {
	// SKIP cachecheck if it's the streaming path
	if (event.request.url.indexOf('/listen') !== -1 || event.request.url.indexOf('file://') !== -1) {
		// nothing to see here, carry on
	} else if (event.request.url.indexOf('music.json') !== -1) {
		// send back from the cache but always update the cache with the networks version.
		event.respondWith(
			caches.open(CACHE_NAME).then(function(cache) {
				return cache.match(event.request).then(function (response) {
					var fetchPromise = fetch(event.request).then(function(networkResponse) {
						cache.put(event.request, networkResponse.clone());
						return networkResponse;
					})
					return response || fetchPromise;
				})
			})
		)
	} else {
		event.respondWith(
			caches.match(event.request).then(function (response) {
				// Cache hit - return response
				if (response) {
					return response;
				}

				// IMPORTANT: Clone the request. A request is a stream and
				// can only be consumed once. Since we are consuming this
				// once by cache and once by the browser for fetch, we need
				// to clone the response.
				var fetchRequest = event.request.clone();

				return fetch(fetchRequest).then(
					function (response) {
						// Check if we received a valid response
						if (!response) {
							return response;
						}
						if (fetchRequest.url.indexOf('/data/') === -1 && fetchRequest.url.indexOf('lastfm-img2') === -1) {
							return response;
						}

						// IMPORTANT: Clone the response. A response is a stream
						// and because we want the browser to consume the response
						// as well as the cache consuming the response, we need
						// to clone it so we have two streams.
						var responseToCache = response.clone();

						caches.open(CACHE_NAME)
							.then(function (cache) {
								cache.put(event.request, responseToCache);
							});

						return response;
					}
				);
			})
		)
	}
});

Update music.json on startup but serving cached version.