@irfanshadikrishad/anilist 1.2.6 → 1.2.8
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/README.md +50 -50
- package/bin/helpers/lists.js +38 -2
- package/bin/helpers/validation.d.ts +29 -0
- package/bin/helpers/validation.js +117 -0
- package/bin/helpers/workers.js +9 -4
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -44,25 +44,25 @@ anilist login -i <client-id> -s <client-secret>
|
|
|
44
44
|
|
|
45
45
|
#### CLI Commands Overview
|
|
46
46
|
|
|
47
|
-
| **Command**
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
47
|
+
| **Command** | **Options** | **Description** |
|
|
48
|
+
| --------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
49
|
+
| `login` | `-i, --id` `-s, --secret` | Log in with your AniList credentials |
|
|
50
|
+
| `logout` | _None_ | Log out from your AniList account |
|
|
51
|
+
| `whoami` | _None_ | Display information about the logged-in user |
|
|
52
|
+
| `-V, --version` | _None_ | Display the current version of the CLI |
|
|
53
|
+
| `-h, --help` | _None_ | Display available commands and options |
|
|
54
|
+
| `trending` <br> _(alias: `tr`)_ | `-c (default: 10)` | Fetch trending anime (default count is 10) |
|
|
55
|
+
| `popular` <br> _(alias: `plr`)_ | `-c (default: 10)` | Fetch popular anime (default count is 10) |
|
|
56
|
+
| `user` | `<username>` | Get information about a specific AniList user |
|
|
57
|
+
| `lists` <br> _(alias: `ls`)_ | `-a, --anime` <br> `-m, --manga` | Fetch anime or manga lists of the logged-in user |
|
|
58
|
+
| `delete` <br> _(alias: `del`)_ | `-a, --anime` <br> `-m, --manga` <br> `-s, --activity` | Delete collections of anime, manga or activities |
|
|
59
|
+
| `upcoming` <br> _(alias:`up`)_ | `-c (default: 10)` | Fetch upcoming anime (default count is 10) |
|
|
60
|
+
| `anime` | `<anime-id>` | Get anime details by Anime Id |
|
|
61
|
+
| `search` <br> _(alias:`srch`/`find`)_ | `<query>` <br> `-a, --anime` <br> `-m, --manga` <br> `-c (default: 10)` | Get anime/manga search results |
|
|
62
|
+
| `status` <br> _(alias: `write`/`post`)_ | `<status>` | Write a status... (text/markdown/html) |
|
|
63
|
+
| `export` <br> _(alias: `exp`)_ | `-a, --anime` <br> `-m, --manga` | Export anime or manga list in JSON, CSV or XML (MyAnimeList/AniDB) |
|
|
64
|
+
| `import` <br> _(alias: `imp`)_ | `-a, --anime` <br> `-m, --manga` | Import anime or manga list from exported JSON, MyAnimeList (XML) or AniDB (json-large) |
|
|
65
|
+
| `social` <br> _(alias: `sol`)_ | `-f, --follow` <br> `-u, --unfollow` | Follow users who follows you or Unfollow who doesn't follow you back with a simple command |
|
|
66
66
|
|
|
67
67
|
#### Command Breakdown:
|
|
68
68
|
|
|
@@ -72,10 +72,10 @@ anilist login -i <client-id> -s <client-secret>
|
|
|
72
72
|
anilist login -i <client-id> -s <client-secret>
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
-
|
|
75
|
+
- Options:
|
|
76
76
|
- `-i, --id`: Specify AniList Client ID
|
|
77
77
|
- `-s, --secret`: Provide the AniList Client Secret
|
|
78
|
-
-
|
|
78
|
+
- Usage: Authenticate and log in to AniList using your ID and secret credentials.
|
|
79
79
|
|
|
80
80
|
#### `logout`:
|
|
81
81
|
|
|
@@ -83,7 +83,7 @@ anilist login -i <client-id> -s <client-secret>
|
|
|
83
83
|
anilist logout
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
-
|
|
86
|
+
- Description: End the current session and log out from your AniList account.
|
|
87
87
|
|
|
88
88
|
#### `me`:
|
|
89
89
|
|
|
@@ -91,7 +91,7 @@ anilist logout
|
|
|
91
91
|
anilist me
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
-
|
|
94
|
+
- Description: Retrieve and display information about the currently logged-in user, including stats and profile details.
|
|
95
95
|
|
|
96
96
|
#### `-V, --version`:
|
|
97
97
|
|
|
@@ -99,7 +99,7 @@ anilist me
|
|
|
99
99
|
anilist -V
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
-
-
|
|
102
|
+
- Description: Quickly check which version of the CLI you are running.
|
|
103
103
|
|
|
104
104
|
#### `-h, --help`:
|
|
105
105
|
|
|
@@ -107,7 +107,7 @@ anilist -V
|
|
|
107
107
|
anilist -h
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
-
|
|
110
|
+
- Description: List all available commands and their usage details for quick reference.
|
|
111
111
|
|
|
112
112
|
#### `trending` _(alias: `tr`)_:
|
|
113
113
|
|
|
@@ -115,9 +115,9 @@ anilist -h
|
|
|
115
115
|
anilist tr -c 15
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
-
-
|
|
118
|
+
- Options:
|
|
119
119
|
- `-c (count)`: Specify how many trending anime to fetch (default: 10).
|
|
120
|
-
-
|
|
120
|
+
- Description: Fetch the current trending anime series, with the option to customize how many results to display.
|
|
121
121
|
|
|
122
122
|
#### `popular` _(alias: `plr`)_:
|
|
123
123
|
|
|
@@ -125,9 +125,9 @@ anilist tr -c 15
|
|
|
125
125
|
anilist popular
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
-
|
|
128
|
+
- Options:
|
|
129
129
|
- `-c (count)`: Specify how many popular anime to fetch (default: 10).
|
|
130
|
-
-
|
|
130
|
+
- Description: Fetch the most popular anime series, with the option to customize how many results to display.
|
|
131
131
|
|
|
132
132
|
#### `upcoming` _(alias: `up`)_:
|
|
133
133
|
|
|
@@ -135,9 +135,9 @@ anilist popular
|
|
|
135
135
|
anilist up -c 25
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
-
-
|
|
138
|
+
- Options:
|
|
139
139
|
- `-c (count)`: Specify how many upcoming anime to fetch (default: 10).
|
|
140
|
-
-
|
|
140
|
+
- Description: Fetch the upcoming anime series next season, with the option to customize how many results to display.
|
|
141
141
|
|
|
142
142
|
#### `user`:
|
|
143
143
|
|
|
@@ -145,9 +145,9 @@ anilist up -c 25
|
|
|
145
145
|
anilist user <username>
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
-
|
|
148
|
+
- Options:
|
|
149
149
|
- `<username>`: Specify the AniList username to fetch.
|
|
150
|
-
-
|
|
150
|
+
- Description: Retrieve profile information about a specific AniList user.
|
|
151
151
|
|
|
152
152
|
#### `lists` _(alias: `ls`)_:
|
|
153
153
|
|
|
@@ -155,10 +155,10 @@ anilist user <username>
|
|
|
155
155
|
anilist ls -a
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
-
-
|
|
158
|
+
- Options:
|
|
159
159
|
- `-a, --anime`: Fetch the authenticated user's anime list.
|
|
160
160
|
- `-m, --manga`: Fetch the authenticated user's manga list.
|
|
161
|
-
-
|
|
161
|
+
- Description: Get the anime or manga lists of the logged-in user.
|
|
162
162
|
|
|
163
163
|
#### `delete` _(alias: `del`)_:
|
|
164
164
|
|
|
@@ -166,11 +166,11 @@ anilist ls -a
|
|
|
166
166
|
anilist del -ac
|
|
167
167
|
```
|
|
168
168
|
|
|
169
|
-
-
|
|
169
|
+
- Options:
|
|
170
170
|
- `-a, --anime`: Delete your specific anime collection that you want.
|
|
171
171
|
- `-m, --manga`: Delete your specific manga collection that you want.
|
|
172
172
|
- `-ac, --activity`: Delete all or any type of activities you want.
|
|
173
|
-
-
|
|
173
|
+
- Description: Delete the entire anime or manga collection from the logged-in user's profile.
|
|
174
174
|
|
|
175
175
|
#### `anime`
|
|
176
176
|
|
|
@@ -178,9 +178,9 @@ anilist del -ac
|
|
|
178
178
|
anilist anime <anime-id>
|
|
179
179
|
```
|
|
180
180
|
|
|
181
|
-
-
|
|
181
|
+
- Options
|
|
182
182
|
- `<anime-id>` _(eg: 21)_ : Id of the anime you want to get details of.
|
|
183
|
-
-
|
|
183
|
+
- Description: Get anime details by anime Id.
|
|
184
184
|
|
|
185
185
|
#### `search` _(alias: `srch`/`find`)_:
|
|
186
186
|
|
|
@@ -188,12 +188,12 @@ anilist anime <anime-id>
|
|
|
188
188
|
anilist search <query> -a -c 20
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
-
-
|
|
191
|
+
- Options:
|
|
192
192
|
- `<query>` : What you want to search (eg: naruto).
|
|
193
193
|
- `-a, --anime`: To get results of anime search.
|
|
194
194
|
- `-m, --manga`: To get results of manga search.
|
|
195
195
|
- `-c (count)`: Specify how many items to fetch (default: 10).
|
|
196
|
-
-
|
|
196
|
+
- Description: Get anime/manga search results
|
|
197
197
|
|
|
198
198
|
#### `status` _(alias: `write`/`post`)_:
|
|
199
199
|
|
|
@@ -201,9 +201,9 @@ anilist search <query> -a -c 20
|
|
|
201
201
|
anilist write <status>
|
|
202
202
|
```
|
|
203
203
|
|
|
204
|
-
-
|
|
204
|
+
- Options:
|
|
205
205
|
- `<status>` : This is what you want to write, It can be HTML, Markdown and/or Text. But wrap it with quotation mark (") else it might get cut-off.
|
|
206
|
-
-
|
|
206
|
+
- Description: Get anime/manga search results
|
|
207
207
|
|
|
208
208
|
#### `export` _(alias: `exp`)_:
|
|
209
209
|
|
|
@@ -211,10 +211,10 @@ anilist write <status>
|
|
|
211
211
|
anilist export -a
|
|
212
212
|
```
|
|
213
213
|
|
|
214
|
-
-
|
|
214
|
+
- Options:
|
|
215
215
|
- `-a, --anime`: To export anime list.
|
|
216
216
|
- `-m, --manga`: To export manga list.
|
|
217
|
-
-
|
|
217
|
+
- Description: Export anime or manga list. For `XML (MyAnimeList/AniDB)` file, to import it on MyAnimeList, go [here](https://myanimelist.net/import.php) and choose `MyAnimeList Import` for `AniDB` go [here](https://anidb.net/user/import) and select `MyAnimeList.net - XML anime list export`.
|
|
218
218
|
|
|
219
219
|
#### `import` _(alias: `imp`)_:
|
|
220
220
|
|
|
@@ -222,10 +222,10 @@ anilist export -a
|
|
|
222
222
|
anilist import -m
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
-
-
|
|
225
|
+
- Options:
|
|
226
226
|
- `-a, --anime`: To import anime list.
|
|
227
227
|
- `-m, --manga`: To import manga list.
|
|
228
|
-
-
|
|
228
|
+
- Description: Import anime or manga list. If you want to import anime/manga list from `MyAnimeList`, export the XML from [here](https://myanimelist.net/panel.php?go=export), for exporting list from `AniDB` go [here](https://anidb.net/user/export).
|
|
229
229
|
|
|
230
230
|
> [!NOTE]
|
|
231
231
|
> If you have exported from `AniDB`, you will have to unzip it, and there should be a file named `mylist.json`, copy and paste it in your systems download folder, and select it from import option.
|
|
@@ -239,10 +239,10 @@ anilist import -m
|
|
|
239
239
|
anilist sol -f
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
-
-
|
|
242
|
+
- Options:
|
|
243
243
|
- `-f, --follow`: To follow users who follows you automatically.
|
|
244
244
|
- `-u, --unfollow`: To unfollow users who doesn't follow you back.
|
|
245
|
-
-
|
|
245
|
+
- Description: It follows users who follows you or unfollow users who doesn't follow you back at ease.
|
|
246
246
|
|
|
247
247
|
#### Security
|
|
248
248
|
|
|
@@ -252,4 +252,4 @@ Since you are creating your own API client for login no else else can get your c
|
|
|
252
252
|
|
|
253
253
|
Want to contribute to the project? Check out complete guideline [here](CONTRIBUTING.md).
|
|
254
254
|
|
|
255
|
-
####
|
|
255
|
+
#### _Thanks for visiting 💙_
|
package/bin/helpers/lists.js
CHANGED
|
@@ -18,15 +18,23 @@ import { fetcher } from "./fetcher.js";
|
|
|
18
18
|
import { addAnimeToListMutation, addMangaToListMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, } from "./mutations.js";
|
|
19
19
|
import { animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, } from "./queries.js";
|
|
20
20
|
import { AniListMediaStatus, } from "./types.js";
|
|
21
|
+
import { Validate } from "./validation.js";
|
|
21
22
|
import { anidbToanilistMapper, createAnimeListXML, createMangaListXML, formatDateObject, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, selectFile, timestampToTimeAgo, } from "./workers.js";
|
|
22
23
|
class AniList {
|
|
23
24
|
static importAnime() {
|
|
24
25
|
return __awaiter(this, void 0, void 0, function* () {
|
|
25
26
|
try {
|
|
26
27
|
const filename = yield selectFile(".json");
|
|
28
|
+
if (!filename) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
27
31
|
const filePath = join(getDownloadFolderPath(), filename);
|
|
28
32
|
const fileContent = yield readFile(filePath, "utf8");
|
|
29
33
|
const importedData = JSON.parse(fileContent);
|
|
34
|
+
if (!Validate.Import_JSON(importedData)) {
|
|
35
|
+
console.error(`\nInvalid JSON file.`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
30
38
|
let count = 0;
|
|
31
39
|
const batchSize = 1; // Number of requests in each batch
|
|
32
40
|
const delay = 1100; // delay to avoid rate-limiting
|
|
@@ -70,9 +78,16 @@ class AniList {
|
|
|
70
78
|
return __awaiter(this, void 0, void 0, function* () {
|
|
71
79
|
try {
|
|
72
80
|
const filename = yield selectFile(".json");
|
|
81
|
+
if (!filename) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
73
84
|
const filePath = join(getDownloadFolderPath(), filename);
|
|
74
85
|
const fileContent = yield readFile(filePath, "utf8");
|
|
75
86
|
const importedData = JSON.parse(fileContent);
|
|
87
|
+
if (!Validate.Import_JSON(importedData)) {
|
|
88
|
+
console.error(`\nInvalid JSON file.`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
76
91
|
let count = 0;
|
|
77
92
|
const batchSize = 1; // Adjust batch size as per rate-limit constraints
|
|
78
93
|
const delay = 1100; // 2 seconds delay to avoid rate-limit
|
|
@@ -813,8 +828,15 @@ class MyAnimeList {
|
|
|
813
828
|
var _a, _b, _c, _d, _e;
|
|
814
829
|
try {
|
|
815
830
|
const filename = yield selectFile(".xml");
|
|
831
|
+
if (!filename) {
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
816
834
|
const filePath = join(getDownloadFolderPath(), filename);
|
|
817
835
|
const fileContent = yield readFile(filePath, "utf8");
|
|
836
|
+
if (!(yield Validate.Import_AnimeXML(fileContent))) {
|
|
837
|
+
console.error(`\nInvalid XML file.`);
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
818
840
|
const parser = new XMLParser();
|
|
819
841
|
if (fileContent) {
|
|
820
842
|
const XMLObject = parser.parse(fileContent);
|
|
@@ -878,8 +900,15 @@ class MyAnimeList {
|
|
|
878
900
|
var _a, _b, _c, _d, _e;
|
|
879
901
|
try {
|
|
880
902
|
const filename = yield selectFile(".xml");
|
|
903
|
+
if (!filename) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
881
906
|
const filePath = join(getDownloadFolderPath(), filename);
|
|
882
907
|
const fileContent = yield readFile(filePath, "utf8");
|
|
908
|
+
if (!(yield Validate.Import_MangaXML(fileContent))) {
|
|
909
|
+
console.error(`\nInvalid XML file.`);
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
883
912
|
const parser = new XMLParser();
|
|
884
913
|
if (fileContent) {
|
|
885
914
|
const XMLObject = parser.parse(fileContent);
|
|
@@ -1023,10 +1052,17 @@ class AniDB {
|
|
|
1023
1052
|
var _a, _b;
|
|
1024
1053
|
try {
|
|
1025
1054
|
const filename = yield selectFile(".json");
|
|
1055
|
+
if (!filename) {
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1026
1058
|
const filePath = join(getDownloadFolderPath(), filename);
|
|
1027
1059
|
const fileContent = yield readFile(filePath, "utf8");
|
|
1028
1060
|
const js0n_repaired = jsonrepair(fileContent);
|
|
1029
|
-
if (
|
|
1061
|
+
if (!(yield Validate.Import_AniDBJSONLarge(js0n_repaired))) {
|
|
1062
|
+
console.error(`\nInvalid JSON Large file.`);
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
if (js0n_repaired) {
|
|
1030
1066
|
const obj3ct = yield JSON.parse(js0n_repaired);
|
|
1031
1067
|
const animeList = obj3ct === null || obj3ct === void 0 ? void 0 : obj3ct.anime;
|
|
1032
1068
|
if ((animeList === null || animeList === void 0 ? void 0 : animeList.length) > 0) {
|
|
@@ -1068,7 +1104,7 @@ class AniDB {
|
|
|
1068
1104
|
const entryId = (_b = (_a = saveResponse === null || saveResponse === void 0 ? void 0 : saveResponse.data) === null || _a === void 0 ? void 0 : _a.SaveMediaListEntry) === null || _b === void 0 ? void 0 : _b.id;
|
|
1069
1105
|
if (entryId) {
|
|
1070
1106
|
count++;
|
|
1071
|
-
console.log(`[${count}]\t${entryId} ✅\t${anidbId}\t${anilistId}\t(${ownEpisodes}/${totalEpisodes})\t${status}
|
|
1107
|
+
console.log(`[${count}]\t${entryId} ✅\t${anidbId}\t${anilistId}\t(${ownEpisodes}/${totalEpisodes})\t${status}–>${getStatus(status, ownEpisodes)}`);
|
|
1072
1108
|
}
|
|
1073
1109
|
// Rate limit each API call to avoid server overload
|
|
1074
1110
|
// await new Promise((resolve) => setTimeout(resolve, 1100))
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
declare class Validate {
|
|
2
|
+
/**
|
|
3
|
+
* Validate importable JSON file
|
|
4
|
+
* @param data string
|
|
5
|
+
* @returns boolean
|
|
6
|
+
*/
|
|
7
|
+
static Import_JSON(data: {
|
|
8
|
+
id: number;
|
|
9
|
+
}[]): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Validate if MyAnimeList Anime XML file is valid or not
|
|
12
|
+
* @param xmlData string
|
|
13
|
+
* @returns boolean
|
|
14
|
+
*/
|
|
15
|
+
static Import_AnimeXML(xmlData: string): Promise<boolean>;
|
|
16
|
+
/**
|
|
17
|
+
* Validate if MyAnimeList Anime XML file is valid or not
|
|
18
|
+
* @param xmlData string
|
|
19
|
+
* @returns boolean
|
|
20
|
+
*/
|
|
21
|
+
static Import_MangaXML(xmlData: string): Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* Validate AniDB json-large file
|
|
24
|
+
* @param file string of anidb json-large
|
|
25
|
+
* @returns boolean
|
|
26
|
+
*/
|
|
27
|
+
static Import_AniDBJSONLarge(file: string): Promise<boolean>;
|
|
28
|
+
}
|
|
29
|
+
export { Validate };
|
|
@@ -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.js
CHANGED
|
@@ -162,12 +162,13 @@ function selectFile(fileType) {
|
|
|
162
162
|
return answers.fileName;
|
|
163
163
|
}
|
|
164
164
|
else {
|
|
165
|
-
|
|
165
|
+
console.error(`\nNo importable ${fileType} file(s) found in download folder.`);
|
|
166
|
+
return null;
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
catch (error) {
|
|
169
170
|
console.error("\nError selecting file:", error);
|
|
170
|
-
|
|
171
|
+
return null;
|
|
171
172
|
}
|
|
172
173
|
});
|
|
173
174
|
}
|
|
@@ -226,7 +227,9 @@ function createAnimeListXML(mediaWithProgress) {
|
|
|
226
227
|
PAUSED: MALAnimeStatus.ON_HOLD,
|
|
227
228
|
DROPPED: MALAnimeStatus.DROPPED,
|
|
228
229
|
};
|
|
229
|
-
|
|
230
|
+
// Filter out anime without malId
|
|
231
|
+
const filteredMedia = mediaWithProgress.filter((anime) => anime.malId);
|
|
232
|
+
const xmlEntries = filteredMedia.map((anime) => {
|
|
230
233
|
const malId = anime.malId;
|
|
231
234
|
const progress = anime.progress;
|
|
232
235
|
const episodes = anime.episodes;
|
|
@@ -259,7 +262,9 @@ function createMangaListXML(mediaWithProgress) {
|
|
|
259
262
|
PAUSED: MALMangaStatus.ON_HOLD,
|
|
260
263
|
DROPPED: MALMangaStatus.DROPPED,
|
|
261
264
|
};
|
|
262
|
-
|
|
265
|
+
// Filter out manga without malId
|
|
266
|
+
const filteredMedia = mediaWithProgress.filter((manga) => manga.malId);
|
|
267
|
+
const xmlEntries = filteredMedia.map((manga) => {
|
|
263
268
|
const malId = manga.malId;
|
|
264
269
|
const progress = manga.progress;
|
|
265
270
|
const chapters = manga.chapters;
|
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.2.
|
|
5
|
+
"version": "1.2.8",
|
|
6
6
|
"main": "./bin/index.js",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"types": "./bin/index.d.ts",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"@types/jest": "^29.5.14",
|
|
60
60
|
"@types/json2csv": "^5.0.7",
|
|
61
61
|
"@types/node": "^22.10.5",
|
|
62
|
+
"@types/xml2js": "^0.4.14",
|
|
62
63
|
"@typescript-eslint/eslint-plugin": "^8.19.0",
|
|
63
64
|
"eslint": "^9.17.0",
|
|
64
65
|
"globals": "^15.14.0",
|
|
@@ -77,6 +78,7 @@
|
|
|
77
78
|
"jsonrepair": "^3.11.2",
|
|
78
79
|
"node-fetch": "^3.3.2",
|
|
79
80
|
"open": "^10.1.0",
|
|
80
|
-
"tiny-spinner": "^2.0.4"
|
|
81
|
+
"tiny-spinner": "^2.0.4",
|
|
82
|
+
"xml2js": "^0.6.2"
|
|
81
83
|
}
|
|
82
84
|
}
|