feat(VersionsMenu): download to custom path (#87)

This commit is contained in:
/home/neo
2026-06-24 19:58:34 +03:00
committed by GitHub
parent e65ea4e06a
commit 5d7d2148cc
8 changed files with 62 additions and 7 deletions

View File

@@ -13,8 +13,7 @@ pub async fn download_and_install(
url: String,
instance_id: String,
) -> Result<String, String> {
let root = util::get_app_dir(&app);
let instance_dir = root.join("instances").join(&instance_id);
let instance_dir = util::get_instance_working_dir(&app, &instance_id);
let token = CancellationToken::new();
let child_token = token.clone();
{
@@ -25,21 +24,18 @@ pub async fn download_and_install(
*lock = Some(token);
}
let root = util::get_app_dir(&app);
let zip_path = root.join(format!("temp_{}.zip", instance_id));
let response = reqwest::get(&url).await.map_err(|e| e.to_string())?;
if !response.status().is_success() {
return Err(format!("Download failed: {}", response.status()));
}
let total_size = response.content_length().unwrap_or(0) as f64;
let last_modified = response.headers().get(reqwest::header::LAST_MODIFIED)
.and_then(|h| h.to_str().ok())
.unwrap_or("")
.to_string();
if !last_modified.is_empty() {
let _ = fs::write(instance_dir.join("update_timestamp.txt"), last_modified);
}
let total_size = response.content_length().unwrap_or(0) as f64;
let mut file = fs::File::create(&zip_path).map_err(|e| e.to_string())?;
let mut downloaded = 0.0;
let mut stream = response.bytes_stream();
@@ -67,6 +63,7 @@ pub async fn download_and_install(
"profile4.dat", "profile5.dat", "profile6.dat", "profile7.dat",
"profile8.dat", "profile9.dat", "profile10.dat", "proton_prefix"
].iter().copied().collect();
if !instance_dir.exists() {
fs::create_dir_all(&instance_dir).map_err(|e| e.to_string())?;
} else {
@@ -98,6 +95,10 @@ pub async fn download_and_install(
}
}
if !last_modified.is_empty() {
let _ = fs::write(instance_dir.join("update_timestamp.txt"), &last_modified);
}
#[cfg(target_os = "linux")]
{
let bsdtar_ok = std::process::Command::new("bsdtar")

View File

@@ -374,6 +374,11 @@ pub fn delete_instance(app: AppHandle, instance_id: String) -> Result<(), String
}
}
}
if let Some(ref custom_paths) = config_val.custom_paths {
if custom_paths.contains_key(&instance_id) {
return Ok(());
}
}
let dir = util::get_app_dir(&app).join("instances").join(&instance_id);
if dir.exists() {
let _ = fs::remove_dir_all(dir);

View File

@@ -22,6 +22,7 @@ pub fn load_config_raw(app: AppHandle) -> AppConfig {
apple_silicon_performance_boost: None,
custom_editions: None,
customizations: None,
custom_paths: None,
profile: Some("legacy_evolved".into()),
animations_enabled: Some(true),
vfx_enabled: Some(true),

View File

@@ -44,6 +44,7 @@ pub struct AppConfig {
pub apple_silicon_performance_boost: Option<bool>,
pub custom_editions: Option<Vec<CustomEdition>>,
pub customizations: Option<std::collections::HashMap<String, CustomizationEntry>>,
pub custom_paths: Option<std::collections::HashMap<String, String>>,
pub profile: Option<String>,
pub animations_enabled: Option<bool>,
pub vfx_enabled: Option<bool>,

View File

@@ -22,6 +22,11 @@ pub fn get_instance_working_dir(app: &AppHandle, instance_id: &str) -> PathBuf {
}
}
}
if let Some(ref custom_paths) = config.custom_paths {
if let Some(path) = custom_paths.get(instance_id) {
return PathBuf::from(path);
}
}
root.join("instances").join(instance_id)
}

View File

@@ -80,6 +80,7 @@ const VersionsView = memo(function VersionsView() {
cycleBranch,
customizations,
updateCustomization,
saveCustomPath,
} = useGame();
const { isDayTime } = useConfig();
const [focusIndex, setFocusIndex] = useState<number>(0);
@@ -508,6 +509,33 @@ const VersionsView = memo(function VersionsView() {
Update Available!
</button>
)}
{!isInstalled && (
<button
onClick={async (e) => {
e.stopPropagation();
playPressSound();
setOpenMenuId(null);
try {
const folder = await TauriService.pickFolder();
if (folder) {
await saveCustomPath(edition.instanceId, folder);
toggleInstall(edition.instanceId);
}
} catch (err) {
if (err !== "CANCELED") console.error(err);
}
}}
className="w-full text-left px-3 py-2 text-xs text-[#dddddd] flex items-center gap-2 mc-text-shadow"
>
<img
src="/images/Download_Icon.png"
alt=""
className="w-3.5 h-3.5 object-contain"
style={{ imageRendering: "pixelated" }}
/>
Download to custom path
</button>
)}
{Array.isArray(edition.branches) &&
edition.branches.length > 0 && (
<button

View File

@@ -521,6 +521,18 @@ export function useGameManager({
[],
);
const saveCustomPath = useCallback(
async (instanceId: string, path: string) => {
const config = await TauriService.loadConfig();
config.customPaths = {
...config.customPaths,
[instanceId]: path,
};
await TauriService.saveConfig(config);
},
[],
);
const addToSteam = useCallback(
async (
id: string,
@@ -578,5 +590,6 @@ export function useGameManager({
cycleBranch,
customizations,
updateCustomization,
saveCustomPath,
};
}

View File

@@ -46,6 +46,7 @@ export interface AppConfig {
launchPrefix?: string;
launchEnvVars?: Record<string, string>;
customizations?: Record<string, { titleImage?: string; panorama?: string }>;
customPaths?: Record<string, string>;
skipIntro?: boolean;
}