@irfanshadikrishad/anilist 1.0.0-forbidden.0
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 +382 -0
- package/README.md +232 -0
- package/bin/helpers/auth.d.ts +25 -0
- package/bin/helpers/auth.js +708 -0
- package/bin/helpers/fetcher.d.ts +12 -0
- package/bin/helpers/fetcher.js +54 -0
- package/bin/helpers/lists.d.ts +22 -0
- package/bin/helpers/lists.js +1023 -0
- package/bin/helpers/mutations.d.ts +8 -0
- package/bin/helpers/mutations.js +43 -0
- package/bin/helpers/queries.d.ts +23 -0
- package/bin/helpers/queries.js +149 -0
- package/bin/helpers/types.d.ts +111 -0
- package/bin/helpers/types.js +26 -0
- package/bin/helpers/workers.d.ts +37 -0
- package/bin/helpers/workers.js +282 -0
- package/bin/index.d.ts +2 -0
- package/bin/index.js +212 -0
- package/package.json +73 -0
|
@@ -0,0 +1,282 @@
|
|
|
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 fs from "fs";
|
|
11
|
+
import { readdir, writeFile } from "fs/promises";
|
|
12
|
+
import inquirer from "inquirer";
|
|
13
|
+
import { parse } from "json2csv";
|
|
14
|
+
import open from "open";
|
|
15
|
+
import { homedir } from "os";
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
import process from "process";
|
|
18
|
+
import { Auth } from "./auth.js";
|
|
19
|
+
import { MALAnimeStatus, MALMangaStatus } from "./types.js";
|
|
20
|
+
const aniListEndpoint = `https://graphql.anilist.co`;
|
|
21
|
+
const redirectUri = "https://anilist.co/api/v2/oauth/pin";
|
|
22
|
+
function getTitle(title) {
|
|
23
|
+
return (title === null || title === void 0 ? void 0 : title.english) || (title === null || title === void 0 ? void 0 : title.romaji) || "???";
|
|
24
|
+
}
|
|
25
|
+
function formatDateObject(dateObj) {
|
|
26
|
+
if (!dateObj)
|
|
27
|
+
return "null";
|
|
28
|
+
return ([dateObj.day, dateObj.month, dateObj.year].filter(Boolean).join("/") ||
|
|
29
|
+
"null");
|
|
30
|
+
}
|
|
31
|
+
function getNextSeasonAndYear() {
|
|
32
|
+
const currentMonth = new Date().getMonth() + 1;
|
|
33
|
+
const currentYear = new Date().getFullYear();
|
|
34
|
+
let nextSeason;
|
|
35
|
+
let nextYear;
|
|
36
|
+
// Determine the current season
|
|
37
|
+
if (currentMonth >= 12 || currentMonth <= 2) {
|
|
38
|
+
nextSeason = "SPRING";
|
|
39
|
+
nextYear = currentMonth === 12 ? currentYear + 1 : currentYear;
|
|
40
|
+
}
|
|
41
|
+
else if (currentMonth >= 3 && currentMonth <= 5) {
|
|
42
|
+
nextSeason = "SUMMER";
|
|
43
|
+
nextYear = currentYear;
|
|
44
|
+
}
|
|
45
|
+
else if (currentMonth >= 6 && currentMonth <= 8) {
|
|
46
|
+
nextSeason = "FALL";
|
|
47
|
+
nextYear = currentYear;
|
|
48
|
+
}
|
|
49
|
+
else if (currentMonth >= 9 && currentMonth <= 11) {
|
|
50
|
+
nextSeason = "WINTER";
|
|
51
|
+
nextYear = currentYear + 1;
|
|
52
|
+
}
|
|
53
|
+
return { nextSeason, nextYear };
|
|
54
|
+
}
|
|
55
|
+
function removeHtmlAndMarkdown(input) {
|
|
56
|
+
if (input) {
|
|
57
|
+
input = input.replace(/<\/?[^>]+(>|$)/g, "");
|
|
58
|
+
input = input.replace(/(^|\n)#{1,6}\s+(.+?)(\n|$)/g, "$2 ");
|
|
59
|
+
input = input.replace(/(\*\*|__)(.*?)\1/g, "$2");
|
|
60
|
+
input = input.replace(/(\*|_)(.*?)\1/g, "$2");
|
|
61
|
+
input = input.replace(/`(.+?)`/g, "$1");
|
|
62
|
+
input = input.replace(/\[(.*?)\]\(.*?\)/g, "$1");
|
|
63
|
+
input = input.replace(/!\[(.*?)\]\(.*?\)/g, "$1");
|
|
64
|
+
input = input.replace(/(^|\n)>\s+(.+?)(\n|$)/g, "$2 ");
|
|
65
|
+
input = input.replace(/(^|\n)-\s+(.+?)(\n|$)/g, "$2 ");
|
|
66
|
+
input = input.replace(/(^|\n)\d+\.\s+(.+?)(\n|$)/g, "$2 ");
|
|
67
|
+
input = input.replace(/(^|\n)\s*([-*_]){3,}\s*(\n|$)/g, "$1");
|
|
68
|
+
input = input.replace(/~~(.*?)~~/g, "$1");
|
|
69
|
+
input = input.replace(/\s+/g, " ").trim();
|
|
70
|
+
}
|
|
71
|
+
return input;
|
|
72
|
+
}
|
|
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 Auth.MyUserName()}@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 Auth.MyUserName()}@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(fileType) {
|
|
140
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
+
try {
|
|
142
|
+
const files = yield listFilesInDownloadFolder();
|
|
143
|
+
// Filter to include only files, not directories, with the specified extension
|
|
144
|
+
const onlyFiles = files.filter((file) => {
|
|
145
|
+
const filePath = `./downloads/${file}`; // Adjust this to the correct path
|
|
146
|
+
const isFile = fs.lstatSync(filePath).isFile(); // Check if it's a file
|
|
147
|
+
return isFile && file.endsWith(fileType);
|
|
148
|
+
});
|
|
149
|
+
if (onlyFiles.length > 0) {
|
|
150
|
+
const answers = yield inquirer.prompt([
|
|
151
|
+
{
|
|
152
|
+
type: "list",
|
|
153
|
+
name: "fileName",
|
|
154
|
+
message: "Select a file to import:",
|
|
155
|
+
choices: onlyFiles,
|
|
156
|
+
},
|
|
157
|
+
]);
|
|
158
|
+
return answers.fileName;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
throw new Error(`\nNo importable ${fileType} file(s) found in download folder.`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
console.error("\nError selecting file:", error);
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function createAnimeXML(malId, progress, status, episodes, title) {
|
|
171
|
+
return `
|
|
172
|
+
<anime>
|
|
173
|
+
<series_animedb_id>${malId}</series_animedb_id>
|
|
174
|
+
<series_title><![CDATA[${title}]]></series_title>
|
|
175
|
+
<series_type>""</series_type>
|
|
176
|
+
<series_episodes>${episodes}</series_episodes>
|
|
177
|
+
<my_id>0</my_id>
|
|
178
|
+
<my_watched_episodes>${progress}</my_watched_episodes>
|
|
179
|
+
<my_start_date>0000-00-00</my_start_date>
|
|
180
|
+
<my_finish_date>0000-00-00</my_finish_date>
|
|
181
|
+
<my_score>0</my_score>
|
|
182
|
+
<my_storage_value>0.00</my_storage_value>
|
|
183
|
+
<my_status>${status}</my_status>
|
|
184
|
+
<my_comments><![CDATA[]]></my_comments>
|
|
185
|
+
<my_times_watched>0</my_times_watched>
|
|
186
|
+
<my_rewatch_value></my_rewatch_value>
|
|
187
|
+
<my_priority>LOW</my_priority>
|
|
188
|
+
<my_tags><![CDATA[]]></my_tags>
|
|
189
|
+
<my_rewatching>0</my_rewatching>
|
|
190
|
+
<my_rewatching_ep>0</my_rewatching_ep>
|
|
191
|
+
<my_discuss>0</my_discuss>
|
|
192
|
+
<my_sns>default</my_sns>
|
|
193
|
+
<update_on_import>1</update_on_import>
|
|
194
|
+
</anime>`;
|
|
195
|
+
}
|
|
196
|
+
function createMangaXML(malId, progress, status, chapters, title) {
|
|
197
|
+
return `
|
|
198
|
+
<manga>
|
|
199
|
+
<manga_mangadb_id>${malId}</manga_mangadb_id>
|
|
200
|
+
<manga_title><![CDATA[${title ? title : "unknown"}]]></manga_title>
|
|
201
|
+
<manga_volumes>0</manga_volumes>
|
|
202
|
+
<manga_chapters>${chapters ? chapters : 0}</manga_chapters>
|
|
203
|
+
<my_id>0</my_id>
|
|
204
|
+
<my_read_chapters>${progress}</my_read_chapters>
|
|
205
|
+
<my_start_date>0000-00-00</my_start_date>
|
|
206
|
+
<my_finish_date>0000-00-00</my_finish_date>
|
|
207
|
+
<my_score>0</my_score>
|
|
208
|
+
<my_status>${status}</my_status>
|
|
209
|
+
<my_reread_value></my_reread_value>
|
|
210
|
+
<my_priority>LOW</my_priority>
|
|
211
|
+
<my_rereading>0</my_rereading>
|
|
212
|
+
<my_discuss>0</my_discuss>
|
|
213
|
+
<update_on_import>1</update_on_import>
|
|
214
|
+
</manga>`;
|
|
215
|
+
}
|
|
216
|
+
function createAnimeListXML(mediaWithProgress) {
|
|
217
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
218
|
+
const statusMap = {
|
|
219
|
+
PLANNING: MALAnimeStatus.PLAN_TO_WATCH,
|
|
220
|
+
COMPLETED: MALAnimeStatus.COMPLETED,
|
|
221
|
+
CURRENT: MALAnimeStatus.WATCHING,
|
|
222
|
+
PAUSED: MALAnimeStatus.ON_HOLD,
|
|
223
|
+
DROPPED: MALAnimeStatus.DROPPED,
|
|
224
|
+
};
|
|
225
|
+
const xmlEntries = mediaWithProgress.map((anime) => {
|
|
226
|
+
const malId = anime.malId;
|
|
227
|
+
const progress = anime.progress;
|
|
228
|
+
const episodes = anime.episodes;
|
|
229
|
+
const title = getTitle(anime.title);
|
|
230
|
+
const status = statusMap[anime.status];
|
|
231
|
+
return createAnimeXML(malId, progress, status, episodes, title);
|
|
232
|
+
});
|
|
233
|
+
return `<myanimelist>
|
|
234
|
+
<myinfo>
|
|
235
|
+
<user_id/>
|
|
236
|
+
<user_name>${yield Auth.MyUserName()}</user_name>
|
|
237
|
+
<user_export_type>1</user_export_type>
|
|
238
|
+
<user_total_anime>0</user_total_anime>
|
|
239
|
+
<user_total_watching>0</user_total_watching>
|
|
240
|
+
<user_total_completed>0</user_total_completed>
|
|
241
|
+
<user_total_onhold>0</user_total_onhold>
|
|
242
|
+
<user_total_dropped>0</user_total_dropped>
|
|
243
|
+
<user_total_plantowatch>0</user_total_plantowatch>
|
|
244
|
+
</myinfo>
|
|
245
|
+
\n${xmlEntries.join("\n")}\n
|
|
246
|
+
</myanimelist>`;
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
function createMangaListXML(mediaWithProgress) {
|
|
250
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
251
|
+
const statusMap = {
|
|
252
|
+
PLANNING: MALMangaStatus.PLAN_TO_READ,
|
|
253
|
+
COMPLETED: MALMangaStatus.COMPLETED,
|
|
254
|
+
CURRENT: MALMangaStatus.READING,
|
|
255
|
+
PAUSED: MALMangaStatus.ON_HOLD,
|
|
256
|
+
DROPPED: MALMangaStatus.DROPPED,
|
|
257
|
+
};
|
|
258
|
+
const xmlEntries = mediaWithProgress.map((manga) => {
|
|
259
|
+
const malId = manga.malId;
|
|
260
|
+
const progress = manga.progress;
|
|
261
|
+
const chapters = manga.chapters;
|
|
262
|
+
const title = getTitle(manga.title);
|
|
263
|
+
const status = statusMap[manga.status];
|
|
264
|
+
return createMangaXML(malId, progress, status, chapters, title);
|
|
265
|
+
});
|
|
266
|
+
return `<myanimelist>
|
|
267
|
+
<myinfo>
|
|
268
|
+
<user_id/>
|
|
269
|
+
<user_name>${yield Auth.MyUserName()}</user_name>
|
|
270
|
+
<user_export_type>2</user_export_type>
|
|
271
|
+
<user_total_manga>5</user_total_manga>
|
|
272
|
+
<user_total_reading>1</user_total_reading>
|
|
273
|
+
<user_total_completed>1</user_total_completed>
|
|
274
|
+
<user_total_onhold>1</user_total_onhold>
|
|
275
|
+
<user_total_dropped>1</user_total_dropped>
|
|
276
|
+
<user_total_plantoread>1</user_total_plantoread>
|
|
277
|
+
</myinfo>
|
|
278
|
+
\n${xmlEntries.join("\n")}\n
|
|
279
|
+
</myanimelist>`;
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
export { aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, selectFile, };
|
package/bin/index.d.ts
ADDED
package/bin/index.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
import { Command } from "commander";
|
|
12
|
+
import process from "process";
|
|
13
|
+
import { Auth } from "./helpers/auth.js";
|
|
14
|
+
import { AniList } from "./helpers/lists.js";
|
|
15
|
+
const cli = new Command();
|
|
16
|
+
cli
|
|
17
|
+
.name("anilist")
|
|
18
|
+
.description("Minimalist unofficial AniList CLI for Anime and Manga Enthusiasts.")
|
|
19
|
+
.version("1.0.0-forbidden.0");
|
|
20
|
+
cli
|
|
21
|
+
.command("login")
|
|
22
|
+
.description("Login with AniList")
|
|
23
|
+
.requiredOption("-i, --id <number>", null)
|
|
24
|
+
.requiredOption("-s, --secret <string>", null)
|
|
25
|
+
.action((_a) => __awaiter(void 0, [_a], void 0, function* ({ id, secret }) {
|
|
26
|
+
if (id && secret) {
|
|
27
|
+
yield Auth.Login(id, secret);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log("\nMust provide both ClientId and ClientSecret!");
|
|
31
|
+
}
|
|
32
|
+
}));
|
|
33
|
+
cli
|
|
34
|
+
.command("me")
|
|
35
|
+
.description("Get details of the logged in user")
|
|
36
|
+
.action(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
|
+
yield Auth.Myself();
|
|
38
|
+
}));
|
|
39
|
+
cli
|
|
40
|
+
.command("trending")
|
|
41
|
+
.alias("tr")
|
|
42
|
+
.description("Get the trending list from AniList")
|
|
43
|
+
.option("-c, --count <number>", "Number of list items to get", "10")
|
|
44
|
+
.action((_a) => __awaiter(void 0, [_a], void 0, function* ({ count }) {
|
|
45
|
+
yield AniList.getTrendingAnime(Number(count));
|
|
46
|
+
}));
|
|
47
|
+
cli
|
|
48
|
+
.command("popular")
|
|
49
|
+
.alias("plr")
|
|
50
|
+
.description("Get the popular list from AniList")
|
|
51
|
+
.option("-c, --count <number>", "Number of list items to get", "10")
|
|
52
|
+
.action((_a) => __awaiter(void 0, [_a], void 0, function* ({ count }) {
|
|
53
|
+
yield AniList.getPopularAnime(Number(count));
|
|
54
|
+
}));
|
|
55
|
+
cli
|
|
56
|
+
.command("user <username>")
|
|
57
|
+
.description("Get user information")
|
|
58
|
+
.action((username) => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
|
+
yield AniList.getUserByUsername(username);
|
|
60
|
+
}));
|
|
61
|
+
cli
|
|
62
|
+
.command("logout")
|
|
63
|
+
.description("Log out the current user.")
|
|
64
|
+
.action(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
65
|
+
yield Auth.Logout();
|
|
66
|
+
}));
|
|
67
|
+
cli
|
|
68
|
+
.command("lists")
|
|
69
|
+
.alias("ls")
|
|
70
|
+
.description("Get anime or manga list of authenticated user.")
|
|
71
|
+
.option("-a, --anime", "For anime list of authenticated user", false)
|
|
72
|
+
.option("-m, --manga", "For manga list of authenticated user", false)
|
|
73
|
+
.action((_a) => __awaiter(void 0, [_a], void 0, function* ({ anime, manga }) {
|
|
74
|
+
if ((!anime && !manga) || (anime && manga)) {
|
|
75
|
+
console.error(`\nMust select an option, either --anime or --manga`);
|
|
76
|
+
}
|
|
77
|
+
else if (anime) {
|
|
78
|
+
yield AniList.MyAnime();
|
|
79
|
+
}
|
|
80
|
+
else if (manga) {
|
|
81
|
+
yield AniList.MyManga();
|
|
82
|
+
}
|
|
83
|
+
}));
|
|
84
|
+
cli
|
|
85
|
+
.command("delete")
|
|
86
|
+
.alias("del")
|
|
87
|
+
.description("Delete entire collections of anime or manga")
|
|
88
|
+
.option("-a, --anime", "For anime list of authenticated user", false)
|
|
89
|
+
.option("-m, --manga", "For manga list of authenticated user", false)
|
|
90
|
+
.option("-ac, --activity", "For activity of authenticated user", false)
|
|
91
|
+
.action((_a) => __awaiter(void 0, [_a], void 0, function* ({ anime, manga, activity }) {
|
|
92
|
+
const selectedOptions = [anime, manga, activity].filter(Boolean).length;
|
|
93
|
+
if (selectedOptions === 0) {
|
|
94
|
+
console.error(`\nMust select one option: either --anime, --manga, or --activity`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
if (selectedOptions > 1) {
|
|
98
|
+
console.error(`\nOnly one option can be selected at a time: --anime, --manga, or --activity`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
if (anime) {
|
|
102
|
+
yield Auth.DeleteMyAnimeList();
|
|
103
|
+
}
|
|
104
|
+
else if (manga) {
|
|
105
|
+
yield Auth.DeleteMyMangaList();
|
|
106
|
+
}
|
|
107
|
+
else if (activity) {
|
|
108
|
+
yield Auth.DeleteMyActivities();
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
111
|
+
cli
|
|
112
|
+
.command("upcoming")
|
|
113
|
+
.alias("up")
|
|
114
|
+
.description("Anime that will be released in upcoming season")
|
|
115
|
+
.option("-c, --count <number>", "Number of items to get", "10")
|
|
116
|
+
.action((_a) => __awaiter(void 0, [_a], void 0, function* ({ count }) {
|
|
117
|
+
yield AniList.getUpcomingAnime(Number(count));
|
|
118
|
+
}));
|
|
119
|
+
cli
|
|
120
|
+
.command("anime <id>")
|
|
121
|
+
.description("Get anime details by their ID")
|
|
122
|
+
.action((id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
123
|
+
if (id && !Number.isNaN(Number(id))) {
|
|
124
|
+
yield AniList.getAnimeDetailsByID(Number(id));
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.error(`\nInvalid or missing ID (${id}). Please provide a valid numeric ID.`);
|
|
128
|
+
}
|
|
129
|
+
}));
|
|
130
|
+
cli
|
|
131
|
+
.command("search <query>")
|
|
132
|
+
.alias("srch")
|
|
133
|
+
.alias("find")
|
|
134
|
+
.description("Search anime or manga.")
|
|
135
|
+
.option("-a, --anime", "To get the anime search results.", false)
|
|
136
|
+
.option("-m, --manga", "To get the manga search results.", false)
|
|
137
|
+
.option("-c, --count <number>", "Number of search results to show.", "10")
|
|
138
|
+
.action((query_1, _a) => __awaiter(void 0, [query_1, _a], void 0, function* (query, { anime, manga, count }) {
|
|
139
|
+
if ((!anime && !manga) || (anime && manga)) {
|
|
140
|
+
console.error(`\nMust select an option, either --anime or --manga`);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
if (anime) {
|
|
144
|
+
yield AniList.searchAnime(query, Number(count));
|
|
145
|
+
}
|
|
146
|
+
else if (manga) {
|
|
147
|
+
yield AniList.searchManga(query, Number(count));
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
console.error(`\nMust select an option, either --anime or --manga`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}));
|
|
154
|
+
cli
|
|
155
|
+
.command("status <status>")
|
|
156
|
+
.alias("post")
|
|
157
|
+
.alias("write")
|
|
158
|
+
.description("Write a status...")
|
|
159
|
+
.action((status) => __awaiter(void 0, void 0, void 0, function* () {
|
|
160
|
+
yield Auth.Write(status);
|
|
161
|
+
}));
|
|
162
|
+
cli
|
|
163
|
+
.command("export")
|
|
164
|
+
.alias("exp")
|
|
165
|
+
.description("Export your anime or manga list.")
|
|
166
|
+
.option("-a, --anime", "To get the anime search results.", false)
|
|
167
|
+
.option("-m, --manga", "To get the manga search results.", false)
|
|
168
|
+
.action((_a) => __awaiter(void 0, [_a], void 0, function* ({ anime, manga }) {
|
|
169
|
+
if ((!anime && !manga) || (anime && manga)) {
|
|
170
|
+
console.error(`\nMust select an option, either --anime or --manga`);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
if (anime) {
|
|
174
|
+
yield AniList.exportAnime();
|
|
175
|
+
}
|
|
176
|
+
else if (manga) {
|
|
177
|
+
yield AniList.exportManga();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}));
|
|
181
|
+
cli
|
|
182
|
+
.command("import")
|
|
183
|
+
.alias("imp")
|
|
184
|
+
.description("Import your anime or manga from anilist or other sources.")
|
|
185
|
+
.option("-a, --anime", "To get the anime search results.", false)
|
|
186
|
+
.option("-m, --manga", "To get the manga search results.", false)
|
|
187
|
+
.action((_a) => __awaiter(void 0, [_a], void 0, function* ({ anime, manga }) {
|
|
188
|
+
if ((!anime && !manga) || (anime && manga)) {
|
|
189
|
+
console.error(`\nMust select an option, either --anime or --manga`);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
if (yield Auth.isLoggedIn()) {
|
|
193
|
+
if (anime) {
|
|
194
|
+
yield Auth.callAnimeImporter();
|
|
195
|
+
}
|
|
196
|
+
else if (manga) {
|
|
197
|
+
yield Auth.callMangaImporter();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
console.error(`\nPlease login to use this feature.`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}));
|
|
205
|
+
cli
|
|
206
|
+
.command("autolike")
|
|
207
|
+
.alias("al")
|
|
208
|
+
.description("Autolike following or global activities.")
|
|
209
|
+
.action(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
210
|
+
yield Auth.AutoLike();
|
|
211
|
+
}));
|
|
212
|
+
cli.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@irfanshadikrishad/anilist",
|
|
3
|
+
"description": "Minimalist unofficial AniList CLI for Anime and Manga Enthusiasts",
|
|
4
|
+
"author": "Irfan Shadik Rishad",
|
|
5
|
+
"version": "1.0.0-forbidden.0",
|
|
6
|
+
"main": "./bin/index.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"types": "./bin/index.d.ts",
|
|
9
|
+
"bin": {
|
|
10
|
+
"anilist": "./bin/index.js"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "rm -rf ./bin && tsc -w",
|
|
17
|
+
"format": "prettier . --write",
|
|
18
|
+
"format:check": "prettier . --check",
|
|
19
|
+
"lint": "eslint ./dist",
|
|
20
|
+
"lint:fix": "eslint ./dist --fix",
|
|
21
|
+
"all": "npm run lint && npm run lint:fix && npm run format"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"anilist",
|
|
25
|
+
"CLI",
|
|
26
|
+
"anime",
|
|
27
|
+
"manga",
|
|
28
|
+
"anime list",
|
|
29
|
+
"manga list",
|
|
30
|
+
"anime tracker",
|
|
31
|
+
"manga tracker",
|
|
32
|
+
"anilist API",
|
|
33
|
+
"anime progress",
|
|
34
|
+
"manga progress",
|
|
35
|
+
"media list",
|
|
36
|
+
"export anime",
|
|
37
|
+
"import anime",
|
|
38
|
+
"export manga",
|
|
39
|
+
"import manga",
|
|
40
|
+
"status tracker",
|
|
41
|
+
"watchlist",
|
|
42
|
+
"reading list",
|
|
43
|
+
"graphql"
|
|
44
|
+
],
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/irfanshadikrishad/anilist"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/irfanshadikrishad/anilist",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/irfanshadikrishad/anilist/issues"
|
|
52
|
+
},
|
|
53
|
+
"license": "MPL-2.0",
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@eslint/js": "^9.13.0",
|
|
56
|
+
"@types/json2csv": "^5.0.7",
|
|
57
|
+
"@types/node": "^22.7.9",
|
|
58
|
+
"eslint": "^9.13.0",
|
|
59
|
+
"globals": "^15.11.0",
|
|
60
|
+
"prettier": "^3.3.3",
|
|
61
|
+
"prettier-plugin-organize-imports": "^4.1.0",
|
|
62
|
+
"typescript": "^5.6.3",
|
|
63
|
+
"typescript-eslint": "^8.11.0"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"commander": "^12.1.0",
|
|
67
|
+
"fast-xml-parser": "^4.5.0",
|
|
68
|
+
"inquirer": "^12.0.0",
|
|
69
|
+
"json2csv": "^6.0.0-alpha.2",
|
|
70
|
+
"node-fetch": "^3.3.2",
|
|
71
|
+
"open": "^10.1.0"
|
|
72
|
+
}
|
|
73
|
+
}
|