@irfanshadikrishad/anilist 1.3.0-forbidden.1 → 1.3.0-forbidden.6

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.
@@ -7,16 +7,29 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ var __rest = (this && this.__rest) || function (s, e) {
11
+ var t = {};
12
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
13
+ t[p] = s[p];
14
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
15
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
16
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
17
+ t[p[i]] = s[p[i]];
18
+ }
19
+ return t;
20
+ };
10
21
  import fs from "fs";
11
22
  import { readdir, writeFile } from "fs/promises";
12
23
  import inquirer from "inquirer";
13
- import { parse } from "json2csv";
14
24
  import { createRequire } from "module";
15
25
  import open from "open";
16
26
  import { homedir } from "os";
27
+ import Papa from "papaparse";
17
28
  import { join } from "path";
18
29
  import process from "process";
19
30
  import { Auth } from "./auth.js";
31
+ import { fetcher } from "./fetcher.js";
32
+ import { animeSearchQuery } from "./queries.js";
20
33
  import { MALAnimeStatus, MALMangaStatus, } from "./types.js";
21
34
  const aniListEndpoint = `https://graphql.anilist.co`;
22
35
  const redirectUri = "https://anilist.co/api/v2/oauth/pin";
@@ -95,13 +108,13 @@ function getFormattedDate() {
95
108
  /**
96
109
  * Export JSON as JSON
97
110
  * @param js0n
98
- * @param dataType (eg: anime/manga)
111
+ * @param dataType (eg: anime|manga)
99
112
  */
100
113
  function saveJSONasJSON(js0n, dataType) {
101
114
  return __awaiter(this, void 0, void 0, function* () {
102
115
  try {
103
116
  const jsonData = JSON.stringify(js0n, null, 2);
104
- const path = join(getDownloadFolderPath(), `${yield Auth.MyUserName()}@irfanshadikrishad-anilist-${dataType}-${getFormattedDate()}.json`);
117
+ const path = yield saveToPath(dataType, ".json");
105
118
  yield writeFile(path, jsonData, "utf8");
106
119
  console.log(`\nSaved as JSON successfully.`);
107
120
  open(getDownloadFolderPath());
@@ -114,13 +127,17 @@ function saveJSONasJSON(js0n, dataType) {
114
127
  /**
115
128
  * Export JSON as CSV
116
129
  * @param js0n
117
- * @param dataType (eg: anime/manga)
130
+ * @param dataType (eg: anime|manga)
118
131
  */
119
132
  function saveJSONasCSV(js0n, dataType) {
120
133
  return __awaiter(this, void 0, void 0, function* () {
121
134
  try {
122
- const csvData = parse(js0n);
123
- const path = join(getDownloadFolderPath(), `${yield Auth.MyUserName()}@irfanshadikrishad-anilist-${dataType}-${getFormattedDate()}.csv`);
135
+ const js0n_WTAS = js0n.map((_a) => {
136
+ var { title } = _a, rest = __rest(_a, ["title"]);
137
+ return (Object.assign(Object.assign({}, rest), { title: getTitle(title) }));
138
+ });
139
+ const csvData = Papa.unparse(js0n_WTAS);
140
+ const path = yield saveToPath(dataType, ".csv");
124
141
  yield writeFile(path, csvData, "utf8");
125
142
  console.log(`\nSaved as CSV successfully.`);
126
143
  open(getDownloadFolderPath());
@@ -130,6 +147,20 @@ function saveJSONasCSV(js0n, dataType) {
130
147
  }
131
148
  });
132
149
  }
150
+ function saveJSONasXML(js0n, data_type) {
151
+ return __awaiter(this, void 0, void 0, function* () {
152
+ try {
153
+ const xmlContent = data_type === 0 ? createAnimeListXML(js0n) : createMangaListXML(js0n);
154
+ const path = yield saveToPath(data_type === 0 ? "anime" : "manga", ".xml");
155
+ yield writeFile(path, yield xmlContent, "utf8");
156
+ console.log(`\nGenerated XML for MyAnimeList.`);
157
+ open(getDownloadFolderPath());
158
+ }
159
+ catch (error) {
160
+ console.error(`Error saving XML data:`, error);
161
+ }
162
+ });
163
+ }
133
164
  function listFilesInDownloadFolder() {
134
165
  return __awaiter(this, void 0, void 0, function* () {
135
166
  const downloadFolderPath = getDownloadFolderPath();
@@ -160,59 +191,60 @@ function selectFile(fileType) {
160
191
  return answers.fileName;
161
192
  }
162
193
  else {
163
- throw new Error(`\nNo importable ${fileType} file(s) found in download folder.`);
194
+ console.error(`\nNo importable ${fileType} file(s) found in download folder.`);
195
+ return null;
164
196
  }
165
197
  }
166
198
  catch (error) {
167
199
  console.error("\nError selecting file:", error);
168
- throw error;
200
+ return null;
169
201
  }
170
202
  });
171
203
  }
172
- function createAnimeXML(malId, progress, status, episodes, title) {
173
- return `
174
- <anime>
175
- <series_animedb_id>${malId}</series_animedb_id>
176
- <series_title><![CDATA[${title}]]></series_title>
177
- <series_type>""</series_type>
178
- <series_episodes>${episodes}</series_episodes>
179
- <my_id>0</my_id>
180
- <my_watched_episodes>${progress}</my_watched_episodes>
181
- <my_start_date>0000-00-00</my_start_date>
182
- <my_finish_date>0000-00-00</my_finish_date>
183
- <my_score>0</my_score>
184
- <my_storage_value>0.00</my_storage_value>
185
- <my_status>${status}</my_status>
186
- <my_comments><![CDATA[]]></my_comments>
187
- <my_times_watched>0</my_times_watched>
188
- <my_rewatch_value></my_rewatch_value>
189
- <my_priority>LOW</my_priority>
190
- <my_tags><![CDATA[]]></my_tags>
191
- <my_rewatching>0</my_rewatching>
192
- <my_rewatching_ep>0</my_rewatching_ep>
193
- <my_discuss>0</my_discuss>
194
- <my_sns>default</my_sns>
195
- <update_on_import>1</update_on_import>
204
+ function createAnimeXML(malId, progress, status, episodes, title, format) {
205
+ return `
206
+ <anime>
207
+ <series_animedb_id>${malId}</series_animedb_id>
208
+ <series_title><![CDATA[${title}]]></series_title>
209
+ <series_type>${format}</series_type>
210
+ <series_episodes>${episodes}</series_episodes>
211
+ <my_id>0</my_id>
212
+ <my_watched_episodes>${progress}</my_watched_episodes>
213
+ <my_start_date>0000-00-00</my_start_date>
214
+ <my_finish_date>0000-00-00</my_finish_date>
215
+ <my_score>0</my_score>
216
+ <my_storage_value>0.00</my_storage_value>
217
+ <my_status>${status}</my_status>
218
+ <my_comments><![CDATA[]]></my_comments>
219
+ <my_times_watched>0</my_times_watched>
220
+ <my_rewatch_value></my_rewatch_value>
221
+ <my_priority>LOW</my_priority>
222
+ <my_tags><![CDATA[]]></my_tags>
223
+ <my_rewatching>0</my_rewatching>
224
+ <my_rewatching_ep>0</my_rewatching_ep>
225
+ <my_discuss>0</my_discuss>
226
+ <my_sns>default</my_sns>
227
+ <update_on_import>1</update_on_import>
196
228
  </anime>`;
197
229
  }
198
230
  function createMangaXML(malId, progress, status, chapters, title) {
199
- return `
200
- <manga>
201
- <manga_mangadb_id>${malId}</manga_mangadb_id>
202
- <manga_title><![CDATA[${title ? title : "unknown"}]]></manga_title>
203
- <manga_volumes>0</manga_volumes>
204
- <manga_chapters>${chapters ? chapters : 0}</manga_chapters>
205
- <my_id>0</my_id>
206
- <my_read_chapters>${progress}</my_read_chapters>
207
- <my_start_date>0000-00-00</my_start_date>
208
- <my_finish_date>0000-00-00</my_finish_date>
209
- <my_score>0</my_score>
210
- <my_status>${status}</my_status>
211
- <my_reread_value></my_reread_value>
212
- <my_priority>LOW</my_priority>
213
- <my_rereading>0</my_rereading>
214
- <my_discuss>0</my_discuss>
215
- <update_on_import>1</update_on_import>
231
+ return `
232
+ <manga>
233
+ <manga_mangadb_id>${malId}</manga_mangadb_id>
234
+ <manga_title><![CDATA[${title ? title : "unknown"}]]></manga_title>
235
+ <manga_volumes>0</manga_volumes>
236
+ <manga_chapters>${chapters ? chapters : 0}</manga_chapters>
237
+ <my_id>0</my_id>
238
+ <my_read_chapters>${progress}</my_read_chapters>
239
+ <my_start_date>0000-00-00</my_start_date>
240
+ <my_finish_date>0000-00-00</my_finish_date>
241
+ <my_score>0</my_score>
242
+ <my_status>${status}</my_status>
243
+ <my_reread_value></my_reread_value>
244
+ <my_priority>LOW</my_priority>
245
+ <my_rereading>0</my_rereading>
246
+ <my_discuss>0</my_discuss>
247
+ <update_on_import>1</update_on_import>
216
248
  </manga>`;
217
249
  }
218
250
  function createAnimeListXML(mediaWithProgress) {
@@ -224,27 +256,30 @@ function createAnimeListXML(mediaWithProgress) {
224
256
  PAUSED: MALAnimeStatus.ON_HOLD,
225
257
  DROPPED: MALAnimeStatus.DROPPED,
226
258
  };
227
- const xmlEntries = mediaWithProgress.map((anime) => {
259
+ // Filter out anime without malId
260
+ const filteredMedia = mediaWithProgress.filter((anime) => anime.malId);
261
+ const xmlEntries = filteredMedia.map((anime) => {
228
262
  const malId = anime.malId;
229
263
  const progress = anime.progress;
230
264
  const episodes = anime.episodes;
231
265
  const title = getTitle(anime.title);
232
266
  const status = statusMap[anime.status];
233
- return createAnimeXML(malId, progress, status, episodes, title);
267
+ const format = anime.format ? anime.format : "";
268
+ return createAnimeXML(malId, progress, status, episodes, title, format);
234
269
  });
235
- return `<myanimelist>
236
- <myinfo>
237
- <user_id/>
238
- <user_name>${yield Auth.MyUserName()}</user_name>
239
- <user_export_type>1</user_export_type>
240
- <user_total_anime>0</user_total_anime>
241
- <user_total_watching>0</user_total_watching>
242
- <user_total_completed>0</user_total_completed>
243
- <user_total_onhold>0</user_total_onhold>
244
- <user_total_dropped>0</user_total_dropped>
245
- <user_total_plantowatch>0</user_total_plantowatch>
246
- </myinfo>
247
- \n${xmlEntries.join("\n")}\n
270
+ return `<myanimelist>
271
+ <myinfo>
272
+ <user_id/>
273
+ <user_name>${yield Auth.MyUserName()}</user_name>
274
+ <user_export_type>1</user_export_type>
275
+ <user_total_anime>0</user_total_anime>
276
+ <user_total_watching>0</user_total_watching>
277
+ <user_total_completed>0</user_total_completed>
278
+ <user_total_onhold>0</user_total_onhold>
279
+ <user_total_dropped>0</user_total_dropped>
280
+ <user_total_plantowatch>0</user_total_plantowatch>
281
+ </myinfo>
282
+ \n${xmlEntries.join("\n")}\n
248
283
  </myanimelist>`;
249
284
  });
250
285
  }
@@ -257,7 +292,9 @@ function createMangaListXML(mediaWithProgress) {
257
292
  PAUSED: MALMangaStatus.ON_HOLD,
258
293
  DROPPED: MALMangaStatus.DROPPED,
259
294
  };
260
- const xmlEntries = mediaWithProgress.map((manga) => {
295
+ // Filter out manga without malId
296
+ const filteredMedia = mediaWithProgress.filter((manga) => manga.malId);
297
+ const xmlEntries = filteredMedia.map((manga) => {
261
298
  const malId = manga.malId;
262
299
  const progress = manga.progress;
263
300
  const chapters = manga.chapters;
@@ -265,19 +302,19 @@ function createMangaListXML(mediaWithProgress) {
265
302
  const status = statusMap[manga.status];
266
303
  return createMangaXML(malId, progress, status, chapters, title);
267
304
  });
268
- return `<myanimelist>
269
- <myinfo>
270
- <user_id/>
271
- <user_name>${yield Auth.MyUserName()}</user_name>
272
- <user_export_type>2</user_export_type>
273
- <user_total_manga>5</user_total_manga>
274
- <user_total_reading>1</user_total_reading>
275
- <user_total_completed>1</user_total_completed>
276
- <user_total_onhold>1</user_total_onhold>
277
- <user_total_dropped>1</user_total_dropped>
278
- <user_total_plantoread>1</user_total_plantoread>
279
- </myinfo>
280
- \n${xmlEntries.join("\n")}\n
305
+ return `<myanimelist>
306
+ <myinfo>
307
+ <user_id/>
308
+ <user_name>${yield Auth.MyUserName()}</user_name>
309
+ <user_export_type>2</user_export_type>
310
+ <user_total_manga>5</user_total_manga>
311
+ <user_total_reading>1</user_total_reading>
312
+ <user_total_completed>1</user_total_completed>
313
+ <user_total_onhold>1</user_total_onhold>
314
+ <user_total_dropped>1</user_total_dropped>
315
+ <user_total_plantoread>1</user_total_plantoread>
316
+ </myinfo>
317
+ \n${xmlEntries.join("\n")}\n
281
318
  </myanimelist>`;
282
319
  });
283
320
  }
@@ -314,24 +351,71 @@ function timestampToTimeAgo(timestamp) {
314
351
  return `${years} year${years === 1 ? "" : "s"} ago`;
315
352
  }
316
353
  }
317
- function activityBy(activity) {
354
+ const anidbToanilistMapper = (romanjiName, year, englishName) => __awaiter(void 0, void 0, void 0, function* () {
355
+ const fetchAnime = (search) => __awaiter(void 0, void 0, void 0, function* () {
356
+ var _a;
357
+ try {
358
+ const response = yield fetcher(animeSearchQuery, {
359
+ search,
360
+ perPage: 50,
361
+ });
362
+ return ((_a = response.data) === null || _a === void 0 ? void 0 : _a.Page.media) || [];
363
+ }
364
+ catch (error) {
365
+ console.error("Error fetching AniList data:", error);
366
+ return [];
367
+ }
368
+ });
369
+ // Search using romanjiName first
370
+ let results = yield fetchAnime(romanjiName);
371
+ // If no results, fallback to englishName
372
+ if (!results.length && englishName) {
373
+ results = yield fetchAnime(englishName);
374
+ }
375
+ // Match using year
376
+ for (const anime of results) {
377
+ if (anime.startDate.year === year) {
378
+ return anime.id;
379
+ }
380
+ }
381
+ return null;
382
+ });
383
+ function activityBy(activity, count) {
318
384
  var _a, _b, _c, _d;
385
+ const countStr = `[${count ? count : "?"}]`.padEnd(6);
319
386
  if ((_a = activity === null || activity === void 0 ? void 0 : activity.messenger) === null || _a === void 0 ? void 0 : _a.name) {
320
- return `[${activity.id}]\t${activity.messenger.name} messaged ${activity.recipient.name}`;
387
+ return `${countStr}${activity.messenger.name} >> messaged ${activity.recipient.name}`;
321
388
  }
322
389
  else if ((_c = (_b = activity === null || activity === void 0 ? void 0 : activity.media) === null || _b === void 0 ? void 0 : _b.title) === null || _c === void 0 ? void 0 : _c.userPreferred) {
323
390
  if (activity.progress) {
324
- return `[${activity.id}]\t${activity.status} ${activity.progress} of ${activity.media.title.userPreferred}`;
391
+ return `${countStr}${activity.user.name} >> ${activity.status} ${activity.progress} of ${activity.media.title.userPreferred}`;
325
392
  }
326
393
  else {
327
- return `[${activity.id}]\t${activity.status} ${activity.media.title.userPreferred}`;
394
+ return `${countStr}${activity.user.name} >> ${activity.status} ${activity.media.title.userPreferred}`;
328
395
  }
329
396
  }
330
397
  else if ((_d = activity === null || activity === void 0 ? void 0 : activity.user) === null || _d === void 0 ? void 0 : _d.name) {
331
- return `[${activity.id}]\t${activity.user.name}`;
398
+ return `${countStr}${activity.user.name}`;
332
399
  }
333
400
  else {
334
- return `[${activity === null || activity === void 0 ? void 0 : activity.id}] ???`;
401
+ return `${countStr}???`;
402
+ }
403
+ }
404
+ /**
405
+ * Extract the save file path
406
+ * @param data_type - anime|manga
407
+ * @param file_format - save format (eg: .json|.csv)
408
+ * @returns string of file path
409
+ */
410
+ function saveToPath(data_type, file_format) {
411
+ return __awaiter(this, void 0, void 0, function* () {
412
+ return join(getDownloadFolderPath(), `${yield Auth.MyUserName()}@irfanshadikrishad-anilist-${data_type}-${getFormattedDate()}.${file_format}`);
413
+ });
414
+ }
415
+ function simpleDateFormat(date) {
416
+ if (!date.day && !date.month && !date.year) {
417
+ return `null`;
335
418
  }
419
+ 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}`;
336
420
  }
337
- export { activityBy, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, selectFile, timestampToTimeAgo, };
421
+ export { activityBy, anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, saveToPath, selectFile, simpleDateFormat, timestampToTimeAgo, };
package/bin/index.js CHANGED
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  import { Command } from "commander";
12
12
  import process from "process";
13
- import { Auth } from "./helpers/auth.js";
13
+ import { Auth, Social } from "./helpers/auth.js";
14
14
  import { AniList } from "./helpers/lists.js";
15
15
  import { getCurrentPackageVersion } from "./helpers/workers.js";
16
16
  const cli = new Command();
@@ -128,6 +128,13 @@ cli
128
128
  console.error(`\nInvalid or missing ID (${id}). Please provide a valid numeric ID.`);
129
129
  }
130
130
  }));
131
+ cli
132
+ .command("manga <id>")
133
+ .description("Get manga details by their ID")
134
+ .option("-c, --count <number>", "Number of items to get", "10")
135
+ .action((id) => __awaiter(void 0, void 0, void 0, function* () {
136
+ yield AniList.getMangaDetailsByID(id);
137
+ }));
131
138
  cli
132
139
  .command("search <query>")
133
140
  .alias("srch")
@@ -210,4 +217,28 @@ cli
210
217
  .action(() => __awaiter(void 0, void 0, void 0, function* () {
211
218
  yield Auth.AutoLike();
212
219
  }));
220
+ cli
221
+ .command("social")
222
+ .alias("sol")
223
+ .description("Automate your process")
224
+ .option("-f, --follow", "Follow the user whos following you.", false)
225
+ .option("-u, --unfollow", "Unfollow the user whos not following you.", false)
226
+ .action((_a) => __awaiter(void 0, [_a], void 0, function* ({ follow, unfollow }) {
227
+ if (!follow && !unfollow) {
228
+ console.error(`\nMust select an option, either --follow or --unfollow`);
229
+ }
230
+ else {
231
+ if (yield Auth.isLoggedIn()) {
232
+ if (follow) {
233
+ yield Social.follow();
234
+ }
235
+ else if (unfollow) {
236
+ yield Social.unfollow();
237
+ }
238
+ }
239
+ else {
240
+ console.error(`\nPlease login to use this feature.`);
241
+ }
242
+ }
243
+ }));
213
244
  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.3.0-forbidden.1",
5
+ "version": "1.3.0-forbidden.6",
6
6
  "main": "./bin/index.js",
7
7
  "type": "module",
8
8
  "types": "./bin/index.d.ts",
@@ -14,12 +14,12 @@
14
14
  },
15
15
  "scripts": {
16
16
  "build": "rm -rf ./bin && tsc",
17
- "buildw": "rm -rf ./bin && tsc -w",
17
+ "build:watch": "rm -rf ./bin && tsc -w",
18
18
  "format": "prettier . --write",
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": [
@@ -54,27 +54,33 @@
54
54
  },
55
55
  "license": "MPL-2.0",
56
56
  "devDependencies": {
57
- "@eslint/js": "^9.17.0",
57
+ "@babel/preset-env": "^7.26.9",
58
+ "@eslint/js": "^9.22.0",
58
59
  "@types/jest": "^29.5.14",
59
- "@types/json2csv": "^5.0.7",
60
- "@types/node": "^22.10.5",
61
- "eslint": "^9.17.0",
62
- "globals": "^15.14.0",
60
+ "@types/node": "^22.13.10",
61
+ "@types/papaparse": "^5.3.15",
62
+ "@types/xml2js": "^0.4.14",
63
+ "@typescript-eslint/eslint-plugin": "^8.26.1",
64
+ "eslint": "^9.22.0",
65
+ "globals": "^16.0.0",
63
66
  "jest": "^29.7.0",
64
- "prettier": "^3.4.2",
67
+ "prettier": "^3.5.3",
65
68
  "prettier-plugin-organize-imports": "^4.1.0",
66
- "ts-jest": "^29.2.5",
69
+ "ts-jest": "^29.2.6",
67
70
  "ts-node": "^10.9.2",
68
- "typescript": "^5.7.2",
69
- "@babel/preset-env": "^7.26.0",
70
- "@typescript-eslint/eslint-plugin": "^8.19.1"
71
+ "typescript": "^5.8.2"
71
72
  },
72
73
  "dependencies": {
73
- "commander": "^13.0.0",
74
- "fast-xml-parser": "^4.5.1",
75
- "inquirer": "^12.3.0",
76
- "json2csv": "^6.0.0-alpha.2",
74
+ "@irfanshadikrishad/cipher": "^1.0.6",
75
+ "cli-truncate": "^4.0.0",
76
+ "commander": "^13.1.0",
77
+ "fast-xml-parser": "^5.0.9",
78
+ "inquirer": "^12.5.0",
79
+ "jsonrepair": "^3.12.0",
77
80
  "node-fetch": "^3.3.2",
78
- "open": "^10.1.0"
81
+ "open": "^10.1.0",
82
+ "papaparse": "^5.5.2",
83
+ "tiny-spinner": "^2.0.5",
84
+ "xml2js": "^0.6.2"
79
85
  }
80
86
  }