rclone-api 1.5.45__py3-none-any.whl → 1.5.47__py3-none-any.whl
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.
- rclone_api/install.py +168 -100
- {rclone_api-1.5.45.dist-info → rclone_api-1.5.47.dist-info}/METADATA +1 -1
- {rclone_api-1.5.45.dist-info → rclone_api-1.5.47.dist-info}/RECORD +7 -7
- {rclone_api-1.5.45.dist-info → rclone_api-1.5.47.dist-info}/WHEEL +0 -0
- {rclone_api-1.5.45.dist-info → rclone_api-1.5.47.dist-info}/entry_points.txt +0 -0
- {rclone_api-1.5.45.dist-info → rclone_api-1.5.47.dist-info}/licenses/LICENSE +0 -0
- {rclone_api-1.5.45.dist-info → rclone_api-1.5.47.dist-info}/top_level.txt +0 -0
rclone_api/install.py
CHANGED
@@ -1,100 +1,168 @@
|
|
1
|
-
import logging
|
2
|
-
import os
|
3
|
-
import platform
|
4
|
-
import shutil
|
5
|
-
from pathlib import Path
|
6
|
-
from tempfile import TemporaryDirectory
|
7
|
-
from warnings import warn
|
8
|
-
|
9
|
-
from download import download
|
10
|
-
|
11
|
-
URL_WINDOWS = "https://downloads.rclone.org/rclone-current-windows-amd64.zip"
|
12
|
-
URL_LINUX = "https://downloads.rclone.org/rclone-current-linux-amd64.zip"
|
13
|
-
URL_MACOS_ARM = "https://downloads.rclone.org/rclone-current-osx-arm64.zip"
|
14
|
-
URL_MACOS_X86 = "https://downloads.rclone.org/rclone-current-osx-amd64.zip"
|
15
|
-
|
16
|
-
logger = logging.getLogger(__name__)
|
17
|
-
logging.basicConfig(level=logging.DEBUG)
|
18
|
-
|
19
|
-
|
20
|
-
def rclone_download_url() -> str:
|
21
|
-
system = platform.system()
|
22
|
-
arch = platform.machine()
|
23
|
-
if system == "Windows":
|
24
|
-
assert "arm" not in arch, f"Unsupported arch: {arch}"
|
25
|
-
return URL_WINDOWS
|
26
|
-
elif system == "Linux":
|
27
|
-
assert "arm" not in arch, f"Unsupported arch: {arch}"
|
28
|
-
return URL_LINUX
|
29
|
-
elif system == "Darwin":
|
30
|
-
if "x86" in arch:
|
31
|
-
return URL_MACOS_X86
|
32
|
-
elif "arm" in arch:
|
33
|
-
return URL_MACOS_ARM
|
34
|
-
else:
|
35
|
-
raise Exception(f"Unsupported arch: {arch}")
|
36
|
-
else:
|
37
|
-
raise Exception("Unsupported system")
|
38
|
-
|
39
|
-
|
40
|
-
def _remove_signed_binary_requirements(out: Path) -> None:
|
41
|
-
if platform.system() == "Windows":
|
42
|
-
return
|
43
|
-
# mac os
|
44
|
-
if platform.system() == "Darwin":
|
45
|
-
# remove signed binary requirements
|
46
|
-
#
|
47
|
-
# xattr -d com.apple.quarantine rclone
|
48
|
-
import subprocess
|
49
|
-
|
50
|
-
subprocess.run(
|
51
|
-
["xattr", "-d", "com.apple.quarantine", str(out)],
|
52
|
-
capture_output=True,
|
53
|
-
check=False,
|
54
|
-
)
|
55
|
-
return
|
56
|
-
|
57
|
-
|
58
|
-
def _make_executable(out: Path) -> None:
|
59
|
-
if platform.system() == "Windows":
|
60
|
-
return
|
61
|
-
# linux and mac os
|
62
|
-
os.chmod(out, 0o755)
|
63
|
-
|
64
|
-
|
65
|
-
def _find_rclone_exe(start: Path) -> Path | None:
|
66
|
-
for root, dirs, files in os.walk(start):
|
67
|
-
if platform.system() == "Windows":
|
68
|
-
if "rclone.exe" in files:
|
69
|
-
return Path(root) / "rclone.exe"
|
70
|
-
else:
|
71
|
-
if "rclone" in files:
|
72
|
-
return Path(root) / "rclone"
|
73
|
-
return None
|
74
|
-
|
75
|
-
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
if
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import platform
|
4
|
+
import shutil
|
5
|
+
from pathlib import Path
|
6
|
+
from tempfile import TemporaryDirectory
|
7
|
+
from warnings import warn
|
8
|
+
|
9
|
+
from download import download
|
10
|
+
|
11
|
+
URL_WINDOWS = "https://downloads.rclone.org/rclone-current-windows-amd64.zip"
|
12
|
+
URL_LINUX = "https://downloads.rclone.org/rclone-current-linux-amd64.zip"
|
13
|
+
URL_MACOS_ARM = "https://downloads.rclone.org/rclone-current-osx-arm64.zip"
|
14
|
+
URL_MACOS_X86 = "https://downloads.rclone.org/rclone-current-osx-amd64.zip"
|
15
|
+
|
16
|
+
logger = logging.getLogger(__name__)
|
17
|
+
logging.basicConfig(level=logging.DEBUG)
|
18
|
+
|
19
|
+
|
20
|
+
def rclone_download_url() -> str:
|
21
|
+
system = platform.system()
|
22
|
+
arch = platform.machine()
|
23
|
+
if system == "Windows":
|
24
|
+
assert "arm" not in arch, f"Unsupported arch: {arch}"
|
25
|
+
return URL_WINDOWS
|
26
|
+
elif system == "Linux":
|
27
|
+
assert "arm" not in arch, f"Unsupported arch: {arch}"
|
28
|
+
return URL_LINUX
|
29
|
+
elif system == "Darwin":
|
30
|
+
if "x86" in arch:
|
31
|
+
return URL_MACOS_X86
|
32
|
+
elif "arm" in arch:
|
33
|
+
return URL_MACOS_ARM
|
34
|
+
else:
|
35
|
+
raise Exception(f"Unsupported arch: {arch}")
|
36
|
+
else:
|
37
|
+
raise Exception("Unsupported system")
|
38
|
+
|
39
|
+
|
40
|
+
def _remove_signed_binary_requirements(out: Path) -> None:
|
41
|
+
if platform.system() == "Windows":
|
42
|
+
return
|
43
|
+
# mac os
|
44
|
+
if platform.system() == "Darwin":
|
45
|
+
# remove signed binary requirements
|
46
|
+
#
|
47
|
+
# xattr -d com.apple.quarantine rclone
|
48
|
+
import subprocess
|
49
|
+
|
50
|
+
subprocess.run(
|
51
|
+
["xattr", "-d", "com.apple.quarantine", str(out)],
|
52
|
+
capture_output=True,
|
53
|
+
check=False,
|
54
|
+
)
|
55
|
+
return
|
56
|
+
|
57
|
+
|
58
|
+
def _make_executable(out: Path) -> None:
|
59
|
+
if platform.system() == "Windows":
|
60
|
+
return
|
61
|
+
# linux and mac os
|
62
|
+
os.chmod(out, 0o755)
|
63
|
+
|
64
|
+
|
65
|
+
def _find_rclone_exe(start: Path) -> Path | None:
|
66
|
+
for root, dirs, files in os.walk(start):
|
67
|
+
if platform.system() == "Windows":
|
68
|
+
if "rclone.exe" in files:
|
69
|
+
return Path(root) / "rclone.exe"
|
70
|
+
else:
|
71
|
+
if "rclone" in files:
|
72
|
+
return Path(root) / "rclone"
|
73
|
+
return None
|
74
|
+
|
75
|
+
|
76
|
+
def _move_to_standard_linux_paths(exe_path: Path) -> None:
|
77
|
+
"""Move rclone to standard paths on Linux systems and create a symlink in the original location."""
|
78
|
+
if platform.system() != "Linux":
|
79
|
+
return
|
80
|
+
|
81
|
+
try:
|
82
|
+
# Try system-wide installation first
|
83
|
+
system_bin_path = Path("/usr/local/bin/rclone")
|
84
|
+
|
85
|
+
if os.access("/usr/local/bin", os.W_OK):
|
86
|
+
# Remove existing binary if it exists
|
87
|
+
if system_bin_path.exists():
|
88
|
+
if system_bin_path.is_symlink():
|
89
|
+
system_bin_path.unlink()
|
90
|
+
else:
|
91
|
+
os.remove(system_bin_path)
|
92
|
+
|
93
|
+
# Move the binary to standard path
|
94
|
+
shutil.move(str(exe_path), str(system_bin_path))
|
95
|
+
|
96
|
+
# Make it executable
|
97
|
+
os.chmod(system_bin_path, 0o755)
|
98
|
+
|
99
|
+
# Create a symlink in the original location
|
100
|
+
exe_path.symlink_to(system_bin_path)
|
101
|
+
|
102
|
+
logger.info(
|
103
|
+
f"Moved rclone to {system_bin_path} and created symlink at {exe_path}"
|
104
|
+
)
|
105
|
+
else:
|
106
|
+
# Fall back to user's home directory if no root access
|
107
|
+
home_bin_dir = Path.home() / ".local" / "bin"
|
108
|
+
home_bin_dir.mkdir(parents=True, exist_ok=True)
|
109
|
+
home_bin_path = home_bin_dir / "rclone"
|
110
|
+
|
111
|
+
# Remove existing binary if it exists
|
112
|
+
if home_bin_path.exists():
|
113
|
+
if home_bin_path.is_symlink():
|
114
|
+
home_bin_path.unlink()
|
115
|
+
else:
|
116
|
+
os.remove(home_bin_path)
|
117
|
+
|
118
|
+
# Move the binary to user's bin directory
|
119
|
+
shutil.move(str(exe_path), str(home_bin_path))
|
120
|
+
|
121
|
+
# Make it executable
|
122
|
+
os.chmod(home_bin_path, 0o755)
|
123
|
+
|
124
|
+
# Create a symlink in the original location
|
125
|
+
exe_path.symlink_to(home_bin_path)
|
126
|
+
|
127
|
+
logger.info(
|
128
|
+
f"Moved rclone to {home_bin_path} and created symlink at {exe_path}"
|
129
|
+
)
|
130
|
+
|
131
|
+
# Check if ~/.local/bin is in PATH, if not suggest adding it
|
132
|
+
if str(home_bin_dir) not in os.environ.get("PATH", ""):
|
133
|
+
logger.warning(
|
134
|
+
f"{home_bin_dir} is not in PATH. Consider adding it to your shell profile."
|
135
|
+
)
|
136
|
+
except Exception as e:
|
137
|
+
logger.warning(f"Failed to move rclone to standard paths: {e}")
|
138
|
+
|
139
|
+
|
140
|
+
def rclone_download(out: Path, replace=False) -> Exception | None:
|
141
|
+
if out.exists() and not replace:
|
142
|
+
return None
|
143
|
+
try:
|
144
|
+
url = rclone_download_url()
|
145
|
+
with TemporaryDirectory() as tmpdir:
|
146
|
+
tmp = Path(tmpdir)
|
147
|
+
logging.info(f"Downloading rclone from {url} to {tmp.absolute()}")
|
148
|
+
download(url, tmp, kind="zip", replace=True)
|
149
|
+
exe = _find_rclone_exe(tmp)
|
150
|
+
if exe is None:
|
151
|
+
raise FileNotFoundError("rclone executable not found")
|
152
|
+
if os.path.exists(out):
|
153
|
+
os.remove(out)
|
154
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
155
|
+
shutil.move(exe, out)
|
156
|
+
_remove_signed_binary_requirements(out)
|
157
|
+
_make_executable(out)
|
158
|
+
|
159
|
+
# Move to standard paths on Linux
|
160
|
+
_move_to_standard_linux_paths(out)
|
161
|
+
|
162
|
+
return None
|
163
|
+
except Exception as e:
|
164
|
+
import traceback
|
165
|
+
|
166
|
+
stacktrace = traceback.format_exc()
|
167
|
+
warn(f"Failed to download rclone: {e}\n{stacktrace}")
|
168
|
+
return e
|
@@ -16,7 +16,7 @@ rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
|
|
16
16
|
rclone_api/fs.py,sha256=xUkoSV2R9dByKgueZ3x2GjyCn9fM7tK1ydgK1Whiph0,8933
|
17
17
|
rclone_api/group_files.py,sha256=H92xPW9lQnbNw5KbtZCl00bD6iRh9yRbCuxku4j_3dg,8036
|
18
18
|
rclone_api/http_server.py,sha256=P-LT2GqCEM9tGbyzzr-CrSOc3FyCw1qXUSQ5bY2KBHU,12113
|
19
|
-
rclone_api/install.py,sha256=
|
19
|
+
rclone_api/install.py,sha256=ZDG8QNj1JciS_DSqYnMTECwhJksUPAoqZQxtX804TDk,5679
|
20
20
|
rclone_api/log.py,sha256=VZHM7pNSXip2ZLBKMP7M1u-rp_F7zoafFDuR8CPUoKI,1271
|
21
21
|
rclone_api/mount.py,sha256=LZqEhuKZunbWVqmsOIqkkCotaxWJpdFRS1InXveoU5E,1428
|
22
22
|
rclone_api/mount_util.py,sha256=jqhJEVTHV6c6lOOzUYb4FLMbqDMHdz7-QRcdH-IobFc,10154
|
@@ -55,9 +55,9 @@ rclone_api/s3/multipart/upload_parts_inline.py,sha256=V7syKjFyVIe4U9Ahl5XgqVTzt9
|
|
55
55
|
rclone_api/s3/multipart/upload_parts_resumable.py,sha256=6-nlMclS8jyVvMvFbQDcZOX9MY1WbCcKA_s9bwuYxnk,9793
|
56
56
|
rclone_api/s3/multipart/upload_parts_server_side_merge.py,sha256=Fp2pdrs5dONQI9LkfNolgAGj1-Z2V1SsRd0r0sreuXI,18040
|
57
57
|
rclone_api/s3/multipart/upload_state.py,sha256=f-Aq2NqtAaMUMhYitlICSNIxCKurWAl2gDEUVizLIqw,6019
|
58
|
-
rclone_api-1.5.
|
59
|
-
rclone_api-1.5.
|
60
|
-
rclone_api-1.5.
|
61
|
-
rclone_api-1.5.
|
62
|
-
rclone_api-1.5.
|
63
|
-
rclone_api-1.5.
|
58
|
+
rclone_api-1.5.47.dist-info/licenses/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
59
|
+
rclone_api-1.5.47.dist-info/METADATA,sha256=UZGLiUY8lZA3ToCEOlD_oo1YtUONlTkAt16OMk9Dzv8,37305
|
60
|
+
rclone_api-1.5.47.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
61
|
+
rclone_api-1.5.47.dist-info/entry_points.txt,sha256=ognh2e11HTjn73_KL5MWI67pBKS2jekBi-QTiRXySXA,316
|
62
|
+
rclone_api-1.5.47.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
|
63
|
+
rclone_api-1.5.47.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|