UNPKG

aniki

Version:

Aniki is an easy-to-use NPM module that gets information about your favorite anime and manga.

312 lines (311 loc) 11.3 kB
/** * @file This file contain the MangaKitsu class, used to get Manga informations from the Kitsu.app API. */ // The url const url = "https://kitsu.app/api/edge"; // Main class /** * @class * @description MangaKitsu is a class that's using the Kitsu.app API, with this class you can find Mangas informations in different ways * @example * Basic usage: * ```js * // CJS * const { MangaKitsu } = require("aniki") * // JS ESM or TS * import { MangaKitsu } from "aniki"; * * const manga = new MangaKitsu(); * * // Normal * manga.find({ query: "Oshi no Ko" }).then(a => console.log(a.data[0])); * * // Find by an id * manga.findById(3600).then(a => console.log(a.data)); * * // Handling errors * * manga.find( * { query: "Oshi no ko" }, * async ({ apiError, moduleError }, status) => { * if (apiError) console.error(await apiError); * if (moduleError) console.error(await moduleError); * }); * * * // Best practice to avoid using .then() method is by using asynchronous functions * * async function getManga(query) { * // ... * const a = await manga.find({ query: query }) * * // Tip to avoid multiple awaits * const a = manga.find({query: ""}); * const b = manga.list({}); * const [A, B] = await Promise.all([a, b]) * } * * ``` * @since 1.0.2 */ class MangaKitsu { defaultHandleError = async (error) => { if (error.apiError) console.error("Aniki: Unhandled API error:", (await error.apiError).errors); if (error.moduleError) console.error("Aniki: Unhandled error:", await error.moduleError); }; // Methods /** * @method * @param {IKitsuMangaFind} params - The parameters for the request. * @param {TKitsuHandleError} [handleError] - Used for handling errors from the method and the API. * @description The find method is used to find mangas with different parameters. * @returns {Promise<IKitsuManga | undefined>} - Returns a Promise with the IKitsuManga inteface. * @example * ```js * manga.find({ query: "Oshi no ko", offset: 0 }).then(a => console.log(a)); // offset is optional. * ``` * @since 1.0.2 */ async find(params, handleError) { const parameters = {}; if (!params.query) { await (handleError || this.defaultHandleError)({ moduleError: "'query' in MangaKitsu#find is empty.", }, 400); return; } Object.assign(parameters, { "filter[text]": params.query }); if (params.offset) { if (isNaN(params.offset)) { await (handleError || this.defaultHandleError)({ moduleError: "'offset' in MangaKitsu#find is not a number.", }, 400); return; } } Object.assign(parameters, { "page[offset]": params.offset ?? 0 }); if (params.perPage) { if (isNaN(params.perPage)) { await (handleError || this.defaultHandleError)({ moduleError: "'perPage' in MangaKitsu#find is not a number.", }, 400); return; } if (params.perPage > 20) { await (handleError || this.defaultHandleError)({ moduleError: "'perPage' in MangaKitsu#find should be less than or equal to 20.", }, 400); return; } } Object.assign(parameters, { "page[limit]": params.perPage ?? 10 }); if (params.season) Object.assign(parameters, { "filter[season]": params.season }); if (params.year) Object.assign(parameters, { "filter[year]": params.year }); if (params.categories) Object.assign(parameters, { "filter[categories]": params.categories }); const p = new URLSearchParams(parameters); const res = await fetch(`${url}/manga?${p}`, { headers: { "Content-Type": "application/vnd.api+json", Accept: "application/vnd.api+json", }, }); if (!res.ok) { await (handleError || this.defaultHandleError)({ apiError: (await res.json()), }, res.status); return; } return res.json(); } /** * @method * @param {number | `${number}`} id - The ID of the manga. * @param {TKitsuHandleError} [handleError] - Used for handling errors from the method and the API. * @description Get an Manga with the ID. * @returns {Promise<IKitsuMangaSingle | undefined>} - Return a Promise. * @example * ```js * manga.findById(30).then(r => console.log(r.data.id)); * * // Or * manga.findById("30").then(r => console.log(r.data.id)); * ``` * @since 1.3.0 */ async findById(id, handleError) { if (!id) { await (handleError || this.defaultHandleError)({ moduleError: "'id' in MangaKitsu#findById is empty.", }, 400); return; } if (isNaN(id)) { await (handleError || this.defaultHandleError)({ moduleError: "'id' in MangaKitsu#findById is not a number.", }, 400); return; } const res = await fetch(`${url}/manga/${id}`, { headers: { "Content-Type": "application/vnd.api+json", Accept: "application/vnd.api+json", }, }); if (!res.ok) { await (handleError || this.defaultHandleError)({ apiError: (await res.json()), }, res.status); return; } return res.json(); } /** * * @method * @param {IKitsuMangaList} params - The parameters for the request. * @param {TKitsuHandleError} [handleError] - Used for handling errors from the method and the API. * @description Get an list of Mangas, you can choose the page, and the number of Mangas per page. * @returns {Promise<IKitsuManga | undefined>} - Return a Promise. * @example * ```js * Manga.list({ offset: 0, perPage: 10 }).then(a => console.log(a)); * ``` * @since 1.0.2 * */ async list(params, handleError) { const parameters = {}; if (params.offset) { if (isNaN(params.offset)) { await (handleError || this.defaultHandleError)({ moduleError: "'offset' in MangaKitsu#list is not a number.", }, 400); return; } } Object.assign(parameters, { "page[offset]": params.offset ?? 0 }); if (params.perPage) { if (isNaN(params.perPage)) { await (handleError || this.defaultHandleError)({ moduleError: "'perPage' in MangaKitsu#list is not a number.", }, 400); return; } if (params.perPage > 20) { await (handleError || this.defaultHandleError)({ moduleError: "'perPage' in MangaKitsu#list should be less than or equal to 20.", }, 400); return; } } Object.assign(parameters, { "page[limit]": params.perPage ?? 10 }); if (params.averageRating) Object.assign(parameters, { "filter[averageRating]": params.averageRating, }); if (params.season) Object.assign(parameters, { "filter[season]": params.season }); if (params.year) Object.assign(parameters, { "filter[year]": params.year }); if (params.categories) Object.assign(parameters, { "filter[categories]": params.categories }); const p = new URLSearchParams(parameters); const res = await fetch(`${url}/manga?${p}`, { headers: { "Content-Type": "application/vnd.api+json", Accept: "application/vnd.api+json", }, }); if (!res.ok) { await (handleError || this.defaultHandleError)({ apiError: (await res.json()), }, res.status); return; } return res.json(); } /** * @method * @param {number | `${number}`} id - The parameters to find a specific chapter of a manga using the chapter ID. * @param {TKitsuHandleError} [handleError] - Used for handling errors from the method and the API. * @description Get an chapter with the ID. * @returns {Promise<IKitsuChapter | undefined>} - Return a IKitsuChapter Promise interface or undefined if it has no result. * @example * ```js * manga.chapter(30).then(r => console.log(r.data.attributes.titles.en)); * ``` * @since 1.3.0 */ async chapter(id, handleError) { if (!id) { await (handleError || this.defaultHandleError)({ moduleError: "'id' in MangaKitsu#chapter is empty.", }, 400); return; } if (isNaN(id)) { await (handleError || this.defaultHandleError)({ moduleError: "'id' in MangaKitsu#chapter is not a number.", }, 400); return; } const res = await fetch(`${url}/chapters/${id}`, { headers: { "Content-Type": "application/vnd.api+json", Accept: "application/vnd.api+json", }, }); if (!res.ok) { await (handleError || this.defaultHandleError)({ apiError: (await res.json()), }, res.status); return; } return res.json(); } /** * @method * @param {number | `${number}`} mangaId - The parameters to find all chapters of an manga using its ID. * @param {TKitsuHandleError} [handleError] - Used for handling errors from the method and the API. * @returns {Promise<IKitsuChapters | undefined>} Return a IKitsuChapters Promise interface or undefined if it has no result. * @example * ```js * manga.chapters(7442).then(r => console.log(r.data.attributes.titles.en)); * ``` * @since 1.3.5 */ async chapters(mangaId, handleError) { if (!mangaId) { await (handleError || this.defaultHandleError)({ moduleError: "'mangaId' in MangaKitsu#chapters is empty.", }, 400); return; } if (isNaN(mangaId)) { await (handleError || this.defaultHandleError)({ moduleError: "'mangaId' in MangaKitsu#chapters is not a number.", }, 400); return; } const res = await fetch(`${url}/chapters?filter[manga_id]=${mangaId}`, { headers: { "Content-Type": "application/vnd.api+json", Accept: "application/vnd.api+json", }, }); if (!res.ok) { await (handleError || this.defaultHandleError)({ apiError: (await res.json()), }, res.status); return; } return res.json(); } } export { MangaKitsu }; export default MangaKitsu;