From 500cde9e1334e04b29f13c81a64db763f1391844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Polakovi=C4=8D?= Date: Mon, 22 Mar 2021 14:28:42 +0100 Subject: [PATCH 1/3] Handle exceeded quote errors more graciously. --- src/js/components/VideoDownloader.js | 30 ++++++++++++++++--- .../VideoDownloader/StorageManager.module.js | 25 ++++++++++++++-- src/js/modules/IDBConnection.module.js | 2 +- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/js/components/VideoDownloader.js b/src/js/components/VideoDownloader.js index 1c2eedb..eee88ba 100644 --- a/src/js/components/VideoDownloader.js +++ b/src/js/components/VideoDownloader.js @@ -312,12 +312,21 @@ export default class extends HTMLElement { * Saves assets to the specified cache using Cache API. * * @param {string[]} urls Array of URLs to be saved to the cache. - * - * @returns {Promise} Resolves when the assets are stored in the cache. */ async saveToCache(urls) { - const cache = await caches.open(this.internal.cacheName); - return cache.addAll(urls); + try { + const cache = await caches.open(this.internal.cacheName); + await cache.addAll(urls); + } catch (error) { + if (error.name === 'QuotaExceededError') { + /** + * @todo Display an alert or snackbar warning instead of console. + */ + + // eslint-disable-next-line no-console + console.log('[VideoDownloader] Quota exceeded. Unable to cache video assets.'); + } + } } /** @@ -369,6 +378,19 @@ export default class extends HTMLElement { this.storageManager.onprogress = (progress) => { this.progress = progress; }; + this.storageManager.onerror = (error) => { + if (this.downloading && error.name === 'QuotaExceededError') { + /** + * Allow some time for any remaining pending transactions to error out, too, + * before we remove the partially removed video. + */ + setTimeout(() => { + this.cancel(); + this.removeFromIDB(); + this.state = 'ready'; + }, 1000); + } + }; this.storageManager.ondone = () => { this.progress = 100; this.downloading = false; diff --git a/src/js/components/VideoDownloader/StorageManager.module.js b/src/js/components/VideoDownloader/StorageManager.module.js index 71cf744..2c70c45 100644 --- a/src/js/components/VideoDownloader/StorageManager.module.js +++ b/src/js/components/VideoDownloader/StorageManager.module.js @@ -10,6 +10,7 @@ export default class { constructor(videoDownloader) { this.done = false; + this.onerror = () => {}; this.onprogress = () => {}; this.ondone = () => {}; @@ -49,23 +50,41 @@ export default class { done: isDone, videoId: this.internal.videoDownloader.getId(), }; + const txAbortHandler = (e) => { + const { error } = e.target; + if (error.name === 'QuotaExceededError') { + this.cancel(); + + /** + * @todo Display an alert or snackbar warning instead of console. + */ + + // eslint-disable-next-line no-console + console.log(`[StorageManager] Quota exceeded. Unable to store more data in '${e.target.objectStoreNames?.[0]}' store.`); + } + this.onerror(error); + }; const metaWritePromise = new Promise((resolve, reject) => { - const metaPutOperation = db.meta.put(videoMeta); + const [transaction, metaPutOperation] = db.meta.put(videoMeta); + + transaction.onabort = txAbortHandler; metaPutOperation.onsuccess = resolve; metaPutOperation.onerror = reject; }); const dataWritePromise = new Promise((resolve, reject) => { - const dataPutOperation = db.data.put(fileChunk); + const [transaction, dataPutOperation] = db.data.put(fileChunk); + transaction.onabort = txAbortHandler; dataPutOperation.onsuccess = resolve; dataPutOperation.onerror = reject; }); const fileWritePromise = new Promise((resolve, reject) => { - const dataPutOperation = db.file.put(fileMeta); + const [transaction, dataPutOperation] = db.file.put(fileMeta); + transaction.onabort = txAbortHandler; dataPutOperation.onsuccess = resolve; dataPutOperation.onerror = reject; }); diff --git a/src/js/modules/IDBConnection.module.js b/src/js/modules/IDBConnection.module.js index 0e05640..d775f6c 100644 --- a/src/js/modules/IDBConnection.module.js +++ b/src/js/modules/IDBConnection.module.js @@ -148,7 +148,7 @@ export default () => { const transaction = abstractedIDB.db.transaction([storeName], 'readwrite'); const store = transaction.objectStore(storeName); - return store.put(data); + return [transaction, store.put(data)]; }, }; } From 8b8860c76ad2cecba78a81082032f92441152e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Polakovi=C4=8D?= Date: Tue, 23 Mar 2021 10:09:33 +0100 Subject: [PATCH 2/3] Add a m3u8 fallback source of streaming video for iOS. --- public/api.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/api.json b/public/api.json index 38ba27f..5243198 100644 --- a/public/api.json +++ b/public/api.json @@ -10,6 +10,10 @@ { "src": "https://storage.googleapis.com/wdm-assets/videos/http-203/background-fetch/manifest.mpd", "type": "application/dash+xml" + }, + { + "src": "https://storage.googleapis.com/wdm-assets/videos/http-203/background-fetch/master.m3u8", + "type": "application/x-mpegURL" } ], "thumbnail": [ From 21a2b2c7649e2227d37daedb276ccd57a44ea296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20Polakovi=C4=8D?= Date: Tue, 23 Mar 2021 11:17:35 +0100 Subject: [PATCH 3/3] Bump asset version. --- src/js/constants.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/js/constants.js b/src/js/constants.js index d849faa..54ce174 100644 --- a/src/js/constants.js +++ b/src/js/constants.js @@ -7,7 +7,7 @@ * There may be adjacent caches used for other purposes and we * want to let the SW know which caches it should purge on upgrade. */ -export const SW_CACHE_NAME = 'static-assets-v1.0.0-alpha1'; +export const SW_CACHE_NAME = 'static-assets-v1.0.0-alpha2'; export const SW_CACHE_FORMAT = /^static-assets-v[a-z0-9.-]+$/; /**