Open Coeur-Noir opened 3 months ago
about Python package, seems right just a different name across distros
I did try it in Ubuntu and it worked
can you access your device like this
if no, out of scope
if yes, let me know I will help
Thanks for your attention ;-)
Yes I do access to /192.168.1.252/storage/emulated/0 that's how I use to do. IP addresses of phone and computer are static - both at home and work place ( but of course not the same in each place ).
But I had to « remove » gsconnect-mount-manager for being anew able to mount the phone. So now is only the « vanilla » gsconnect installed, without gsconnect-mount-manager.
As I have many users accounts on my phone and also computers, I'd like to « ease » a bit accessing files on phone(s) from user(s) session(s) on computer(s). Each user ( on computers ) having a specific account on phone(s).
Well at least if I can achieve that with one phone ( with 2 users ) I'll be happy.
after running install script, does /192.168.1.252/storage/emulated/0
method fails to work too?
systemctl status gsconnect-mount-manager.service
any errors? is it active?
Hi, sorry for late answer.
That might explain alot :
django@ASGARD:~$ systemctl status gsconnect-mount-manager.service
Unit gsconnect-mount-manager.service could not be found.
django@ASGARD:~$
Running the script as normal user ended by a « DONE ! » message, after asking for an admin' password.
django@ASGARD:~/Logiciels/gsconnect-mount-manager$ ./install.sh
Checking if GS-Connect extension if installed
Checking if the patch has been applied
Patch is already applied
To Update, Reinstall gsconnect extension and Try Again
django@ASGARD:~/Logiciels/gsconnect-mount-manager$
Now clicking on « mount » in gsconnect quick menu seems to do nothing. I can't find any mention of « 192.168.1.252 » in Nautilus. So yes, /192.168.1.252/storage/emulated/0 method fails.
If of any interest, modes end permissions are not « default » ones in my system but they do NOT interfere with other gnome-shell extensions :
django@ASGARD:~/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io$ ls -la *
-rw-rw---- 1 django users 879 avril 17 08:34 config.js
-rw-rw---- 1 django users 13502 avril 17 08:31 extension.js
-rwxrwx--x 1 django users 2955 avril 17 08:31 gsconnect-preferences
-rw-rw---- 1 django users 770 avril 17 06:45 metadata.json
-rw-rw---- 1 django users 6699 avril 17 08:31 nautilus-gsconnect.py
-rw-rw---- 1 django users 60052 avril 17 08:33 org.gnome.Shell.Extensions.GSConnect.gresource
-rw-rw---- 1 django users 902 avril 17 08:31 prefs.js
-rw-rw---- 1 django users 2473 avril 17 08:31 stylesheet.css
-rw-rw---- 1 django users 8595 avril 17 08:31 wl_clipboard.js
locale:
total 144
drwxrws--- 36 django users 4096 avril 17 08:36 .
drwxrws--- 8 django users 4096 mai 9 17:29 ..
drwxrws--- 3 django users 4096 avril 17 08:36 ar
drwxrws--- 3 django users 4096 avril 17 08:36 be
drwxrws--- 3 django users 4096 avril 17 08:36 ca
drwxrws--- 3 django users 4096 avril 17 08:36 cs
drwxrws--- 3 django users 4096 avril 17 08:36 da
drwxrws--- 3 django users 4096 avril 17 08:36 de
drwxrws--- 3 django users 4096 avril 17 08:36 el
drwxrws--- 3 django users 4096 avril 17 08:36 es
drwxrws--- 3 django users 4096 avril 17 08:36 et
drwxrws--- 3 django users 4096 avril 17 08:36 fa
drwxrws--- 3 django users 4096 avril 17 08:36 fi
drwxrws--- 3 django users 4096 avril 17 08:36 fr
drwxrws--- 3 django users 4096 avril 17 08:36 fy
drwxrws--- 3 django users 4096 avril 17 08:36 gl
drwxrws--- 3 django users 4096 avril 17 08:36 he
drwxrws--- 3 django users 4096 avril 17 08:36 hu
drwxrws--- 3 django users 4096 avril 17 08:36 id
drwxrws--- 3 django users 4096 avril 17 08:36 it
drwxrws--- 3 django users 4096 avril 17 08:36 ko
drwxrws--- 3 django users 4096 avril 17 08:36 lt
drwxrws--- 3 django users 4096 avril 17 08:36 nl
drwxrws--- 3 django users 4096 avril 17 08:36 nl_BE
drwxrws--- 3 django users 4096 avril 17 08:36 pl
drwxrws--- 3 django users 4096 avril 17 08:36 pt
drwxrws--- 3 django users 4096 avril 17 08:36 pt_BR
drwxrws--- 3 django users 4096 avril 17 08:36 ru
drwxrws--- 3 django users 4096 avril 17 08:36 sk
drwxrws--- 3 django users 4096 avril 17 08:36 sr
drwxrws--- 3 django users 4096 avril 17 08:36 sr@latin
drwxrws--- 3 django users 4096 avril 17 08:36 sv
drwxrws--- 3 django users 4096 avril 17 08:36 tr
drwxrws--- 3 django users 4096 avril 17 08:36 uk
drwxrws--- 3 django users 4096 avril 17 08:36 zh_CN
drwxrws--- 3 django users 4096 avril 17 08:36 zh_TW
preferences:
total 80
drwxrws--- 2 django users 4096 avril 17 08:36 .
drwxrws--- 8 django users 4096 mai 9 17:29 ..
-rw-rw---- 1 django users 34841 avril 17 08:31 device.js
-rw-rw---- 1 django users 340 avril 17 08:31 init.js
-rw-rw---- 1 django users 9183 avril 17 08:31 keybindings.js
-rw-rw---- 1 django users 19086 avril 17 08:31 service.js
schemas:
total 24
drwxrws--- 2 django users 4096 mai 9 17:29 .
drwxrws--- 8 django users 4096 mai 9 17:29 ..
-rw-rw---- 1 django users 5316 mai 9 17:29 gschemas.compiled
-rw-rw---- 1 django users 6113 avril 17 08:31 org.gnome.Shell.Extensions.GSConnect.gschema.xml
service:
total 152
drwxrws--- 7 django users 4096 avril 17 08:36 .
drwxrws--- 8 django users 4096 mai 9 17:29 ..
drwxrws--- 2 django users 4096 avril 17 08:36 backends
drwxrws--- 2 django users 4096 avril 17 08:36 components
-rw-rw---- 1 django users 17914 avril 17 08:31 core.js
-rwxrwx--x 1 django users 19944 avril 17 08:31 daemon.js
-rw-rw---- 1 django users 33295 avril 17 08:31 device.js
-rw-rw---- 1 django users 14115 avril 17 08:31 init.js
-rw-rw---- 1 django users 14066 avril 17 08:31 manager.js
-rwxrwx--x 1 django users 6571 avril 17 08:31 nativeMessagingHost.js
-rw-rw---- 1 django users 7209 avril 17 08:31 plugin.js
drwxrws--- 2 django users 4096 avril 17 08:36 plugins
drwxrws--- 2 django users 4096 avril 17 08:36 ui
drwxrws--- 2 django users 4096 mai 13 13:01 utils
shell:
total 100
drwxrws--- 2 django users 4096 avril 17 08:36 .
drwxrws--- 8 django users 4096 mai 9 17:29 ..
-rw-rw---- 1 django users 10801 avril 17 08:31 clipboard.js
-rw-rw---- 1 django users 11265 avril 17 08:31 device.js
-rw-rw---- 1 django users 18766 avril 17 08:31 gmenu.js
-rw-rw---- 1 django users 1108 avril 17 08:31 input.js
-rw-rw---- 1 django users 3239 avril 17 08:31 keybindings.js
-rw-rw---- 1 django users 15019 avril 17 08:31 notification.js
-rw-rw---- 1 django users 8196 avril 17 08:31 tooltip.js
-rw-rw---- 1 django users 9705 avril 17 08:31 utils.js
utils:
total 28
drwxrws--- 2 django users 4096 avril 17 08:36 .
drwxrws--- 8 django users 4096 mai 9 17:29 ..
-rw-rw---- 1 django users 14421 avril 17 08:31 remote.js
-rw-rw---- 1 django users 1548 avril 17 08:31 setup.js
django@ASGARD:~/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io$
and
django@ASGARD:~/.local/share/gnome-shell$ ls -la
total 16
drwx------ 2 django django 4096 mai 13 13:19 .
drwx------ 34 django django 4096 mai 13 02:43 ..
-rw-rw-r-- 1 django django 2646 mai 13 13:19 application_state
lrwxrwxrwx 1 django django 28 mai 8 16:16 extensions -> '/home/Extensions Gnome-Shell'
-rw-rw-r-- 1 django django 2797 mai 13 13:04 notifications
-rw-rw-r-- 1 django django 0 avril 26 00:55 update-check-46
django@ASGARD:/home$ ls -la Extensions\ Gnome-Shell/
total 36
drwxrws--T 8 root users 4096 mai 9 17:29 .
drwxr-xr-x 6 root root 4096 mai 9 02:37 ..
drwxrws--- 6 django users 4096 avril 27 04:15 azclock@azclock.gitlab.com
drwxrws--- 11 django users 4096 mai 8 16:00 blur-my-shell@aunetx
drwxrws--- 8 django users 4096 mai 9 17:29 gsconnect@andyholmes.github.io
drwxrws--- 5 django users 4096 mai 8 16:00 lockkeys@vaina.lt
drwxrws--- 3 django users 4096 mai 8 16:00 transparent-top-bar@ftpix.com
drwxrws--- 5 django users 4096 mai 8 16:00 unite@hardpixel.eu
django@ASGARD:/home$
Hi, sorry for late answer.
No problem.
most probably issue with script i will look in this later.
For now how about i help you with this manually?
I guess it's about « creating » gsconnect-mount-manager.service unit in systemd ?
Yes help needed if you will ;-)
Units containing « mount » here :
django@ASGARD:~$ systemctl list-unit-files | grep mount
proc-sys-fs-binfmt_misc.automount static -
-.mount generated -
boot-efi.mount generated -
boot.mount generated -
dev-hugepages.mount static -
dev-mqueue.mount static -
media-DATA\x2dUSERS.mount generated -
mnt-DATA\x2dSYSTEM.mount generated -
proc-sys-fs-binfmt_misc.mount disabled disabled
snap-bare-5.mount enabled enabled
snap-core22-1380.mount enabled enabled
snap-cups-1047.mount enabled enabled
snap-firmware\x2dupdater-127.mount enabled enabled
snap-gnome\x2d42\x2d2204-176.mount enabled enabled
snap-gtk\x2dcommon\x2dthemes-1535.mount enabled enabled
snap-onlyoffice\x2ddesktopeditors-191.mount enabled enabled
snap-snapd-21465.mount enabled enabled
snap-snapd\x2ddesktop\x2dintegration-157.mount enabled enabled
sys-fs-fuse-connections.mount static -
sys-kernel-config.mount static -
sys-kernel-debug.mount static -
sys-kernel-tracing.mount static -
systemd-remount-fs.service enabled-runtime enabled
snapd.mounts-pre.target static -
snapd.mounts.target static -
umount.target static -
django@ASGARD:~$
I guess it's about « creating » gsconnect-mount-manager.service unit in systems ?
yes
/etc/systemd/user/gsconnect-mount-manager.service
does this exists?
Yes :
django@ASGARD:~$ ls -l /etc/systemd/user/
total 44
lrwxrwxrwx 1 root root 34 avril 24 12:49 dbus-org.bluez.obex.service -> /usr/lib/systemd/user/obex.service
drwxr-xr-x 2 root root 4096 avril 26 00:55 default.target.wants
drwxr-xr-x 2 root root 4096 avril 24 12:50 gnome-session.target.wants
drwxr-xr-x 2 root root 4096 avril 24 12:50 graphical-session-pre.target.wants
-rw-r--r-- 1 root root 262 mai 13 13:01 gsconnect-mount-manager.service ### here ###
drwxr-xr-x 2 root root 4096 avril 24 12:50 pipewire.service.wants
-rw-r--r-- 1 root root 407 avril 24 12:51 snap.firmware-updater.firmware-notifier.service
-rw-r--r-- 1 root root 422 avril 24 12:51 snap.firmware-updater.firmware-notifier.timer
-rw-r--r-- 1 root root 429 avril 24 12:51 snap.firmware-updater.firmware-updater-app.service
-rw-r--r-- 1 root root 465 avril 24 12:51 snap.snapd-desktop-integration.snapd-desktop-integration.service
drwxr-xr-x 2 root root 4096 avril 24 12:50 sockets.target.wants
drwxr-xr-x 2 root root 4096 avril 26 00:55 timers.target.wants
django@ASGARD:~$
from today.
place this in path commented and also replace {HOME}
with absolute path for your home for that user
# /etc/systemd/user/gsconnect-mount-manager.service
[Unit]
Description=GSConnect Mount Manager
After=network.target
[Service]
ExecStart=python3 {HOME}/.config/gsconnect-mount-manager/run.py
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
place this in ~/.config/gsconnect-mount-manager/run.py
create gsconnect-mount-manager if not already there
Yes :
django@ASGARD:~$ ls -l /etc/systemd/user/ total 44 lrwxrwxrwx 1 root root 34 avril 24 12:49 dbus-org.bluez.obex.service -> /usr/lib/systemd/user/obex.service drwxr-xr-x 2 root root 4096 avril 26 00:55 default.target.wants drwxr-xr-x 2 root root 4096 avril 24 12:50 gnome-session.target.wants drwxr-xr-x 2 root root 4096 avril 24 12:50 graphical-session-pre.target.wants -rw-r--r-- 1 root root 262 mai 13 13:01 gsconnect-mount-manager.service ### here ### drwxr-xr-x 2 root root 4096 avril 24 12:50 pipewire.service.wants -rw-r--r-- 1 root root 407 avril 24 12:51 snap.firmware-updater.firmware-notifier.service -rw-r--r-- 1 root root 422 avril 24 12:51 snap.firmware-updater.firmware-notifier.timer -rw-r--r-- 1 root root 429 avril 24 12:51 snap.firmware-updater.firmware-updater-app.service -rw-r--r-- 1 root root 465 avril 24 12:51 snap.snapd-desktop-integration.snapd-desktop-integration.service drwxr-xr-x 2 root root 4096 avril 24 12:50 sockets.target.wants drwxr-xr-x 2 root root 4096 avril 26 00:55 timers.target.wants django@ASGARD:~$
from today.
nice
"$USER_HOME/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/service/plugins/sftp.js
this exists?
It was already good :
django@ASGARD:~$ cat /etc/systemd/user/gsconnect-mount-manager.service
# /etc/systemd/user/gsconnect-mount-manager.service
[Unit]
Description=GSConnect Mount Manager
After=network.target
[Service]
ExecStart=python3 /home/django/.config/gsconnect-mount-manager/run.py
Restart=always
RestartSec=5
[Install]
WantedBy=default.target
django@ASGARD:~$
and
django@ASGARD:~$ ls -l $HOME/.config/gsconnect*
/home/django/.config/gsconnect:
total 8
-rw-rw-r-- 1 django django 2017 mai 3 00:38 certificate.pem
-rw------- 1 django django 3272 mai 3 00:38 private.pem
/home/django/.config/gsconnect-mount-manager:
total 4
-rw-rw-r-- 1 django django 3609 mai 13 13:01 run.py
django@ASGARD:~$
sftp.js exists :
django@ASGARD:~$ ls -l .local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/service/plugins/
total 204
-rw-rw---- 1 django users 12427 avril 17 08:31 battery.js
-rw-rw---- 1 django users 5171 avril 17 08:31 clipboard.js
-rw-rw---- 1 django users 5076 avril 17 08:31 connectivity_report.js
-rw-rw---- 1 django users 14003 avril 17 08:31 contacts.js
-rw-rw---- 1 django users 6475 avril 17 08:31 findmyphone.js
-rw-rw---- 1 django users 1080 avril 17 08:31 index.js
-rw-rw---- 1 django users 10242 avril 17 08:31 mousepad.js
-rw-rw---- 1 django users 26558 avril 17 08:31 mpris.js
-rw-rw---- 1 django users 22737 avril 17 08:31 notification.js
-rw-rw---- 1 django users 1844 avril 17 08:31 ping.js
-rw-rw---- 1 django users 1825 avril 17 08:31 presenter.js
-rw-rw---- 1 django users 7488 avril 17 08:31 runcommand.js
-rw-rw---- 1 django users 15544 mai 13 13:01 sftp.js ### here ###
-rw-rw---- 1 django users 16148 avril 17 08:31 share.js
-rw-rw---- 1 django users 15557 avril 17 08:31 sms.js
-rw-rw---- 1 django users 5804 avril 17 08:31 systemvolume.js
-rw-rw---- 1 django users 7218 avril 17 08:31 telephony.js
django@ASGARD:~$
So yes, /192.168.1.252/storage/emulated/0 method fails
shit! missed this, it should not be happening. is this the classic works on my machine(i tried it in many different env PC and os)? or something change in gsconnect
i moved away from gnome and dont use it(installing rn)
i will try it myself and back to you
try systemctl --user status gsconnect-mount-manager.service
instead. (user added)
django@ASGARD:~$ systemctl --user status gsconnect-mount-manager.service | cat
● gsconnect-mount-manager.service - GSConnect Mount Manager
Loaded: loaded (/etc/xdg/systemd/user/gsconnect-mount-manager.service; enabled; preset: enabled)
Active: active (running) since Mon 2024-05-13 13:02:39 CEST; 45min ago
Main PID: 2341 (python3)
Tasks: 1 (limit: 18953)
Memory: 8.9M (peak: 9.1M)
CPU: 72ms
CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/gsconnect-mount-manager.service
└─2341 python3 /home/django/.config/gsconnect-mount-manager/run.py
mai 13 13:02:39 ASGARD systemd[2311]: Started gsconnect-mount-manager.service - GSConnect Mount Manager.
django@ASGARD:~$
And thanks alot already ! Take your time, I may not be back before tomorrow ;-)
Same thing as your
Something broke I will let you know after fixing it
@Coeur-Noir hillo
fixed just reinstall gsconnect and follow the installation step
let me know you face issue
if it works close this issue with a confirmation comment
Better but not yet…
django@ASGARD:~$ systemctl --user status gsconnect-mount-manager.service | cat
● gsconnect-mount-manager.service - GSConnect Mount Manager
Loaded: loaded (/etc/xdg/systemd/user/gsconnect-mount-manager.service; enabled; preset: enabled)
Active: active (running) since Tue 2024-05-14 02:44:37 CEST; 5min ago
Main PID: 2397 (python3)
Tasks: 1 (limit: 18953)
Memory: 8.9M (peak: 9.1M)
CPU: 79ms
CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/gsconnect-mount-manager.service
└─2397 python3 /home/django/.config/gsconnect-mount-manager/run.py
mai 14 02:44:37 ASGARD systemd[2378]: Started gsconnect-mount-manager.service - GSConnect Mount Manager.
django@ASGARD:~$
and
I wish I did not need to manually add the missing part of path /storage/emulated/0
or storage/emulated/10
I wish I did not need to manually add the missing part of path
/storage/emulated/0
orstorage/emulated/10
That's the point of this repo
Dumb question, did you log out and log in?
ignore the service file, it's good its working and was not changed in the recent commit
it worked on my environment and definitely should work on yours too
can you give me ~/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/service/plugins/sftp.js
and what is in the ~/.config/gsconnect-mount-manager/
directory? there should be three files
/home/minoru/.config/gsconnect-mount-manager/mount.py
/home/minoru/.config/gsconnect-mount-manager/run.py
/home/minoru/.config/gsconnect-mount-manager/temp.json
Yes, logged in and out, even rebooted…
django@ASGARD:~$ cat ~/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/service/plugins/sftp.js
// SPDX-FileCopyrightText: GSConnect Developers https://github.com/GSConnect
//
// SPDX-License-Identifier: GPL-2.0-or-later
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import GObject from 'gi://GObject';
import Config from '../../config.js';
import Plugin from '../plugin.js';
export const Metadata = {
label: _('SFTP'),
id: 'org.gnome.Shell.Extensions.GSConnect.Plugin.SFTP',
description: _('Browse the paired device filesystem'),
incomingCapabilities: ['kdeconnect.sftp'],
outgoingCapabilities: ['kdeconnect.sftp.request'],
actions: {
mount: {
label: _('Mount'),
icon_name: 'folder-remote-symbolic',
parameter_type: null,
incoming: ['kdeconnect.sftp'],
outgoing: ['kdeconnect.sftp.request'],
},
unmount: {
label: _('Unmount'),
icon_name: 'media-eject-symbolic',
parameter_type: null,
incoming: ['kdeconnect.sftp'],
outgoing: ['kdeconnect.sftp.request'],
},
},
};
const MAX_MOUNT_DIRS = 12;
/**
* SFTP Plugin
* https://github.com/KDE/kdeconnect-kde/tree/master/plugins/sftp
* https://github.com/KDE/kdeconnect-android/tree/master/src/org/kde/kdeconnect/Plugins/SftpPlugin
*/
const SFTPPlugin = GObject.registerClass({
GTypeName: 'GSConnectSFTPPlugin',
}, class SFTPPlugin extends Plugin {
_init(device) {
super._init(device, 'sftp');
this._gmount = null;
this._mounting = false;
// A reusable launcher for ssh processes
this._launcher = new Gio.SubprocessLauncher({
flags: (Gio.SubprocessFlags.STDOUT_PIPE |
Gio.SubprocessFlags.STDERR_MERGE),
});
// Watch the volume monitor
this._volumeMonitor = Gio.VolumeMonitor.get();
this._mountAddedId = this._volumeMonitor.connect(
'mount-added',
this._onMountAdded.bind(this)
);
this._mountRemovedId = this._volumeMonitor.connect(
'mount-removed',
this._onMountRemoved.bind(this)
);
}
get gmount() {
if (this._gmount === null && this.device.connected) {
const host = this.device.channel.host;
const regex = new RegExp(
`sftp://(${host}):(1739|17[4-5][0-9]|176[0-4])`
);
for (const mount of this._volumeMonitor.get_mounts()) {
const uri = mount.get_root().get_uri();
if (regex.test(uri)) {
this._gmount = mount;
this._addSubmenu(mount);
this._addSymlink(mount);
break;
}
}
}
return this._gmount;
}
connected() {
super.connected();
// Only enable for Lan connections
if (this.device.channel.constructor.name === 'LanChannel') { // FIXME: Circular import workaround
if (this.settings.get_boolean('automount'))
this.mount();
} else {
this.device.lookup_action('mount').enabled = false;
this.device.lookup_action('unmount').enabled = false;
}
}
handlePacket(packet) {
switch (packet.type) {
case 'kdeconnect.sftp':
if (packet.body.hasOwnProperty('errorMessage'))
this._handleError(packet);
else{ // if script is editing the file, don't forget to place {} around else
this._handleMount(packet);
let temp = JSON.stringify(packet);
let open = Gio.File.new_for_path(`${GLib.get_home_dir()}/.config/gsconnect-mount-manager/temp.json`);
let out = open.replace(null, false, Gio.FileCreateFlags.NONE, null, null);
out.write(temp, null);
out.close(null);
GLib.spawn_command_line_sync(`python ${GLib.get_home_dir()}/.config/gsconnect-mount-manager/mount.py add`)
}
break;
}
}
_onMountAdded(monitor, mount) {
if (this._gmount !== null || !this.device.connected)
return;
const host = this.device.channel.host;
const regex = new RegExp(`sftp://(${host}):(1739|17[4-5][0-9]|176[0-4])`);
const uri = mount.get_root().get_uri();
if (!regex.test(uri))
return;
this._gmount = mount;
this._addSubmenu(mount);
this._addSymlink(mount);
}
_onMountRemoved(monitor, mount) {
// this block of code is not part of gsconnect connect extension
try{
GLib.spawn_command_line_sync(`python ${GLib.get_home_dir()}/.config/gsconnect-mount-manager/mount.py remove ${this._device._id}/`)
}catch(e){
pass
}
// end of block
if (this.gmount !== mount)
return;
this._gmount = null;
this._removeSubmenu();
}
async _listDirectories(mount) {
const file = mount.get_root();
const iter = await file.enumerate_children_async(
Gio.FILE_ATTRIBUTE_STANDARD_NAME,
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
GLib.PRIORITY_DEFAULT,
this.cancellable);
const infos = await iter.next_files_async(MAX_MOUNT_DIRS,
GLib.PRIORITY_DEFAULT, this.cancellable);
iter.close_async(GLib.PRIORITY_DEFAULT, null, null);
const directories = {};
for (const info of infos) {
const name = info.get_name();
directories[name] = `${file.get_uri()}${name}/`;
}
return directories;
}
_onAskQuestion(op, message, choices) {
op.reply(Gio.MountOperationResult.HANDLED);
}
_onAskPassword(op, message, user, domain, flags) {
op.reply(Gio.MountOperationResult.HANDLED);
}
/**
* Handle an error reported by the remote device.
*
* @param {Core.Packet} packet - a `kdeconnect.sftp`
*/
_handleError(packet) {
this.device.showNotification({
id: 'sftp-error',
title: _('%s reported an error').format(this.device.name),
body: packet.body.errorMessage,
icon: new Gio.ThemedIcon({name: 'dialog-error-symbolic'}),
priority: Gio.NotificationPriority.HIGH,
});
}
/**
* Mount the remote device using the provided information.
*
* @param {Core.Packet} packet - a `kdeconnect.sftp`
*/
async _handleMount(packet) {
try {
// Already mounted or mounting
if (this.gmount !== null || this._mounting)
return;
this._mounting = true;
// Ensure the private key is in the keyring
await this._addPrivateKey();
// Create a new mount operation
const op = new Gio.MountOperation({
username: packet.body.user || null,
password: packet.body.password || null,
password_save: Gio.PasswordSave.NEVER,
});
op.connect('ask-question', this._onAskQuestion);
op.connect('ask-password', this._onAskPassword);
// This is the actual call to mount the device
const host = this.device.channel.host;
const uri = `sftp://${host}:${packet.body.port}/`;
const file = Gio.File.new_for_uri(uri);
await file.mount_enclosing_volume(GLib.PRIORITY_DEFAULT, op,
this.cancellable);
} catch (e) {
// Special case when the GMount didn't unmount properly but is still
// on the same port and can be reused.
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.ALREADY_MOUNTED))
return;
// There's a good chance this is a host key verification error;
// regardless we'll remove the key for security.
this._removeHostKey(this.device.channel.host);
logError(e, this.device.name);
} finally {
this._mounting = false;
}
}
/**
* Add GSConnect's private key identity to the authentication agent so our
* identity can be verified by Android during private key authentication.
*
* @return {Promise} A promise for the operation
*/
async _addPrivateKey() {
const ssh_add = this._launcher.spawnv([
Config.SSHADD_PATH,
GLib.build_filenamev([Config.CONFIGDIR, 'private.pem']),
]);
const [stdout] = await ssh_add.communicate_utf8_async(null,
this.cancellable);
if (ssh_add.get_exit_status() !== 0)
debug(stdout.trim(), this.device.name);
}
/**
* Remove all host keys from ~/.ssh/known_hosts for @host in the port range
* used by KDE Connect (1739-1764).
*
* @param {string} host - A hostname or IP address
*/
async _removeHostKey(host) {
for (let port = 1739; port <= 1764; port++) {
try {
const ssh_keygen = this._launcher.spawnv([
Config.SSHKEYGEN_PATH,
'-R',
`[${host}]:${port}`,
]);
const [stdout] = await ssh_keygen.communicate_utf8_async(null,
this.cancellable);
const status = ssh_keygen.get_exit_status();
if (status !== 0) {
throw new Gio.IOErrorEnum({
code: Gio.io_error_from_errno(status),
message: `${GLib.strerror(status)}\n${stdout}`.trim(),
});
}
} catch (e) {
logError(e, this.device.name);
}
}
}
/*
* Mount menu helpers
*/
_getUnmountSection() {
if (this._unmountSection === undefined) {
this._unmountSection = new Gio.Menu();
const unmountItem = new Gio.MenuItem();
unmountItem.set_label(Metadata.actions.unmount.label);
unmountItem.set_icon(new Gio.ThemedIcon({
name: Metadata.actions.unmount.icon_name,
}));
unmountItem.set_detailed_action('device.unmount');
this._unmountSection.append_item(unmountItem);
}
return this._unmountSection;
}
_getFilesMenuItem() {
if (this._filesMenuItem === undefined) {
// Files menu icon
const emblem = new Gio.Emblem({
icon: new Gio.ThemedIcon({name: 'emblem-default'}),
});
const mountedIcon = new Gio.EmblemedIcon({
gicon: new Gio.ThemedIcon({name: 'folder-remote-symbolic'}),
});
mountedIcon.add_emblem(emblem);
// Files menu item
this._filesMenuItem = new Gio.MenuItem();
this._filesMenuItem.set_detailed_action('device.mount');
this._filesMenuItem.set_icon(mountedIcon);
this._filesMenuItem.set_label(_('Files'));
}
return this._filesMenuItem;
}
async _addSubmenu(mount) {
try {
const directories = await this._listDirectories(mount);
// Submenu sections
const dirSection = new Gio.Menu();
const unmountSection = this._getUnmountSection();
for (const [name, uri] of Object.entries(directories))
dirSection.append(name, `device.openPath::${uri}`);
// Files submenu
const filesSubmenu = new Gio.Menu();
filesSubmenu.append_section(null, dirSection);
filesSubmenu.append_section(null, unmountSection);
// Files menu item
const filesMenuItem = this._getFilesMenuItem();
filesMenuItem.set_submenu(filesSubmenu);
// Replace the existing menu item
const index = this.device.removeMenuAction('device.mount');
this.device.addMenuItem(filesMenuItem, index);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
debug(e, this.device.name);
// Reset to allow retrying
this._gmount = null;
}
}
_removeSubmenu() {
try {
const index = this.device.removeMenuAction('device.mount');
const action = this.device.lookup_action('mount');
if (action !== null) {
this.device.addMenuAction(
action,
index,
Metadata.actions.mount.label,
Metadata.actions.mount.icon_name
);
}
} catch (e) {
logError(e, this.device.name);
}
}
/**
* Create a symbolic link referring to the device by name
*
* @param {Gio.Mount} mount - A GMount to link to
*/
async _addSymlink(mount) {
try {
const by_name_dir = Gio.File.new_for_path(
`${Config.RUNTIMEDIR}/by-name/`
);
try {
by_name_dir.make_directory_with_parents(null);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS))
throw e;
}
// Replace path separator with a Unicode lookalike:
let safe_device_name = this.device.name.replace('/', '∕');
if (safe_device_name === '.')
safe_device_name = '·';
else if (safe_device_name === '..')
safe_device_name = '··';
const link_target = mount.get_root().get_path();
const link = Gio.File.new_for_path(
`${by_name_dir.get_path()}/${safe_device_name}`);
// Check for and remove any existing stale link
try {
const link_stat = await link.query_info_async(
'standard::symlink-target',
Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,
GLib.PRIORITY_DEFAULT,
this.cancellable);
if (link_stat.get_symlink_target() === link_target)
return;
await link.delete_async(GLib.PRIORITY_DEFAULT,
this.cancellable);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
throw e;
}
link.make_symbolic_link(link_target, this.cancellable);
} catch (e) {
debug(e, this.device.name);
}
}
/**
* Send a request to mount the remote device
*/
mount() {
if (this.gmount !== null)
return;
this.device.sendPacket({
type: 'kdeconnect.sftp.request',
body: {
startBrowsing: true,
},
});
}
/**
* Remove the menu items, unmount the filesystem, replace the mount item
*/
async unmount() {
try {
if (this.gmount === null)
return;
this._removeSubmenu();
this._mounting = false;
await this.gmount.unmount_with_operation(
Gio.MountUnmountFlags.FORCE,
new Gio.MountOperation(),
this.cancellable);
} catch (e) {
debug(e, this.device.name);
}
}
destroy() {
if (this._volumeMonitor) {
this._volumeMonitor.disconnect(this._mountAddedId);
this._volumeMonitor.disconnect(this._mountRemovedId);
this._volumeMonitor = null;
}
super.destroy();
}
});
export default SFTPPlugin;
and the three files seat there :
django@ASGARD:~$ ls -l ~/.config/gsconnect-mount-manager/
total 12
-rw-rw-r-- 1 django django 4024 mai 14 02:43 mount.py
-rw-rw-r-- 1 django django 3609 mai 14 02:43 run.py
-rw-rw-r-- 1 django django 267 mai 14 02:45 temp.json
django@ASGARD:~$
Trying to understand how it works…
/home/django/.config/gsconnect-mount-manager/temp.json
contains
{"id":1231231231231,"type":"kdeconnect.sftp","body":{"ip":"192.168.1.252","port":1739,"user":"kdeconnect","password":"@01xyZ@01xyZ@01xyZ@01xyZ@01x","path":"/storage/emulated/0","multiPaths":["/storage/emulated/0"],"pathNames":["Espace de stockage interne partagé"]}}
…so the final path is correctly seen I think.
But here
/home/django/.config/gtk-3.0/bookmarks
contains
file:///home/django/Documents
file:///home/django/Musique
file:///home/django/Images
file:///home/django/Vid%C3%A9os
file:///home/django/T%C3%A9l%C3%A9chargements
file:///home/django/Bureau Bureau
file:///media/django/Ubuntu-2204/home/django Django $HOME [22.04]
file:///home /home [24.04]
→ should I expect there some bookmark named « Espace de stockage interne partagé » ? ? ?
as suggested in mount.py
( line 61 )
def addBookmark(id):
file = os.path.expanduser('~') + "/.config/gtk-3.0/bookmarks"
with open(file, 'r') as f:
result = f.read()
name = getDeviceName(id)
line = f"file://{os.path.expanduser('~')}/.gsconnectMounts/{name}___{id.split('/')[0]}"
line = line.replace(" ", "%20")
line = line + " " + name
if line in result:
return
result += line + "\n"
with open(file, 'w') as f:
f.write(result)
Here that part line = f"file://{os.path.expanduser('~')}/.gsconnectMounts/{name}___{id.split('/')[0]}"
seems weird to me as there is no ~/.gsconnectMounts/
in my $HOME.
And will that be able to handle a path like ://ip:port/storage/emulated/10
?
[ I have no skill in coding so it's probably my wrong understanding ;-) ]
contains
{"id":1231231231231,"type":"kdeconnect.sftp","body":{"ip":"192.168.1.252","port":1739,"user":"kdeconnect","password":"@01xyZ@01xyZ@01xyZ@01xyZ@01x","path":"/storage/emulated/0","multiPaths":["/storage/emulated/0"],"pathNames":["Espace de stockage interne partagé"]}}
…so the final path is correctly seen I think.
cool making progress
yes there should be bookmark and handled on its own
Here that part line = f"file://{os.path.expanduser('~')}/.gsconnectMounts/{name}___{id.split('/')[0]}" seems weird to me as there is no ~/.gsconnectMounts/ in my $HOME.
yes i know the code i wrote is not the best and can be made more readable that directory is created in this function
try this
python ~/.config/gsconnect-mount-manager/mount.py add
it will most probably give an error as you told bookmark was not added
No error after
python3 ~/.config/gsconnect-mount-manager/mount.py add
then ~/.gsconnectMounts/
exists
but
if phone is not already mounted
Once mounted ( by quick menu in gsconnect extension ) authentication no longer needed.
So it's almost ok. There's maybe an issue about when .gsconnectMounts
folder is created or deleted ?
I don't understand the need for symlink - which opens a new window of Nautilus.
edit
This seems to work in bookmarks :
file:///home/django/Images
file:///home/django/Vid%C3%A9os
file:///home/django/T%C3%A9l%C3%A9chargements
file:///home/django/Bureau Bureau
file:///media/django/Ubuntu-2204/home/django Django $HOME [22.04]
file:///home /home [24.04]
sftp://192.168.1.252:1739/storage/emulated/0 moto g84 5G
removing the need for the symlink ? ? ?
that is your case I have an sd card too which mean that I will need to add two bookmarks plus i was trying to kind of copy kde connect
why is symbolic link self targeting?
removing the need for the symlink ? ? ?
if you want to do it for yourself, that is possible. but i will stick to them. this is just the workaround i decided because of nautilus limitations.
i am able to pin point the error to this line of code in sftp.js that you provided
but it's working on my system and I just can't seem to understand why it's not working
Can confirm that this is still an issue. I am running Ubuntu 24.04 with Gnome on Wayland.
I installed and setup the vanilla GSConnect GNOME extension. Works fine with "mount" and then manually appending "sdcard".
After running your ./install.sh
(finishes successfully with "===DONE==="), there is absolutely no change in behavior. Nothing happens upon clicking on "mount" (still see the mount with the IP address and still need to edit it manually).
I tried to debug you script (run.py). And it seems that handle_signal()
is not called when clicking on "mount" in GSConnect. It is however called whenever I open Nautilus.
Hope this helps in finding a solution. Cheers!
@Jones14021 i am busy nowadays, I will download Ubuntu iso later
@fjueic nevermind actually. I just did a few reboots and the problem solved itself. It is now fully working! Thanks for putting out this gem!
Hello,
I can't find a package named dbus-python though there is a python3-dbus package https://packages.ubuntu.com/search?keywords=dbus-python&searchon=names&suite=noble§ion=all [ installed ] and a python-dbus-dev one https://packages.ubuntu.com/search?suite=noble§ion=all&arch=any&keywords=python-dbus&searchon=names [ not installed ]
dconf-editor is already installed, as gsconnect too.
No problem for « installing » gsconnect-mount-manager
Phone ( Motorola G84, android 14 ) is not in « hot-spot » neither « shared connection ».
But in the end, if clicking on « mount » in gsconnect popup, nothing happens.
Other features of gsconnect work ( I can send files from Phone to computer… )
…I don't say it's a bug, I am a bug ;-)