@irfanshadikrishad/anilist 1.0.11 → 1.1.0-forbidden.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.
- package/LICENSE +373 -382
- package/LICENSE.md +382 -0
- package/README.md +87 -58
- package/bin/helpers/auth.d.ts +24 -8
- package/bin/helpers/auth.js +661 -258
- package/bin/helpers/fetcher.d.ts +2 -1
- package/bin/helpers/fetcher.js +14 -7
- package/bin/helpers/lists.d.ts +5 -1
- package/bin/helpers/lists.js +516 -405
- package/bin/helpers/mutations.d.ts +8 -4
- package/bin/helpers/mutations.js +14 -10
- package/bin/helpers/queries.d.ts +13 -9
- package/bin/helpers/queries.js +62 -16
- package/bin/helpers/truncate.d.ts +6 -0
- package/bin/helpers/truncate.js +10 -0
- package/bin/helpers/types.d.ts +319 -28
- package/bin/helpers/validation.d.ts +29 -0
- package/bin/helpers/validation.js +117 -0
- package/bin/helpers/workers.d.ts +24 -9
- package/bin/helpers/workers.js +210 -16
- package/bin/index.js +43 -4
- package/package.json +29 -16
- package/assets/binance.jpg +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
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 { parseStringPromise } from "xml2js";
|
|
11
|
+
class Validate {
|
|
12
|
+
/**
|
|
13
|
+
* Validate importable JSON file
|
|
14
|
+
* @param data string
|
|
15
|
+
* @returns boolean
|
|
16
|
+
*/
|
|
17
|
+
static Import_JSON(data) {
|
|
18
|
+
return (Array.isArray(data) &&
|
|
19
|
+
data.every((item) => typeof item === "object" && item !== null && "id" in item));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Validate if MyAnimeList Anime XML file is valid or not
|
|
23
|
+
* @param xmlData string
|
|
24
|
+
* @returns boolean
|
|
25
|
+
*/
|
|
26
|
+
static Import_AnimeXML(xmlData) {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
try {
|
|
29
|
+
const result = yield parseStringPromise(xmlData, { explicitArray: false });
|
|
30
|
+
if (!result || !result.myanimelist) {
|
|
31
|
+
console.error("Invalid XML structure: Missing 'myanimelist' root element.");
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const animeList = result.myanimelist.anime;
|
|
35
|
+
if (!animeList) {
|
|
36
|
+
console.error("Invalid XML structure: Missing 'anime' elements.");
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const animeArray = Array.isArray(animeList) ? animeList : [animeList];
|
|
40
|
+
const isValid = animeArray.every((anime) => {
|
|
41
|
+
const isValidId = anime.series_animedb_id && !isNaN(Number(anime.series_animedb_id));
|
|
42
|
+
const hasRequiredFields = anime.series_title && anime.my_status;
|
|
43
|
+
return isValidId && hasRequiredFields;
|
|
44
|
+
});
|
|
45
|
+
if (!isValid) {
|
|
46
|
+
console.error("Validation failed: Some anime entries are missing required fields or have invalid IDs.");
|
|
47
|
+
}
|
|
48
|
+
return isValid;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error("Error parsing or validating XML:", error);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Validate if MyAnimeList Anime XML file is valid or not
|
|
58
|
+
* @param xmlData string
|
|
59
|
+
* @returns boolean
|
|
60
|
+
*/
|
|
61
|
+
static Import_MangaXML(xmlData) {
|
|
62
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
+
try {
|
|
64
|
+
const result = yield parseStringPromise(xmlData, { explicitArray: false });
|
|
65
|
+
if (!result || !result.myanimelist) {
|
|
66
|
+
console.error("Invalid XML structure: Missing 'myanimelist' root element.");
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
const mangaList = result.myanimelist.manga;
|
|
70
|
+
if (!mangaList) {
|
|
71
|
+
console.error("Invalid XML structure: Missing 'manga' elements.");
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
const mangaArray = Array.isArray(mangaList) ? mangaList : [mangaList];
|
|
75
|
+
const isValid = mangaArray.every((manga) => {
|
|
76
|
+
const isValidId = manga.manga_mangadb_id && !isNaN(Number(manga.manga_mangadb_id));
|
|
77
|
+
const hasRequiredFields = manga.manga_title && manga.my_status;
|
|
78
|
+
return isValidId && hasRequiredFields;
|
|
79
|
+
});
|
|
80
|
+
if (!isValid) {
|
|
81
|
+
console.error("Validation failed: Some manga entries are missing required fields or have invalid IDs.");
|
|
82
|
+
}
|
|
83
|
+
return isValid;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.error("Error parsing or validating XML:", error);
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Validate AniDB json-large file
|
|
93
|
+
* @param file string of anidb json-large
|
|
94
|
+
* @returns boolean
|
|
95
|
+
*/
|
|
96
|
+
static Import_AniDBJSONLarge(file) {
|
|
97
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
+
try {
|
|
99
|
+
if (!(file === null || file === void 0 ? void 0 : file.trim())) {
|
|
100
|
+
console.error("File content is empty or invalid.");
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const obj3ct = JSON.parse(file);
|
|
104
|
+
if (!obj3ct || !Array.isArray(obj3ct.anime)) {
|
|
105
|
+
console.error("Invalid JSON structure: Missing or malformed 'anime' array.");
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
console.error("Failed to parse JSON file:", error);
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export { Validate };
|
package/bin/helpers/workers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MALAnimeStatus, MALMangaStatus, MediaWithProgress } from "./types.js";
|
|
1
|
+
import { DateMonthYear, MALAnimeStatus, MALMangaStatus, MediaWithProgress, TheActivity, 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: {
|
|
@@ -6,9 +6,9 @@ declare function getTitle(title: {
|
|
|
6
6
|
romaji?: string;
|
|
7
7
|
}): string;
|
|
8
8
|
declare function formatDateObject(dateObj: {
|
|
9
|
-
day?:
|
|
10
|
-
month?:
|
|
11
|
-
year?:
|
|
9
|
+
day?: number;
|
|
10
|
+
month?: number;
|
|
11
|
+
year?: number;
|
|
12
12
|
} | null): string;
|
|
13
13
|
declare function getNextSeasonAndYear(): {
|
|
14
14
|
nextSeason: string;
|
|
@@ -20,18 +20,33 @@ declare function getFormattedDate(): string;
|
|
|
20
20
|
/**
|
|
21
21
|
* Export JSON as JSON
|
|
22
22
|
* @param js0n
|
|
23
|
-
* @param dataType (eg: anime
|
|
23
|
+
* @param dataType (eg: anime|manga)
|
|
24
24
|
*/
|
|
25
25
|
declare function saveJSONasJSON(js0n: object, dataType: string): Promise<void>;
|
|
26
26
|
/**
|
|
27
27
|
* Export JSON as CSV
|
|
28
28
|
* @param js0n
|
|
29
|
-
* @param dataType (eg: anime
|
|
29
|
+
* @param dataType (eg: anime|manga)
|
|
30
30
|
*/
|
|
31
|
-
declare function saveJSONasCSV(js0n:
|
|
31
|
+
declare function saveJSONasCSV(js0n: MediaWithProgress[], dataType: string): Promise<void>;
|
|
32
|
+
declare function saveJSONasXML(js0n: MediaWithProgress[], data_type: 0 | 1): Promise<void>;
|
|
32
33
|
declare function selectFile(fileType: string): Promise<string>;
|
|
33
|
-
declare function createAnimeXML(malId: number, progress: number, status: MALAnimeStatus, episodes: number, title: string): string;
|
|
34
|
+
declare function createAnimeXML(malId: number, progress: number, status: MALAnimeStatus, episodes: number, title: string, format: string): string;
|
|
34
35
|
declare function createMangaXML(malId: number, progress: number, status: MALMangaStatus, chapters: number, title: string): string;
|
|
35
36
|
declare function createAnimeListXML(mediaWithProgress: MediaWithProgress[]): Promise<string>;
|
|
36
37
|
declare function createMangaListXML(mediaWithProgress: MediaWithProgress[]): Promise<string>;
|
|
37
|
-
|
|
38
|
+
declare function getCurrentPackageVersion(): string | null;
|
|
39
|
+
declare function timestampToTimeAgo(timestamp: number): string;
|
|
40
|
+
declare const anidbToanilistMapper: (romanjiName: string, year: number, englishName?: string) => Promise<number | null>;
|
|
41
|
+
declare function activityBy(activity: TheActivity, count?: number): string;
|
|
42
|
+
/**
|
|
43
|
+
* Extract the save file path
|
|
44
|
+
* @param data_type - anime|manga
|
|
45
|
+
* @param file_format - save format (eg: .json|.csv)
|
|
46
|
+
* @returns string of file path
|
|
47
|
+
*/
|
|
48
|
+
declare function saveToPath(data_type: string, file_format: string): Promise<string>;
|
|
49
|
+
declare function simpleDateFormat(date: DateMonthYear): string;
|
|
50
|
+
declare function handleRateLimitRetry(retryCount: number): Promise<void>;
|
|
51
|
+
declare function logUserDetails(user: User, followersCount: number, followingCount: number): void;
|
|
52
|
+
export { activityBy, anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, handleRateLimitRetry, logUserDetails, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, saveToPath, selectFile, simpleDateFormat, timestampToTimeAgo, };
|
package/bin/helpers/workers.js
CHANGED
|
@@ -7,18 +7,34 @@ 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 {
|
|
24
|
+
import { createRequire } from "module";
|
|
14
25
|
import open from "open";
|
|
15
26
|
import { homedir } from "os";
|
|
27
|
+
import Papa from "papaparse";
|
|
16
28
|
import { join } from "path";
|
|
17
29
|
import process from "process";
|
|
30
|
+
import Spinner from "tiny-spinner";
|
|
18
31
|
import { Auth } from "./auth.js";
|
|
19
|
-
import {
|
|
32
|
+
import { fetcher } from "./fetcher.js";
|
|
33
|
+
import { animeSearchQuery } from "./queries.js";
|
|
34
|
+
import { MALAnimeStatus, MALMangaStatus, } from "./types.js";
|
|
20
35
|
const aniListEndpoint = `https://graphql.anilist.co`;
|
|
21
36
|
const redirectUri = "https://anilist.co/api/v2/oauth/pin";
|
|
37
|
+
const spinner = new Spinner();
|
|
22
38
|
function getTitle(title) {
|
|
23
39
|
return (title === null || title === void 0 ? void 0 : title.english) || (title === null || title === void 0 ? void 0 : title.romaji) || "???";
|
|
24
40
|
}
|
|
@@ -94,13 +110,13 @@ function getFormattedDate() {
|
|
|
94
110
|
/**
|
|
95
111
|
* Export JSON as JSON
|
|
96
112
|
* @param js0n
|
|
97
|
-
* @param dataType (eg: anime
|
|
113
|
+
* @param dataType (eg: anime|manga)
|
|
98
114
|
*/
|
|
99
115
|
function saveJSONasJSON(js0n, dataType) {
|
|
100
116
|
return __awaiter(this, void 0, void 0, function* () {
|
|
101
117
|
try {
|
|
102
118
|
const jsonData = JSON.stringify(js0n, null, 2);
|
|
103
|
-
const path =
|
|
119
|
+
const path = yield saveToPath(dataType, ".json");
|
|
104
120
|
yield writeFile(path, jsonData, "utf8");
|
|
105
121
|
console.log(`\nSaved as JSON successfully.`);
|
|
106
122
|
open(getDownloadFolderPath());
|
|
@@ -113,13 +129,17 @@ function saveJSONasJSON(js0n, dataType) {
|
|
|
113
129
|
/**
|
|
114
130
|
* Export JSON as CSV
|
|
115
131
|
* @param js0n
|
|
116
|
-
* @param dataType (eg: anime
|
|
132
|
+
* @param dataType (eg: anime|manga)
|
|
117
133
|
*/
|
|
118
134
|
function saveJSONasCSV(js0n, dataType) {
|
|
119
135
|
return __awaiter(this, void 0, void 0, function* () {
|
|
120
136
|
try {
|
|
121
|
-
const
|
|
122
|
-
|
|
137
|
+
const js0n_WTAS = js0n.map((_a) => {
|
|
138
|
+
var { title } = _a, rest = __rest(_a, ["title"]);
|
|
139
|
+
return (Object.assign(Object.assign({}, rest), { title: getTitle(title) }));
|
|
140
|
+
});
|
|
141
|
+
const csvData = Papa.unparse(js0n_WTAS);
|
|
142
|
+
const path = yield saveToPath(dataType, ".csv");
|
|
123
143
|
yield writeFile(path, csvData, "utf8");
|
|
124
144
|
console.log(`\nSaved as CSV successfully.`);
|
|
125
145
|
open(getDownloadFolderPath());
|
|
@@ -129,6 +149,20 @@ function saveJSONasCSV(js0n, dataType) {
|
|
|
129
149
|
}
|
|
130
150
|
});
|
|
131
151
|
}
|
|
152
|
+
function saveJSONasXML(js0n, data_type) {
|
|
153
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
154
|
+
try {
|
|
155
|
+
const xmlContent = data_type === 0 ? createAnimeListXML(js0n) : createMangaListXML(js0n);
|
|
156
|
+
const path = yield saveToPath(data_type === 0 ? "anime" : "manga", ".xml");
|
|
157
|
+
yield writeFile(path, yield xmlContent, "utf8");
|
|
158
|
+
console.log(`\nGenerated XML for MyAnimeList.`);
|
|
159
|
+
open(getDownloadFolderPath());
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
console.error(`Error saving XML data:`, error);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
132
166
|
function listFilesInDownloadFolder() {
|
|
133
167
|
return __awaiter(this, void 0, void 0, function* () {
|
|
134
168
|
const downloadFolderPath = getDownloadFolderPath();
|
|
@@ -140,9 +174,10 @@ function selectFile(fileType) {
|
|
|
140
174
|
return __awaiter(this, void 0, void 0, function* () {
|
|
141
175
|
try {
|
|
142
176
|
const files = yield listFilesInDownloadFolder();
|
|
177
|
+
console.log(getDownloadFolderPath());
|
|
143
178
|
// Filter to include only files, not directories, with the specified extension
|
|
144
179
|
const onlyFiles = files.filter((file) => {
|
|
145
|
-
const filePath =
|
|
180
|
+
const filePath = `${getDownloadFolderPath()}/${file}`; // Adjust this to the correct path
|
|
146
181
|
const isFile = fs.lstatSync(filePath).isFile(); // Check if it's a file
|
|
147
182
|
return isFile && file.endsWith(fileType);
|
|
148
183
|
});
|
|
@@ -158,21 +193,22 @@ function selectFile(fileType) {
|
|
|
158
193
|
return answers.fileName;
|
|
159
194
|
}
|
|
160
195
|
else {
|
|
161
|
-
|
|
196
|
+
console.error(`\nNo importable ${fileType} file(s) found in download folder.`);
|
|
197
|
+
return null;
|
|
162
198
|
}
|
|
163
199
|
}
|
|
164
200
|
catch (error) {
|
|
165
201
|
console.error("\nError selecting file:", error);
|
|
166
|
-
|
|
202
|
+
return null;
|
|
167
203
|
}
|
|
168
204
|
});
|
|
169
205
|
}
|
|
170
|
-
function createAnimeXML(malId, progress, status, episodes, title) {
|
|
206
|
+
function createAnimeXML(malId, progress, status, episodes, title, format) {
|
|
171
207
|
return `
|
|
172
208
|
<anime>
|
|
173
209
|
<series_animedb_id>${malId}</series_animedb_id>
|
|
174
210
|
<series_title><![CDATA[${title}]]></series_title>
|
|
175
|
-
<series_type
|
|
211
|
+
<series_type>${format}</series_type>
|
|
176
212
|
<series_episodes>${episodes}</series_episodes>
|
|
177
213
|
<my_id>0</my_id>
|
|
178
214
|
<my_watched_episodes>${progress}</my_watched_episodes>
|
|
@@ -222,13 +258,16 @@ function createAnimeListXML(mediaWithProgress) {
|
|
|
222
258
|
PAUSED: MALAnimeStatus.ON_HOLD,
|
|
223
259
|
DROPPED: MALAnimeStatus.DROPPED,
|
|
224
260
|
};
|
|
225
|
-
|
|
261
|
+
// Filter out anime without malId
|
|
262
|
+
const filteredMedia = mediaWithProgress.filter((anime) => anime.malId);
|
|
263
|
+
const xmlEntries = filteredMedia.map((anime) => {
|
|
226
264
|
const malId = anime.malId;
|
|
227
265
|
const progress = anime.progress;
|
|
228
266
|
const episodes = anime.episodes;
|
|
229
267
|
const title = getTitle(anime.title);
|
|
230
268
|
const status = statusMap[anime.status];
|
|
231
|
-
|
|
269
|
+
const format = anime.format ? anime.format : "";
|
|
270
|
+
return createAnimeXML(malId, progress, status, episodes, title, format);
|
|
232
271
|
});
|
|
233
272
|
return `<myanimelist>
|
|
234
273
|
<myinfo>
|
|
@@ -255,7 +294,9 @@ function createMangaListXML(mediaWithProgress) {
|
|
|
255
294
|
PAUSED: MALMangaStatus.ON_HOLD,
|
|
256
295
|
DROPPED: MALMangaStatus.DROPPED,
|
|
257
296
|
};
|
|
258
|
-
|
|
297
|
+
// Filter out manga without malId
|
|
298
|
+
const filteredMedia = mediaWithProgress.filter((manga) => manga.malId);
|
|
299
|
+
const xmlEntries = filteredMedia.map((manga) => {
|
|
259
300
|
const malId = manga.malId;
|
|
260
301
|
const progress = manga.progress;
|
|
261
302
|
const chapters = manga.chapters;
|
|
@@ -279,4 +320,157 @@ function createMangaListXML(mediaWithProgress) {
|
|
|
279
320
|
</myanimelist>`;
|
|
280
321
|
});
|
|
281
322
|
}
|
|
282
|
-
|
|
323
|
+
function getCurrentPackageVersion() {
|
|
324
|
+
const require = createRequire(import.meta.url);
|
|
325
|
+
const packageJson = require("../../package.json");
|
|
326
|
+
const version = packageJson.version;
|
|
327
|
+
return version || null;
|
|
328
|
+
}
|
|
329
|
+
function timestampToTimeAgo(timestamp) {
|
|
330
|
+
const now = Math.floor(Date.now() / 1000);
|
|
331
|
+
const elapsed = now - timestamp;
|
|
332
|
+
if (elapsed < 60) {
|
|
333
|
+
return `${elapsed} second${elapsed === 1 ? "" : "s"} ago`;
|
|
334
|
+
}
|
|
335
|
+
else if (elapsed < 3600) {
|
|
336
|
+
const minutes = Math.floor(elapsed / 60);
|
|
337
|
+
return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
|
|
338
|
+
}
|
|
339
|
+
else if (elapsed < 86400) {
|
|
340
|
+
const hours = Math.floor(elapsed / 3600);
|
|
341
|
+
return `${hours} hour${hours === 1 ? "" : "s"} ago`;
|
|
342
|
+
}
|
|
343
|
+
else if (elapsed < 2592000) {
|
|
344
|
+
const days = Math.floor(elapsed / 86400);
|
|
345
|
+
return `${days} day${days === 1 ? "" : "s"} ago`;
|
|
346
|
+
}
|
|
347
|
+
else if (elapsed < 31536000) {
|
|
348
|
+
const months = Math.floor(elapsed / 2592000);
|
|
349
|
+
return `${months} month${months === 1 ? "" : "s"} ago`;
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
const years = Math.floor(elapsed / 31536000);
|
|
353
|
+
return `${years} year${years === 1 ? "" : "s"} ago`;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
const anidbToanilistMapper = (romanjiName, year, englishName) => __awaiter(void 0, void 0, void 0, function* () {
|
|
357
|
+
const fetchAnime = (search) => __awaiter(void 0, void 0, void 0, function* () {
|
|
358
|
+
var _a;
|
|
359
|
+
try {
|
|
360
|
+
const response = yield fetcher(animeSearchQuery, {
|
|
361
|
+
search,
|
|
362
|
+
perPage: 50,
|
|
363
|
+
});
|
|
364
|
+
return ((_a = response.data) === null || _a === void 0 ? void 0 : _a.Page.media) || [];
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
console.error("Error fetching AniList data:", error);
|
|
368
|
+
return [];
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
// Search using romanjiName first
|
|
372
|
+
let results = yield fetchAnime(romanjiName);
|
|
373
|
+
// If no results, fallback to englishName
|
|
374
|
+
if (!results.length && englishName) {
|
|
375
|
+
results = yield fetchAnime(englishName);
|
|
376
|
+
}
|
|
377
|
+
// Match using year
|
|
378
|
+
for (const anime of results) {
|
|
379
|
+
if (anime.startDate.year === year) {
|
|
380
|
+
return anime.id;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
});
|
|
385
|
+
function activityBy(activity, count) {
|
|
386
|
+
var _a, _b, _c, _d;
|
|
387
|
+
const countStr = `[${count ? count : "?"}]`.padEnd(6);
|
|
388
|
+
if ((_a = activity === null || activity === void 0 ? void 0 : activity.messenger) === null || _a === void 0 ? void 0 : _a.name) {
|
|
389
|
+
return `${countStr}${activity.messenger.name} >> messaged ${activity.recipient.name}`;
|
|
390
|
+
}
|
|
391
|
+
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) {
|
|
392
|
+
if (activity.progress) {
|
|
393
|
+
return `${countStr}${activity.user.name} >> ${activity.status} ${activity.progress} of ${activity.media.title.userPreferred}`;
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
return `${countStr}${activity.user.name} >> ${activity.status} ${activity.media.title.userPreferred}`;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
else if ((_d = activity === null || activity === void 0 ? void 0 : activity.user) === null || _d === void 0 ? void 0 : _d.name) {
|
|
400
|
+
return `${countStr}${activity.user.name}`;
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
return `${countStr}???`;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Extract the save file path
|
|
408
|
+
* @param data_type - anime|manga
|
|
409
|
+
* @param file_format - save format (eg: .json|.csv)
|
|
410
|
+
* @returns string of file path
|
|
411
|
+
*/
|
|
412
|
+
function saveToPath(data_type, file_format) {
|
|
413
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
414
|
+
return join(getDownloadFolderPath(), `${yield Auth.MyUserName()}@irfanshadikrishad-anilist-${data_type}-${getFormattedDate()}.${file_format}`);
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
function simpleDateFormat(date) {
|
|
418
|
+
if (!date.day && !date.month && !date.year) {
|
|
419
|
+
return `null`;
|
|
420
|
+
}
|
|
421
|
+
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}`;
|
|
422
|
+
}
|
|
423
|
+
function handleRateLimitRetry(retryCount) {
|
|
424
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
425
|
+
let seconds = Math.pow(2, retryCount) * 1000;
|
|
426
|
+
const maxWait = 60 * 1000;
|
|
427
|
+
seconds = Math.min(seconds, maxWait);
|
|
428
|
+
spinner.start(`Rate limit reached. Retrying in ${seconds / 1000} sec...`);
|
|
429
|
+
let remainingTime = seconds / 1000;
|
|
430
|
+
const interval = setInterval(() => {
|
|
431
|
+
remainingTime--;
|
|
432
|
+
spinner.update(`Rate limit reached. Retrying in ${remainingTime} sec...`);
|
|
433
|
+
if (remainingTime <= 0)
|
|
434
|
+
clearInterval(interval);
|
|
435
|
+
}, 1000);
|
|
436
|
+
yield new Promise((resolve) => setTimeout(resolve, seconds));
|
|
437
|
+
clearInterval(interval);
|
|
438
|
+
spinner.stop();
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function formatDate(timestamp) {
|
|
442
|
+
return timestamp ? new Date(timestamp * 1000).toUTCString() : "N/A";
|
|
443
|
+
}
|
|
444
|
+
function logUserDetails(user, followersCount, followingCount) {
|
|
445
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
446
|
+
console.log("\n📌 User Information:");
|
|
447
|
+
console.table({
|
|
448
|
+
"ID": user.id,
|
|
449
|
+
"Name": user.name,
|
|
450
|
+
"Site URL": user.siteUrl,
|
|
451
|
+
"Donator Tier": user.donatorTier,
|
|
452
|
+
"Donator Badge": user.donatorBadge,
|
|
453
|
+
"Account Created": formatDate(user.createdAt),
|
|
454
|
+
"Account Updated": formatDate(user.updatedAt),
|
|
455
|
+
"Blocked": user.isBlocked,
|
|
456
|
+
"isFollower": user.isFollower,
|
|
457
|
+
"isFollowing": user.isFollowing,
|
|
458
|
+
"Profile Color": ((_a = user.options) === null || _a === void 0 ? void 0 : _a.profileColor) || "N/A",
|
|
459
|
+
"Timezone": ((_b = user.options) === null || _b === void 0 ? void 0 : _b.timezone) || "N/A",
|
|
460
|
+
"Followers": followersCount,
|
|
461
|
+
"Following": followingCount,
|
|
462
|
+
});
|
|
463
|
+
console.log("\n📊 Anime Statistics:");
|
|
464
|
+
console.table({
|
|
465
|
+
"Count": ((_d = (_c = user.statistics) === null || _c === void 0 ? void 0 : _c.anime) === null || _d === void 0 ? void 0 : _d.count) || 0,
|
|
466
|
+
"Episodes Watched": ((_f = (_e = user.statistics) === null || _e === void 0 ? void 0 : _e.anime) === null || _f === void 0 ? void 0 : _f.episodesWatched) || 0,
|
|
467
|
+
"Minutes Watched": ((_h = (_g = user.statistics) === null || _g === void 0 ? void 0 : _g.anime) === null || _h === void 0 ? void 0 : _h.minutesWatched) || 0,
|
|
468
|
+
});
|
|
469
|
+
console.log("\n📖 Manga Statistics:");
|
|
470
|
+
console.table({
|
|
471
|
+
"Count": ((_k = (_j = user.statistics) === null || _j === void 0 ? void 0 : _j.manga) === null || _k === void 0 ? void 0 : _k.count) || 0,
|
|
472
|
+
"Chapters Read": ((_m = (_l = user.statistics) === null || _l === void 0 ? void 0 : _l.manga) === null || _m === void 0 ? void 0 : _m.chaptersRead) || 0,
|
|
473
|
+
"Volumes Read": ((_p = (_o = user.statistics) === null || _o === void 0 ? void 0 : _o.manga) === null || _p === void 0 ? void 0 : _p.volumesRead) || 0,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
export { activityBy, anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, handleRateLimitRetry, logUserDetails, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, saveToPath, selectFile, simpleDateFormat, timestampToTimeAgo, };
|
package/bin/index.js
CHANGED
|
@@ -10,13 +10,14 @@ 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
|
+
import { getCurrentPackageVersion } from "./helpers/workers.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(
|
|
20
|
+
.version(getCurrentPackageVersion());
|
|
20
21
|
cli
|
|
21
22
|
.command("login")
|
|
22
23
|
.description("Login with AniList")
|
|
@@ -31,7 +32,7 @@ cli
|
|
|
31
32
|
}
|
|
32
33
|
}));
|
|
33
34
|
cli
|
|
34
|
-
.command("
|
|
35
|
+
.command("whoami")
|
|
35
36
|
.description("Get details of the logged in user")
|
|
36
37
|
.action(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
38
|
yield Auth.Myself();
|
|
@@ -87,7 +88,7 @@ cli
|
|
|
87
88
|
.description("Delete entire collections of anime or manga")
|
|
88
89
|
.option("-a, --anime", "For anime list of authenticated user", false)
|
|
89
90
|
.option("-m, --manga", "For manga list of authenticated user", false)
|
|
90
|
-
.option("-
|
|
91
|
+
.option("-s, --activity", "For activity of authenticated user", false)
|
|
91
92
|
.action((_a) => __awaiter(void 0, [_a], void 0, function* ({ anime, manga, activity }) {
|
|
92
93
|
const selectedOptions = [anime, manga, activity].filter(Boolean).length;
|
|
93
94
|
if (selectedOptions === 0) {
|
|
@@ -127,6 +128,13 @@ cli
|
|
|
127
128
|
console.error(`\nInvalid or missing ID (${id}). Please provide a valid numeric ID.`);
|
|
128
129
|
}
|
|
129
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
|
+
}));
|
|
130
138
|
cli
|
|
131
139
|
.command("search <query>")
|
|
132
140
|
.alias("srch")
|
|
@@ -202,4 +210,35 @@ cli
|
|
|
202
210
|
}
|
|
203
211
|
}
|
|
204
212
|
}));
|
|
213
|
+
cli
|
|
214
|
+
.command("autolike")
|
|
215
|
+
.alias("al")
|
|
216
|
+
.description("Autolike following or global activities.")
|
|
217
|
+
.action(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
218
|
+
yield Auth.AutoLike();
|
|
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
|
+
}));
|
|
205
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.0.
|
|
5
|
+
"version": "1.1.0-forbidden.7",
|
|
6
6
|
"main": "./bin/index.js",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"types": "./bin/index.d.ts",
|
|
@@ -13,12 +13,14 @@
|
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"build": "rm -rf ./bin && tsc
|
|
16
|
+
"build": "rm -rf ./bin && tsc",
|
|
17
|
+
"build:watch": "rm -rf ./bin && tsc -w",
|
|
17
18
|
"format": "prettier . --write",
|
|
18
19
|
"format:check": "prettier . --check",
|
|
19
20
|
"lint": "eslint ./dist",
|
|
20
21
|
"lint:fix": "eslint ./dist --fix",
|
|
21
|
-
"all": "npm run lint && npm run lint:fix && npm run format"
|
|
22
|
+
"all": "npm run build && npm run lint && npm run lint:fix && npm run format && npm test",
|
|
23
|
+
"test": "jest ./tests"
|
|
22
24
|
},
|
|
23
25
|
"keywords": [
|
|
24
26
|
"anilist",
|
|
@@ -52,22 +54,33 @@
|
|
|
52
54
|
},
|
|
53
55
|
"license": "MPL-2.0",
|
|
54
56
|
"devDependencies": {
|
|
55
|
-
"@
|
|
56
|
-
"@
|
|
57
|
-
"@types/
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
57
|
+
"@babel/preset-env": "^7.27.2",
|
|
58
|
+
"@eslint/js": "^9.26.0",
|
|
59
|
+
"@types/jest": "^29.5.14",
|
|
60
|
+
"@types/node": "^22.15.17",
|
|
61
|
+
"@types/papaparse": "^5.3.16",
|
|
62
|
+
"@types/xml2js": "^0.4.14",
|
|
63
|
+
"@typescript-eslint/eslint-plugin": "^8.32.0",
|
|
64
|
+
"eslint": "^9.26.0",
|
|
65
|
+
"globals": "^16.1.0",
|
|
66
|
+
"jest": "^29.7.0",
|
|
67
|
+
"prettier": "^3.5.3",
|
|
61
68
|
"prettier-plugin-organize-imports": "^4.1.0",
|
|
62
|
-
"
|
|
63
|
-
"
|
|
69
|
+
"ts-jest": "^29.3.2",
|
|
70
|
+
"ts-node": "^10.9.2",
|
|
71
|
+
"typescript": "^5.8.3"
|
|
64
72
|
},
|
|
65
73
|
"dependencies": {
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
74
|
+
"@irfanshadikrishad/cipher": "^1.1.0",
|
|
75
|
+
"cli-truncate": "^4.0.0",
|
|
76
|
+
"commander": "^13.1.0",
|
|
77
|
+
"fast-xml-parser": "^5.2.2",
|
|
78
|
+
"inquirer": "^12.6.1",
|
|
79
|
+
"jsonrepair": "^3.12.0",
|
|
70
80
|
"node-fetch": "^3.3.2",
|
|
71
|
-
"open": "^10.1.
|
|
81
|
+
"open": "^10.1.2",
|
|
82
|
+
"papaparse": "^5.5.2",
|
|
83
|
+
"tiny-spinner": "^2.0.5",
|
|
84
|
+
"xml2js": "^0.6.2"
|
|
72
85
|
}
|
|
73
86
|
}
|
package/assets/binance.jpg
DELETED
|
Binary file
|