@irfanshadikrishad/anilist 1.0.5 → 1.0.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,120 +1,122 @@
1
- const currentUserQuery = `{
2
- Viewer {
3
- id name about bans siteUrl options { profileColor timezone activityMergeTime }
4
- donatorTier donatorBadge createdAt updatedAt unreadNotificationCount previousNames { name createdAt updatedAt }
5
- moderatorRoles favourites { anime { nodes { id title { romaji english } } } manga { nodes { id title { romaji english } } } }
6
- statistics { anime { count meanScore minutesWatched } manga { count chaptersRead volumesRead } }
7
- mediaListOptions { scoreFormat rowOrder animeList { sectionOrder } mangaList { sectionOrder } }
8
- }
9
- }`;
10
- const trendingQuery = `query ($page: Int, $perPage: Int) {
11
- Page(page: $page, perPage: $perPage) {
12
- media(sort: TRENDING_DESC, type: ANIME) { id title { romaji english } }
13
- }
14
- }`;
15
- const popularQuery = `query ($page: Int, $perPage: Int) {
16
- Page(page: $page, perPage: $perPage) {
17
- media(sort: POPULARITY_DESC, type: ANIME) { id title { romaji english } }
18
- }
19
- }`;
20
- const userQuery = `query ($username: String) {
21
- User(name: $username) {
22
- id name siteUrl donatorTier donatorBadge createdAt updatedAt previousNames { name createdAt updatedAt }
23
- isBlocked isFollower isFollowing options { profileColor timezone activityMergeTime }
24
- statistics { anime { count episodesWatched minutesWatched } manga { count chaptersRead volumesRead } }
25
- }
26
- }`;
27
- const currentUserAnimeList = `query ($id: Int) {
28
- MediaListCollection(userId: $id, type: ANIME) {
29
- lists { name entries { id media { id title { romaji english } } } }
30
- }
31
- }`;
32
- const currentUserMangaList = `query ($id: Int) {
33
- MediaListCollection(userId: $id, type: MANGA) {
34
- lists { name entries { id media { id title { romaji english } } } }
35
- }
36
- }`;
37
- const deleteMediaEntryMutation = `mutation($id: Int!) {
38
- DeleteMediaListEntry(id: $id) { deleted }
39
- }`;
40
- const deleteMangaEntryMutation = `mutation($id: Int) {
41
- DeleteMediaListEntry(id: $id) { deleted }
42
- }`;
43
- const upcomingAnimesQuery = `query GetNextSeasonAnime($nextSeason: MediaSeason, $nextYear: Int, $perPage: Int) {
44
- Page(perPage: $perPage) {
45
- media(season: $nextSeason, seasonYear: $nextYear, type: ANIME, sort: POPULARITY_DESC) {
46
- id title { romaji english native userPreferred } season seasonYear startDate { year month day }
47
- episodes description genres
48
- }
49
- }
50
- }`;
51
- const animeDetailsQuery = `query ($id: Int) {
52
- Media(id: $id) {
53
- id idMal title { romaji english native userPreferred } episodes nextAiringEpisode { id }
54
- duration startDate { year month day } endDate { year month day } countryOfOrigin description isAdult status season format genres siteUrl
55
- stats { scoreDistribution { score amount } statusDistribution { status amount } }
56
- }
57
- }`;
58
- const userActivityQuery = `query ($id: Int, $page: Int, $perPage: Int) {
59
- Page(page: $page, perPage: $perPage) {
60
- activities(userId: $id, type_in: [ANIME_LIST, MANGA_LIST], sort: ID_DESC) {
61
- ... on ListActivity { id status progress createdAt media { id title { romaji english } } }
62
- }
63
- }
64
- }`;
65
- const animeSearchQuery = `query ($search: String, $perPage: Int) {
66
- Page(perPage: $perPage) {
67
- media(search: $search, type: ANIME) { id title { romaji english native userPreferred } episodes status description }
68
- }
69
- }`;
70
- const mangaSearchQuery = `query ($search: String, $perPage: Int) {
71
- Page(perPage: $perPage) {
72
- media(search: $search, type: MANGA) { id title { romaji english native userPreferred } chapters status description }
73
- }
74
- }`;
75
- const activityTextQuery = `query ($userId: Int, $page: Int, $perPage: Int) {
76
- Page(page: $page, perPage: $perPage) {
77
- activities(userId: $userId, type: TEXT, sort: ID_DESC) {
78
- ... on TextActivity { id type text createdAt user { id name } }
79
- }
80
- }
81
- }`;
82
- const activityAnimeListQuery = `query ($userId: Int, $page: Int, $perPage: Int) {
83
- Page(page: $page, perPage: $perPage) {
84
- activities(userId: $userId, type: ANIME_LIST, sort: ID_DESC) {
85
- ... on ListActivity { id type status progress createdAt media { id title { romaji english native } } }
86
- }
87
- }
88
- }`;
89
- const activityMangaListQuery = `query ($userId: Int, $page: Int, $perPage: Int) {
90
- Page(page: $page, perPage: $perPage) {
91
- activities(userId: $userId, type: MANGA_LIST, sort: ID_DESC) {
92
- ... on ListActivity { id type status progress createdAt media { id title { romaji english native } } }
93
- }
94
- }
95
- }`;
96
- const activityMessageQuery = `query ($userId: Int, $page: Int, $perPage: Int) {
97
- Page(page: $page, perPage: $perPage) {
98
- activities(userId: $userId, type: MESSAGE, sort: ID_DESC) {
99
- ... on MessageActivity { id type message recipient { id name } createdAt }
100
- }
101
- }
102
- }`;
103
- const activityAllQuery = `query ($userId: Int, $page: Int, $perPage: Int) {
104
- Page(page: $page, perPage: $perPage) {
105
- activities(userId: $userId, sort: ID_DESC) {
106
- ... on TextActivity { id type text createdAt user { id name } }
107
- ... on ListActivity { id type status progress createdAt media { id title { romaji english native } } }
108
- ... on MessageActivity { id type message recipient { id name } createdAt }
109
- }
110
- }
111
- }`;
112
- const activityMediaList = `query ($userId: Int, $page: Int, $perPage: Int, $type: ActivityType) {
113
- Page(page: $page, perPage: $perPage) {
114
- pageInfo { total currentPage lastPage hasNextPage perPage }
115
- activities(userId: $userId, type: $type, sort: ID_DESC) {
116
- ... on ListActivity { id type status progress media { id title { romaji english native } format } createdAt }
117
- }
118
- }
1
+ const currentUserQuery = `{
2
+ Viewer {
3
+ id name about bans siteUrl options { profileColor timezone activityMergeTime }
4
+ donatorTier donatorBadge createdAt updatedAt unreadNotificationCount previousNames { name createdAt updatedAt }
5
+ moderatorRoles favourites { anime { nodes { id title { romaji english } } } manga { nodes { id title { romaji english } } } }
6
+ statistics { anime { count meanScore minutesWatched } manga { count chaptersRead volumesRead } }
7
+ mediaListOptions { scoreFormat rowOrder animeList { sectionOrder } mangaList { sectionOrder } }
8
+ }
9
+ }`;
10
+ const trendingQuery = `query ($page: Int, $perPage: Int) {
11
+ Page(page: $page, perPage: $perPage) {
12
+ media(sort: TRENDING_DESC, type: ANIME) { id title { romaji english } }
13
+ }
14
+ }`;
15
+ const popularQuery = `query ($page: Int, $perPage: Int) {
16
+ Page(page: $page, perPage: $perPage) {
17
+ media(sort: POPULARITY_DESC, type: ANIME) { id title { romaji english } }
18
+ }
19
+ }`;
20
+ const userQuery = `query ($username: String) {
21
+ User(name: $username) {
22
+ id name siteUrl donatorTier donatorBadge createdAt updatedAt previousNames { name createdAt updatedAt }
23
+ isBlocked isFollower isFollowing options { profileColor timezone activityMergeTime }
24
+ statistics { anime { count episodesWatched minutesWatched } manga { count chaptersRead volumesRead } }
25
+ }
26
+ }`;
27
+ const currentUserAnimeList = `query ($id: Int) {
28
+ MediaListCollection(userId: $id, type: ANIME) {
29
+ lists { name entries { id progress hiddenFromStatusLists status media { id title { romaji english } status episodes siteUrl } } }
30
+ }
31
+ }
32
+ `;
33
+ const currentUserMangaList = `query ($id: Int) {
34
+ MediaListCollection(userId: $id, type: MANGA) {
35
+ lists { name entries { id progress hiddenFromStatusLists private status media { id title { romaji english } status chapters } } }
36
+ }
37
+ }
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
+ const upcomingAnimesQuery = `query GetNextSeasonAnime($nextSeason: MediaSeason, $nextYear: Int, $perPage: Int) {
46
+ Page(perPage: $perPage) {
47
+ media(season: $nextSeason, seasonYear: $nextYear, type: ANIME, sort: POPULARITY_DESC) {
48
+ id title { romaji english native userPreferred } season seasonYear startDate { year month day }
49
+ episodes description genres
50
+ }
51
+ }
52
+ }`;
53
+ const animeDetailsQuery = `query ($id: Int) {
54
+ Media(id: $id) {
55
+ id idMal title { romaji english native userPreferred } episodes nextAiringEpisode { id }
56
+ duration startDate { year month day } endDate { year month day } countryOfOrigin description isAdult status season format genres siteUrl
57
+ stats { scoreDistribution { score amount } statusDistribution { status amount } }
58
+ }
59
+ }`;
60
+ const userActivityQuery = `query ($id: Int, $page: Int, $perPage: Int) {
61
+ Page(page: $page, perPage: $perPage) {
62
+ activities(userId: $id, type_in: [ANIME_LIST, MANGA_LIST], sort: ID_DESC) {
63
+ ... on ListActivity { id status progress createdAt media { id title { romaji english } } }
64
+ }
65
+ }
66
+ }`;
67
+ const animeSearchQuery = `query ($search: String, $perPage: Int) {
68
+ Page(perPage: $perPage) {
69
+ media(search: $search, type: ANIME) { id title { romaji english native userPreferred } episodes status description }
70
+ }
71
+ }`;
72
+ const mangaSearchQuery = `query ($search: String, $perPage: Int) {
73
+ Page(perPage: $perPage) {
74
+ media(search: $search, type: MANGA) { id title { romaji english native userPreferred } chapters status description }
75
+ }
76
+ }`;
77
+ const activityTextQuery = `query ($userId: Int, $page: Int, $perPage: Int) {
78
+ Page(page: $page, perPage: $perPage) {
79
+ activities(userId: $userId, type: TEXT, sort: ID_DESC) {
80
+ ... on TextActivity { id type text createdAt user { id name } }
81
+ }
82
+ }
83
+ }`;
84
+ const activityAnimeListQuery = `query ($userId: Int, $page: Int, $perPage: Int) {
85
+ Page(page: $page, perPage: $perPage) {
86
+ activities(userId: $userId, type: ANIME_LIST, sort: ID_DESC) {
87
+ ... on ListActivity { id type status progress createdAt media { id title { romaji english native } } }
88
+ }
89
+ }
90
+ }`;
91
+ const activityMangaListQuery = `query ($userId: Int, $page: Int, $perPage: Int) {
92
+ Page(page: $page, perPage: $perPage) {
93
+ activities(userId: $userId, type: MANGA_LIST, sort: ID_DESC) {
94
+ ... on ListActivity { id type status progress createdAt media { id title { romaji english native } } }
95
+ }
96
+ }
97
+ }`;
98
+ const activityMessageQuery = `query ($userId: Int, $page: Int, $perPage: Int) {
99
+ Page(page: $page, perPage: $perPage) {
100
+ activities(userId: $userId, type: MESSAGE, sort: ID_DESC) {
101
+ ... on MessageActivity { id type message recipient { id name } createdAt }
102
+ }
103
+ }
104
+ }`;
105
+ const activityAllQuery = `query ($userId: Int, $page: Int, $perPage: Int) {
106
+ Page(page: $page, perPage: $perPage) {
107
+ activities(userId: $userId, sort: ID_DESC) {
108
+ ... on TextActivity { id type text createdAt user { id name } }
109
+ ... on ListActivity { id type status progress createdAt media { id title { romaji english native } } }
110
+ ... on MessageActivity { id type message recipient { id name } createdAt }
111
+ }
112
+ }
113
+ }`;
114
+ const activityMediaList = `query ($userId: Int, $page: Int, $perPage: Int, $type: ActivityType) {
115
+ Page(page: $page, perPage: $perPage) {
116
+ pageInfo { total currentPage lastPage hasNextPage perPage }
117
+ activities(userId: $userId, type: $type, sort: ID_DESC) {
118
+ ... on ListActivity { id type status progress media { id title { romaji english native } format } createdAt }
119
+ }
120
+ }
119
121
  }`;
120
122
  export { currentUserQuery, trendingQuery, popularQuery, userQuery, currentUserAnimeList, currentUserMangaList, deleteMediaEntryMutation, deleteMangaEntryMutation, upcomingAnimesQuery, animeDetailsQuery, userActivityQuery, animeSearchQuery, mangaSearchQuery, activityAllQuery, activityMediaList, activityAnimeListQuery, activityMangaListQuery, activityMessageQuery, activityTextQuery, };
@@ -0,0 +1,11 @@
1
+ interface DeleteMangaResponse {
2
+ data?: {
3
+ DeleteMediaListEntry?: {
4
+ deleted?: boolean;
5
+ };
6
+ };
7
+ errors?: {
8
+ message: string;
9
+ }[];
10
+ }
11
+ export { DeleteMangaResponse };
@@ -0,0 +1 @@
1
+ export {};
@@ -14,4 +14,18 @@ declare function getNextSeasonAndYear(): {
14
14
  nextYear: number;
15
15
  };
16
16
  declare function removeHtmlAndMarkdown(input: string): string;
17
- export { aniListEndpoint, redirectUri, getTitle, getNextSeasonAndYear, formatDateObject, removeHtmlAndMarkdown, };
17
+ /**
18
+ * Export JSON as JSON
19
+ * @param js0n
20
+ * @param dataType (eg: anime/manga)
21
+ */
22
+ declare function saveJSONasJSON(js0n: object, dataType: string): Promise<void>;
23
+ /**
24
+ * Export JSON as CSV
25
+ * @param js0n
26
+ * @param dataType (eg: anime/manga)
27
+ */
28
+ declare function saveJSONasCSV(js0n: object, dataType: string): Promise<void>;
29
+ declare function importAnimeListFromExportedJSON(): Promise<void>;
30
+ declare function importMangaListFromExportedJSON(): Promise<void>;
31
+ export { aniListEndpoint, redirectUri, getTitle, getNextSeasonAndYear, formatDateObject, removeHtmlAndMarkdown, saveJSONasJSON, saveJSONasCSV, importAnimeListFromExportedJSON, importMangaListFromExportedJSON, };
@@ -1,3 +1,22 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import inquirer from "inquirer";
11
+ import open from "open";
12
+ import { join } from "path";
13
+ import { homedir } from "os";
14
+ import process from "process";
15
+ import { parse } from "json2csv";
16
+ import { writeFile, readdir, readFile } from "fs/promises";
17
+ import { currentUsersName } from "./auth.js";
18
+ import { saveAnimeWithProgressMutation, saveMangaWithProgressMutation, } from "./mutations.js";
19
+ import { fetcher } from "./fetcher.js";
1
20
  const aniListEndpoint = `https://graphql.anilist.co`;
2
21
  const redirectUri = "https://anilist.co/api/v2/oauth/pin";
3
22
  function getTitle(title) {
@@ -51,4 +70,187 @@ function removeHtmlAndMarkdown(input) {
51
70
  }
52
71
  return input;
53
72
  }
54
- export { aniListEndpoint, redirectUri, getTitle, getNextSeasonAndYear, formatDateObject, removeHtmlAndMarkdown, };
73
+ function getDownloadFolderPath() {
74
+ const homeDirectory = homedir();
75
+ // Determine the Downloads folder path based on the platform
76
+ if (process.platform === "win32") {
77
+ return join(homeDirectory, "Downloads");
78
+ }
79
+ else if (process.platform === "darwin" || process.platform === "linux") {
80
+ return join(homeDirectory, "Downloads");
81
+ }
82
+ return homeDirectory;
83
+ }
84
+ function getFormattedDate() {
85
+ const date = new Date();
86
+ const day = String(date.getDate()).padStart(2, "0");
87
+ const month = String(date.getMonth() + 1).padStart(2, "0");
88
+ const year = date.getFullYear();
89
+ const hours = String(date.getHours()).padStart(2, "0");
90
+ const minutes = String(date.getMinutes()).padStart(2, "0");
91
+ // Format as DD-MM-YYYY-HH-MM
92
+ return `${day}-${month}-${year}-${hours}-${minutes}`;
93
+ }
94
+ /**
95
+ * Export JSON as JSON
96
+ * @param js0n
97
+ * @param dataType (eg: anime/manga)
98
+ */
99
+ function saveJSONasJSON(js0n, dataType) {
100
+ return __awaiter(this, void 0, void 0, function* () {
101
+ try {
102
+ const jsonData = JSON.stringify(js0n, null, 2);
103
+ const path = join(getDownloadFolderPath(), `${yield currentUsersName()}@irfanshadikrishad-anilist-${dataType}-${getFormattedDate()}.json`);
104
+ yield writeFile(path, jsonData, "utf8");
105
+ console.log(`\nSaved as JSON successfully.`);
106
+ open(getDownloadFolderPath());
107
+ }
108
+ catch (error) {
109
+ console.error("\nError saving JSON data:", error);
110
+ }
111
+ });
112
+ }
113
+ /**
114
+ * Export JSON as CSV
115
+ * @param js0n
116
+ * @param dataType (eg: anime/manga)
117
+ */
118
+ function saveJSONasCSV(js0n, dataType) {
119
+ return __awaiter(this, void 0, void 0, function* () {
120
+ try {
121
+ const csvData = parse(js0n);
122
+ const path = join(getDownloadFolderPath(), `${yield currentUsersName()}@irfanshadikrishad-anilist-${dataType}-${getFormattedDate()}.csv`);
123
+ yield writeFile(path, csvData, "utf8");
124
+ console.log(`\nSaved as CSV successfully.`);
125
+ open(getDownloadFolderPath());
126
+ }
127
+ catch (error) {
128
+ console.error("\nError saving CSV data:", error);
129
+ }
130
+ });
131
+ }
132
+ function listFilesInDownloadFolder() {
133
+ return __awaiter(this, void 0, void 0, function* () {
134
+ const downloadFolderPath = getDownloadFolderPath();
135
+ const files = yield readdir(downloadFolderPath);
136
+ return files;
137
+ });
138
+ }
139
+ function selectFile() {
140
+ return __awaiter(this, void 0, void 0, function* () {
141
+ try {
142
+ const files = yield listFilesInDownloadFolder();
143
+ const onlyJSONfiles = files.filter((file) => file.endsWith(".json"));
144
+ if (onlyJSONfiles.length > 0) {
145
+ const answers = yield inquirer.prompt([
146
+ {
147
+ type: "list",
148
+ name: "fileName",
149
+ message: "Select a file to import:",
150
+ choices: onlyJSONfiles,
151
+ },
152
+ ]);
153
+ return answers.fileName;
154
+ }
155
+ else {
156
+ throw new Error(`\nNo importable JSON file(s) found in download folder.`);
157
+ }
158
+ }
159
+ catch (error) {
160
+ console.error("\nError selecting file:", error);
161
+ throw error;
162
+ }
163
+ });
164
+ }
165
+ function importAnimeListFromExportedJSON() {
166
+ return __awaiter(this, void 0, void 0, function* () {
167
+ try {
168
+ const filename = yield selectFile();
169
+ const filePath = join(getDownloadFolderPath(), filename);
170
+ const fileContent = yield readFile(filePath, "utf8");
171
+ const importedData = JSON.parse(fileContent);
172
+ let count = 0;
173
+ const batchSize = 1; // Number of requests in each batch
174
+ const delay = 2000; // delay to avoid rate-limiting
175
+ for (let i = 0; i < importedData.length; i += batchSize) {
176
+ const batch = importedData.slice(i, i + batchSize);
177
+ yield Promise.all(batch.map((anime) => __awaiter(this, void 0, void 0, function* () {
178
+ var _a, _b;
179
+ const query = saveAnimeWithProgressMutation;
180
+ const variables = {
181
+ mediaId: anime === null || anime === void 0 ? void 0 : anime.id,
182
+ progress: anime === null || anime === void 0 ? void 0 : anime.progress,
183
+ status: anime === null || anime === void 0 ? void 0 : anime.status,
184
+ hiddenFromStatusLists: false,
185
+ };
186
+ try {
187
+ const save = yield fetcher(query, variables);
188
+ if (save) {
189
+ const id = (_b = (_a = save === null || save === void 0 ? void 0 : save.data) === null || _a === void 0 ? void 0 : _a.SaveMediaListEntry) === null || _b === void 0 ? void 0 : _b.id;
190
+ count++;
191
+ console.log(`[${count}] ${anime === null || anime === void 0 ? void 0 : anime.id}-${id} ✅`);
192
+ }
193
+ else {
194
+ console.error(`\nError saving ${anime === null || anime === void 0 ? void 0 : anime.id}`);
195
+ }
196
+ }
197
+ catch (error) {
198
+ console.error(`\nError saving ${anime === null || anime === void 0 ? void 0 : anime.id}: ${error.message}`);
199
+ }
200
+ })));
201
+ // Avoid rate-limiting: Wait before sending the next batch
202
+ yield new Promise((resolve) => setTimeout(resolve, delay));
203
+ }
204
+ console.log(`\nTotal ${count} anime(s) imported successfully.`);
205
+ }
206
+ catch (error) {
207
+ console.error(`\n${error.message}`);
208
+ }
209
+ });
210
+ }
211
+ function importMangaListFromExportedJSON() {
212
+ return __awaiter(this, void 0, void 0, function* () {
213
+ try {
214
+ const filename = yield selectFile();
215
+ const filePath = join(getDownloadFolderPath(), filename);
216
+ const fileContent = yield readFile(filePath, "utf8");
217
+ const importedData = JSON.parse(fileContent);
218
+ let count = 0;
219
+ const batchSize = 1; // Adjust batch size as per rate-limit constraints
220
+ const delay = 2000; // 2 seconds delay to avoid rate-limit
221
+ // Process in batches
222
+ for (let i = 0; i < importedData.length; i += batchSize) {
223
+ const batch = importedData.slice(i, i + batchSize);
224
+ yield Promise.all(batch.map((manga) => __awaiter(this, void 0, void 0, function* () {
225
+ var _a, _b;
226
+ const query = saveMangaWithProgressMutation;
227
+ const variables = {
228
+ mediaId: manga === null || manga === void 0 ? void 0 : manga.id,
229
+ progress: manga === null || manga === void 0 ? void 0 : manga.progress,
230
+ status: manga === null || manga === void 0 ? void 0 : manga.status,
231
+ hiddenFromStatusLists: false,
232
+ private: manga === null || manga === void 0 ? void 0 : manga.private,
233
+ };
234
+ try {
235
+ const save = yield fetcher(query, variables);
236
+ if (save) {
237
+ const id = (_b = (_a = save === null || save === void 0 ? void 0 : save.data) === null || _a === void 0 ? void 0 : _a.SaveMediaListEntry) === null || _b === void 0 ? void 0 : _b.id;
238
+ count++;
239
+ console.log(`[${count}] ${manga === null || manga === void 0 ? void 0 : manga.id}-${id} ✅`);
240
+ }
241
+ }
242
+ catch (err) {
243
+ console.error(`\nError saving ${manga === null || manga === void 0 ? void 0 : manga.id}: ${err.message}`);
244
+ }
245
+ })));
246
+ // Avoid rate-limit by adding delay after processing each batch
247
+ yield new Promise((resolve) => setTimeout(resolve, delay));
248
+ }
249
+ console.log(`\nTotal ${count} manga(s) imported successfully.`);
250
+ }
251
+ catch (error) {
252
+ console.error(`\nError: ${error.message}`);
253
+ }
254
+ });
255
+ }
256
+ export { aniListEndpoint, redirectUri, getTitle, getNextSeasonAndYear, formatDateObject, removeHtmlAndMarkdown, saveJSONasJSON, saveJSONasCSV, importAnimeListFromExportedJSON, importMangaListFromExportedJSON, };
package/bin/index.js CHANGED
@@ -9,14 +9,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  import { Command } from "commander";
12
- import { anilistUserLogin, currentUserInfo, logoutUser, } from "./helpers/auth.js";
12
+ import process from "process";
13
+ import { anilistUserLogin, currentUserInfo, isLoggedIn, logoutUser, } from "./helpers/auth.js";
13
14
  import { deleteAnimeCollection, deleteMangaCollection, getPopular, getTrending, getUpcomingAnimes, loggedInUsersAnimeLists, loggedInUsersMangaLists, } from "./helpers/lists.js";
14
- import { getAnimeDetailsByID, getAnimeSearchResults, getMangaSearchResults, deleteUserActivities, getUserInfoByUsername, writeTextActivity, } from "./helpers/more.js";
15
+ import { deleteUserActivities, exportAnimeList, exportMangaList, getAnimeDetailsByID, getAnimeSearchResults, getMangaSearchResults, getUserInfoByUsername, importAnimeList, importMangaList, writeTextActivity, } from "./helpers/more.js";
15
16
  const cli = new Command();
16
17
  cli
17
18
  .name("anilist")
18
19
  .description("Minimalist unofficial AniList CLI for Anime and Manga Enthusiasts.")
19
- .version("1.0.5");
20
+ .version("1.0.7");
20
21
  cli
21
22
  .command("login")
22
23
  .description("Login with AniList")
@@ -159,4 +160,47 @@ cli
159
160
  .action((status) => __awaiter(void 0, void 0, void 0, function* () {
160
161
  yield writeTextActivity(status);
161
162
  }));
163
+ cli
164
+ .command("export")
165
+ .alias("exp")
166
+ .description("Export your anime or manga list.")
167
+ .option("-a, --anime", "To get the anime search results.", false)
168
+ .option("-m, --manga", "To get the manga search results.", false)
169
+ .action((_a) => __awaiter(void 0, [_a], void 0, function* ({ anime, manga }) {
170
+ if ((!anime && !manga) || (anime && manga)) {
171
+ console.error(`\nMust select an option, either --anime or --manga`);
172
+ }
173
+ else {
174
+ if (anime) {
175
+ yield exportAnimeList();
176
+ }
177
+ else if (manga) {
178
+ yield exportMangaList();
179
+ }
180
+ }
181
+ }));
182
+ cli
183
+ .command("import")
184
+ .alias("imp")
185
+ .description("Import your anime or manga from anilist or other sources.")
186
+ .option("-a, --anime", "To get the anime search results.", false)
187
+ .option("-m, --manga", "To get the manga search results.", false)
188
+ .action((_a) => __awaiter(void 0, [_a], void 0, function* ({ anime, manga }) {
189
+ if ((!anime && !manga) || (anime && manga)) {
190
+ console.error(`\nMust select an option, either --anime or --manga`);
191
+ }
192
+ else {
193
+ if (yield isLoggedIn()) {
194
+ if (anime) {
195
+ yield importAnimeList();
196
+ }
197
+ else if (manga) {
198
+ yield importMangaList();
199
+ }
200
+ }
201
+ else {
202
+ console.error(`\nPlease login to use this feature.`);
203
+ }
204
+ }
205
+ }));
162
206
  cli.parse(process.argv);
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.0.5",
5
+ "version": "1.0.7",
6
6
  "main": "./bin/index.js",
7
7
  "type": "module",
8
8
  "types": "./bin/index.d.ts",
@@ -13,11 +13,32 @@
13
13
  "access": "public"
14
14
  },
15
15
  "scripts": {
16
- "build": "rm -rf ./bin && tsc -w"
16
+ "build": "rm -rf ./bin && tsc -w",
17
+ "format": "prettier . --write",
18
+ "lint": "eslint ./dist",
19
+ "lint:fix": "eslint ./dist --fix"
17
20
  },
18
21
  "keywords": [
19
22
  "anilist",
20
- "CLI"
23
+ "CLI",
24
+ "anime",
25
+ "manga",
26
+ "anime list",
27
+ "manga list",
28
+ "anime tracker",
29
+ "manga tracker",
30
+ "anilist API",
31
+ "anime progress",
32
+ "manga progress",
33
+ "media list",
34
+ "export anime",
35
+ "import anime",
36
+ "export manga",
37
+ "import manga",
38
+ "status tracker",
39
+ "watchlist",
40
+ "reading list",
41
+ "graphql"
21
42
  ],
22
43
  "repository": {
23
44
  "type": "git",
@@ -29,12 +50,19 @@
29
50
  },
30
51
  "license": "MPL-2.0",
31
52
  "devDependencies": {
32
- "@types/node": "^22.7.6",
33
- "typescript": "^5.6.3"
53
+ "@eslint/js": "^9.13.0",
54
+ "@types/json2csv": "^5.0.7",
55
+ "@types/node": "^22.7.7",
56
+ "eslint": "^9.13.0",
57
+ "globals": "^15.11.0",
58
+ "prettier": "^3.3.3",
59
+ "typescript": "^5.6.3",
60
+ "typescript-eslint": "^8.11.0"
34
61
  },
35
62
  "dependencies": {
36
63
  "commander": "^12.1.0",
37
64
  "inquirer": "^12.0.0",
65
+ "json2csv": "^6.0.0-alpha.2",
38
66
  "node-fetch": "^3.3.2",
39
67
  "open": "^10.1.0"
40
68
  }