diff --git a/src/lib/services/base-api.service.ts b/src/lib/services/base-api.service.ts index 731e848..6c0aba1 100644 --- a/src/lib/services/base-api.service.ts +++ b/src/lib/services/base-api.service.ts @@ -21,7 +21,7 @@ interface KomgaRequestInit extends RequestInit { interface KomgaUrlBuilder { path: string; - params?: Record; + params?: Record; } export abstract class BaseApiService { @@ -136,14 +136,22 @@ export abstract class BaseApiService { protected static buildUrl( config: AuthConfig, path: string, - params?: Record + params?: Record ): string { const url = new URL(`${config.serverUrl}/api/v1/${path}`); if (params) { Object.entries(params).forEach(([key, value]) => { if (value !== undefined) { - url.searchParams.append(key, value); + if (Array.isArray(value)) { + value.forEach((v) => { + if (v !== undefined) { + url.searchParams.append(key, v); + } + }); + } else { + url.searchParams.append(key, value); + } } }); } diff --git a/src/lib/services/library.service.ts b/src/lib/services/library.service.ts index c25278f..973db6a 100644 --- a/src/lib/services/library.service.ts +++ b/src/lib/services/library.service.ts @@ -85,19 +85,37 @@ export class LibraryService extends BaseApiService { const headers = { "Content-Type": "application/json" }; // Construction du body de recherche pour Komga - const condition: Record = { - libraryId: { - operator: "is", - value: libraryId, - }, - }; + let condition: any; + + if (unreadOnly) { + // Utiliser allOf pour combiner les conditions + condition = { + allOf: [ + { + libraryId: { + operator: "is", + value: libraryId, + }, + }, + { + readStatus: { + operator: "is", + value: "UNREAD", + }, + }, + ], + }; + } else { + condition = { + libraryId: { + operator: "is", + value: libraryId, + }, + }; + } const searchBody = { condition }; - // Pour le filtre unread, on récupère plus d'éléments car on filtre côté client - // Estimation : ~50% des séries sont unread, donc on récupère 2x pour être sûr - const fetchSize = unreadOnly ? size * 2 : size; - // Clé de cache incluant tous les paramètres const cacheKey = `library-${libraryId}-series-p${page}-s${size}-u${unreadOnly}-q${ search || "" @@ -106,9 +124,9 @@ export class LibraryService extends BaseApiService { const response = await this.fetchWithCache>( cacheKey, async () => { - const params: Record = { + const params: Record = { page: String(page), - size: String(fetchSize), + size: String(size), sort: "metadata.titleSort,asc", }; @@ -129,27 +147,13 @@ export class LibraryService extends BaseApiService { "SERIES" ); - // Filtrer les séries supprimées côté client (léger) - let filteredContent = response.content.filter((series) => !series.deleted); + // Filtrer uniquement les séries supprimées côté client (léger) + const filteredContent = response.content.filter((series) => !series.deleted); - // Filtre unread côté client (Komga n'a pas de filtre natif pour booksReadCount < booksCount) - if (unreadOnly) { - filteredContent = filteredContent.filter( - (series) => series.booksReadCount < series.booksCount - ); - // Prendre uniquement les `size` premiers après filtrage - filteredContent = filteredContent.slice(0, size); - } - - // Note: Les totaux (totalElements, totalPages) restent ceux de Komga - // Ils sont approximatifs après filtrage côté client mais fonctionnels pour la pagination - // Le filtrage côté client est léger (seulement deleted + unread) return { ...response, content: filteredContent, numberOfElements: filteredContent.length, - // Garder totalElements et totalPages de Komga pour la pagination - // Ils seront légèrement inexacts mais fonctionnels }; } catch (error) { throw new AppError(ERROR_CODES.SERIES.FETCH_ERROR, {}, error); diff --git a/src/lib/services/series.service.ts b/src/lib/services/series.service.ts index cc46325..1510ec9 100644 --- a/src/lib/services/series.service.ts +++ b/src/lib/services/series.service.ts @@ -101,36 +101,46 @@ export class SeriesService extends BaseApiService { const headers = { "Content-Type": "application/json" }; // Construction du body de recherche pour Komga - const condition: Record = { - seriesId: { - operator: "is", - value: seriesId, - }, - }; + let condition: any; - // Filtre unread natif Komga (readStatus != READ) if (unreadOnly) { - condition.readStatus = { - operator: "isNot", - value: "READ", + // Utiliser allOf pour combiner les conditions + condition = { + allOf: [ + { + seriesId: { + operator: "is", + value: seriesId, + }, + }, + { + readStatus: { + operator: "is", + value: "UNREAD", + }, + }, + ], + }; + } else { + condition = { + seriesId: { + operator: "is", + value: seriesId, + }, }; } const searchBody = { condition }; - // Pour le filtre unread, on récupère plus d'éléments car on filtre aussi les deleted côté client - // Estimation : ~10% des livres sont supprimés, donc on récupère légèrement plus - const fetchSize = unreadOnly ? size : size; - // Clé de cache incluant tous les paramètres const cacheKey = `series-${seriesId}-books-p${page}-s${size}-u${unreadOnly}`; const response = await this.fetchWithCache>( cacheKey, async () => { - const params: Record = { + const params: Record = { page: String(page), - size: String(fetchSize), + size: String(size), sort: "number,asc", }; @@ -146,23 +156,13 @@ export class SeriesService extends BaseApiService { "BOOKS" ); - // Filtrer les livres supprimés côté client (léger) - let filteredContent = response.content.filter((book: KomgaBook) => !book.deleted); + // Filtrer uniquement les livres supprimés côté client (léger) + const filteredContent = response.content.filter((book: KomgaBook) => !book.deleted); - // Si on a filtré des livres supprimés, prendre uniquement les `size` premiers - if (filteredContent.length > size) { - filteredContent = filteredContent.slice(0, size); - } - - // Note: Les totaux (totalElements, totalPages) restent ceux de Komga - // Ils sont approximatifs après filtrage côté client mais fonctionnels pour la pagination - // Le filtrage côté client est léger (seulement deleted) return { ...response, content: filteredContent, numberOfElements: filteredContent.length, - // Garder totalElements et totalPages de Komga pour la pagination - // Ils seront légèrement inexacts mais fonctionnels }; } catch (error) { throw new AppError(ERROR_CODES.SERIES.FETCH_ERROR, {}, error);