aniki
Version:
Aniki is an easy-to-use NPM module that gets information about your favorite anime and manga.
320 lines (318 loc) • 11.8 kB
JavaScript
/**
* @file This file contain the AnimeKitsu class, used to get anime informations from the Kitsu.app API.
*/
// The url
const url = "https://kitsu.app/api/edge";
// Main class
/**
* @class
* @since 1.0.2
* @description AnimeKitsu is a class that's using the Kitsu.app API, with this class you can find animes informations in different ways
* @example
* Basic usage:
* ```js
* // CJS
* const { AnimeKitsu } = require("aniki")
* // JS ESM or TS
* import { AnimeKitsu } from "aniki";
*
* const anime = new AnimeKitsu();
*
* // Normal
* anime.find({ query: "Oshi no Ko" }).then(a => console.log(a.data[0]));
*
* // Find by an id
* anime.findById(3600).then(a => console.log(a.data));
*
* // Handling errors
*
* anime.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 function
*
* async function getAnime(query) {
* // ...
* const a = await anime.find({ query: query })
* // ...
* }
*
* ```
*/
class AnimeKitsu {
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 {IKitsuAnimeFind} 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 animes with different parameters.
* @returns {Promise<IKitsuAnime | undefined>} - Returns a Promise with the IKitsuAnime inteface.
* @example
* ```js
* // Searching an anime
* anime.find({ query: "Oshi no ko", offset: 0 }).then(r=> console.log(r.data[0]))
* ```
* @since 1.0.2
*/
async find(params, handleError) {
const parameters = {};
if (!params.query) {
await (handleError || this.defaultHandleError)({
moduleError: "'query' in AnimeKitsu#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 AnimeKitsu#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 AnimeKitsu#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.averageRating)
Object.assign(parameters, {
"filter[averageRating]": params.averageRating,
});
if (params.season)
Object.assign(parameters, { "filter[season]": params.season });
if (params.ageRating)
Object.assign(parameters, { "filter[ageRating]": params.ageRating });
if (params.year)
Object.assign(parameters, { "filter[year]": params.year });
if (params.streamers)
Object.assign(parameters, { "filter[streamers]": params.streamers });
if (params.categories)
Object.assign(parameters, { "filter[categories]": params.categories });
const p = new URLSearchParams(parameters);
const res = await fetch(`${url}/anime?${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 anime.
* @param {TKitsuHandleError} [handleError] - Used for handling errors from the method and the API.
* @description Get an anime with the ID.
* @returns {Promise<IKitsuAnimeSingle | undefined>} - Return a Promise.
* @example
* ```js
* anime.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 AnimeKitsu#findById is empty.",
}, 400);
return;
}
if (isNaN(id)) {
await (handleError || this.defaultHandleError)({
moduleError: "'id' in AnimeKitsu#findById is not a number.",
}, 400);
return;
}
const res = await fetch(`${url}/anime/${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 {IKitsuAnimeList} params - The parameters for the request.
* @param {TKitsuHandleError} [handleError] - Used for handling errors from the method and the API.
* @description Get an list of animes, you can choose the page, and the number of animes per page.
* @returns {Promise<IKitsuAnime | undefined>} - Return a Promise.
* @example
* ```js
* anime.list({ offset: 0, perPage: 10 }).then(a => console.log(a));
* ```
* @since 1.0.2
*
*/
async list(params, handleError) {
// Maybe i should delete this method and using find method only...
const parameters = {};
if (params.offset) {
if (isNaN(params.offset)) {
await (handleError || this.defaultHandleError)({
moduleError: "'offset' in AnimeKitsu#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 AnimeKitsu#list 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.averageRating)
Object.assign(parameters, {
"filter[averageRating]": params.averageRating,
});
if (params.ageRating)
Object.assign(parameters, { "filter[averageRating]": params.ageRating });
if (params.season)
Object.assign(parameters, { "filter[season]": params.season });
if (params.year)
Object.assign(parameters, { "filter[year]": params.year });
if (params.streamers)
Object.assign(parameters, { "filter[streamers]": params.streamers });
if (params.categories)
Object.assign(parameters, { "filter[categories]": params.categories });
const p = new URLSearchParams(parameters);
const res = await fetch(`${url}/anime?${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 episode of an anime using the episode ID.
* @param {TKitsuHandleError} [handleError] - Used for handling errors from the method and the API.
* @description Get an episode with the ID.
* @returns {Promise<IKitsuEpisode | undefined>} - Return a IKitsuEpisode Promise interface or undefined if it has no result.
* @example
* ```js
* anime.episode(30).then(r => console.log(r.data.attributes.titles.en));
* ```
* @since 1.3.0
*/
async episode(id, handleError) {
if (!id) {
await (handleError || this.defaultHandleError)({
moduleError: "'id' in AnimeKitsu#episode is empty.",
}, 400);
return;
}
if (isNaN(id)) {
await (handleError || this.defaultHandleError)({
moduleError: "'id' in AnimeKitsu#episode is not a number.",
}, 400);
return;
}
const res = await fetch(`${url}/episodes/${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}`} mediaId - The parameters to find all episodes of an anime using its ID.
* @param {TKitsuHandleError} [handleError] - Used for handling errors from the method and the API.
* @description Get an episode with the ID.
* @returns {Promise<IKitsuEpisodes | undefined>} - Return a IKitsuEpisode Promise interface or undefined if it has no result.
* @example
* ```js
* anime.episodes(7442).then(r => console.log(r.data.attributes.titles.en));
* ```
* @since 1.3.5
*/
async episodes(mediaId, handleError) {
if (!mediaId) {
await (handleError || this.defaultHandleError)({
moduleError: "'mediaId' in AnimeKitsu#episodes is empty.",
}, 400);
return;
}
if (isNaN(mediaId)) {
await (handleError || this.defaultHandleError)({
moduleError: "'mediaId' in AnimeKitsu#episodes is not a number.",
}, 400);
return;
}
const res = await fetch(`${url}/episodes?filter[media_id]=${mediaId}`, {
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 { AnimeKitsu };
export default AnimeKitsu;