Add PS5 checks

This commit is contained in:
unicxrn committed 2026-10-10 22:01:11 +02:00
1 parent fb2b40a019
commit cbca4d4bb8
4 files changed
+33 -4

No files matched your search

+3 -1
View File
@@ -52,7 +52,9 @@ impl Ftp {
.next()
.ok_or_else(|| format!("Can't resolve {host}"))?;
let stream = TcpStream::connect_timeout(&addr, connect_timeout)
.map_err(|e| format!("No FTP server at {host}:{port} ({e})"))?;
.map_err(|e| {
format!("Can't reach the PS5 at {host}:{port} ({e}). Check it's on, jailbroken, and running an FTP server.")
})?;
stream.set_read_timeout(Some(TIMEOUT)).ok();
stream.set_write_timeout(Some(TIMEOUT)).ok();
stream.set_nodelay(true).ok();
+12 -1
View File
@@ -108,6 +108,16 @@ async fn ps5_status(value: Option<settings::Settings>) -> Result<ps5::Ps5Status,
blocking(move || ps5::status(&value.unwrap_or_else(settings::load))).await
}
/// Free space for the folder, or its nearest existing parent (the image folder may not exist yet).
#[tauri::command]
fn free_space(path: String) -> Option<u64> {
let mut p = PathBuf::from(path);
while !p.exists() {
p = p.parent()?.to_path_buf();
}
pipeline::free_space(&p)
}
#[tauri::command]
async fn discover_ps5() -> Result<Vec<discover::Found>, String> {
blocking(discover::discover).await
@@ -152,7 +162,8 @@ pub fn run() {
remove_job,
resume_job,
ps5_status,
discover_ps5
discover_ps5,
free_space
])
.run(tauri::generate_context!())
.expect("error while running Prospack");
+6
View File
@@ -950,6 +950,11 @@ mod e2e {
};
settings::save(&s).unwrap();
// Wizard checks against the fake PS5: ShadowMountPlus yes, Game Compressor not yet.
let st = ps5::status(&s);
assert!(st.online && st.shadowmount && !st.game_compressor, "{st:?}");
assert!(ps5::status(&Settings { ps5_host: "".into(), ..s.clone() }).error.unwrap().contains("No PS5 address set"));
let engine = Arc::new(Engine::new(&settings::data_dir()));
let q = Queue::new(Arc::new(|_, _| {}), engine);
let id = q.add(crate::dump::inspect(&dump).unwrap()).unwrap();
@@ -979,6 +984,7 @@ mod e2e {
assert_eq!(rec["sourceSize"], local);
assert_eq!(rec["compressionSourceSize"], j.dump.total_bytes);
assert!(ps5_root.join("data/GameCompressor/validations/PPSA00001.json").is_file());
assert!(ps5::status(&s).game_compressor, "the record created /data/GameCompressor");
let ml = std::fs::read_to_string(ps5_root.join("data/shadowmount/manual.lst")).unwrap();
assert_eq!(ml, "/data/homebrew/PPSA00001.ffpfsc\n");
assert!(dump.join("ampr_emu.index").is_file(), "MkPFS should have generated the AMPR index");
+12 -2
View File
@@ -21,10 +21,15 @@ pub struct Ps5Status {
pub free_bytes: Option<u64>,
pub total_bytes: Option<u64>,
pub shadowmount: bool,
/// /data/GameCompressor exists (optional; used for the validated badge).
pub game_compressor: bool,
}
pub fn connect(s: &Settings) -> Result<Ftp, String> {
Ftp::connect(&s.ps5_host, s.ftp_port, Duration::from_secs(4))
if s.ps5_host.trim().is_empty() {
return Err("No PS5 address set. Open Settings → Run setup again, or enter your PS5's IP in Settings.".into());
}
Ftp::connect(s.ps5_host.trim(), s.ftp_port, Duration::from_secs(4))
}
/// Read-only check: FTP login + NOOP, ShadowMountPlus config present, and free space from
@@ -35,14 +40,19 @@ pub fn status(s: &Settings) -> Ps5Status {
Ok(f) => f,
Err(e) => return Ps5Status { error: Some(e), ..Default::default() },
};
// (connect() already rejects an empty address with a helpful message.)
if let Err(e) = ftp.noop() {
return Ps5Status { error: Some(e), ..Default::default() };
}
let latency_ms = Some(t0.elapsed().as_millis() as u64);
let shadowmount = ftp.size(&format!("{SMP_DIR}/config.ini")).ok().flatten().is_some();
let game_compressor = ftp
.list("/data")
.map(|entries| entries.iter().any(|e| e.is_dir && e.name == "GameCompressor"))
.unwrap_or(false);
ftp.quit();
let (free_bytes, total_bytes) = space(s).unwrap_or((None, None));
Ps5Status { online: true, latency_ms, error: None, free_bytes, total_bytes, shadowmount }
Ps5Status { online: true, latency_ms, error: None, free_bytes, total_bytes, shadowmount, game_compressor }
}
/// GET /api/space?path=/data on the PS5 web file manager. Optional; a missing server is fine.