2025-10-07 08:33:06 +02:00
|
|
|
/**
|
|
|
|
|
* Service API pour interagir avec le cache d'images de couvertures
|
|
|
|
|
*/
|
|
|
|
|
|
2025-10-26 18:30:50 +01:00
|
|
|
export interface CacheMetadata {
|
|
|
|
|
origin_url?: string;
|
|
|
|
|
[key: string]: unknown;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-07 08:33:06 +02:00
|
|
|
export interface CacheEntry {
|
|
|
|
|
pk: string;
|
2025-10-26 18:30:50 +01:00
|
|
|
id: string | null;
|
|
|
|
|
collection?: string | null;
|
2025-10-07 08:33:06 +02:00
|
|
|
hits: number;
|
|
|
|
|
last_used: string | null;
|
2025-10-26 18:30:50 +01:00
|
|
|
metadata?: CacheMetadata | null;
|
2025-10-07 08:33:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface AddImageRequest {
|
|
|
|
|
url: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface AddImageResponse {
|
|
|
|
|
pk: string;
|
|
|
|
|
url: string;
|
|
|
|
|
message: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ApiError {
|
|
|
|
|
error: string;
|
|
|
|
|
message: string;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-25 17:14:24 +02:00
|
|
|
export interface DownloadStatus {
|
|
|
|
|
pk: string;
|
|
|
|
|
finished: boolean;
|
|
|
|
|
current_size?: number;
|
|
|
|
|
expected_size?: number;
|
|
|
|
|
transformed_size?: number;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-07 08:33:06 +02:00
|
|
|
/**
|
|
|
|
|
* Liste toutes les images en cache
|
|
|
|
|
*/
|
|
|
|
|
export async function listImages(): Promise<CacheEntry[]> {
|
2025-10-07 13:12:44 +02:00
|
|
|
const response = await fetch("/api/covers");
|
2025-10-07 08:33:06 +02:00
|
|
|
if (!response.ok) {
|
|
|
|
|
const error: ApiError = await response.json();
|
|
|
|
|
throw new Error(error.message || "Failed to fetch images");
|
|
|
|
|
}
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Récupère les informations d'une image spécifique
|
|
|
|
|
*/
|
|
|
|
|
export async function getImageInfo(pk: string): Promise<CacheEntry> {
|
2025-10-07 13:12:44 +02:00
|
|
|
const response = await fetch(`/api/covers/${pk}`);
|
2025-10-07 08:33:06 +02:00
|
|
|
if (!response.ok) {
|
|
|
|
|
const error: ApiError = await response.json();
|
|
|
|
|
throw new Error(error.message || "Failed to fetch image info");
|
|
|
|
|
}
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Ajoute une nouvelle image au cache depuis une URL
|
|
|
|
|
*/
|
|
|
|
|
export async function addImage(url: string): Promise<AddImageResponse> {
|
2025-10-07 13:12:44 +02:00
|
|
|
const response = await fetch("/api/covers", {
|
2025-10-07 08:33:06 +02:00
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({ url }),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
const error: ApiError = await response.json();
|
|
|
|
|
throw new Error(error.message || "Failed to add image");
|
|
|
|
|
}
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Supprime une image du cache
|
|
|
|
|
*/
|
|
|
|
|
export async function deleteImage(pk: string): Promise<void> {
|
2025-10-07 13:12:44 +02:00
|
|
|
const response = await fetch(`/api/covers/${pk}`, {
|
2025-10-07 08:33:06 +02:00
|
|
|
method: "DELETE",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
const error: ApiError = await response.json();
|
|
|
|
|
throw new Error(error.message || "Failed to delete image");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Purge complètement le cache
|
|
|
|
|
*/
|
|
|
|
|
export async function purgeCache(): Promise<void> {
|
2025-10-07 13:12:44 +02:00
|
|
|
const response = await fetch("/api/covers", {
|
2025-10-07 08:33:06 +02:00
|
|
|
method: "DELETE",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
const error: ApiError = await response.json();
|
|
|
|
|
throw new Error(error.message || "Failed to purge cache");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Consolide le cache (re-télécharge les images manquantes)
|
|
|
|
|
*/
|
|
|
|
|
export async function consolidateCache(): Promise<void> {
|
2025-10-07 13:12:44 +02:00
|
|
|
const response = await fetch("/api/covers/consolidate", {
|
2025-10-07 08:33:06 +02:00
|
|
|
method: "POST",
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
const error: ApiError = await response.json();
|
|
|
|
|
throw new Error(error.message || "Failed to consolidate cache");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-25 17:14:24 +02:00
|
|
|
/**
|
|
|
|
|
* Récupère le statut du téléchargement d'une image
|
|
|
|
|
*/
|
|
|
|
|
export async function getDownloadStatus(pk: string): Promise<DownloadStatus> {
|
|
|
|
|
const response = await fetch(`/api/covers/${pk}/status`);
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
const error: ApiError = await response.json();
|
|
|
|
|
throw new Error(error.message || "Failed to get download status");
|
|
|
|
|
}
|
|
|
|
|
return response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Attend que le téléchargement d'une image soit terminé
|
|
|
|
|
*
|
|
|
|
|
* @param pk - Clé primaire de l'image
|
|
|
|
|
* @param maxWaitMs - Temps maximum d'attente en millisecondes (défaut: 30000)
|
|
|
|
|
* @param pollIntervalMs - Intervalle entre les vérifications en millisecondes (défaut: 500)
|
|
|
|
|
*/
|
|
|
|
|
export async function waitForDownload(
|
|
|
|
|
pk: string,
|
|
|
|
|
maxWaitMs: number = 30000,
|
|
|
|
|
pollIntervalMs: number = 500
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
const startTime = Date.now();
|
|
|
|
|
|
|
|
|
|
while (Date.now() - startTime < maxWaitMs) {
|
|
|
|
|
try {
|
|
|
|
|
const status = await getDownloadStatus(pk);
|
|
|
|
|
if (status.finished) {
|
|
|
|
|
return; // Téléchargement terminé
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// Si l'API retourne une erreur, on continue d'attendre
|
|
|
|
|
console.warn(`Error checking download status for ${pk}:`, error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Attendre avant la prochaine vérification
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Timeout atteint, on lance une dernière vérification
|
|
|
|
|
const finalStatus = await getDownloadStatus(pk);
|
|
|
|
|
if (!finalStatus.finished) {
|
|
|
|
|
console.warn(`Download timeout for ${pk}, but continuing anyway`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-07 08:33:06 +02:00
|
|
|
/**
|
|
|
|
|
* Génère l'URL pour afficher une image
|
|
|
|
|
*/
|
2025-10-26 18:30:50 +01:00
|
|
|
export function getOriginUrl(entry: CacheEntry): string | undefined {
|
|
|
|
|
const metadata = entry.metadata;
|
|
|
|
|
if (metadata && typeof metadata === "object") {
|
|
|
|
|
const origin = (metadata as { origin_url?: unknown }).origin_url;
|
|
|
|
|
if (typeof origin === "string" && origin.trim().length > 0) {
|
|
|
|
|
return origin;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-07 08:33:06 +02:00
|
|
|
export function getImageUrl(pk: string, size?: number): string {
|
|
|
|
|
if (size) {
|
2025-10-18 22:46:55 +02:00
|
|
|
return `/covers/image/${pk}/${size}`;
|
2025-10-07 08:33:06 +02:00
|
|
|
}
|
2025-10-18 22:46:55 +02:00
|
|
|
return `/covers/image/${pk}`;
|
2025-10-07 08:33:06 +02:00
|
|
|
}
|