forked from StrafesNET/asset-tool
more
This commit is contained in:
parent
9916b54166
commit
90a92447b6
101
src/main.rs
101
src/main.rs
@ -137,6 +137,12 @@ struct CreateAssetMediasSubcommand{
|
|||||||
api_key_envvar:Option<String>,
|
api_key_envvar:Option<String>,
|
||||||
#[arg(long,group="api_key",required=true)]
|
#[arg(long,group="api_key",required=true)]
|
||||||
api_key_file:Option<PathBuf>,
|
api_key_file:Option<PathBuf>,
|
||||||
|
#[arg(long,group="cookie",required=true)]
|
||||||
|
cookie_literal:Option<String>,
|
||||||
|
#[arg(long,group="cookie",required=true)]
|
||||||
|
cookie_envvar:Option<String>,
|
||||||
|
#[arg(long,group="cookie",required=true)]
|
||||||
|
cookie_file:Option<PathBuf>,
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
description:Option<String>,
|
description:Option<String>,
|
||||||
#[arg(long,group="creator",required=true)]
|
#[arg(long,group="creator",required=true)]
|
||||||
@ -451,6 +457,11 @@ async fn main()->AResult<()>{
|
|||||||
subcommand.api_key_envvar,
|
subcommand.api_key_envvar,
|
||||||
subcommand.api_key_file,
|
subcommand.api_key_file,
|
||||||
).await?,
|
).await?,
|
||||||
|
cookie:cookie_from_args(
|
||||||
|
subcommand.cookie_literal,
|
||||||
|
subcommand.cookie_envvar,
|
||||||
|
subcommand.cookie_file,
|
||||||
|
).await?,
|
||||||
creator:match (subcommand.creator_user_id,subcommand.creator_group_id){
|
creator:match (subcommand.creator_user_id,subcommand.creator_group_id){
|
||||||
(Some(user_id),None)=>rbx_asset::cloud::Creator::userId(user_id.to_string()),
|
(Some(user_id),None)=>rbx_asset::cloud::Creator::userId(user_id.to_string()),
|
||||||
(None,Some(group_id))=>rbx_asset::cloud::Creator::groupId(group_id.to_string()),
|
(None,Some(group_id))=>rbx_asset::cloud::Creator::groupId(group_id.to_string()),
|
||||||
@ -650,8 +661,10 @@ async fn create_asset_media(config:CreateAssetMediaConfig)->AResult<()>{
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// complex operation requires both api key and cookie! how horrible! roblox please fix!
|
||||||
struct CreateAssetMediasConfig{
|
struct CreateAssetMediasConfig{
|
||||||
api_key:ApiKey,
|
api_key:ApiKey,
|
||||||
|
cookie:Cookie,
|
||||||
description:String,
|
description:String,
|
||||||
input_files:Vec<PathBuf>,
|
input_files:Vec<PathBuf>,
|
||||||
creator:rbx_asset::cloud::Creator,
|
creator:rbx_asset::cloud::Creator,
|
||||||
@ -670,19 +683,41 @@ impl std::fmt::Display for CreateAssetMediasError{
|
|||||||
}
|
}
|
||||||
impl std::error::Error for CreateAssetMediasError{}
|
impl std::error::Error for CreateAssetMediasError{}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum DownloadDecalError{
|
||||||
|
ParseInt(std::num::ParseIntError),
|
||||||
|
Get(rbx_asset::cookie::GetError),
|
||||||
|
LoadDom(LoadDomError),
|
||||||
|
FromUtf8(std::string::FromUtf8Error),
|
||||||
|
NoFirstInstance,
|
||||||
|
NoTextureProperty,
|
||||||
|
TexturePropertyInvalid,
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for DownloadDecalError{
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for DownloadDecalError{}
|
||||||
|
|
||||||
async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{
|
async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{
|
||||||
let context=CloudContext::new(config.api_key);
|
let context=CloudContext::new(config.api_key);
|
||||||
|
let cookie_context=CookieContext::new(config.cookie);
|
||||||
|
let expected_price=Some(config.expected_price.unwrap_or(0));
|
||||||
futures::stream::iter(config.input_files.into_iter()
|
futures::stream::iter(config.input_files.into_iter()
|
||||||
//step 1: read file, make create request
|
//step 1: read file, make create request
|
||||||
.map(|path|{
|
.map(|path|{
|
||||||
let config=&config;
|
let description=&config.description;
|
||||||
|
let creator=&config.creator;
|
||||||
let context=&context;
|
let context=&context;
|
||||||
async move{
|
async move{
|
||||||
let model_name=path.file_stem()
|
let model_name=path.file_stem()
|
||||||
.and_then(std::ffi::OsStr::to_str)
|
.and_then(std::ffi::OsStr::to_str)
|
||||||
.ok_or(CreateAssetMediasError::NoFileStem(path.clone()))?;
|
.ok_or(CreateAssetMediasError::NoFileStem(path.clone()))?
|
||||||
|
.to_owned();
|
||||||
let file=tokio::fs::read(path).await?;
|
let file=tokio::fs::read(path).await?;
|
||||||
let asset_type=match file.get(0..4){
|
let asset_type=match file.get(0..4){
|
||||||
|
//png
|
||||||
Some(b"\x89PNG")=>rbx_asset::cloud::AssetType::Decal,
|
Some(b"\x89PNG")=>rbx_asset::cloud::AssetType::Decal,
|
||||||
//jpeg
|
//jpeg
|
||||||
Some(b"\xFF\xD8\xFF\xE0")=>rbx_asset::cloud::AssetType::Decal,
|
Some(b"\xFF\xD8\xFF\xE0")=>rbx_asset::cloud::AssetType::Decal,
|
||||||
@ -692,11 +727,11 @@ async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{
|
|||||||
};
|
};
|
||||||
Ok(context.create_asset(rbx_asset::cloud::CreateAssetRequest{
|
Ok(context.create_asset(rbx_asset::cloud::CreateAssetRequest{
|
||||||
assetType:asset_type,
|
assetType:asset_type,
|
||||||
displayName:model_name.to_owned(),
|
displayName:model_name,
|
||||||
description:config.description.clone(),
|
description:description.clone(),
|
||||||
creationContext:rbx_asset::cloud::CreationContext{
|
creationContext:rbx_asset::cloud::CreationContext{
|
||||||
creator:config.creator,
|
creator:creator.clone(),
|
||||||
expectedPrice:Some(config.expected_price.unwrap_or(0)),
|
expectedPrice:expected_price,
|
||||||
}
|
}
|
||||||
},file).await?)
|
},file).await?)
|
||||||
}
|
}
|
||||||
@ -723,8 +758,31 @@ async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
//step 3: read decal id from operation and download it
|
//step 3: read decal id from operation and download it
|
||||||
.filter_map(|operation|{
|
.filter_map(|asset_response|{
|
||||||
|
let parse_result=asset_response.assetId.parse();
|
||||||
|
async{
|
||||||
|
match async{
|
||||||
|
let file=cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
|
||||||
|
asset_id:parse_result.map_err(DownloadDecalError::ParseInt)?,
|
||||||
|
version:None,
|
||||||
|
}).await.map_err(DownloadDecalError::Get)?;
|
||||||
|
println!("{}",String::from_utf8(file.clone()).map_err(DownloadDecalError::FromUtf8)?);
|
||||||
|
let dom=load_dom(std::io::Cursor::new(file)).map_err(DownloadDecalError::LoadDom)?;
|
||||||
|
let instance=dom.get_by_ref(
|
||||||
|
*dom.root().children().first().ok_or(DownloadDecalError::NoFirstInstance)?
|
||||||
|
).ok_or(DownloadDecalError::NoFirstInstance)?;
|
||||||
|
match instance.properties.get("Texture").ok_or(DownloadDecalError::NoTextureProperty)?{
|
||||||
|
rbx_dom_weak::types::Variant::String(s)=>Ok(s.clone()),
|
||||||
|
_=>Err(DownloadDecalError::TexturePropertyInvalid),
|
||||||
|
}
|
||||||
|
}.await{
|
||||||
|
Ok(yeah)=>Some(yeah),
|
||||||
|
Err(e)=>{
|
||||||
|
eprintln!("get_asset error: {}",e);
|
||||||
|
None
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -974,18 +1032,33 @@ async fn download_history(mut config:DownloadHistoryConfig)->AResult<()>{
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_dom<R:Read>(input:R)->AResult<rbx_dom_weak::WeakDom>{
|
#[derive(Debug)]
|
||||||
|
enum LoadDomError{
|
||||||
|
IO(std::io::Error),
|
||||||
|
RbxBinary(rbx_binary::DecodeError),
|
||||||
|
RbxXml(rbx_xml::DecodeError),
|
||||||
|
UnknownRobloxFile([u8;4]),
|
||||||
|
UnsupportedFile,
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for LoadDomError{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for LoadDomError{}
|
||||||
|
|
||||||
|
fn load_dom<R:Read>(input:R)->Result<rbx_dom_weak::WeakDom,LoadDomError>{
|
||||||
let mut buf=std::io::BufReader::new(input);
|
let mut buf=std::io::BufReader::new(input);
|
||||||
let peek=std::io::BufRead::fill_buf(&mut buf)?;
|
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(LoadDomError::IO)?;
|
||||||
match &peek[0..4]{
|
match &peek[0..4]{
|
||||||
b"<rob"=>{
|
b"<rob"=>{
|
||||||
match &peek[4..8]{
|
match &peek[4..8]{
|
||||||
b"lox!"=>rbx_binary::from_reader(buf).map_err(anyhow::Error::msg),
|
b"lox!"=>rbx_binary::from_reader(buf).map_err(LoadDomError::RbxBinary),
|
||||||
b"lox "=>rbx_xml::from_reader_default(buf).map_err(anyhow::Error::msg),
|
b"lox "=>rbx_xml::from_reader_default(buf).map_err(LoadDomError::RbxXml),
|
||||||
other=>Err(anyhow::Error::msg(format!("Unknown Roblox file type {:?}",other))),
|
other=>Err(LoadDomError::UnknownRobloxFile(other.try_into().unwrap())),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_=>Err(anyhow::Error::msg("unsupported file type")),
|
_=>Err(LoadDomError::UnsupportedFile),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user