From 6eca84a08af3b910462f1009956a926fa88fac9d Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Wed, 12 Feb 2025 14:55:19 -0800
Subject: [PATCH] DownloadVersion command

---
 src/main.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 44 insertions(+), 3 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 2bea961..272d5a4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -22,6 +22,7 @@ struct Cli{
 enum Commands{
 	DownloadHistory(DownloadHistorySubcommand),
 	Download(DownloadSubcommand),
+	DownloadVersion(DownloadVersionSubcommand),
 	DownloadDecompile(DownloadDecompileSubcommand),
 	DownloadCreationsJson(DownloadCreationsJsonSubcommand),
 	DownloadUserInventoryJson(DownloadUserInventoryJsonSubcommand),
@@ -59,7 +60,7 @@ struct DownloadHistorySubcommand{
 	#[arg(long)]
 	end_version:Option<u64>,
 }
-/// Download a single asset by id.
+/// Download a list of assets by id.
 #[derive(Args)]
 struct DownloadSubcommand{
 	#[arg(long,group="cookie",required=true)]
@@ -73,6 +74,22 @@ struct DownloadSubcommand{
 	#[arg(required=true)]
 	asset_ids:Vec<AssetID>,
 }
+/// Download a single asset by id, optionally specifying the version to download.
+#[derive(Args)]
+struct DownloadVersionSubcommand{
+	#[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)]
+	output_folder:Option<PathBuf>,
+	#[arg(long)]
+	asset_id:AssetID,
+	#[arg(long)]
+	asset_version:Option<u64>,
+}
 /// Download the list of asset ids (not the assets themselves) created by a group or user.  The output is written to `output_folder/versions.json`
 #[derive(Args)]
 struct DownloadCreationsJsonSubcommand{
@@ -137,7 +154,7 @@ struct CreateAssetMediaSubcommand{
 	#[arg(long)]
 	model_name:String,
 	#[arg(long)]
-	description:Option<String>,
+	description:String,
 	#[arg(long)]
 	input_file:PathBuf,
 	#[arg(long)]
@@ -429,6 +446,23 @@ async fn main()->AResult<()>{
 				}).collect()
 			).await
 		},
+		Commands::DownloadVersion(subcommand)=>{
+			let output_folder=subcommand.output_folder.unwrap_or_else(||std::env::current_dir().unwrap());
+			download_version(
+				cookie_from_args(
+					subcommand.cookie_literal,
+					subcommand.cookie_envvar,
+					subcommand.cookie_file,
+				).await?,
+				subcommand.asset_id,
+				subcommand.asset_version,
+				{
+					let mut path=output_folder.clone();
+					path.push(subcommand.asset_id.to_string());
+					path
+				},
+			).await
+		},
 		Commands::DownloadDecompile(subcommand)=>{
 			download_decompile(DownloadDecompileConfig{
 				cookie:cookie_from_args(
@@ -492,7 +526,7 @@ async fn main()->AResult<()>{
 			input_file:subcommand.input_file,
 			asset_type:subcommand.asset_type.cloud(),
 			model_name:subcommand.model_name,
-			description:subcommand.description.unwrap_or_else(||String::with_capacity(0)),
+			description:subcommand.description,
 			expected_price:subcommand.expected_price,
 		}).await,
 		Commands::CreateAssetMedias(subcommand)=>create_asset_medias(CreateAssetMediasConfig{
@@ -906,6 +940,13 @@ async fn upload_place(config:UploadPlaceConfig)->AResult<()>{
 	Ok(())
 }
 
+async fn download_version(cookie:Cookie,asset_id:AssetID,version:Option<u64>,dest:PathBuf)->AResult<()>{
+	let context=CookieContext::new(cookie);
+	let data=context.get_asset(rbx_asset::cookie::GetAssetRequest{asset_id,version}).await?;
+	tokio::fs::write(dest,data).await?;
+	Ok(())
+}
+
 async fn download_list(cookie:Cookie,asset_id_file_map:AssetIDFileMap)->AResult<()>{
 	let context=CookieContext::new(cookie);
 	futures::stream::iter(asset_id_file_map.into_iter()