mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 09:19:56 -05:00
Fix: Attempt to Improve UI-Login Cache (#63)
This commit is contained in:
+8
-12
@@ -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;
|
||||||
|
|||||||
Vendored
+7
-2
@@ -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;
|
||||||
|
|||||||
Vendored
+10
-7
@@ -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;
|
||||||
})
|
})
|
||||||
|
|||||||
Vendored
+13
@@ -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(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 = ""; });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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)}
|
||||||
{: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="eager" 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={i < 5 ? "eager" : "lazy"} decoding="async" />
|
{:then src}
|
||||||
{/await}
|
<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}
|
||||||
|
{: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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user