mirror of
https://github.com/mrfluffy-dev/kami.git
synced 2026-01-17 04:50:32 +00:00
added anilist tracking
This commit is contained in:
24
Cargo.lock
generated
24
Cargo.lock
generated
@@ -322,6 +322,7 @@ dependencies = [
|
|||||||
"dirs",
|
"dirs",
|
||||||
"isahc",
|
"isahc",
|
||||||
"regex",
|
"regex",
|
||||||
|
"serde_json",
|
||||||
"termsize",
|
"termsize",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -548,6 +549,12 @@ version = "0.6.26"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schannel"
|
name = "schannel"
|
||||||
version = "0.1.20"
|
version = "0.1.20"
|
||||||
@@ -558,6 +565,23 @@ dependencies = [
|
|||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.142"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e590c437916fb6b221e1d00df6e3294f3fccd70ca7e92541c475d6ed6ef5fee2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
|
|||||||
@@ -14,3 +14,4 @@ isahc = "1.7.2"
|
|||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
termsize = "0.1.6"
|
termsize = "0.1.6"
|
||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
|
serde_json = "1.0.83"
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
use crate::main;
|
use crate::main;
|
||||||
use crate::open_video;
|
use crate::open_video;
|
||||||
use crate::{anime_ep_range, anime_link, anime_names};
|
use crate::{anime_ep_range, anime_link, anime_names};
|
||||||
|
use crate::{get_anime_id, get_token, get_user_anime_progress, update_anime_progress};
|
||||||
use crate::{int_input, string_input};
|
use crate::{int_input, string_input};
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
//use crate
|
//use crate
|
||||||
pub fn anime_stream(search: String, episode: u32) {
|
pub fn anime_stream(search: String, episode: u32) {
|
||||||
|
let token = get_token();
|
||||||
let query = if search != "" {
|
let query = if search != "" {
|
||||||
search
|
search
|
||||||
} else {
|
} else {
|
||||||
@@ -40,9 +42,11 @@ pub fn anime_stream(search: String, episode: u32) {
|
|||||||
let title = &anime_list[anime_num];
|
let title = &anime_list[anime_num];
|
||||||
let ep_range = anime_ep_range(title);
|
let ep_range = anime_ep_range(title);
|
||||||
// if there is only one episode, then don't ask user to choose episode
|
// if there is only one episode, then don't ask user to choose episode
|
||||||
|
let id = get_anime_id(&title.replace("-", " "));
|
||||||
if ep_range == 1 {
|
if ep_range == 1 {
|
||||||
let link = anime_link(title, 1);
|
let link = anime_link(title, 1);
|
||||||
open_video(link);
|
open_video(link);
|
||||||
|
update_anime_progress(id, &title.replace("-", " "), 1, &token);
|
||||||
main();
|
main();
|
||||||
} else {
|
} else {
|
||||||
let mut ep_num: usize = usize::MAX;
|
let mut ep_num: usize = usize::MAX;
|
||||||
@@ -52,6 +56,8 @@ pub fn anime_stream(search: String, episode: u32) {
|
|||||||
} else if episode != 0 {
|
} else if episode != 0 {
|
||||||
ep_num = episode as usize;
|
ep_num = episode as usize;
|
||||||
} else {
|
} else {
|
||||||
|
let current_progress = get_user_anime_progress(id, &token);
|
||||||
|
println!("you are currently on episode: {}", current_progress);
|
||||||
println!("select episode 1-{}: ", ep_range);
|
println!("select episode 1-{}: ", ep_range);
|
||||||
while ep_num == usize::max_value() || ep_num > ep_range as usize {
|
while ep_num == usize::max_value() || ep_num > ep_range as usize {
|
||||||
ep_num = int_input("Enter episode number: ");
|
ep_num = int_input("Enter episode number: ");
|
||||||
@@ -63,6 +69,9 @@ pub fn anime_stream(search: String, episode: u32) {
|
|||||||
loop {
|
loop {
|
||||||
let link = anime_link(title, ep_num as u64);
|
let link = anime_link(title, ep_num as u64);
|
||||||
open_video(link);
|
open_video(link);
|
||||||
|
let id = get_anime_id(&title.replace("-", " "));
|
||||||
|
println!("{}", get_user_anime_progress(id, &token));
|
||||||
|
update_anime_progress(id, &title.replace("-", " "), ep_num, &token);
|
||||||
println!("{}", "n: next episode".green());
|
println!("{}", "n: next episode".green());
|
||||||
println!("{}", "p: previous episode".yellow());
|
println!("{}", "p: previous episode".yellow());
|
||||||
println!("{}", "s: search another anime".green());
|
println!("{}", "s: search another anime".green());
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pub mod anime;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
pub mod scraper;
|
pub mod scraper;
|
||||||
pub mod anime;
|
pub mod trackers;
|
||||||
|
|||||||
183
src/anime/trackers.rs
Normal file
183
src/anime/trackers.rs
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
use crate::string_input;
|
||||||
|
use isahc::{ReadResponseExt, Request, RequestExt};
|
||||||
|
use serde_json::json;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
pub fn get_token() -> String {
|
||||||
|
//if not on windows create folder ~/.config/kami
|
||||||
|
let config_path = dirs::config_dir().unwrap().join("kami");
|
||||||
|
if !config_path.exists() {
|
||||||
|
fs::create_dir_all(&config_path).unwrap();
|
||||||
|
}
|
||||||
|
let token_path = config_path.join("token.txt");
|
||||||
|
if !token_path.exists() {
|
||||||
|
//create empty file
|
||||||
|
fs::File::create(&token_path).unwrap();
|
||||||
|
} else {
|
||||||
|
//read token from file
|
||||||
|
let token = fs::read_to_string(&token_path).unwrap();
|
||||||
|
if token.is_empty() {
|
||||||
|
println!("please go to the below link and copy and past the token below");
|
||||||
|
println!(
|
||||||
|
"https://anilist.co/api/v2/oauth/authorize?client_id=9121&response_type=token"
|
||||||
|
);
|
||||||
|
let token = string_input("token: ");
|
||||||
|
fs::write(&token_path, token).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let token = fs::read_to_string(&token_path).unwrap();
|
||||||
|
token
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_anime_id(anime: &str) -> i32 {
|
||||||
|
const QUERY: &str = "
|
||||||
|
query ($id: Int, $search: String) {
|
||||||
|
Media (id: $id, search: $search, type: ANIME) {
|
||||||
|
id
|
||||||
|
title {
|
||||||
|
native
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
let json = json!({
|
||||||
|
"query": QUERY,
|
||||||
|
"variables": {
|
||||||
|
"search": anime,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let resp = Request::builder()
|
||||||
|
.method("POST")
|
||||||
|
.uri("https://graphql.anilist.co/")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.body(json.to_string())
|
||||||
|
.unwrap()
|
||||||
|
.send()
|
||||||
|
.unwrap()
|
||||||
|
.text();
|
||||||
|
//println!("{}", resp);
|
||||||
|
let regex = regex::Regex::new(r#"id":(.*?),"#).unwrap();
|
||||||
|
let resp: String = resp.as_ref().unwrap().to_string();
|
||||||
|
let id = regex
|
||||||
|
.captures(&resp)
|
||||||
|
.unwrap()
|
||||||
|
.get(1)
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.parse::<i32>()
|
||||||
|
.unwrap();
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
//get the user id from the token
|
||||||
|
fn get_user_id(token: &str) -> i32 {
|
||||||
|
const QUERY: &str = "query {
|
||||||
|
Viewer {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
let json = json!({ "query": QUERY });
|
||||||
|
let resp = Request::builder()
|
||||||
|
.method("POST")
|
||||||
|
.uri("https://graphql.anilist.co/")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.header("Authorization", format!("Bearer {}", token))
|
||||||
|
.body(json.to_string())
|
||||||
|
.unwrap()
|
||||||
|
.send()
|
||||||
|
.unwrap()
|
||||||
|
.text();
|
||||||
|
//println!("{}", resp);
|
||||||
|
let regex = regex::Regex::new(r#"id":(.*?)}"#).unwrap();
|
||||||
|
let resp: String = resp.as_ref().unwrap().to_string();
|
||||||
|
let id = regex
|
||||||
|
.captures(&resp)
|
||||||
|
.unwrap()
|
||||||
|
.get(1)
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.parse::<i32>()
|
||||||
|
.unwrap();
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_user_anime_progress(anime_id: i32, token: &str) -> i32 {
|
||||||
|
let user_id = get_user_id(&token);
|
||||||
|
const QUERY: &str = "query ($user_id: Int, $media_id: Int) {
|
||||||
|
MediaList (userId: $user_id, mediaId: $media_id, type: ANIME) {
|
||||||
|
progress
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
let json = json!({
|
||||||
|
"query": QUERY,
|
||||||
|
"variables": {
|
||||||
|
"user_id": user_id,
|
||||||
|
"media_id": anime_id,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let resp = Request::builder()
|
||||||
|
.method("POST")
|
||||||
|
.uri("https://graphql.anilist.co/")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.header("Authorization", format!("Bearer {}", token))
|
||||||
|
.body(json.to_string())
|
||||||
|
.unwrap()
|
||||||
|
.send()
|
||||||
|
.unwrap()
|
||||||
|
.text();
|
||||||
|
//println!("{}", resp);
|
||||||
|
let regex = regex::Regex::new(r#"progress":(.*?)}"#).unwrap();
|
||||||
|
let resp: String = resp.as_ref().unwrap().to_string();
|
||||||
|
//if resp contains "404"set progress to 1
|
||||||
|
// else set progress to the number in the regex
|
||||||
|
if resp.contains("404") {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
let progress = regex
|
||||||
|
.captures(&resp)
|
||||||
|
.unwrap()
|
||||||
|
.get(1)
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.parse::<i32>()
|
||||||
|
.unwrap();
|
||||||
|
progress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_anime_progress(anime_id: i32, anime: &str, progress: usize, token: &str) {
|
||||||
|
const UPDATE: &str = "
|
||||||
|
mutation ($mediaId: Int, $status: MediaListStatus, $progress: Int) {
|
||||||
|
SaveMediaListEntry (mediaId: $mediaId, status: $status, progress: $progress) {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
progress
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
let json = json!({
|
||||||
|
"query": UPDATE,
|
||||||
|
"variables": {
|
||||||
|
"mediaId": anime_id,
|
||||||
|
"status": "CURRENT",
|
||||||
|
"progress": progress
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let _resp = Request::builder()
|
||||||
|
.method("POST")
|
||||||
|
.uri("https://graphql.anilist.co/")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.header("Authorization", format!("Bearer {}", token))
|
||||||
|
.body(json.to_string())
|
||||||
|
.unwrap()
|
||||||
|
.send()
|
||||||
|
.unwrap()
|
||||||
|
.text();
|
||||||
|
println!("updated progress of {} to episode {}", anime, progress);
|
||||||
|
}
|
||||||
36
src/main.rs
36
src/main.rs
@@ -4,12 +4,13 @@ mod ln;
|
|||||||
|
|
||||||
use anime::anime::anime_stream;
|
use anime::anime::anime_stream;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use ln::{scraper::get_ln_next_page, ln::ln_read};
|
|
||||||
use ln::search::search_ln;
|
use ln::search::search_ln;
|
||||||
|
use ln::{ln::ln_read, scraper::get_ln_next_page};
|
||||||
|
|
||||||
use crate::anime::{
|
use crate::anime::{
|
||||||
player::open_video,
|
player::open_video,
|
||||||
scraper::{anime_ep_range, anime_link, anime_names},
|
scraper::{anime_ep_range, anime_link, anime_names},
|
||||||
|
trackers::*,
|
||||||
};
|
};
|
||||||
use crate::helpers::take_input::{int_input, string_input};
|
use crate::helpers::take_input::{int_input, string_input};
|
||||||
use crate::ln::{menu::chapter_selector, open_text::open_bat, scraper::get_full_text};
|
use crate::ln::{menu::chapter_selector, open_text::open_bat, scraper::get_full_text};
|
||||||
@@ -35,7 +36,6 @@ fn main() {
|
|||||||
search = arg;
|
search = arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if arg == "--ln" || arg == "-l" {
|
if arg == "--ln" || arg == "-l" {
|
||||||
ln = true;
|
ln = true;
|
||||||
@@ -49,21 +49,21 @@ fn main() {
|
|||||||
if arg == "--chapter" || arg == "-c" {
|
if arg == "--chapter" || arg == "-c" {
|
||||||
if let Some(arg) = std::env::args().nth(count + 1) {
|
if let Some(arg) = std::env::args().nth(count + 1) {
|
||||||
chapter = arg.parse::<u32>().unwrap();
|
chapter = arg.parse::<u32>().unwrap();
|
||||||
}else{
|
} else {
|
||||||
chapter = 0;
|
chapter = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if arg == "--episode" || arg == "-e" {
|
if arg == "--episode" || arg == "-e" {
|
||||||
if let Some(arg) = std::env::args().nth(count + 1) {
|
if let Some(arg) = std::env::args().nth(count + 1) {
|
||||||
episode = arg.parse::<u32>().unwrap();
|
episode = arg.parse::<u32>().unwrap();
|
||||||
}else{
|
} else {
|
||||||
episode = 0;
|
episode = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if help == true{
|
if help == true {
|
||||||
print_help();
|
print_help();
|
||||||
}
|
}
|
||||||
if anime == false && ln == false {
|
if anime == false && ln == false {
|
||||||
@@ -71,10 +71,10 @@ fn main() {
|
|||||||
println!("2: Light Novel");
|
println!("2: Light Novel");
|
||||||
|
|
||||||
let a = int_input("pick your poison: ");
|
let a = int_input("pick your poison: ");
|
||||||
match a{
|
match a {
|
||||||
1 => anime = true,
|
1 => anime = true,
|
||||||
2 => ln = true,
|
2 => ln = true,
|
||||||
_=>println!("invalid option. ")
|
_ => println!("invalid option. "),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if anime == true && ln == true {
|
if anime == true && ln == true {
|
||||||
@@ -94,24 +94,36 @@ fn page_selector(ln_id: &str, selected_page: u32) -> String {
|
|||||||
get_ln_next_page(ln_id, &selected_page.to_string())
|
get_ln_next_page(ln_id, &selected_page.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_help(){
|
fn print_help() {
|
||||||
println!("anime:\t\t{}", format_args!("{}", "-a --anime".red()));
|
println!("anime:\t\t{}", format_args!("{}", "-a --anime".red()));
|
||||||
println!("{}", "after this^^^ argument you can enter a search term".green());
|
println!(
|
||||||
|
"{}",
|
||||||
|
"after this^^^ argument you can enter a search term".green()
|
||||||
|
);
|
||||||
println!("{}", "for exaple kami -a \"one piece\"");
|
println!("{}", "for exaple kami -a \"one piece\"");
|
||||||
//print blank line
|
//print blank line
|
||||||
println!("");
|
println!("");
|
||||||
println!("episode:\t{}", format_args!("{}", "-e --episode".red()));
|
println!("episode:\t{}", format_args!("{}", "-e --episode".red()));
|
||||||
println!("{}", "after this^^^ argument you can enter a chapter number".green());
|
println!(
|
||||||
|
"{}",
|
||||||
|
"after this^^^ argument you can enter a chapter number".green()
|
||||||
|
);
|
||||||
println!("{}", "for exaple kami -c 200");
|
println!("{}", "for exaple kami -c 200");
|
||||||
//print blank line
|
//print blank line
|
||||||
println!("");
|
println!("");
|
||||||
println!("light novel:\t{}", format_args!("{}", "-l --ln".red()));
|
println!("light novel:\t{}", format_args!("{}", "-l --ln".red()));
|
||||||
println!("{}", "after this^^^ argument you can enter a search term".green());
|
println!(
|
||||||
|
"{}",
|
||||||
|
"after this^^^ argument you can enter a search term".green()
|
||||||
|
);
|
||||||
println!("{}", "for exaple kami -l \"one piece\"");
|
println!("{}", "for exaple kami -l \"one piece\"");
|
||||||
//print blank line
|
//print blank line
|
||||||
println!("");
|
println!("");
|
||||||
println!("chapter:\t{}", format_args!("{}", "-c --chapter".red()));
|
println!("chapter:\t{}", format_args!("{}", "-c --chapter".red()));
|
||||||
println!("{}", "after this^^^ argument you can enter a chapter number".green());
|
println!(
|
||||||
|
"{}",
|
||||||
|
"after this^^^ argument you can enter a chapter number".green()
|
||||||
|
);
|
||||||
println!("{}", "for exaple kami -c 200");
|
println!("{}", "for exaple kami -c 200");
|
||||||
//print blank line
|
//print blank line
|
||||||
println!("");
|
println!("");
|
||||||
|
|||||||
Reference in New Issue
Block a user