ani-cli-npm 1.4.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,586 @@
1
+ #! /usr/bin/env node
2
+
3
+ const {emitWarning} = process;
4
+
5
+ process.emitWarning = (warning, ...args) => {
6
+ if (args[0] === 'ExperimentalWarning') {
7
+ return;
8
+ }
9
+
10
+ if (args[0] && typeof args[0] === 'object' && args[0].type === 'ExperimentalWarning') {
11
+ return;
12
+ }
13
+
14
+ return emitWarning(warning, ...args);
15
+ };
16
+ const fetch = require('node-fetch');
17
+ const PlayerController = require("media-player-controller")
18
+ const open = require("open")
19
+ const prompt = require("simple-input");
20
+ const getAppDataPath = require("appdata-path")
21
+ const fs = require("fs")
22
+ const dl = require("download-file-with-progressbar");
23
+
24
+
25
+
26
+ let config = {
27
+ player: "BROWSER",
28
+ proxy: "",
29
+ user_agent: "Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0",
30
+ most_recent: {
31
+ episode_number: 0,
32
+ anime_id: "",
33
+ episodes: []
34
+ },
35
+ download_folder: "./",
36
+ download_progress_bar: true
37
+ }
38
+
39
+
40
+ function read_config(){
41
+ try{
42
+ try {
43
+ config = JSON.parse(fs.readFileSync(getAppDataPath() + "/ani-cli-npm.conf")) //getAppDataPath()
44
+ if (!config.hasOwnProperty("player")) config.player = "BROWSER";
45
+ if (!config.hasOwnProperty("user_agent")) config.user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0";
46
+ if (!config.hasOwnProperty("proxy")) config.user_agent = "";
47
+ if (!config.hasOwnProperty("most_recent")) config.most_recent = null;
48
+ if (!config.hasOwnProperty("download_folder")) config.download_folder = "./";
49
+ if (!config.hasOwnProperty("download_progress_bar")) config.download_progress_bar = true;
50
+ fs.writeFileSync(getAppDataPath() + "/ani-cli-npm.conf", JSON.stringify(config))
51
+ } catch {
52
+ fs.writeFileSync(getAppDataPath() + "/ani-cli-npm.conf", JSON.stringify(config))
53
+ }
54
+ }catch{
55
+ console.log(colors.Red, "Failed to read config file")
56
+ }
57
+ }
58
+
59
+ function write_config(){
60
+ try{
61
+ fs.writeFileSync(getAppDataPath() + "/ani-cli-npm.conf", JSON.stringify(config))
62
+ }catch{
63
+ console.log(colors.Red, "Failed to write to config file")
64
+ }
65
+ }
66
+
67
+
68
+
69
+ const gogohd_url="https://gogohd.net/"
70
+ const base_url="https://animixplay.to"
71
+ const colors = {
72
+ Black: "\x1b[30m%s\x1b[0m",
73
+ Red: "\x1b[31m%s\x1b[0m",
74
+ Green: "\x1b[32m%s\x1b[0m",
75
+ Yellow: "\x1b[33m%s\x1b[0m",
76
+ Blue: "\x1b[34m%s\x1b[0m",
77
+ Magenta: "\x1b[35m%s\x1b[0m",
78
+ Cyan: "\x1b[36m%s\x1b[0m",
79
+ White: "\x1b[37m%s\x1b[0m"
80
+ }
81
+
82
+
83
+ //const HttpsProxyAgent = require('https-proxy-agent');
84
+ //let proxyAgent = new HttpsProxyAgent(config.proxy);
85
+
86
+
87
+ async function config_(temp){
88
+ console.clear()
89
+ console.log(colors.Blue, "ANI-CLI-NPM \n")
90
+ console.log(colors.Yellow, "Config:\n")
91
+ console.log(colors.Cyan, `1) Player; ${temp.player}`)
92
+ console.log(colors.Cyan, `2) Proxy; ${temp.proxy}`)
93
+ console.log(colors.Cyan, `3) User agent; ${temp.user_agent}`)
94
+ console.log(colors.Cyan, `4) Download folder; ${config.download_folder}`)
95
+ console.log(colors.Cyan, "5) Save and exit")
96
+ console.log(colors.Cyan, "6) Exit without saving changes")
97
+ let choice = parseInt(await input(""));
98
+ switch (choice){
99
+ case 1:
100
+ console.log(colors.Cyan, `1) VLC (default)`)
101
+ console.log(colors.Cyan, `2) Browser`)
102
+ console.log(colors.Cyan, `3) MPV`)
103
+ let player = parseInt(await input("New Player;"))
104
+ switch (player){
105
+ case 1:
106
+ temp.player = "VLC"
107
+ break
108
+ case 2:
109
+ temp.player = "BROWSER"
110
+ break
111
+ case 3:
112
+ temp.player = "MPV"
113
+ break
114
+ }
115
+ return temp,0
116
+ case 2:
117
+ temp.proxy = (await(input("New Proxy;"))).replaceAll(" ", "")
118
+ return temp, 0
119
+ case 3:
120
+ temp.user_agent = await(input("New User agent;"))
121
+ return temp, 0
122
+ case 4:
123
+ temp.download_folder = await(input("New download folder: "))
124
+ return temp, 0
125
+ case 5:
126
+ return temp, 1
127
+ case 6:
128
+ return temp, 2
129
+ }
130
+ }
131
+
132
+ async function input(message){
133
+ if (message){
134
+ console.log(colors.Magenta,message)
135
+ }
136
+ return await prompt(">")
137
+ }
138
+
139
+ async function curl(url, method="GET", redirect = false){
140
+ //try{
141
+ let response = await fetch(url, {
142
+ //"agent": proxyAgent,
143
+ "headers": {
144
+ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/100.0',
145
+ "X-Requested-With": "XMLHttpRequest"
146
+ },
147
+ "referrerPolicy": "origin",
148
+ "body": null,
149
+ "method": method,
150
+ "redirect": 'follow',
151
+ "follow": 10,
152
+ }).catch(async function(err) {
153
+ console.warn(colors.Red, `Something went wrong connecting to ${url}.`);
154
+ await search();
155
+ process.exit()
156
+ })
157
+ if (redirect){
158
+ return response.url
159
+ }else{
160
+ return await response.text()
161
+ }
162
+ /*}catch{
163
+ console.log(colors.Red, "Something went wrong in curl()")
164
+ await main()
165
+ }*/
166
+
167
+ }
168
+
169
+ async function _continue(){
170
+ let link = await get_video_link(config.most_recent.episodes[config.most_recent.episode_number])
171
+ await play(link, config.most_recent)
172
+ process.exit()
173
+ }
174
+
175
+
176
+ function RegexParse(str, rule) {
177
+ let escapeRegex = (str) => str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
178
+ return new RegExp("^" + rule.split("*").map(escapeRegex).join(".*") + "$").test(str);
179
+ }
180
+
181
+ async function search_anime(search){
182
+ let filter = "*<ahref=\"/category/*\"title=\"*\">"
183
+ let html = (await curl("https://gogoanime.dk//search.html?keyword="+search)).split("\n")
184
+ let lines = []
185
+ for (x in html){
186
+ html[x] = html[x].replaceAll(/ /g,'').replaceAll(/\t/g,'')
187
+ if (RegexParse(html[x], filter)){
188
+ html[x] = html[x].slice(html[x].indexOf("/category/")+10);
189
+ html[x] = html[x].slice(0, html[x].indexOf("\"title="));
190
+ lines.push(html[x])
191
+ }
192
+ }
193
+ if (!lines[0]){
194
+ lines.pop()
195
+ }
196
+
197
+
198
+ return lines
199
+ }
200
+
201
+ async function episode_list(anime_id){
202
+ let html = (await curl(base_url+"/v1/"+anime_id)).split("\n")
203
+ let lines = ""
204
+
205
+ for (let x in html){
206
+ if(RegexParse(html[x], "*<div id=\"epslistplace\"*")){
207
+ lines = (html[x])
208
+ }
209
+ }
210
+
211
+ lines = lines.slice(55, lines.length).replace("}</div>", "")
212
+ lines = "{" + lines.slice(lines.indexOf(",")+1, lines.length) + "}"
213
+ lines = JSON.parse(lines)
214
+
215
+ let json = []
216
+ for (x in lines){
217
+ json.push(lines[x])
218
+ }
219
+ return json
220
+ }
221
+
222
+ function download(link, file_name){
223
+ if (link.includes("m3u8")) console.log(colors.Red, "Warning: Animixplay will download an m3u8 file. This will require some extra steps to play. It is advised to use a 3rd party website or tool to download these from the link.");
224
+ let option = {
225
+ filename: link.includes("m3u8") ? file_name.replace("mp4", "m3u8") : file_name,
226
+ dir: config.download_folder,
227
+ onDone: (info)=>{
228
+ console.log(colors.Green, `\n -- Download finished -- \nLocation: ${info.path}\nSize: ${Math.round(info.size/100000)*10} Bytes`);
229
+ return 0;
230
+ },
231
+ onError: (err) => {
232
+ console.log(colors.Red, 'error', err);
233
+ },
234
+ onProgress: (curr, total) => {
235
+ process.stdout.clearLine(0);
236
+ process.stdout.cursorTo(0);
237
+ if (config.download_progress_bar){
238
+ process.stdout.write("\x1b[32m -- "+(curr / total * 100).toFixed(2)+"% "+"#". repeat(Math.ceil((curr / total)*(process.stdout.columns-20)))+"~".repeat(Math.ceil((process.stdout.columns-20) - (curr / total)*(process.stdout.columns-20)))+" -- \x1b[0m")
239
+
240
+ }else{
241
+ process.stdout.write(('\x1b[32m -- progress '+ (curr / total * 100).toFixed(2) + '% -- \x1b[0m'));
242
+ }
243
+ },
244
+ }
245
+ console.log(colors.White, `${option.dir}/${option.filename}`)
246
+
247
+ return dl(link, option);
248
+
249
+ }
250
+
251
+ async function selection(options, prompt, extra_options = []){
252
+ let selection = 0
253
+ while (true){
254
+ selection = (await input(prompt))
255
+ if ((selection <= options && selection >= 1) || extra_options.includes(selection)){
256
+ break
257
+ }
258
+ console.log(colors.Red,`Please input a valid option.`)
259
+ }
260
+ return selection
261
+ }
262
+
263
+ async function process_search(query) {
264
+ console.log(colors.Yellow, "Searching: "+query)
265
+
266
+ let search_results = await search_anime(query)
267
+ if (!search_results[0]) {
268
+ console.log(colors.Red, "No results.")
269
+ await main()
270
+ process.exit()
271
+ } else {
272
+ for (x in search_results) {
273
+ console.log(colors.Cyan,`${parseInt(x)+1})${" ".repeat(((search_results.length).toString().length+1)-((parseInt(x)+1).toString().length))}${search_results[x].replaceAll("-", " ")}`)
274
+ }
275
+ }
276
+
277
+ let anime_id = search_results[await selection(search_results.length, "Please select an anime.")-1]
278
+ let episodes = await episode_list(anime_id)
279
+ let episode_number = 0;
280
+ if (episodes.length > 1){
281
+ episode_number = (await selection(episodes.length, `Please select an episode (1-${episodes.length}).`))-1
282
+ }
283
+ return {
284
+ anime_id: anime_id,
285
+ episodes: episodes,
286
+ episode_number: episode_number
287
+ }
288
+ }
289
+
290
+ async function get_video_link(episode_dpage){
291
+ let id = episode_dpage.replace("//gogohd.net/streaming.php?id=","")
292
+ id = id.slice(0, id.indexOf("="))
293
+ let link = await generate_link(1,id)
294
+ if (!link){
295
+ link = await generate_link(2,id)
296
+ }
297
+ return link
298
+ }
299
+
300
+ async function generate_link(provider, id){
301
+ let html = ""
302
+ let provider_name = ""
303
+ switch (provider) {
304
+ case 1:
305
+ html = await curl(`${gogohd_url}streaming.php?id=${id}`)
306
+ provider_name = 'Xstreamcdn'
307
+ console.log(colors.Yellow, `Fetching ${provider_name} links...`)
308
+ html = html.split("\n")
309
+ let fb_id = ""
310
+ for (x in html){
311
+ if (RegexParse(html[x], "*<li class=\"linkserver\" data-status=\"1\" data-video=\"https://fembed9hd.com/v/*")){
312
+ fb_id = html[x].slice(html[x].indexOf("/v/")+3, html[x].indexOf("\">X"))
313
+ break
314
+ }
315
+ }
316
+ if (!fb_id){
317
+ console.log("Error, no fb_id found.")
318
+ return 0
319
+ }
320
+
321
+ //let refr = "https://fembed-hd.com/v/"+fb_id
322
+ let post = await curl("https://fembed-hd.com/api/source/"+fb_id, "POST")
323
+ post = post.slice(post.indexOf(",\"data\":[{\"file\":\"")+18, post.length)
324
+ post = post.slice(0, post.indexOf("\"")).replaceAll("\\/","/")
325
+ return post
326
+ case 2:
327
+ provider_name = 'Animixplay'
328
+ let buffer = new Buffer(id)
329
+ let enc_id = buffer.toString("base64")
330
+ buffer = new Buffer(id+"LTXs3GrU8we9O"+enc_id)
331
+ let ani_id = buffer.toString("base64")
332
+ buffer = Buffer.from((await curl(`${base_url}/api/live${ani_id}`, "GET", true)).split("#")[1], "base64")
333
+ if (config.player === "BROWSER"){
334
+ return `${base_url}/api/live${ani_id}`
335
+ }
336
+ return buffer.toString("utf-8") //TODO m3u8 player
337
+
338
+
339
+ }
340
+ }
341
+
342
+ async function post_play(link, anime, player = null){
343
+ while (true){
344
+ config.most_recent = anime
345
+ write_config()
346
+
347
+ console.log(colors.Yellow, `Playing episode ${anime.episode_number+1} of ${anime.anime_id.replaceAll("-", " ")}\n`)
348
+ console.log(colors.Cyan, "1/l) Show Link")
349
+ console.log(colors.Cyan, "2/n) Next Episode")
350
+ console.log(colors.Cyan, "3/p) Prev Episode")
351
+ console.log(colors.Cyan, "4/d) Download")
352
+ console.log(colors.Cyan, "5/q) Quit")
353
+ switch (await selection(5, "select;", ["l", "n", "p", "d", "q"])){
354
+ case "l":
355
+ case "1":
356
+ console.log(colors.Yellow, "Link: "+link)
357
+ break
358
+ case "n":
359
+ case "2":
360
+ if (anime.episode_number > anime.episodes.length-2){
361
+ console.log(colors.Red, "Damn, all out of episodes?")
362
+ break
363
+ }
364
+ anime.episode_number += 1
365
+ link = await get_video_link(anime.episodes[anime.episode_number])
366
+ await play(link, anime, player)
367
+ process.exit()
368
+ break
369
+ //EVEN MORE NEEDLESS QUIT STATEMENTS!!!!!!
370
+ case "p":
371
+ case "3":
372
+ if (anime.episode_number < 2){
373
+ console.log(colors.Red, "Error; Episode 0 is only available for premium members")
374
+ break
375
+ }
376
+ anime.episode_number -= 1
377
+ link = await get_video_link(anime.episodes[anime.episode_number])
378
+ await play(link, anime, player)
379
+ process.exit()
380
+ break
381
+ case "d":
382
+ case "4":
383
+ console.log(colors.Yellow, "Beware of the dysfunctional await clause.")
384
+ await download(link, anime.anime_id+(anime.episode_number+1)+".mp4")
385
+ post_play(link, anime, player)
386
+ break
387
+ case "q":
388
+ case "5":
389
+ console.clear()
390
+ await main()
391
+ process.exit()
392
+ break
393
+ }
394
+ }
395
+ }
396
+
397
+ async function play(link, anime, player){
398
+ console.clear()
399
+ console.log(colors.Blue, "ANI-CLI-NPM \n")
400
+ if (config.player === "VLC"){
401
+ if (!player){
402
+ console.log(colors.Yellow, "Loading VLC... ")
403
+ player = new PlayerController({
404
+ app: 'vlc',
405
+ args: ['--fullscreen'],
406
+ media: link
407
+ });
408
+ await player.launch(err => {
409
+ if (err) return console.error(err.message);
410
+ });
411
+ }else{
412
+ player.quit()
413
+ console.log(colors.Yellow, "Loading VLC... ")
414
+ player = new PlayerController({
415
+ app: 'vlc',
416
+ args: ['--fullscreen'],
417
+ media: link
418
+ });
419
+ await player.launch(err => {
420
+ if (err) return console.error(err.message);
421
+ });
422
+ }
423
+
424
+ await post_play(link, anime, player)
425
+ process.exit()
426
+
427
+
428
+ }else if (config.player === "BROWSER"){
429
+ console.log(colors.Yellow, "Opening video in browser... ")
430
+ open(link)
431
+ await post_play(link, anime)
432
+ process.exit()
433
+ }else if (config.player === "MPV"){
434
+ if (!player){
435
+ console.log(colors.Yellow, "Loading MPV... ")
436
+ player = new PlayerController({
437
+ app: 'mpv',
438
+ args: ['--fullscreen'],
439
+ media: link
440
+ });
441
+ await player.launch(err => {
442
+ if (err) return console.error(err.message);
443
+ });
444
+ }else{
445
+ player.load(link)
446
+ }
447
+
448
+ await post_play(link, anime, player)
449
+ process.exit()
450
+ }
451
+ }
452
+
453
+ async function search(){
454
+ console.log(colors.Magenta, "Search...")
455
+ let choice = await input("")
456
+ let anime = await process_search(choice)
457
+
458
+ console.log("\n")
459
+
460
+ console.log(colors.Blue, "Indexing video...")
461
+ let link = await get_video_link(anime.episodes[anime.episode_number])
462
+ if (!link){
463
+ console.log(colors.Red, "Np link for this episode found?")
464
+ console.log(colors.Yellow, "^ at current this is due to not all of the required video repos being implemented.")
465
+ console.log(colors.Yellow, "Sorry for any inconvenience, this should soon by implemented. We appreciate your patience.")
466
+ process.exit()
467
+ }
468
+ console.log(colors.Blue, `Episode ${anime.episode_number+1} of ${anime.anime_id.replaceAll("-", " ")} found.\n`)
469
+ if (link.includes("animixplay.to") && config.player === "VLC"){
470
+ console.log(colors.Red, "Warning; animix.to uses m3u8 playlists. Without custom plugins, VLC will not be able to play this file format. It is recomended to use another player for this show/film.")
471
+ }
472
+ console.log(colors.Cyan, "1/p) Play")
473
+ console.log(colors.Cyan, "2/d) Download")
474
+ console.log(colors.Cyan, "3/l) Show Link")
475
+ console.log(colors.Cyan, "4/q) quit")
476
+ choice = (await selection(4, "select;", ["p", "d", "l", "q"]))
477
+ switch (choice){
478
+ case "p":
479
+ case "1":
480
+ await play(link, anime, null)
481
+ break
482
+ case "d":
483
+ case "2":
484
+ await download(link, anime.anime_id+(anime.episode_number+1)+".mp4")
485
+ break
486
+ case "l":
487
+ case "3":
488
+ console.log(colors.Yellow, "Link: "+link)
489
+ console.log(colors.Cyan, "\n1/p) Play")
490
+ console.log(colors.Cyan, "2/d) Download")
491
+ console.log(colors.Cyan, "3/q) quit")
492
+ choice = (await selection(3, "select;", ["p", "d", "q"]))
493
+ switch (choice){
494
+ case "p":
495
+ case "1":
496
+ await play(link, anime, null)
497
+ break
498
+ case "d":
499
+ case "2":
500
+ await download(link, anime.anime_id+anime.episode_number+".mp4")
501
+ break
502
+ case "q":
503
+ case "3":
504
+ await main()
505
+ process.exit()
506
+ }
507
+ break
508
+ case "q":
509
+ case "4":
510
+ await main()
511
+ process.exit()
512
+ }
513
+ }
514
+
515
+
516
+ console.clear()
517
+ read_config()
518
+ console.log(colors.Blue, "Welcome to Ani-Cli-npm\n")
519
+ if (config.most_recent.episode_number !== 0){
520
+ console.log(colors.White, `Most recently played: ${config.most_recent.anime_id}, ep${config.most_recent.episode_number+1}`)
521
+ }
522
+ async function main(){
523
+ if (config.player === "VLC"){
524
+ console.log(colors.Red, "Warning; if you do not have mpv video player installed, you will have to change your video player to either vlc or browser in config.\n")
525
+ }
526
+ console.log(colors.Cyan, "\n1/s) Search")
527
+ if (config.most_recent.episode_number !== 0){
528
+ console.log(colors.Cyan, "2/c) Continue")
529
+ }else{
530
+ console.log(colors.White, "2/c) Continue")
531
+ }
532
+ console.log(colors.Cyan, "3/C) Config")
533
+ console.log(colors.Cyan, "4/q) Quit")
534
+ let choice = await selection(4, "select;", ["s", "c", "C", "q"])
535
+ switch (choice){
536
+ case "s":
537
+ case "1":
538
+ await search()
539
+ break
540
+ case "c":
541
+ case "2":
542
+ if (config.most_recent.episode_number !== 0){
543
+ await _continue()
544
+ }else{
545
+ console.log(colors.White, "No episodes watched recently")
546
+ await main()
547
+ }
548
+
549
+ break
550
+ case "C":
551
+ case "3":
552
+ let temp = structuredClone(config);
553
+ let exit_code;
554
+ while (true) {
555
+ temp, exit_code = await config_(temp)
556
+ if (exit_code === 1) {
557
+ config = temp
558
+ //proxyAgent = new HttpsProxyAgent(config.proxy);
559
+ console.clear()
560
+ console.log(colors.Blue, "ANI-CLI-NPM \n")
561
+ console.log(colors.Yellow, "Config changed.")
562
+ break
563
+ } else if (exit_code === 2) {
564
+ temp = config
565
+ console.clear()
566
+ console.log(colors.Blue, "ANI-CLI-NPM \n")
567
+ console.log(colors.Yellow, "Config changes disregarded.")
568
+ break
569
+ }
570
+ }
571
+ try{
572
+ fs.writeFileSync(getAppDataPath()+"/ani-cli-npm.conf", JSON.stringify(config))
573
+ }catch{
574
+ console.log(colors.Red, "Error writing to .conf file.")
575
+ }
576
+ await main()
577
+ break
578
+ case "q":
579
+ case "4":
580
+ console.log(colors.Black, "Exiting...")
581
+ process.exit()
582
+ }
583
+ }
584
+
585
+
586
+ main()
package/bin/input.js ADDED
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.number_input = exports.input = exports.selection = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const _prompt = require("simple-input");
9
+ async function selection(options, extra_options = [], color1 = ((thing) => { return chalk_1.default.yellow(thing); }), color2 = ((thing) => { return chalk_1.default.green(thing); })) {
10
+ let color = true;
11
+ for (let x in options) {
12
+ if (color) {
13
+ console.log(color1((parseInt(x) + 1).toString() +
14
+ ((extra_options[x] == undefined) ? "" : "/" + extra_options[x]) +
15
+ ") " + options[x]));
16
+ }
17
+ else {
18
+ console.log(color2((parseInt(x) + 1).toString() +
19
+ ((extra_options[x] == undefined) ? "" : "/" + extra_options[x]) +
20
+ ") " + options[x]));
21
+ }
22
+ color = !color;
23
+ }
24
+ let input = "";
25
+ do {
26
+ // @ts-ignore
27
+ input = (await _prompt(">")).toLowerCase();
28
+ for (let x in extra_options) {
29
+ if (extra_options[x].toLowerCase() == input) {
30
+ input = (parseInt(x) + 1).toString();
31
+ }
32
+ }
33
+ if (!(1 <= parseInt(input) && parseInt(input) <= options.length)) {
34
+ console.log(chalk_1.default.red("Invalid choice."));
35
+ }
36
+ } while (!(1 <= parseInt(input) && parseInt(input) <= options.length));
37
+ return parseInt(input) - 1;
38
+ }
39
+ exports.selection = selection;
40
+ async function input() {
41
+ return await _prompt(">");
42
+ }
43
+ exports.input = input;
44
+ async function number_input(max, min = 1) {
45
+ let selection;
46
+ do {
47
+ selection = parseInt(await _prompt(">"));
48
+ if (!(min <= selection && selection <= max)) {
49
+ console.log(chalk_1.default.red("Invalid choice."));
50
+ }
51
+ } while (!(min <= selection && selection <= max));
52
+ return selection;
53
+ }
54
+ exports.number_input = number_input;
55
+ async function number_input_range(max, min = 1) {
56
+ let selection;
57
+ do {
58
+ selection = await _prompt(">");
59
+ if (!(min <= parseInt(selection[0]) && parseInt(selection[0]) <= max)) {
60
+ console.log(chalk_1.default.red("Invalid choice."));
61
+ }
62
+ } while (!(min <= parseInt(selection[0]) && parseInt(selection[0]) <= max));
63
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/bin/libs.js ADDED
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ // Random functions
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.range = void 0;
5
+ function range(start, end) {
6
+ let ans = [];
7
+ for (let i = start; i <= end; i++) {
8
+ ans.push(i);
9
+ }
10
+ return ans;
11
+ }
12
+ exports.range = range;