UNPKG

wakitsu

Version:

Hobby project for managing anime watch list on Kitsu through CLI

239 lines 9.04 kB
import { CLI, CLIFlag } from '../cli.js'; import { Kitsu } from '../../kitsu/kitsu.js'; import { Printer } from '../../printer/printer.js'; import { Config } from '../../config.js'; export class FindAnimeFlag extends CLIFlag { name = ['a', 'anime']; type = 'multiArg'; helpAliases = [ ...this.name, 'find anime', 'get anime', 'lookup anime', 'search anime', 'add anime', 'track anime', 'drop anime', 'drop', 'add to watch list', 'add anime to watch list', ]; getHelpLogs() { return [ ['h1', ['Anime']], [ 'p', 'This flag allows you to perform different operations with anime. The ' + 'available operations are: ;x;adding;bk;, ;x;finding;bk;, and ;x;dropping ' + ';bk;anime. For a more detailed description of each, check the syntax ' + 'below.', ], null, ]; } getSyntaxHelpLogs() { return [ ['h2', ['Usage']], ['s', ['a', 'anime'], '<find|add|drop> ;bm;<query>'], null, ['h2', ['Details']], [ 'd', [ 'find', 'Looks up the ;bm;query ;x;in your cache; if an anime is found, then ' + 'that anime is retrieved from Kitsu to display more detailed info.', ], ], null, [ 'd', [ 'add', 'Looks up the ;bm;query ;x;on Kitsu, retrieves the top ;bw;5 ;x;results ' + 'and allows you to select which anime to add.', ], 1, ], null, [ 'd', [ 'drop', 'Looks up the ;bm;query ;bk;in your cache; if the anime is found, ' + 'then that anime is dropped from your watch list on Kitsu.', ], ], null, ['h2', ['Examples']], ['e', ['a', 'find ;bm;boku no hero']], ['e', ['anime', 'find ;bm;re zero']], null, ['e', ['a', 'add ;bm;berserk']], ['e', ['anime', 'add ;bm;dragonball z']], null, ['e', ['a', 'drop ;bm;bleach']], ['e', ['anime', 'drop ;bm;mushoku tensei']], ]; } async exec() { const [arg, ...query] = CLI.nonFlagArgs; const hasValidArgs = CLI.validateSingleArg({ args: ['find', 'add', 'drop'], argHasArgs: true, flag: this, }); if (hasValidArgs) { if (arg == 'find') { const queryStr = query.join(' '); const animeList = await findAnime(queryStr); Printer.print([ null, ['h2', ['Details Found'], 3], ['py', ['Query', queryStr], 4], ['py', ['Status', `;m;${animeList.length} ;g;Anime Found`], 3], null, ['hl', 'c', 70, 3], ...getAnimeListLogs(animeList), ]); } if (arg == 'add') { return addAnime(); } if (arg == 'drop') { return dropAnime(query.join(' ')); } } } } async function findAnime(query) { const stopLoader = Printer.printLoader('Find Anime'); const animeList = await Kitsu.findLibraryAnime(query); stopLoader(); if (!animeList.length) { Printer.printWarning('The anime is either not in your cache or your search terms were ' + 'incorrectly spelled', 'No Entries Found', 3); process.exit(0); } return animeList; } async function addAnime() { const isCurrentlyAiring = await Printer.promptYesNo('Are you looking for a currently airing anime'); const stopLoader = Printer.printLoader('Generating Anime Selection'); const animeQuery = CLI.nonFlagArgs.slice(1).join(' '); const animeResults = await Kitsu.findAnime(animeQuery, isCurrentlyAiring ? 'current' : 'finished'); stopLoader(); if (!animeResults.length) { Printer.printWarning('Try using a different query strategy like an alternate title, if ' + `you don't know the exact Japanese name. You could also try describing ` + 'the anime in a short phrase which might be in its description.', 'Anime Not Found', 3); process.exit(0); } displayAnimeSelection(animeResults); const userChoice = await promptAnimeSelection(animeResults.length); if (!userChoice) { Printer.printWarning('Operation cancelled manually', 'Aborted', 3); process.exit(0); } Printer.print([null, ['h3', ['Adding Anime']]]); if (userChoice > 0) { const anime = animeResults[userChoice - 1]; const foundAnime = Config.getKitsuProp('cache').find((a) => anime.slug == a.slug); if (foundAnime) { Printer.printWarning('Anime already added to watch list', 'Aborted', 3); process.exit(0); } const resp = await Kitsu.trackAnime(anime.id); const { synopsis: _, avgRating: __, ...cacheAnime } = anime; Config.getKitsuProp('cache').push({ libID: resp.data.id, ...cacheAnime, epProgress: 0, }); Config.save(); Printer.printInfo(`Added ;b;${anime.jpTitle};g; to Watch List`, 'Success', 3); } } async function dropAnime(query) { const animeList = Kitsu.findCachedAnime(query); if (!animeList.length) { return Printer.printWarning([`The anime ";y;${query};c;" could not be found.`], 'Anime Not Found'); } const [[anime]] = animeList; Printer.print([ null, ['h3', ['Anime Found']], null, ['py', ['Title JP', anime.jpTitle]], ['py', ['Title EN', `;x;${anime.enTitle}`]], ['py', ['Progress', `;bg;${anime.epProgress}`]], ]); if (!(await Printer.promptYesNo(`Do you want to drop the above anime`))) { return Printer.printWarning('User cancelled the operation manually.', 'Operation Aborted', 3); } const stopLoader = Printer.printLoader('Dropping Anime', 2); const { data } = await Kitsu.dropAnime(anime.libID); if (data.attributes.status != 'dropped') { stopLoader(); return Printer.printError('Failed to update status; unknown reason.'); } Kitsu.removeAnimeFromCache(anime); stopLoader(); Printer.printInfo(`;x;${anime.jpTitle} ;g;has been dropped`, 'Success', 3); } function getAnimeListLogs(animeList) { const listLogs = []; for (const anime of animeList) { const totalEps = anime.epCount ? anime.epCount : `;r;unknown`; const synonyms = anime.synonyms.map((s) => ['py', [';b;Alt Title', s], 5]); const logs = [ ['py', ['Title JP', anime.title_jp], 6], ['py', ['Title EN', anime.title_en || ';r;undefined'], 6], ...synonyms, ['py', ['Progress', `;g;${anime.epProgress} ;by;/ ;m;${totalEps}`], 6], ['py', ['My Rating', `;g;${anime.rating ? anime.rating : ';y;Not Rated'}`], 5], ['py', ['Avg. Rating', `;g;${anime.avgRating}`], 3], ['py', ['Synopsis', `${anime.synopsis.trim()}`], 6], ['', `;b;Link: ;x;${anime.link}`, 13], ['hl', 'c', 70, 3], ]; listLogs.push(...logs); } return listLogs; } function displayAnimeSelection(animeArray) { Printer.print([null]); animeArray.forEach((anime, i) => { const synonyms = anime.synonyms.map((s) => ['py', ['Alias', `;b;${s}`], 6]); Printer.print([ ['h1', [`${i + 1}`], 3], ['py', ['Title JP', `;y;${anime.jpTitle}`], 3], ['py', ['Title EN', anime.enTitle ?? ';m;None'], 3], ['py', ['Title US', anime.usTitle || ';m;None'], 3], ...synonyms, ['', `;c;Link: ;x;https://kitsu.app/anime/${anime.slug}`, 10], null, ]); }); } async function promptAnimeSelection(animeResultsLength) { let userNumber = 0; while (userNumber == 0) { const userChoice = await Printer.prompt('Type the ;x;Number ;bb;of an Anime to add:'); if (userChoice == '') { return 0; } const possibleNumChoice = Number(userChoice); if (!possibleNumChoice || possibleNumChoice > animeResultsLength || possibleNumChoice < 0) { Printer.print([null]); Printer.printError('Pick a number between ;bw;1 ;y;and ;bw;5 ;y;or hit enter to skip', 'Invalid Choice', 3); continue; } userNumber = possibleNumChoice; break; } return userNumber; } //# sourceMappingURL=flag-anime.js.map