Create multiple assets concurrently #5
87
src/main.rs
87
src/main.rs
@ -626,7 +626,7 @@ async fn get_asset_exp_backoff(
|
|||||||
context:&CloudContext,
|
context:&CloudContext,
|
||||||
create_asset_response:&rbx_asset::cloud::CreateAssetResponse
|
create_asset_response:&rbx_asset::cloud::CreateAssetResponse
|
||||||
)->Result<rbx_asset::cloud::AssetResponse,rbx_asset::cloud::CreateAssetResponseGetAssetError>{
|
)->Result<rbx_asset::cloud::AssetResponse,rbx_asset::cloud::CreateAssetResponseGetAssetError>{
|
||||||
let mut backoff:u64=0;
|
let mut backoff:u16=0;
|
||||||
loop{
|
loop{
|
||||||
match create_asset_response.try_get_asset(&context).await{
|
match create_asset_response.try_get_asset(&context).await{
|
||||||
//try again when the operation is not done
|
//try again when the operation is not done
|
||||||
@ -672,9 +672,12 @@ struct CreateAssetMediasConfig{
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
enum CreateAssetMediasError{
|
enum CreateAssetMediasError{
|
||||||
NoFileStem(PathBuf),
|
NoFileStem(PathBuf),
|
||||||
UnknownFourCC(Option<Vec<u8>>),
|
IO(std::io::Error),
|
||||||
|
UnknownFourCC(Option<[u8;4]>),
|
||||||
|
Create(rbx_asset::cloud::CreateError),
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for CreateAssetMediasError{
|
impl std::fmt::Display for CreateAssetMediasError{
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
@ -684,7 +687,22 @@ impl std::fmt::Display for CreateAssetMediasError{
|
|||||||
impl std::error::Error for CreateAssetMediasError{}
|
impl std::error::Error for CreateAssetMediasError{}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum PollOperationError{
|
||||||
|
CreateAssetMedias(CreateAssetMediasError),
|
||||||
|
CreateAssetResponseGetAsset(rbx_asset::cloud::CreateAssetResponseGetAssetError),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for PollOperationError{
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for PollOperationError{}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
enum DownloadDecalError{
|
enum DownloadDecalError{
|
||||||
|
PollOperation(PollOperationError),
|
||||||
ParseInt(std::num::ParseIntError),
|
ParseInt(std::num::ParseIntError),
|
||||||
Get(rbx_asset::cookie::GetError),
|
Get(rbx_asset::cookie::GetError),
|
||||||
LoadDom(LoadDomError),
|
LoadDom(LoadDomError),
|
||||||
@ -703,7 +721,7 @@ 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 cookie_context=CookieContext::new(config.cookie);
|
||||||
let expected_price=Some(config.expected_price.unwrap_or(0));
|
let expected_price=Some(config.expected_price.unwrap_or(0));
|
||||||
let asset_id_list=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 description=&config.description;
|
let description=&config.description;
|
||||||
@ -712,9 +730,9 @@ async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{
|
|||||||
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_else(||CreateAssetMediasError::NoFileStem(path.clone()))?
|
||||||
.to_owned();
|
.to_owned();
|
||||||
let file=tokio::fs::read(path).await?;
|
let file=tokio::fs::read(path).await.map_err(CreateAssetMediasError::IO)?;
|
||||||
let asset_type=match file.get(0..4){
|
let asset_type=match file.get(0..4){
|
||||||
//png
|
//png
|
||||||
Some(b"\x89PNG")=>rbx_asset::cloud::AssetType::Decal,
|
Some(b"\x89PNG")=>rbx_asset::cloud::AssetType::Decal,
|
||||||
@ -722,9 +740,9 @@ async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{
|
|||||||
Some(b"\xFF\xD8\xFF\xE0")=>rbx_asset::cloud::AssetType::Decal,
|
Some(b"\xFF\xD8\xFF\xE0")=>rbx_asset::cloud::AssetType::Decal,
|
||||||
//Some("fbx")=>rbx_asset::cloud::AssetType::Model,
|
//Some("fbx")=>rbx_asset::cloud::AssetType::Model,
|
||||||
//Some("ogg")=>rbx_asset::cloud::AssetType::Audio,
|
//Some("ogg")=>rbx_asset::cloud::AssetType::Audio,
|
||||||
fourcc=>Err(CreateAssetMediasError::UnknownFourCC(fourcc.map(<[u8]>::to_owned)))?,
|
fourcc=>Err(CreateAssetMediasError::UnknownFourCC(fourcc.map(|s|s.try_into().unwrap())))?,
|
||||||
};
|
};
|
||||||
Ok(context.create_asset(rbx_asset::cloud::CreateAssetRequest{
|
context.create_asset(rbx_asset::cloud::CreateAssetRequest{
|
||||||
assetType:asset_type,
|
assetType:asset_type,
|
||||||
displayName:model_name,
|
displayName:model_name,
|
||||||
description:description.clone(),
|
description:description.clone(),
|
||||||
@ -732,59 +750,46 @@ async fn create_asset_medias(config:CreateAssetMediasConfig)->AResult<()>{
|
|||||||
creator:creator.clone(),
|
creator:creator.clone(),
|
||||||
expectedPrice:expected_price,
|
expectedPrice:expected_price,
|
||||||
}
|
}
|
||||||
},file).await?)
|
},file).await.map_err(CreateAssetMediasError::Create)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
//parallel requests
|
//parallel requests
|
||||||
.buffer_unordered(CONCURRENT_REQUESTS)
|
.buffer_unordered(CONCURRENT_REQUESTS)
|
||||||
//step 2: poll operation until it completes
|
//step 2: poll operation until it completes
|
||||||
.filter_map(|create_result:AResult<_>|{
|
.then(|create_result|{
|
||||||
let context=&context;
|
let context=&context;
|
||||||
async{
|
async{
|
||||||
match create_result{
|
let create_asset_response=create_result.map_err(PollOperationError::CreateAssetMedias)?;
|
||||||
Ok(create_asset_response)=>match get_asset_exp_backoff(context,&create_asset_response).await{
|
get_asset_exp_backoff(context,&create_asset_response).await.map_err(PollOperationError::CreateAssetResponseGetAsset)
|
||||||
Ok(asset_response)=>Some(asset_response),
|
|
||||||
Err(e)=>{
|
|
||||||
eprintln!("operation error: {}",e);
|
|
||||||
None
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Err(e)=>{
|
|
||||||
eprintln!("create_asset error: {}",e);
|
|
||||||
None
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
//step 3: read decal id from operation and download it, decode it as a roblox file and extract the texture content url
|
//step 3: read decal id from operation and download it, decode it as a roblox file and extract the texture content url
|
||||||
.filter_map(|asset_response|{
|
.then(|asset_response_result|{
|
||||||
let parse_result=asset_response.assetId.parse();
|
let cookie_context=&cookie_context;
|
||||||
async{
|
async move{
|
||||||
match async{
|
let asset_response=asset_response_result.map_err(DownloadDecalError::PollOperation)?;
|
||||||
let file=cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
|
let file=cookie_context.get_asset(rbx_asset::cookie::GetAssetRequest{
|
||||||
asset_id:parse_result.map_err(DownloadDecalError::ParseInt)?,
|
asset_id:asset_response.assetId.parse().map_err(DownloadDecalError::ParseInt)?,
|
||||||
version:None,
|
version:None,
|
||||||
}).await.map_err(DownloadDecalError::Get)?;
|
}).await.map_err(DownloadDecalError::Get)?;
|
||||||
let dom=load_dom(std::io::Cursor::new(file)).map_err(DownloadDecalError::LoadDom)?;
|
let dom=load_dom(std::io::Cursor::new(file)).map_err(DownloadDecalError::LoadDom)?;
|
||||||
let instance=dom.get_by_ref(
|
let instance=dom.get_by_ref(
|
||||||
*dom.root().children().first().ok_or(DownloadDecalError::NoFirstInstance)?
|
*dom.root().children().first().ok_or(DownloadDecalError::NoFirstInstance)?
|
||||||
).ok_or(DownloadDecalError::NoFirstInstance)?;
|
).ok_or(DownloadDecalError::NoFirstInstance)?;
|
||||||
match instance.properties.get("Texture").ok_or(DownloadDecalError::NoTextureProperty)?{
|
let texture=instance.properties.get("Texture").ok_or(DownloadDecalError::NoTextureProperty)?;
|
||||||
rbx_dom_weak::types::Variant::Content(url)=>Ok(url.clone().into_string()),
|
let asset_url=match texture{
|
||||||
_=>Err(DownloadDecalError::TexturePropertyInvalid),
|
rbx_dom_weak::types::Variant::Content(url)=>url.clone().into_string(),
|
||||||
|
_=>Err(DownloadDecalError::TexturePropertyInvalid)?,
|
||||||
|
};
|
||||||
|
Ok::<_,DownloadDecalError>((asset_response.displayName,asset_url))
|
||||||
}
|
}
|
||||||
}.await{
|
})
|
||||||
Ok(asset_url)=>Some((asset_response.displayName,asset_url)),
|
.for_each(|download_decal_result|async{
|
||||||
Err(e)=>{
|
match download_decal_result{
|
||||||
eprintln!("get_asset error: {}",e);
|
Ok((file_name,asset_url))=>println!("{}={}",file_name,asset_url),
|
||||||
None
|
Err(e)=>eprintln!("ERROR error={e}"),
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).collect::<Vec<(String,String)>>().await;
|
|
||||||
for (file_name,asset_url) in asset_id_list{
|
|
||||||
println!("{}={}",file_name,asset_url);
|
|
||||||
}
|
}
|
||||||
|
}).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user