Fix: Attempt to Improve UI-Login Cache (#63)

This commit is contained in:
Youwes09
2026-05-03 12:29:20 -05:00
parent 093b395cc1
commit 0d53e3f102
8 changed files with 58 additions and 42 deletions
+8 -12
View File
@@ -1,6 +1,7 @@
import { store } from "@store/state.svelte"; import { store } from "@store/state.svelte";
import { fetchAuthenticated, AuthRequiredError, uiAuth } from "../core/auth"; import { fetchAuthenticated, AuthRequiredError, uiAuth } from "../core/auth";
import { boot } from "@store/boot.svelte"; import { boot } from "@store/boot.svelte";
import { getBlobUrl } from "@core/cache/imageCache";
const DEFAULT_URL = "http://127.0.0.1:4567"; const DEFAULT_URL = "http://127.0.0.1:4567";
@@ -25,20 +26,15 @@ export function getServerUrl(): string {
export function plainThumbUrl(path: string): string { export function plainThumbUrl(path: string): string {
if (!path) return ""; if (!path) return "";
if (path.startsWith("http")) return path; if (path.startsWith("http")) return path;
return `${getServerUrl()}${path}`;
}
const base = `${getServerUrl()}${path}`; export async function resolveImageUrl(path: string): Promise<string> {
if (!path) return "";
const url = path.startsWith("http") ? path : `${getServerUrl()}${path}`;
const mode = store.settings.serverAuthMode ?? "NONE"; const mode = store.settings.serverAuthMode ?? "NONE";
if (mode === "NONE") return url;
if (mode === "UI_LOGIN") { return getBlobUrl(url);
const token = uiAuth.getToken();
if (token) {
const url = new URL(base);
url.searchParams.set("authorization", token);
return url.toString();
}
}
return base;
} }
export const thumbUrl = plainThumbUrl; export const thumbUrl = plainThumbUrl;
+7 -2
View File
@@ -56,7 +56,7 @@ function drain() {
active++; active++;
doFetch(entry.url) doFetch(entry.url)
.then(entry.resolve, entry.reject) .then(entry.resolve, entry.reject)
.finally(() => { inflight.delete(entry.url); active--; drain(); }); .finally(() => { active--; drain(); });
} }
} }
@@ -67,7 +67,12 @@ function scheduleDrain() {
} }
function enqueue(url: string, priority: number): Promise<string> { function enqueue(url: string, priority: number): Promise<string> {
const promise = new Promise<string>((resolve, reject) => { insertSorted({ url, priority, resolve, reject }); }); const promise = new Promise<string>((resolve, reject) => {
insertSorted({ url, priority, resolve, reject });
}).catch(err => {
inflight.delete(url);
return Promise.reject(err);
});
inflight.set(url, promise); inflight.set(url, promise);
scheduleDrain(); scheduleDrain();
return promise; return promise;
+10 -7
View File
@@ -1,5 +1,5 @@
import { gql, getServerUrl } from "@api/client"; import { gql, getServerUrl } from "@api/client";
import { getBlobUrl, preloadBlobUrls } from "@core/cache/imageCache"; import { getBlobUrl } from "@core/cache/imageCache";
import { dedupeRequest } from "@core/async/batchRequests"; import { dedupeRequest } from "@core/async/batchRequests";
import { FETCH_CHAPTER_PAGES } from "@api/mutations/chapters"; import { FETCH_CHAPTER_PAGES } from "@api/mutations/chapters";
@@ -11,8 +11,14 @@ const aspectCache = new Map<string, number>();
export function resolveUrl(url: string, useBlob: boolean, priority = 0): Promise<string> { export function resolveUrl(url: string, useBlob: boolean, priority = 0): Promise<string> {
if (!useBlob) return Promise.resolve(url); if (!useBlob) return Promise.resolve(url);
if (!resolvedUrlCache.has(url)) resolvedUrlCache.set(url, getBlobUrl(url, priority)); const cached = resolvedUrlCache.get(url);
return resolvedUrlCache.get(url)!; if (cached) return cached;
const p = getBlobUrl(url, priority).catch(err => {
resolvedUrlCache.delete(url);
return Promise.reject(err);
});
resolvedUrlCache.set(url, p);
return p;
} }
export function fetchPages( export function fetchPages(
@@ -30,10 +36,7 @@ export function fetchPages(
gql<{ fetchChapterPages: { pages: string[] } }>(FETCH_CHAPTER_PAGES, { chapterId }) gql<{ fetchChapterPages: { pages: string[] } }>(FETCH_CHAPTER_PAGES, { chapterId })
.then(d => { .then(d => {
const urls = d.fetchChapterPages.pages.map(p => p.startsWith("http") ? p : `${getServerUrl()}${p}`); const urls = d.fetchChapterPages.pages.map(p => p.startsWith("http") ? p : `${getServerUrl()}${p}`);
if (useBlob) { if (useBlob && urls[priorityPage]) getBlobUrl(urls[priorityPage], 999);
if (urls[priorityPage]) getBlobUrl(urls[priorityPage], urls.length + 999);
preloadBlobUrls(urls.filter((_, i) => i !== priorityPage), urls.length);
}
pageCache.set(chapterId, urls); pageCache.set(chapterId, urls);
return urls; return urls;
}) })
+13
View File
@@ -159,3 +159,16 @@ export function getTopSources<T extends { id: string }>(sources: T[]): T[] {
} }
return sources.slice(0, MAX_FRECENCY_SOURCES); return sources.slice(0, MAX_FRECENCY_SOURCES);
} }
export async function refreshMangaCache(mangaId: number, thumbnailUrl?: string): Promise<void> {
cache.clear(CACHE_KEYS.MANGA(mangaId));
cache.clear(CACHE_KEYS.CHAPTERS(mangaId));
cache.clear(CACHE_KEYS.LIBRARY);
cache.clear(CACHE_KEYS.ALL_MANGA);
if (thumbnailUrl) {
const { revokeBlobUrl, getBlobUrl } = await import("@core/cache/imageCache");
revokeBlobUrl(thumbnailUrl);
getBlobUrl(thumbnailUrl, 999).catch(() => {});
}
}
+2 -8
View File
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import { onMount, untrack } from "svelte"; import { onMount, untrack } from "svelte";
import { gql, thumbUrl } from "@api/client"; import { gql, resolveImageUrl } from "@api/client";
import { getBlobUrl } from "@core/cache/imageCache";
import { GET_CHAPTERS } from "@api/queries/chapters"; import { GET_CHAPTERS } from "@api/queries/chapters";
import { GET_LIBRARY } from "@api/queries/manga"; import { GET_LIBRARY } from "@api/queries/manga";
import { cache, CACHE_KEYS } from "@core/cache"; import { cache, CACHE_KEYS } from "@core/cache";
@@ -95,13 +94,8 @@
let heroThumb = $state(""); let heroThumb = $state("");
$effect(() => { $effect(() => {
const path = heroThumbSrc; const path = heroThumbSrc;
const mode = store.settings.serverAuthMode ?? "NONE";
if (!path) { heroThumb = ""; return; } if (!path) { heroThumb = ""; return; }
resolveImageUrl(path)
const needsBlob = mode === "BASIC_AUTH" || mode === "UI_LOGIN";
if (!needsBlob) { heroThumb = thumbUrl(path); return; }
getBlobUrl(thumbUrl(path))
.then(url => { heroThumb = url; }) .then(url => { heroThumb = url; })
.catch(() => { heroThumb = ""; }); .catch(() => { heroThumb = ""; });
}); });
+11 -3
View File
@@ -224,11 +224,19 @@
{#if style === "longstrip"} {#if style === "longstrip"}
{#each stripToRender as chunk} {#each stripToRender as chunk}
{#each chunk.urls as url, i} {#each chunk.urls as url, i}
{#await resolveUrl(url, chunk.urls.length - i)} {#if i < 8}
<img src="" alt="{chunk.chapterName} Page {i + 1}" data-local-page={i + 1} data-chapter={chunk.chapterId} data-total={chunk.urls.length} class="{imgCls}{store.settings.pageGap ? ' strip-gap' : ''}" loading={i < 5 ? "eager" : "lazy"} decoding="async" /> {#await resolveUrl(url, 8 - i)}
<img src="" alt="{chunk.chapterName} Page {i + 1}" data-local-page={i + 1} data-chapter={chunk.chapterId} data-total={chunk.urls.length} class="{imgCls}{store.settings.pageGap ? ' strip-gap' : ''}" loading="eager" decoding="async" />
{:then src} {:then src}
<img {src} alt="{chunk.chapterName} Page {i + 1}" data-local-page={i + 1} data-chapter={chunk.chapterId} data-total={chunk.urls.length} class="{imgCls}{store.settings.pageGap ? ' strip-gap' : ''}" loading={i < 5 ? "eager" : "lazy"} decoding="async" /> <img {src} alt="{chunk.chapterName} Page {i + 1}" data-local-page={i + 1} data-chapter={chunk.chapterId} data-total={chunk.urls.length} class="{imgCls}{store.settings.pageGap ? ' strip-gap' : ''}" loading="eager" decoding="async" />
{/await} {/await}
{:else}
{#await resolveUrl(url, 0)}
<img src="" alt="{chunk.chapterName} Page {i + 1}" data-local-page={i + 1} data-chapter={chunk.chapterId} data-total={chunk.urls.length} class="{imgCls}{store.settings.pageGap ? ' strip-gap' : ''}" loading="lazy" decoding="async" />
{:then src}
<img {src} alt="{chunk.chapterName} Page {i + 1}" data-local-page={i + 1} data-chapter={chunk.chapterId} data-total={chunk.urls.length} class="{imgCls}{store.settings.pageGap ? ' strip-gap' : ''}" loading="lazy" decoding="async" />
{/await}
{/if}
{/each} {/each}
{/each} {/each}
<div style="height:1px;flex-shrink:0"></div> <div style="height:1px;flex-shrink:0"></div>
+1 -1
View File
@@ -25,7 +25,7 @@
import ReaderPresetPanel from "./ReaderPresetPanel.svelte"; import ReaderPresetPanel from "./ReaderPresetPanel.svelte";
const win = getCurrentWindow(); const win = getCurrentWindow();
const useBlob = $derived((store.settings.serverAuthMode ?? "NONE") === "BASIC_AUTH"); const useBlob = $derived((store.settings.serverAuthMode ?? "NONE") !== "NONE");
const effectiveReaderSettings = $derived.by(() => { const effectiveReaderSettings = $derived.by(() => {
const mangaId = store.activeManga?.id; const mangaId = store.activeManga?.id;
+3 -6
View File
@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { thumbUrl, getServerUrl } from "@api/client"; import { plainThumbUrl, getServerUrl } from "@api/client";
import { store } from "@store/state.svelte"; import { store } from "@store/state.svelte";
import { getBlobUrl } from "@core/cache/imageCache"; import { getBlobUrl } from "@core/cache/imageCache";
@@ -23,10 +23,7 @@
[key: string]: any; [key: string]: any;
} = $props(); } = $props();
const isAuth = $derived( const isAuth = $derived((store.settings.serverAuthMode ?? "NONE") !== "NONE");
store.settings.serverAuthMode === "BASIC_AUTH" ||
store.settings.serverAuthMode === "UI_LOGIN"
);
let blobUrl = $state(""); let blobUrl = $state("");
let reqId = 0; let reqId = 0;
@@ -48,7 +45,7 @@
const resolved = $derived( const resolved = $derived(
isAuth isAuth
? (blobUrl || undefined) ? (blobUrl || undefined)
: (src ? thumbUrl(src) : undefined) : (src ? plainThumbUrl(src) : undefined)
); );
</script> </script>