@irfanshadikrishad/anilist 1.4.11 → 1.5.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/README.md CHANGED
@@ -64,6 +64,7 @@ anilist login -i <client-id> -s <client-secret>
64
64
  | `export` <br> _(alias: `exp`)_ | `-a, --anime` <br> `-m, --manga` | Export anime or manga list in JSON, CSV or XML (MyAnimeList/AniDB) |
65
65
  | `import` <br> _(alias: `imp`)_ | `-a, --anime` <br> `-m, --manga` | Import anime or manga list from exported JSON, MyAnimeList (XML) or AniDB (json-large) |
66
66
  | `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 |
67
+ | `move` <br> _(alias: `mv`)_ | `-a, --anime` <br> `-m, --manga` | Move entire list to another list |
67
68
 
68
69
  #### Command Breakdown:
69
70
 
@@ -23,4 +23,8 @@ declare class MyAnimeList {
23
23
  declare class AniDB {
24
24
  static importAnime(): Promise<void>;
25
25
  }
26
- export { AniDB, AniList, MyAnimeList };
26
+ declare class MoveTo {
27
+ static AnimeList(): Promise<void>;
28
+ static MangaList(): Promise<void>;
29
+ }
30
+ export { AniDB, AniList, MoveTo, MyAnimeList };
@@ -12,14 +12,16 @@ 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";
15
16
  import { Auth } from "./auth.js";
16
17
  import { fetcher } from "./fetcher.js";
17
- import { addAnimeToListMutation, addMangaToListMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, } from "./mutations.js";
18
+ import { addAnimeToListMutation, addMangaToListMutation, moveListMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, } from "./mutations.js";
18
19
  import { animeDetailsQuery, animeSearchQuery, currentUserAnimeList, currentUserMangaList, malIdToAnilistAnimeId, malIdToAnilistMangaId, mangaDetailsQuery, mangaSearchQuery, popularQuery, trendingQuery, upcomingAnimesQuery, userActivityQuery, userFollowersQuery, userFollowingQuery, userQuery, } from "./queries.js";
19
20
  import { responsiveOutput } from "./truncate.js";
20
21
  import { AniListMediaStatus, } from "./types.js";
21
22
  import { Validate } from "./validation.js";
22
23
  import { anidbToanilistMapper, formatDateObject, getDownloadFolderPath, getNextSeasonAndYear, getTitle, logUserDetails, removeHtmlAndMarkdown, saveJSONasCSV, saveJSONasJSON, saveJSONasXML, selectFile, simpleDateFormat, timestampToTimeAgo, } from "./workers.js";
24
+ const spinner = new Spinner();
23
25
  class AniList {
24
26
  static importAnime() {
25
27
  return __awaiter(this, void 0, void 0, function* () {
@@ -1131,4 +1133,125 @@ class AniDB {
1131
1133
  });
1132
1134
  }
1133
1135
  }
1134
- export { AniDB, AniList, MyAnimeList };
1136
+ class MoveTo {
1137
+ static AnimeList() {
1138
+ return __awaiter(this, void 0, void 0, function* () {
1139
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1140
+ try {
1141
+ spinner.start(`Fetching your anime list...`);
1142
+ const animeList = yield fetcher(currentUserAnimeList, {
1143
+ id: yield Auth.MyUserId(),
1144
+ });
1145
+ 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) {
1146
+ spinner.stop();
1147
+ 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;
1148
+ const { selectedList } = yield inquirer.prompt([
1149
+ {
1150
+ type: "list",
1151
+ name: "selectedList",
1152
+ message: "Select a list to move:",
1153
+ choices: list.map((list) => list.name),
1154
+ },
1155
+ ]);
1156
+ const selectedEntries = list.find((list) => list.name === selectedList);
1157
+ if (!selectedEntries || selectedEntries.entries.length === 0) {
1158
+ return console.log("\nNo anime entries found in the selected list.");
1159
+ }
1160
+ const { toMoveList } = yield inquirer.prompt([
1161
+ {
1162
+ type: "list",
1163
+ name: "toMoveList",
1164
+ message: "Select a list to move to:",
1165
+ choices: list.map((list) => list.name),
1166
+ },
1167
+ ]);
1168
+ const entries = selectedEntries.entries;
1169
+ for (const entry of entries) {
1170
+ const mediaId = (_e = entry === null || entry === void 0 ? void 0 : entry.media) === null || _e === void 0 ? void 0 : _e.id;
1171
+ const status = entry === null || entry === void 0 ? void 0 : entry.status;
1172
+ // const progress = entry?.progress
1173
+ // console.log(mediaId, status)
1174
+ const response = yield fetcher(moveListMutation, {
1175
+ mediaId,
1176
+ status,
1177
+ customList: toMoveList,
1178
+ });
1179
+ if (response === null || response === void 0 ? void 0 : response.data) {
1180
+ const moved = (_f = response === null || response === void 0 ? void 0 : response.data) === null || _f === void 0 ? void 0 : _f.SaveMediaListEntry;
1181
+ console.log(`✅ Entry ${moved === null || moved === void 0 ? void 0 : moved.id}. Moved from ${selectedList} to ${toMoveList}.`);
1182
+ }
1183
+ else {
1184
+ 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"}`);
1185
+ }
1186
+ }
1187
+ }
1188
+ else {
1189
+ console.log(`\nHey, ${yield Auth.MyUserName()}. Your anime list seems to be empty.`);
1190
+ }
1191
+ }
1192
+ catch (error) {
1193
+ console.error(`\nError from AnimeList. ${error.message}`);
1194
+ }
1195
+ });
1196
+ }
1197
+ static MangaList() {
1198
+ return __awaiter(this, void 0, void 0, function* () {
1199
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1200
+ try {
1201
+ spinner.start(`Fetching your manga list...`);
1202
+ const mangaList = yield fetcher(currentUserMangaList, {
1203
+ id: yield Auth.MyUserId(),
1204
+ });
1205
+ 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) {
1206
+ spinner.stop();
1207
+ 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;
1208
+ const { selectedList } = yield inquirer.prompt([
1209
+ {
1210
+ type: "list",
1211
+ name: "selectedList",
1212
+ message: "Select a list to move:",
1213
+ choices: list.map((list) => list.name),
1214
+ },
1215
+ ]);
1216
+ const selectedEntries = list.find((list) => list.name === selectedList);
1217
+ if (!selectedEntries || selectedEntries.entries.length === 0) {
1218
+ return console.log("\nNo manga entries found in the selected list.");
1219
+ }
1220
+ const { toMoveList } = yield inquirer.prompt([
1221
+ {
1222
+ type: "list",
1223
+ name: "toMoveList",
1224
+ message: "Select a list to move to:",
1225
+ choices: list.map((list) => list.name),
1226
+ },
1227
+ ]);
1228
+ const entries = selectedEntries.entries;
1229
+ for (const entry of entries) {
1230
+ const mediaId = (_e = entry === null || entry === void 0 ? void 0 : entry.media) === null || _e === void 0 ? void 0 : _e.id;
1231
+ const status = entry === null || entry === void 0 ? void 0 : entry.status;
1232
+ // console.log(mediaId, status)
1233
+ const response = yield fetcher(moveListMutation, {
1234
+ mediaId,
1235
+ status,
1236
+ customList: toMoveList,
1237
+ });
1238
+ if (response === null || response === void 0 ? void 0 : response.data) {
1239
+ const moved = (_f = response === null || response === void 0 ? void 0 : response.data) === null || _f === void 0 ? void 0 : _f.SaveMediaListEntry;
1240
+ console.log(`✅ Entry ${moved === null || moved === void 0 ? void 0 : moved.id}. Moved from ${selectedList} to ${toMoveList}.`);
1241
+ }
1242
+ else {
1243
+ 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"}`);
1244
+ }
1245
+ }
1246
+ }
1247
+ else {
1248
+ console.log(`\nHey, ${yield Auth.MyUserName()}. Your manga list seems to be empty.`);
1249
+ }
1250
+ }
1251
+ catch (error) {
1252
+ console.error(`\nError from MangaList. ${error.message}`);
1253
+ }
1254
+ });
1255
+ }
1256
+ }
1257
+ export { AniDB, AniList, MoveTo, MyAnimeList };
@@ -7,4 +7,5 @@ declare const saveMangaWithProgressMutation = "\nmutation ($mediaId: Int, $progr
7
7
  declare const toggleFollowMutation = "mutation ($userId: Int!) { ToggleFollow(userId: $userId) { id name isFollower isFollowing } }";
8
8
  declare const deleteMediaEntryMutation = "mutation($id: Int!) { DeleteMediaListEntry(id: $id) { deleted } }";
9
9
  declare const deleteMangaEntryMutation = "mutation($id: Int) {\n DeleteMediaListEntry(id: $id) { deleted }\n}";
10
- export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, toggleFollowMutation, };
10
+ declare const moveListMutation = "mutation ($mediaId: Int, $status: MediaListStatus, $customList: String) {\n SaveMediaListEntry(\n mediaId: $mediaId\n status: $status\n customLists: [$customList]\n hiddenFromStatusLists: true\n ) { id status hiddenFromStatusLists customLists media { title { romaji english native userPreferred } } }\n}";
11
+ export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, moveListMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, toggleFollowMutation, };
@@ -34,4 +34,12 @@ const deleteMediaEntryMutation = `mutation($id: Int!) { DeleteMediaListEntry(id:
34
34
  const deleteMangaEntryMutation = `mutation($id: Int) {
35
35
  DeleteMediaListEntry(id: $id) { deleted }
36
36
  }`;
37
- export { addAnimeToListMutation, addMangaToListMutation, deleteActivityMutation, deleteMangaEntryMutation, deleteMediaEntryMutation, saveAnimeWithProgressMutation, saveMangaWithProgressMutation, saveTextActivityMutation, toggleFollowMutation, };
37
+ const moveListMutation = `mutation ($mediaId: Int, $status: MediaListStatus, $customList: String) {
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, };
@@ -347,4 +347,18 @@ type MangaDetails = {
347
347
  };
348
348
  errors?: Error;
349
349
  };
350
- export { Activity, AniListMediaStatus, AnimeDetails, AnimeList, AnimeSearchResponse, DateMonthYear, DeleteMangaResponse, DeleteMediaListResponse, List, MALAnimeStatus, MALAnimeXML, MALMangaStatus, MalIdToAnilistIdResponse, MangaDetails, MediaEntry, MediaList, MediaListCollectionResponse, MediaListEntry, MediaTitle, MediaWithProgress, Myself, SaveMediaListEntryResponse, SaveTextActivityResponse, ToggleFollowResponse, User, UserActivitiesResponse, UserFollower, UserFollowing, UserResponse, saveAnimeWithProgressResponse, };
350
+ type MoveListsResponse = {
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, };
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 } from "./helpers/lists.js";
14
+ import { AniList, MoveTo } from "./helpers/lists.js";
15
15
  import { getCurrentPackageVersion } from "./helpers/workers.js";
16
16
  const cli = new Command();
17
17
  cli
@@ -234,4 +234,25 @@ cli
234
234
  }
235
235
  }
236
236
  }));
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
+ }));
237
258
  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.4.11",
5
+ "version": "1.5.0",
6
6
  "main": "./bin/index.js",
7
7
  "type": "module",
8
8
  "types": "./bin/index.d.ts",
@@ -55,13 +55,13 @@
55
55
  "license": "MPL-2.0",
56
56
  "devDependencies": {
57
57
  "@babel/preset-env": "^7.26.9",
58
- "@eslint/js": "^9.25.0",
58
+ "@eslint/js": "^9.25.1",
59
59
  "@types/jest": "^29.5.14",
60
60
  "@types/node": "^22.14.1",
61
61
  "@types/papaparse": "^5.3.15",
62
62
  "@types/xml2js": "^0.4.14",
63
- "@typescript-eslint/eslint-plugin": "^8.30.1",
64
- "eslint": "^9.25.0",
63
+ "@typescript-eslint/eslint-plugin": "^8.31.0",
64
+ "eslint": "^9.25.1",
65
65
  "globals": "^16.0.0",
66
66
  "jest": "^29.7.0",
67
67
  "prettier": "^3.5.3",