ani-cli-npm 1.4.2 → 1.4.3

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