From cb41f0aee30930a79d66f872346f9a6cceb262cc Mon Sep 17 00:00:00 2001 From: Brahim Hadriche Date: Tue, 24 Oct 2023 11:54:07 -0400 Subject: [PATCH 1/5] Message for no bookmarks --- .../src/lib/Screens/BookmarksScreen.svelte | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/playlet-web/src/lib/Screens/BookmarksScreen.svelte b/playlet-web/src/lib/Screens/BookmarksScreen.svelte index e8af7ad8..b66d06d4 100644 --- a/playlet-web/src/lib/Screens/BookmarksScreen.svelte +++ b/playlet-web/src/lib/Screens/BookmarksScreen.svelte @@ -6,7 +6,21 @@
- {#each $bookmarksStore as feed} - - {/each} + {#if $bookmarksStore.length === 0} +
+
No Bookmarks
+
+ You currently have no bookmarks.
+ To add bookmarks, select a video, playlist or channel, and add a bookmark.
+ Please note that Bookmarks is an experimental feature. +
+
+ {:else} + {#each $bookmarksStore as feed} + + {/each} + {/if}
From 3969cfd8ddb55a0cd509b79b347fc92582a43dbb Mon Sep 17 00:00:00 2001 From: Brahim Hadriche Date: Tue, 24 Oct 2023 12:59:02 -0400 Subject: [PATCH 2/5] feed lazy loading --- .../src/lib/Screens/Home/VideoListRow.svelte | 81 ++++++++++++------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/playlet-web/src/lib/Screens/Home/VideoListRow.svelte b/playlet-web/src/lib/Screens/Home/VideoListRow.svelte index 151f9e57..a4b5f1b1 100644 --- a/playlet-web/src/lib/Screens/Home/VideoListRow.svelte +++ b/playlet-web/src/lib/Screens/Home/VideoListRow.svelte @@ -6,7 +6,7 @@ import ChannelCell from "./ChannelCell.svelte"; export let feed: any = undefined; - export let videos = undefined; + export let videos = []; enum FeedLoadState { None, @@ -22,6 +22,7 @@ let itemWidths = []; let carouselElement; + let carouselElementIntersecting = false; let scrollStart = 0; let scrollEnd = 0; @@ -30,8 +31,23 @@ const videoItemWidth = 320 + 16 * 2; const channelItemWidth = 240 + 16 * 2; + const intersectionObserver = new IntersectionObserver( + function (entries) { + carouselElementIntersecting = entries[0].isIntersecting; + if (carouselElementIntersecting) { + loadRow(); + } + }, + { threshold: [0] } + ); + $: { - if (carouselElement && itemWidths && itemWidths.length) { + if ( + carouselElement && + carouselElementIntersecting && + itemWidths && + itemWidths.length + ) { recalculateVisibileCells(); } } @@ -42,6 +58,12 @@ } } + $: { + if (carouselElement) { + intersectionObserver.observe(carouselElement); + } + } + let invidiousApi = new InvidiousApi(); playletStateStore.subscribe((value) => { @@ -61,6 +83,14 @@ return; } + if (!carouselElementIntersecting) { + return; + } + + if (scrollEnd < videos.length - 3) { + return; + } + if ( feedLoadState === FeedLoadState.Loading || feedLoadState === FeedLoadState.Loaded @@ -191,29 +221,26 @@ } -{#if videos} -
- {feed.title} -
- From c6bd6dfcf04568fecd950aeb5b01a2705e0454cb Mon Sep 17 00:00:00 2001 From: Brahim Hadriche Date: Tue, 24 Oct 2023 13:33:58 -0400 Subject: [PATCH 3/5] cached fetch --- playlet-web/src/lib/Api/InvidiousApi.ts | 111 ++++++++++++++++-------- 1 file changed, 77 insertions(+), 34 deletions(-) diff --git a/playlet-web/src/lib/Api/InvidiousApi.ts b/playlet-web/src/lib/Api/InvidiousApi.ts index 7f799c77..9cf1df3b 100644 --- a/playlet-web/src/lib/Api/InvidiousApi.ts +++ b/playlet-web/src/lib/Api/InvidiousApi.ts @@ -83,7 +83,6 @@ export class InvidiousApi { } public async makeRequest(feedSource: any) { - // TODO:P0 implement localStorage caching if (!feedSource || !this.instance || !this.endpoints) { return null; } @@ -95,7 +94,6 @@ export class InvidiousApi { let url = this.instance + endpoint.url let queryParams = {} - let headers = {} if (endpoint.authenticated) { // Authenticated requests on the web app would be blocked by CORS, so we use the Playlet API as a proxy @@ -137,66 +135,66 @@ export class InvidiousApi { } url = this.makeUrl(url, queryParams); - const response = await fetch(url, { headers: headers }); + + let cacheSeconds = undefined + if (feedSource.cacheSeconds !== undefined) { + cacheSeconds = feedSource.cacheSeconds + } else if (endpoint.cacheSeconds !== undefined) { + cacheSeconds = endpoint.cacheSeconds + } + + const responseJson = await this.cachedFetch(url, cacheSeconds); let responseHandler = endpoint.responseHandler !== undefined ? this.responseHandlers[endpoint.responseHandler] : this.responseHandlers["DefaultHandler"]; if (!responseHandler) { return null; } - return await responseHandler(feedSource, response); + return await responseHandler(feedSource, responseJson); } - private async DefaultHandler(feedSource, response) { - const items = await response.json(); - return { items }; + private async DefaultHandler(feedSource, responseJson) { + return { items: responseJson }; } - private async PlaylistHandler(feedSource, response) { - const json = await response.json(); + private async PlaylistHandler(feedSource, responseJson) { return { - items: json.videos, + items: responseJson.videos, }; } - private async VideoInfoHandler(feedSource, response) { - const info = await response.json(); - info.type = "video"; - return { items: [info] }; + private async VideoInfoHandler(feedSource, responseJson) { + responseJson.type = "video"; + return { items: [responseJson] }; } - private async ChannelInfoHandler(feedSource, response) { - const info = await response.json(); - info.type = "channel"; - return { items: [info] }; + private async ChannelInfoHandler(feedSource, responseJson) { + responseJson.type = "channel"; + return { items: [responseJson] }; } - private async PlaylistInfoHandler(feedSource, response) { - const info = await response.json(); - info.type = "playlist"; - return { items: [info] }; + private async PlaylistInfoHandler(feedSource, responseJson) { + responseJson.type = "playlist"; + return { items: [responseJson] }; } - private async ChannelVideosHandler(feedSource, response) { - const json = await response.json(); + private async ChannelVideosHandler(feedSource, responseJson) { return { - items: json.videos, - continuation: json.continuation + items: responseJson.videos, + continuation: responseJson.continuation }; } - private async ChannelPlaylistsHandler(feedSource, response) { - const json = await response.json(); + private async ChannelPlaylistsHandler(feedSource, responseJson) { return { - items: json.playlists, - continuation: json.continuation + items: responseJson.playlists, + continuation: responseJson.continuation }; } - private async ChannelRelatedChannelsHandler(feedSource, response) { - const json = await response.json(); + private async ChannelRelatedChannelsHandler(feedSource, responseJson) { return { - items: json.relatedChannels, - continuation: json.continuation + items: responseJson.relatedChannels, + continuation: responseJson.continuation }; } @@ -212,4 +210,49 @@ export class InvidiousApi { encodedUrl.search = mergedParams.toString(); return encodedUrl.toString(); } + + private async cachedFetch(url: string, cacheSeconds?: number) { + if (!cacheSeconds) { + const response = await fetch(url); + return await response.json(); + } + + const cache = this.getCache(url, cacheSeconds); + if (cache) { + return cache; + } + + const response = await fetch(url); + const data = await response.json(); + this.setCache(url, data); + return data; + } + + private getCache(url: string, cacheSeconds: number) { + const cache = localStorage.getItem(url); + if (!cache) { + return null; + } + + try { + const cacheData = JSON.parse(cache); + if (cacheData.timestamp + cacheSeconds * 1000 < Date.now()) { + return null; + } + console.log(`Cache hit for ${url}`); + return cacheData.data; + } catch (error) { + console.error(error); + return null; + } + } + + private setCache(url: string, data: any) { + const cacheData = { + timestamp: Date.now(), + data + }; + + localStorage.setItem(url, JSON.stringify(cacheData)); + } } From 0dc629c8efbbcafdb3c6140cda6c243b27a6906d Mon Sep 17 00:00:00 2001 From: Brahim Hadriche Date: Tue, 24 Oct 2023 13:35:44 -0400 Subject: [PATCH 4/5] Versioned cache --- playlet-web/src/lib/Api/InvidiousApi.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/playlet-web/src/lib/Api/InvidiousApi.ts b/playlet-web/src/lib/Api/InvidiousApi.ts index 9cf1df3b..901a5ecc 100644 --- a/playlet-web/src/lib/Api/InvidiousApi.ts +++ b/playlet-web/src/lib/Api/InvidiousApi.ts @@ -249,6 +249,7 @@ export class InvidiousApi { private setCache(url: string, data: any) { const cacheData = { + __version: 1, timestamp: Date.now(), data }; From 8ab143c7cbd12f10a256b59e8cfa5d39860f7d35 Mon Sep 17 00:00:00 2001 From: Brahim Hadriche Date: Tue, 24 Oct 2023 13:39:13 -0400 Subject: [PATCH 5/5] comment --- playlet-web/src/lib/Api/InvidiousApi.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/playlet-web/src/lib/Api/InvidiousApi.ts b/playlet-web/src/lib/Api/InvidiousApi.ts index 901a5ecc..990d6ee6 100644 --- a/playlet-web/src/lib/Api/InvidiousApi.ts +++ b/playlet-web/src/lib/Api/InvidiousApi.ts @@ -228,6 +228,7 @@ export class InvidiousApi { return data; } + // TODO:P2 use more appropriate cache storage private getCache(url: string, cacheSeconds: number) { const cache = localStorage.getItem(url); if (!cache) {