rclone-api 1.1.26__py2.py3-none-any.whl → 1.1.29__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/rclone.py CHANGED
@@ -691,7 +691,7 @@ class Rclone:
691
691
  from rclone_api.util import S3PathInfo, random_str, split_s3_path
692
692
 
693
693
  other_args: list[str] = ["--no-modtime", "--vfs-read-wait", "1s"]
694
- chunk_size = chunk_size or SizeSuffix("128M")
694
+ chunk_size = chunk_size or SizeSuffix("64M")
695
695
  unit_chunk_size = chunk_size / read_threads
696
696
  vfs_read_chunk_size = unit_chunk_size
697
697
  vfs_read_chunk_size_limit = chunk_size
@@ -726,8 +726,6 @@ class Rclone:
726
726
  verbose=False,
727
727
  other_args=other_args,
728
728
  ):
729
- # raise NotImplementedError("Not implemented yet")
730
-
731
729
  path_info: S3PathInfo = split_s3_path(dst)
732
730
  remote = path_info.remote
733
731
  bucket_name = path_info.bucket
@@ -813,6 +811,50 @@ class Rclone:
813
811
  )
814
812
  return out
815
813
 
814
+ def copy_bytes(
815
+ self,
816
+ src: str,
817
+ offset: int,
818
+ length: int,
819
+ transfers: int = 16,
820
+ ) -> bytes | Exception:
821
+ """Copy bytes from a file to another file."""
822
+ from rclone_api.util import random_str
823
+
824
+ tmp_mnt = Path("tmp_mnt") / random_str(12)
825
+ src_parent_path = Path(src).parent.as_posix()
826
+ src_file = Path(src).name
827
+ other_args: list[str] = ["--no-modtime", "--vfs-read-wait", "1s"]
828
+ unit_chunk_size = length
829
+ vfs_read_chunk_size = unit_chunk_size
830
+ vfs_read_chunk_size_limit = length
831
+ vfs_read_chunk_streams = transfers
832
+ vfs_disk_space_total_size = length
833
+ other_args += ["--vfs-read-chunk-size", str(vfs_read_chunk_size)]
834
+ other_args += ["--vfs-read-chunk-size-limit", str(vfs_read_chunk_size_limit)]
835
+ other_args += ["--vfs-read-chunk-streams", str(vfs_read_chunk_streams)]
836
+ other_args += ["--vfs-disk-space-total-size", str(vfs_disk_space_total_size)]
837
+ other_args += ["--read-only"]
838
+ other_args += ["--direct-io"]
839
+
840
+ try:
841
+ # use scoped mount to do the read, then write the bytes to the destination
842
+ with self.scoped_mount(
843
+ src_parent_path,
844
+ tmp_mnt,
845
+ use_links=True,
846
+ verbose=False,
847
+ vfs_cache_mode="minimal",
848
+ other_args=other_args,
849
+ ):
850
+ src_file_mnt = tmp_mnt / src_file
851
+ with open(src_file_mnt, "rb") as f:
852
+ f.seek(offset)
853
+ data = f.read(length)
854
+ return data
855
+ except Exception as e:
856
+ return e
857
+
816
858
  def copy_dir(
817
859
  self, src: str | Dir, dst: str | Dir, args: list[str] | None = None
818
860
  ) -> CompletedProcess:
rclone_api/s3/api.py CHANGED
@@ -9,9 +9,9 @@ from rclone_api.s3.basic_ops import (
9
9
  list_bucket_contents,
10
10
  upload_file,
11
11
  )
12
- from rclone_api.s3.chunk_uploader import MultiUploadResult, upload_file_multipart
13
12
  from rclone_api.s3.create import create_s3_client
14
13
  from rclone_api.s3.types import S3Credentials, S3MutliPartUploadConfig, S3UploadTarget
14
+ from rclone_api.s3.upload_file_multipart import MultiUploadResult, upload_file_multipart
15
15
 
16
16
  _MIN_THRESHOLD_FOR_CHUNKING = 5 * 1024 * 1024
17
17
 
rclone_api/types.py CHANGED
@@ -37,41 +37,68 @@ class SizeResult:
37
37
 
38
38
 
39
39
  def _to_size_suffix(size: int) -> str:
40
- if size < 1024:
41
- return f"{size}B"
42
- elif size < 1024**2:
43
- val = size / 1024
44
- unit = "K"
45
- elif size < 1024**3:
46
- val = size / (1024**2)
47
- unit = "M"
48
- elif size < 1024**4:
49
- val = size / (1024**3)
50
- unit = "G"
51
- elif size < 1024**5:
52
- val = size / (1024**4)
53
- unit = "T"
54
- elif size < 1024**6:
55
- val = size / (1024**5)
56
- unit = "P"
57
- else:
58
- raise ValueError(f"Invalid size: {size}")
59
-
60
- # If the float is an integer, drop the decimal, otherwise format with one decimal.
61
- return f"{int(val) if val.is_integer() else f'{val:.1f}'}{unit}"
40
+ def _convert(size: int) -> tuple[float, str]:
41
+ val: float
42
+ unit: str
43
+ if size < 1024:
44
+ val = size
45
+ unit = "B"
46
+ elif size < 1024**2:
47
+ val = size / 1024
48
+ unit = "K"
49
+ elif size < 1024**3:
50
+ val = size / (1024**2)
51
+ unit = "M"
52
+ elif size < 1024**4:
53
+ val = size / (1024**3)
54
+ unit = "G"
55
+ elif size < 1024**5:
56
+ val = size / (1024**4)
57
+ unit = "T"
58
+ elif size < 1024**6:
59
+ val = size / (1024**5)
60
+ unit = "P"
61
+ else:
62
+ raise ValueError(f"Invalid size: {size}")
63
+
64
+ return val, unit
65
+
66
+ def _fmt(_val: float | int, _unit: str) -> str:
67
+ # If the float is an integer, drop the decimal, otherwise format with one decimal.
68
+ val_str: str = str(_val)
69
+ if not val_str.endswith(".0"):
70
+ first_str: str = f"{_val:.1f}"
71
+ else:
72
+ first_str = str(int(_val))
73
+ return first_str + _unit
74
+
75
+ val, unit = _convert(size)
76
+ out = _fmt(val, unit)
77
+ # Now round trip the value to fix floating point issues via rounding.
78
+ int_val = _from_size_suffix(out)
79
+ val, unit = _convert(int_val)
80
+ out = _fmt(val, unit)
81
+ return out
62
82
 
63
83
 
64
84
  # Update regex to allow decimals (e.g., 16.5MB)
65
85
  _PATTERN_SIZE_SUFFIX = re.compile(r"^(\d+(?:\.\d+)?)([A-Za-z]+)$")
66
86
 
67
87
 
88
+ def _parse_elements(value: str) -> tuple[str, str] | None:
89
+ match = _PATTERN_SIZE_SUFFIX.match(value)
90
+ if match is None:
91
+ return None
92
+ return match.group(1), match.group(2)
93
+
94
+
68
95
  def _from_size_suffix(size: str) -> int:
69
96
  if size == "0":
70
97
  return 0
71
- match = _PATTERN_SIZE_SUFFIX.match(size)
72
- if match is None:
98
+ pair = _parse_elements(size)
99
+ if pair is None:
73
100
  raise ValueError(f"Invalid size suffix: {size}")
74
- num_str, suffix = match.group(1), match.group(2)
101
+ num_str, suffix = pair
75
102
  n = float(num_str)
76
103
  # Determine the unit from the first letter (e.g., "M" from "MB")
77
104
  unit = suffix[0].upper()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rclone_api
3
- Version: 1.1.26
3
+ Version: 1.1.29
4
4
  Summary: rclone api in python
5
5
  Home-page: https://github.com/zackees/rclone-api
6
6
  License: BSD 3-Clause License
@@ -13,11 +13,11 @@ 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=g9YkKCGJbWvGF7gzSKtWa8pEgphuW0-e0SySxtOirH4,6093
15
15
  rclone_api/process.py,sha256=Hgn8MGEPkBt8C6C4oIuh-n1t1GkFF2miPlIE1lh_Zbc,5045
16
- rclone_api/rclone.py,sha256=gszjs6MJFoT5tKmBB06sd0OjbtZZAtRdjvha4RrE-lI,40252
16
+ rclone_api/rclone.py,sha256=bdAhJAW5KuxmTCZsSp5LfVUgD5WWfaKtuWyZTM-AGtI,41881
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
20
- rclone_api/types.py,sha256=-jb8_cU-vTw7oFRcjUiMhXlskavp26Q67EBmENxjsPU,3941
20
+ rclone_api/types.py,sha256=AWVIoCaRkUBCXOQffxdkFCOeUhPwolQh9d2albyAGHc,4730
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
@@ -25,16 +25,16 @@ rclone_api/cmd/copy_large_s3.py,sha256=-rfedi-ZzPUdCSP8ai9LRL0y1xVkvN-viQQlk8HVU
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
- rclone_api/s3/api.py,sha256=VstlaEnBjO2JDQuCRLdTfUGvQLbfshlXXhAzimFv4Vc,3763
28
+ rclone_api/s3/api.py,sha256=qxtRDUpHYqJ7StJRtP8U_PbF_BvYRg705568SyvF-R0,3770
29
29
  rclone_api/s3/basic_ops.py,sha256=hK3366xhVEzEcjz9Gk_8lFx6MRceAk72cax6mUrr6ko,2104
30
30
  rclone_api/s3/chunk_file.py,sha256=YELR-EzR7RHpzCDGpYdzlwu21NZW5wttIDvLoONI4aU,3477
31
31
  rclone_api/s3/chunk_types.py,sha256=LbXayXY1KgVU1LkdbASD_BQ7TpVpwVnzMjtz--8LBaE,10316
32
- rclone_api/s3/chunk_uploader.py,sha256=1jQAdk35Fa9Tcq36bS65262cs7AcNG2DAFQ-NdYlWSw,9961
33
32
  rclone_api/s3/create.py,sha256=wgfkapv_j904CfKuWyiBIWJVxfAx_ftemFSUV14aT68,3149
34
33
  rclone_api/s3/types.py,sha256=yBnJ38Tjk6RlydJ-sqZ7DSfyFloy8KDYJ0mv3vlOzLE,1388
35
- rclone_api-1.1.26.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
36
- rclone_api-1.1.26.dist-info/METADATA,sha256=XP4PVPRFpIgNBCDE-dj1s2Ah4Hw5zBAo-AjAQKwu-f4,4514
37
- rclone_api-1.1.26.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
38
- rclone_api-1.1.26.dist-info/entry_points.txt,sha256=6eNqTRXKhVf8CpWNjXiOa_0Du9tHiW_HD2iQSXRsUg8,132
39
- rclone_api-1.1.26.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
40
- rclone_api-1.1.26.dist-info/RECORD,,
34
+ rclone_api/s3/upload_file_multipart.py,sha256=1jQAdk35Fa9Tcq36bS65262cs7AcNG2DAFQ-NdYlWSw,9961
35
+ rclone_api-1.1.29.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
36
+ rclone_api-1.1.29.dist-info/METADATA,sha256=BmYo65cIJ7V2miFqEfW5Kk1Y2-tpcMz1cbTUQNTZlNo,4514
37
+ rclone_api-1.1.29.dist-info/WHEEL,sha256=rF4EZyR2XVS6irmOHQIJx2SUqXLZKRMUrjsg8UwN-XQ,109
38
+ rclone_api-1.1.29.dist-info/entry_points.txt,sha256=6eNqTRXKhVf8CpWNjXiOa_0Du9tHiW_HD2iQSXRsUg8,132
39
+ rclone_api-1.1.29.dist-info/top_level.txt,sha256=EvZ7uuruUpe9RiUyEp25d1Keq7PWYNT0O_-mr8FCG5g,11
40
+ rclone_api-1.1.29.dist-info/RECORD,,