cloudhop 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cloudhop-0.6.0/LICENSE +21 -0
- cloudhop-0.6.0/PKG-INFO +100 -0
- cloudhop-0.6.0/README.md +82 -0
- cloudhop-0.6.0/cloudhop/__init__.py +2 -0
- cloudhop-0.6.0/cloudhop/__main__.py +4 -0
- cloudhop-0.6.0/cloudhop/cli.py +200 -0
- cloudhop-0.6.0/cloudhop/server.py +285 -0
- cloudhop-0.6.0/cloudhop/static/dashboard.css +630 -0
- cloudhop-0.6.0/cloudhop/static/dashboard.js +872 -0
- cloudhop-0.6.0/cloudhop/static/favicon.svg +4 -0
- cloudhop-0.6.0/cloudhop/static/wizard.css +247 -0
- cloudhop-0.6.0/cloudhop/static/wizard.js +604 -0
- cloudhop-0.6.0/cloudhop/templates/__init__.py +22 -0
- cloudhop-0.6.0/cloudhop/templates/dashboard.html +268 -0
- cloudhop-0.6.0/cloudhop/templates/wizard.html +300 -0
- cloudhop-0.6.0/cloudhop/tests/__init__.py +0 -0
- cloudhop-0.6.0/cloudhop/tests/conftest.py +1 -0
- cloudhop-0.6.0/cloudhop/tests/test_server.py +208 -0
- cloudhop-0.6.0/cloudhop/tests/test_transfer.py +953 -0
- cloudhop-0.6.0/cloudhop/tests/test_utils.py +539 -0
- cloudhop-0.6.0/cloudhop/transfer.py +1508 -0
- cloudhop-0.6.0/cloudhop/utils.py +257 -0
- cloudhop-0.6.0/cloudhop.egg-info/PKG-INFO +100 -0
- cloudhop-0.6.0/cloudhop.egg-info/SOURCES.txt +27 -0
- cloudhop-0.6.0/cloudhop.egg-info/dependency_links.txt +1 -0
- cloudhop-0.6.0/cloudhop.egg-info/entry_points.txt +2 -0
- cloudhop-0.6.0/cloudhop.egg-info/top_level.txt +1 -0
- cloudhop-0.6.0/pyproject.toml +33 -0
- cloudhop-0.6.0/setup.cfg +4 -0
cloudhop-0.6.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Husam Soboh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cloudhop-0.6.0/PKG-INFO
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cloudhop
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: The easiest way to copy files between cloud storage services
|
|
5
|
+
Author: Husam Soboh
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/husamsoboh-cyber/cloudhop
|
|
8
|
+
Project-URL: Issues, https://github.com/husamsoboh-cyber/cloudhop/issues
|
|
9
|
+
Keywords: cloud,file-transfer,rclone,google-drive,onedrive,dropbox,icloud
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
[](https://www.python.org/downloads/)
|
|
20
|
+
[](LICENSE)
|
|
21
|
+
[](https://pypi.org/project/cloudhop/)
|
|
22
|
+
[](https://github.com/husamsoboh-cyber/cloudhop)
|
|
23
|
+
|
|
24
|
+
# CloudHop - Free Cloud File Transfer
|
|
25
|
+
|
|
26
|
+
**Switching cloud providers? CloudHop copies everything for you. Free, open source, runs on your machine.**
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
## Download / Install
|
|
31
|
+
|
|
32
|
+
**Mac** - Download `CloudHop.dmg` from [Releases](https://github.com/husamsoboh-cyber/cloudhop/releases)
|
|
33
|
+
|
|
34
|
+
**Windows** - Download `CloudHop-Setup.exe` from [Releases](https://github.com/husamsoboh-cyber/cloudhop/releases)
|
|
35
|
+
|
|
36
|
+
**pip**
|
|
37
|
+
```bash
|
|
38
|
+
pip install cloudhop && cloudhop
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**From source**
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/husamsoboh-cyber/cloudhop && cd cloudhop && pip install -e . && cloudhop
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Why CloudHop?
|
|
47
|
+
|
|
48
|
+
- **Free and open source** -- no limits, no account needed
|
|
49
|
+
- **Runs on your machine** -- files never touch our servers
|
|
50
|
+
- **Works with 70+ cloud providers** -- Google Drive, OneDrive, Dropbox, iCloud, MEGA, S3, Proton Drive...
|
|
51
|
+
- **Visual wizard** -- no command line needed
|
|
52
|
+
- **Pause and resume** across restarts
|
|
53
|
+
|
|
54
|
+
## How is this different from...
|
|
55
|
+
|
|
56
|
+
**rclone?**
|
|
57
|
+
CloudHop uses rclone as its engine. If you're comfortable with CLI, you don't need this. CloudHop adds a visual wizard and live dashboard.
|
|
58
|
+
|
|
59
|
+
**MultCloud / CloudFuze?**
|
|
60
|
+
Those are paid SaaS that route files through their servers. CloudHop is free and your files transfer directly between providers.
|
|
61
|
+
|
|
62
|
+
**Download and re-upload?**
|
|
63
|
+
That requires local disk space and 2x transfer time. CloudHop uses server-side copy where supported.
|
|
64
|
+
|
|
65
|
+
## How it works
|
|
66
|
+
|
|
67
|
+
1. **Run CloudHop** -- launch the app or run `cloudhop` in a terminal
|
|
68
|
+
2. **Pick source** -- choose where your files are (e.g., OneDrive)
|
|
69
|
+
3. **Pick destination** -- choose where to copy them (e.g., Google Drive)
|
|
70
|
+
4. **Configure options** -- set parallel transfers, exclude folders, limit bandwidth
|
|
71
|
+
5. **Connect accounts** -- authorize each cloud provider in your browser
|
|
72
|
+
6. **Start transfer** -- watch progress in the live dashboard with speed charts and ETA
|
|
73
|
+
|
|
74
|
+
## CLI Usage
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
cloudhop source: dest: [--transfers=8] [--bwlimit=10M] [--exclude="*.tmp"]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Supported Providers
|
|
81
|
+
|
|
82
|
+
Google Drive, OneDrive, Dropbox, iCloud Drive, MEGA, Amazon S3, Proton Drive, Local Folder + 70 more via rclone
|
|
83
|
+
|
|
84
|
+
## Links
|
|
85
|
+
|
|
86
|
+
[Security](SECURITY.md) | [Contributing](CONTRIBUTING.md) | [Changelog](CHANGELOG.md)
|
|
87
|
+
|
|
88
|
+
## Built by
|
|
89
|
+
|
|
90
|
+
Built by an orthodontist who needed to move 500GB between cloud services and found no free, simple tool to do it.
|
|
91
|
+
|
|
92
|
+
## Donate
|
|
93
|
+
|
|
94
|
+
If CloudHop saved you time, consider supporting development:
|
|
95
|
+
|
|
96
|
+
[GitHub Sponsors](https://github.com/sponsors/husamsoboh-cyber) | [Buy Me a Coffee](https://buymeacoffee.com/husamsoboh)
|
|
97
|
+
|
|
98
|
+
## License
|
|
99
|
+
|
|
100
|
+
MIT License -- see [LICENSE](LICENSE) for details.
|
cloudhop-0.6.0/README.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
[](https://www.python.org/downloads/)
|
|
2
|
+
[](LICENSE)
|
|
3
|
+
[](https://pypi.org/project/cloudhop/)
|
|
4
|
+
[](https://github.com/husamsoboh-cyber/cloudhop)
|
|
5
|
+
|
|
6
|
+
# CloudHop - Free Cloud File Transfer
|
|
7
|
+
|
|
8
|
+
**Switching cloud providers? CloudHop copies everything for you. Free, open source, runs on your machine.**
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
## Download / Install
|
|
13
|
+
|
|
14
|
+
**Mac** - Download `CloudHop.dmg` from [Releases](https://github.com/husamsoboh-cyber/cloudhop/releases)
|
|
15
|
+
|
|
16
|
+
**Windows** - Download `CloudHop-Setup.exe` from [Releases](https://github.com/husamsoboh-cyber/cloudhop/releases)
|
|
17
|
+
|
|
18
|
+
**pip**
|
|
19
|
+
```bash
|
|
20
|
+
pip install cloudhop && cloudhop
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**From source**
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/husamsoboh-cyber/cloudhop && cd cloudhop && pip install -e . && cloudhop
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Why CloudHop?
|
|
29
|
+
|
|
30
|
+
- **Free and open source** -- no limits, no account needed
|
|
31
|
+
- **Runs on your machine** -- files never touch our servers
|
|
32
|
+
- **Works with 70+ cloud providers** -- Google Drive, OneDrive, Dropbox, iCloud, MEGA, S3, Proton Drive...
|
|
33
|
+
- **Visual wizard** -- no command line needed
|
|
34
|
+
- **Pause and resume** across restarts
|
|
35
|
+
|
|
36
|
+
## How is this different from...
|
|
37
|
+
|
|
38
|
+
**rclone?**
|
|
39
|
+
CloudHop uses rclone as its engine. If you're comfortable with CLI, you don't need this. CloudHop adds a visual wizard and live dashboard.
|
|
40
|
+
|
|
41
|
+
**MultCloud / CloudFuze?**
|
|
42
|
+
Those are paid SaaS that route files through their servers. CloudHop is free and your files transfer directly between providers.
|
|
43
|
+
|
|
44
|
+
**Download and re-upload?**
|
|
45
|
+
That requires local disk space and 2x transfer time. CloudHop uses server-side copy where supported.
|
|
46
|
+
|
|
47
|
+
## How it works
|
|
48
|
+
|
|
49
|
+
1. **Run CloudHop** -- launch the app or run `cloudhop` in a terminal
|
|
50
|
+
2. **Pick source** -- choose where your files are (e.g., OneDrive)
|
|
51
|
+
3. **Pick destination** -- choose where to copy them (e.g., Google Drive)
|
|
52
|
+
4. **Configure options** -- set parallel transfers, exclude folders, limit bandwidth
|
|
53
|
+
5. **Connect accounts** -- authorize each cloud provider in your browser
|
|
54
|
+
6. **Start transfer** -- watch progress in the live dashboard with speed charts and ETA
|
|
55
|
+
|
|
56
|
+
## CLI Usage
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
cloudhop source: dest: [--transfers=8] [--bwlimit=10M] [--exclude="*.tmp"]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Supported Providers
|
|
63
|
+
|
|
64
|
+
Google Drive, OneDrive, Dropbox, iCloud Drive, MEGA, Amazon S3, Proton Drive, Local Folder + 70 more via rclone
|
|
65
|
+
|
|
66
|
+
## Links
|
|
67
|
+
|
|
68
|
+
[Security](SECURITY.md) | [Contributing](CONTRIBUTING.md) | [Changelog](CHANGELOG.md)
|
|
69
|
+
|
|
70
|
+
## Built by
|
|
71
|
+
|
|
72
|
+
Built by an orthodontist who needed to move 500GB between cloud services and found no free, simple tool to do it.
|
|
73
|
+
|
|
74
|
+
## Donate
|
|
75
|
+
|
|
76
|
+
If CloudHop saved you time, consider supporting development:
|
|
77
|
+
|
|
78
|
+
[GitHub Sponsors](https://github.com/sponsors/husamsoboh-cyber) | [Buy Me a Coffee](https://buymeacoffee.com/husamsoboh)
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
MIT License -- see [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""CloudHop CLI entry point."""
|
|
2
|
+
import sys
|
|
3
|
+
import signal
|
|
4
|
+
import platform
|
|
5
|
+
import webbrowser
|
|
6
|
+
import threading
|
|
7
|
+
import http.server
|
|
8
|
+
from typing import Any, List
|
|
9
|
+
|
|
10
|
+
from .server import CloudHopHandler
|
|
11
|
+
from .transfer import TransferManager, ensure_rclone
|
|
12
|
+
from .utils import PORT
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main() -> None:
|
|
16
|
+
"""Main entry point -- wizard mode (no args) or CLI mode (source dest [flags])."""
|
|
17
|
+
args = sys.argv[1:]
|
|
18
|
+
manager = TransferManager()
|
|
19
|
+
CloudHopHandler.manager = manager
|
|
20
|
+
|
|
21
|
+
signal.signal(signal.SIGTERM, lambda signum, frame: _signal_handler(manager))
|
|
22
|
+
|
|
23
|
+
if len(args) == 0:
|
|
24
|
+
# Web wizard mode
|
|
25
|
+
print()
|
|
26
|
+
print(" +==================================================+")
|
|
27
|
+
print(" | Welcome to CloudHop |")
|
|
28
|
+
print(" | |")
|
|
29
|
+
print(" | Starting web setup wizard... |")
|
|
30
|
+
print(" +==================================================+")
|
|
31
|
+
if platform.system() == "Windows":
|
|
32
|
+
print(" Note: Some features (pause/resume) may not work on Windows.")
|
|
33
|
+
print(" For best results, use macOS or Linux.")
|
|
34
|
+
print()
|
|
35
|
+
start_dashboard(manager, start_rclone=False)
|
|
36
|
+
else:
|
|
37
|
+
# CLI mode for advanced users
|
|
38
|
+
ensure_rclone()
|
|
39
|
+
parse_cli_args(manager, args)
|
|
40
|
+
print()
|
|
41
|
+
if manager.rclone_pid and not manager.rclone_cmd:
|
|
42
|
+
# Attach mode - monitoring existing process
|
|
43
|
+
print(f" CloudHop - Monitoring PID {manager.rclone_pid}")
|
|
44
|
+
print(f" Log: {manager.log_file}")
|
|
45
|
+
start_dashboard(manager, start_rclone=False)
|
|
46
|
+
else:
|
|
47
|
+
print(" CloudHop - Advanced Mode")
|
|
48
|
+
print(f" Command: {' '.join(manager.rclone_cmd)}")
|
|
49
|
+
start_dashboard(manager, start_rclone=True)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def start_dashboard(manager: TransferManager, start_rclone: bool = False) -> None:
|
|
53
|
+
"""Start the web dashboard and optionally the rclone process."""
|
|
54
|
+
import subprocess
|
|
55
|
+
|
|
56
|
+
# Load RCLONE_CMD from state if not set (enables resume after restart)
|
|
57
|
+
if not manager.rclone_cmd and "rclone_cmd" in manager.state:
|
|
58
|
+
manager.rclone_cmd = manager.state["rclone_cmd"]
|
|
59
|
+
# Restore LOG_FILE from the saved command
|
|
60
|
+
for arg in manager.rclone_cmd:
|
|
61
|
+
if arg.startswith("--log-file="):
|
|
62
|
+
manager.log_file = arg.split("=", 1)[1]
|
|
63
|
+
break
|
|
64
|
+
|
|
65
|
+
# Initial full log scan
|
|
66
|
+
print()
|
|
67
|
+
print(" Scanning transfer log...")
|
|
68
|
+
manager.scan_full_log()
|
|
69
|
+
session_count = len(manager.state.get("sessions", []))
|
|
70
|
+
if session_count > 0:
|
|
71
|
+
print(f" Found {session_count} previous session(s)")
|
|
72
|
+
|
|
73
|
+
# Start background scanner
|
|
74
|
+
scanner = threading.Thread(target=manager.background_scanner, daemon=True)
|
|
75
|
+
scanner.start()
|
|
76
|
+
|
|
77
|
+
# Start rclone if requested
|
|
78
|
+
if start_rclone and manager.rclone_cmd:
|
|
79
|
+
print(" Starting file transfer...")
|
|
80
|
+
proc = subprocess.Popen(
|
|
81
|
+
manager.rclone_cmd,
|
|
82
|
+
stdout=subprocess.DEVNULL,
|
|
83
|
+
stderr=subprocess.DEVNULL,
|
|
84
|
+
start_new_session=True,
|
|
85
|
+
)
|
|
86
|
+
manager.rclone_pid = proc.pid
|
|
87
|
+
print(f" Transfer started (PID {proc.pid})")
|
|
88
|
+
manager.transfer_active = True
|
|
89
|
+
|
|
90
|
+
port = PORT
|
|
91
|
+
|
|
92
|
+
print()
|
|
93
|
+
print(f" CloudHop: http://localhost:{port}")
|
|
94
|
+
print()
|
|
95
|
+
if manager.transfer_active:
|
|
96
|
+
print(" Open the link above in your browser to monitor progress.")
|
|
97
|
+
else:
|
|
98
|
+
print(" Open the link above in your browser to start the setup wizard.")
|
|
99
|
+
print(" Press Ctrl+C to stop the server.")
|
|
100
|
+
print()
|
|
101
|
+
|
|
102
|
+
server = None
|
|
103
|
+
for try_port in range(port, port + 5):
|
|
104
|
+
try:
|
|
105
|
+
server = http.server.ThreadingHTTPServer(("127.0.0.1", try_port), CloudHopHandler)
|
|
106
|
+
if try_port != port:
|
|
107
|
+
print(f" Port {port} was busy, using port {try_port} instead.")
|
|
108
|
+
port = try_port
|
|
109
|
+
CloudHopHandler.actual_port = port
|
|
110
|
+
break
|
|
111
|
+
except OSError as e:
|
|
112
|
+
if ("Address already in use" in str(e) or e.errno == 48) and try_port < port + 4:
|
|
113
|
+
continue
|
|
114
|
+
if "Address already in use" in str(e) or e.errno == 48:
|
|
115
|
+
print(f"\n Error: Ports {port}-{port + 4} are all in use.")
|
|
116
|
+
print(" Please stop the other process(es) and try again.\n")
|
|
117
|
+
sys.exit(1)
|
|
118
|
+
raise
|
|
119
|
+
|
|
120
|
+
if server is None:
|
|
121
|
+
print(f"\n Error: Could not bind to any port in range {port}-{port + 4}.\n")
|
|
122
|
+
sys.exit(1)
|
|
123
|
+
|
|
124
|
+
# Try to open browser automatically (after port binding succeeds)
|
|
125
|
+
try:
|
|
126
|
+
webbrowser.open(f"http://localhost:{port}")
|
|
127
|
+
except Exception:
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
server.serve_forever()
|
|
132
|
+
except KeyboardInterrupt:
|
|
133
|
+
_signal_handler(manager)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def parse_cli_args(manager: TransferManager, args: List[str]) -> None:
|
|
137
|
+
"""Parse CLI arguments for advanced usage: cloudhop source: dest: [flags]"""
|
|
138
|
+
source = None
|
|
139
|
+
dest = None
|
|
140
|
+
extra_flags = []
|
|
141
|
+
attach_pid = None
|
|
142
|
+
attach_log = None
|
|
143
|
+
|
|
144
|
+
for arg in args:
|
|
145
|
+
if arg.startswith("--attach-pid="):
|
|
146
|
+
try:
|
|
147
|
+
attach_pid = int(arg.split("=", 1)[1])
|
|
148
|
+
except ValueError:
|
|
149
|
+
print(f" Error: --attach-pid requires a numeric PID")
|
|
150
|
+
sys.exit(1)
|
|
151
|
+
elif arg.startswith("--attach-log="):
|
|
152
|
+
attach_log = arg.split("=", 1)[1]
|
|
153
|
+
elif arg.startswith("--"):
|
|
154
|
+
extra_flags.append(arg)
|
|
155
|
+
elif source is None:
|
|
156
|
+
source = arg
|
|
157
|
+
elif dest is None:
|
|
158
|
+
dest = arg
|
|
159
|
+
else:
|
|
160
|
+
extra_flags.append(arg)
|
|
161
|
+
|
|
162
|
+
if not source or not dest:
|
|
163
|
+
print("Usage: cloudhop <source> <destination> [--flags]")
|
|
164
|
+
print("Example: cloudhop onedrive: gdrive:backup --transfers=8")
|
|
165
|
+
print()
|
|
166
|
+
print("Or just run without arguments for the interactive wizard:")
|
|
167
|
+
print(" cloudhop")
|
|
168
|
+
sys.exit(1)
|
|
169
|
+
|
|
170
|
+
manager.set_transfer_paths(source, dest)
|
|
171
|
+
|
|
172
|
+
# Attach to an existing rclone process instead of starting a new one
|
|
173
|
+
if attach_pid:
|
|
174
|
+
manager.rclone_pid = attach_pid
|
|
175
|
+
manager.transfer_active = True
|
|
176
|
+
if attach_log:
|
|
177
|
+
manager.log_file = attach_log
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
manager.rclone_cmd = [
|
|
181
|
+
"rclone", "copy", source, dest,
|
|
182
|
+
f"--log-file={manager.log_file}",
|
|
183
|
+
"--log-level=INFO",
|
|
184
|
+
"--stats=10s",
|
|
185
|
+
"--stats-log-level=INFO",
|
|
186
|
+
] + extra_flags
|
|
187
|
+
|
|
188
|
+
# Add default transfers if not specified
|
|
189
|
+
if not any(f.startswith("--transfers") for f in extra_flags):
|
|
190
|
+
manager.rclone_cmd.append("--transfers=8")
|
|
191
|
+
if not any(f.startswith("--checkers") for f in extra_flags):
|
|
192
|
+
manager.rclone_cmd.append("--checkers=16")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _signal_handler(manager: TransferManager) -> None:
|
|
196
|
+
print("\n CloudHop stopped.")
|
|
197
|
+
if manager.transfer_active:
|
|
198
|
+
print(" (The file transfer continues in the background)")
|
|
199
|
+
print()
|
|
200
|
+
sys.exit(0)
|