mirror of
https://github.com/LCE-Hub/LCE-Emerald-Launcher.git
synced 2026-07-15 23:10:48 +00:00
feat(VersionsMenu): download to custom path (#87)
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user