From 258063a5a14e2c6aa2f6bee9aff1997d360b43dd Mon Sep 17 00:00:00 2001 From: WardPearce Date: Sat, 21 Sep 2024 01:10:48 +1200 Subject: [PATCH] Improved load times on android & desktop --- materialious/android/app/build.gradle | 4 +- materialious/electron/package.json | 2 +- materialious/package.json | 2 +- materialious/src/lib/Api/index.ts | 129 ++++++++++-------- materialious/src/lib/Thumbnail.svelte | 4 +- .../src/lib/patches/androidRequests.ts | 3 +- materialious/src/routes/(app)/+page.svelte | 2 +- .../src/routes/(app)/watch/[slug]/+page.ts | 8 +- update_versions.py | 2 +- 9 files changed, 86 insertions(+), 70 deletions(-) diff --git a/materialious/android/app/build.gradle b/materialious/android/app/build.gradle index 9f2fda21..14751195 100644 --- a/materialious/android/app/build.gradle +++ b/materialious/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "us.materialio.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 43 - versionName "1.5.2" + versionCode 44 + versionName "1.5.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/materialious/electron/package.json b/materialious/electron/package.json index 56d4e21a..bbb21784 100644 --- a/materialious/electron/package.json +++ b/materialious/electron/package.json @@ -1,6 +1,6 @@ { "name": "Materialious", - "version": "1.5.2", + "version": "1.5.3", "description": "Modern material design for Invidious.", "author": { "name": "Ward Pearce", diff --git a/materialious/package.json b/materialious/package.json index dce4e069..8cf5c93e 100644 --- a/materialious/package.json +++ b/materialious/package.json @@ -1,6 +1,6 @@ { "name": "materialious", - "version": "1.5.2", + "version": "1.5.3", "private": true, "scripts": { "dev": "vite dev", diff --git a/materialious/src/lib/Api/index.ts b/materialious/src/lib/Api/index.ts index 8373ebe3..32e8fb7f 100644 --- a/materialious/src/lib/Api/index.ts +++ b/materialious/src/lib/Api/index.ts @@ -60,18 +60,18 @@ export function buildAuthHeaders(): { headers: { Authorization: string; }; } { return { headers: { Authorization: `Bearer ${get(authStore)?.token}` } }; } -export async function getTrending(): Promise { - const resp = await fetchErrorHandle(await fetch(setRegion(buildPath('trending')))); +export async function getTrending(fetchOptions?: RequestInit): Promise { + const resp = await fetchErrorHandle(await fetch(setRegion(buildPath('trending')), fetchOptions)); return await resp.json(); } -export async function getPopular(): Promise { - const resp = await fetchErrorHandle(await fetch(buildPath('popular'))); +export async function getPopular(fetchOptions?: RequestInit): Promise { + const resp = await fetchErrorHandle(await fetch(buildPath('popular'), fetchOptions)); return await resp.json(); } -export async function getVideo(videoId: string, local: boolean = false): Promise { - const resp = await fetch(setRegion(buildPath(`videos/${videoId}?local=${local}`))); +export async function getVideo(videoId: string, local: boolean = false, fetchOptions?: RequestInit): Promise { + const resp = await fetch(setRegion(buildPath(`videos/${videoId}?local=${local}`)), fetchOptions); if (!resp.ok && Capacitor.isNativePlatform() && get(playerYouTubeJsFallback)) { return await patchYoutubeJs(videoId); } else { @@ -80,9 +80,9 @@ export async function getVideo(videoId: string, local: boolean = false): Promise return await resp.json(); } -export async function getDislikes(videoId: string): Promise { +export async function getDislikes(videoId: string, fetchOptions?: RequestInit): Promise { const resp = await fetchErrorHandle( - await fetch(`${get(returnYTDislikesInstanceStore)}/votes?videoId=${videoId}`) + await fetch(`${get(returnYTDislikesInstanceStore)}/votes?videoId=${videoId}`, fetchOptions) ); return await resp.json(); } @@ -93,7 +93,8 @@ export async function getComments( sort_by?: 'top' | 'new'; source?: 'youtube' | 'reddit'; continuation?: string; - } + }, + fetchOptions?: RequestInit ): Promise { if (typeof parameters.sort_by === 'undefined') { parameters.sort_by = 'top'; @@ -105,12 +106,12 @@ export async function getComments( const path = buildPath(`comments/${videoId}`); path.search = new URLSearchParams(parameters).toString(); - const resp = await fetchErrorHandle(await fetch(path)); + const resp = await fetchErrorHandle(await fetch(path, fetchOptions)); return await resp.json(); } -export async function getChannel(channelId: string): Promise { - const resp = await fetchErrorHandle(await fetch(buildPath(`channels/${channelId}`))); +export async function getChannel(channelId: string, fetchOptions?: RequestInit): Promise { + const resp = await fetchErrorHandle(await fetch(buildPath(`channels/${channelId}`), fetchOptions)); return await resp.json(); } @@ -119,7 +120,8 @@ export async function getChannelContent( parameters: { type?: 'videos' | 'playlists' | 'streams' | 'shorts'; continuation?: string; - } + }, + fetchOptions?: RequestInit ): Promise { if (typeof parameters.type === 'undefined') parameters.type = 'videos'; @@ -128,14 +130,14 @@ export async function getChannelContent( if (typeof parameters.continuation !== 'undefined') url.searchParams.set('continuation', parameters.continuation); - const resp = await fetchErrorHandle(await fetch(url.toString())); + const resp = await fetchErrorHandle(await fetch(url.toString(), fetchOptions)); return await resp.json(); } -export async function getSearchSuggestions(search: string): Promise { +export async function getSearchSuggestions(search: string, fetchOptions?: RequestInit): Promise { const path = buildPath('search/suggestions'); path.search = new URLSearchParams({ q: search }).toString(); - const resp = await fetchErrorHandle(await fetch(path)); + const resp = await fetchErrorHandle(await fetch(path, fetchOptions)); return await resp.json(); } @@ -145,7 +147,8 @@ export async function getSearch( sort_by?: 'relevance' | 'rating' | 'upload_date' | 'view_count'; type?: 'video' | 'playlist' | 'channel' | 'all'; page?: string; - } + }, + fetchOptions?: RequestInit ): Promise<(Channel | Video | Playlist)[]> { if (typeof options.sort_by === 'undefined') { options.sort_by = 'relevance'; @@ -161,67 +164,69 @@ export async function getSearch( const path = buildPath('search'); path.search = new URLSearchParams({ ...options, q: search }).toString(); - const resp = await fetchErrorHandle(await fetch(setRegion(path))); + const resp = await fetchErrorHandle(await fetch(setRegion(path), fetchOptions)); return await resp.json(); } -export async function getFeed(maxResults: number, page: number): Promise { +export async function getFeed(maxResults: number, page: number, fetchOptions: RequestInit = {}): Promise { const path = buildPath('auth/feed'); path.search = new URLSearchParams({ max_results: maxResults.toString(), page: page.toString() }).toString(); - const resp = await fetchErrorHandle(await fetch(path, buildAuthHeaders())); + const resp = await fetchErrorHandle(await fetch(path, { ...buildAuthHeaders(), ...fetchOptions })); return await resp.json(); } -export async function getSubscriptions(): Promise { +export async function getSubscriptions(fetchOptions: RequestInit = {}): Promise { const resp = await fetchErrorHandle( - await fetch(buildPath('auth/subscriptions'), buildAuthHeaders()) + await fetch(buildPath('auth/subscriptions'), { ...buildAuthHeaders(), ...fetchOptions }) ); return await resp.json(); } -export async function amSubscribed(authorId: string): Promise { +export async function amSubscribed(authorId: string, fetchOptions: RequestInit = {}): Promise { if (!get(authStore)) return false; try { - const subscriptions = (await getSubscriptions()).filter((sub) => sub.authorId === authorId); + const subscriptions = (await getSubscriptions(fetchOptions)).filter((sub) => sub.authorId === authorId); return subscriptions.length === 1; } catch { return false; } } -export async function postSubscribe(authorId: string) { +export async function postSubscribe(authorId: string, fetchOptions: RequestInit = {}) { await fetchErrorHandle( await fetch(buildPath(`auth/subscriptions/${authorId}`), { method: 'POST', - ...buildAuthHeaders() + ...buildAuthHeaders(), + ...fetchOptions }) ); } -export async function deleteUnsubscribe(authorId: string) { +export async function deleteUnsubscribe(authorId: string, fetchOptions: RequestInit = {}) { await fetchErrorHandle( await fetch(buildPath(`auth/subscriptions/${authorId}`), { method: 'DELETE', - ...buildAuthHeaders() + ...buildAuthHeaders(), + ...fetchOptions }) ); } -export async function getHistory(page: number = 1, maxResults: number = 20): Promise { +export async function getHistory(page: number = 1, maxResults: number = 20, fetchOptions: RequestInit = {}): Promise { const resp = await fetchErrorHandle( await fetch( buildPath(`auth/history?page=${page}&max_results=${maxResults}`), - buildAuthHeaders() + { ...buildAuthHeaders(), ...fetchOptions }, ) ); return await resp.json(); } -export async function deleteHistory(videoId: string | undefined = undefined) { +export async function deleteHistory(videoId: string | undefined = undefined, fetchOptions: RequestInit = {}) { let url = '/api/v1/auth/history'; if (typeof videoId !== 'undefined') { url += `/${videoId}`; @@ -230,34 +235,36 @@ export async function deleteHistory(videoId: string | undefined = undefined) { await fetchErrorHandle( await fetch(buildPath(url), { method: 'DELETE', - ...buildAuthHeaders() + ...buildAuthHeaders(), + ...fetchOptions }) ); } -export async function postHistory(videoId: string) { +export async function postHistory(videoId: string, fetchOptions: RequestInit = {}) { await fetchErrorHandle( await fetch(buildPath(`auth/history/${videoId}`), { method: 'POST', - ...buildAuthHeaders() + ...buildAuthHeaders(), + ...fetchOptions }) ); } -export async function getPlaylist(playlistId: string, page: number = 1): Promise { +export async function getPlaylist(playlistId: string, page: number = 1, fetchOptions: RequestInit = {}): Promise { let resp; if (get(authStore)) { - resp = await fetch(buildPath(`auth/playlists/${playlistId}?page=${page}`), buildAuthHeaders()); + resp = await fetch(buildPath(`auth/playlists/${playlistId}?page=${page}`), { ...buildAuthHeaders(), ...fetchOptions }); } else { - resp = await fetch(buildPath(`playlists/${playlistId}?page=${page}`)); + resp = await fetch(buildPath(`playlists/${playlistId}?page=${page}`), fetchOptions); } await fetchErrorHandle(resp); return await resp.json(); } -export async function getPersonalPlaylists(): Promise { - const resp = await fetchErrorHandle(await fetch(buildPath('auth/playlists'), buildAuthHeaders())); +export async function getPersonalPlaylists(fetchOptions: RequestInit = {}): Promise { + const resp = await fetchErrorHandle(await fetch(buildPath('auth/playlists'), { ...buildAuthHeaders(), ...fetchOptions })); return await resp.json(); } @@ -272,7 +279,8 @@ export async function deletePersonalPlaylist(playlistId: string) { export async function postPersonalPlaylist( title: string, - privacy: 'public' | 'private' | 'unlisted' + privacy: 'public' | 'private' | 'unlisted', + fetchOptions: RequestInit = {} ) { const headers: Record> = buildAuthHeaders(); headers['headers']['Content-type'] = 'application/json'; @@ -284,12 +292,13 @@ export async function postPersonalPlaylist( title: title, privacy: privacy }), - ...headers + ...headers, + ...fetchOptions }) ); } -export async function addPlaylistVideo(playlistId: string, videoId: string) { +export async function addPlaylistVideo(playlistId: string, videoId: string, fetchOptions: RequestInit = {}) { const headers: Record> = buildAuthHeaders(); headers['headers']['Content-type'] = 'application/json'; @@ -299,48 +308,51 @@ export async function addPlaylistVideo(playlistId: string, videoId: string) { body: JSON.stringify({ videoId: videoId }), - ...headers + ...headers, + ...fetchOptions }) ); } -export async function removePlaylistVideo(playlistId: string, indexId: string) { +export async function removePlaylistVideo(playlistId: string, indexId: string, fetchOptions: RequestInit = {}) { await fetchErrorHandle( await fetch(buildPath(`auth/playlists/${playlistId}/videos/${indexId}`), { method: 'DELETE', - ...buildAuthHeaders() + ...buildAuthHeaders(), + ...fetchOptions }) ); } -export async function getDeArrow(videoId: string): Promise { +export async function getDeArrow(videoId: string, fetchOptions?: RequestInit): Promise { const resp = await fetchErrorHandle( - await fetch(`${get(deArrowInstanceStore)}/api/branding?videoID=${videoId}`) + await fetch(`${get(deArrowInstanceStore)}/api/branding?videoID=${videoId}`, fetchOptions) ); return await resp.json(); } -export async function getThumbnail(videoId: string, time: number): Promise { +export async function getThumbnail(videoId: string, time: number, fetchOptions?: RequestInit): Promise { const resp = await fetchErrorHandle( await fetch( - `${get(deArrowThumbnailInstanceStore)}/api/v1/getThumbnail?videoID=${videoId}&time=${time}` + `${get(deArrowThumbnailInstanceStore)}/api/v1/getThumbnail?videoID=${videoId}&time=${time}`, + fetchOptions ) ); return URL.createObjectURL(await resp.blob()); } -export async function getVideoProgress(videoId: string): Promise { +export async function getVideoProgress(videoId: string, fetchOptions: RequestInit = {}): Promise { const resp = await fetchErrorHandle( await fetch( `${get(synciousInstanceStore)}/video/${encodeURIComponent(videoId)}`, - buildAuthHeaders() + { ...buildAuthHeaders(), ...fetchOptions } ) ); return resp.json(); } -export async function saveVideoProgress(videoId: string, time: number) { +export async function saveVideoProgress(videoId: string, time: number, fetchOptions: RequestInit = {}) { const headers: Record> = buildAuthHeaders(); headers['headers']['Content-type'] = 'application/json'; @@ -350,25 +362,28 @@ export async function saveVideoProgress(videoId: string, time: number) { method: 'POST', body: JSON.stringify({ time: time - }) + }), + ...fetchOptions }) ); } -export async function deleteVideoProgress(videoId: string) { +export async function deleteVideoProgress(videoId: string, fetchOptions: RequestInit = {}) { await fetchErrorHandle( await fetch(`${get(synciousInstanceStore)}/video/${videoId}`, { method: 'DELETE', - ...buildAuthHeaders() + ...buildAuthHeaders(), + ...fetchOptions }) ); } -export async function deleteAllVideoProgress() { +export async function deleteAllVideoProgress(fetchOptions: RequestInit = {}) { await fetchErrorHandle( await fetch(`${get(synciousInstanceStore)}/videos`, { method: 'DELETE', - ...buildAuthHeaders() + ...buildAuthHeaders(), + ...fetchOptions }) ); } diff --git a/materialious/src/lib/Thumbnail.svelte b/materialious/src/lib/Thumbnail.svelte index 85b792d4..131e8ca0 100644 --- a/materialious/src/lib/Thumbnail.svelte +++ b/materialious/src/lib/Thumbnail.svelte @@ -84,7 +84,7 @@ } async function loadAuthor() { - const channel = await getChannel(video.authorId); + const channel = await getChannel(video.authorId, { priority: 'low' }); const img = new Image(); img.src = proxyGoogleImage(getBestThumbnail(channel.authorThumbnails, 75, 75)); @@ -197,7 +197,7 @@ if (get(synciousStore) && get(synciousInstanceStore) && get(authStore)) { try { - progress = (await getVideoProgress(video.videoId))[0].time.toString(); + progress = (await getVideoProgress(video.videoId, { priority: 'low' }))[0].time.toString(); } catch {} } }); diff --git a/materialious/src/lib/patches/androidRequests.ts b/materialious/src/lib/patches/androidRequests.ts index 2c6ae055..155a240e 100644 --- a/materialious/src/lib/patches/androidRequests.ts +++ b/materialious/src/lib/patches/androidRequests.ts @@ -1,6 +1,7 @@ import { goto } from "$app/navigation"; import { Capacitor } from "@capacitor/core"; + if (Capacitor.getPlatform() === 'android') { const originalFetch = window.fetch; @@ -13,7 +14,7 @@ if (Capacitor.getPlatform() === 'android') { typeof requestInput === 'string' ? requestInput : requestInput.toString(); // Check if the URL is already proxied, to avoid double proxying - if (!requestUrl.startsWith(corsAnywhereProxyUrl)) { + if (!requestUrl.startsWith(corsAnywhereProxyUrl) && !requestUrl.startsWith('/')) { requestInput = corsAnywhereProxyUrl + requestUrl; } diff --git a/materialious/src/routes/(app)/+page.svelte b/materialious/src/routes/(app)/+page.svelte index 093f5af1..d78f3d8b 100644 --- a/materialious/src/routes/(app)/+page.svelte +++ b/materialious/src/routes/(app)/+page.svelte @@ -11,7 +11,7 @@
{:else} diff --git a/materialious/src/routes/(app)/watch/[slug]/+page.ts b/materialious/src/routes/(app)/watch/[slug]/+page.ts index 035747af..900e22e7 100644 --- a/materialious/src/routes/(app)/watch/[slug]/+page.ts +++ b/materialious/src/routes/(app)/watch/[slug]/+page.ts @@ -20,7 +20,7 @@ import { get } from 'svelte/store'; export async function load({ params, url }) { let video; try { - video = await getVideo(params.slug, get(playerProxyVideosStore)); + video = await getVideo(params.slug, get(playerProxyVideosStore), { priority: "high" }); } catch (errorMessage: any) { error(500, errorMessage); } @@ -28,7 +28,7 @@ export async function load({ params, url }) { let personalPlaylists; if (get(authStore)) { postHistory(video.videoId); - personalPlaylists = getPersonalPlaylists(); + personalPlaylists = getPersonalPlaylists({ priority: 'low' }); } else { personalPlaylists = null; } @@ -37,7 +37,7 @@ export async function load({ params, url }) { try { comments = video.liveNow ? null - : getComments(params.slug, { sort_by: 'top', source: 'youtube' }); + : getComments(params.slug, { sort_by: 'top', source: 'youtube' }, { priority: "low" }); } catch { comments = null; } @@ -46,7 +46,7 @@ export async function load({ params, url }) { const returnYTDislikesInstance = get(returnYTDislikesInstanceStore); if (returnYTDislikesInstance && returnYTDislikesInstance !== '') { try { - returnYTDislikes = get(returnYtDislikesStore) ? getDislikes(params.slug) : null; + returnYTDislikes = get(returnYtDislikesStore) ? getDislikes(params.slug, { priority: "low" }) : null; } catch { } } diff --git a/update_versions.py b/update_versions.py index 1507f096..093cc790 100644 --- a/update_versions.py +++ b/update_versions.py @@ -5,7 +5,7 @@ import json import os import re -LATEST_VERSION = "1.5.2" +LATEST_VERSION = "1.5.3" WORKING_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "materialious") ROOT_PACKAGE = os.path.join(WORKING_DIR, "package.json")