Skip to content

Commit b155f1f

Browse files
committed
MTP: Show live disk space for MTP volumes
- `get_volume_space` detects `mtp://` paths and queries the device via USB (`GetStorageInfo`) instead of falling through to NSURL/statvfs - New `get_live_storage_space()` on `MtpConnectionManager` fetches fresh space from the device and updates the cached `MtpStorageInfo` so other consumers stay current - Both macOS and Linux volume commands handle MTP paths
1 parent d37b8a5 commit b155f1f

3 files changed

Lines changed: 98 additions & 1 deletion

File tree

apps/desktop/src-tauri/src/commands/volumes.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,30 @@ pub async fn find_containing_volume(path: String) -> TimedOut<Option<VolumeInfo>
5151

5252
/// Gets space information for a volume at the given path.
5353
/// Returns total and available bytes for the volume.
54+
/// For MTP paths (`mtp://`), fetches from the MTP connection manager instead of macOS NSURL.
5455
#[tauri::command]
5556
pub async fn get_volume_space(path: String) -> TimedOut<Option<VolumeSpaceInfo>> {
57+
if let Some(space) = get_mtp_space_info(&path).await {
58+
return TimedOut {
59+
data: Some(space),
60+
timed_out: false,
61+
};
62+
}
5663
blocking_with_timeout_flag(VOLUME_TIMEOUT, None, move || volumes::get_volume_space(&path)).await
5764
}
65+
66+
/// Queries live MTP space info from a `mtp://{device_id}/{storage_id}/...` path.
67+
async fn get_mtp_space_info(path: &str) -> Option<VolumeSpaceInfo> {
68+
let rest = path.strip_prefix("mtp://")?;
69+
let mut parts = rest.splitn(3, '/');
70+
let device_id = parts.next()?;
71+
let storage_id: u32 = parts.next()?.parse().ok()?;
72+
73+
let (total_bytes, available_bytes) = crate::mtp::connection_manager()
74+
.get_live_storage_space(device_id, storage_id)
75+
.await?;
76+
Some(VolumeSpaceInfo {
77+
total_bytes,
78+
available_bytes,
79+
})
80+
}

apps/desktop/src-tauri/src/commands/volumes_linux.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,33 @@ pub fn find_containing_volume(path: String) -> TimedOut<Option<VolumeInfo>> {
4545
}
4646

4747
/// Gets space information for a volume at the given path.
48+
/// For MTP paths (`mtp://`), fetches from the MTP connection manager instead of statvfs.
4849
#[tauri::command]
49-
pub fn get_volume_space(path: String) -> TimedOut<Option<VolumeSpaceInfo>> {
50+
pub async fn get_volume_space(path: String) -> TimedOut<Option<VolumeSpaceInfo>> {
51+
if let Some(space) = get_mtp_space_info(&path).await {
52+
return TimedOut {
53+
data: Some(space),
54+
timed_out: false,
55+
};
56+
}
5057
TimedOut {
5158
data: volumes_linux::get_volume_space(&path),
5259
timed_out: false,
5360
}
5461
}
62+
63+
/// Queries live MTP space info from a `mtp://{device_id}/{storage_id}/...` path.
64+
async fn get_mtp_space_info(path: &str) -> Option<VolumeSpaceInfo> {
65+
let rest = path.strip_prefix("mtp://")?;
66+
let mut parts = rest.splitn(3, '/');
67+
let device_id = parts.next()?;
68+
let storage_id: u32 = parts.next()?.parse().ok()?;
69+
70+
let (total_bytes, available_bytes) = crate::mtp::connection_manager()
71+
.get_live_storage_space(device_id, storage_id)
72+
.await?;
73+
Some(VolumeSpaceInfo {
74+
total_bytes,
75+
available_bytes,
76+
})
77+
}

apps/desktop/src-tauri/src/mtp/connection/mod.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,57 @@ impl MtpConnectionManager {
418418
storages: entry.storages.clone(),
419419
})
420420
}
421+
422+
/// Queries live storage space from the device and updates the cache.
423+
///
424+
/// Returns `(total_bytes, available_bytes)` freshly read from the device,
425+
/// or `None` if the device/storage is not found or the query fails.
426+
pub async fn get_live_storage_space(
427+
&self,
428+
device_id: &str,
429+
storage_id: u32,
430+
) -> Option<(u64, u64)> {
431+
let device_arc = {
432+
let devices = self.devices.lock().await;
433+
devices.get(device_id)?.device.clone()
434+
};
435+
436+
let device = match acquire_device_lock(&device_arc, device_id, "get_live_storage_space").await {
437+
Ok(d) => d,
438+
Err(e) => {
439+
warn!("get_live_storage_space: failed to acquire lock: {:?}", e);
440+
return None;
441+
}
442+
};
443+
444+
let mtp_storage_id = mtp_rs::ptp::StorageId(storage_id);
445+
let storage = match device.storage(mtp_storage_id).await {
446+
Ok(s) => s,
447+
Err(e) => {
448+
warn!("get_live_storage_space: failed to query storage {}: {:?}", storage_id, e);
449+
return None;
450+
}
451+
};
452+
let info = storage.info();
453+
let total = info.max_capacity;
454+
let available = info.free_space_bytes;
455+
456+
// Release the device lock before updating the cache
457+
drop(device);
458+
459+
// Update cache so other consumers (e.g. volume breadcrumb) see fresh data too
460+
{
461+
let mut devices = self.devices.lock().await;
462+
if let Some(entry) = devices.get_mut(device_id) {
463+
if let Some(cached) = entry.storages.iter_mut().find(|s| s.id == storage_id) {
464+
cached.total_bytes = total;
465+
cached.available_bytes = available;
466+
}
467+
}
468+
}
469+
470+
Some((total, available))
471+
}
421472
}
422473

423474
// Remaining impl blocks are in submodules:

0 commit comments

Comments
 (0)