Compare commits

...

1 Commits

Author SHA1 Message Date
midzelis
7818477bd3 feat: ability to check if asset image is cached 2025-12-06 18:44:04 +00:00
4 changed files with 56 additions and 4 deletions

View File

@@ -1,8 +1,39 @@
const broadcast = new BroadcastChannel('immich'); const broadcast = new BroadcastChannel('immich');
let isLoadedReplyListeners: ((url: string, isUrlCached: boolean) => void)[] = [];
broadcast.addEventListener('message', (event) => {
if (event.data.type == 'isImageUrlCachedReply') {
for (const listener of isLoadedReplyListeners) {
listener(event.data.url, event.data.isImageUrlCached);
}
}
});
export function cancelImageUrl(url: string) { export function cancelImageUrl(url: string) {
broadcast.postMessage({ type: 'cancel', url }); broadcast.postMessage({ type: 'cancel', url });
} }
export function preloadImageUrl(url: string) { export function preloadImageUrl(url: string) {
broadcast.postMessage({ type: 'preload', url }); broadcast.postMessage({ type: 'preload', url });
} }
export function isImageUrlCached(url: string) {
if (!globalThis.isSecureContext) {
return Promise.resolve(false);
}
return new Promise((resolve) => {
const listener = (urlReply: string, isUrlCached: boolean) => {
if (urlReply === url) {
cleanup(isUrlCached);
}
};
const cleanup = (isUrlCached: boolean) => {
isLoadedReplyListeners = isLoadedReplyListeners.filter((element) => element !== listener);
resolve(isUrlCached);
};
isLoadedReplyListeners.push(listener);
broadcast.postMessage({ type: 'isImageUrlCached', url });
setTimeout(() => cleanup(false), 5000);
});
}

View File

@@ -1,7 +1,8 @@
import { handleCancel, handlePreload } from './request'; import { handleCancel, handleIsUrlCached, handlePreload } from './request';
export const broadcast = new BroadcastChannel('immich');
export const installBroadcastChannelListener = () => { export const installBroadcastChannelListener = () => {
const broadcast = new BroadcastChannel('immich');
// eslint-disable-next-line unicorn/prefer-add-event-listener // eslint-disable-next-line unicorn/prefer-add-event-listener
broadcast.onmessage = (event) => { broadcast.onmessage = (event) => {
if (!event.data) { if (!event.data) {
@@ -20,6 +21,15 @@ export const installBroadcastChannelListener = () => {
handleCancel(url); handleCancel(url);
break; break;
} }
case 'isImageUrlCached': {
void handleIsUrlCached(url);
break;
}
} }
}; };
}; };
export const replyIsImageUrlCached = (url: string, isImageUrlCached: boolean) => {
broadcast.postMessage({ type: 'isImageUrlCachedReply', url, isImageUrlCached });
};

View File

@@ -30,7 +30,11 @@ export const put = async (key: string, response: Response) => {
return; return;
} }
cache.put(key, response.clone()); try {
await cache.put(key, response.clone());
} catch (error) {
console.error('Ignoring error during cache put', error);
}
}; };
export const prune = async () => { export const prune = async () => {

View File

@@ -1,3 +1,4 @@
import { replyIsImageUrlCached } from './broadcast-channel';
import { get, put } from './cache'; import { get, put } from './cache';
const pendingRequests = new Map<string, AbortController>(); const pendingRequests = new Map<string, AbortController>();
@@ -44,7 +45,7 @@ export const handleRequest = async (request: URL | Request) => {
const response = await fetch(request, { signal: cancelToken.signal }); const response = await fetch(request, { signal: cancelToken.signal });
assertResponse(response); assertResponse(response);
put(cacheKey, response); await put(cacheKey, response);
return response; return response;
} catch (error) { } catch (error) {
@@ -71,3 +72,9 @@ export const handleCancel = (url: URL) => {
pendingRequest.abort(); pendingRequest.abort();
pendingRequests.delete(cacheKey); pendingRequests.delete(cacheKey);
}; };
export const handleIsUrlCached = async (url: URL) => {
const cacheKey = getCacheKey(url);
const isImageUrlCached = !!(await get(cacheKey));
replyIsImageUrlCached(url.pathname + url.search + url.hash, isImageUrlCached);
};