@irfanshadikrishad/anilist 1.8.0 → 2.0.0-forbidden
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.md +382 -0
- package/README.md +0 -1
- package/bin/helpers/auth.d.ts +5 -0
- package/bin/helpers/auth.js +352 -18
- package/bin/helpers/lists.d.ts +1 -5
- package/bin/helpers/lists.js +19 -141
- package/bin/helpers/mutations.d.ts +2 -2
- package/bin/helpers/mutations.js +6 -9
- package/bin/helpers/queries.d.ts +4 -1
- package/bin/helpers/queries.js +35 -1
- package/bin/helpers/types.d.ts +53 -15
- package/bin/helpers/validation.d.ts +2 -2
- package/bin/helpers/validation.js +3 -3
- package/bin/helpers/workers.d.ts +3 -2
- package/bin/helpers/workers.js +22 -2
- package/bin/index.js +8 -22
- package/package.json +8 -9
- package/CITATION.cff +0 -8
- /package/bin/{lib → helpers/lib}/colorize.d.ts +0 -0
- /package/bin/{lib → helpers/lib}/colorize.js +0 -0
package/bin/helpers/lists.js
CHANGED
|
@@ -12,16 +12,14 @@ import { readFile } from 'fs/promises';
|
|
|
12
12
|
import inquirer from 'inquirer';
|
|
13
13
|
import { jsonrepair } from 'jsonrepair';
|
|
14
14
|
import { join } from 'path';
|
|
15
|
-
import Spinner from 'tiny-spinner';
|
|
16
15
|
import { Auth } from './auth.js';
|
|
17
16
|
import { fetcher } from './fetcher.js';
|
|
18
|
-
import { addAnimeToListMutation, addMangaToListMutation,
|
|
17
|
+
import { addAnimeToListMutation, addMangaToListMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, } from './mutations.js';
|
|
19
18
|
import { animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, } from './queries.js';
|
|
20
19
|
import { responsiveOutput } from './truncate.js';
|
|
21
20
|
import { AniListMediaStatus, } from './types.js';
|
|
22
21
|
import { Validate } from './validation.js';
|
|
23
22
|
import { anidbToanilistMapper, formatDateObject, getDownloadFolderPath, getNextSeasonAndYear, getTitle, logUserDetails, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, selectFile, simpleDateFormat, timestampToTimeAgo, } from './workers.js';
|
|
24
|
-
const spinner = new Spinner();
|
|
25
23
|
class AniList {
|
|
26
24
|
static importAnime() {
|
|
27
25
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -55,7 +53,7 @@ class AniList {
|
|
|
55
53
|
if (save) {
|
|
56
54
|
const id = (_b = (_a = save === null || save === void 0 ? void 0 : save.data) === null || _a === void 0 ? void 0 : _a.SaveMediaListEntry) === null || _b === void 0 ? void 0 : _b.id;
|
|
57
55
|
count++;
|
|
58
|
-
console.log(`[${count}]\t${id}\t${anime === null || anime === void 0 ? void 0 : anime.id}
|
|
56
|
+
console.log(`[${count}]\t${id}\t${anime === null || anime === void 0 ? void 0 : anime.id} ✅`);
|
|
59
57
|
}
|
|
60
58
|
else {
|
|
61
59
|
console.error(`\nError saving ${anime === null || anime === void 0 ? void 0 : anime.id}`);
|
|
@@ -873,7 +871,7 @@ class MyAnimeList {
|
|
|
873
871
|
const entryId = (_e = (_d = saveResponse === null || saveResponse === void 0 ? void 0 : saveResponse.data) === null || _d === void 0 ? void 0 : _d.SaveMediaListEntry) === null || _e === void 0 ? void 0 : _e.id;
|
|
874
872
|
if (entryId) {
|
|
875
873
|
count++;
|
|
876
|
-
console.log(`[${count}] ${entryId}
|
|
874
|
+
console.log(`[${count}] ${entryId} ✅`);
|
|
877
875
|
}
|
|
878
876
|
}
|
|
879
877
|
else {
|
|
@@ -943,7 +941,7 @@ class MyAnimeList {
|
|
|
943
941
|
const entryId = (_e = (_d = saveResponse === null || saveResponse === void 0 ? void 0 : saveResponse.data) === null || _d === void 0 ? void 0 : _d.SaveMediaListEntry) === null || _e === void 0 ? void 0 : _e.id;
|
|
944
942
|
if (entryId) {
|
|
945
943
|
count++;
|
|
946
|
-
console.log(`[${count}] ${entryId}
|
|
944
|
+
console.log(`[${count}] ${entryId} ✅`);
|
|
947
945
|
}
|
|
948
946
|
else {
|
|
949
947
|
console.error(`Failed to save entry for ${malId}`);
|
|
@@ -1044,17 +1042,6 @@ class AniDB {
|
|
|
1044
1042
|
static importAnime() {
|
|
1045
1043
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1046
1044
|
var _a, _b;
|
|
1047
|
-
function getStatus(anidbStatus, episodesSeen) {
|
|
1048
|
-
if (anidbStatus === 'complete') {
|
|
1049
|
-
return AniListMediaStatus.COMPLETED;
|
|
1050
|
-
}
|
|
1051
|
-
else if (anidbStatus === 'incomplete' && Number(episodesSeen) > 0) {
|
|
1052
|
-
return AniListMediaStatus.CURRENT;
|
|
1053
|
-
}
|
|
1054
|
-
else {
|
|
1055
|
-
return AniListMediaStatus.PLANNING;
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
1045
|
try {
|
|
1059
1046
|
const filename = yield selectFile('.json');
|
|
1060
1047
|
if (!filename) {
|
|
@@ -1084,7 +1071,19 @@ class AniDB {
|
|
|
1084
1071
|
const ownEpisodes = anime.ownEpisodes;
|
|
1085
1072
|
const romanjiName = anime.romanjiName;
|
|
1086
1073
|
const englishName = anime.englishName;
|
|
1087
|
-
|
|
1074
|
+
function getStatus(anidbStatus, episodesSeen) {
|
|
1075
|
+
if (anidbStatus === 'complete') {
|
|
1076
|
+
return AniListMediaStatus.COMPLETED;
|
|
1077
|
+
}
|
|
1078
|
+
else if (anidbStatus === 'incomplete' &&
|
|
1079
|
+
Number(episodesSeen) > 0) {
|
|
1080
|
+
return AniListMediaStatus.CURRENT;
|
|
1081
|
+
}
|
|
1082
|
+
else {
|
|
1083
|
+
return AniListMediaStatus.PLANNING;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
let anilistId = yield anidbToanilistMapper(romanjiName, Number(released.split('.')[2]), englishName);
|
|
1088
1087
|
if (anilistId) {
|
|
1089
1088
|
try {
|
|
1090
1089
|
const saveResponse = yield fetcher(saveAnimeWithProgressMutation, {
|
|
@@ -1097,7 +1096,7 @@ class AniDB {
|
|
|
1097
1096
|
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;
|
|
1098
1097
|
if (entryId) {
|
|
1099
1098
|
count++;
|
|
1100
|
-
responsiveOutput(`[${count}]\t${entryId}
|
|
1099
|
+
responsiveOutput(`[${count}]\t${entryId} ✅\t${anidbId}\t${anilistId}\t(${ownEpisodes}/${totalEpisodes})\t${status}–>${getStatus(status, ownEpisodes)}`);
|
|
1101
1100
|
}
|
|
1102
1101
|
}
|
|
1103
1102
|
catch (error) {
|
|
@@ -1132,125 +1131,4 @@ class AniDB {
|
|
|
1132
1131
|
});
|
|
1133
1132
|
}
|
|
1134
1133
|
}
|
|
1135
|
-
|
|
1136
|
-
static AnimeList() {
|
|
1137
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1138
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1139
|
-
try {
|
|
1140
|
-
spinner.start(`Fetching your anime list...`);
|
|
1141
|
-
const animeList = yield fetcher(currentUserAnimeList, {
|
|
1142
|
-
id: yield Auth.MyUserId(),
|
|
1143
|
-
});
|
|
1144
|
-
if (((_b = (_a = animeList === null || animeList === void 0 ? void 0 : animeList.data) === null || _a === void 0 ? void 0 : _a.MediaListCollection) === null || _b === void 0 ? void 0 : _b.lists.length) > 0) {
|
|
1145
|
-
spinner.stop();
|
|
1146
|
-
const list = (_d = (_c = animeList === null || animeList === void 0 ? void 0 : animeList.data) === null || _c === void 0 ? void 0 : _c.MediaListCollection) === null || _d === void 0 ? void 0 : _d.lists;
|
|
1147
|
-
const { selectedList } = yield inquirer.prompt([
|
|
1148
|
-
{
|
|
1149
|
-
type: 'list',
|
|
1150
|
-
name: 'selectedList',
|
|
1151
|
-
message: 'Select a list to move:',
|
|
1152
|
-
choices: list.map((list) => list.name),
|
|
1153
|
-
},
|
|
1154
|
-
]);
|
|
1155
|
-
const selectedEntries = list.find((list) => list.name === selectedList);
|
|
1156
|
-
if (!selectedEntries || selectedEntries.entries.length === 0) {
|
|
1157
|
-
return console.log('\nNo anime entries found in the selected list.');
|
|
1158
|
-
}
|
|
1159
|
-
const { toMoveList } = yield inquirer.prompt([
|
|
1160
|
-
{
|
|
1161
|
-
type: 'list',
|
|
1162
|
-
name: 'toMoveList',
|
|
1163
|
-
message: 'Select a list to move to:',
|
|
1164
|
-
choices: list.map((list) => list.name),
|
|
1165
|
-
},
|
|
1166
|
-
]);
|
|
1167
|
-
const entries = selectedEntries.entries;
|
|
1168
|
-
for (const entry of entries) {
|
|
1169
|
-
const mediaId = (_e = entry === null || entry === void 0 ? void 0 : entry.media) === null || _e === void 0 ? void 0 : _e.id;
|
|
1170
|
-
const status = entry === null || entry === void 0 ? void 0 : entry.status;
|
|
1171
|
-
// const progress = entry?.progress
|
|
1172
|
-
// console.log(mediaId, status)
|
|
1173
|
-
const response = yield fetcher(moveListMutation, {
|
|
1174
|
-
mediaId,
|
|
1175
|
-
status,
|
|
1176
|
-
customList: toMoveList,
|
|
1177
|
-
});
|
|
1178
|
-
if (response === null || response === void 0 ? void 0 : response.data) {
|
|
1179
|
-
const moved = (_f = response === null || response === void 0 ? void 0 : response.data) === null || _f === void 0 ? void 0 : _f.SaveMediaListEntry;
|
|
1180
|
-
console.log(`✔ Entry ${moved === null || moved === void 0 ? void 0 : moved.id}. Moved from ${selectedList} to ${toMoveList}.`);
|
|
1181
|
-
}
|
|
1182
|
-
else {
|
|
1183
|
-
console.error(`\nFailed to move the anime. ${((_h = (_g = response === null || response === void 0 ? void 0 : response.errors) === null || _g === void 0 ? void 0 : _g[0]) === null || _h === void 0 ? void 0 : _h.message) || 'Unknown error'}`);
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
else {
|
|
1188
|
-
console.log(`\nHey, ${yield Auth.MyUserName()}. Your anime list seems to be empty.`);
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
catch (error) {
|
|
1192
|
-
console.error(`\nError from AnimeList. ${error.message}`);
|
|
1193
|
-
}
|
|
1194
|
-
});
|
|
1195
|
-
}
|
|
1196
|
-
static MangaList() {
|
|
1197
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1198
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1199
|
-
try {
|
|
1200
|
-
spinner.start(`Fetching your manga list...`);
|
|
1201
|
-
const mangaList = yield fetcher(currentUserMangaList, {
|
|
1202
|
-
id: yield Auth.MyUserId(),
|
|
1203
|
-
});
|
|
1204
|
-
if (((_b = (_a = mangaList === null || mangaList === void 0 ? void 0 : mangaList.data) === null || _a === void 0 ? void 0 : _a.MediaListCollection) === null || _b === void 0 ? void 0 : _b.lists.length) > 0) {
|
|
1205
|
-
spinner.stop();
|
|
1206
|
-
const list = (_d = (_c = mangaList === null || mangaList === void 0 ? void 0 : mangaList.data) === null || _c === void 0 ? void 0 : _c.MediaListCollection) === null || _d === void 0 ? void 0 : _d.lists;
|
|
1207
|
-
const { selectedList } = yield inquirer.prompt([
|
|
1208
|
-
{
|
|
1209
|
-
type: 'list',
|
|
1210
|
-
name: 'selectedList',
|
|
1211
|
-
message: 'Select a list to move:',
|
|
1212
|
-
choices: list.map((list) => list.name),
|
|
1213
|
-
},
|
|
1214
|
-
]);
|
|
1215
|
-
const selectedEntries = list.find((list) => list.name === selectedList);
|
|
1216
|
-
if (!selectedEntries || selectedEntries.entries.length === 0) {
|
|
1217
|
-
return console.log('\nNo manga entries found in the selected list.');
|
|
1218
|
-
}
|
|
1219
|
-
const { toMoveList } = yield inquirer.prompt([
|
|
1220
|
-
{
|
|
1221
|
-
type: 'list',
|
|
1222
|
-
name: 'toMoveList',
|
|
1223
|
-
message: 'Select a list to move to:',
|
|
1224
|
-
choices: list.map((list) => list.name),
|
|
1225
|
-
},
|
|
1226
|
-
]);
|
|
1227
|
-
const entries = selectedEntries.entries;
|
|
1228
|
-
for (const entry of entries) {
|
|
1229
|
-
const mediaId = (_e = entry === null || entry === void 0 ? void 0 : entry.media) === null || _e === void 0 ? void 0 : _e.id;
|
|
1230
|
-
const status = entry === null || entry === void 0 ? void 0 : entry.status;
|
|
1231
|
-
// console.log(mediaId, status)
|
|
1232
|
-
const response = yield fetcher(moveListMutation, {
|
|
1233
|
-
mediaId,
|
|
1234
|
-
status,
|
|
1235
|
-
customList: toMoveList,
|
|
1236
|
-
});
|
|
1237
|
-
if (response === null || response === void 0 ? void 0 : response.data) {
|
|
1238
|
-
const moved = (_f = response === null || response === void 0 ? void 0 : response.data) === null || _f === void 0 ? void 0 : _f.SaveMediaListEntry;
|
|
1239
|
-
console.log(`✅ Entry ${moved === null || moved === void 0 ? void 0 : moved.id}. Moved from ${selectedList} to ${toMoveList}.`);
|
|
1240
|
-
}
|
|
1241
|
-
else {
|
|
1242
|
-
console.error(`\nFailed to move the manga. ${((_h = (_g = response === null || response === void 0 ? void 0 : response.errors) === null || _g === void 0 ? void 0 : _g[0]) === null || _h === void 0 ? void 0 : _h.message) || 'Unknown error'}`);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
else {
|
|
1247
|
-
console.log(`\nHey, ${yield Auth.MyUserName()}. Your manga list seems to be empty.`);
|
|
1248
|
-
}
|
|
1249
|
-
}
|
|
1250
|
-
catch (error) {
|
|
1251
|
-
console.error(`\nError from MangaList. ${error.message}`);
|
|
1252
|
-
}
|
|
1253
|
-
});
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
export { AniDB, AniList, MoveTo, MyAnimeList };
|
|
1134
|
+
export { AniDB, AniList, MyAnimeList };
|
|
@@ -4,8 +4,8 @@ declare const deleteActivityMutation = "\nmutation($id: Int!) { DeleteActivity(i
|
|
|
4
4
|
declare const saveTextActivityMutation = "\nmutation SaveTextActivity($status: String!) { SaveTextActivity(text: $status) { id text userId createdAt } }\n";
|
|
5
5
|
declare const saveAnimeWithProgressMutation = "\nmutation ($mediaId: Int, $progress: Int, $status: MediaListStatus, $hiddenFromStatusLists: Boolean) {\n SaveMediaListEntry(mediaId: $mediaId, progress: $progress, status: $status, hiddenFromStatusLists: $hiddenFromStatusLists) {\n id progress hiddenFromStatusLists\n }\n}\n";
|
|
6
6
|
declare const saveMangaWithProgressMutation = "\nmutation ($mediaId: Int, $progress: Int, $status: MediaListStatus, $hiddenFromStatusLists: Boolean, $private: Boolean) {\n SaveMediaListEntry( mediaId: $mediaId, progress: $progress, status: $status, hiddenFromStatusLists: $hiddenFromStatusLists, private: $private\n ) { id progress hiddenFromStatusLists private }\n}\n";
|
|
7
|
+
declare const likeActivityMutation = "\nmutation($activityId: Int!) {\n ToggleLike(id: $activityId, type: ACTIVITY) { id }\n}\n";
|
|
7
8
|
declare const toggleFollowMutation = "mutation ($userId: Int!) { ToggleFollow(userId: $userId) { id name isFollower isFollowing } }";
|
|
8
9
|
declare const deleteMediaEntryMutation = "mutation($id: Int!) { DeleteMediaListEntry(id: $id) { deleted } }";
|
|
9
10
|
declare const deleteMangaEntryMutation = "mutation($id: Int) {\n DeleteMediaListEntry(id: $id) { deleted }\n}";
|
|
10
|
-
|
|
11
|
-
export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, moveListMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, toggleFollowMutation, };
|
|
11
|
+
export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, likeActivityMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, toggleFollowMutation, };
|
package/bin/helpers/mutations.js
CHANGED
|
@@ -29,17 +29,14 @@ mutation ($mediaId: Int, $progress: Int, $status: MediaListStatus, $hiddenFromSt
|
|
|
29
29
|
) { id progress hiddenFromStatusLists private }
|
|
30
30
|
}
|
|
31
31
|
`;
|
|
32
|
+
const likeActivityMutation = `
|
|
33
|
+
mutation($activityId: Int!) {
|
|
34
|
+
ToggleLike(id: $activityId, type: ACTIVITY) { id }
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
32
37
|
const toggleFollowMutation = `mutation ($userId: Int!) { ToggleFollow(userId: $userId) { id name isFollower isFollowing } }`;
|
|
33
38
|
const deleteMediaEntryMutation = `mutation($id: Int!) { DeleteMediaListEntry(id: $id) { deleted } }`;
|
|
34
39
|
const deleteMangaEntryMutation = `mutation($id: Int) {
|
|
35
40
|
DeleteMediaListEntry(id: $id) { deleted }
|
|
36
41
|
}`;
|
|
37
|
-
|
|
38
|
-
SaveMediaListEntry(
|
|
39
|
-
mediaId: $mediaId
|
|
40
|
-
status: $status
|
|
41
|
-
customLists: [$customList]
|
|
42
|
-
hiddenFromStatusLists: true
|
|
43
|
-
) { id status hiddenFromStatusLists customLists media { title { romaji english native userPreferred } } }
|
|
44
|
-
}`;
|
|
45
|
-
export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, moveListMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, toggleFollowMutation, };
|
|
42
|
+
export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, likeActivityMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, toggleFollowMutation, };
|
package/bin/helpers/queries.d.ts
CHANGED
|
@@ -17,7 +17,10 @@ declare const activityAllQuery = "query ($userId: Int, $page: Int, $perPage: Int
|
|
|
17
17
|
declare const activityMediaList = "query ($userId: Int, $page: Int, $perPage: Int, $type: ActivityType) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total currentPage lastPage hasNextPage perPage }\n activities(userId: $userId, type: $type, sort: ID_DESC) {\n ... on ListActivity { id type status progress media { id title { romaji english native } format } createdAt }\n }\n }\n}";
|
|
18
18
|
declare const malIdToAnilistAnimeId = "query ($malId: Int) {\n Media(idMal: $malId, type: ANIME) { id title { romaji english } } }\n";
|
|
19
19
|
declare const malIdToAnilistMangaId = "query ($malId: Int) {\n Media(idMal: $malId, type: MANGA) { id title { romaji english } } }\n";
|
|
20
|
+
declare const followingActivitiesQuery = "\nquery ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n activities(isFollowing: true, sort: ID_DESC) {\n ... on TextActivity { id type isLiked createdAt user { id name } }\n ... on ListActivity { id type isLiked status progress media { title { userPreferred } } createdAt user { id name } }\n ... on MessageActivity { id type isLiked message createdAt recipient { id name } }\n }\n }\n}\n";
|
|
21
|
+
declare const globalActivitiesQuery = "\nquery ($page: Int, $perPage: Int) {\n Page(page: $page, perPage: $perPage) {\n activities(sort: ID_DESC) {\n ... on TextActivity { id type isLiked createdAt user { id name } }\n ... on ListActivity { id type isLiked status progress media { title { userPreferred } } createdAt user { id name } }\n ... on MessageActivity { id type isLiked message createdAt recipient { id name } }\n }\n }\n}\n";
|
|
22
|
+
declare const specificUserActivitiesQuery = "\nquery ($page: Int, $perPage: Int, $userId: Int) {\n Page(page: $page, perPage: $perPage) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n activities(userId: $userId, sort: ID_DESC) {\n ... on TextActivity { id type isLiked createdAt user { id name } }\n ... on ListActivity { id type isLiked status progress media { title { userPreferred } } createdAt user { id name } }\n ... on MessageActivity { messenger { name } id type isLiked message createdAt recipient { id name } }\n }\n }\n}\n";
|
|
20
23
|
declare const userFollowingQuery = "query ($userId: Int!, $page: Int) {\n Page (page: $page) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n following(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }\n }\n}\n";
|
|
21
24
|
declare const userFollowersQuery = "query ($userId: Int!, $page: Int) {\n Page (page: $page) {\n pageInfo { total perPage currentPage lastPage hasNextPage }\n followers(userId: $userId, sort: [USERNAME]) { id name avatar { large medium } bannerImage isFollowing isFollower }\n }\n}\n";
|
|
22
25
|
declare const mangaDetailsQuery = "query ($id: Int) {\n Media(id: $id, type: MANGA) {\n id title { romaji english native userPreferred } coverImage { color medium large extraLarge } \n bannerImage description chapters volumes status genres\n startDate { year month day } endDate { year month day }\n }\n}\n";
|
|
23
|
-
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
|
|
26
|
+
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, followingActivitiesQuery, globalActivitiesQuery, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, specificUserActivitiesQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
|
package/bin/helpers/queries.js
CHANGED
|
@@ -119,6 +119,40 @@ const malIdToAnilistAnimeId = `query ($malId: Int) {
|
|
|
119
119
|
const malIdToAnilistMangaId = `query ($malId: Int) {
|
|
120
120
|
Media(idMal: $malId, type: MANGA) { id title { romaji english } } }
|
|
121
121
|
`;
|
|
122
|
+
const followingActivitiesQuery = `
|
|
123
|
+
query ($page: Int, $perPage: Int) {
|
|
124
|
+
Page(page: $page, perPage: $perPage) {
|
|
125
|
+
activities(isFollowing: true, sort: ID_DESC) {
|
|
126
|
+
... on TextActivity { id type isLiked createdAt user { id name } }
|
|
127
|
+
... on ListActivity { id type isLiked status progress media { title { userPreferred } } createdAt user { id name } }
|
|
128
|
+
... on MessageActivity { id type isLiked message createdAt recipient { id name } }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
`;
|
|
133
|
+
const globalActivitiesQuery = `
|
|
134
|
+
query ($page: Int, $perPage: Int) {
|
|
135
|
+
Page(page: $page, perPage: $perPage) {
|
|
136
|
+
activities(sort: ID_DESC) {
|
|
137
|
+
... on TextActivity { id type isLiked createdAt user { id name } }
|
|
138
|
+
... on ListActivity { id type isLiked status progress media { title { userPreferred } } createdAt user { id name } }
|
|
139
|
+
... on MessageActivity { id type isLiked message createdAt recipient { id name } }
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
`;
|
|
144
|
+
const specificUserActivitiesQuery = `
|
|
145
|
+
query ($page: Int, $perPage: Int, $userId: Int) {
|
|
146
|
+
Page(page: $page, perPage: $perPage) {
|
|
147
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
148
|
+
activities(userId: $userId, sort: ID_DESC) {
|
|
149
|
+
... on TextActivity { id type isLiked createdAt user { id name } }
|
|
150
|
+
... on ListActivity { id type isLiked status progress media { title { userPreferred } } createdAt user { id name } }
|
|
151
|
+
... on MessageActivity { messenger { name } id type isLiked message createdAt recipient { id name } }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
`;
|
|
122
156
|
const userFollowingQuery = `query ($userId: Int!, $page: Int) {
|
|
123
157
|
Page (page: $page) {
|
|
124
158
|
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
@@ -141,4 +175,4 @@ const mangaDetailsQuery = `query ($id: Int) {
|
|
|
141
175
|
}
|
|
142
176
|
}
|
|
143
177
|
`;
|
|
144
|
-
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
|
|
178
|
+
export { activityAllQuery, activityAnimeListQuery, activityMangaListQuery, activityMediaList, activityMessageQuery, activityTextQuery, animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, currentUserQuery, followingActivitiesQuery, globalActivitiesQuery, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, specificUserActivitiesQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, };
|
package/bin/helpers/types.d.ts
CHANGED
|
@@ -323,6 +323,58 @@ type Activity = {
|
|
|
323
323
|
};
|
|
324
324
|
createdAt: number;
|
|
325
325
|
};
|
|
326
|
+
interface TheActivity {
|
|
327
|
+
type: string;
|
|
328
|
+
id: number;
|
|
329
|
+
message?: string;
|
|
330
|
+
createdAt: number;
|
|
331
|
+
recipient?: {
|
|
332
|
+
id: number;
|
|
333
|
+
name: string;
|
|
334
|
+
};
|
|
335
|
+
isLiked?: boolean;
|
|
336
|
+
user?: {
|
|
337
|
+
id?: number;
|
|
338
|
+
name?: string;
|
|
339
|
+
};
|
|
340
|
+
messenger?: {
|
|
341
|
+
name: string;
|
|
342
|
+
};
|
|
343
|
+
media?: {
|
|
344
|
+
title?: {
|
|
345
|
+
userPreferred: string;
|
|
346
|
+
};
|
|
347
|
+
};
|
|
348
|
+
progress?: string | null;
|
|
349
|
+
status?: string;
|
|
350
|
+
}
|
|
351
|
+
type LikeActivityResponse = {
|
|
352
|
+
data?: {
|
|
353
|
+
ToggleLike: {
|
|
354
|
+
id: number;
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
errors?: {
|
|
358
|
+
message: string;
|
|
359
|
+
}[];
|
|
360
|
+
};
|
|
361
|
+
type SpecificUserActivitiesResponse = {
|
|
362
|
+
data?: {
|
|
363
|
+
Page: {
|
|
364
|
+
pageInfo: {
|
|
365
|
+
total: number;
|
|
366
|
+
perPage: number;
|
|
367
|
+
currentPage: number;
|
|
368
|
+
lastPage: number;
|
|
369
|
+
hasNextPage: boolean;
|
|
370
|
+
};
|
|
371
|
+
activities: TheActivity[];
|
|
372
|
+
};
|
|
373
|
+
};
|
|
374
|
+
errors?: {
|
|
375
|
+
message: string;
|
|
376
|
+
}[];
|
|
377
|
+
};
|
|
326
378
|
type CoverImage = {
|
|
327
379
|
color: string;
|
|
328
380
|
medium: string;
|
|
@@ -347,18 +399,4 @@ type MangaDetails = {
|
|
|
347
399
|
};
|
|
348
400
|
errors?: Error;
|
|
349
401
|
};
|
|
350
|
-
|
|
351
|
-
data?: {
|
|
352
|
-
SaveMediaListEntry: {
|
|
353
|
-
id: 474487691;
|
|
354
|
-
status: 'COMPLETED';
|
|
355
|
-
hiddenFromStatusLists: true;
|
|
356
|
-
customLists: Record<string, boolean>;
|
|
357
|
-
media: {
|
|
358
|
-
title: MediaTitle;
|
|
359
|
-
};
|
|
360
|
-
};
|
|
361
|
-
};
|
|
362
|
-
errors?: Error;
|
|
363
|
-
};
|
|
364
|
-
export { Activity, AniListMediaStatus, AnimeDetails, AnimeList, AnimeSearchResponse, DateMonthYear, DeleteMangaResponse, DeleteMediaListResponse, List, MALAnimeStatus, MALAnimeXML, MALMangaStatus, MalIdToAnilistIdResponse, MangaDetails, MediaEntry, MediaList, MediaListCollectionResponse, MediaListEntry, MediaTitle, MediaWithProgress, MoveListsResponse, Myself, SaveMediaListEntryResponse, SaveTextActivityResponse, ToggleFollowResponse, User, UserActivitiesResponse, UserFollower, UserFollowing, UserResponse, saveAnimeWithProgressResponse, };
|
|
402
|
+
export { Activity, AniListMediaStatus, AnimeDetails, AnimeList, AnimeSearchResponse, DateMonthYear, DeleteMangaResponse, DeleteMediaListResponse, LikeActivityResponse, List, MALAnimeStatus, MALAnimeXML, MALMangaStatus, MalIdToAnilistIdResponse, MangaDetails, MediaEntry, MediaList, MediaListCollectionResponse, MediaListEntry, MediaTitle, MediaWithProgress, Myself, SaveMediaListEntryResponse, SaveTextActivityResponse, SpecificUserActivitiesResponse, TheActivity, ToggleFollowResponse, User, UserActivitiesResponse, UserFollower, UserFollowing, UserResponse, saveAnimeWithProgressResponse, };
|
|
@@ -9,8 +9,8 @@ declare class Validate {
|
|
|
9
9
|
}[]): boolean;
|
|
10
10
|
/**
|
|
11
11
|
* Validate if MyAnimeList Anime XML file is valid or not
|
|
12
|
-
* @param
|
|
13
|
-
* @returns
|
|
12
|
+
* @param xmlData string
|
|
13
|
+
* @returns boolean
|
|
14
14
|
*/
|
|
15
15
|
static Import_AnimeXML(xmlData: string): Promise<boolean>;
|
|
16
16
|
/**
|
|
@@ -20,8 +20,8 @@ class Validate {
|
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Validate if MyAnimeList Anime XML file is valid or not
|
|
23
|
-
* @param
|
|
24
|
-
* @returns
|
|
23
|
+
* @param xmlData string
|
|
24
|
+
* @returns boolean
|
|
25
25
|
*/
|
|
26
26
|
static Import_AnimeXML(xmlData) {
|
|
27
27
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -100,7 +100,7 @@ class Validate {
|
|
|
100
100
|
console.error('File content is empty or invalid.');
|
|
101
101
|
return false;
|
|
102
102
|
}
|
|
103
|
-
const obj3ct =
|
|
103
|
+
const obj3ct = JSON.parse(file);
|
|
104
104
|
if (!obj3ct || !Array.isArray(obj3ct.anime)) {
|
|
105
105
|
console.error("Invalid JSON structure: Missing or malformed 'anime' array.");
|
|
106
106
|
return false;
|
package/bin/helpers/workers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DateMonthYear, MALAnimeStatus, MALMangaStatus, MediaWithProgress, User } 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: {
|
|
@@ -38,6 +38,7 @@ declare function createMangaListXML(mediaWithProgress: MediaWithProgress[]): Pro
|
|
|
38
38
|
declare function getCurrentPackageVersion(): string | null;
|
|
39
39
|
declare function timestampToTimeAgo(timestamp: number): string;
|
|
40
40
|
declare const anidbToanilistMapper: (romanjiName: string, year: number, englishName?: string) => Promise<number | null>;
|
|
41
|
+
declare function activityBy(activity: TheActivity, count?: number): string;
|
|
41
42
|
/**
|
|
42
43
|
* Extract the save file path
|
|
43
44
|
* @param data_type - anime|manga
|
|
@@ -48,4 +49,4 @@ declare function saveToPath(data_type: string, file_format: string): Promise<str
|
|
|
48
49
|
declare function simpleDateFormat(date: DateMonthYear): string;
|
|
49
50
|
declare function handleRateLimitRetry(retryCount: number): Promise<void>;
|
|
50
51
|
declare function logUserDetails(user: User, followersCount: number, followingCount: number): void;
|
|
51
|
-
export { anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, handleRateLimitRetry, logUserDetails, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, saveToPath, selectFile, simpleDateFormat, timestampToTimeAgo, };
|
|
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
|
@@ -261,7 +261,6 @@ function createAnimeListXML(mediaWithProgress) {
|
|
|
261
261
|
// Filter out anime without malId
|
|
262
262
|
const filteredMedia = mediaWithProgress.filter((anime) => anime.malId);
|
|
263
263
|
const xmlEntries = filteredMedia.map((anime) => {
|
|
264
|
-
console.log(anime);
|
|
265
264
|
const malId = anime.malId;
|
|
266
265
|
const progress = anime.progress;
|
|
267
266
|
const episodes = anime.episodes;
|
|
@@ -383,6 +382,27 @@ const anidbToanilistMapper = (romanjiName, year, englishName) => __awaiter(void
|
|
|
383
382
|
}
|
|
384
383
|
return null;
|
|
385
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
|
+
}
|
|
386
406
|
/**
|
|
387
407
|
* Extract the save file path
|
|
388
408
|
* @param data_type - anime|manga
|
|
@@ -453,4 +473,4 @@ function logUserDetails(user, followersCount, followingCount) {
|
|
|
453
473
|
'Volumes Read': ((_p = (_o = user.statistics) === null || _o === void 0 ? void 0 : _o.manga) === null || _p === void 0 ? void 0 : _p.volumesRead) || 0,
|
|
454
474
|
});
|
|
455
475
|
}
|
|
456
|
-
export { anidbToanilistMapper, aniListEndpoint, createAnimeListXML, createAnimeXML, createMangaListXML, createMangaXML, formatDateObject, getCurrentPackageVersion, getDownloadFolderPath, getFormattedDate, getNextSeasonAndYear, getTitle, handleRateLimitRetry, logUserDetails, redirectUri, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, saveToPath, selectFile, simpleDateFormat, timestampToTimeAgo, };
|
|
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
|
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
import { Command } from 'commander';
|
|
12
12
|
import process from 'process';
|
|
13
13
|
import { Auth, Social } from './helpers/auth.js';
|
|
14
|
-
import { AniList
|
|
14
|
+
import { AniList } from './helpers/lists.js';
|
|
15
15
|
import { getCurrentPackageVersion } from './helpers/workers.js';
|
|
16
16
|
const cli = new Command();
|
|
17
17
|
cli
|
|
@@ -210,6 +210,13 @@ cli
|
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
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
|
+
}));
|
|
213
220
|
cli
|
|
214
221
|
.command('social')
|
|
215
222
|
.alias('sol')
|
|
@@ -234,25 +241,4 @@ cli
|
|
|
234
241
|
}
|
|
235
242
|
}
|
|
236
243
|
}));
|
|
237
|
-
cli
|
|
238
|
-
.command('move')
|
|
239
|
-
.alias('mv')
|
|
240
|
-
.description('Move anime or manga from one list to another')
|
|
241
|
-
.option('-a, --anime', 'To move anime lists.', false)
|
|
242
|
-
.option('-m, --manga', 'To move manga lists.', false)
|
|
243
|
-
.action((_a) => __awaiter(void 0, [_a], void 0, function* ({ anime, manga }) {
|
|
244
|
-
if (!(yield Auth.isLoggedIn())) {
|
|
245
|
-
console.error(`\nPlease login to use this feature.`);
|
|
246
|
-
process.exit(1);
|
|
247
|
-
}
|
|
248
|
-
if ((!anime && !manga) || (anime && manga)) {
|
|
249
|
-
console.error(`\nMust select an option, either --anime or --manga`);
|
|
250
|
-
}
|
|
251
|
-
if (anime) {
|
|
252
|
-
yield MoveTo.AnimeList();
|
|
253
|
-
}
|
|
254
|
-
else if (manga) {
|
|
255
|
-
yield MoveTo.MangaList();
|
|
256
|
-
}
|
|
257
|
-
}));
|
|
258
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": "
|
|
5
|
+
"version": "2.0.0-forbidden",
|
|
6
6
|
"main": "./bin/index.js",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"types": "./bin/index.d.ts",
|
|
@@ -56,15 +56,14 @@
|
|
|
56
56
|
"prettier": "@irfanshadikrishad/prettier",
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@babel/preset-env": "^7.28.0",
|
|
59
|
-
"@eslint/js": "^9.
|
|
59
|
+
"@eslint/js": "^9.31.0",
|
|
60
60
|
"@irfanshadikrishad/prettier": "^1.1.0",
|
|
61
61
|
"@types/jest": "^30.0.0",
|
|
62
|
-
"@types/node": "^24.0.
|
|
62
|
+
"@types/node": "^24.0.14",
|
|
63
63
|
"@types/papaparse": "^5.3.16",
|
|
64
64
|
"@types/xml2js": "^0.4.14",
|
|
65
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
66
|
-
"
|
|
67
|
-
"eslint": "^9.30.1",
|
|
65
|
+
"@typescript-eslint/eslint-plugin": "^8.37.0",
|
|
66
|
+
"eslint": "^9.31.0",
|
|
68
67
|
"globals": "^16.3.0",
|
|
69
68
|
"jest": "^30.0.4",
|
|
70
69
|
"prettier": "^3.6.2",
|
|
@@ -74,15 +73,15 @@
|
|
|
74
73
|
"typescript": "^5.8.3"
|
|
75
74
|
},
|
|
76
75
|
"dependencies": {
|
|
77
|
-
"@irfanshadikrishad/cipher": "^1.
|
|
76
|
+
"@irfanshadikrishad/cipher": "^1.3.0",
|
|
78
77
|
"chalk": "^5.4.1",
|
|
79
78
|
"cli-truncate": "^4.0.0",
|
|
80
79
|
"commander": "^14.0.0",
|
|
81
80
|
"fast-xml-parser": "^5.2.5",
|
|
82
81
|
"inquirer": "^12.7.0",
|
|
83
|
-
"jsonrepair": "^3.
|
|
82
|
+
"jsonrepair": "^3.13.0",
|
|
84
83
|
"node-fetch": "^3.3.2",
|
|
85
|
-
"open": "^10.
|
|
84
|
+
"open": "^10.2.0",
|
|
86
85
|
"papaparse": "^5.5.3",
|
|
87
86
|
"tiny-spinner": "^2.0.5",
|
|
88
87
|
"xml2js": "^0.6.2"
|
package/CITATION.cff
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
cff-version: 1.2.0
|
|
2
|
-
message: 'If you use this software, please cite it as below.'
|
|
3
|
-
title: '@irfanshadikrishad/anilist'
|
|
4
|
-
authors:
|
|
5
|
-
- name: 'Irfan Shadik Rishad'
|
|
6
|
-
orcid: '0009-0001-6745-5291'
|
|
7
|
-
date-released: '2024-10-12'
|
|
8
|
-
repository-code: 'https://github.com/irfanshadikrishad/anilist'
|
|
File without changes
|
|
File without changes
|