pack
This commit is contained in:
parent
2bb78b6924
commit
50fb19094b
6 changed files with 85 additions and 67 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -270,9 +270,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.106"
|
||||
version = "0.2.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673"
|
||||
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
|
@ -721,9 +721,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
|
||||
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[package]
|
||||
name = "modrinth-cli"
|
||||
description = "unofficial modrinth cli interface"
|
||||
description = "unofficial modrinth cli"
|
||||
authors = ["tezlm", "zestylemonade", "sample-text-here"]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
|
4
README.md
Normal file
4
README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# modrinth-cli
|
||||
|
||||
an unofficial cli for managing mods downloaded from [modrinth](http://modrinth.com)
|
||||
|
1
mods.json
Normal file
1
mods.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"version":"1.17.0","mods":[{"id":"AANobbMI","url":"https://cdn.modrinth.com/data/AANobbMI/versions/mc1.16.5-0.2.0/sodium-fabric-mc1.16.5-0.2.0+build.4.jar","filename":"sodium-fabric-mc1.16.5-0.2.0+build.4.jar"},{"id":"AANobbMI","url":"https://cdn.modrinth.com/data/AANobbMI/versions/mc1.16.5-0.2.0/sodium-fabric-mc1.16.5-0.2.0+build.4.jar","filename":"sodium-fabric-mc1.16.5-0.2.0+build.4.jar"},{"id":"gvQqBUqZ","url":"https://cdn.modrinth.com/data/gvQqBUqZ/versions/mc1.17.1-0.7.5/lithium-fabric-mc1.17.1-0.7.5.jar","filename":"lithium-fabric-mc1.17.1-0.7.5.jar"},{"id":"AZomiSrC","url":"https://cdn.modrinth.com/data/AZomiSrC/versions/mc1.17.1-0.3.1/hydrogen-fabric-mc1.17.1-0.3.jar","filename":"hydrogen-fabric-mc1.17.1-0.3.jar"},{"id":"Gov5Dboq","url":"https://cdn.modrinth.com/data/Gov5Dboq/versions/0.3.17/Modern-Industrialization-0.3.17.jar","filename":"Modern-Industrialization-0.3.17.jar"},{"id":"NK39zBp2","url":"https://cdn.modrinth.com/data/NK39zBp2/versions/2.4.0/blur-2.4.0.jar","filename":"blur-2.4.0.jar"},{"id":"P7dR8mSH","url":"https://cdn.modrinth.com/data/P7dR8mSH/versions/0.42.0+1.16/fabric-api-0.42.0+1.16.jar","filename":"fabric-api-0.42.0+1.16.jar"},{"id":"Ha28R6CL","url":"https://cdn.modrinth.com/data/Ha28R6CL/versions/1.6.4+kotlin.1.5.30/fabric-language-kotlin-1.6.4+kotlin.1.5.30.jar","filename":"fabric-language-kotlin-1.6.4+kotlin.1.5.30.jar"},{"id":"Orvt0mRa","url":"https://cdn.modrinth.com/data/Orvt0mRa/versions/1.0.0+mc1.16.5/indium-1.0.0+mc1.16.5.jar","filename":"indium-1.0.0+mc1.16.5.jar"},{"id":"YL57xq9U","url":"https://cdn.modrinth.com//data/YL57xq9U/versions/mc1.16.5-1.1.2/iris-and-sodium-mc1.16.5-1.1.2+build.4.jar","filename":"iris-and-sodium-mc1.16.5-1.1.2+build.4.jar"},{"id":"LQ3K71Q1","url":"https://cdn.modrinth.com/data/LQ3K71Q1/versions/v2.0.4/dynamic-fps-2.0.4.jar","filename":"dynamic-fps-2.0.4.jar"},{"id":"hvFnDODi","url":"https://cdn.modrinth.com/data/hvFnDODi/versions/0.1.2/lazydfu-0.1.2.jar","filename":"lazydfu-0.1.2.jar"},{"id":"8qkXwOnk","url":"https://cdn.modrinth.com//data/8qkXwOnk/versions/mc1.17.1-1.1.0/morechathistory-1.17.1-1.1.0.jar","filename":"morechathistory-1.17.1-1.1.0.jar"},{"id":"mOgUt4GM","url":"https://cdn.modrinth.com/data/mOgUt4GM/versions/2.0.14/modmenu-2.0.14.jar","filename":"modmenu-2.0.14.jar"},{"id":"AANobbMI","url":"https://cdn.modrinth.com/data/AANobbMI/versions/mc1.16.5-0.2.0/sodium-fabric-mc1.16.5-0.2.0+build.4.jar","filename":"sodium-fabric-mc1.16.5-0.2.0+build.4.jar"},{"id":"AANobbMI","url":"https://cdn.modrinth.com/data/AANobbMI/versions/mc1.16.5-0.2.0/sodium-fabric-mc1.16.5-0.2.0+build.4.jar","filename":"sodium-fabric-mc1.16.5-0.2.0+build.4.jar"},{"id":"AANobbMI","url":"https://cdn.modrinth.com/data/AANobbMI/versions/mc1.16.5-0.2.0/sodium-fabric-mc1.16.5-0.2.0+build.4.jar","filename":"sodium-fabric-mc1.16.5-0.2.0+build.4.jar"},{"id":"Gov5Dboq","url":"https://cdn.modrinth.com/data/Gov5Dboq/versions/0.3.17/Modern-Industrialization-0.3.17.jar","filename":"Modern-Industrialization-0.3.17.jar"}]}
|
124
src/main.rs
124
src/main.rs
|
@ -10,7 +10,7 @@ use semver::{Version,VersionReq};
|
|||
|
||||
fn parse_config() -> Options {
|
||||
match std::fs::read_to_string("./mods.json") {
|
||||
Ok(str) => serde_json::from_str(&str).unwrap(),
|
||||
Ok(str) => serde_json::from_str(&str).expect("invalid json in mods.json!"),
|
||||
Err(_) => {
|
||||
let version = Text::new("(exact) minecraft version")
|
||||
.with_validator(&|str| match Version::parse(str) {
|
||||
|
@ -67,8 +67,9 @@ pub fn find_correct_version(id: &String, target: &Version) -> Result<ModFile, st
|
|||
.iter()
|
||||
.any(|ver| match VersionReq::parse(ver) {
|
||||
Ok(ver) => ver.matches(&target),
|
||||
Err(_) => false,
|
||||
Err(_) => true,
|
||||
});
|
||||
println!("{}, {:?}", found, version);
|
||||
if found {
|
||||
return Ok(version.files.get(0).unwrap().to_owned());
|
||||
}
|
||||
|
@ -77,9 +78,9 @@ pub fn find_correct_version(id: &String, target: &Version) -> Result<ModFile, st
|
|||
Err(std::io::Error::new(std::io::ErrorKind::NotFound, "cant find mod"))
|
||||
}
|
||||
|
||||
pub fn download(url: &String) -> Result<(u64, attohttpc::ResponseReader), std::io::Error> {
|
||||
fn install<T: std::io::Write>(url: &String, filename: &String, mut bar: ProgressBar<T>) -> Result<(), std::io::Error> {
|
||||
let res = get(&url).send()?;
|
||||
let (_, headers, body) = res.split();
|
||||
let (_, headers, mut body) = res.split();
|
||||
let len = headers
|
||||
.get("Content-Length")
|
||||
.unwrap()
|
||||
|
@ -88,16 +89,7 @@ pub fn download(url: &String) -> Result<(u64, attohttpc::ResponseReader), std::i
|
|||
.to_string()
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
Ok((len, body))
|
||||
}
|
||||
|
||||
fn install(id: &String, target: &Version) -> Result<OptionMod, std::io::Error> {
|
||||
let bullseye = find_correct_version(&id, &target).expect("couldnt find mod");
|
||||
let ModFile { url, filename } = bullseye;
|
||||
|
||||
let (len, mut body) = download(&url).unwrap();
|
||||
let mut bar = ProgressBar::new(len);
|
||||
let mut buffer = [0u8; 0x4000];
|
||||
let mut file = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
|
@ -105,10 +97,11 @@ fn install(id: &String, target: &Version) -> Result<OptionMod, std::io::Error> {
|
|||
.create(true)
|
||||
.open(&filename)?;
|
||||
|
||||
bar.total = len;
|
||||
bar.set_units(Units::Bytes);
|
||||
bar.show_tick = false;
|
||||
bar.show_percent = false;
|
||||
bar.show_time_left = true;
|
||||
bar.show_time_left = false;
|
||||
bar.show_speed = false;
|
||||
bar.message(&format!("{} {} ", "downloading".cyan().bold(), &filename));
|
||||
|
||||
|
@ -119,8 +112,18 @@ fn install(id: &String, target: &Version) -> Result<OptionMod, std::io::Error> {
|
|||
bar.add(size as u64);
|
||||
}
|
||||
|
||||
bar.finish_print(&format!("{} {} ", "downloaded".green().bold(), &filename));
|
||||
bar.finish_println(&format!("{} {} ", "downloaded".green().bold(), &filename));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn install_single(id: &String, target: &Version) -> Result<OptionMod, std::io::Error> {
|
||||
let bullseye = find_correct_version(&id, &target).expect("couldnt find mod");
|
||||
let ModFile { url, filename } = bullseye;
|
||||
let bar = ProgressBar::new(0);
|
||||
|
||||
install(&url, &filename, bar)?;
|
||||
|
||||
Ok(OptionMod {
|
||||
id: id.to_string(),
|
||||
filename: filename.to_owned(),
|
||||
|
@ -128,39 +131,39 @@ fn install(id: &String, target: &Version) -> Result<OptionMod, std::io::Error> {
|
|||
})
|
||||
}
|
||||
|
||||
//fn install_pack(mods: &Vec<OptionMod>) -> Result<(), std::io::Error> {
|
||||
// let mut bar = ProgressBar::new(mods.len());
|
||||
// bar.set_action("downloading", Color::Cyan, Style::Normal);
|
||||
//
|
||||
// for m in mods {
|
||||
// let (len, mut body) = download(&m.url).unwrap();
|
||||
// let mut progress = 0;
|
||||
// let mut buffer = [0u8; 0x4000];
|
||||
// let mut file = std::fs::OpenOptions::new()
|
||||
// .write(true)
|
||||
// .create(true)
|
||||
// .open(&m.filename)?;
|
||||
//
|
||||
// loop {
|
||||
// let size = body.read(&mut buffer)?;
|
||||
// if size == 0 { break }
|
||||
// file.write_all(&buffer).unwrap();
|
||||
// progress += size;
|
||||
// bar.set_progression(progress);
|
||||
// }
|
||||
//
|
||||
// bar.inc();
|
||||
// }
|
||||
//
|
||||
// bar.set_action("downloaded", Color::Green, Style::Normal);
|
||||
// bar.finalize();
|
||||
// Ok(())
|
||||
//}
|
||||
fn install_pack(mods: &Vec<OptionMod>) -> Result<(), std::io::Error> {
|
||||
let multi = pbr::MultiBar::new();
|
||||
multi.println(&"downloading mods".cyan().bold());
|
||||
|
||||
for m in mods {
|
||||
let bar = multi.create_bar(0);
|
||||
// TODO: make multithreaded?
|
||||
install(&m.url, &m.filename, bar)?;
|
||||
}
|
||||
|
||||
multi.listen();
|
||||
multi.println(&"done!".green().bold());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn already_installed(id: &String, options: &Options) -> bool {
|
||||
match options.mods.iter().find(|h| h.id.eq(id)) {
|
||||
Some(found) => {
|
||||
if std::fs::metadata(&found.filename).is_ok() {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_mod(query: &str, mods: &MinecraftMods, options: &Options) -> Option<ModState> {
|
||||
if mods.hits.len() == 1 {
|
||||
let id = mods.hits[0].id.to_owned();
|
||||
if options.mods.iter().any(|h| h.id == id) {
|
||||
if already_installed(&id, &options) {
|
||||
return Some(ModState::Installed(id));
|
||||
} else {
|
||||
return Some(ModState::Uninstalled(id));
|
||||
|
@ -169,7 +172,7 @@ fn find_mod(query: &str, mods: &MinecraftMods, options: &Options) -> Option<ModS
|
|||
|
||||
for (i, m) in mods.hits.iter().enumerate() {
|
||||
if m.title.to_lowercase() == query {
|
||||
if options.mods.iter().any(|h| h.id == m.id) {
|
||||
if already_installed(&m.id, &options) {
|
||||
return Some(ModState::Installed(m.id.to_owned()));
|
||||
} else {
|
||||
return Some(ModState::Uninstalled(m.id.to_owned()));
|
||||
|
@ -201,20 +204,21 @@ fn main() -> Result<(), std::io::Error> {
|
|||
println!(" {}: {}", "install, i".blue(), "install a mod");
|
||||
println!(" {}: {}", "remove, rm".blue(), "remove a mod");
|
||||
println!(" {}: {}", "update, u".blue(), "update all mods");
|
||||
}
|
||||
println!(" {}: {}", "pack, p".blue(), "install from mods.json");
|
||||
},
|
||||
"search" | "s" => {
|
||||
let mods = search_mods(&query).unwrap();
|
||||
let mods = search_mods(&query)?;
|
||||
for i in mods.hits {
|
||||
print_mod(&i);
|
||||
};
|
||||
}
|
||||
},
|
||||
"install" | "i" => {
|
||||
let query = if query.is_empty() { Text::new("query").prompt().unwrap() } else { query };
|
||||
let mods = search_mods(&query).unwrap();
|
||||
let mods = search_mods(&query)?;
|
||||
match find_mod(&query, &mods, &options) {
|
||||
Some(ModState::Uninstalled(m)) => {
|
||||
let version = Version::parse(&options.version).unwrap();
|
||||
options.mods.push(install(&m, &version)?)
|
||||
options.mods.push(install_single(&m, &version)?)
|
||||
},
|
||||
Some(ModState::Installed(_)) => {
|
||||
println!("already installed!");
|
||||
|
@ -223,7 +227,7 @@ fn main() -> Result<(), std::io::Error> {
|
|||
println!("{} no mods", "error:".bold().red());
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
"remove" | "rm" => {
|
||||
let query = query.to_lowercase();
|
||||
match options.mods.iter().position(|m| m.filename.to_lowercase().contains(&query)) {
|
||||
|
@ -240,7 +244,14 @@ fn main() -> Result<(), std::io::Error> {
|
|||
println!("{} cant find mod", "error:".bold().red());
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
"pack" | "p" => {
|
||||
if options.mods.len() == 0 {
|
||||
println!("no mods in pack!");
|
||||
} else {
|
||||
install_pack(&options.mods)?;
|
||||
}
|
||||
},
|
||||
"update" | "u" => {
|
||||
let mut outdated = Vec::new();
|
||||
let version = Version::parse(&options.version).unwrap();
|
||||
|
@ -250,16 +261,17 @@ fn main() -> Result<(), std::io::Error> {
|
|||
}
|
||||
for m in &mut outdated {
|
||||
std::fs::remove_file(&m.filename).unwrap_or_default();
|
||||
let OptionMod { filename, url, ..} = install(&m.id, &version)?;
|
||||
let OptionMod { filename, url, ..} = install_single(&m.id, &version)?;
|
||||
m.filename = filename;
|
||||
m.url = url;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
eprintln!("{} {} is not a command", "error:".bold().red(), subcommand);
|
||||
println!("{} {} is not a command", "error:".bold().red(), subcommand);
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
std::fs::write("mods.json", serde_json::to_string(&options)?)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Options {
|
||||
pub version: String,
|
||||
pub mods: Vec<OptionMod>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct OptionMod {
|
||||
pub id: String,
|
||||
pub url: String,
|
||||
pub filename: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct MinecraftMods {
|
||||
pub hits: Vec<MinecraftMod>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct MinecraftMod {
|
||||
#[serde(rename = "mod_id")]
|
||||
pub id: String,
|
||||
|
@ -28,7 +28,7 @@ pub struct MinecraftMod {
|
|||
pub description: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ModVersion {
|
||||
#[serde(rename = "game_versions")]
|
||||
pub versions: Vec<String>,
|
||||
|
@ -37,7 +37,7 @@ pub struct ModVersion {
|
|||
pub files: Vec<ModFile>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ModFile {
|
||||
pub url: String,
|
||||
pub filename: String,
|
||||
|
|
Loading…
Reference in a new issue