ani-cli-npm
Version:
ani-cli tool rewritten as npm package
410 lines (399 loc) • 18.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Anime = void 0;
const curl_1 = require("./core_utils/curl");
const regex_1 = require("./core_utils/regex");
const generate_link_1 = require("./url_genoration/generate_link");
const cache_1 = require("./file_managment/cache");
const input_1 = require("./IO/input");
const load_config_1 = require("./file_managment/load_config");
const W2GClient = require("w2g-client");
const open = require("open");
const PlayerController = require("media-player-controller");
const dl = require("download-file-with-progressbar");
const chalk = require("chalk");
const m3u8ToMp4 = require("m3u8-to-mp4");
const converter = new m3u8ToMp4();
class Anime {
/*
Class for handling a show/film
Stores anime dpage links assigned with anime_id.
Initialised with Anime.init()
*/
id = "";
episode_list = [];
most_recent = 0;
player = new class {
play(episode_link) {
return {};
}
player;
};
current_pos = 0;
current_episode = 0;
async init(anime_id, cache_folder) {
/*
Initiate Anime object
Will first search cache folder for cache file (this will contain id and dpage links)
If no cache is found, it will use get_ep_bases(anime_id) to get links. (Webscrapes from animixplay.to), then creates cache
anime_id:
*/
let cache_object = (0, cache_1.search_cache)(cache_folder, anime_id);
this.id = anime_id;
if (cache_object == 0) {
await this.get_ep_bases(this.id);
(0, cache_1.new_cache)(cache_folder, {
id: this.id,
episode_list: this.episode_list,
most_recent: this.most_recent
});
}
else {
try {
this.episode_list = cache_object.episode_list;
if (cache_object.most_recent != undefined) {
this.most_recent = cache_object.most_recent;
}
if (cache_object.position != undefined) {
this.current_pos = cache_object.position;
}
}
catch {
await this.get_ep_bases(this.id);
}
}
(0, cache_1.new_cache)(cache_folder, {
id: this.id,
episode_list: this.episode_list,
most_recent: this.most_recent
});
return 0;
}
async get_episode_link(episode, player = "VLC") {
let episode_dpage = this.episode_list[episode];
let id = episode_dpage.replace("//gogohd.net/streaming.php?id=", "");
id = id.slice(0, id.indexOf("="));
let link = await (0, generate_link_1.generate_link)(1, id, player);
if (!link) {
link = await (0, generate_link_1.generate_link)(2, id, player);
}
if (!link) {
console.log(chalk.red("Failed to generate links"));
}
if (player == "VLC" && link.includes("m3u8")) {
console.log(chalk.red("Warning; VLC is not compatible with m3u8 playlist files without custom plugins."));
}
return link;
}
async get_ep_bases(anime_id) {
/*
Scrapes animixplay.to for dpage links.
returns array with all dpage links
*/
let html = (await ((0, curl_1.curl)("https://animixplay.to/v1/" + anime_id))).split("\n"); //POTENTIAL BREAK POINT. animixplay.to may change domain address
let lines = "";
for (let x in html) {
if ((0, regex_1.RegexParse)(html[x], "*<div id=\"epslistplace\"*")) {
lines = (html[x]);
}
}
lines = lines.slice(55, lines.length).replace("}</div>", "");
lines = "{" + lines.slice(lines.indexOf(",") + 1, lines.length) + "}";
let json = JSON.parse(lines);
for (const value of Object.entries(json)) {
if (typeof value[1] == "string") {
this.episode_list.push(value[1].replace("//gogohd.net/streaming.php?id=", ""));
}
}
}
async play_head(episode, config, config_dir) {
/*
# Starts play cascade.
## Takes in:
### Episode number, counting from 0
### Config object
### Config save directory
- If config.player is set to MPV or VLC, it will use the media-player-controller package.
- If set to Browser, it will use the "open" packer.
- If set to Link, it will simply print the media stream link to console, primarily for debuting peruses.
*/
console.clear();
console.log(`Playing ${this.id} episode ${episode + 1}`); // from ${new Date(this.current_pos * 1000).toISOString().slice(11, 19)}`)
if (this.current_pos != 0) {
console.log(`Most recent position: ${new Date(this.current_pos * 1000).toISOString().slice(11, 19)}`);
}
switch (config.player) {
case "MPV":
console.log(("Opening MPV.."));
this.player.player = await new PlayerController({
app: 'mpv',
args: ['--fullscreen', '--keep-open=yes'],
media: await this.get_episode_link(episode, config.player),
ipcPath: config.mpv_socket_path
});
this.player.play = (async (episode_link) => {
this.player.player.load(episode_link);
});
this.player.player.on('playback', (data) => {
if (data.name == "time-pos" && data.value >= 0) {
this.current_pos = data.value;
}
//console.log(data)
});
this.player.player.on('app-exit', (code) => {
config.most_recent.anime_id = this.id;
config.most_recent.episode_number = episode;
config.most_recent.episode_second = this.current_pos;
(0, load_config_1.write_config)(config_dir, config);
this.most_recent = episode;
(0, cache_1.new_cache)(config_dir, {
id: this.id,
episode_list: this.episode_list,
most_recent: this.most_recent,
position: this.current_pos
});
});
// @ts-ignore
await this.player.player.launch(err => {
if (err)
return console.error(err.message);
});
break;
case "VLC":
console.log(("Opening VLC.."));
this.player.player = await new PlayerController({
app: 'vlc',
args: ['--fullscreen'],
media: await this.get_episode_link(episode, config.player),
//httpPort: (config.vlc_socket !== 0)? config.vlc_socket : null,
//httpPass: (config.vlc_pass !== "")? config.vlc_socket : null,
});
// @ts-ignore
await this.player.player.launch(err => {
if (err)
return console.error(err.message);
});
this.player.play = (async (episode_link) => {
this.player.player.quit();
this.player.player = await new PlayerController({
app: 'vlc',
args: ['--fullscreen'],
media: episode_link
//httpPort: (config.vlc_socket !== 0)? config.vlc_socket : null,
//httpPass: (config.vlc_pass !== "")? config.vlc_socket : null,
});
this.player.player.on('playback', (data) => {
if (data.name == "time-pos" && data.value >= 0) {
this.current_pos = data.value;
}
//console.log(data)
});
this.player.player.on('app-exit', (code) => {
config.most_recent.anime_id = this.id;
config.most_recent.episode_number = episode;
config.most_recent.episode_second = this.current_pos;
(0, load_config_1.write_config)(config_dir, config);
this.most_recent = episode;
(0, cache_1.new_cache)(config_dir, {
id: this.id,
episode_list: this.episode_list,
most_recent: this.most_recent,
position: this.current_pos
});
});
// @ts-ignore
await this.player.player.launch(err => {
if (err)
return console.error(err.message);
});
});
break;
case "BROWSER":
this.player.play = (async (episode_link) => {
console.log(("Opening browser..."));
await open(episode_link);
});
console.log(("Opening browser..."));
await open(await this.get_episode_link(episode, config.player));
break;
case "W2G":
try {
this.player.player = new W2GClient.W2GClient(config.w2g_api_key);
await this.player.player.create(await this.get_episode_link(episode, config.player));
console.log(chalk.green("Room link: " + await this.player.player.getLink()));
}
catch {
console.log(chalk.red("Failed to create w2g.tv room. \nthis can often be because your API token is invalid. You can change it in options."));
process.exit();
}
this.player.play = (async (episode_link) => {
console.log(("Updating W2G room..."));
console.log(chalk.green("Room link: " + await this.player.player.getLink()));
try {
await this.player.player.update(episode_link);
}
catch {
console.log(chalk.red("Error updating W2G room. Very sorry, w2g functionality is a bit broken at present. Worst case you should be able to just restart with a new room for each episode."));
}
});
console.log("Opening W2G.tv...");
await open(await this.player.player.getLink());
break;
case "LINK":
this.player.play = (async (episode_link) => {
console.log(chalk.green(episode_link));
});
this.player.play(await this.get_episode_link(episode));
break;
}
await this.play_controller(episode, config, config_dir, true);
}
async next(player) {
this.current_episode += 1;
this.current_pos = 0;
this.player.play(await this.get_episode_link(this.current_episode, player));
}
async previous(player) {
this.current_episode -= 1;
this.current_pos = 0;
this.player.play(await this.get_episode_link(this.current_episode, player));
}
async play_controller(episode, config, config_dir, first = false) {
if (config.show_cover) {
}
console.clear();
console.log(`Playing ${this.id} episode ${episode + 1}`); // from ${new Date(this.current_pos * 1000).toISOString().slice(11, 19)}`)
// if (!first){
// console.clear()
// console.log(chalk.blue(`Playing ${this.id} episode ${episode+1}`))
// if (this.player == 0){
// await open(await this.get_episode_link(episode, "BROWSER"))
// }else if(this.player == 1){
// console.log(await this.get_episode_link(episode))
// } else if (this.player.roomID != undefined){
// console.log(chalk.green("Room link: "+ await this.player.getLink()));
// this.player.update(await this.get_episode_link(episode))
// } else if (this.player.opts.app == "mpv"){
// await this.player.load(await this.get_episode_link(episode))
// }else{
// this.player.quit()
// this.player = await new PlayerController({
// app: 'vlc',
// args: ['--fullscreen'],
// media: await this.get_episode_link(episode, config.player)
// });
// // @ts-ignore
// await this.player.launch(err => {
// if (err) return console.error(err.message);
// });
// }
// }
this.current_episode = episode;
config.most_recent.anime_id = this.id;
config.most_recent.episode_number = episode;
config.most_recent.episode_second = this.current_pos;
(0, load_config_1.write_config)(config_dir, config);
this.most_recent = episode;
(0, cache_1.new_cache)(config_dir, {
id: this.id,
episode_list: this.episode_list,
most_recent: this.most_recent,
position: this.current_pos
});
let selected; // Look, I'm sorry, but there is no way I can possibly document this in a sane way. It's a dumb patch.
do {
console.clear();
selected = await (0, input_1.selection)((episode <= 0) ? [chalk.yellow("1/n) Next"), chalk.grey("2/p) Previous"), chalk.yellow("3/s) Select"), chalk.green("4/q) Quit")] :
(episode >= this.episode_list.length - 1) ? [chalk.grey("1/n) Next"), chalk.green("2/p) Previous"), chalk.yellow("3/s) Select"), chalk.green("4/q) Quit")] :
[chalk.yellow("1/n) Next"), chalk.green("2/p) Previous"), chalk.yellow("3/s) Select"), chalk.green("4/q) Quit")], ["n", "p", "s", "q"], ((thing) => { return (thing); }), ((thing) => { return (thing); }), true);
if (!(selected != ((episode <= 0) ? 1 : (episode >= this.episode_list.length - 1) ? 0 : -1))) {
console.log(chalk.red("Invalid choice."));
}
} while (!(selected != ((episode <= 0) ? 1 : (episode >= this.episode_list.length - 1) ? 0 : -1)));
switch (selected) {
case 0:
if (episode >= this.episode_list.length - 1) {
await this.next(config.player);
await this.play_controller(episode - 1, config, config_dir);
}
else {
await this.next(config.player);
await this.play_controller(episode + 1, config, config_dir);
}
break;
case 1:
if ((episode >= this.episode_list.length - 1) || (episode <= 0)) {
break;
}
await this.previous(config.player);
await this.play_controller(episode - 1, config, config_dir);
break;
case 2:
if (this.episode_list.length == 1) {
this.player.play(await this.get_episode_link(0, config.player));
await this.play_controller(0, config, config_dir);
}
else {
console.log(`Select episode [1-${this.episode_list.length}]`);
let episode = await (0, input_1.number_input)(this.episode_list.length, 1) - 1;
this.player.play(await this.get_episode_link(episode, config.player));
await this.play_controller(episode, config, config_dir);
}
break;
case 3:
break;
}
}
async download(episode, download_folder, final_ep) {
/*
## Downloads an episode (counting from 0) to download_folder, with progress bar.
*/
while (episode <= final_ep) {
}
try {
let ep_link = await this.get_episode_link(episode);
let file_name = `${this.id}-${episode + 1}.mp4`;
if (ep_link.includes(".m3u8")) {
console.log(chalk.green(`Downloading episode ${episode + 1} of ${this.id.replace("-", "")} to ${download_folder + "/" + file_name}...`));
console.log("Progress bar unavailable for m3u8 files.");
await converter
.setInputFile(ep_link)
.setOutputFile((download_folder + "/" + file_name))
.start();
console.log(chalk.green("Download finished."));
}
else {
// @ts-ignore
let option = {
filename: (ep_link.includes("m3u8") ? file_name.replace("mp4", "m3u8") : file_name),
dir: download_folder,
onDone: (final_ep > episode) ? ((info) => {
// @ts-ignore
console.log(chalk.green(`\n -- 1Download finished -- \nLocation: ${info.path}. Size: ${Math.round(info.size / 100000) * 10} Bytes\n`));
this.download(episode + 1, download_folder, final_ep);
}) : ((info) => {
// @ts-ignore
console.log(chalk.green(`\n -- 2Download finished -- \n${info.path}. Size: ${Math.round(info.size / 100000) * 10} Bytes\n`));
}),
// @ts-ignore
onError: (err) => {
console.log(chalk.red('error', err));
this.download(episode, download_folder, final_ep);
},
// @ts-ignore
onProgress: (curr, total) => {
process.stdout.clearLine(0);
process.stdout.cursorTo(0);
process.stdout.write("\x1b[32m -- " + (curr / total * 100).toFixed(2) + "% " + "#".repeat(Math.ceil((curr / total) * ((process.stdout.columns - 20) / 1.5))) + "~".repeat(Math.ceil(((process.stdout.columns - 20) / 1.5) - (curr / total) * ((process.stdout.columns - 20) / 1.5))) + " -- \x1b[0m");
}
};
//console.log((`${option.dir}/${option.filename}`))
return await dl(ep_link, option);
}
}
catch {
await this.download(episode, download_folder, final_ep);
}
}
}
exports.Anime = Anime;