rclone-api 1.2.12__tar.gz → 1.2.14__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.2.12 → rclone_api-1.2.14}/PKG-INFO +1 -1
- {rclone_api-1.2.12 → rclone_api-1.2.14}/pyproject.toml +1 -1
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/mount.py +9 -111
- rclone_api-1.2.14/src/rclone_api/mount_read_chunker.py +123 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/profile/mount_copy_bytes.py +1 -1
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/rclone.py +2 -1
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/s3/chunk_file.py +4 -1
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/s3/types.py +1 -1
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/s3/upload_file_multipart.py +1 -1
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api.egg-info/PKG-INFO +1 -1
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api.egg-info/SOURCES.txt +1 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/.aiderignore +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/.github/workflows/lint.yml +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/.github/workflows/push_macos.yml +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/.github/workflows/push_ubuntu.yml +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/.github/workflows/push_win.yml +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/.gitignore +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/.pylintrc +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/.vscode/launch.json +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/.vscode/settings.json +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/.vscode/tasks.json +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/LICENSE +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/MANIFEST.in +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/README.md +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/clean +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/install +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/lint +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/requirements.testing.txt +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/setup.cfg +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/setup.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/__init__.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/assets/example.txt +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/cli.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/cmd/copy_large_s3.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/cmd/list_files.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/completed_process.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/config.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/convert.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/deprecated.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/diff.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/dir.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/dir_listing.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/exec.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/experimental/flags.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/experimental/flags_base.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/file.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/filelist.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/group_files.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/process.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/remote.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/rpath.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/s3/api.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/s3/basic_ops.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/s3/chunk_types.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/s3/create.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/scan_missing_folders.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/types.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/util.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api/walk.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api.egg-info/dependency_links.txt +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api.egg-info/entry_points.txt +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api.egg-info/requires.txt +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/src/rclone_api.egg-info/top_level.txt +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/test +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/archive/test_paramiko.py.disabled +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_cmd_list_files.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_copy.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_copy_bytes.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_copy_file_resumable_s3.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_copy_files.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_diff.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_group_files.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_is_synced.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_ls.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_mount.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_mount_s3.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_obscure.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_rclone_config.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_remote_control.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_remotes.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_s3.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_scan_missing_folders.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_size_files.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_size_suffix.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tests/test_walk.py +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/tox.ini +0 -0
- {rclone_api-1.2.12 → rclone_api-1.2.14}/upload_package.sh +0 -0
|
@@ -4,17 +4,14 @@ import platform
|
|
|
4
4
|
import shutil
|
|
5
5
|
import subprocess
|
|
6
6
|
import time
|
|
7
|
-
import traceback
|
|
8
7
|
import warnings
|
|
9
8
|
import weakref
|
|
10
|
-
from concurrent.futures import
|
|
9
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
11
10
|
from dataclasses import dataclass
|
|
12
11
|
from pathlib import Path
|
|
13
|
-
from threading import Lock, Semaphore
|
|
14
12
|
from typing import Any
|
|
15
13
|
|
|
16
14
|
from rclone_api.process import Process
|
|
17
|
-
from rclone_api.types import FilePart
|
|
18
15
|
|
|
19
16
|
_SYSTEM = platform.system() # "Linux", "Darwin", "Windows", etc.
|
|
20
17
|
|
|
@@ -37,6 +34,14 @@ def _cleanup_mounts() -> None:
|
|
|
37
34
|
executor.submit(mount.close)
|
|
38
35
|
|
|
39
36
|
|
|
37
|
+
def _cache_dir_delete_on_exit(cache_dir: Path) -> None:
|
|
38
|
+
if cache_dir.exists():
|
|
39
|
+
try:
|
|
40
|
+
shutil.rmtree(cache_dir)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
warnings.warn(f"Error removing cache directory {cache_dir}: {e}")
|
|
43
|
+
|
|
44
|
+
|
|
40
45
|
atexit.register(_cleanup_mounts)
|
|
41
46
|
|
|
42
47
|
|
|
@@ -278,110 +283,3 @@ def clean_mount(mount: Mount | Path, verbose: bool = False, wait=True) -> None:
|
|
|
278
283
|
else:
|
|
279
284
|
if verbose:
|
|
280
285
|
print(f"{mount_path} successfully cleaned up.")
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
def _cache_dir_delete_on_exit(cache_dir: Path) -> None:
|
|
284
|
-
if cache_dir.exists():
|
|
285
|
-
try:
|
|
286
|
-
shutil.rmtree(cache_dir)
|
|
287
|
-
except Exception as e:
|
|
288
|
-
warnings.warn(f"Error removing cache directory {cache_dir}: {e}")
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
def _read_from_mount_task(
|
|
292
|
-
offset: int, size: int, path: Path, verbose: bool
|
|
293
|
-
) -> bytes | Exception:
|
|
294
|
-
if verbose or True:
|
|
295
|
-
print(f"Fetching chunk: offset={offset}, size={size}, path={path}")
|
|
296
|
-
try:
|
|
297
|
-
with path.open("rb") as f:
|
|
298
|
-
f.seek(offset)
|
|
299
|
-
payload = f.read(size)
|
|
300
|
-
assert len(payload) == size, f"Invalid read size: {len(payload)}"
|
|
301
|
-
return payload
|
|
302
|
-
|
|
303
|
-
except KeyboardInterrupt as e:
|
|
304
|
-
import _thread
|
|
305
|
-
|
|
306
|
-
_thread.interrupt_main()
|
|
307
|
-
return Exception(e)
|
|
308
|
-
except Exception as e:
|
|
309
|
-
stack_trace = traceback.format_exc()
|
|
310
|
-
warnings.warn(
|
|
311
|
-
f"Error fetching file chunk at offset {offset} + {size}: {e}\n{stack_trace}"
|
|
312
|
-
)
|
|
313
|
-
return e
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
class MultiMountFileChunker:
|
|
317
|
-
def __init__(
|
|
318
|
-
self,
|
|
319
|
-
filename: str,
|
|
320
|
-
filesize: int,
|
|
321
|
-
mounts: list[Mount],
|
|
322
|
-
executor: ThreadPoolExecutor,
|
|
323
|
-
verbose: bool | None,
|
|
324
|
-
) -> None:
|
|
325
|
-
from rclone_api.util import get_verbose
|
|
326
|
-
|
|
327
|
-
self.filename = filename
|
|
328
|
-
self.filesize = filesize
|
|
329
|
-
self.executor = executor
|
|
330
|
-
self.mounts_processing: list[Mount] = []
|
|
331
|
-
self.mounts_availabe: list[Mount] = mounts
|
|
332
|
-
self.semaphore = Semaphore(len(mounts))
|
|
333
|
-
self.lock = Lock()
|
|
334
|
-
self.verbose = get_verbose(verbose)
|
|
335
|
-
|
|
336
|
-
def shutdown(self) -> None:
|
|
337
|
-
self.executor.shutdown(wait=True, cancel_futures=True)
|
|
338
|
-
with ThreadPoolExecutor() as executor:
|
|
339
|
-
for mount in self.mounts_processing:
|
|
340
|
-
executor.submit(lambda: mount.close())
|
|
341
|
-
|
|
342
|
-
def _acquire_mount(self) -> Mount:
|
|
343
|
-
self.semaphore.acquire()
|
|
344
|
-
with self.lock:
|
|
345
|
-
mount = self.mounts_availabe.pop()
|
|
346
|
-
self.mounts_processing.append(mount)
|
|
347
|
-
return mount
|
|
348
|
-
|
|
349
|
-
def _release_mount(self, mount: Mount) -> None:
|
|
350
|
-
with self.lock:
|
|
351
|
-
self.mounts_processing.remove(mount)
|
|
352
|
-
self.mounts_availabe.append(mount)
|
|
353
|
-
self.semaphore.release()
|
|
354
|
-
|
|
355
|
-
def fetch(self, offset: int, size: int, extra: Any) -> Future[FilePart]:
|
|
356
|
-
if self.verbose:
|
|
357
|
-
print(f"Fetching data range: offset={offset}, size={size}")
|
|
358
|
-
|
|
359
|
-
assert size > 0, f"Invalid size: {size}"
|
|
360
|
-
assert offset >= 0, f"Invalid offset: {offset}"
|
|
361
|
-
assert (
|
|
362
|
-
offset + size <= self.filesize
|
|
363
|
-
), f"Invalid offset + size: {offset} + {size} ({offset+size}) <= {self.filesize}"
|
|
364
|
-
|
|
365
|
-
try:
|
|
366
|
-
mount = self._acquire_mount()
|
|
367
|
-
path = mount.mount_path / self.filename
|
|
368
|
-
|
|
369
|
-
def task_fetch_file_range(
|
|
370
|
-
size=size, path=path, mount=mount, verbose=self.verbose
|
|
371
|
-
) -> FilePart:
|
|
372
|
-
bytes_or_err = _read_from_mount_task(
|
|
373
|
-
offset=offset, size=size, path=path, verbose=verbose
|
|
374
|
-
)
|
|
375
|
-
self._release_mount(mount)
|
|
376
|
-
|
|
377
|
-
if isinstance(bytes_or_err, Exception):
|
|
378
|
-
return FilePart(payload=bytes_or_err, extra=extra)
|
|
379
|
-
out = FilePart(payload=bytes_or_err, extra=extra)
|
|
380
|
-
return out
|
|
381
|
-
|
|
382
|
-
fut = self.executor.submit(task_fetch_file_range)
|
|
383
|
-
return fut
|
|
384
|
-
except Exception as e:
|
|
385
|
-
warnings.warn(f"Error fetching file chunk: {e}")
|
|
386
|
-
fp = FilePart(payload=e, extra=extra)
|
|
387
|
-
return self.executor.submit(lambda: fp)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import traceback
|
|
3
|
+
from concurrent.futures import Future, ThreadPoolExecutor
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from threading import Lock, Semaphore
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from rclone_api.mount import Mount
|
|
9
|
+
from rclone_api.types import FilePart
|
|
10
|
+
|
|
11
|
+
# Create a logger for this module
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _read_from_mount_task(
|
|
16
|
+
offset: int, size: int, path: Path, verbose: bool
|
|
17
|
+
) -> bytes | Exception:
|
|
18
|
+
if verbose:
|
|
19
|
+
logger.debug(f"Fetching chunk: offset={offset}, size={size}, path={path}")
|
|
20
|
+
try:
|
|
21
|
+
with path.open("rb") as f:
|
|
22
|
+
f.seek(offset)
|
|
23
|
+
payload = f.read(size)
|
|
24
|
+
assert len(payload) == size, f"Invalid read size: {len(payload)}"
|
|
25
|
+
return payload
|
|
26
|
+
|
|
27
|
+
except KeyboardInterrupt as e:
|
|
28
|
+
import _thread
|
|
29
|
+
|
|
30
|
+
logger.error("KeyboardInterrupt received during chunk read")
|
|
31
|
+
_thread.interrupt_main()
|
|
32
|
+
return Exception(e)
|
|
33
|
+
except Exception as e:
|
|
34
|
+
stack_trace = traceback.format_exc()
|
|
35
|
+
logger.error(
|
|
36
|
+
f"Error fetching file chunk at offset {offset} + {size}: {e}\n{stack_trace}"
|
|
37
|
+
)
|
|
38
|
+
return e
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class MultiMountFileChunker:
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
filename: str,
|
|
45
|
+
filesize: int,
|
|
46
|
+
mounts: list[Mount],
|
|
47
|
+
executor: ThreadPoolExecutor,
|
|
48
|
+
verbose: bool | None,
|
|
49
|
+
) -> None:
|
|
50
|
+
from rclone_api.util import get_verbose
|
|
51
|
+
|
|
52
|
+
self.filename = filename
|
|
53
|
+
self.filesize = filesize
|
|
54
|
+
self.executor = executor
|
|
55
|
+
self.mounts_processing: list[Mount] = []
|
|
56
|
+
self.mounts_availabe: list[Mount] = mounts
|
|
57
|
+
self.semaphore = Semaphore(len(mounts))
|
|
58
|
+
self.lock = Lock()
|
|
59
|
+
self.verbose = get_verbose(verbose)
|
|
60
|
+
logger.info(
|
|
61
|
+
f"Initialized MultiMountFileChunker for {filename} ({filesize} bytes)"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def shutdown(self) -> None:
|
|
65
|
+
logger.info("Shutting down MultiMountFileChunker")
|
|
66
|
+
self.executor.shutdown(wait=True, cancel_futures=True)
|
|
67
|
+
with ThreadPoolExecutor() as executor:
|
|
68
|
+
for mount in self.mounts_processing:
|
|
69
|
+
executor.submit(lambda: mount.close())
|
|
70
|
+
logger.debug("MultiMountFileChunker shutdown complete")
|
|
71
|
+
|
|
72
|
+
def _acquire_mount(self) -> Mount:
|
|
73
|
+
logger.debug("Acquiring mount")
|
|
74
|
+
self.semaphore.acquire()
|
|
75
|
+
with self.lock:
|
|
76
|
+
mount = self.mounts_availabe.pop()
|
|
77
|
+
self.mounts_processing.append(mount)
|
|
78
|
+
logger.debug(f"Mount acquired: {mount}")
|
|
79
|
+
return mount
|
|
80
|
+
|
|
81
|
+
def _release_mount(self, mount: Mount) -> None:
|
|
82
|
+
logger.debug(f"Releasing mount: {mount}")
|
|
83
|
+
with self.lock:
|
|
84
|
+
self.mounts_processing.remove(mount)
|
|
85
|
+
self.mounts_availabe.append(mount)
|
|
86
|
+
self.semaphore.release()
|
|
87
|
+
logger.debug("Mount released")
|
|
88
|
+
|
|
89
|
+
def fetch(self, offset: int, size: int, extra: Any) -> Future[FilePart]:
|
|
90
|
+
if self.verbose:
|
|
91
|
+
logger.debug(f"Fetching data range: offset={offset}, size={size}")
|
|
92
|
+
|
|
93
|
+
assert size > 0, f"Invalid size: {size}"
|
|
94
|
+
assert offset >= 0, f"Invalid offset: {offset}"
|
|
95
|
+
assert (
|
|
96
|
+
offset + size <= self.filesize
|
|
97
|
+
), f"Invalid offset + size: {offset} + {size} ({offset+size}) <= {self.filesize}"
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
mount = self._acquire_mount()
|
|
101
|
+
path = mount.mount_path / self.filename
|
|
102
|
+
|
|
103
|
+
def task_fetch_file_range(
|
|
104
|
+
size=size, path=path, mount=mount, verbose=self.verbose
|
|
105
|
+
) -> FilePart:
|
|
106
|
+
bytes_or_err = _read_from_mount_task(
|
|
107
|
+
offset=offset, size=size, path=path, verbose=verbose
|
|
108
|
+
)
|
|
109
|
+
self._release_mount(mount)
|
|
110
|
+
|
|
111
|
+
if isinstance(bytes_or_err, Exception):
|
|
112
|
+
logger.warning(f"Fetch task returned exception: {bytes_or_err}")
|
|
113
|
+
return FilePart(payload=bytes_or_err, extra=extra)
|
|
114
|
+
logger.debug(f"Successfully fetched {size} bytes from offset {offset}")
|
|
115
|
+
out = FilePart(payload=bytes_or_err, extra=extra)
|
|
116
|
+
return out
|
|
117
|
+
|
|
118
|
+
fut = self.executor.submit(task_fetch_file_range)
|
|
119
|
+
return fut
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.error(f"Error setting up file chunk fetch: {e}", exc_info=True)
|
|
122
|
+
fp = FilePart(payload=e, extra=extra)
|
|
123
|
+
return self.executor.submit(lambda: fp)
|
|
@@ -14,7 +14,7 @@ import psutil
|
|
|
14
14
|
from dotenv import load_dotenv
|
|
15
15
|
|
|
16
16
|
from rclone_api import Config, Rclone, SizeSuffix
|
|
17
|
-
from rclone_api.
|
|
17
|
+
from rclone_api.mount_read_chunker import MultiMountFileChunker
|
|
18
18
|
from rclone_api.types import FilePart
|
|
19
19
|
|
|
20
20
|
os.environ["RCLONE_API_VERBOSE"] = "1"
|
|
@@ -26,7 +26,8 @@ from rclone_api.dir_listing import DirListing
|
|
|
26
26
|
from rclone_api.exec import RcloneExec
|
|
27
27
|
from rclone_api.file import File
|
|
28
28
|
from rclone_api.group_files import group_files
|
|
29
|
-
from rclone_api.mount import Mount,
|
|
29
|
+
from rclone_api.mount import Mount, clean_mount, prepare_mount
|
|
30
|
+
from rclone_api.mount_read_chunker import MultiMountFileChunker
|
|
30
31
|
from rclone_api.process import Process
|
|
31
32
|
from rclone_api.remote import Remote
|
|
32
33
|
from rclone_api.rpath import RPath
|
|
@@ -7,7 +7,7 @@ from queue import Queue
|
|
|
7
7
|
from threading import Event
|
|
8
8
|
from typing import Any, Callable
|
|
9
9
|
|
|
10
|
-
from rclone_api.
|
|
10
|
+
from rclone_api.mount_read_chunker import FilePart
|
|
11
11
|
from rclone_api.s3.chunk_types import UploadState
|
|
12
12
|
from rclone_api.types import EndOfStream
|
|
13
13
|
from rclone_api.util import locked_print
|
|
@@ -85,6 +85,7 @@ def file_chunker(
|
|
|
85
85
|
return
|
|
86
86
|
|
|
87
87
|
while not should_stop():
|
|
88
|
+
print("@@@@@@@@@@")
|
|
88
89
|
curr_part_number = next_part_number()
|
|
89
90
|
if curr_part_number is None:
|
|
90
91
|
locked_print(f"File {file_path} has completed chunking all parts")
|
|
@@ -106,6 +107,7 @@ def file_chunker(
|
|
|
106
107
|
cpn: int = curr_part_number
|
|
107
108
|
|
|
108
109
|
def on_complete(fut: Future[FilePart]) -> None:
|
|
110
|
+
print("ON COMPLETE")
|
|
109
111
|
fp: FilePart = fut.result()
|
|
110
112
|
if fp.is_error():
|
|
111
113
|
warnings.warn(
|
|
@@ -127,6 +129,7 @@ def file_chunker(
|
|
|
127
129
|
queue_upload.put(fp)
|
|
128
130
|
|
|
129
131
|
offset = (curr_part_number - 1) * chunk_size
|
|
132
|
+
print(f"Reading chunk {curr_part_number} of {num_parts} for {file_path}")
|
|
130
133
|
fut = fetcher(offset, file_size, S3FileInfo(upload_info.upload_id, cpn))
|
|
131
134
|
fut.add_done_callback(on_complete)
|
|
132
135
|
# wait until the queue_upload queue can accept the next chunk
|
|
@@ -10,7 +10,7 @@ from typing import Any, Callable
|
|
|
10
10
|
|
|
11
11
|
from botocore.client import BaseClient
|
|
12
12
|
|
|
13
|
-
from rclone_api.
|
|
13
|
+
from rclone_api.mount_read_chunker import FilePart
|
|
14
14
|
from rclone_api.s3.chunk_file import S3FileInfo, file_chunker
|
|
15
15
|
from rclone_api.s3.chunk_types import (
|
|
16
16
|
FinishedPiece,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|