rclone-api 1.0.14__py2.py3-none-any.whl → 1.0.17__py2.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/__init__.py +13 -1
- rclone_api/exec.py +15 -2
- rclone_api/file.py +1 -1
- rclone_api/filelist.py +30 -0
- rclone_api/process.py +124 -0
- rclone_api/rclone.py +72 -13
- rclone_api/util.py +4 -3
- rclone_api-1.0.17.dist-info/METADATA +149 -0
- rclone_api-1.0.17.dist-info/RECORD +21 -0
- rclone_api-1.0.14.dist-info/METADATA +0 -34
- rclone_api-1.0.14.dist-info/RECORD +0 -19
- {rclone_api-1.0.14.dist-info → rclone_api-1.0.17.dist-info}/LICENSE +0 -0
- {rclone_api-1.0.14.dist-info → rclone_api-1.0.17.dist-info}/WHEEL +0 -0
- {rclone_api-1.0.14.dist-info → rclone_api-1.0.17.dist-info}/top_level.txt +0 -0
rclone_api/__init__.py
CHANGED
@@ -2,8 +2,20 @@ from .config import Config
|
|
2
2
|
from .dir import Dir
|
3
3
|
from .dir_listing import DirListing
|
4
4
|
from .file import File
|
5
|
+
from .filelist import FileList
|
6
|
+
from .process import Process
|
5
7
|
from .rclone import Rclone
|
6
8
|
from .remote import Remote
|
7
9
|
from .rpath import RPath
|
8
10
|
|
9
|
-
__all__ = [
|
11
|
+
__all__ = [
|
12
|
+
"Rclone",
|
13
|
+
"File",
|
14
|
+
"Config",
|
15
|
+
"Remote",
|
16
|
+
"Dir",
|
17
|
+
"RPath",
|
18
|
+
"DirListing",
|
19
|
+
"FileList",
|
20
|
+
"Process",
|
21
|
+
]
|
rclone_api/exec.py
CHANGED
@@ -3,6 +3,7 @@ from dataclasses import dataclass
|
|
3
3
|
from pathlib import Path
|
4
4
|
|
5
5
|
from rclone_api.config import Config
|
6
|
+
from rclone_api.process import Process, ProcessArgs
|
6
7
|
|
7
8
|
|
8
9
|
@dataclass
|
@@ -12,8 +13,20 @@ class RcloneExec:
|
|
12
13
|
rclone_config: Path | Config
|
13
14
|
rclone_exe: Path
|
14
15
|
|
15
|
-
def execute(self, cmd: list[str]) -> subprocess.CompletedProcess:
|
16
|
+
def execute(self, cmd: list[str], check: bool) -> subprocess.CompletedProcess:
|
16
17
|
"""Execute rclone command."""
|
17
18
|
from rclone_api.util import rclone_execute
|
18
19
|
|
19
|
-
return rclone_execute(cmd, self.rclone_config, self.rclone_exe)
|
20
|
+
return rclone_execute(cmd, self.rclone_config, self.rclone_exe, check=check)
|
21
|
+
|
22
|
+
def launch_process(self, cmd: list[str]) -> Process:
|
23
|
+
"""Launch rclone process."""
|
24
|
+
|
25
|
+
args: ProcessArgs = ProcessArgs(
|
26
|
+
cmd=cmd,
|
27
|
+
rclone_conf=self.rclone_config,
|
28
|
+
rclone_exe=self.rclone_exe,
|
29
|
+
cmd_list=cmd,
|
30
|
+
)
|
31
|
+
process = Process(args)
|
32
|
+
return process
|
rclone_api/file.py
CHANGED
@@ -31,7 +31,7 @@ class File:
|
|
31
31
|
if self.path.is_dir:
|
32
32
|
raise RuntimeError("Cannot read a directory as bytes")
|
33
33
|
|
34
|
-
result = self.path.rclone._run(["cat", self.path.path])
|
34
|
+
result = self.path.rclone._run(["cat", self.path.path], check=True)
|
35
35
|
return result.stdout
|
36
36
|
|
37
37
|
def to_json(self) -> dict:
|
rclone_api/filelist.py
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
|
3
|
+
from rclone_api.dir import Dir
|
4
|
+
from rclone_api.dir_listing import DirListing
|
5
|
+
from rclone_api.file import File
|
6
|
+
from rclone_api.rpath import RPath
|
7
|
+
|
8
|
+
|
9
|
+
@dataclass
|
10
|
+
class FileList:
|
11
|
+
"""Remote file dataclass."""
|
12
|
+
|
13
|
+
dirs: list[Dir]
|
14
|
+
files: list[File]
|
15
|
+
|
16
|
+
def _to_dir_list(self) -> list[RPath]:
|
17
|
+
pathlist: list[RPath] = []
|
18
|
+
for d in self.dirs:
|
19
|
+
pathlist.append(d.path)
|
20
|
+
for f in self.files:
|
21
|
+
pathlist.append(f.path)
|
22
|
+
return pathlist
|
23
|
+
|
24
|
+
def __str__(self) -> str:
|
25
|
+
pathlist: list[RPath] = self._to_dir_list()
|
26
|
+
return str(DirListing(pathlist))
|
27
|
+
|
28
|
+
def __repr__(self) -> str:
|
29
|
+
pathlist: list[RPath] = self._to_dir_list()
|
30
|
+
return repr(DirListing(pathlist))
|
rclone_api/process.py
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
import os
|
2
|
+
import subprocess
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from pathlib import Path
|
5
|
+
from tempfile import TemporaryDirectory
|
6
|
+
from typing import Any
|
7
|
+
|
8
|
+
from rclone_api.config import Config
|
9
|
+
from rclone_api.util import get_verbose
|
10
|
+
|
11
|
+
# def rclone_launch_process(
|
12
|
+
# cmd: list[str],
|
13
|
+
# rclone_conf: Path | Config,
|
14
|
+
# rclone_exe: Path,
|
15
|
+
# verbose: bool | None = None,
|
16
|
+
# ) -> subprocess.Popen:
|
17
|
+
# tempdir: TemporaryDirectory | None = None
|
18
|
+
# verbose = _get_verbose(verbose)
|
19
|
+
# assert verbose is not None
|
20
|
+
|
21
|
+
# try:
|
22
|
+
# if isinstance(rclone_conf, Config):
|
23
|
+
# tempdir = TemporaryDirectory()
|
24
|
+
# tmpfile = Path(tempdir.name) / "rclone.conf"
|
25
|
+
# tmpfile.write_text(rclone_conf.text, encoding="utf-8")
|
26
|
+
# rclone_conf = tmpfile
|
27
|
+
# cmd = (
|
28
|
+
# [str(rclone_exe.resolve())] + ["--config", str(rclone_conf.resolve())] + cmd
|
29
|
+
# )
|
30
|
+
# if verbose:
|
31
|
+
# cmd_str = subprocess.list2cmdline(cmd)
|
32
|
+
# print(f"Running: {cmd_str}")
|
33
|
+
# cp = subprocess.Popen(
|
34
|
+
# cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False
|
35
|
+
# )
|
36
|
+
# return cp
|
37
|
+
# finally:
|
38
|
+
# if tempdir:
|
39
|
+
# try:
|
40
|
+
# tempdir.cleanup()
|
41
|
+
# except Exception as e:
|
42
|
+
# print(f"Error cleaning up tempdir: {e}")
|
43
|
+
|
44
|
+
|
45
|
+
def _get_verbose(verbose: bool | None) -> bool:
|
46
|
+
if verbose is not None:
|
47
|
+
return verbose
|
48
|
+
# get it from the environment
|
49
|
+
return bool(int(os.getenv("RCLONE_API_VERBOSE", "0")))
|
50
|
+
|
51
|
+
|
52
|
+
@dataclass
|
53
|
+
class ProcessArgs:
|
54
|
+
cmd: list[str]
|
55
|
+
rclone_conf: Path | Config
|
56
|
+
rclone_exe: Path
|
57
|
+
cmd_list: list[str]
|
58
|
+
verbose: bool | None = None
|
59
|
+
|
60
|
+
|
61
|
+
class Process:
|
62
|
+
def __init__(self, args: ProcessArgs) -> None:
|
63
|
+
assert args.rclone_exe.exists()
|
64
|
+
self.args = args
|
65
|
+
self.tempdir: TemporaryDirectory | None = None
|
66
|
+
verbose = get_verbose(args.verbose)
|
67
|
+
if isinstance(args.rclone_conf, Config):
|
68
|
+
self.tempdir = TemporaryDirectory()
|
69
|
+
tmpfile = Path(self.tempdir.name) / "rclone.conf"
|
70
|
+
tmpfile.write_text(args.rclone_conf.text, encoding="utf-8")
|
71
|
+
rclone_conf = tmpfile
|
72
|
+
else:
|
73
|
+
rclone_conf = args.rclone_conf
|
74
|
+
|
75
|
+
assert rclone_conf.exists()
|
76
|
+
|
77
|
+
self.cmd = (
|
78
|
+
[str(args.rclone_exe.resolve())]
|
79
|
+
+ ["--config", str(rclone_conf.resolve())]
|
80
|
+
+ args.cmd
|
81
|
+
)
|
82
|
+
if verbose:
|
83
|
+
cmd_str = subprocess.list2cmdline(self.cmd)
|
84
|
+
print(f"Running: {cmd_str}")
|
85
|
+
self.process = subprocess.Popen(self.cmd, shell=False)
|
86
|
+
|
87
|
+
def cleanup(self) -> None:
|
88
|
+
if self.tempdir:
|
89
|
+
try:
|
90
|
+
self.tempdir.cleanup()
|
91
|
+
except Exception as e:
|
92
|
+
print(f"Error cleaning up tempdir: {e}")
|
93
|
+
|
94
|
+
def __del__(self) -> None:
|
95
|
+
self.cleanup()
|
96
|
+
|
97
|
+
def kill(self) -> None:
|
98
|
+
self.cleanup()
|
99
|
+
return self.process.kill()
|
100
|
+
|
101
|
+
def terminate(self) -> None:
|
102
|
+
self.cleanup()
|
103
|
+
return self.process.terminate()
|
104
|
+
|
105
|
+
@property
|
106
|
+
def returncode(self) -> int | None:
|
107
|
+
return self.process.returncode
|
108
|
+
|
109
|
+
@property
|
110
|
+
def stdout(self) -> Any:
|
111
|
+
return self.process.stdout
|
112
|
+
|
113
|
+
@property
|
114
|
+
def stderr(self) -> Any:
|
115
|
+
return self.process.stderr
|
116
|
+
|
117
|
+
def poll(self) -> int | None:
|
118
|
+
return self.process.poll()
|
119
|
+
|
120
|
+
def wait(self) -> int:
|
121
|
+
return self.process.wait()
|
122
|
+
|
123
|
+
def send_signal(self, signal: int) -> None:
|
124
|
+
return self.process.send_signal(signal)
|
rclone_api/rclone.py
CHANGED
@@ -3,6 +3,7 @@ Unit test file.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import subprocess
|
6
|
+
import time
|
6
7
|
from concurrent.futures import ThreadPoolExecutor
|
7
8
|
from fnmatch import fnmatch
|
8
9
|
from pathlib import Path
|
@@ -14,6 +15,8 @@ from rclone_api.convert import convert_to_filestr_list, convert_to_str
|
|
14
15
|
from rclone_api.dir_listing import DirListing
|
15
16
|
from rclone_api.exec import RcloneExec
|
16
17
|
from rclone_api.file import File
|
18
|
+
from rclone_api.filelist import FileList
|
19
|
+
from rclone_api.process import Process
|
17
20
|
from rclone_api.remote import Remote
|
18
21
|
from rclone_api.rpath import RPath
|
19
22
|
from rclone_api.util import get_rclone_exe, to_path
|
@@ -29,8 +32,11 @@ class Rclone:
|
|
29
32
|
raise ValueError(f"Rclone config file not found: {rclone_conf}")
|
30
33
|
self._exec = RcloneExec(rclone_conf, get_rclone_exe(rclone_exe))
|
31
34
|
|
32
|
-
def _run(self, cmd: list[str]) -> subprocess.CompletedProcess:
|
33
|
-
return self._exec.execute(cmd)
|
35
|
+
def _run(self, cmd: list[str], check: bool = True) -> subprocess.CompletedProcess:
|
36
|
+
return self._exec.execute(cmd, check=check)
|
37
|
+
|
38
|
+
def _launch_process(self, cmd: list[str]) -> Process:
|
39
|
+
return self._exec.launch_process(cmd)
|
34
40
|
|
35
41
|
def ls(
|
36
42
|
self,
|
@@ -63,7 +69,7 @@ class Rclone:
|
|
63
69
|
remote = path.remote if isinstance(path, Dir) else path
|
64
70
|
assert isinstance(remote, Remote)
|
65
71
|
|
66
|
-
cp = self._run(cmd)
|
72
|
+
cp = self._run(cmd, check=True)
|
67
73
|
text = cp.stdout
|
68
74
|
parent_path: str | None = None
|
69
75
|
if isinstance(path, Dir):
|
@@ -159,30 +165,36 @@ class Rclone:
|
|
159
165
|
# self._run(cmd_list)
|
160
166
|
executor.submit(self._run, cmd_list)
|
161
167
|
|
162
|
-
def copy(
|
168
|
+
def copy(
|
169
|
+
self, src: Dir | str, dst: Dir | str, filelist: FileList | None = None
|
170
|
+
) -> subprocess.CompletedProcess:
|
163
171
|
"""Copy files from source to destination.
|
164
172
|
|
165
173
|
Args:
|
166
174
|
src: Source directory
|
167
175
|
dst: Destination directory
|
168
176
|
"""
|
169
|
-
src_dir = src.path.path
|
170
|
-
dst_dir = dst.path.path
|
177
|
+
# src_dir = src.path.path
|
178
|
+
# dst_dir = dst.path.path
|
179
|
+
src_dir = convert_to_str(src)
|
180
|
+
dst_dir = convert_to_str(dst)
|
171
181
|
cmd_list: list[str] = ["copy", src_dir, dst_dir]
|
172
|
-
self._run(cmd_list)
|
182
|
+
return self._run(cmd_list)
|
173
183
|
|
174
|
-
def purge(self, path: Dir | str) ->
|
184
|
+
def purge(self, path: Dir | str) -> subprocess.CompletedProcess:
|
175
185
|
"""Purge a directory"""
|
176
186
|
# path should always be a string
|
177
187
|
path = path if isinstance(path, str) else str(path.path)
|
178
188
|
cmd_list: list[str] = ["purge", str(path)]
|
179
|
-
self._run(cmd_list)
|
189
|
+
return self._run(cmd_list)
|
180
190
|
|
181
|
-
def deletefiles(
|
191
|
+
def deletefiles(
|
192
|
+
self, files: str | File | list[str] | list[File]
|
193
|
+
) -> subprocess.CompletedProcess:
|
182
194
|
"""Delete a directory"""
|
183
195
|
payload: list[str] = convert_to_filestr_list(files)
|
184
196
|
cmd_list: list[str] = ["delete"] + payload
|
185
|
-
self._run(cmd_list)
|
197
|
+
return self._run(cmd_list)
|
186
198
|
|
187
199
|
def exists(self, path: Dir | Remote | str | File) -> bool:
|
188
200
|
"""Check if a file or directory exists."""
|
@@ -205,10 +217,57 @@ class Rclone:
|
|
205
217
|
except subprocess.CalledProcessError:
|
206
218
|
return False
|
207
219
|
|
208
|
-
def copy_dir(
|
220
|
+
def copy_dir(
|
221
|
+
self, src: str | Dir, dst: str | Dir, args: list[str] | None = None
|
222
|
+
) -> subprocess.CompletedProcess:
|
209
223
|
"""Copy a directory from source to destination."""
|
210
224
|
# convert src to str, also dst
|
211
225
|
src = convert_to_str(src)
|
212
226
|
dst = convert_to_str(dst)
|
213
227
|
cmd_list: list[str] = ["copy", src, dst]
|
214
|
-
|
228
|
+
if args is not None:
|
229
|
+
cmd_list += args
|
230
|
+
return self._run(cmd_list)
|
231
|
+
|
232
|
+
def copy_remote(
|
233
|
+
self, src: Remote, dst: Remote, args: list[str] | None = None
|
234
|
+
) -> subprocess.CompletedProcess:
|
235
|
+
"""Copy a remote to another remote."""
|
236
|
+
cmd_list: list[str] = ["copy", str(src), str(dst)]
|
237
|
+
if args is not None:
|
238
|
+
cmd_list += args
|
239
|
+
return self._run(cmd_list)
|
240
|
+
|
241
|
+
def mount(
|
242
|
+
self, src: Remote | Dir | str, outdir: Path, allow_writes=False, use_links=True
|
243
|
+
) -> Process:
|
244
|
+
"""Mount a remote or directory to a local path.
|
245
|
+
|
246
|
+
Args:
|
247
|
+
src: Remote or directory to mount
|
248
|
+
outdir: Local path to mount to
|
249
|
+
|
250
|
+
Returns:
|
251
|
+
CompletedProcess from the mount command execution
|
252
|
+
|
253
|
+
Raises:
|
254
|
+
subprocess.CalledProcessError: If the mount operation fails
|
255
|
+
"""
|
256
|
+
if outdir.exists():
|
257
|
+
is_empty = not list(outdir.iterdir())
|
258
|
+
if not is_empty:
|
259
|
+
raise ValueError(
|
260
|
+
f"Mount directory already exists and is not empty: {outdir}"
|
261
|
+
)
|
262
|
+
outdir.rmdir()
|
263
|
+
src_str = convert_to_str(src)
|
264
|
+
cmd_list: list[str] = ["mount", src_str, str(outdir)]
|
265
|
+
if not allow_writes:
|
266
|
+
cmd_list.append("--read-only")
|
267
|
+
if use_links:
|
268
|
+
cmd_list.append("--links")
|
269
|
+
proc = self._launch_process(cmd_list)
|
270
|
+
time.sleep(2) # give it a moment to mount
|
271
|
+
if proc.poll() is not None:
|
272
|
+
raise ValueError("Mount process failed to start")
|
273
|
+
return proc
|
rclone_api/util.py
CHANGED
@@ -54,7 +54,7 @@ def to_path(item: Dir | Remote | str, rclone: Any) -> RPath:
|
|
54
54
|
raise ValueError(f"Invalid type for item: {type(item)}")
|
55
55
|
|
56
56
|
|
57
|
-
def
|
57
|
+
def get_verbose(verbose: bool | None) -> bool:
|
58
58
|
if verbose is not None:
|
59
59
|
return verbose
|
60
60
|
# get it from the environment
|
@@ -75,10 +75,11 @@ def rclone_execute(
|
|
75
75
|
cmd: list[str],
|
76
76
|
rclone_conf: Path | Config,
|
77
77
|
rclone_exe: Path,
|
78
|
+
check: bool,
|
78
79
|
verbose: bool | None = None,
|
79
80
|
) -> subprocess.CompletedProcess:
|
80
81
|
tempdir: TemporaryDirectory | None = None
|
81
|
-
verbose =
|
82
|
+
verbose = get_verbose(verbose)
|
82
83
|
assert verbose is not None
|
83
84
|
|
84
85
|
try:
|
@@ -94,7 +95,7 @@ def rclone_execute(
|
|
94
95
|
cmd_str = subprocess.list2cmdline(cmd)
|
95
96
|
print(f"Running: {cmd_str}")
|
96
97
|
cp = subprocess.run(
|
97
|
-
cmd, capture_output=True, encoding="utf-8", check=
|
98
|
+
cmd, capture_output=True, encoding="utf-8", check=check, shell=False
|
98
99
|
)
|
99
100
|
if cp.returncode != 0:
|
100
101
|
cmd_str = subprocess.list2cmdline(cmd)
|
@@ -0,0 +1,149 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: rclone_api
|
3
|
+
Version: 1.0.17
|
4
|
+
Summary: rclone api in python
|
5
|
+
Home-page: https://github.com/zackees/rclone-api
|
6
|
+
Maintainer: Zachary Vorhies
|
7
|
+
License: BSD 3-Clause License
|
8
|
+
Keywords: template-python-cmd
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Requires-Python: >=3.10
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
License-File: LICENSE
|
13
|
+
Requires-Dist: python-dotenv>=1.0.0
|
14
|
+
Dynamic: home-page
|
15
|
+
Dynamic: maintainer
|
16
|
+
|
17
|
+
# rclone-api
|
18
|
+
|
19
|
+
[](https://github.com/zackees/rclone-api/actions/workflows/lint.yml)
|
20
|
+
[](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml)
|
21
|
+
[](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml)
|
22
|
+
[](https://github.com/zackees/rclone-api/actions/workflows/push_win.yml)
|
23
|
+
|
24
|
+
Api version of rclone. It's well tested. It's a pretty low level api without the bells and whistles of other apis, but it will get the job done.
|
25
|
+
|
26
|
+
You will need to have rclone installed and on your path.
|
27
|
+
|
28
|
+
One of the benefits of this api is that it does not use `shell=True`, which can keep `rclone` running in some instances even when try to kill the process.
|
29
|
+
|
30
|
+
# Install
|
31
|
+
|
32
|
+
`pip install rclone-api`
|
33
|
+
|
34
|
+
|
35
|
+
# Examples
|
36
|
+
|
37
|
+
You can use env variables or use a `.env` file to store your secrets.
|
38
|
+
|
39
|
+
|
40
|
+
# Rclone API Usage Examples
|
41
|
+
|
42
|
+
This script demonstrates how to interact with DigitalOcean Spaces using `rclone_api`.
|
43
|
+
|
44
|
+
## Setup & Usage
|
45
|
+
|
46
|
+
Ensure you have set the required environment variables:
|
47
|
+
|
48
|
+
- `BUCKET_NAME`
|
49
|
+
- `BUCKET_KEY_PUBLIC`
|
50
|
+
- `BUCKET_KEY_SECRET`
|
51
|
+
- `BUCKET_URL`
|
52
|
+
|
53
|
+
Then, run the following Python script:
|
54
|
+
|
55
|
+
```python
|
56
|
+
import os
|
57
|
+
from rclone_api import Config, DirListing, File, Rclone, Remote
|
58
|
+
|
59
|
+
# Load environment variables
|
60
|
+
BUCKET_NAME = os.getenv("BUCKET_NAME")
|
61
|
+
BUCKET_KEY_PUBLIC = os.getenv("BUCKET_KEY_PUBLIC")
|
62
|
+
BUCKET_KEY_SECRET = os.getenv("BUCKET_KEY_SECRET")
|
63
|
+
BUCKET_URL = "sfo3.digitaloceanspaces.com"
|
64
|
+
|
65
|
+
# Generate Rclone Configuration
|
66
|
+
def generate_rclone_config() -> Config:
|
67
|
+
config_text = f"""
|
68
|
+
[dst]
|
69
|
+
type = s3
|
70
|
+
provider = DigitalOcean
|
71
|
+
access_key_id = {BUCKET_KEY_PUBLIC}
|
72
|
+
secret_access_key = {BUCKET_KEY_SECRET}
|
73
|
+
endpoint = {BUCKET_URL}
|
74
|
+
"""
|
75
|
+
return Config(config_text)
|
76
|
+
|
77
|
+
rclone = Rclone(generate_rclone_config())
|
78
|
+
|
79
|
+
# List Available Remotes
|
80
|
+
print("\n=== Available Remotes ===")
|
81
|
+
remotes = rclone.listremotes()
|
82
|
+
for remote in remotes:
|
83
|
+
print(remote)
|
84
|
+
|
85
|
+
# List Contents of the Root Bucket
|
86
|
+
print("\n=== Listing Root Bucket ===")
|
87
|
+
listing = rclone.ls(f"dst:{BUCKET_NAME}", max_depth=-1)
|
88
|
+
|
89
|
+
print("\nDirectories:")
|
90
|
+
for dir in listing.dirs:
|
91
|
+
print(dir)
|
92
|
+
|
93
|
+
print("\nFiles:")
|
94
|
+
for file in listing.files:
|
95
|
+
print(file)
|
96
|
+
|
97
|
+
# List a Specific Subdirectory
|
98
|
+
print("\n=== Listing 'zachs_video' Subdirectory ===")
|
99
|
+
path = f"dst:{BUCKET_NAME}/zachs_video"
|
100
|
+
listing = rclone.ls(path)
|
101
|
+
print(listing)
|
102
|
+
|
103
|
+
# List PNG Files in a Subdirectory
|
104
|
+
print("\n=== Listing PNG Files ===")
|
105
|
+
listing = rclone.ls(path, glob="*.png")
|
106
|
+
|
107
|
+
if listing.files:
|
108
|
+
for file in listing.files:
|
109
|
+
print(file)
|
110
|
+
|
111
|
+
# Copy a File
|
112
|
+
print("\n=== Copying a File ===")
|
113
|
+
if listing.files:
|
114
|
+
file = listing.files[0]
|
115
|
+
new_path = f"dst:{BUCKET_NAME}/zachs_video/{file.name}_copy"
|
116
|
+
rclone.copyfile(file, new_path)
|
117
|
+
print(f"Copied {file.name} to {new_path}")
|
118
|
+
|
119
|
+
# Copy Multiple Files
|
120
|
+
print("\n=== Copying Multiple Files ===")
|
121
|
+
if listing.files:
|
122
|
+
file_mapping = {file.name: file.name + "_copy" for file in listing.files[:2]}
|
123
|
+
rclone.copyfiles(file_mapping)
|
124
|
+
print(f"Copied files: {file_mapping}")
|
125
|
+
|
126
|
+
# Delete a File
|
127
|
+
print("\n=== Deleting a File ===")
|
128
|
+
file_to_delete = f"dst:{BUCKET_NAME}/zachs_video/sample.png_copy"
|
129
|
+
rclone.deletefiles([file_to_delete])
|
130
|
+
print(f"Deleted {file_to_delete}")
|
131
|
+
|
132
|
+
# Walk Through a Directory
|
133
|
+
print("\n=== Walking Through a Directory ===")
|
134
|
+
for dirlisting in rclone.walk(f"dst:{BUCKET_NAME}", max_depth=1):
|
135
|
+
print(dirlisting)
|
136
|
+
|
137
|
+
print("Done.")
|
138
|
+
```
|
139
|
+
|
140
|
+
|
141
|
+
To develop software, run `. ./activate`
|
142
|
+
|
143
|
+
# Windows
|
144
|
+
|
145
|
+
This environment requires you to use `git-bash`.
|
146
|
+
|
147
|
+
# Linting
|
148
|
+
|
149
|
+
Run `./lint`
|
@@ -0,0 +1,21 @@
|
|
1
|
+
rclone_api/__init__.py,sha256=UWQMbhE4WOQ3Skfb0LagFKW8PUKUGOm9_T3z--5FHiY,388
|
2
|
+
rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
|
3
|
+
rclone_api/config.py,sha256=tP6cU9DnCCEIRc_KP9HPur1jFLLg2QGFSxNwFm6_MVw,118
|
4
|
+
rclone_api/convert.py,sha256=Mx9Qo7zhkOedJd8LdhPvNGHp8znJzOk4f_2KWnoGc78,1012
|
5
|
+
rclone_api/dir.py,sha256=xUV4i9_E7QHrqHld2IPBlon1CGaAp8qQYMKfJTyVvoQ,2088
|
6
|
+
rclone_api/dir_listing.py,sha256=8t5Jx9ZVOJPqGKTJbWaES6bjgogUT2bnpbPVWwK1Fcs,1124
|
7
|
+
rclone_api/exec.py,sha256=9qSOpZo8YRYxv3hOvNr57ApnY2KbjxwT1QNr8OgcLM4,883
|
8
|
+
rclone_api/file.py,sha256=D02iHJW1LhfOiM_R_yPHP8_ApnDiYrkuraVcrV8-qkw,1246
|
9
|
+
rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
|
10
|
+
rclone_api/process.py,sha256=QLI-65R4Jvov9oOJSVY5UpDOS2xj6m-9YCg8upR4-0M,3560
|
11
|
+
rclone_api/rclone.py,sha256=hvzo5zZmP5IQAtuwLcsTeeazDY0slqz76DdulV2mn3s,9640
|
12
|
+
rclone_api/remote.py,sha256=c9hlRKBCg1BFB9MCINaQIoCg10qyAkeqiS4brl8ce-8,343
|
13
|
+
rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
|
14
|
+
rclone_api/util.py,sha256=BDRJ2MIceDoKVTjUQBwyhjbA8UwPrZ-0ZSa9xyMJd0E,3343
|
15
|
+
rclone_api/walk.py,sha256=J78-bY2AhNpt2ICsI5LqmXRE7oC6wVDoKoicIoU6XMg,1953
|
16
|
+
rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
17
|
+
rclone_api-1.0.17.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
18
|
+
rclone_api-1.0.17.dist-info/METADATA,sha256=9_ahew4G4YXDjXQljGkoA8s1KZa76mPrv3TZ99c2Xo4,4421
|
19
|
+
rclone_api-1.0.17.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
|
20
|
+
rclone_api-1.0.17.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
|
21
|
+
rclone_api-1.0.17.dist-info/RECORD,,
|
@@ -1,34 +0,0 @@
|
|
1
|
-
Metadata-Version: 2.2
|
2
|
-
Name: rclone_api
|
3
|
-
Version: 1.0.14
|
4
|
-
Summary: rclone api in python
|
5
|
-
Home-page: https://github.com/zackees/rclone-api
|
6
|
-
Maintainer: Zachary Vorhies
|
7
|
-
License: BSD 3-Clause License
|
8
|
-
Keywords: template-python-cmd
|
9
|
-
Classifier: Programming Language :: Python :: 3
|
10
|
-
Requires-Python: >=3.10
|
11
|
-
Description-Content-Type: text/markdown
|
12
|
-
License-File: LICENSE
|
13
|
-
Requires-Dist: python-dotenv>=1.0.0
|
14
|
-
Dynamic: home-page
|
15
|
-
Dynamic: maintainer
|
16
|
-
|
17
|
-
# rclone-api
|
18
|
-
|
19
|
-
[](https://github.com/zackees/rclone-api/actions/workflows/lint.yml)
|
20
|
-
[](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml)
|
21
|
-
[](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml)
|
22
|
-
[](https://github.com/zackees/rclone-api/actions/workflows/push_win.yml)
|
23
|
-
|
24
|
-
Api version of rclone. It's well tested. It's just released so this readme is a little to be desired.
|
25
|
-
|
26
|
-
To develop software, run `. ./activate`
|
27
|
-
|
28
|
-
# Windows
|
29
|
-
|
30
|
-
This environment requires you to use `git-bash`.
|
31
|
-
|
32
|
-
# Linting
|
33
|
-
|
34
|
-
Run `./lint`
|
@@ -1,19 +0,0 @@
|
|
1
|
-
rclone_api/__init__.py,sha256=gca3IDhDdKBtyoS1ekGQ-xTa6zrShZ6Gb4XS8ChdlYo,266
|
2
|
-
rclone_api/cli.py,sha256=dibfAZIh0kXWsBbfp3onKLjyZXo54mTzDjUdzJlDlWo,231
|
3
|
-
rclone_api/config.py,sha256=tP6cU9DnCCEIRc_KP9HPur1jFLLg2QGFSxNwFm6_MVw,118
|
4
|
-
rclone_api/convert.py,sha256=Mx9Qo7zhkOedJd8LdhPvNGHp8znJzOk4f_2KWnoGc78,1012
|
5
|
-
rclone_api/dir.py,sha256=xUV4i9_E7QHrqHld2IPBlon1CGaAp8qQYMKfJTyVvoQ,2088
|
6
|
-
rclone_api/dir_listing.py,sha256=8t5Jx9ZVOJPqGKTJbWaES6bjgogUT2bnpbPVWwK1Fcs,1124
|
7
|
-
rclone_api/exec.py,sha256=PgiNF5eQpsY8Pb62ZPwLli1WA9iMEJeMZwX43VEYKII,472
|
8
|
-
rclone_api/file.py,sha256=bb9FWQy54wKRydy0M12MQwNxycBwRSn_10ubYGAw0ag,1234
|
9
|
-
rclone_api/rclone.py,sha256=m2V3BQX0buK3mwImBiTSNTUh_JcdQJ27OeJWxqvgRZs,7455
|
10
|
-
rclone_api/remote.py,sha256=c9hlRKBCg1BFB9MCINaQIoCg10qyAkeqiS4brl8ce-8,343
|
11
|
-
rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
|
12
|
-
rclone_api/util.py,sha256=uiTfOlKCFB5FdUkYR2-Hriz0fVyrWeuZtSgHgc9RrpA,3328
|
13
|
-
rclone_api/walk.py,sha256=J78-bY2AhNpt2ICsI5LqmXRE7oC6wVDoKoicIoU6XMg,1953
|
14
|
-
rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
15
|
-
rclone_api-1.0.14.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
16
|
-
rclone_api-1.0.14.dist-info/METADATA,sha256=pATg2JeY8MQ2I7kywlJj6x4KPdL0QKKcbxjxbvq1OCs,1375
|
17
|
-
rclone_api-1.0.14.dist-info/WHEEL,sha256=9Hm2OB-j1QcCUq9Jguht7ayGIIZBRTdOXD1qg9cCgPM,109
|
18
|
-
rclone_api-1.0.14.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
|
19
|
-
rclone_api-1.0.14.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|