Merge pull request #1379 from FreddyFunk/bugfix/external-drives-metadata
Show metadata, thumbnails and gallery view on mounted drives
This commit is contained in:
commit
29a1f7f518
4 changed files with 92 additions and 7 deletions
16
src/app.rs
16
src/app.rs
|
|
@ -1543,7 +1543,11 @@ impl App {
|
||||||
b = b.text(item.name()).data(MounterData(key, item.clone()));
|
b = b.text(item.name()).data(MounterData(key, item.clone()));
|
||||||
let uri = item.uri();
|
let uri = item.uri();
|
||||||
if let Some(path) = item.path() {
|
if let Some(path) = item.path() {
|
||||||
b = b.data(Location::Network(uri, item.name(), Some(path)));
|
if item.is_remote() {
|
||||||
|
b = b.data(Location::Network(uri, item.name(), Some(path)));
|
||||||
|
} else {
|
||||||
|
b = b.data(Location::Path(path));
|
||||||
|
}
|
||||||
} else if !uri.is_empty() {
|
} else if !uri.is_empty() {
|
||||||
b = b.data(Location::Network(uri, item.name(), None));
|
b = b.data(Location::Network(uri, item.name(), None));
|
||||||
}
|
}
|
||||||
|
|
@ -3088,6 +3092,16 @@ impl Application for App {
|
||||||
Message::MountResult(mounter_key, item, res) => match res {
|
Message::MountResult(mounter_key, item, res) => match res {
|
||||||
Ok(true) => {
|
Ok(true) => {
|
||||||
log::info!("connected to {item:?}");
|
log::info!("connected to {item:?}");
|
||||||
|
// Automatically navigate to the mounted location
|
||||||
|
if let Some(path) = item.path() {
|
||||||
|
let location = if item.is_remote() {
|
||||||
|
Location::Network(item.uri(), item.name(), Some(path))
|
||||||
|
} else {
|
||||||
|
Location::Path(path)
|
||||||
|
};
|
||||||
|
let message = Message::TabMessage(None, tab::Message::Location(location));
|
||||||
|
return self.update(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(false) => {
|
Ok(false) => {
|
||||||
log::info!("cancelled connection to {item:?}");
|
log::info!("cancelled connection to {item:?}");
|
||||||
|
|
|
||||||
|
|
@ -33,15 +33,26 @@ fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
|
||||||
let mut items: MounterItems = (monitor.mounts().into_iter())
|
let mut items: MounterItems = (monitor.mounts().into_iter())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, mount)| {
|
.map(|(i, mount)| {
|
||||||
|
let root = MountExt::root(&mount);
|
||||||
|
let is_remote = root
|
||||||
|
.query_filesystem_info(
|
||||||
|
gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE,
|
||||||
|
gio::Cancellable::NONE,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
.and_then(|info| Some(info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE)))
|
||||||
|
.unwrap_or(true); // Default to remote if query fails
|
||||||
|
|
||||||
MounterItem::Gvfs(Item {
|
MounterItem::Gvfs(Item {
|
||||||
uri: mount.root().uri().into(),
|
uri: mount.root().uri().into(),
|
||||||
kind: ItemKind::Mount,
|
kind: ItemKind::Mount,
|
||||||
index: i,
|
index: i,
|
||||||
name: mount.name().into(),
|
name: mount.name().into(),
|
||||||
is_mounted: true,
|
is_mounted: true,
|
||||||
|
is_remote,
|
||||||
icon_opt: gio_icon_to_path(&MountExt::icon(&mount), sizes.grid()),
|
icon_opt: gio_icon_to_path(&MountExt::icon(&mount), sizes.grid()),
|
||||||
icon_symbolic_opt: gio_icon_to_path(&MountExt::symbolic_icon(&mount), 16),
|
icon_symbolic_opt: gio_icon_to_path(&MountExt::symbolic_icon(&mount), 16),
|
||||||
path_opt: MountExt::root(&mount).path(),
|
path_opt: root.path(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -61,6 +72,7 @@ fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
|
||||||
index: i,
|
index: i,
|
||||||
name: volume.name().into(),
|
name: volume.name().into(),
|
||||||
is_mounted: false,
|
is_mounted: false,
|
||||||
|
is_remote: false,
|
||||||
icon_opt: gio_icon_to_path(&VolumeExt::icon(&volume), sizes.grid()),
|
icon_opt: gio_icon_to_path(&VolumeExt::icon(&volume), sizes.grid()),
|
||||||
icon_symbolic_opt: gio_icon_to_path(&VolumeExt::symbolic_icon(&volume), 16),
|
icon_symbolic_opt: gio_icon_to_path(&VolumeExt::symbolic_icon(&volume), 16),
|
||||||
path_opt: None,
|
path_opt: None,
|
||||||
|
|
@ -283,6 +295,7 @@ pub struct Item {
|
||||||
index: usize,
|
index: usize,
|
||||||
name: String,
|
name: String,
|
||||||
is_mounted: bool,
|
is_mounted: bool,
|
||||||
|
is_remote: bool,
|
||||||
icon_opt: Option<PathBuf>,
|
icon_opt: Option<PathBuf>,
|
||||||
icon_symbolic_opt: Option<PathBuf>,
|
icon_symbolic_opt: Option<PathBuf>,
|
||||||
path_opt: Option<PathBuf>,
|
path_opt: Option<PathBuf>,
|
||||||
|
|
@ -297,6 +310,10 @@ impl Item {
|
||||||
self.is_mounted
|
self.is_mounted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn is_remote(&self) -> bool {
|
||||||
|
self.is_remote
|
||||||
|
}
|
||||||
|
|
||||||
pub fn uri(&self) -> String {
|
pub fn uri(&self) -> String {
|
||||||
self.uri.clone()
|
self.uri.clone()
|
||||||
}
|
}
|
||||||
|
|
@ -406,6 +423,7 @@ impl Gvfs {
|
||||||
let mount_op = mount_op(name.to_string(), event_tx.clone());
|
let mount_op = mount_op(name.to_string(), event_tx.clone());
|
||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
let mounter_item = mounter_item.clone();
|
let mounter_item = mounter_item.clone();
|
||||||
|
let volume_for_callback = volume.clone();
|
||||||
VolumeExt::mount(
|
VolumeExt::mount(
|
||||||
&volume,
|
&volume,
|
||||||
gio::MountMountFlags::NONE,
|
gio::MountMountFlags::NONE,
|
||||||
|
|
@ -413,7 +431,29 @@ impl Gvfs {
|
||||||
gio::Cancellable::NONE,
|
gio::Cancellable::NONE,
|
||||||
move |res| {
|
move |res| {
|
||||||
log::info!("mount {name}: result {res:?}");
|
log::info!("mount {name}: result {res:?}");
|
||||||
event_tx.send(Event::MountResult(mounter_item, match res {
|
// Update the mounter_item with mount information after successful mount
|
||||||
|
let mut updated_item = mounter_item.clone();
|
||||||
|
if res.is_ok() {
|
||||||
|
if let MounterItem::Gvfs(ref mut item) = updated_item {
|
||||||
|
if let Some(mount) = volume_for_callback.get_mount() {
|
||||||
|
let root = MountExt::root(&mount);
|
||||||
|
item.path_opt = root.path();
|
||||||
|
item.is_mounted = true;
|
||||||
|
// Query if remote
|
||||||
|
item.is_remote = root
|
||||||
|
.query_filesystem_info(
|
||||||
|
gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE,
|
||||||
|
gio::Cancellable::NONE,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
.and_then(|info| {
|
||||||
|
Some(info.boolean(gio::FILE_ATTRIBUTE_FILESYSTEM_REMOTE))
|
||||||
|
})
|
||||||
|
.unwrap_or(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
event_tx.send(Event::MountResult(updated_item, match res {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
_ = complete_tx.send(Ok(()));
|
_ = complete_tx.send(Ok(()));
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,14 @@ impl MounterItem {
|
||||||
Self::None => unreachable!(),
|
Self::None => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_remote(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
#[cfg(feature = "gvfs")]
|
||||||
|
Self::Gvfs(item) => item.is_remote(),
|
||||||
|
Self::None => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type MounterItems = Vec<MounterItem>;
|
pub type MounterItems = Vec<MounterItem>;
|
||||||
|
|
|
||||||
31
src/tab.rs
31
src/tab.rs
|
|
@ -540,11 +540,34 @@ pub fn fs_kind(metadata: &Metadata) -> FsKind {
|
||||||
let major = major_str.parse::<libc::c_uint>().ok()?;
|
let major = major_str.parse::<libc::c_uint>().ok()?;
|
||||||
let minor = minor_str.parse::<libc::c_uint>().ok()?;
|
let minor = minor_str.parse::<libc::c_uint>().ok()?;
|
||||||
let dev = libc::makedev(major, minor);
|
let dev = libc::makedev(major, minor);
|
||||||
//TODO: make sure this list is exhaustive
|
// Network and distributed filesystem types
|
||||||
|
// Based on common remote filesystem types found in /proc/mounts
|
||||||
let kind = match mount_info.fs_type.as_str() {
|
let kind = match mount_info.fs_type.as_str() {
|
||||||
"cifs" | "fuse.rclone" | "fuse.sshfs" | "nfs" | "nfs4" | "smb"
|
// SMB/CIFS variants
|
||||||
| "smb2" => FsKind::Remote,
|
"cifs" | "smb" | "smb2" | "smbfs" => FsKind::Remote,
|
||||||
|
|
||||||
|
// NFS variants
|
||||||
|
"nfs" | "nfs4" => FsKind::Remote,
|
||||||
|
|
||||||
|
// FUSE-based remote filesystems
|
||||||
|
"fuse.rclone" | "fuse.sshfs" | "fuse.davfs2" | "fuse.ceph"
|
||||||
|
| "fuse.glusterfs" | "fuse.s3fs" | "fuse.goofys" | "fuse.gcsfuse"
|
||||||
|
| "fuse.afp" | "fuse.afpfs" => FsKind::Remote,
|
||||||
|
|
||||||
|
// Other network protocols
|
||||||
|
"afs" | "coda" | "ncpfs" | "davfs" | "davfs2" | "shfs" => {
|
||||||
|
FsKind::Remote
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cluster/distributed filesystems
|
||||||
|
"ceph" | "glusterfs" | "lustre" | "gfs" | "gfs2" | "ocfs2" => {
|
||||||
|
FsKind::Remote
|
||||||
|
}
|
||||||
|
|
||||||
|
// GVFS (GNOME Virtual File System)
|
||||||
"fuse.gvfsd-fuse" => FsKind::Gvfs,
|
"fuse.gvfsd-fuse" => FsKind::Gvfs,
|
||||||
|
|
||||||
|
// Everything else is local
|
||||||
_ => FsKind::Local,
|
_ => FsKind::Local,
|
||||||
};
|
};
|
||||||
Some((dev, kind))
|
Some((dev, kind))
|
||||||
|
|
@ -622,7 +645,7 @@ fn display_name_for_file(path: &Path, name: &str, get_from_gvfs: bool, is_deskto
|
||||||
);
|
);
|
||||||
} else if get_from_gvfs {
|
} else if get_from_gvfs {
|
||||||
#[cfg(feature = "gvfs")]
|
#[cfg(feature = "gvfs")]
|
||||||
return Item::display_name(glib::filename_display_name(path).as_str())
|
return Item::display_name(glib::filename_display_name(path).as_str());
|
||||||
}
|
}
|
||||||
Item::display_name(name)
|
Item::display_name(name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue