rclone-api 1.1.10__py2.py3-none-any.whl → 1.1.12__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/cmd/copy_large_s3.py +11 -2
- rclone_api/rclone.py +6 -3
- rclone_api/s3/chunk_types.py +14 -0
- rclone_api/s3/chunk_uploader.py +21 -3
- rclone_api/s3/types.py +1 -0
- {rclone_api-1.1.10.dist-info → rclone_api-1.1.12.dist-info}/METADATA +1 -1
- {rclone_api-1.1.10.dist-info → rclone_api-1.1.12.dist-info}/RECORD +11 -11
- {rclone_api-1.1.10.dist-info → rclone_api-1.1.12.dist-info}/LICENSE +0 -0
- {rclone_api-1.1.10.dist-info → rclone_api-1.1.12.dist-info}/WHEEL +0 -0
- {rclone_api-1.1.10.dist-info → rclone_api-1.1.12.dist-info}/entry_points.txt +0 -0
- {rclone_api-1.1.10.dist-info → rclone_api-1.1.12.dist-info}/top_level.txt +0 -0
rclone_api/cmd/copy_large_s3.py
CHANGED
|
@@ -14,6 +14,7 @@ class Args:
|
|
|
14
14
|
dst: str
|
|
15
15
|
chunk_size: SizeSuffix
|
|
16
16
|
threads: int
|
|
17
|
+
write_threads: int
|
|
17
18
|
retries: int
|
|
18
19
|
save_state_json: Path
|
|
19
20
|
verbose: bool
|
|
@@ -38,7 +39,7 @@ def _parse_args() -> Args:
|
|
|
38
39
|
"--chunk-size",
|
|
39
40
|
help="Chunk size that will be read and uploaded in in SizeSuffix (i.e. 128M = 128 megabytes) form",
|
|
40
41
|
type=str,
|
|
41
|
-
default="
|
|
42
|
+
default="512MB",
|
|
42
43
|
)
|
|
43
44
|
parser.add_argument(
|
|
44
45
|
"--threads",
|
|
@@ -46,6 +47,12 @@ def _parse_args() -> Args:
|
|
|
46
47
|
type=int,
|
|
47
48
|
default=64,
|
|
48
49
|
)
|
|
50
|
+
parser.add_argument(
|
|
51
|
+
"--write-threads",
|
|
52
|
+
help="Max number of chunks to upload in parallel to the destination",
|
|
53
|
+
type=int,
|
|
54
|
+
default=64,
|
|
55
|
+
)
|
|
49
56
|
parser.add_argument("--retries", help="Number of retries", type=int, default=3)
|
|
50
57
|
parser.add_argument(
|
|
51
58
|
"--resume-json",
|
|
@@ -61,6 +68,7 @@ def _parse_args() -> Args:
|
|
|
61
68
|
dst=args.dst,
|
|
62
69
|
chunk_size=SizeSuffix(args.chunk_size),
|
|
63
70
|
threads=args.threads,
|
|
71
|
+
write_threads=args.write_threads,
|
|
64
72
|
retries=args.retries,
|
|
65
73
|
save_state_json=args.resume_json,
|
|
66
74
|
verbose=args.verbose,
|
|
@@ -77,7 +85,8 @@ def main() -> int:
|
|
|
77
85
|
src=args.src,
|
|
78
86
|
dst=args.dst,
|
|
79
87
|
chunk_size=args.chunk_size,
|
|
80
|
-
|
|
88
|
+
read_threads=args.threads,
|
|
89
|
+
write_threads=args.write_threads,
|
|
81
90
|
# vfs_read_chunk_size=unit_chunk,
|
|
82
91
|
# vfs_read_chunk_size_limit=args.chunk_size,
|
|
83
92
|
# vfs_read_chunk_streams=args.threads,
|
rclone_api/rclone.py
CHANGED
|
@@ -678,7 +678,8 @@ class Rclone:
|
|
|
678
678
|
dst: str,
|
|
679
679
|
save_state_json: Path,
|
|
680
680
|
chunk_size: SizeSuffix | None = None,
|
|
681
|
-
|
|
681
|
+
read_threads: int = 16,
|
|
682
|
+
write_threads: int = 16,
|
|
682
683
|
retries: int = 3,
|
|
683
684
|
verbose: bool | None = None,
|
|
684
685
|
max_chunks_before_suspension: int | None = None,
|
|
@@ -691,10 +692,10 @@ class Rclone:
|
|
|
691
692
|
|
|
692
693
|
other_args: list[str] = ["--no-modtime", "--vfs-read-wait", "1s"]
|
|
693
694
|
chunk_size = chunk_size or SizeSuffix("128M")
|
|
694
|
-
unit_chunk_size = chunk_size /
|
|
695
|
+
unit_chunk_size = chunk_size / read_threads
|
|
695
696
|
vfs_read_chunk_size = unit_chunk_size
|
|
696
697
|
vfs_read_chunk_size_limit = chunk_size
|
|
697
|
-
vfs_read_chunk_streams =
|
|
698
|
+
vfs_read_chunk_streams = read_threads
|
|
698
699
|
vfs_disk_space_total_size = chunk_size
|
|
699
700
|
assert (
|
|
700
701
|
chunk_size.as_int() % vfs_read_chunk_size.as_int() == 0
|
|
@@ -773,6 +774,7 @@ class Rclone:
|
|
|
773
774
|
client = S3Client(s3_creds)
|
|
774
775
|
config: S3MutliPartUploadConfig = S3MutliPartUploadConfig(
|
|
775
776
|
chunk_size=chunk_size.as_int(),
|
|
777
|
+
max_write_threads=write_threads,
|
|
776
778
|
retries=retries,
|
|
777
779
|
resume_path_json=save_state_json,
|
|
778
780
|
max_chunks_before_suspension=max_chunks_before_suspension,
|
|
@@ -797,6 +799,7 @@ class Rclone:
|
|
|
797
799
|
upload_config = S3MutliPartUploadConfig(
|
|
798
800
|
chunk_size=chunk_size.as_int(),
|
|
799
801
|
retries=retries,
|
|
802
|
+
max_write_threads=write_threads,
|
|
800
803
|
resume_path_json=save_state_json,
|
|
801
804
|
max_chunks_before_suspension=max_chunks_before_suspension,
|
|
802
805
|
)
|
rclone_api/s3/chunk_types.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import hashlib
|
|
1
2
|
import json
|
|
2
3
|
import os
|
|
3
4
|
import time
|
|
@@ -103,6 +104,19 @@ class UploadInfo:
|
|
|
103
104
|
return
|
|
104
105
|
self._total_chunks = self.total_chunks()
|
|
105
106
|
|
|
107
|
+
def fingerprint(self) -> str:
|
|
108
|
+
# hash the attributes that are used to identify the upload
|
|
109
|
+
hasher = hashlib.sha256()
|
|
110
|
+
# first is file size
|
|
111
|
+
hasher.update(str(self.file_size).encode("utf-8"))
|
|
112
|
+
# second is the file path
|
|
113
|
+
hasher.update(str(self.src_file_path).encode("utf-8"))
|
|
114
|
+
# next is chunk size
|
|
115
|
+
hasher.update(str(self.chunk_size).encode("utf-8"))
|
|
116
|
+
# next is the number of parts
|
|
117
|
+
hasher.update(str(self._total_chunks).encode("utf-8"))
|
|
118
|
+
return hasher.hexdigest()
|
|
119
|
+
|
|
106
120
|
def to_json(self) -> dict:
|
|
107
121
|
json_dict = {}
|
|
108
122
|
for f in fields(self):
|
rclone_api/s3/chunk_uploader.py
CHANGED
|
@@ -115,6 +115,7 @@ def upload_file_multipart(
|
|
|
115
115
|
object_name: str,
|
|
116
116
|
resumable_info_path: Path | None,
|
|
117
117
|
chunk_size: int = 16 * 1024 * 1024, # Default chunk size is 16MB; can be overridden
|
|
118
|
+
upload_threads: int = 16,
|
|
118
119
|
retries: int = 20,
|
|
119
120
|
max_chunks_before_suspension: int | None = None,
|
|
120
121
|
abort_transfer_on_failure: bool = False,
|
|
@@ -162,7 +163,25 @@ def upload_file_multipart(
|
|
|
162
163
|
return upload_state
|
|
163
164
|
|
|
164
165
|
filechunks: Queue[FileChunk | None] = Queue(10)
|
|
165
|
-
|
|
166
|
+
new_state = make_new_state()
|
|
167
|
+
loaded_state = get_upload_state()
|
|
168
|
+
|
|
169
|
+
if loaded_state is None:
|
|
170
|
+
upload_state = new_state
|
|
171
|
+
else:
|
|
172
|
+
# if the file size has changed, we cannot resume
|
|
173
|
+
if (
|
|
174
|
+
loaded_state.upload_info.fingerprint()
|
|
175
|
+
!= new_state.upload_info.fingerprint()
|
|
176
|
+
):
|
|
177
|
+
locked_print(
|
|
178
|
+
f"Cannot resume upload: file size changed, starting over for {file_path}"
|
|
179
|
+
)
|
|
180
|
+
_abort_previous_upload(loaded_state)
|
|
181
|
+
upload_state = new_state
|
|
182
|
+
else:
|
|
183
|
+
upload_state = loaded_state
|
|
184
|
+
|
|
166
185
|
try:
|
|
167
186
|
upload_state.update_source_file(file_path)
|
|
168
187
|
except ValueError as e:
|
|
@@ -179,7 +198,6 @@ def upload_file_multipart(
|
|
|
179
198
|
)
|
|
180
199
|
started_new_upload = finished == 0
|
|
181
200
|
upload_info = upload_state.upload_info
|
|
182
|
-
max_workers = 8
|
|
183
201
|
|
|
184
202
|
chunker_errors: Queue[Exception] = Queue()
|
|
185
203
|
|
|
@@ -202,7 +220,7 @@ def upload_file_multipart(
|
|
|
202
220
|
thread_chunker = Thread(target=chunker_task, daemon=True)
|
|
203
221
|
thread_chunker.start()
|
|
204
222
|
|
|
205
|
-
with ThreadPoolExecutor(max_workers=
|
|
223
|
+
with ThreadPoolExecutor(max_workers=upload_threads) as executor:
|
|
206
224
|
while True:
|
|
207
225
|
file_chunk: FileChunk | None = filechunks.get()
|
|
208
226
|
if file_chunk is None:
|
rclone_api/s3/types.py
CHANGED
|
@@ -46,6 +46,7 @@ class S3MutliPartUploadConfig:
|
|
|
46
46
|
chunk_size: int
|
|
47
47
|
retries: int
|
|
48
48
|
resume_path_json: Path
|
|
49
|
+
max_write_threads: int
|
|
49
50
|
max_chunks_before_suspension: int | None = None
|
|
50
51
|
mount_path: Path | None = (
|
|
51
52
|
None # If set this will be used to mount the src file, otherwise it's one is chosen automatically
|
|
@@ -13,7 +13,7 @@ rclone_api/filelist.py,sha256=xbiusvNgaB_b_kQOZoHMJJxn6TWGtPrWd2J042BI28o,767
|
|
|
13
13
|
rclone_api/group_files.py,sha256=H92xPW9lQnbNw5KbtZCl00bD6iRh9yRbCuxku4j_3dg,8036
|
|
14
14
|
rclone_api/mount.py,sha256=Xj3BMVSDEwUbtMi8ycn5mUom-fAf-F9lJOjR_BzVllw,6073
|
|
15
15
|
rclone_api/process.py,sha256=xYUgU17txkZfZdr4vtRfvD8YjvSfdrbjM7PYW1npAMI,4264
|
|
16
|
-
rclone_api/rclone.py,sha256=
|
|
16
|
+
rclone_api/rclone.py,sha256=MelTVgBhV-JQdDUMLYusEuNSOq-RyrZCcKkkkSpsRfA,39955
|
|
17
17
|
rclone_api/remote.py,sha256=O9WDUFQy9f6oT1HdUbTixK2eg0xtBBm8k4Xl6aa6K00,431
|
|
18
18
|
rclone_api/rpath.py,sha256=8ZA_1wxWtskwcy0I8V2VbjKDmzPkiWd8Q2JQSvh-sYE,2586
|
|
19
19
|
rclone_api/scan_missing_folders.py,sha256=Kulca2Q6WZodt00ATFHkmqqInuoPvBkhTcS9703y6po,4740
|
|
@@ -21,20 +21,20 @@ rclone_api/types.py,sha256=zfTb0iM6mhfqgaYS6j6T0NIOA4e9GymNOXLPhVELe4A,3853
|
|
|
21
21
|
rclone_api/util.py,sha256=_Z-GUMVXnHYOGdo2dy2ie2P5fGgyg8KdGjHKicx68Ko,4573
|
|
22
22
|
rclone_api/walk.py,sha256=-54NVE8EJcCstwDoaC_UtHm73R2HrZwVwQmsnv55xNU,3369
|
|
23
23
|
rclone_api/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
|
|
24
|
-
rclone_api/cmd/copy_large_s3.py,sha256=
|
|
24
|
+
rclone_api/cmd/copy_large_s3.py,sha256=3GVr7ZCLyMnVhDysCRGzsd6zlsK_5MUF0kHFZ_i6YbQ,3358
|
|
25
25
|
rclone_api/cmd/list_files.py,sha256=x8FHODEilwKqwdiU1jdkeJbLwOqUkUQuDWPo2u_zpf0,741
|
|
26
26
|
rclone_api/experimental/flags.py,sha256=qCVD--fSTmzlk9hloRLr0q9elzAOFzPsvVpKM3aB1Mk,2739
|
|
27
27
|
rclone_api/experimental/flags_base.py,sha256=ajU_czkTcAxXYU-SlmiCfHY7aCQGHvpCLqJ-Z8uZLk0,2102
|
|
28
28
|
rclone_api/s3/api.py,sha256=VstlaEnBjO2JDQuCRLdTfUGvQLbfshlXXhAzimFv4Vc,3763
|
|
29
29
|
rclone_api/s3/basic_ops.py,sha256=hK3366xhVEzEcjz9Gk_8lFx6MRceAk72cax6mUrr6ko,2104
|
|
30
30
|
rclone_api/s3/chunk_file.py,sha256=XPoDl7DJMJIGBMRoPO2wqwqCMT7ZrIsEkDqlbMH8jzs,3506
|
|
31
|
-
rclone_api/s3/chunk_types.py,sha256=
|
|
32
|
-
rclone_api/s3/chunk_uploader.py,sha256=
|
|
31
|
+
rclone_api/s3/chunk_types.py,sha256=pZUKc3L418XTwET1rLm4eRo_2swKQckSqlc4XjYmhDo,9147
|
|
32
|
+
rclone_api/s3/chunk_uploader.py,sha256=7Qumc2emQ-SpbnTL8PBN4ElpghnFWJfozVHPhm6LrQ4,9683
|
|
33
33
|
rclone_api/s3/create.py,sha256=SK3IGHZwsSkoG4Zb4NCphcVg9_f7VifDKng-tExMS2s,3088
|
|
34
|
-
rclone_api/s3/types.py,sha256=
|
|
35
|
-
rclone_api-1.1.
|
|
36
|
-
rclone_api-1.1.
|
|
37
|
-
rclone_api-1.1.
|
|
38
|
-
rclone_api-1.1.
|
|
39
|
-
rclone_api-1.1.
|
|
40
|
-
rclone_api-1.1.
|
|
34
|
+
rclone_api/s3/types.py,sha256=yBnJ38Tjk6RlydJ-sqZ7DSfyFloy8KDYJ0mv3vlOzLE,1388
|
|
35
|
+
rclone_api-1.1.12.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
|
|
36
|
+
rclone_api-1.1.12.dist-info/METADATA,sha256=Gs7HUdcg-9dw534zO3eyi5hCNyMXBD0Js5P1fe94mnM,4479
|
|
37
|
+
rclone_api-1.1.12.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
|
|
38
|
+
rclone_api-1.1.12.dist-info/entry_points.txt,sha256=6eNqTRXKhVf8CpWNjXiOa_0Du9tHiW_HD2iQSXRsUg8,132
|
|
39
|
+
rclone_api-1.1.12.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
|
|
40
|
+
rclone_api-1.1.12.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|