@yuna0x0/anilist-node
Version:
A lightweight Node.js wrapper for the AniList API
518 lines (469 loc) • 19.2 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8" />
<title>fetcher.js - AniList-Node Documentation</title>
<meta name="description" content="Documentation for the AniList-Node NPM package." />
<meta property="og:title" content="AniList-Node Documentation" />
<meta property="og:type" content="website" />
<meta property="og:image" content="" />
<meta property="og:site_name" content="AniList-Node Documentation" />
<meta property="og:url" content="https://katsurin.com/docs/anilist-node/" />
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify.css" />
<link type="text/css" rel="stylesheet" href="styles/jsdoc.css" />
<script src="scripts/nav.js" defer></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav>
<input type="text" id="nav-search" placeholder="Search" />
<h2><a href="index.html">Home</a></h2>
<h2>
<a
href="https://github.com/AurelicButter/AniList-Node"
target="_blank"
class="menu-item"
id="website_link"
>GitHub Repository</a
>
</h2>
<h2>
<a href="https://www.npmjs.com/package/anilist-node" target="_blank" class="menu-item" id="website_link"
>AniList-Node on NPM</a
>
</h2>
<h2>
<a href="https://discord.gg/qKfqsjW" target="_blank" class="menu-item" id="website_link"
>Support Server</a
>
</h2>
<h3>Classes</h3>
<ul>
<li>
<a href="AniList.html">AniList</a>
<ul class="methods">
<li data-type="method"><a href="AniList.html#favouriteStudio">favouriteStudio</a></li>
<li data-type="method"><a href="AniList.html#genres">genres</a></li>
<li data-type="method"><a href="AniList.html#mediaTags">mediaTags</a></li>
<li data-type="method"><a href="AniList.html#search">search</a></li>
<li data-type="method"><a href="AniList.html#siteStatistics">siteStatistics</a></li>
<li data-type="method"><a href="AniList.html#studio">studio</a></li>
</ul>
</li>
<li>
<a href="AniList.Activity.html">Activity</a>
<ul class="methods">
<li data-type="method"><a href="AniList.Activity.html#delete">delete</a></li>
<li data-type="method"><a href="AniList.Activity.html#get">get</a></li>
<li data-type="method"><a href="AniList.Activity.html#getUserActivity">getUserActivity</a></li>
<li data-type="method"><a href="AniList.Activity.html#postMessage">postMessage</a></li>
<li data-type="method"><a href="AniList.Activity.html#postText">postText</a></li>
</ul>
</li>
<li>
<a href="AniList.Lists.html">Lists</a>
<ul class="methods">
<li data-type="method"><a href="AniList.Lists.html#addEntry">addEntry</a></li>
<li data-type="method"><a href="AniList.Lists.html#anime">anime</a></li>
<li data-type="method"><a href="AniList.Lists.html#manga">manga</a></li>
<li data-type="method"><a href="AniList.Lists.html#removeEntry">removeEntry</a></li>
<li data-type="method"><a href="AniList.Lists.html#updateEntry">updateEntry</a></li>
</ul>
</li>
<li>
<a href="AniList.Media.html">Media</a>
<ul class="methods">
<li data-type="method"><a href="AniList.Media.html#anime">anime</a></li>
<li data-type="method"><a href="AniList.Media.html#favouriteAnime">favouriteAnime</a></li>
<li data-type="method"><a href="AniList.Media.html#favouriteManga">favouriteManga</a></li>
<li data-type="method"><a href="AniList.Media.html#manga">manga</a></li>
</ul>
</li>
<li>
<a href="AniList.People.html">People</a>
<ul class="methods">
<li data-type="method"><a href="AniList.People.html#character">character</a></li>
<li data-type="method"><a href="AniList.People.html#favouriteChar">favouriteChar</a></li>
<li data-type="method"><a href="AniList.People.html#favouriteStaff">favouriteStaff</a></li>
<li data-type="method">
<a href="AniList.People.html#getBirthdayCharacters">getBirthdayCharacters</a>
</li>
<li data-type="method"><a href="AniList.People.html#getBirthdayStaff">getBirthdayStaff</a></li>
<li data-type="method"><a href="AniList.People.html#staff">staff</a></li>
</ul>
</li>
<li>
<a href="AniList.Recommendation.html">Recommendation</a>
<ul class="methods">
<li data-type="method"><a href="AniList.Recommendation.html#get">get</a></li>
<li data-type="method"><a href="AniList.Recommendation.html#getList">getList</a></li>
</ul>
</li>
<li>
<a href="AniList.Search.html">Search</a>
<ul class="methods">
<li data-type="method"><a href="AniList.Search.html#activity">activity</a></li>
<li data-type="method"><a href="AniList.Search.html#anime">anime</a></li>
<li data-type="method"><a href="AniList.Search.html#character">character</a></li>
<li data-type="method"><a href="AniList.Search.html#manga">manga</a></li>
<li data-type="method"><a href="AniList.Search.html#staff">staff</a></li>
<li data-type="method"><a href="AniList.Search.html#studio">studio</a></li>
<li data-type="method"><a href="AniList.Search.html#user">user</a></li>
</ul>
</li>
<li>
<a href="AniList.Thread.html">Thread</a>
<ul class="methods">
<li data-type="method"><a href="AniList.Thread.html#delete">delete</a></li>
<li data-type="method"><a href="AniList.Thread.html#get">get</a></li>
<li data-type="method"><a href="AniList.Thread.html#getComments">getComments</a></li>
</ul>
</li>
<li>
<a href="AniList.User.html">User</a>
<ul class="methods">
<li data-type="method"><a href="AniList.User.html#all">all</a></li>
<li data-type="method"><a href="AniList.User.html#follow">follow</a></li>
<li data-type="method"><a href="AniList.User.html#getAuthorized">getAuthorized</a></li>
<li data-type="method"><a href="AniList.User.html#getRecentActivity">getRecentActivity</a></li>
<li data-type="method"><a href="AniList.User.html#profile">profile</a></li>
<li data-type="method"><a href="AniList.User.html#stats">stats</a></li>
<li data-type="method"><a href="AniList.User.html#update">update</a></li>
</ul>
</li>
</ul>
<h3>Tutorials</h3>
<ul>
<li><a href="tutorial-Filtering.html">Filtering</a></li>
<li><a href="tutorial-Getting Started.html">Getting Started</a></li>
</ul>
<h3>Global</h3>
<ul>
<li><a href="global.html#ActivityEntry">ActivityEntry</a></li>
<li><a href="global.html#ActivityFilterTypes">ActivityFilterTypes</a></li>
<li><a href="global.html#ActivitySort">ActivitySort</a></li>
<li><a href="global.html#ActivityType">ActivityType</a></li>
<li><a href="global.html#AiringEntry">AiringEntry</a></li>
<li><a href="global.html#AniListStats">AniListStats</a></li>
<li><a href="global.html#AnimeEntry">AnimeEntry</a></li>
<li><a href="global.html#CharacterEntry">CharacterEntry</a></li>
<li><a href="global.html#CharacterName">CharacterName</a></li>
<li><a href="global.html#CountryCode">CountryCode</a></li>
<li><a href="global.html#EntryStatus">EntryStatus</a></li>
<li><a href="global.html#FuzzyDateInt">FuzzyDateInt</a></li>
<li><a href="global.html#FuzzyDateObj">FuzzyDateObj</a></li>
<li><a href="global.html#InitOptions">InitOptions</a></li>
<li><a href="global.html#ListActivity">ListActivity</a></li>
<li><a href="global.html#ListEntry">ListEntry</a></li>
<li><a href="global.html#MangaEntry">MangaEntry</a></li>
<li><a href="global.html#MediaFilterTypes">MediaFilterTypes</a></li>
<li><a href="global.html#MediaFormat">MediaFormat</a></li>
<li><a href="global.html#MediaListOptions">MediaListOptions</a></li>
<li><a href="global.html#MediaListOptionsInput">MediaListOptionsInput</a></li>
<li><a href="global.html#MediaRelation">MediaRelation</a></li>
<li><a href="global.html#MediaSeason">MediaSeason</a></li>
<li><a href="global.html#MediaSort">MediaSort</a></li>
<li><a href="global.html#MediaSource">MediaSource</a></li>
<li><a href="global.html#MediaStatus">MediaStatus</a></li>
<li><a href="global.html#MediaTag">MediaTag</a></li>
<li><a href="global.html#MediaTitle">MediaTitle</a></li>
<li><a href="global.html#MediaType">MediaType</a></li>
<li><a href="global.html#MessageActivity">MessageActivity</a></li>
<li><a href="global.html#ModRole">ModRole</a></li>
<li><a href="global.html#NotificationOption">NotificationOption</a></li>
<li><a href="global.html#NotificationType">NotificationType</a></li>
<li><a href="global.html#PersonName">PersonName</a></li>
<li><a href="global.html#PersonRelation">PersonRelation</a></li>
<li><a href="global.html#RecommendationEntry">RecommendationEntry</a></li>
<li><a href="global.html#RecommendationList">RecommendationList</a></li>
<li><a href="global.html#RecommendationRating">RecommendationRating</a></li>
<li><a href="global.html#ScoreFormat">ScoreFormat</a></li>
<li><a href="global.html#SearchEntry">SearchEntry</a></li>
<li><a href="global.html#SingleRecommendation">SingleRecommendation</a></li>
<li><a href="global.html#StaffEntry">StaffEntry</a></li>
<li><a href="global.html#StaffName">StaffName</a></li>
<li><a href="global.html#StudioEntry">StudioEntry</a></li>
<li><a href="global.html#TextActivity">TextActivity</a></li>
<li><a href="global.html#ThreadComment">ThreadComment</a></li>
<li><a href="global.html#ThreadEntry">ThreadEntry</a></li>
<li><a href="global.html#UpdateEntryOptions">UpdateEntryOptions</a></li>
<li><a href="global.html#UpdatedEntry">UpdatedEntry</a></li>
<li><a href="global.html#UserList">UserList</a></li>
<li><a href="global.html#UserOptions">UserOptions</a></li>
<li><a href="global.html#UserOptionsInput">UserOptionsInput</a></li>
<li><a href="global.html#UserProfile">UserProfile</a></li>
<li><a href="global.html#UserRelation">UserRelation</a></li>
<li><a href="global.html#UserStaffNameLanguage">UserStaffNameLanguage</a></li>
<li><a href="global.html#UserStats">UserStats</a></li>
<li><a href="global.html#UserTitleLanguage">UserTitleLanguage</a></li>
<li><a href="global.html#headerBuilder">headerBuilder</a></li>
</ul>
</nav>
<div id="main">
<h1 class="page-title">fetcher.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>const fetch = require("node-fetch");
const { AbortController } = require("abort-controller");
/**
* Converts date object into null if year, month, and day are missing
* @private
* @param { Object } obj
* @returns { Object | null }
*/
function convertPossibleDateNull(obj) {
if (obj.year === null && obj.month === null && obj.day === null) {
return null;
}
return obj;
}
/**
* Moves data up levels in the object for better use.
* @private
* @param { Object } obj - Required. The object to edit.
* @returns { Object } Returns the edited object.
*/
function edgeRemove(obj) {
let list = [];
for (let x = 0; x < obj.length; x++) {
if (obj[x].name) {
obj[x].name = obj[x].name.english || obj[x].name.full;
}
if (obj[x].node) {
list.push(obj[x].node);
} else if (obj[x].id && obj[x].length === 1) {
list.push(obj[x].id);
} else if (obj[x].url) {
list.push(obj[x].url);
} else {
list.push(obj[x]);
}
}
if (list.length < 1) {
list = null;
}
return list;
}
/**
* Converts a fuzzyDate into a Javascript Date
* @private
* @param { fuzzyDate } fuzzyDate - Date provided by AniList's API.
* @returns { Date } Returns a date object of the data provided.
*/
function convertFuzzyDate(fuzzyDate) {
if (Object.values(fuzzyDate).some((d) => d === null)) return null;
return new Date(fuzzyDate.year, fuzzyDate.month - 1, fuzzyDate.day);
}
/**
* Formats the media data to read better.
* @private
* @param { Object } media
*/
function formatMedia(media) {
media.reviews = media.reviews.nodes.length === 0 ? null : media.reviews.nodes;
media.externalLinks = edgeRemove(media.externalLinks);
media.characters = edgeRemove(media.characters.nodes);
media.staff = edgeRemove(media.staff.nodes);
if (media.airingSchedule) {
media.airingSchedule = media.airingSchedule.nodes;
}
if (media.studios) {
media.studios = media.studios.nodes;
}
media.recommendations = media.recommendations.nodes;
media.relations = media.relations.nodes;
media.trends = media.trends.nodes;
if (media.synonyms.length < 1) {
media.synonyms = null;
}
if (media.trailer) {
switch (media.trailer.site) {
case "youtube":
media.trailer = `https://www.youtube.com/watch?v=${media.trailer.id}`;
break;
case "dailymotion":
media.trailer = `https://www.dailymotion.com/video/${media.trailer.id}`;
break;
case undefined:
media.trailer = null;
break;
default:
break;
}
}
return media;
}
module.exports = {
/**
* Send a call to the AniList API with a query and variables.
* @param { String } query
* @param { Object } variables
* @returns { Object } Returns a customized object containing all of the data fetched.
*/
send: async function (query, variables) {
if (!query) {
throw new Error("Query is not given!");
}
if (query.startsWith("mutation") && this.key === null) {
throw new Error("Function requires authenciation but no authorization found.");
}
const controller = new AbortController();
const requestTimeout = setTimeout(() => {
controller.abort();
}, this.options.timeout);
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json"
},
signal: controller.signal
};
if (this.key) {
options.headers.Authorization = `Bearer ${this.key}`;
}
if (variables) {
options.body = JSON.stringify({ query: query, variables: variables });
} else {
options.body = JSON.stringify({ query: query });
}
const response = await fetch("https://graphql.anilist.co", options)
.catch((error) => {
if (error.name === "AbortError") {
throw new Error(`ERROR: Request timed out after ${this.options.timeout}ms, is AniList up?`);
}
})
.finally(() => {
clearTimeout(requestTimeout);
});
if (response.status !== 200) {
if (response.statusText) {
throw new Error(
`ERROR: AniList API returned with a ${response.status} error code. Message: ${response.statusText}`
);
}
throw new Error(`ERROR: AniList API returned with a ${response.status} error code.`);
}
const json = await response.json();
if (Object.keys(json).length < 0) {
throw new Error("ERROR: AniList API is down. Please refer to official channels for more information.");
}
if (json.errors) {
return json.errors;
}
if (json.data.Media) {
return formatMedia(json.data.Media);
}
if (json.data.Character) {
json.data.Character.media = json.data.Character.media.nodes;
json.data.Character.dateOfBirth = convertPossibleDateNull(json.data.Character.dateOfBirth);
return json.data.Character;
}
if (json.data.Staff) {
if (json.data.Staff.description.length < 1) {
json.data.Staff.description = null;
}
json.data.Staff.dateOfBirth = convertPossibleDateNull(json.data.Staff.dateOfBirth);
json.data.Staff.dateOfDeath = convertPossibleDateNull(json.data.Staff.dateOfDeath);
json.data.Staff.staffMedia = json.data.Staff.staffMedia.nodes;
json.data.Staff.characters = json.data.Staff.characters.nodes;
json.data.Staff.characterMedia = json.data.Staff.characterMedia.nodes;
return json.data.Staff;
}
if (json.data.Page) {
if (json.data.Page.activities) {
// For list of recent activities with getRecentActivity.
return json.data.Page.activities;
}
if (json.data.Recommendation) {
// For recommendation lists.
json.data.Recommendation.recommendations = json.data.Page.recommendations;
return json.data.Recommendation;
}
return json.data.Page; // For general searching
}
if (json.data.Studio) {
json.data.Studio.media = edgeRemove(json.data.Studio.media.nodes);
return json.data.Studio;
}
if (json.data.User || json.data.Viewer) {
let userObj = json.data.User || json.data.Viewer;
if (userObj.statistics) {
//Move all names up a level.
userObj.statistics.anime.staff.forEach((e) => {
e.staff.name = e.staff.name.english;
});
userObj.statistics.anime.voiceActors.forEach((e) => {
e.voiceActor.name = e.voiceActor.name.english;
});
userObj.statistics.manga.staff.forEach((e) => {
e.staff.name = e.staff.name.english;
});
}
if (userObj.statistics && !userObj.avatar) {
return userObj.statistics;
}
//Move all node objects up one level.
userObj.favourites.anime = userObj.favourites.anime.nodes;
userObj.favourites.manga = userObj.favourites.manga.nodes;
userObj.favourites.characters = edgeRemove(userObj.favourites.characters.nodes);
userObj.favourites.staff = edgeRemove(userObj.favourites.staff.nodes);
userObj.favourites.studios = userObj.favourites.studios.nodes;
return userObj;
}
if (json.data.MediaListCollection) {
json.data.MediaListCollection.lists.forEach((list) => {
list.entries.map((entry) => {
//Media does not need to be formatted in a list query.
entry.dates = {
startedAt: convertFuzzyDate(entry.startedAt),
completedAt: convertFuzzyDate(entry.completedAt),
updatedAt: new Date(entry.updatedAt * 1000),
createdAt: entry.createdAt === 0 ? null : new Date(entry.createdAt * 1000)
};
["startedAt", "completedAt", "updatedAt", "createdAt"].forEach((e) => delete entry[e]);
});
});
return json.data.MediaListCollection.lists;
}
if (json.data.SiteStatistics) {
for (const key in json.data.SiteStatistics) {
json.data.SiteStatistics[key] = json.data.SiteStatistics[key].nodes;
for (const entry in json.data.SiteStatistics[key]) {
// Date is given in epoch time. x1000 with UTC seconds for date
json.data.SiteStatistics[key][entry].date = new Date(
json.data.SiteStatistics[key][entry].date * 1000
);
}
}
}
return json.data; //If nothing matches, return collected data
}
};
</code></pre>
</article>
</section>
</div>
<br class="clear" />
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a> using the
<a href="https://github.com/clenemt/docdash">docdash</a> theme.
</footer>
<script>
prettyPrint();
</script>
<script src="scripts/polyfill.js"></script>
<script src="scripts/linenumber.js"></script>
<script src="scripts/search.js" defer></script>
</body>
</html>