Compare commits

...

2 Commits

Author SHA1 Message Date
32a9d75a62 download version history metadata 2024-01-05 13:26:56 -08:00
1712e0c7e3 download version history 2024-01-04 17:47:08 -08:00
3 changed files with 161 additions and 22 deletions

89
Cargo.lock generated
View File

@ -26,6 +26,21 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "anstream" name = "anstream"
version = "0.6.5" version = "0.6.5"
@ -97,6 +112,7 @@ name = "asset-tool"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono",
"clap", "clap",
"flate2", "flate2",
"futures", "futures",
@ -107,6 +123,8 @@ dependencies = [
"rbx_reflection_database", "rbx_reflection_database",
"rbx_xml", "rbx_xml",
"reqwest", "reqwest",
"serde",
"serde_json",
"tokio", "tokio",
] ]
@ -201,6 +219,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets 0.48.5",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.4.12" version = "4.4.12"
@ -600,6 +633,29 @@ dependencies = [
"tokio-native-tls", "tokio-native-tls",
] ]
[[package]]
name = "iana-time-zone"
version = "0.1.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.2.3" version = "0.2.3"
@ -913,9 +969,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.71" version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -957,9 +1013,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -1230,18 +1286,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.193" version = "1.0.194"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.193" version = "1.0.194"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1250,9 +1306,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.108" version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -1298,9 +1354,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.43" version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1629,6 +1685,15 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.0",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"

View File

@ -7,6 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0.75" anyhow = "1.0.75"
chrono = { version = "0.4.31", features = ["serde"] }
clap = { version = "4.4.2", features = ["derive"] } clap = { version = "4.4.2", features = ["derive"] }
flate2 = "1.0.28" flate2 = "1.0.28"
futures = "0.3.30" futures = "0.3.30"
@ -16,7 +17,9 @@ rbx_binary = "0.7.1"
rbx_dom_weak = "2.5.0" rbx_dom_weak = "2.5.0"
rbx_reflection_database = "0.2.7" rbx_reflection_database = "0.2.7"
rbx_xml = "0.13.1" rbx_xml = "0.13.1"
reqwest = { version = "0.11.23", features = ["cookies"] } reqwest = { version = "0.11.23", features = ["cookies", "json"] }
serde = { version = "1.0.194", features = ["derive"] }
serde_json = "1.0.111"
tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread", "fs"] } tokio = { version = "1.35.1", features = ["macros", "rt-multi-thread", "fs"] }
[profile.release] [profile.release]

View File

@ -2,6 +2,7 @@ use std::io::{Read,Seek};
use clap::{Args,Parser,Subcommand}; use clap::{Args,Parser,Subcommand};
use anyhow::Result as AResult; use anyhow::Result as AResult;
use futures::StreamExt; use futures::StreamExt;
use serde::Serialize;
type AssetID=u64; type AssetID=u64;
type AssetIDFileMap=Vec<(AssetID,std::path::PathBuf)>; type AssetIDFileMap=Vec<(AssetID,std::path::PathBuf)>;
@ -53,6 +54,24 @@ struct PathBufList{
paths:Vec<std::path::PathBuf> paths:Vec<std::path::PathBuf>
} }
#[derive(serde::Deserialize)]
struct VersionPage{
previousPageCursor:Option<String>,
nextPageCursor:Option<String>,
data:Vec<AssetVersion>,
}
#[derive(serde::Deserialize,serde::Serialize)]
struct AssetVersion{
Id:u64,
assetId:AssetID,
assetVersionNumber:u64,
creatorType:String,
creatorTargetId:u64,
creatingUniverseId:Option<u64>,
created:chrono::DateTime<chrono::Utc>,
isPublished:bool,
}
#[tokio::main] #[tokio::main]
async fn main()->AResult<()>{ async fn main()->AResult<()>{
let cli=Cli::parse(); let cli=Cli::parse();
@ -68,19 +87,58 @@ async fn main()->AResult<()>{
let cookie=format!(".ROBLOSECURITY={}",match cookie_enum{ let cookie=format!(".ROBLOSECURITY={}",match cookie_enum{
Cookie::Literal(s)=>s, Cookie::Literal(s)=>s,
Cookie::Environment(var)=>std::env::var(var)?, Cookie::Environment(var)=>std::env::var(var)?,
Cookie::File(path)=>tokio::fs::read_to_string(path).await?, Cookie::File(path)=>tokio::fs::read_to_string(path).await?.trim().to_string(),
}); });
let group=match cli.group{ let group=match cli.group{
Some(group_id)=>Owner::Group(group_id), Some(group_id)=>Owner::Group(group_id),
None=>Owner::User, None=>Owner::User,
}; };
{
match cli.command{ let client=reqwest::Client::new();
Commands::Download=>download_list(cookie,vec![cli.asset_id]).await, let mut cursor:Option<String>=None;
Commands::Upload=>upload_list(cookie,group,vec![cli.asset_id]).await, let mut asset_list=Vec::new();
loop{
let mut url=reqwest::Url::parse("https://develop.roblox.com/v1/assets/252877716/saved-versions")?;
//url borrow scope
{
let mut query=url.query_pairs_mut();//borrow here
//query.append_pair("sortOrder","Asc");
//query.append_pair("limit","100");
//query.append_pair("count","100");
match &cursor{
Some(next_page)=>{query.append_pair("cursor",next_page);}
None=>(),
} }
} }
println!("url={}",url);
let resp=client.get(url)
.header("Cookie",cookie.clone())
.send().await?;
println!("resp:{:?}",resp);
match resp.json::<VersionPage>().await{
Ok(mut page)=>{
asset_list.append(&mut page.data);
if page.nextPageCursor.is_none(){
break;
}
cursor=page.nextPageCursor;
},
Err(e)=>panic!("error: {}",e),
}
}
asset_list.sort_by(|a,b|a.assetVersionNumber.cmp(&b.assetVersionNumber));
let mut path=std::path::PathBuf::new();
path.set_file_name("versions.json");
tokio::fs::write(path,serde_json::to_string(&asset_list)?).await?;
};
Ok(())
// match cli.command{
// Commands::Download=>download_list(cookie,vec![cli.asset_id]).await,
// Commands::Upload=>upload_list(cookie,group,vec![cli.asset_id]).await,
// }
}
enum Owner{ enum Owner{
Group(u64), Group(u64),
@ -169,17 +227,30 @@ fn read_readable(mut readable:impl Read)->AResult<Vec<u8>>{
Ok(contents) Ok(contents)
} }
const BHOP_PLACEID:u64=252877716;
async fn download_list(cookie:String,asset_id_file_map:AssetIDFileMap)->AResult<()>{ async fn download_list(cookie:String,asset_id_file_map:AssetIDFileMap)->AResult<()>{
let client=reqwest::Client::new(); let client=reqwest::Client::new();
futures::stream::iter(asset_id_file_map) futures::stream::iter(1..=1006)
.map(|(asset_id,file)|{ .map(|version_id|{
let client=&client; let client=&client;
let cookie=cookie.as_str(); let cookie=cookie.as_str();
async move{ async move{
let resp=client.get(format!("https://assetdelivery.roblox.com/v1/asset/?ID={}",asset_id)) let mut url=reqwest::Url::parse("https://assetdelivery.roblox.com/v1/asset/")?;
//url borrow scope
{
let mut query=url.query_pairs_mut();//borrow here
query.append_pair("ID",BHOP_PLACEID.to_string().as_str());
query.append_pair("version",version_id.to_string().as_str());
}
println!("url={}",url);
let resp=client.get(url)
.header("Cookie",cookie) .header("Cookie",cookie)
.send().await?; .send().await?;
Ok((file,resp.bytes().await?))
let mut path=std::path::PathBuf::new();
path.set_file_name(BHOP_PLACEID.to_string()+"_v"+version_id.to_string().as_str()+".rbxl");
Ok((path,resp.bytes().await?))
} }
}) })
.buffer_unordered(CONCURRENT_REQUESTS) .buffer_unordered(CONCURRENT_REQUESTS)