@irfanshadikrishad/anilist 1.4.6 → 1.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { MediaTitle } from "./types.js";
1
+ import { MediaTitle, User } from "./types.js";
2
2
  declare class Auth {
3
3
  /**
4
4
  * Get access-token from user
@@ -7,35 +7,7 @@ declare class Auth {
7
7
  static StoreAccessToken(token: string): Promise<void>;
8
8
  static RetriveAccessToken(): Promise<string | null>;
9
9
  static Login(clientId: number, clientSecret: string): Promise<void>;
10
- static Myself(): Promise<{
11
- id: number;
12
- name: string;
13
- siteUrl: string;
14
- options: {
15
- profileColor: string;
16
- timezone: string;
17
- activityMergeTime: string;
18
- };
19
- donatorTier: string;
20
- donatorBadge: string;
21
- unreadNotificationCount: number;
22
- createdAt: number;
23
- updatedAt: number;
24
- statistics: {
25
- anime: {
26
- count: number;
27
- meanScore: string;
28
- minutesWatched: string;
29
- episodesWatched: number;
30
- };
31
- manga: {
32
- count: number;
33
- meanScore: string;
34
- chaptersRead: number;
35
- volumesRead: number;
36
- };
37
- };
38
- }>;
10
+ static Myself(): Promise<User>;
39
11
  static isLoggedIn(): Promise<boolean>;
40
12
  static Logout(): Promise<void>;
41
13
  static MyUserId(): Promise<number>;
@@ -17,8 +17,8 @@ import path from "path";
17
17
  import Spinner from "tiny-spinner";
18
18
  import { fetcher } from "./fetcher.js";
19
19
  import { AniDB, AniList, MyAnimeList } from "./lists.js";
20
- import { deleteActivityMutation, saveTextActivityMutation, } from "./mutations.js";
21
- import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, toggleFollowMutation, userActivityQuery, userFollowersQuery, userFollowingQuery, } from "./queries.js";
20
+ import { deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, saveTextActivityMutation, toggleFollowMutation, } from "./mutations.js";
21
+ import { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, } from "./queries.js";
22
22
  import { responsiveOutput } from "./truncate.js";
23
23
  import { aniListEndpoint, getTitle, redirectUri, timestampToTimeAgo, } from "./workers.js";
24
24
  const home_dir = os.homedir();
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import fetch from "node-fetch";
11
11
  import { Auth } from "./auth.js";
12
- import { aniListEndpoint } from "./workers.js";
12
+ import { aniListEndpoint, handleRateLimitRetry } from "./workers.js";
13
13
  /**
14
14
  * Sends a GraphQL request to the AniList API.
15
15
  *
@@ -28,9 +28,11 @@ function fetcher(query, variables) {
28
28
  const headers = {
29
29
  "content-type": "application/json",
30
30
  };
31
- if (yield Auth.isLoggedIn()) {
32
- headers["Authorization"] = `Bearer ${yield Auth.RetriveAccessToken()}`;
33
- }
31
+ const token = (yield Auth.isLoggedIn())
32
+ ? yield Auth.RetriveAccessToken()
33
+ : null;
34
+ if (token)
35
+ headers["Authorization"] = `Bearer ${token}`;
34
36
  const request = yield fetch(aniListEndpoint, {
35
37
  method: "POST",
36
38
  headers: headers,
@@ -41,9 +43,8 @@ function fetcher(query, variables) {
41
43
  return response;
42
44
  }
43
45
  else if (request.status === 429) {
44
- console.warn("Rate limit reached. Retrying in 1 minute...");
45
- yield new Promise((resolve) => setTimeout(resolve, 60000)); // Wait for 1 minute
46
- return yield fetcher(query, variables); // Retry the request
46
+ yield handleRateLimitRetry(60);
47
+ return yield fetcher(query, variables);
47
48
  }
48
49
  else {
49
50
  console.error(`\n${request.status} ${((_b = (_a = response === null || response === void 0 ? void 0 : response.errors) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.message) || "Unknown error"}.`);
@@ -19,7 +19,7 @@ import { animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserM
19
19
  import { responsiveOutput } from "./truncate.js";
20
20
  import { AniListMediaStatus, } from "./types.js";
21
21
  import { Validate } from "./validation.js";
22
- import { anidbToanilistMapper, formatDateObject, getDownloadFolderPath, getNextSeasonAndYear, getTitle, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, selectFile, simpleDateFormat, timestampToTimeAgo, } from "./workers.js";
22
+ import { anidbToanilistMapper, formatDateObject, getDownloadFolderPath, getNextSeasonAndYear, getTitle, logUserDetails, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, selectFile, simpleDateFormat, timestampToTimeAgo, } from "./workers.js";
23
23
  class AniList {
24
24
  static importAnime() {
25
25
  return __awaiter(this, void 0, void 0, function* () {
@@ -36,8 +36,7 @@ class AniList {
36
36
  return;
37
37
  }
38
38
  let count = 0;
39
- const batchSize = 1; // Number of requests in each batch
40
- const delay = 1100; // delay to avoid rate-limiting
39
+ const batchSize = 1;
41
40
  for (let i = 0; i < importedData.length; i += batchSize) {
42
41
  const batch = importedData.slice(i, i + batchSize);
43
42
  yield Promise.all(batch.map((anime) => __awaiter(this, void 0, void 0, function* () {
@@ -64,8 +63,6 @@ class AniList {
64
63
  console.error(`\nError saving ${anime === null || anime === void 0 ? void 0 : anime.id}: ${error.message}`);
65
64
  }
66
65
  })));
67
- // Avoid rate-limiting: Wait before sending the next batch
68
- yield new Promise((resolve) => setTimeout(resolve, delay));
69
66
  }
70
67
  console.log(`\nTotal ${count} anime(s) imported successfully.`);
71
68
  }
@@ -89,9 +86,7 @@ class AniList {
89
86
  return;
90
87
  }
91
88
  let count = 0;
92
- const batchSize = 1; // Adjust batch size as per rate-limit constraints
93
- const delay = 1100; // 2 seconds delay to avoid rate-limit
94
- // Process in batches
89
+ const batchSize = 1;
95
90
  for (let i = 0; i < importedData.length; i += batchSize) {
96
91
  const batch = importedData.slice(i, i + batchSize);
97
92
  yield Promise.all(batch.map((manga) => __awaiter(this, void 0, void 0, function* () {
@@ -116,8 +111,6 @@ class AniList {
116
111
  console.error(`\nError saving ${manga === null || manga === void 0 ? void 0 : manga.id}: ${err.message}`);
117
112
  }
118
113
  })));
119
- // Avoid rate-limit by adding delay after processing each batch
120
- yield new Promise((resolve) => setTimeout(resolve, delay));
121
114
  }
122
115
  console.log(`\nTotal ${count} manga(s) imported successfully.`);
123
116
  }
@@ -621,7 +614,7 @@ class AniList {
621
614
  }
622
615
  static getUserByUsername(username) {
623
616
  return __awaiter(this, void 0, void 0, function* () {
624
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
617
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
625
618
  try {
626
619
  const response = yield fetcher(userQuery, { username });
627
620
  if (!((_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.User)) {
@@ -643,22 +636,7 @@ class AniList {
643
636
  });
644
637
  const followersCount = ((_j = (_h = (_g = req_followers === null || req_followers === void 0 ? void 0 : req_followers.data) === null || _g === void 0 ? void 0 : _g.Page) === null || _h === void 0 ? void 0 : _h.pageInfo) === null || _j === void 0 ? void 0 : _j.total) || 0;
645
638
  const followingCount = ((_m = (_l = (_k = req_following === null || req_following === void 0 ? void 0 : req_following.data) === null || _k === void 0 ? void 0 : _k.Page) === null || _l === void 0 ? void 0 : _l.pageInfo) === null || _m === void 0 ? void 0 : _m.total) || 0;
646
- console.log(`\nID:\t\t${user.id}`);
647
- console.log(`Name:\t\t${user.name}`);
648
- console.log(`Site URL:\t${user.siteUrl}`);
649
- console.log(`Donator Tier:\t${user.donatorTier}`);
650
- console.log(`Donator Badge:\t${user.donatorBadge}`);
651
- console.log(`Account Created:\t${user.createdAt ? new Date(user.createdAt * 1000).toUTCString() : "N/A"}`);
652
- console.log(`Account Updated:\t${user.updatedAt ? new Date(user.updatedAt * 1000).toUTCString() : "N/A"}`);
653
- console.log(`Blocked:\t${user.isBlocked}`);
654
- console.log(`Follower:\t${user.isFollower}`);
655
- console.log(`Following:\t${user.isFollowing}`);
656
- console.log(`Profile Color:\t${(_o = user.options) === null || _o === void 0 ? void 0 : _o.profileColor}`);
657
- console.log(`Timezone:\t${((_p = user.options) === null || _p === void 0 ? void 0 : _p.timezone) ? (_q = user.options) === null || _q === void 0 ? void 0 : _q.timezone : "N/A"}`);
658
- console.log(`\nFollowers:\t${followersCount}`);
659
- console.log(`Following:\t${followingCount}`);
660
- console.log(`\nStatistics (Anime)\n\tCount: ${((_s = (_r = user.statistics) === null || _r === void 0 ? void 0 : _r.anime) === null || _s === void 0 ? void 0 : _s.count) || 0}\tEpisodes Watched: ${((_u = (_t = user.statistics) === null || _t === void 0 ? void 0 : _t.anime) === null || _u === void 0 ? void 0 : _u.episodesWatched) || 0}\tMinutes Watched: ${((_w = (_v = user.statistics) === null || _v === void 0 ? void 0 : _v.anime) === null || _w === void 0 ? void 0 : _w.minutesWatched) || 0}`);
661
- console.log(`Statistics (Manga)\n\tCount: ${((_y = (_x = user.statistics) === null || _x === void 0 ? void 0 : _x.manga) === null || _y === void 0 ? void 0 : _y.count) || 0}\tChapters Read: ${((_0 = (_z = user.statistics) === null || _z === void 0 ? void 0 : _z.manga) === null || _0 === void 0 ? void 0 : _0.chaptersRead) || 0}\tVolumes Read: ${((_2 = (_1 = user.statistics) === null || _1 === void 0 ? void 0 : _1.manga) === null || _2 === void 0 ? void 0 : _2.volumesRead) || 0}`);
639
+ logUserDetails(user, followersCount, followingCount);
662
640
  if (activities.length > 0) {
663
641
  console.log(`\nRecent Activities:`);
664
642
  activities.forEach(({ status, progress, media, createdAt }) => {
@@ -895,8 +873,6 @@ class MyAnimeList {
895
873
  count++;
896
874
  console.log(`[${count}] ${entryId} āœ…`);
897
875
  }
898
- // Rate limit each API call to avoid server overload
899
- yield new Promise((resolve) => setTimeout(resolve, 1100));
900
876
  }
901
877
  else {
902
878
  console.error(`Could not retrieve AniList ID for MAL ID ${malId}`);
@@ -1122,8 +1098,6 @@ class AniDB {
1122
1098
  count++;
1123
1099
  responsiveOutput(`[${count}]\t${entryId} āœ…\t${anidbId}\t${anilistId}\t(${ownEpisodes}/${totalEpisodes})\t${status}–>${getStatus(status, ownEpisodes)}`);
1124
1100
  }
1125
- // Rate limit each API call to avoid server overload
1126
- // await new Promise((resolve) => setTimeout(resolve, 1100))
1127
1101
  }
1128
1102
  catch (error) {
1129
1103
  console.error(`Error processing AniDB ID ${anidbId}: ${error.message}`);
@@ -1,7 +1,10 @@
1
1
  declare const addAnimeToListMutation = "\nmutation($mediaId: Int, $status: MediaListStatus) {\n SaveMediaListEntry(mediaId: $mediaId, status: $status) { id status }\n}\n";
2
- declare const addMangaToListMutation = "\n mutation($mediaId: Int, $status: MediaListStatus) {\n SaveMediaListEntry(mediaId: $mediaId, status: $status) {\n id\n status\n media { id title { romaji english } }\n }\n }\n";
3
- declare const deleteActivityMutation = "\nmutation($id: Int!) {\n DeleteActivity(id: $id) { deleted }\n}\n";
4
- declare const saveTextActivityMutation = "\nmutation SaveTextActivity($status: String!) {\n SaveTextActivity(text: $status) { id text userId createdAt }\n}\n";
2
+ declare const addMangaToListMutation = "\n mutation($mediaId: Int, $status: MediaListStatus) {\n SaveMediaListEntry(mediaId: $mediaId, status: $status) {\n id status media { id title { romaji english } }\n }\n }\n";
3
+ declare const deleteActivityMutation = "\nmutation($id: Int!) { DeleteActivity(id: $id) { deleted } }\n";
4
+ declare const saveTextActivityMutation = "\nmutation SaveTextActivity($status: String!) { SaveTextActivity(text: $status) { id text userId createdAt } }\n";
5
5
  declare const saveAnimeWithProgressMutation = "\nmutation ($mediaId: Int, $progress: Int, $status: MediaListStatus, $hiddenFromStatusLists: Boolean) {\n SaveMediaListEntry(mediaId: $mediaId, progress: $progress, status: $status, hiddenFromStatusLists: $hiddenFromStatusLists) {\n id progress hiddenFromStatusLists\n }\n}\n";
6
6
  declare const saveMangaWithProgressMutation = "\nmutation ($mediaId: Int, $progress: Int, $status: MediaListStatus, $hiddenFromStatusLists: Boolean, $private: Boolean) {\n SaveMediaListEntry( mediaId: $mediaId, progress: $progress, status: $status, hiddenFromStatusLists: $hiddenFromStatusLists, private: $private\n ) { id progress hiddenFromStatusLists private }\n}\n";
7
- export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, };
7
+ declare const toggleFollowMutation = "mutation ($userId: Int!) { ToggleFollow(userId: $userId) { id name isFollower isFollowing } }";
8
+ declare const deleteMediaEntryMutation = "mutation($id: Int!) { DeleteMediaListEntry(id: $id) { deleted } }";
9
+ declare const deleteMangaEntryMutation = "mutation($id: Int) {\n DeleteMediaListEntry(id: $id) { deleted }\n}";
10
+ export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, toggleFollowMutation, };
@@ -6,21 +6,15 @@ mutation($mediaId: Int, $status: MediaListStatus) {
6
6
  const addMangaToListMutation = `
7
7
  mutation($mediaId: Int, $status: MediaListStatus) {
8
8
  SaveMediaListEntry(mediaId: $mediaId, status: $status) {
9
- id
10
- status
11
- media { id title { romaji english } }
9
+ id status media { id title { romaji english } }
12
10
  }
13
11
  }
14
12
  `;
15
13
  const deleteActivityMutation = `
16
- mutation($id: Int!) {
17
- DeleteActivity(id: $id) { deleted }
18
- }
14
+ mutation($id: Int!) { DeleteActivity(id: $id) { deleted } }
19
15
  `;
20
16
  const saveTextActivityMutation = `
21
- mutation SaveTextActivity($status: String!) {
22
- SaveTextActivity(text: $status) { id text userId createdAt }
23
- }
17
+ mutation SaveTextActivity($status: String!) { SaveTextActivity(text: $status) { id text userId createdAt } }
24
18
  `;
25
19
  const saveAnimeWithProgressMutation = `
26
20
  mutation ($mediaId: Int, $progress: Int, $status: MediaListStatus, $hiddenFromStatusLists: Boolean) {
@@ -35,4 +29,9 @@ mutation ($mediaId: Int, $progress: Int, $status: MediaListStatus, $hiddenFromSt
35
29
  ) { id progress hiddenFromStatusLists private }
36
30
  }
37
31
  `;
38
- export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, };
32
+ const toggleFollowMutation = `mutation ($userId: Int!) { ToggleFollow(userId: $userId) { id name isFollower isFollowing } }`;
33
+ const deleteMediaEntryMutation = `mutation($id: Int!) { DeleteMediaListEntry(id: $id) { deleted } }`;
34
+ const deleteMangaEntryMutation = `mutation($id: Int) {
35
+ DeleteMediaListEntry(id: $id) { deleted }
36
+ }`;
37
+ export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, toggleFollowMutation, };
@@ -4,8 +4,6 @@ declare const popularQuery = "query ($page: Int, $perPage: Int) {\n Page(page:
4
4
  declare const userQuery = "query ($username: String) {\n User(name: $username) {\n id name siteUrl donatorTier donatorBadge createdAt updatedAt previousNames { name createdAt updatedAt }\n isBlocked isFollower isFollowing options { profileColor timezone activityMergeTime }\n statistics { anime { count episodesWatched minutesWatched } manga { count chaptersRead volumesRead } }\n }\n}";
5
5
  declare const currentUserAnimeList = "query ($id: Int) {\n MediaListCollection(userId: $id, type: ANIME) {\n lists { name entries { id progress hiddenFromStatusLists status media { id idMal title { romaji english native userPreferred } status episodes siteUrl format } } }\n }\n}\n";
6
6
  declare const currentUserMangaList = "query ($id: Int) {\n MediaListCollection(userId: $id, type: MANGA) {\n lists { name entries { id progress hiddenFromStatusLists private status media { id idMal title { romaji english native userPreferred } status chapters } } }\n }\n}\n";
7
- declare const deleteMediaEntryMutation = "mutation($id: Int!) {\n DeleteMediaListEntry(id: $id) { deleted }\n}";
8
- declare const deleteMangaEntryMutation = "mutation($id: Int) {\n DeleteMediaListEntry(id: $id) { deleted }\n}";
9
7
  declare const upcomingAnimesQuery = "query GetNextSeasonAnime($nextSeason: MediaSeason, $nextYear: Int, $perPage: Int) {\n Page(perPage: $perPage) {\n media(season: $nextSeason, seasonYear: $nextYear, type: ANIME, sort: POPULARITY_DESC) {\n id title { romaji english native userPreferred } season seasonYear startDate { year month day }\n episodes description genres\n }\n }\n}";
10
8
  declare const animeDetailsQuery = "query ($id: Int) {\n Media(id: $id) {\n id idMal title { romaji english native userPreferred } episodes nextAiringEpisode { id }\n duration startDate { year month day } endDate { year month day } countryOfOrigin description isAdult status season format genres siteUrl\n stats { scoreDistribution { score amount } statusDistribution { status amount } }\n }\n}";
11
9
  declare const userActivityQuery = "query ($id: Int, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n activities(userId: $id, type_in: [ANIME_LIST, MANGA_LIST], sort: ID_DESC) {\n ... on ListActivity { id status progress createdAt media { id title { romaji english } } }\n }\n }\n}";
@@ -17,10 +15,9 @@ declare const activityMangaListQuery = "query ($userId: Int, $page: Int, $perPag
17
15
  declare const activityMessageQuery = "query ($userId: Int, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n activities(userId: $userId, type: MESSAGE, sort: ID_DESC) {\n ... on MessageActivity { id type message recipient { id name } createdAt }\n }\n }\n}";
18
16
  declare const activityAllQuery = "query ($userId: Int, $page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n activities(userId: $userId, sort: ID_DESC) {\n ... on TextActivity { id type text createdAt user { id name } }\n ... on ListActivity { id type status progress createdAt media { id title { romaji english native } } }\n ... on MessageActivity { id type message recipient { id name } createdAt }\n }\n }\n}";
19
17
  declare const activityMediaList = "query ($userId: Int, $page: Int, $perPage: Int, $type: ActivityType) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total currentPage lastPage hasNextPage perPage }\n activities(userId: $userId, type: $type, sort: ID_DESC) {\n ... on ListActivity { id type status progress media { id title { romaji english native } format } createdAt }\n }\n }\n}";
20
- declare const malIdToAnilistAnimeId = "query ($malId: Int) {\n Media(idMal: $malId, type: ANIME) {\n id title { romaji english } } \n}\n";
21
- declare const malIdToAnilistMangaId = "query ($malId: Int) {\n Media(idMal: $malId, type: MANGA) {\n id title { romaji english } } \n}\n";
18
+ declare const malIdToAnilistAnimeId = "query ($malId: Int) {\n Media(idMal: $malId, type: ANIME) { id title { romaji english } } }\n";
19
+ declare const malIdToAnilistMangaId = "query ($malId: Int) {\n Media(idMal: $malId, type: MANGA) { id title { romaji english } } }\n";
22
20
  declare const userFollowingQuery = "query ($userId: Int!, $page: Int) {\n Page (page: $page) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n following(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }\n }\n}\n";
23
21
  declare const userFollowersQuery = "query ($userId: Int!, $page: Int) {\n Page (page: $page) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n followers(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }\n }\n}\n";
24
- declare const toggleFollowMutation = "mutation ($userId: Int!) {\n ToggleFollow(userId: $userId) { id name isFollower isFollowing }\n}\n";
25
22
  declare const mangaDetailsQuery = "query ($id: Int) {\n Media(id: $id, type: MANGA) {\n id title { romaji english native userPreferred } coverImage { color medium large extraLarge } \n bannerImage description chapters volumes status genres\n startDate { year month day } endDate { year month day }\n }\n}\n";
26
- export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, toggleFollowMutation, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
23
+ export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
@@ -36,12 +36,6 @@ const currentUserMangaList = `query ($id: Int) {
36
36
  }
37
37
  }
38
38
  `;
39
- const deleteMediaEntryMutation = `mutation($id: Int!) {
40
- DeleteMediaListEntry(id: $id) { deleted }
41
- }`;
42
- const deleteMangaEntryMutation = `mutation($id: Int) {
43
- DeleteMediaListEntry(id: $id) { deleted }
44
- }`;
45
39
  const upcomingAnimesQuery = `query GetNextSeasonAnime($nextSeason: MediaSeason, $nextYear: Int, $perPage: Int) {
46
40
  Page(perPage: $perPage) {
47
41
  media(season: $nextSeason, seasonYear: $nextYear, type: ANIME, sort: POPULARITY_DESC) {
@@ -120,14 +114,10 @@ const activityMediaList = `query ($userId: Int, $page: Int, $perPage: Int, $type
120
114
  }
121
115
  }`;
122
116
  const malIdToAnilistAnimeId = `query ($malId: Int) {
123
- Media(idMal: $malId, type: ANIME) {
124
- id title { romaji english } }
125
- }
117
+ Media(idMal: $malId, type: ANIME) { id title { romaji english } } }
126
118
  `;
127
119
  const malIdToAnilistMangaId = `query ($malId: Int) {
128
- Media(idMal: $malId, type: MANGA) {
129
- id title { romaji english } }
130
- }
120
+ Media(idMal: $malId, type: MANGA) { id title { romaji english } } }
131
121
  `;
132
122
  const userFollowingQuery = `query ($userId: Int!, $page: Int) {
133
123
  Page (page: $page) {
@@ -143,10 +133,6 @@ const userFollowersQuery = `query ($userId: Int!, $page: Int) {
143
133
  }
144
134
  }
145
135
  `;
146
- const toggleFollowMutation = `mutation ($userId: Int!) {
147
- ToggleFollow(userId: $userId) { id name isFollower isFollowing }
148
- }
149
- `;
150
136
  const mangaDetailsQuery = `query ($id: Int) {
151
137
  Media(id: $id, type: MANGA) {
152
138
  id title { romaji english native userPreferred } coverImage { color medium large extraLarge }
@@ -155,4 +141,4 @@ const mangaDetailsQuery = `query ($id: Int) {
155
141
  }
156
142
  }
157
143
  `;
158
- export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, deleteMangaEntryMutation, deleteMediaEntryMutation, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, toggleFollowMutation, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
144
+ export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
@@ -1,12 +1,13 @@
1
+ type Error = {
2
+ message: string;
3
+ }[];
1
4
  interface DeleteMangaResponse {
2
5
  data?: {
3
6
  DeleteMediaListEntry?: {
4
7
  deleted?: boolean;
5
8
  };
6
9
  };
7
- errors?: {
8
- message: string;
9
- }[];
10
+ errors?: Error;
10
11
  }
11
12
  declare enum AniListMediaStatus {
12
13
  CURRENT = "CURRENT",
@@ -48,9 +49,7 @@ interface MalIdToAnilistIdResponse {
48
49
  title: MediaTitle;
49
50
  };
50
51
  };
51
- errors?: {
52
- message: string;
53
- }[];
52
+ errors?: Error;
54
53
  }
55
54
  interface saveAnimeWithProgressResponse {
56
55
  data?: {
@@ -60,9 +59,7 @@ interface saveAnimeWithProgressResponse {
60
59
  hiddenFromStatusLists: boolean;
61
60
  };
62
61
  };
63
- errors?: {
64
- message: string;
65
- }[];
62
+ errors?: Error;
66
63
  }
67
64
  declare enum MALAnimeStatus {
68
65
  ON_HOLD = "On-Hold",
@@ -84,9 +81,7 @@ interface AnimeList {
84
81
  lists: MediaList[];
85
82
  };
86
83
  };
87
- errors?: {
88
- message: string;
89
- }[];
84
+ errors?: Error;
90
85
  }
91
86
  interface MediaWithProgress {
92
87
  malId?: number;
@@ -125,9 +120,7 @@ interface SaveTextActivityResponse {
125
120
  createdAt: number;
126
121
  };
127
122
  };
128
- errors?: {
129
- message: string;
130
- }[];
123
+ errors?: Error;
131
124
  }
132
125
  interface MediaListCollectionResponse {
133
126
  data?: {
@@ -135,9 +128,7 @@ interface MediaListCollectionResponse {
135
128
  lists: MediaList[];
136
129
  };
137
130
  };
138
- errors?: {
139
- message: string;
140
- }[];
131
+ errors?: Error;
141
132
  }
142
133
  interface List {
143
134
  name: string;
@@ -151,39 +142,9 @@ interface MediaList {
151
142
  }
152
143
  interface Myself {
153
144
  data?: {
154
- Viewer: {
155
- id: number;
156
- name: string;
157
- siteUrl: string;
158
- options: {
159
- profileColor: string;
160
- timezone: string;
161
- activityMergeTime: string;
162
- };
163
- donatorTier: string;
164
- donatorBadge: string;
165
- unreadNotificationCount: number;
166
- createdAt: number;
167
- updatedAt: number;
168
- statistics: {
169
- anime: {
170
- count: number;
171
- meanScore: string;
172
- minutesWatched: string;
173
- episodesWatched: number;
174
- };
175
- manga: {
176
- count: number;
177
- meanScore: string;
178
- chaptersRead: number;
179
- volumesRead: number;
180
- };
181
- };
182
- };
145
+ Viewer: User;
183
146
  };
184
- errors?: {
185
- message: string;
186
- }[];
147
+ errors?: Error;
187
148
  }
188
149
  interface DateMonthYear {
189
150
  day?: number | null;
@@ -208,9 +169,7 @@ interface AnimeDetails {
208
169
  siteUrl: string;
209
170
  };
210
171
  };
211
- errors?: {
212
- message: string;
213
- }[];
172
+ errors?: Error;
214
173
  }
215
174
  interface SaveMediaListEntryResponse {
216
175
  data?: {
@@ -219,9 +178,7 @@ interface SaveMediaListEntryResponse {
219
178
  status: string;
220
179
  };
221
180
  };
222
- errors?: {
223
- message: string;
224
- }[];
181
+ errors?: Error;
225
182
  }
226
183
  interface MediaListEntry {
227
184
  id?: number;
@@ -245,55 +202,51 @@ type UserActivitiesResponse = {
245
202
  activities: Activity[];
246
203
  };
247
204
  };
248
- errors?: {
249
- message: string;
250
- }[];
205
+ errors?: Error;
251
206
  };
252
207
  type UserResponse = {
253
208
  data?: {
254
- User: {
255
- id: number;
256
- name: string;
257
- siteUrl: string;
258
- donatorTier: string;
259
- donatorBadge: string;
260
- createdAt: number;
261
- updatedAt: number;
262
- isBlocked: boolean;
263
- isFollower: boolean;
264
- isFollowing: boolean;
265
- options: {
266
- profileColor: string;
267
- timezone: string;
268
- };
269
- statistics: {
270
- anime: {
271
- count: number;
272
- episodesWatched: number;
273
- minutesWatched: number;
274
- };
275
- manga: {
276
- count: number;
277
- chaptersRead: number;
278
- volumesRead: number;
279
- };
280
- };
281
- };
209
+ User: User;
282
210
  };
283
- errors?: {
284
- message: string;
285
- }[];
211
+ errors?: Error;
212
+ };
213
+ type Avatar = {
214
+ large?: string;
215
+ medium?: string;
286
216
  };
287
217
  type User = {
288
218
  id: number;
289
- name: string;
290
- avatar: {
291
- large: string;
292
- medium: string;
219
+ name?: string;
220
+ avatar?: Avatar;
221
+ bannerImage?: string;
222
+ isFollower?: boolean;
223
+ isFollowing?: boolean;
224
+ siteUrl?: string;
225
+ donatorTier?: string;
226
+ donatorBadge?: string;
227
+ createdAt?: number;
228
+ updatedAt?: number;
229
+ isBlocked?: boolean;
230
+ unreadNotificationCount?: number;
231
+ options?: {
232
+ profileColor?: string;
233
+ timezone?: string;
234
+ activityMergeTime?: number;
235
+ };
236
+ statistics?: {
237
+ anime?: {
238
+ count?: number;
239
+ episodesWatched?: number;
240
+ minutesWatched?: number;
241
+ meanScore?: number;
242
+ };
243
+ manga?: {
244
+ count?: number;
245
+ chaptersRead?: number;
246
+ volumesRead?: number;
247
+ meanScore?: number;
248
+ };
293
249
  };
294
- bannerImage: string;
295
- isFollower: boolean;
296
- isFollowing: boolean;
297
250
  };
298
251
  type UserFollower = {
299
252
  data?: {
@@ -308,9 +261,7 @@ type UserFollower = {
308
261
  followers: User[];
309
262
  };
310
263
  };
311
- errors?: {
312
- message: string;
313
- }[];
264
+ errors?: Error;
314
265
  };
315
266
  type UserFollowing = {
316
267
  data?: {
@@ -325,9 +276,7 @@ type UserFollowing = {
325
276
  following: User[];
326
277
  };
327
278
  };
328
- errors?: {
329
- message: string;
330
- }[];
279
+ errors?: Error;
331
280
  };
332
281
  type AnimeSearchResponse = {
333
282
  data?: {
@@ -342,9 +291,7 @@ type AnimeSearchResponse = {
342
291
  }[];
343
292
  };
344
293
  };
345
- errors?: {
346
- message: string;
347
- }[];
294
+ errors?: Error;
348
295
  };
349
296
  type ToggleFollowResponse = {
350
297
  data?: {
@@ -355,9 +302,7 @@ type ToggleFollowResponse = {
355
302
  isFollowing: boolean;
356
303
  };
357
304
  };
358
- errors?: {
359
- message: string;
360
- }[];
305
+ errors?: Error;
361
306
  };
362
307
  type DeleteMediaListResponse = {
363
308
  data?: {
@@ -365,9 +310,7 @@ type DeleteMediaListResponse = {
365
310
  deleted: boolean;
366
311
  };
367
312
  };
368
- errors?: {
369
- message: string;
370
- }[];
313
+ errors?: Error;
371
314
  };
372
315
  type Activity = {
373
316
  id: number;
@@ -380,9 +323,6 @@ type Activity = {
380
323
  };
381
324
  createdAt: number;
382
325
  };
383
- type Error = {
384
- message: string;
385
- }[];
386
326
  type CoverImage = {
387
327
  color: string;
388
328
  medium: string;
@@ -1,4 +1,4 @@
1
- import { DateMonthYear, MALAnimeStatus, MALMangaStatus, MediaWithProgress } from "./types.js";
1
+ import { DateMonthYear, MALAnimeStatus, MALMangaStatus, MediaWithProgress, User } from "./types.js";
2
2
  declare const aniListEndpoint = "https://graphql.anilist.co";
3
3
  declare const redirectUri = "https://anilist.co/api/v2/oauth/pin";
4
4
  declare function getTitle(title: {
@@ -46,4 +46,6 @@ declare const anidbToanilistMapper: (romanjiName: string, year: number, englishN
46
46
  */
47
47
  declare function saveToPath(data_type: string, file_format: string): Promise<string>;
48
48
  declare function simpleDateFormat(date: DateMonthYear): string;
49
- export { anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, saveToPath, selectFile, simpleDateFormat, timestampToTimeAgo, };
49
+ declare function handleRateLimitRetry(retryCount: number): Promise<void>;
50
+ declare function logUserDetails(user: User, followersCount: number, followingCount: number): void;
51
+ export { anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, handleRateLimitRetry, logUserDetails, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, saveToPath, selectFile, simpleDateFormat, timestampToTimeAgo, };
@@ -27,12 +27,14 @@ import { homedir } from "os";
27
27
  import Papa from "papaparse";
28
28
  import { join } from "path";
29
29
  import process from "process";
30
+ import Spinner from "tiny-spinner";
30
31
  import { Auth } from "./auth.js";
31
32
  import { fetcher } from "./fetcher.js";
32
33
  import { animeSearchQuery } from "./queries.js";
33
34
  import { MALAnimeStatus, MALMangaStatus, } from "./types.js";
34
35
  const aniListEndpoint = `https://graphql.anilist.co`;
35
36
  const redirectUri = "https://anilist.co/api/v2/oauth/pin";
37
+ const spinner = new Spinner();
36
38
  function getTitle(title) {
37
39
  return (title === null || title === void 0 ? void 0 : title.english) || (title === null || title === void 0 ? void 0 : title.romaji) || "???";
38
40
  }
@@ -398,4 +400,57 @@ function simpleDateFormat(date) {
398
400
  }
399
401
  return `${date === null || date === void 0 ? void 0 : date.day}/${date === null || date === void 0 ? void 0 : date.month}/${date === null || date === void 0 ? void 0 : date.year}`;
400
402
  }
401
- export { anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, saveToPath, selectFile, simpleDateFormat, timestampToTimeAgo, };
403
+ function handleRateLimitRetry(retryCount) {
404
+ return __awaiter(this, void 0, void 0, function* () {
405
+ let seconds = Math.pow(2, retryCount) * 1000;
406
+ const maxWait = 60 * 1000;
407
+ seconds = Math.min(seconds, maxWait);
408
+ spinner.start(`Rate limit reached. Retrying in ${seconds / 1000} sec...`);
409
+ let remainingTime = seconds / 1000;
410
+ const interval = setInterval(() => {
411
+ remainingTime--;
412
+ spinner.update(`Rate limit reached. Retrying in ${remainingTime} sec...`);
413
+ if (remainingTime <= 0)
414
+ clearInterval(interval);
415
+ }, 1000);
416
+ yield new Promise((resolve) => setTimeout(resolve, seconds));
417
+ clearInterval(interval);
418
+ spinner.stop();
419
+ });
420
+ }
421
+ function formatDate(timestamp) {
422
+ return timestamp ? new Date(timestamp * 1000).toUTCString() : "N/A";
423
+ }
424
+ function logUserDetails(user, followersCount, followingCount) {
425
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
426
+ console.log("\nšŸ“Œ User Information:");
427
+ console.table({
428
+ "ID": user.id,
429
+ "Name": user.name,
430
+ "Site URL": user.siteUrl,
431
+ "Donator Tier": user.donatorTier,
432
+ "Donator Badge": user.donatorBadge,
433
+ "Account Created": formatDate(user.createdAt),
434
+ "Account Updated": formatDate(user.updatedAt),
435
+ "Blocked": user.isBlocked,
436
+ "isFollower": user.isFollower,
437
+ "isFollowing": user.isFollowing,
438
+ "Profile Color": ((_a = user.options) === null || _a === void 0 ? void 0 : _a.profileColor) || "N/A",
439
+ "Timezone": ((_b = user.options) === null || _b === void 0 ? void 0 : _b.timezone) || "N/A",
440
+ "Followers": followersCount,
441
+ "Following": followingCount,
442
+ });
443
+ console.log("\nšŸ“Š Anime Statistics:");
444
+ console.table({
445
+ "Count": ((_d = (_c = user.statistics) === null || _c === void 0 ? void 0 : _c.anime) === null || _d === void 0 ? void 0 : _d.count) || 0,
446
+ "Episodes Watched": ((_f = (_e = user.statistics) === null || _e === void 0 ? void 0 : _e.anime) === null || _f === void 0 ? void 0 : _f.episodesWatched) || 0,
447
+ "Minutes Watched": ((_h = (_g = user.statistics) === null || _g === void 0 ? void 0 : _g.anime) === null || _h === void 0 ? void 0 : _h.minutesWatched) || 0,
448
+ });
449
+ console.log("\nšŸ“– Manga Statistics:");
450
+ console.table({
451
+ "Count": ((_k = (_j = user.statistics) === null || _j === void 0 ? void 0 : _j.manga) === null || _k === void 0 ? void 0 : _k.count) || 0,
452
+ "Chapters Read": ((_m = (_l = user.statistics) === null || _l === void 0 ? void 0 : _l.manga) === null || _m === void 0 ? void 0 : _m.chaptersRead) || 0,
453
+ "Volumes Read": ((_p = (_o = user.statistics) === null || _o === void 0 ? void 0 : _o.manga) === null || _p === void 0 ? void 0 : _p.volumesRead) || 0,
454
+ });
455
+ }
456
+ export { anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, handleRateLimitRetry, logUserDetails, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, saveToPath, selectFile, simpleDateFormat, timestampToTimeAgo, };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@irfanshadikrishad/anilist",
3
3
  "description": "Minimalist unofficial AniList CLI for Anime and Manga Enthusiasts",
4
4
  "author": "Irfan Shadik Rishad",
5
- "version": "1.4.6",
5
+ "version": "1.4.7",
6
6
  "main": "./bin/index.js",
7
7
  "type": "module",
8
8
  "types": "./bin/index.d.ts",
@@ -19,7 +19,7 @@
19
19
  "format:check": "prettier . --check",
20
20
  "lint": "eslint ./dist",
21
21
  "lint:fix": "eslint ./dist --fix",
22
- "all": "npm run lint && npm run lint:fix && npm run format && npm test",
22
+ "all": "npm run build && npm run lint && npm run lint:fix && npm run format && npm test",
23
23
  "test": "jest ./tests"
24
24
  },
25
25
  "keywords": [
@@ -57,21 +57,21 @@
57
57
  "@babel/preset-env": "^7.26.9",
58
58
  "@eslint/js": "^9.23.0",
59
59
  "@types/jest": "^29.5.14",
60
- "@types/node": "^22.13.11",
60
+ "@types/node": "^22.13.14",
61
61
  "@types/papaparse": "^5.3.15",
62
62
  "@types/xml2js": "^0.4.14",
63
- "@typescript-eslint/eslint-plugin": "^8.27.0",
63
+ "@typescript-eslint/eslint-plugin": "^8.28.0",
64
64
  "eslint": "^9.23.0",
65
65
  "globals": "^16.0.0",
66
66
  "jest": "^29.7.0",
67
67
  "prettier": "^3.5.3",
68
68
  "prettier-plugin-organize-imports": "^4.1.0",
69
- "ts-jest": "^29.2.6",
69
+ "ts-jest": "^29.3.0",
70
70
  "ts-node": "^10.9.2",
71
71
  "typescript": "^5.8.2"
72
72
  },
73
73
  "dependencies": {
74
- "@irfanshadikrishad/cipher": "^1.0.6",
74
+ "@irfanshadikrishad/cipher": "^1.0.7",
75
75
  "cli-truncate": "^4.0.0",
76
76
  "commander": "^13.1.0",
77
77
  "fast-xml-parser": "^5.0.9",