refacto(services): fetchFromApi simpler + bug libraries fetch

This commit is contained in:
Julien Froidefond
2025-02-23 10:42:04 +01:00
parent 9c1889cf7d
commit fd2632c394
6 changed files with 90 additions and 84 deletions

View File

@@ -9,6 +9,11 @@ interface KomgaRequestInit extends RequestInit {
isImage?: boolean; isImage?: boolean;
} }
interface KomgaUrlBuilder {
path: string;
params?: Record<string, string>;
}
export abstract class BaseApiService { export abstract class BaseApiService {
protected static async getKomgaConfig(): Promise<AuthConfig> { protected static async getKomgaConfig(): Promise<AuthConfig> {
try { try {
@@ -78,10 +83,24 @@ export abstract class BaseApiService {
} }
protected static async fetchFromApi<T>( protected static async fetchFromApi<T>(
url: string, urlBuilder: KomgaUrlBuilder,
headers: Headers, headersOptions = {},
options: KomgaRequestInit = {} options: KomgaRequestInit = {}
): Promise<T> { ): Promise<T> {
const config = await this.getKomgaConfig();
const { path, params } = urlBuilder;
const url = this.buildUrl(config, path, params);
const headers = this.getAuthHeaders(config);
if (headersOptions) {
for (const [key, value] of Object.entries(headersOptions)) {
headers.set(key as string, value as string);
}
}
// console.log("Fetching from", url);
// console.log("Headers", headers);
// console.log("headersOptions", headersOptions);
// console.log("options", options);
const response = await fetch(url, { headers, ...options }); const response = await fetch(url, { headers, ...options });
if (!response.ok) { if (!response.ok) {

View File

@@ -6,23 +6,16 @@ import { PreferencesService } from "./preferences.service";
export class BookService extends BaseApiService { export class BookService extends BaseApiService {
static async getBook(bookId: string): Promise<{ book: KomgaBook; pages: number[] }> { static async getBook(bookId: string): Promise<{ book: KomgaBook; pages: number[] }> {
try { try {
const config = await this.getKomgaConfig();
const headers = this.getAuthHeaders(config);
return this.fetchWithCache<{ book: KomgaBook; pages: number[] }>( return this.fetchWithCache<{ book: KomgaBook; pages: number[] }>(
`book-${bookId}`, `book-${bookId}`,
async () => { async () => {
// Récupération des détails du tome // Récupération des détails du tome
const book = await this.fetchFromApi<KomgaBook>( const book = await this.fetchFromApi<KomgaBook>({ path: `books/${bookId}` });
this.buildUrl(config, `books/${bookId}`),
headers
);
// Récupération des pages du tome // Récupération des pages du tome
const pages = await this.fetchFromApi<{ number: number }[]>( const pages = await this.fetchFromApi<{ number: number }[]>({
this.buildUrl(config, `books/${bookId}/pages`), path: `books/${bookId}/pages`,
headers });
);
return { return {
book, book,

View File

@@ -12,45 +12,47 @@ interface HomeData {
export class HomeService extends BaseApiService { export class HomeService extends BaseApiService {
static async getHomeData(): Promise<HomeData> { static async getHomeData(): Promise<HomeData> {
try { try {
const config = await this.getKomgaConfig(); // Appels API parallèles avec cache individuel
const headers = this.getAuthHeaders(config); const [ongoing, recentlyRead, onDeck] = await Promise.all([
this.fetchWithCache<LibraryResponse<KomgaSeries>>(
// Construction des URLs avec des paramètres optimisés "home-ongoing",
const ongoingUrl = this.buildUrl(config, "series", { async () =>
this.fetchFromApi<LibraryResponse<KomgaSeries>>({
path: "series",
params: {
read_status: "IN_PROGRESS", read_status: "IN_PROGRESS",
sort: "readDate,desc", sort: "readDate,desc",
page: "0", page: "0",
size: "10", size: "10",
media_status: "READY", media_status: "READY",
}); },
}),
const recentlyReadUrl = this.buildUrl(config, "books/latest", {
page: "0",
size: "10",
media_status: "READY",
});
const onDeckUrl = this.buildUrl(config, "books/ondeck", {
page: "0",
size: "10",
media_status: "READY",
});
// Appels API parallèles avec cache individuel
const [ongoing, recentlyRead, onDeck] = await Promise.all([
this.fetchWithCache<LibraryResponse<KomgaSeries>>(
"home-ongoing",
async () => this.fetchFromApi<LibraryResponse<KomgaSeries>>(ongoingUrl, headers),
"HOME" "HOME"
), ),
this.fetchWithCache<LibraryResponse<KomgaBook>>( this.fetchWithCache<LibraryResponse<KomgaBook>>(
"home-recently-read", "home-recently-read",
async () => this.fetchFromApi<LibraryResponse<KomgaBook>>(recentlyReadUrl, headers), async () =>
this.fetchFromApi<LibraryResponse<KomgaBook>>({
path: "books/latest",
params: {
page: "0",
size: "10",
media_status: "READY",
},
}),
"HOME" "HOME"
), ),
this.fetchWithCache<LibraryResponse<KomgaBook>>( this.fetchWithCache<LibraryResponse<KomgaBook>>(
"home-on-deck", "home-on-deck",
async () => this.fetchFromApi<LibraryResponse<KomgaBook>>(onDeckUrl, headers), async () =>
this.fetchFromApi<LibraryResponse<KomgaBook>>({
path: "books/ondeck",
params: {
page: "0",
size: "10",
media_status: "READY",
},
}),
"HOME" "HOME"
), ),
]); ]);

View File

@@ -8,15 +8,12 @@ interface ImageResponse {
export class ImageService extends BaseApiService { export class ImageService extends BaseApiService {
static async getImage(path: string): Promise<ImageResponse> { static async getImage(path: string): Promise<ImageResponse> {
try { try {
const config = await this.getKomgaConfig(); const headers = { Accept: "image/jpeg, image/png, image/gif, image/webp, */*" };
const url = this.buildUrl(config, path);
const headers = this.getAuthHeaders(config);
headers.set("Accept", "image/jpeg, image/png, image/gif, image/webp, */*");
return this.fetchWithCache<ImageResponse>( return this.fetchWithCache<ImageResponse>(
`image-${path}`, `image-${path}`,
async () => { async () => {
const response = await this.fetchFromApi<Response>(url, headers, { isImage: true }); const response = await this.fetchFromApi<Response>({ path }, headers, { isImage: true });
const contentType = response.headers.get("content-type"); const contentType = response.headers.get("content-type");
const arrayBuffer = await response.arrayBuffer(); const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer); const buffer = Buffer.from(arrayBuffer);

View File

@@ -6,13 +6,9 @@ import { serverCacheService } from "./server-cache.service";
export class LibraryService extends BaseApiService { export class LibraryService extends BaseApiService {
static async getLibraries(): Promise<Library[]> { static async getLibraries(): Promise<Library[]> {
try { try {
const config = await this.getKomgaConfig();
const url = this.buildUrl(config, "libraries");
const headers = this.getAuthHeaders(config);
return this.fetchWithCache<Library[]>( return this.fetchWithCache<Library[]>(
"libraries", "libraries",
async () => this.fetchFromApi<Library[]>(url, headers), async () => this.fetchFromApi<Library[]>({ path: "libraries" }),
"LIBRARIES" "LIBRARIES"
); );
} catch (error) { } catch (error) {
@@ -31,12 +27,7 @@ export class LibraryService extends BaseApiService {
static async getAllLibrarySeries(libraryId: string): Promise<Series[]> { static async getAllLibrarySeries(libraryId: string): Promise<Series[]> {
try { try {
const config = await this.getKomgaConfig(); const headers = { "Content-Type": "application/json" };
const url = this.buildUrl(config, "series/list", {
size: "1000", // On récupère un maximum de séries
});
const headers = this.getAuthHeaders(config);
headers.set("Content-Type", "application/json");
const searchBody = { const searchBody = {
condition: { condition: {
@@ -51,10 +42,19 @@ export class LibraryService extends BaseApiService {
const response = await this.fetchWithCache<LibraryResponse<Series>>( const response = await this.fetchWithCache<LibraryResponse<Series>>(
cacheKey, cacheKey,
async () => async () =>
this.fetchFromApi<LibraryResponse<Series>>(url, headers, { this.fetchFromApi<LibraryResponse<Series>>(
{
path: "series/list",
params: {
size: "1000", // On récupère un maximum de livres
},
},
headers,
{
method: "POST", method: "POST",
body: JSON.stringify(searchBody), body: JSON.stringify(searchBody),
}), }
),
"SERIES" "SERIES"
); );
@@ -74,7 +74,6 @@ export class LibraryService extends BaseApiService {
try { try {
// Récupérer toutes les séries depuis le cache // Récupérer toutes les séries depuis le cache
const allSeries = await this.getAllLibrarySeries(libraryId); const allSeries = await this.getAllLibrarySeries(libraryId);
// Filtrer les séries // Filtrer les séries
let filteredSeries = allSeries; let filteredSeries = allSeries;

View File

@@ -9,13 +9,9 @@ import { serverCacheService } from "./server-cache.service";
export class SeriesService extends BaseApiService { export class SeriesService extends BaseApiService {
static async getSeries(seriesId: string): Promise<KomgaSeries> { static async getSeries(seriesId: string): Promise<KomgaSeries> {
try { try {
const config = await this.getKomgaConfig();
const url = this.buildUrl(config, `series/${seriesId}`);
const headers = this.getAuthHeaders(config);
return this.fetchWithCache<KomgaSeries>( return this.fetchWithCache<KomgaSeries>(
`series-${seriesId}`, `series-${seriesId}`,
async () => this.fetchFromApi<KomgaSeries>(url, headers), async () => this.fetchFromApi<KomgaSeries>({ path: `series/${seriesId}` }),
"SERIES" "SERIES"
); );
} catch (error) { } catch (error) {
@@ -29,12 +25,7 @@ export class SeriesService extends BaseApiService {
static async getAllSeriesBooks(seriesId: string): Promise<KomgaBook[]> { static async getAllSeriesBooks(seriesId: string): Promise<KomgaBook[]> {
try { try {
const config = await this.getKomgaConfig(); const headers = { "Content-Type": "application/json" };
const url = this.buildUrl(config, "books/list", {
size: "1000", // On récupère un maximum de livres
});
const headers = this.getAuthHeaders(config);
headers.set("Content-Type", "application/json");
const searchBody = { const searchBody = {
condition: { condition: {
@@ -49,10 +40,19 @@ export class SeriesService extends BaseApiService {
const response = await this.fetchWithCache<LibraryResponse<KomgaBook>>( const response = await this.fetchWithCache<LibraryResponse<KomgaBook>>(
cacheKey, cacheKey,
async () => async () =>
this.fetchFromApi<LibraryResponse<KomgaBook>>(url, headers, { this.fetchFromApi<LibraryResponse<KomgaBook>>(
{
path: "books/list",
params: {
size: "1000", // On récupère un maximum de livres
},
},
headers,
{
method: "POST", method: "POST",
body: JSON.stringify(searchBody), body: JSON.stringify(searchBody),
}), }
),
"BOOKS" "BOOKS"
); );
@@ -131,17 +131,13 @@ export class SeriesService extends BaseApiService {
static async getFirstBook(seriesId: string): Promise<string> { static async getFirstBook(seriesId: string): Promise<string> {
try { try {
const config = await this.getKomgaConfig();
const url = this.buildUrl(config, `series/${seriesId}/books`);
const headers = this.getAuthHeaders(config);
return this.fetchWithCache<string>( return this.fetchWithCache<string>(
`series-first-book-${seriesId}`, `series-first-book-${seriesId}`,
async () => { async () => {
const data = await this.fetchFromApi<LibraryResponse<KomgaBook>>( const data = await this.fetchFromApi<LibraryResponse<KomgaBook>>({
`series/${seriesId}/books?page=0&size=1`, path: `series/${seriesId}/books`,
headers params: { page: "0", size: "1" },
); });
if (!data.content || data.content.length === 0) { if (!data.content || data.content.length === 0) {
throw new Error("Aucun livre trouvé dans la série"); throw new Error("Aucun livre trouvé dans la série");
} }