LCE-Emerald-Launcher/src-tauri/src/commands/skin.rs

186 lines
8.2 KiB
Rust

use std::fs;
use std::path::PathBuf;
use tauri::AppHandle;
use tauri_plugin_opener::OpenerExt;
use base64::Engine;
use crate::types::ScreenshotInfo;
use crate::config;
use crate::util;
#[tauri::command]
pub async fn fetch_skin(username: String) -> Result<(String, String), String> {
let client = reqwest::Client::new();
let mojang_url = format!("https://api.mojang.com/users/profiles/minecraft/{}", username);
let mojang_res = client.get(&mojang_url).send().await.map_err(|e| format!("Failed request to mojang: {}", e))?;
if !mojang_res.status().is_success() {
return Err("Player not found".to_string());
}
let mojang_text = mojang_res.text().await.map_err(|e| format!("Failed to read mojang text: {}", e))?;
let mojang_data: serde_json::Value = serde_json::from_str(&mojang_text).map_err(|e| format!("Invalid Mojang JSON: {}", e))?;
let id = mojang_data.get("id").and_then(|v| v.as_str()).ok_or_else(|| "Invalid Moajng response format".to_string())?;
let name_exact = mojang_data.get("name").and_then(|v| v.as_str()).unwrap_or(&username).to_string();
let mc_api_url = format!("https://api.minecraftapi.net/v3/profile/{}", id);
let mc_api_res = client.get(&mc_api_url).send().await.map_err(|e| format!("Failed request to mc api: {}", e))?;
if !mc_api_res.status().is_success() {
return Err("Error fetching skin data".to_string());
}
let mc_api_text = mc_api_res.text().await.map_err(|e| format!("Failed to read mc api text: {}", e))?;
let mc_api_data: serde_json::Value = serde_json::from_str(&mc_api_text).map_err(|e| format!("Invalid MC API JSON: {}", e))?;
let image_b64 = mc_api_data.get("skin")
.and_then(|s| s.get("image"))
.and_then(|v| v.as_str())
.ok_or_else(|| "No skin found".to_string())?;
Ok((image_b64.to_string(), name_exact))
}
#[tauri::command]
pub async fn download_logo(app: AppHandle, id: String, url: String) -> Result<String, String> {
let logos_dir = util::get_app_dir(&app).join("logos");
fs::create_dir_all(&logos_dir).map_err(|e| e.to_string())?;
let file_ext = if url.to_lowercase().ends_with(".png") {
"png"
} else if url.to_lowercase().ends_with(".jpg") || url.to_lowercase().ends_with(".jpeg") {
"jpg"
} else {
"png"
};
let filename = format!("{}.{}", id, file_ext);
let dest_path = logos_dir.join(&filename);
let response = reqwest::get(&url).await.map_err(|e| e.to_string())?;
if !response.status().is_success() {
return Err(format!("Failed to download logo: {}", response.status()));
}
let bytes = response.bytes().await.map_err(|e| e.to_string())?;
fs::write(&dest_path, bytes).map_err(|e| e.to_string())?;
Ok(dest_path.to_string_lossy().to_string())
}
#[tauri::command]
pub fn get_screenshots(app: AppHandle) -> Vec<ScreenshotInfo> {
let mut screenshots = Vec::new();
let root = util::get_app_dir(&app);
let mut instance_dirs = vec![root.join("instances")];
let config_val = config::load_config_raw(app.clone());
if let Some(ref editions) = config_val.custom_editions {
for ed in editions {
if let Some(path) = &ed.path {
instance_dirs.push(PathBuf::from(path));
}
}
}
for base_dir in instance_dirs {
if base_dir.ends_with("instances") {
if let Ok(entries) = fs::read_dir(&base_dir) {
for entry in entries.flatten() {
if entry.path().is_dir() {
let instance_id = entry.file_name().to_string_lossy().to_string();
let dirs_to_check = [
entry.path().join("screenshots"),
entry.path().join("Windows64").join("GameHDD"),
];
for screenshots_dir in dirs_to_check {
if let Ok(files) = fs::read_dir(screenshots_dir) {
for file in files.flatten() {
let path = file.path();
if path.extension().and_then(|s| s.to_str()) == Some("png") {
let name = path.file_name().unwrap_or_default().to_string_lossy().to_string();
let date = path.metadata().and_then(|m| m.modified()).ok()
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.as_secs())
.unwrap_or(0);
screenshots.push(ScreenshotInfo {
path: path.to_string_lossy().to_string(),
instance_id: instance_id.clone(),
name,
date,
});
}
}
}
}
}
}
}
} else {
let instance_id = base_dir.file_name().and_then(|n| n.to_str()).unwrap_or("custom").to_string();
let final_id = config_val.custom_editions.as_ref()
.and_then(|eds| eds.iter().find(|e| e.path.as_deref() == base_dir.to_str()).map(|e| e.id.clone()))
.unwrap_or(instance_id);
let dirs_to_check = [
base_dir.join("screenshots"),
base_dir.join("Windows64").join("GameHDD"),
];
for screenshots_dir in dirs_to_check {
if let Ok(files) = fs::read_dir(screenshots_dir) {
for file in files.flatten() {
let path = file.path();
if path.extension().and_then(|s| s.to_str()) == Some("png") {
let name = path.file_name().unwrap_or_default().to_string_lossy().to_string();
let date = path.metadata().and_then(|m| m.modified()).ok()
.and_then(|t| t.duration_since(std::time::UNIX_EPOCH).ok())
.map(|d| d.as_secs())
.unwrap_or(0);
screenshots.push(ScreenshotInfo {
path: path.to_string_lossy().to_string(),
instance_id: final_id.clone(),
name,
date,
});
}
}
}
}
}
}
screenshots.sort_by(|a, b| b.date.cmp(&a.date));
screenshots
}
#[tauri::command]
pub fn delete_screenshot(path: String) -> Result<(), String> {
fs::remove_file(path).map_err(|e| e.to_string())
}
#[tauri::command]
pub fn open_screenshot_folder(app: AppHandle, path: String) {
let p = std::path::Path::new(&path);
if let Some(parent) = p.parent() {
if parent.exists() {
let _ = app.opener().open_path(parent.to_str().unwrap(), None::<&str>);
}
}
}
#[tauri::command]
pub async fn save_global_skin_pck(app: AppHandle, pck_data: Vec<u8>) -> Result<(), String> {
let app_dir = util::get_app_dir(&app);
let _ = fs::write(app_dir.join("Skin.pck"), pck_data);
Ok(())
}
#[tauri::command]
pub fn read_screenshot_as_data_url(path: String) -> Result<String, String> {
let data = fs::read(&path).map_err(|e| e.to_string())?;
let mime = if path.to_lowercase().ends_with(".png") {
"image/png"
} else if path.to_lowercase().ends_with(".jpg") || path.to_lowercase().ends_with(".jpeg") {
"image/jpeg"
} else if path.to_lowercase().ends_with(".gif") {
"image/gif"
} else if path.to_lowercase().ends_with(".webp") {
"image/webp"
} else if data.len() > 4 && data[..4] == [0x89, 0x50, 0x4E, 0x47] {
"image/png"
} else if data.len() > 2 && data[..2] == [0xFF, 0xD8] {
"image/jpeg"
} else {
"image/png"
};
let b64 = base64::engine::general_purpose::STANDARD.encode(&data);
Ok(format!("data:{};base64,{}", mime, b64))
}