├── README.md
├── icons
├── back.png
├── browse.png
├── cloud-square.png
├── cloud.png
├── cloud.svg
├── download.png
├── dropbox-24.png
├── dropbox.png
├── eject.png
├── folder.png
├── gdrive-24.png
├── gdrive.png
├── link.png
├── mount.png
├── move.png
├── new.png
├── none-24.png
├── onedrive-24.png
├── onedrive.png
├── other-24.png
├── remove.png
├── stop.png
├── trash.png
├── upload.png
└── webinterface.png
├── main.sh
└── terminal-run
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 | # CloudBuddy
4 | Bash-based GUI client for [`rclone`](https://rclone.org/). This was written by Botspot on 7/29/2021.
5 | 
6 |
7 | ## Installation:
8 | [](https://github.com/Botspot/pi-apps)
9 | ### Or, to download manually:
10 | ```
11 | git clone https://github.com/Botspot/cloudbuddy
12 | ```
13 | CloudBuddy is portable and can be executed from anywhere on your filesystem. For simplicity, this README will assume CloudBuddy is located in your `$HOME` directory.
14 | ## First run:
15 | ```
16 | ~/cloudbuddy/main.sh
17 | ```
18 | When running for the first time, CloudBuddy will:
19 | - Check and install **dependencies**: `yad`, `xclip`, `rclone`. Note: If `rclone` is already installed with **`apt`**, CloudBuddy will overwrite it with the latest version of [`rclone` from source](https://rclone.org/downloads/).
20 | - Add a **Main menu launcher**. (`~/.local/share/applications/cloudbuddy.desktop`) This launcher is located under the **Internet** category.
21 | - Make sure the "**`fuse`**" kernel module has been loaded so that `rclone` can mount drives. If fuse is not loaded, CloudBuddy will attempt to load it by running `sudo modprobe fuse`. If that fails, CloudBuddy's mounting capabilities will be disabled and hidden.
22 | - **Check for updates**. If the last local commit and the latest online commit do not match, CloudBuddy will run `git pull` and refresh the script if it was modified. Note: If you make a fork of this repository, you should change the github URL in the script to point to your repository. To disable update-checking, create a file at: `~/cloudbuddy/no-update`.
23 |
24 | ## Usage
25 | Note: CloudBuddy is intended to be **self-explanatory**. Everything should proceed in a logical fashon and nearly all users will have no problem using this program.
26 | But for those few users who wish to get acquainted with CloudBuddy before they try it out, **here you go.**
27 | ## Connecting to a new cloud drive
28 | This automates the [`rclone config`](https://rclone.org/commands/rclone_config/) process. Feel free to manually run `rclone config` for more options and a wider selection of cloud storage providers.
29 | 1. In the main window, click this button:
30 | 
31 | 2. Choose a name for the new cloud drive, and select which type of drive it is.
32 | 
33 | 3. Within seconds, a web browser will appear for authorization.
34 | 
35 | 4. That's it!
36 | ## Disconnecting from a cloud drive
37 | 1. In the main window, click this button:
38 | 
39 | 2. Choose a cloud drive to sign out of.
40 | 
41 | 3. That's it! First, this uses [`rclone config disconnect`](https://rclone.org/commands/rclone_config_disconnect/) to ask the cloud to revoke rclone's API key, then deletes the cloud drive from `rclone`'s config file using [`rclone config delete`](https://rclone.org/commands/rclone_config_delete/).
42 | ## Mounting a drive
43 | This uses [rclone's `mount` command](https://rclone.org/commands/rclone_mount/) to view your cloud drive as if it was an external data disk.
44 | 1. In the main window, click this button:
45 | 
46 | 2. Select a drive to continue.
47 | 
48 | 3. Choose an empty directory to mount the cloud drive to.
49 | 
50 | 4. Wait for it...
51 | 
52 | 5. Done!
53 | ## Unmounting a drive
54 | 1. In the main window, click this button:
55 | 
56 | 2. Choose a drive to be unmounted.
57 | 
58 | 3. Done!
59 | ## Browsing a drive
60 | 1. In the main window, click this button:
61 | 
62 | 2. Select a drive to continue.
63 | 
64 | 3. This is CloudBuddy's built-in file browser. It exclusively uses rclone commands for navigation and all operations are performed server-side.
65 | 
66 | The file browser is capable of:
67 | - **Uploading** files or folders with this handy drag-n-drop window:
68 | 
69 | - **Downloading** the selected file(s) or folder(s) from the list to $HOME/Downloads. This takes advantage of [`rclone copy`](https://rclone.org/commands/rclone_copy/). Just look at that pretty terminal output!
70 | 
71 | - **Moving** or **Renaming** an item. This uses [the `rclone moveto` command](https://rclone.org/commands/rclone_moveto/) - it makes an API call to the cloud drive to perform the operation so no large downloads or uploads have to occur.
72 | 
73 | - **Deleting** the selected file(s) or folder(s) from the cloud drive. This uses [`rclone purge`](https://rclone.org/commands/rclone_purge/) or [`rclone deletefile`](https://rclone.org/commands/rclone_deletefile/) commands. Keep in mind that most cloud drives will keep deleted items in a recovery folder for a while.
74 | - **Creating a publically sharable link** to the selected file or folder.
75 | 
76 | Notice the above **Copy** button. Because it uses `xclip` to copy the URL, CloudBuddy treats `xclip` as a required dependency.
77 | ## Web Interface
78 | This simple button runs [rclone's built-in browser interface](https://rclone.org/gui/).
79 | 1. In the main window, click this button:
80 | 
81 | 2. A web browser should open with rclone's HTML-based interface.
82 | Fun fact: I (Botspot, the maker of CloudBuddy) was not aware rclone had this feature until *after* CloudBuddy was mostly complete.
83 |
84 | And with that, the GUI usage tutorial comes to an end. If CloudBuddy was designed correctly, you didn't have to read this to use CloudBuddy.
85 |
86 | ## Command-line options
87 | CloudBuddy is run by a single bash ***megascript***. It's not that long a script though, so why do I call it called a *mega*script?
88 | Because CloudBuddy's `main.sh` script is actually **many bash scripts in one**.
89 | When you launch CloudBuddy, it will start at the top of the script, run [a little preliminary stuff](https://github.com/Botspot/cloudbuddy#first-run) like update-checking, then ***skip*** nearly all the way to the end of the script and launch the main choice window. When you click a button, *that window will run another background instance* of CloudBuddy's `main.sh` with a command-line flag to preserve which button you clicked.
90 | This approach has its downsides, but it is necessary for the main window to have a button layout of **3-by-2**. Otherwise, all 6 buttons would be in one long row - which would look horrible and would prevent additional buttons from being added due to space constraints.
91 | Did I mention that all CloudBuddy windows use **`yad`**? This dialog box utility is the most robust GTK dialog available for bash scripting, and is the basis of [Pi-Apps](https://github.com/Botspot/pi-apps), [Pi Power Tools](https://github.com/Botspot/Pi-Power-Tools), [YouTubuddy](https://github.com/Botspot/youtubuddy), [Update Buddy](https://github.com/Botspot/update-buddy), [The TwisterOS Patcher](https://github.com/Botspot/TwistUP), and [Windows Screensavers for RPi](https://github.com/Botspot/Screensavers). YAD is not perfect though, and its inability to display multiple rows of exit-code-type buttons is a major hindrance.
92 | **Enough said. Command-line options for CloudBuddy are below.**
93 | PRO TIP: if you run CloudBuddy in a terminal, it will dynamically generate a custom-tailored command to instantly reach the exact same place later. For example, if I was using the file browser and wanted to quickly reach a deep subdirectory later, this information would be very helpful:
94 | 
95 | #### Set up CloudBuddy and then exit
96 | On every launch, CloudBuddy checks for dependencies and creates a menu launcher. To run this preliminary stuff and then exit immediately, use this command-line flag:
97 | ```
98 | ~/cloudbuddy/main.sh setup
99 | ```
100 | #### Source CloudBuddy's functions and then exit
101 | Necessary for when CloudBuddy is downloading or uploading something in a terminal and we want to reuse CloudBuddy's colorized-`echo` functions.
102 | ```
103 | source ~/cloudbuddy/main.sh source
104 | ```
105 | More functions may be added in the future, but at the time of writing this they are: `error`, `warning`, `echobright`, `echocommand`, `echoprogress`, `list_descendants`, `back`, `drivetype`, and `choosedrive`.
106 | #### New drive
107 | ```
108 | ~/cloudbuddy/main.sh newdrive
109 | ```
110 | Optionally, a drive name and drive type can be specified (in that order) on the command-line to skip launching the selection window.
111 | #### Remove drive
112 | ```
113 | ~/cloudbuddy/main.sh removedrive
114 | ```
115 | Optionally, a drive name can be specified on the command-line to skip launching the selection window.
116 | #### Mount drive
117 | ```
118 | ~/cloudbuddy/main.sh mountdrive
119 | ```
120 | Optionally, a drive name and mountpoint can be specified (in that order) on the command-line to skip launching the selection window.
121 | #### Unmount drive
122 | ```
123 | ~/cloudbuddy/main.sh unmountdrive
124 | ```
125 | Optionally, a drive name, OR a mountpoint can be specified on the command-line to skip launching the selection window.
126 | #### Browse drive
127 | ```
128 | ~/cloudbuddy/main.sh browsedrive
129 | ```
130 | Optionally, a drive can be specified on the command-line to skip launching the selection window. Additionally, you can specify a subfolder to begin in, like this:
131 | ```
132 | ~/cloudbuddy/main.sh browsedrive "My Google Drive:Attachments/old stuff"
133 | ```
134 | Additionally, a simpler, faster file browser can be launched if the `fastmode` variable is set to **`1`**, like this:
135 | ```
136 | fastmode=1 ~/cloudbuddy/main.sh browsedrive
137 | ```
138 | #### Web Interface
139 | ```
140 | ~/cloudbuddy/main.sh webinterface
141 | ```
142 |
--------------------------------------------------------------------------------
/icons/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/back.png
--------------------------------------------------------------------------------
/icons/browse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/browse.png
--------------------------------------------------------------------------------
/icons/cloud-square.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/cloud-square.png
--------------------------------------------------------------------------------
/icons/cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/cloud.png
--------------------------------------------------------------------------------
/icons/cloud.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/icons/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/download.png
--------------------------------------------------------------------------------
/icons/dropbox-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/dropbox-24.png
--------------------------------------------------------------------------------
/icons/dropbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/dropbox.png
--------------------------------------------------------------------------------
/icons/eject.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/eject.png
--------------------------------------------------------------------------------
/icons/folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/folder.png
--------------------------------------------------------------------------------
/icons/gdrive-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/gdrive-24.png
--------------------------------------------------------------------------------
/icons/gdrive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/gdrive.png
--------------------------------------------------------------------------------
/icons/link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/link.png
--------------------------------------------------------------------------------
/icons/mount.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/mount.png
--------------------------------------------------------------------------------
/icons/move.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/move.png
--------------------------------------------------------------------------------
/icons/new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/new.png
--------------------------------------------------------------------------------
/icons/none-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/none-24.png
--------------------------------------------------------------------------------
/icons/onedrive-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/onedrive-24.png
--------------------------------------------------------------------------------
/icons/onedrive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/onedrive.png
--------------------------------------------------------------------------------
/icons/other-24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/other-24.png
--------------------------------------------------------------------------------
/icons/remove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/remove.png
--------------------------------------------------------------------------------
/icons/stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/stop.png
--------------------------------------------------------------------------------
/icons/trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/trash.png
--------------------------------------------------------------------------------
/icons/upload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/upload.png
--------------------------------------------------------------------------------
/icons/webinterface.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Botspot/cloudbuddy/4bbc160f4efbafb6b39f95bb54c79ff9731e25e5/icons/webinterface.png
--------------------------------------------------------------------------------
/main.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -a
4 | DIRECTORY="$(readlink -f "$(dirname "$0")")"
5 |
6 | error() {
7 | echo -e "\e[91m$1\e[39m" | sed 's||| ; |||g' 1>&2
8 | zenity --error --width 300 --text "$(echo -e "$1" | sed 's/&/&/g')"
9 | kill $(cat "${DIRECTORY}/mypid") $(list_descendants $(cat "${DIRECTORY}/mypid")) 2>/dev/null
10 | exit 1
11 | }
12 |
13 | errorback() {
14 | echo -e "\e[91m$1\e[39m" | sed 's|||g' | sed 's|||g' 1>&2
15 | zenity --error --width 300 --text "$(echo -e "$1" | sed 's/&/&/g' | tr -d '<>')"
16 | back
17 | }
18 |
19 | warning() { #just like error, but doesn't exit
20 | echo -e "\e[91m$1\e[39m" | sed 's|||g ; s|||g' 1>&2
21 | zenity --error --width 300 --text "$(echo -e "$1" | sed 's/&/&/g')"
22 | }
23 |
24 | #Displays something meaningful to the user that is brighter than normal output
25 | echobright() {
26 | echo -e "\e[97m$@\e[39m"
27 | }
28 |
29 | #displays a command CloudBuddy is running.
30 | echocommand() {
31 | echo -e "\e[32mRunning this command: \e[96m$@\e[39m"
32 | }
33 |
34 | #Yellow text with clock to display things like "Creating new drive..."
35 | echoprogress() {
36 | echo -e "\e[33m🕑 $@\e[39m"
37 | }
38 |
39 | list_descendants() { #list descent PIDs to the given parent PID.
40 | #This is used to kill yad when a parent script is killed.
41 | #https://unix.stackexchange.com/a/124148/369481
42 | local children=$(ps -o pid= --ppid "$1")
43 |
44 | for pid in $children
45 | do
46 | list_descendants "$pid"
47 | done
48 |
49 | echo "$children"
50 | }
51 |
52 | back() { #handy function to go back to the main start page
53 | "$0" &
54 | kill -s SIGHUP $$
55 | exit 0
56 | }
57 |
58 | drivetype() { #determine the type of a $1-specified drive. 'gdrive', 'dropbox', 'onedrive'
59 | [ -z "$1" ] && error "drivetype(): requires an argument"
60 |
61 | #ordinary rclone config file location: $HOME/.config/rclone/rclone.conf
62 | if [ ! -f "$(rclone config file | sed -n 2p)" ];then
63 | warning "drivetype(): rclone config file not found!\nIt is: $(rclone config file)" 1>&2
64 | echo other
65 | return 0
66 | fi
67 |
68 | drivetype="$(cat "$HOME/.config/rclone/rclone.conf" | grep -x "\[$1\]" --after=1 | sed -n 2p | sed 's/type = //g' | \
69 | sed 's/^drive$/gdrive/g')"
70 |
71 | #rename drivetype variable to 'other' if unknown
72 | if [ "$drivetype" != gdrive ] && [ "$drivetype" != onedrive ] && [ "$drivetype" != dropbox ];then
73 | drivetype=other
74 | fi
75 | echo "$drivetype"
76 | }
77 |
78 | choosedrive() { #gui selector for drives
79 | #Variable 1 is the text prompt, if set.
80 | useicons=1
81 | LIST=''
82 | if [ ! -z "$useicons" ] && [ "$useicons" == 1 ];then
83 | IFS=$'\n'
84 | for drive in $(echo "$remotes" | sed 's/:$//g') ;do
85 | LIST="$LIST
86 | ${DIRECTORY}/icons/$(drivetype "$drive")-24.png
87 | $drive"
88 | done
89 | LIST="$(echo "$LIST" | grep .)"
90 | #echo "$LIST"
91 |
92 | echo "$LIST" | yad "${yadflags[@]}" --list \
93 | --width=260 --height=300 \
94 | --column=':IMG' --column='' --print-column=2 --no-headers --text="$([ -z "$1" ] && echo "Select a drive to continue." || echo "$1")"
95 | else #no icons in list - faster
96 | echo "$remotes" | sed 's/:$//g' | yad "${yadflags[@]}" --list \
97 | --width=260 --height=300 \
98 | --column='' --no-headers --text="Select a drive to continue."
99 | fi
100 | }
101 |
102 | #get OS architecture (32-bit or 64-bit)
103 | if [ "$(od -An -t x1 -j 4 -N 1 "$(readlink -f /sbin/init)")" = ' 02' ];then
104 | arch=64
105 | elif [ "$(od -An -t x1 -j 4 -N 1 "$(readlink -f /sbin/init)")" = ' 01' ];then
106 | arch=32
107 | else
108 | error "Failed to detect OS CPU architecture! Something is very wrong."
109 | fi
110 |
111 | [ "$1" == source ] && return 0 #this script's functions can be sourced, then immediately exit, if you run 'main.sh source'
112 | #END OF FUNCTIONS, BEGINNING OF SCRIPT
113 |
114 | #kill other instances of this script. Necessary for yad's --form buttons as they spawn child processes
115 | if [ -f "${DIRECTORY}/mypid" ] ;then
116 | #echo -e "parent: $(cat "${DIRECTORY}/mypid")\nchildren: $(list_descendants $(cat "${DIRECTORY}/mypid"))"
117 | kill -s SIGHUP $(cat "${DIRECTORY}/mypid") $(list_descendants $(cat "${DIRECTORY}/mypid")) 2>/dev/null
118 | fi
119 | mypid=$$
120 | echo $mypid > "${DIRECTORY}/mypid"
121 |
122 | #check for updates and auto-update if the no-update files does not exist
123 | if [ ! -f "${DIRECTORY}/no-update" ];then
124 | prepwd="$(pwd)"
125 | cd "$DIRECTORY"
126 | localhash="$(git rev-parse HEAD)"
127 | latesthash="$(git ls-remote https://github.com/Botspot/cloudbuddy HEAD | awk '{print $1}')"
128 |
129 | if [ "$localhash" != "$latesthash" ] && [ ! -z "$latesthash" ] && [ ! -z "$localhash" ];then
130 | echobright "CloudBuddy is out of date. Downloading new version..."
131 | echobright "To prevent update checking from now on, create a file at ${DIRECTORY}/no-update"
132 | sleep 1
133 |
134 | #get file hash of this running script to compare it later
135 | oldhash="$(shasum "$0")"
136 |
137 | echobright "running 'git pull'..."
138 | git pull
139 |
140 | if [ "$oldhash" == "$(shasum "$0")" ];then
141 | #this script not modified by git pull
142 | echobright "git pull finished. Proceeding..."
143 | else
144 | echobright "git pull finished. Reloading script..."
145 | #run updated script in background
146 | ( "$0" "$@" ) &
147 | exit 0
148 | fi
149 | fi
150 | cd "$prepwd"
151 | fi
152 |
153 | #install dependencies
154 | command -v yad >/dev/null || (echobright "Installing 'yad'..." ; sudo apt update && sudo apt install -y yad || error "Failed to install yad!")
155 | command -v xclip >/dev/null || (echobright "Installing 'xclip'..." ; sudo apt update && sudo apt install -y xclip || error "Failed to install xclip!")
156 | #rclone installation - if rclone command missing ------------ or if armhf rclone is installed on a 64-bit system ------------------------------- or if installed version is out of date
157 | if ! command -v rclone >/dev/null || ([ "$arch" == 64 ] && [ "$(od -An -t x1 -j 4 -N 1 /usr/bin/rclone)" == ' 01' ]) || (version="$(cat /tmp/rclone-version >/dev/null || wget -O /tmp/rclone-version https://downloads.rclone.org/version.txt && cat /tmp/rclone-version)" && [ -s /tmp/rclone-version ] && [ "$(rclone --version | head -n1)" != "$version" ]);then
158 | echobright "Installing 'rclone'..."
159 |
160 | archname=arm
161 | if [ "$arch" == 64 ];then
162 | archname=arm64
163 | fi
164 | echocommand "wget https://downloads.rclone.org/rclone-current-linux-$archname.zip -O rclone-current-linux-$archname.zip"
165 | wget https://downloads.rclone.org/rclone-current-linux-$archname.zip -O rclone-current-linux-$archname.zip || error "Failed to download rclone from downloads.rclone.org!"
166 |
167 | echocommand "unzip -j -o -d rclone-temp rclone-current-linux-$archname.zip"
168 | unzip -j -o -d rclone-temp rclone-current-linux-$archname.zip || error "Failed to extract rclone-current-linux-$archname.zip"
169 |
170 | echocommand "sudo mv rclone-temp/rclone /usr/bin/rclone"
171 | sudo mv -f rclone-temp/rclone /usr/bin/rclone || error "Failed to move rclone binary to /usr/bin/rclone"
172 | echobright "sudo mv rclone-temp/rclone.1 /usr/share/man/man1/rclone.1"
173 | sudo mv -f rclone-temp/rclone.1 /usr/share/man/man1/rclone.1
174 |
175 | echocommand "sudo chown root: /usr/bin/rclone"
176 | sudo chown root: /usr/bin/rclone
177 | echocommand "rm -rf rclone-current-linux-$archname.zip rclone-temp"
178 | rm -rf rclone-current-linux-$archname.zip rclone-temp
179 | echobright "Rclone installation successful. Proceeding..."
180 | fi
181 |
182 | #check for fuse kernel module. Launch it if necessary. If still not running, set usefuse var to zero.
183 | usefuse=1
184 | if ! ls /sys/module | awk '{print $1}' | grep -qFx fuse ;then
185 | echobright "Kernel module 'fuse' not found. Attempting to load it..."
186 | echocommand "sudo modprobe fuse"
187 | sudo modprobe fuse
188 |
189 | #check again to ensure fuse module running
190 | if ! ls /sys/module | awk '{print $1}' | grep -qFx fuse ;then
191 | usefuse=0
192 | warning "Failed to load kernel module 'fuse'!\nAs a result, rclone cannot mount cloud drives."
193 | fi
194 | fi
195 |
196 | #menu button
197 | if [ ! -f ~/.local/share/applications/cloudbuddy.desktop ];then
198 | echobright "Creating menu button..."
199 | mkdir -p ~/.local/share/applications
200 | echo "[Desktop Entry]
201 | Name=CloudBuddy
202 | Comment=The ultimate cloud storage manager.
203 | Exec=$0
204 | Icon=${DIRECTORY}/icons/cloud-square.png
205 | Terminal=false
206 | Type=Application
207 | Categories=Application;Network;RemoteAccess;
208 | StartupNotify=true" > ~/.local/share/applications/cloudbuddy.desktop
209 | fi
210 |
211 | #icon
212 | if [ ! -f ~/.local/share/icons/cloudbuddy.png ];then
213 | echobright "copying program icon..."
214 | mkdir -p ~/.local/share/icons
215 | cp ${DIRECTORY}/icons/cloud-square.png ~/.local/share/icons/cloudbuddy.png
216 | fi
217 |
218 | if [ "$1" == setup ];then
219 | echo "CloudBuddy setup complete."
220 | exit 0
221 | fi
222 |
223 | if [ -d /dev/shm ];then
224 | #if linux kernel's RAM disk directory exists, use it for signaling
225 | useshm=1
226 | else
227 | useshm=0
228 | fi
229 |
230 | yadflags=(--center --title="CloudBuddy" --separator='\n' --window-icon="${DIRECTORY}/icons/cloud-square.png" --class CloudBuddy --name CloudBuddy)
231 |
232 | #generate list of remotes that rclone currently knows about
233 | remotes="$(rclone listremotes)"
234 |
235 | #COMMAND-LINE SUBSCRIPTS
236 | if [ "$1" == newdrive ];then
237 | #Usage: main.sh newdrive driveName drivetype
238 | echobright "New drive"
239 | drive="$3"
240 | drivetype="$2"
241 |
242 | while [ -z "$drive" ];do #skip the drive selection window if drive is cli-specified
243 | drive="$(yad "${yadflags[@]}" --form --no-escape \
244 | --field="Name of new drive: " '' \
245 | --button='Google Drive'!"${DIRECTORY}/icons/gdrive.png":4 \
246 | --button='OneDrive'!"${DIRECTORY}/icons/onedrive.png":2 \
247 | --button='Dropbox'!"${DIRECTORY}/icons/dropbox.png":0)"
248 | button=$?
249 |
250 | #If window closed some other way than the drive selection buttons, go back to start page
251 | if [ $button != 4 ] && [ $button != 2 ] && [ $button != 0 ];then
252 | echobright "User did not choose a drive type to create. Going back..."
253 | break
254 | fi
255 |
256 | if [ -z "$drive" ];then
257 | yad "${yadflags[@]}" --text=" A Drive must be given a name. " \
258 | --button=OK:0 || back
259 | fi
260 | #change button number to script-readable drive type
261 | drivetype="$(echo "$button" | sed s/4/drive/g | sed s/2/onedrive/g | sed s/0/dropbox/g)"
262 | done #past this point, $drive is populated and a valid drive type has been selected.
263 |
264 | if [ $button == 4 ] || [ $button == 2 ] || [ $button == 0 ] || [ ! -z "$drivetype" ];then #this will be skipped if "Going back..."
265 | echobright "To get here from the command line, run this:\n$0 newdrive "\""$drive"\"" "\""$drivetype"\"""
266 |
267 | configfile="$((rclone config file || echo $'\n'"$HOME/.config/rclone/rclone.conf") | tail -n +2)"
268 | mkdir -p "$(dirname "$configfile")"
269 | echocommand "rclone config create "\""$drive"\"" "\""$drivetype"\"" env_auth=false >> "\""$configfile"\"""
270 |
271 | rclone config create "$drive" "$drivetype" env_auth=false >> "$configfile" &
272 | rclonepid=$!
273 | echo "$rclonepid"
274 |
275 | (yad "${yadflags[@]}" --text="A web browser should appear to authorize the connection." --button=' Cancel operation ':0
276 | button=$?
277 | if [ $button != 0 ];then
278 | sleep infinity
279 | fi
280 | ) &
281 | yadpid=$!
282 |
283 | while true;do
284 | #if rclone finishes, then close cancel-button window
285 | if [ ! -e /proc/$rclonepid ];then
286 | kill $yadpid $(list_descendants $yadpid)
287 | break
288 | fi
289 | #if cancel-button window closes, then kill rclone as user must have clicked 'Cancel'
290 | if [ ! -e /proc/$yadpid ];then
291 | kill $rclonepid
292 | break
293 | fi
294 | sleep 1
295 | done
296 | fi #everything in the above if statement is skipped if $button is not 0, 2, or 4
297 |
298 | elif [ "$1" == removedrive ];then
299 | #Usage: main.sh removedrive driveName
300 | echobright "Remove drive"
301 | drive="$(echo "$2" | sed 's/:$//g')"
302 | if [ -z "$(echo "$remotes" | grep "$drive")" ];then
303 | echobright "CLI-selected drive '$drive' does not exist."
304 | drive=''
305 | fi
306 | if [ -z "$drive" ];then
307 | drive="$(choosedrive "Choose a cloud drive to sign out of.")"
308 | fi
309 | [ -z "$drive" ] && back
310 |
311 | echobright "To get here from the command line, run this:\n$0 removedrive "\""$drive"\"""
312 |
313 | echocommand "rclone config disconnect "\""$drive:"\"""
314 | rclone config disconnect "$drive": || echobright "Don't worry - rclone errors above this point are normal and expected for certain cloud storage providers."
315 |
316 | echocommand "rclone config delete "\""$drive"\"""
317 | rclone config delete "$drive" #this rclone option DOES NOT ACCEPT A COLON!
318 |
319 | elif [ "$1" == mountdrive ];then
320 | #Usage: main.sh mountdrive driveName /path/to/mountpoint
321 | echobright "Mount drive"
322 | if [ "$usefuse" == 0 ];then
323 | errorback "Rclone cannot mount a drive when the 'fuse' kernel module is not loaded.\nRun this command to load the kernel module: 'sudo modprobe fuse'"
324 | fi
325 |
326 | drive="$(echo "$2" | sed 's/:$//g')"
327 | if [ -z "$(echo "$remotes" | grep "$drive")" ];then
328 | echobright "CLI-selected drive '$drive' does not exist."
329 | drive=''
330 | fi
331 | if [ -z "$drive" ];then
332 | echo "Choosing drive..."
333 | drive="$(choosedrive)"
334 | fi
335 | [ -z "$drive" ] && back
336 |
337 | directory="$3" #attempt to mount to folder specified by command-line
338 | while true;do
339 | if [ -z "$directory" ];then
340 | echo "Choosing mountpoint..."
341 | directory="$(yad "${yadflags[@]}" --file --directory --mime-filter="Directories | inode/directory" --width=600 --height=400 \
342 | --text="Choose an empty directory to mount $drive to.")"
343 | [ $? != 0 ] && back
344 | fi
345 |
346 | if [ -z "$directory" ];then
347 | echobright "No directory chosen!"
348 | yad "${yadflags[@]}" --text="No directory chosen!" --button="Try again"
349 | elif [ ! -d "$directory" ];then
350 | echobright "Directory $directory does not exist!"
351 | yad "${yadflags[@]}" --text="No directory chosen!" --button="Try again"
352 | elif [ ! -z "$(ls -A "$directory")" ];then
353 | echobright "Directory $directory contains files!"
354 | yad "${yadflags[@]}" --text=" Directory $directory contains files! "$'\n'" It must be empty. " --button="Choose another folder"
355 | else
356 | break
357 | fi
358 | directory=''
359 | done #past this point, a valid directory is selected that contains no files
360 |
361 | echobright "To get here from the command line, run this:\n$0 mountdrive "\""$drive"\"" "\""$directory"\"""
362 |
363 | #this will run for a minimum of 7 seconds before returning to the main window.
364 | sleep 7 &
365 | sleeppid=$!
366 | (echo "# "; [ "$useshm" == 1 ] && [ "$fastmode" == 0 ] && tail -F /dev/shm/cloudbuddy_current_file || sleep 20; echo "# This is taking longer than expected to complete."; sleep infinity) | yad "${yadflags[@]}" --progress --pulsate --title=Mounting \
367 | --text="Mounting: $drive"$'\n'"To: $directory..." \
368 | --width=300 --height=100 --center --auto-close --auto-kill \
369 | --no-buttons 2>/dev/null &
370 | loader_pid=$!
371 | echocommand "rclone mount "\""$drive:"\"" "\""$directory"\"""
372 | setsid bash -c 'errors="$(rclone mount "'"$drive"'": "'"$directory"'" 2>&1)"
373 | [ $? != 0 ] && warning "Rclone failed to mount '"$drive"' to '"$directory"'!'$'\n''Errors: $errors"' &
374 | wait $sleeppid
375 | kill $loader_pid
376 |
377 | elif [ "$1" == unmountdrive ];then
378 | #Usage: main.sh unmountdrive driveName, OR main.sh unmountdrive /path/to/mountpoint
379 | echobright "Unmount drive"
380 |
381 | if [ "$usefuse" == 0 ];then
382 | errorback "Rclone cannot unmount a drive when the 'fuse' kernel module is not loaded.\nRun this command to load the kernel module: 'sudo modprobe fuse'"
383 | fi
384 |
385 | mounts="$(mount | grep fuse.rclone | sed 's/ type fuse.rclone (.*)//g' | sed 's/: on /:/g')"
386 |
387 | drives="$(echo "$mounts" | sed 's/:.*//g')"
388 | mountdirs="$(echo "$mounts" | sed 's/.*://g')"
389 | #echobright "Mounted drives: $drives\nMountdirs: $mountdirs"
390 |
391 | if [ ! -z "$2" ] && echo "$drives" | grep -xq "$(echo "$2" | sed 's/:$//g')" ;then
392 | #cli-specified drive name to unmount
393 | mountpoint="$(echo "$mounts" | grep "$(echo "$2" | sed 's/:$//g'):" | sed 's/.*://g')"
394 | elif [ ! -z "$2" ] && echo "$mountdirs" | grep -q "$2" ;then
395 | #cli-specified directory to unmount
396 | mountpoint="$2"
397 | else
398 | mountpoint="$(echo "$mounts" | sed 's|:| mounted on |g' | sed 's|^||g' | sed 's|$||g' | yad "${yadflags[@]}" --list \
399 | --width=430 --height=200 \
400 | --column='' --no-headers --text="Which drive to unmount?")" || back
401 |
402 | #change yad output back to script-readable: only mountpoint remains
403 | mountpoint="$(echo "$mountpoint" | sed 's|||g' | sed 's|||g' | sed 's/.*mounted on //g')"
404 | fi
405 |
406 | #for the terminal-usage announcement, determine what drive is associated with the mountpoint.
407 | drive="$(echo "$mounts" | grep ":$(echo "$mountpoint" | sed 's|/$||g')" | sed 's/:.*//g')"
408 | echobright "To get here from the command line, run this:\n$0 unmountdrive "\""$mountpoint"\""\nOr you could specify the drive name like this:\n$0 unmountdrive "\""$drive"\"""
409 |
410 | echocommand "fusermount -u "\""$mountpoint"\"""
411 | fusermount -u "$mountpoint" || (echocommand "sudo umount "\""$mountpoint"\"""; sudo umount "$mountpoint") || errorback "Failed to unmount $mountpoint!\nErrors: $(fusermount -u "$mountpoint" 2>&1)"
412 |
413 | elif [ "$1" == browsedrive ];then
414 | #Usage: main.sh browsedrive driveName:prefix
415 | echobright "Browse drive"
416 |
417 | drive="$(echo "$2" | sed 's/:.*//g')"
418 | prefix="$(echo "$2" | sed 's/.*://g')" #variable to store subfolder information for the interactive file browser
419 |
420 | if [ -z "$(echo "$remotes" | grep "$drive")" ];then
421 | echo "CLI-selected drive '$drive' does not exist."
422 | drive=''
423 | fi
424 | if [ -z "$drive" ];then
425 | drive="$(choosedrive)"
426 | fi
427 | [ -z "$drive" ] && back
428 |
429 | #CloudBuddy is equipped with two file browsers: a faster one and a slower one. The fast one has fewer bells and whistles, but it's load time is significantly shorter when dealing with hundreds of files.
430 | if [ -z "$fastmode" ];then
431 | fastmode=0 #Set fastmode=0 for more detailed file browser, set fastmode=1 for a simpler, more streamlined file browser
432 | fi
433 |
434 | while true;do
435 |
436 | #ensure prefix has a trailing slash. This GUI does this by design, but there is no guarantee it will be there if cli-specified.
437 | if [ ! -z "$prefix" ];then
438 | prefix="$(echo "$prefix"/ | sed 's|//$|/|g')"
439 | fi
440 |
441 | echobright "To get here from the command line, run this:\n$0 browsedrive "\""$(echo "$drive:$prefix" | sed 's|/$||g' | sed 's/:$//g')"\"""
442 |
443 | [ $useshm == 1 ] && echo '' > /dev/shm/cloudbuddy_current_file
444 | (echo "# "; [ $useshm == 1 ] && [ $fastmode == 0 ] && tail -F /dev/shm/cloudbuddy_current_file || sleep 20; echo "# This is taking longer than expected to complete."; sleep infinity) | yad "${yadflags[@]}" --progress --pulsate --title=Scanning \
445 | --text="Scanning $(echo "$drive:$prefix" | sed 's/:$//g' | sed 's|/$||g')..." \
446 | --width=300 --height=100 --center --auto-close --auto-kill \
447 | --no-buttons 2>/dev/null &
448 | loader_pid=$!
449 |
450 | if [ $fastmode == 0 ];then
451 | #get detailed information about each file in current folder.
452 | echocommand "rclone lsjson "\""$drive:$(echo "$prefix" | sed 's|/$||g')"\"""
453 | #Json format converted to parsable raw text. Example line:
454 | #Path=Tripod.JPG,Name=Tripod.JPG,Size=1472358,MimeType=image/jpeg,ModTime=2018-09-02T04:12:36.807Z,IsDir=false,ID=1229VLzjsD1XUm5LgdwlfoWEqwpzbWU9p
455 | filelist="$(rclone lsjson "$drive:$(echo "$prefix" | sed 's|/$||g')" 2>&1 | sed 's/":"/=/g' | sed 's/":/=/g' | sed 's/^{"//g' | sed 's/","/,/g' | sed 's/,"/,/g' | sed 's/"}$//g' | sed 's/},$//g' | sed 's/"},$//g' | sed 's/}$//g' | sed '/^\]$/d' | sed '/^\[$/d')"
456 | [ $? != 0 ] && errorback "rclone failed to acquire a file list from $drive:$prefix!\nErrors: $filelist"
457 | #echo "$filelist"
458 |
459 | #Don't kill the loading window now, kill it when list parsing is done.
460 | #kill $loader_pid #close the progress bar window
461 |
462 | IFS=$'\n'
463 | LIST=''
464 | for file in $filelist ;do
465 | #echo "File info: $file"
466 |
467 | #read in values. Unneeded values are commented out to save time
468 | path="$(echo "$file" | sed 's/^Path=//g' | sed 's/,Name=.*//g')"
469 | name="$(echo "$file" | sed 's/.*,Name=//g' | sed 's/,Size=.*//g')"
470 | #size="$(echo "$file" | sed 's/.*Size=//g' | sed 's/,MimeType=.*//g')"
471 | #mimetype="$(echo "$file" | sed 's/.*MimeType=//g' | sed 's/,ModTime=.*//g')"
472 | #modtime="$(echo "$file" | sed 's/.*ModTime=//g' | sed 's/,IsDir=.*//g')"
473 | #isdir="$(echo "$file" | sed 's/.*IsDir=//g' | sed 's/,ID=.*//g')"
474 | #id="$(echo "$file" | sed 's/.*ID=//g')"
475 |
476 | #to prevent commas in filenames from borking up remainder of parsing, remove Path= and Name= from $file before proceeding
477 | file=",,Size=$(echo "$file" | sed 's/.*,Size=//g')"
478 |
479 | #path="$(echo "$file" | awk -F, '{print $1}')"
480 | #name="$(echo "$file" | awk -F, '{print $2}')"
481 | size="$(echo "$file" | awk -F, '{print $3}')"
482 | mimetype="$(echo "$file" | awk -F, '{print $4}')"
483 | modtime="$(echo "$file" | awk -F, '{print $5}')"
484 | isdir="$(echo "$file" | awk -F, '{print $6}')"
485 | id="$(echo "$file" | awk -F, '{print $7}')"
486 |
487 | #if current file is a directory, add a trailing slash to filename
488 | if [ "$isdir" == 'IsDir=true' ];then
489 | #file="$(echo "$file" | sed 's|,Size=|/,Size=|g')"
490 | [ ! -z "$name" ] && name="${name}/"
491 | icon="${DIRECTORY}/icons/folder.png"
492 | else
493 | icon="${DIRECTORY}/icons/none-24.png"
494 | fi
495 |
496 | echo "# $name" >> /dev/shm/cloudbuddy_current_file
497 |
498 | #LIST="$LIST
499 | #$icon
500 | #$file"
501 | LIST="$LIST
502 | $icon
503 | $path
504 | $name
505 | $size
506 | $mimetype
507 | $modtime
508 | $isdir
509 | $id"
510 | #echo -e "Debug info:\nPath: $path\nName: $name\nSize: $size\nMimeType: $mimetype\nModTime: $modtime\nIsDir: $isdir\nID: $id\n"
511 | done
512 | #ABOVE THIS POINT, $LIST CONTAINS THE "XXXX=" TAKEN FROM THE JSON.
513 | LIST="$(echo "$LIST" | sed 's/.*=//g' | tail -n +2 | sed 's/^-1$/0/g')"
514 | #echo "$LIST"
515 |
516 | #Now that list parsing is done, kill the loading window.
517 | kill $loader_pid #close the progress bar window
518 |
519 | else #fastmode == 1
520 | #simpler file browser mode with less GUI features but it loads faster
521 | echocommand "rclone lsf "\""$drive:$(echo "$prefix" | sed 's|/$||g')"\"""
522 | filelist="$(rclone lsf "$drive:$(echo "$prefix" | sed 's|/$||g')" 2>&1)"
523 | [ $? != 0 ] && errorback "rclone failed to acquire a file list from $drive:$prefix!\nErrors: $filelist"
524 | kill $loader_pid #close the progress bar window
525 | filelist="$(echo "$filelist" | tac | sed 's/$/\n/g' | sed "s|/"'$'"|/\n${DIRECTORY}/icons/folder.png|g" | tac | sed -z "s|\n${DIRECTORY}/icons/folder.png|${DIRECTORY}/icons/folder.png|g" | sed -z "s|\n\n|\n${DIRECTORY}/icons/none-24.png\n|g" | sed -z "s|^\n|${DIRECTORY}/icons/none-24.png\n|g")"
526 | #echo "$filelist"
527 | fi
528 |
529 | #array of buttons to send to file browser window
530 | buttons=()
531 | if [ ! -z "$prefix" ];then
532 | buttons+=(--button=Back!"${DIRECTORY}/icons/back.png"!"Up one level to $(echo "$drive:$(dirname "$prefix" | sed 's/^.$//g')" | sed 's/:$//g')":2)
533 | fi
534 | buttons+=(--button='Add files'!"${DIRECTORY}/icons/upload.png"!"Upload files to the current folder, or into a subfolder if one is selected.":10 \
535 | --button=Download!"${DIRECTORY}/icons/download.png"!"Download selected item(s) to your local Downloads folder.":8 \
536 | --button=Move!"${DIRECTORY}/icons/move.png"!"Move the selected file/folder to another location on this drive."$'\n'"If you select multiple items, only the Delete button will be available.":6 \
537 | --button=Link!"${DIRECTORY}/icons/link.png"!"Generate a link to publicly share the selected file/folder.":4)
538 |
539 | if [ $fastmode == 0 ];then
540 | output="$(echo "$LIST" | sed 's/&/&/g' | yad "${yadflags[@]}" --list --multiple --separator='\n' --title="CloudBuddy - browsing $(echo "$drive:$prefix" | sed 's/:$//g' | sed 's|/$||g')" \
541 | --column=:IMG --column=Name --column=echoName:HD --column=Size:SZ --column=MimeType:HD --column=ModTime --column=IsDir:HD --column=ID:HD --print-column=3 \
542 | --width=560 --height=400 --text="$([ ! -z "$prefix" ] && echo -e "Currently in $(echo "$prefix" | sed 's|/$||g').\r")Double-click to open files/folders." \
543 | "${buttons[@]}" )"
544 | button=$?
545 | else
546 | output="$(echo "$filelist" | sed 's/&/&/g' | yad "${yadflags[@]}" --list --multiple --separator='\n' \
547 | --column=:IMG --column=Name --print-column=2 \
548 | --width=300 --height=400 --text="$([ ! -z "$prefix" ] && echo -e "Currently in $(echo "$prefix" | sed 's|/$||g').\r")Double-click to open files/folders." \
549 | "${buttons[@]}" )"
550 | button=$?
551 | fi
552 |
553 | output="$(echo "$output" | grep .)"
554 | echo "Prefix is $prefix, Output is '$output'"
555 |
556 | if [ $button == 2 ];then
557 | #back button: go up a directory in prefix.
558 | prefix="$(dirname "$prefix" | sed 's/^.$//g')"
559 | elif [ $button == 4 ] && [ ! "$(echo "$output" | wc -l)" -gt 1 ];then
560 | #get link for selected
561 | if [ -z "$output" ];then
562 | warning "In order to generate a link, you must select a file or folder."
563 | else
564 | (echo "# "; sleep 20; echo "# This is taking longer than expected to complete."; sleep infinity) | yad "${yadflags[@]}" --progress --pulsate --title="CloudBuddy" \
565 | --text="Generating link..." \
566 | --width=300 --height=100 --center --auto-close --auto-kill \
567 | --no-buttons 2>/dev/null &
568 | loader_pid=$!
569 | echocommand "rclone link "\""$drive:$prefix$output"\"""
570 | link="$(rclone link "$drive:$prefix$output" 2>&1)"
571 | exitcode=$?
572 | kill $loader_pid
573 | if [ $exitcode != 0 ];then
574 | warning "Failed to generate a link for $drive:$prefix$output\nErrors: $link"
575 | else
576 | yad "${yadflags[@]}" --form --columns=2 --width=400 \
577 | --image="${DIRECTORY}/icons/$(drivetype "$drive").png" \
578 | --text="Public link below." \
579 | --field='' "$link" --field=Copy:FBTN "bash -c 'echo "\""$link"\"" | xclip -sel clip'" \
580 | --button=Close:0
581 | fi
582 | fi
583 | elif [ $button == 6 ];then
584 | #move selected
585 | if [ -z "$output" ];then
586 | warning "A file or folder must be selected in order to move it!"
587 | else
588 | if [ "$(echo "$output" | wc -l)" -gt 1 ];then
589 | #multiple items selected, only display Delete option.
590 | moveto="$(echo -en "$output" | yad "${yadflags[@]}" --text-info --fontname=12 --wrap \
591 | --width=400 --text="When multiple items are selected, only the Delete option is available."$'\n'"These items were selected:" \
592 | --button=Cancel:1 \
593 | --button=Delete!"${DIRECTORY}/icons/trash.png"!" Delete the selected $(echo "$output" | wc -l) items from the cloud."$'\n'" Note: Most cloud storage providers will save deleted items in a recovery folder. ":2)"
594 | button=$?
595 | else
596 | #single file/folder selected, display the usual 'Move' options.
597 | moveto="$(yad "${yadflags[@]}" --form --width=400 --text=" Enter a path to move the chosen file:" \
598 | --field="From ":RO "$prefix$(echo "$output" | sed 's|/$||g')" \
599 | --field="To " "$prefix$(echo "$output" | sed 's|/$||g')" \
600 | --button=Cancel:1 \
601 | --button=Delete!"${DIRECTORY}/icons/trash.png"!"Delete $drive:$prefix$(echo "$output" | sed 's|/$||g') from the cloud."$'\n'"Note: Most cloud storage providers will save deleted items in a recovery folder.":2 \
602 | --button=Move!"${DIRECTORY}/icons/move.png":0)"
603 | button=$?
604 | fi
605 |
606 | if [ $button == 0 ];then
607 | #move file
608 |
609 | #remove trailing and leading slashes from output, and only keep second line
610 | moveto="$(echo "$moveto" | grep . | sed 's|^/||g' | sed 's|/$||g' | sed -n 2p)"
611 |
612 | (echo "# "; sleep 20; echo "# This is taking longer than expected to complete."; sleep infinity) | yad "${yadflags[@]}" --progress --pulsate --title="Moving..." \
613 | --text="Moving $prefix$output"$'\n'"to $moveto..." \
614 | --width=300 --height=100 --center --auto-close --auto-kill \
615 | --no-buttons 2>/dev/null &
616 | loader_pid=$!
617 | echocommand "rclone moveto "\""$drive:$prefix$output"\"" "\""$drive:$moveto"\"""
618 | errors="$(rclone moveto "$drive:$prefix$output" "$drive:$moveto" 2>&1)"
619 | if [ $? != 0 ];then
620 | warning "Failed to move $prefix$output to $moveto.\nErrors: $errors"
621 | fi
622 | kill $loader_pid
623 |
624 | elif [ $button == 2 ];then
625 | #delete file
626 |
627 | #delete every file found in $output, one at a time
628 | IFS=$'\n'
629 | for file in $output ;do
630 | (echo "# "; sleep 20; echo "# This is taking longer than expected to complete."; sleep infinity) | yad "${yadflags[@]}" --progress --pulsate --title="Deleting..." \
631 | --text="Deleting $drive:$prefix$file..." \
632 | --width=300 --height=100 --center --auto-close --auto-kill \
633 | --no-buttons 2>/dev/null &
634 | loader_pid=$!
635 | if [[ "$file" == */ ]];then
636 | #if folder, delete folder
637 | echocommand "rclone purge "\""$drive:$prefix$file"\"""
638 | set -o pipefail
639 | errors="$(rclone purge "$drive:$prefix$file" 2>&1 | tee /dev/stderr)"
640 | exitcode=$?
641 | else
642 | echocommand "rclone deletefile "\""$drive:$prefix$file"\"""
643 | set -o pipefail
644 | errors="$(rclone deletefile "$drive:$prefix$file" 2>&1 | tee /dev/stderr)"
645 | exitcode=$?
646 | fi
647 | [ $exitcode != 0 ] && warning "Rclone failed to delete $drive:$prefix$file. Errors:\n$errors" &
648 | kill $loader_pid #close the progress bar window
649 | done
650 | fi
651 | fi
652 | elif [ $button == 8 ];then
653 | #download selected item(s)
654 | echo Download
655 | if [ -z "$output" ];then
656 | warning "At least one file or folder must be selected in order the Download button to do anything!"
657 | else
658 | destinationdir="$HOME/Downloads"
659 |
660 | "${DIRECTORY}/terminal-run" "
661 | trap 'sleep infinity' EXIT
662 | output="\""$output"\""
663 | source "\""$0"\"" source
664 | IFS=$'\n'
665 | failed=0
666 | for file in "\$"output ;do
667 | echoprogress "\""Downloading "\"""\\""\"""\""$drive:$prefix"\$"file"\"""\\""\"""\"""\""
668 | if [[ "\"""\$"file"\"" == */ ]];then
669 | #if downloading directory, create subdirectory on destination to preserve structure
670 | echocommand "\""rclone copy -P --stats-one-line "\"""\\""\"""\""$drive:$prefix"\$"file"\"""\\""\"""\"" "\"""\\""\"""\""$destinationdir/"\$"file"\"""\\""\"""\"""\""
671 | #re-run on failure if it took over 10 minutes
672 | while true;do
673 | start_time="\$"(date +%s)
674 | rclone copy -P --stats-one-line "\""$drive:$prefix"\$"file"\"" "\""$destinationdir/"\$"file"\""
675 | exitcode="\$"?
676 | end_time="\$"(date +%s)
677 | if [ "\$"exitcode == 0 ];then
678 | break
679 | elif [ "\$"exitcode != 0 ] && [ "\$"((end_time-start_time)) -ge 600 ];then
680 | echo -e '\n\e[101m\e[30mDownload failed! But it was going for 10 minutes, so trying again.\e[39m\e[49m'
681 | else
682 | break #give up with exitcode=1
683 | fi
684 | done
685 | else
686 | echocommand "\""rclone copy -P --stats-one-line "\"""\\""\"""\""$drive:$prefix"\$"file"\"""\\""\"""\"" "\"""\\""\"""\""$destinationdir"\"""\\""\"""\"""\""
687 | rclone copy -P --stats-one-line "\""$drive:$prefix"\$"file"\"" "\""$destinationdir"\""
688 | exitcode="\$"?
689 | fi
690 | if [ "\$"exitcode != 0 ];then
691 | failed=1
692 | fi
693 | done
694 | if [ "\$"failed == 0 ];then
695 | echo -e '\e[102m\e[30mDownload succeeded! Close this window to exit.\e[39m\e[49m'
696 | else
697 | echo -e '\n\e[101m\e[30mDownload failed! Errors above.\e[39m\e[49m'
698 | fi
699 | " "Downloading $(echo "$output" | wc -l) file$([ $(echo "$output" | wc -l) != 1 ] && echo s) to $(echo "$destinationdir" | sed "s|$HOME/|~/|g")"
700 | fi
701 | elif [ $button == 10 ] && [ ! "$(echo "$output" | wc -l)" -gt 1 ];then
702 | #upload files
703 | if [ ! -z "$output" ] && [[ "$output" == */ ]];then
704 | #if folder selected, destination is prefix/output
705 | destinationdir="$prefix$output"
706 | else
707 | destinationdir="$prefix"
708 | fi
709 | destinationdir="$(echo "$destinationdir" | sed 's|/$||g')"
710 |
711 | output="$(yad "${yadflags[@]}" --dnd --on-top --text=" Drag-n-drop files here to upload them to $(echo "$drive:$destinationdir" | sed 's/:$//g'). " \
712 | --button=Cancel!!"Go back":1 --button=Upload!"${DIRECTORY}/icons/upload.png"!"Upload the files you have dropped onto this window":0)"
713 | button=$?
714 | #convert output from "file://" format to absolute paths format
715 | output="$(echo "$output" | sed 's|^file://||')"
716 |
717 | if [ $button == 0 ] && [ ! -z "$output" ];then
718 | #upload each file at a time with rclone
719 | "${DIRECTORY}/terminal-run" "
720 | trap 'sleep infinity' EXIT
721 | output="\""$output"\""
722 | source "\""$0"\"" source
723 | IFS=$'\n'
724 | failed=0
725 | for file in "\$"output ;do
726 | echoprogress "\""Copying "\$"file..."\""
727 |
728 | if [ -d "\"""\$"file"\"" ];then
729 | #if uploading directory, create subdirectory on destination to preserve structure
730 | echocommand rclone copy -P --stats-one-line "\"""\$"file"\"" "\""$drive:$destinationdir/"\$"(basename "\"""\$"file"\"")"\""
731 | rclone copy -P --stats-one-line "\"""\$"file"\"" "\""$drive:$destinationdir/"\$"(basename "\"""\$"file"\"")"\""
732 | exitcode="\$"?
733 | else
734 | echocommand rclone copy -P --stats-one-line "\"""\$"file"\"" "\""$drive:$destinationdir"\""
735 | rclone copy -P --stats-one-line "\"""\$"file"\"" "\""$drive:$destinationdir"\""
736 | exitcode="\$"?
737 | fi
738 | if [ "\$"exitcode != 0 ];then
739 | failed=1
740 | fi
741 | done
742 | if [ "\$"failed == 0 ];then
743 | echo -e '\e[102m\e[30mUpload succeeded! Close this window to exit.\e[39m\e[49m'
744 | else
745 | echo -e '\e[101m\e[30mUpload failed! Errors above.\e[39m\e[49m'
746 | fi
747 | #create yad button-box to refresh main window
748 | yad "\\"-"\\"-title=Refresh? --text="\""Upload complete. Refresh the file list?"\"" --width=300 --height=100 --center --on-top --focus --window-icon="\""${DIRECTORY}/icons/cloud-square.png"\"" --button=Refresh:0 && "\""$0"\"" browsedrive "\""$drive:$prefix"\""
749 | " "Uploading $(echo "$output" | wc -l) file$([ $(echo "$output" | wc -l) != 1 ] && echo s) to $(echo "$drive:$destinationdir" | sed 's/:$//g')"
750 | elif [ $button == 0 ] && [ -z "$output" ];then
751 | warning "No files were dropped onto the Upload window.\nAs a result, no files will be uploaded to $(echo "$drive:$destinationdir" | sed 's/:$//g')."
752 | fi
753 |
754 | elif [ $button == 0 ] && [ ! "$(echo "$output" | wc -l)" -gt 1 ];then
755 | #open file/folder - double-clicked
756 | if [[ "$output" == */ ]];then
757 | echobright "Folder selected: $(echo "$output" | sed 's|/$||g')"
758 | if [ ! -z $prefix ];then
759 | prefix="$prefix$output"
760 | else
761 | prefix="$output"
762 | fi
763 | else
764 | echobright "File selected: $(echo "$output")"
765 |
766 | #download file
767 | tmpdir="$(mktemp -d)"
768 |
769 | set -o pipefail
770 | (echo "# "; sleep infinity) | yad "${yadflags[@]}" --progress --pulsate --title=Downloading \
771 | --text="Downloading:"$'\n'"$drive:$prefix$output"$'\n'"To: $tmpdir..." \
772 | --width=300 --height=100 --center --auto-close --auto-kill \
773 | --button=Cancel:"bash -c '$0 browsedrive "\""$drive"\$"(echo Ogo= | base64 -d)$prefix"\""; kill $$ $(list_descendants $$)'" &
774 | loader_pid=$!
775 |
776 | echocommand "rclone copy -P --stats-one-line "\""$drive:$prefix$output\""" "\""$tmpdir"\"""
777 | set -o pipefail
778 | errors="$(rclone copy -P --stats-one-line "$drive:$prefix$output" "$tmpdir" 2>&1 | tee /dev/stderr)"
779 | exitcode=$?
780 | kill $loader_pid #close the progress bar window
781 |
782 | if [ $button == 1 ];then
783 | true #cancel button clicked
784 | elif [ ! -f "$tmpdir/$output" ];then
785 | #check for output file existence
786 | warning "Rclone succeeded to download $output, but $tmpdir/$output does not exist.\nErrors: $errors"
787 | else
788 | #open file
789 | xdg-open "$tmpdir/$output"
790 | fi
791 | fi
792 | elif [ "$(echo "$output" | wc -l)" -gt 1 ] && ([ $button == 0 ] || [ $button == 4 ] || [ $button == 10 ]);then
793 | #multiple items selected
794 | warning "This button cannot work when multiple items are selected!"
795 | else #unknown button
796 | break
797 | fi
798 | done #end of interactive file browser - forever loop
799 |
800 | elif [ "$1" == webinterface ];then
801 | echobright "Web Interface"
802 | echobright "To get here from the command line, run this:\n$0 webinterface"
803 | "${DIRECTORY}/terminal-run" "trap 'sleep infinity' EXIT
804 | source "\""$0"\"" source
805 | echocommand 'rclone rcd --rc-web-gui'
806 | rclone rcd --rc-web-gui
807 | " "Running Rclone Web Interface"
808 | fi #END OF COMMAND-LINE subscripts
809 |
810 | #Run main window as a child process if command-line flags were used above for a subscript. This prevents subsequent subscripts from reading undesired cli values.
811 | if [ ! -z "$1" ];then
812 | back
813 | fi
814 |
815 | #if rclone drive mounted anywhere, display button to unmount drives
816 | if [ ! -z "$(mount | grep fuse.rclone)" ] && [ "$usefuse" == 1 ];then
817 | unmountbutton=(--field="Unmount Drive"!"${DIRECTORY}/icons/eject.png"!"Unmount a cloud drive from your filesystem":FBTN "$0 unmountdrive")
818 | fi
819 |
820 | #if user has configured at least 1 drive, display these buttons:
821 | if [ ! -z "$remotes" ];then
822 | if [ "$usefuse" == 1 ];then
823 | mountbutton=(--field="Mount Drive"!"${DIRECTORY}/icons/mount.png"!"Connect a cloud drive to your computer like a USB drive":FBTN "$0 mountdrive")
824 | fi
825 | browsebutton=(--field="Browse Drive"!"${DIRECTORY}/icons/browse.png"!"A built-in file browser to create sharable links, upload files, download files, move files, or delete files.":FBTN "$0 browsedrive")
826 | removebutton=(--field="Remove Drive"!"${DIRECTORY}/icons/remove.png"!"Sign out of an existing cloud drive":FBTN "$0 removedrive")
827 | fi
828 |
829 | yad "${yadflags[@]}" --width=400 --form --columns=2 --no-buttons \
830 | --field="New Drive"!"${DIRECTORY}/icons/new.png"!"Connect to a new cloud drive":FBTN "$0 newdrive" \
831 | "${mountbutton[@]}" \
832 | "${browsebutton[@]}" \
833 | "${removebutton[@]}" \
834 | "${unmountbutton[@]}" \
835 | --field="Web Interface"!"${DIRECTORY}/icons/webinterface.png"!"Launch rclone's built-in browser interface.":FBTN "$0 webinterface" \
836 |
837 |
--------------------------------------------------------------------------------
/terminal-run:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #$1 is the command to be run.
4 | #$2 is the title.
5 |
6 | #set to 1 to display which terminal is being used
7 | DEBUG=0
8 |
9 | if [ -f /usr/bin/lxterminal ];then
10 | lxterminal --title="$2" -e bash -c "$1"
11 | [ $DEBUG == 1 ] && echo lxterminal
12 |
13 | elif [ -f /usr/bin/xfce4-terminal ];then
14 | xfce4-terminal --title="$2" -x bash -c "$1"
15 | [ $DEBUG == 1 ] && echo xfce4-terminal
16 |
17 | elif [ -f /usr/bin/mate-terminal ];then
18 | #mate-terminal --title="$2" -e "bash -c "\""$1"\"""
19 | mate-terminal --title="$2" -x bash -c "$1"
20 | [ $DEBUG == 1 ] && echo mate-terminal
21 |
22 | elif [ -f /usr/bin/xterm ];then
23 | xterm -T "$2" -e bash -c "$1"
24 | [ $DEBUG == 1 ] && echo xterm
25 |
26 | elif [ -f /usr/bin/konsole ];then
27 | konsole -p tabtitle="$2" -e bash <(echo "$1")
28 | [ $DEBUG == 1 ] && echo konsole
29 |
30 | elif [ -f /usr/bin/terminator ];then
31 | terminator -T "$2" -x bash -c "$1"
32 | [ $DEBUG == 1 ] && echo terminator
33 |
34 | elif [ -f /usr/bin/gnome-terminal ];then
35 | gnome-terminal --title "$2" -x bash -c "$1"
36 | [ $DEBUG == 1 ] && echo gnome-terminal
37 |
38 | elif [ -f /usr/bin/qterminal ];then
39 | qterminal -e bash <(echo "$1")
40 | [ $DEBUG == 1 ] && echo qterminal
41 |
42 | elif [ -f /usr/bin/x-terminal-emulator ];then
43 | $(readlink -f /usr/bin/x-terminal-emulator) -e bash -c "$1"
44 | [ $DEBUG == 1 ] && echo x-terminal-emulator
45 |
46 | else
47 | echo "Failed to locate any terminal emulators!!!"
48 | exit 1
49 | fi
--------------------------------------------------------------------------------