rclone-api 1.4.18__tar.gz → 1.4.19__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.
- {rclone_api-1.4.18 → rclone_api-1.4.19}/PKG-INFO +1 -1
- {rclone_api-1.4.18 → rclone_api-1.4.19}/pyproject.toml +1 -1
- rclone_api-1.4.19/src/rclone_api/cmd/copy_large_s3_finish.py +73 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/detail/copy_file_parts.py +4 -1
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/s3/merge_state.py +47 -15
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/s3/multipart/finished_piece.py +0 -4
- rclone_api-1.4.19/src/rclone_api/s3/s3_multipart_uploader_by_copy.py +518 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api.egg-info/PKG-INFO +1 -1
- rclone_api-1.4.18/src/rclone_api/cmd/copy_large_s3_finish.py +0 -225
- rclone_api-1.4.18/src/rclone_api/s3/s3_multipart_uploader_by_copy.py +0 -327
- {rclone_api-1.4.18 → rclone_api-1.4.19}/.aiderignore +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/.github/workflows/lint.yml +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/.github/workflows/push_macos.yml +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/.github/workflows/push_ubuntu.yml +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/.github/workflows/push_win.yml +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/.gitignore +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/.pylintrc +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/.vscode/launch.json +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/.vscode/settings.json +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/.vscode/tasks.json +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/LICENSE +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/MANIFEST.in +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/README.md +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/clean +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/install +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/lint +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/requirements.testing.txt +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/setup.cfg +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/setup.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/__init__.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/assets/example.txt +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/cli.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/cmd/analyze.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/cmd/copy_large_s3.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/cmd/list_files.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/cmd/save_to_db.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/completed_process.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/config.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/convert.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/db/__init__.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/db/db.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/db/models.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/deprecated.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/detail/walk.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/diff.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/dir.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/dir_listing.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/exec.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/experimental/flags.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/experimental/flags_base.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/file.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/file_item.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/file_part.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/file_stream.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/filelist.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/group_files.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/http_server.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/log.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/mount.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/process.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/rclone_impl.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/remote.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/rpath.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/s3/api.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/s3/basic_ops.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/s3/chunk_task.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/s3/create.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/s3/multipart/file_info.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/s3/multipart/upload_info.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/s3/multipart/upload_state.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/s3/types.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/s3/upload_file_multipart.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/scan_missing_folders.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/types.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api/util.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api.egg-info/SOURCES.txt +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api.egg-info/dependency_links.txt +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api.egg-info/entry_points.txt +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api.egg-info/requires.txt +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/src/rclone_api.egg-info/top_level.txt +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/test +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/archive/test_paramiko.py.disabled +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_cmd_list_files.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_copy.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_copy_bytes.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_copy_file_resumable_s3.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_copy_files.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_db.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_diff.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_file_item.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_group_files.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_is_synced.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_ls.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_ls_stream_files.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_mount.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_mount_s3.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_obscure.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_rclone_config.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_read_write_text.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_remote_control.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_remotes.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_s3.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_scan_missing_folders.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_serve_http.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_size_files.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_size_suffix.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tests/test_walk.py +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/tox.ini +0 -0
- {rclone_api-1.4.18 → rclone_api-1.4.19}/upload_package.sh +0 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
import argparse
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
from rclone_api import Rclone
|
6
|
+
from rclone_api.s3.s3_multipart_uploader_by_copy import (
|
7
|
+
s3_server_side_multi_part_merge,
|
8
|
+
)
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass
|
12
|
+
class Args:
|
13
|
+
config_path: Path
|
14
|
+
src: str # like dst:TorrentBooks/aa_misc_data/aa_misc_data/world_lending_library_2024_11.tar.zst-parts/ (info.json will be located here)
|
15
|
+
verbose: bool
|
16
|
+
|
17
|
+
|
18
|
+
def list_files(rclone: Rclone, path: str):
|
19
|
+
"""List files in a remote path."""
|
20
|
+
for dirlisting in rclone.walk(path):
|
21
|
+
for file in dirlisting.files:
|
22
|
+
print(file.path)
|
23
|
+
|
24
|
+
|
25
|
+
def _parse_args() -> Args:
|
26
|
+
parser = argparse.ArgumentParser(description="List files in a remote path.")
|
27
|
+
parser.add_argument("src", help="Directory that holds the info.json file")
|
28
|
+
parser.add_argument("-v", "--verbose", help="Verbose output", action="store_true")
|
29
|
+
parser.add_argument(
|
30
|
+
"--config", help="Path to rclone config file", type=Path, required=False
|
31
|
+
)
|
32
|
+
args = parser.parse_args()
|
33
|
+
config: Path | None = args.config
|
34
|
+
if config is None:
|
35
|
+
config = Path("rclone.conf")
|
36
|
+
if not config.exists():
|
37
|
+
raise FileNotFoundError(f"Config file not found: {config}")
|
38
|
+
assert config is not None
|
39
|
+
out = Args(
|
40
|
+
config_path=config,
|
41
|
+
src=args.src,
|
42
|
+
verbose=args.verbose,
|
43
|
+
)
|
44
|
+
return out
|
45
|
+
|
46
|
+
|
47
|
+
def _get_info_path(src: str) -> str:
|
48
|
+
if src.endswith("/"):
|
49
|
+
src = src[:-1]
|
50
|
+
info_path = f"{src}/info.json"
|
51
|
+
return info_path
|
52
|
+
|
53
|
+
|
54
|
+
def main() -> int:
|
55
|
+
"""Main entry point."""
|
56
|
+
args = _parse_args()
|
57
|
+
rclone = Rclone(rclone_conf=args.config_path)
|
58
|
+
info_path = _get_info_path(src=args.src)
|
59
|
+
s3_server_side_multi_part_merge(
|
60
|
+
rclone=rclone.impl, info_path=info_path, max_workers=1
|
61
|
+
)
|
62
|
+
return 0
|
63
|
+
|
64
|
+
|
65
|
+
if __name__ == "__main__":
|
66
|
+
import sys
|
67
|
+
|
68
|
+
sys.argv.append("--config")
|
69
|
+
sys.argv.append("rclone.conf")
|
70
|
+
sys.argv.append(
|
71
|
+
"dst:TorrentBooks/aa_misc_data/aa_misc_data/world_lending_library_2024_11.tar.zst-parts/"
|
72
|
+
)
|
73
|
+
main()
|
@@ -209,7 +209,10 @@ class InfoJson:
|
|
209
209
|
|
210
210
|
@property
|
211
211
|
def parts_dir(self) -> str:
|
212
|
-
|
212
|
+
parts_dir = os.path.dirname(self.src_info)
|
213
|
+
if parts_dir.endswith("/"):
|
214
|
+
parts_dir = parts_dir[:-1]
|
215
|
+
return parts_dir
|
213
216
|
|
214
217
|
@property
|
215
218
|
def dst(self) -> str:
|
@@ -8,8 +8,9 @@ from existing S3 objects using upload_part_copy.
|
|
8
8
|
|
9
9
|
import json
|
10
10
|
from dataclasses import dataclass
|
11
|
-
from typing import Any
|
11
|
+
from typing import Any
|
12
12
|
|
13
|
+
from rclone_api.rclone_impl import RcloneImpl
|
13
14
|
from rclone_api.s3.multipart.finished_piece import FinishedPiece
|
14
15
|
|
15
16
|
|
@@ -46,34 +47,59 @@ class Part:
|
|
46
47
|
|
47
48
|
class MergeState:
|
48
49
|
|
49
|
-
def __init__(
|
50
|
+
def __init__(
|
51
|
+
self,
|
52
|
+
rclone_impl: RcloneImpl,
|
53
|
+
merge_path: str,
|
54
|
+
upload_id: str,
|
55
|
+
bucket: str,
|
56
|
+
dst_key: str,
|
57
|
+
finished: list[FinishedPiece],
|
58
|
+
all_parts: list[Part],
|
59
|
+
) -> None:
|
60
|
+
self.rclone_impl: RcloneImpl = rclone_impl
|
61
|
+
self.merge_path: str = merge_path
|
62
|
+
self.merge_parts_path: str = f"{merge_path}/merge" # future use?
|
63
|
+
self.upload_id: str = upload_id
|
64
|
+
self.bucket: str = bucket
|
65
|
+
self.dst_key: str = dst_key
|
50
66
|
self.finished: list[FinishedPiece] = list(finished)
|
51
67
|
self.all_parts: list[Part] = list(all_parts)
|
52
|
-
self.callbacks: list[Callable[[FinishedPiece], None]] = []
|
53
|
-
|
54
|
-
def add_callback(self, callback: Callable[[FinishedPiece], None]) -> None:
|
55
|
-
self.callbacks.append(callback)
|
56
68
|
|
57
69
|
def on_finished(self, finished_piece: FinishedPiece) -> None:
|
58
|
-
|
59
|
-
|
70
|
+
self.finished.append(finished_piece)
|
71
|
+
|
72
|
+
def remaining_parts(self) -> list[Part]:
|
73
|
+
finished_parts: set[int] = set([p.part_number for p in self.finished])
|
74
|
+
remaining = [p for p in self.all_parts if p.part_number not in finished_parts]
|
75
|
+
return remaining
|
60
76
|
|
61
77
|
@staticmethod
|
62
|
-
def
|
78
|
+
def from_json(rclone_impl: RcloneImpl, json: dict) -> "MergeState | Exception":
|
63
79
|
try:
|
80
|
+
merge_path = json["merge_path"]
|
81
|
+
bucket = json["bucket"]
|
82
|
+
dst_key = json["dst_key"]
|
64
83
|
finished: list[FinishedPiece] = FinishedPiece.from_json_array(
|
65
|
-
|
84
|
+
json["finished"]
|
66
85
|
)
|
67
|
-
all_parts: list[Part | Exception] = [
|
68
|
-
Part.from_json(j) for j in json_array["all"]
|
69
|
-
]
|
86
|
+
all_parts: list[Part | Exception] = [Part.from_json(j) for j in json["all"]]
|
70
87
|
all_parts_no_err: list[Part] = [
|
71
88
|
p for p in all_parts if not isinstance(p, Exception)
|
72
89
|
]
|
90
|
+
upload_id: str = json["upload_id"]
|
73
91
|
errs: list[Exception] = [p for p in all_parts if isinstance(p, Exception)]
|
74
92
|
if len(errs):
|
75
93
|
return Exception(f"Errors in parts: {errs}")
|
76
|
-
return MergeState(
|
94
|
+
return MergeState(
|
95
|
+
rclone_impl=rclone_impl,
|
96
|
+
merge_path=merge_path,
|
97
|
+
upload_id=upload_id,
|
98
|
+
bucket=bucket,
|
99
|
+
dst_key=dst_key,
|
100
|
+
finished=finished,
|
101
|
+
all_parts=all_parts_no_err,
|
102
|
+
)
|
77
103
|
except Exception as e:
|
78
104
|
return e
|
79
105
|
|
@@ -81,12 +107,18 @@ class MergeState:
|
|
81
107
|
finished = self.finished.copy()
|
82
108
|
all_parts = self.all_parts.copy()
|
83
109
|
return {
|
110
|
+
"merge_path": self.merge_path,
|
111
|
+
"bucket": self.bucket,
|
112
|
+
"dst_key": self.dst_key,
|
113
|
+
"upload_id": self.upload_id,
|
84
114
|
"finished": FinishedPiece.to_json_array(finished),
|
85
115
|
"all": [part.to_json() for part in all_parts],
|
86
116
|
}
|
87
117
|
|
88
118
|
def to_json_str(self) -> str:
|
89
|
-
|
119
|
+
data = self.to_json()
|
120
|
+
out = json.dumps(data, indent=2)
|
121
|
+
return out
|
90
122
|
|
91
123
|
def __str__(self):
|
92
124
|
return self.to_json_str()
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import json
|
2
1
|
import warnings
|
3
2
|
from dataclasses import dataclass
|
4
3
|
|
@@ -13,9 +12,6 @@ class FinishedPiece:
|
|
13
12
|
def to_json(self) -> dict:
|
14
13
|
return {"part_number": self.part_number, "etag": self.etag}
|
15
14
|
|
16
|
-
def to_json_str(self) -> str:
|
17
|
-
return json.dumps(self.to_json(), indent=0)
|
18
|
-
|
19
15
|
@staticmethod
|
20
16
|
def to_json_array(
|
21
17
|
parts: list["FinishedPiece | EndOfStream"] | list["FinishedPiece"],
|