rclone-api 1.5.40__py3-none-any.whl → 1.5.42__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.
@@ -1,1100 +1,1108 @@
1
- Metadata-Version: 2.4
2
- Name: rclone_api
3
- Version: 1.5.40
4
- Summary: rclone api in python
5
- Home-page: https://github.com/zackees/rclone-api
6
- License: BSD 3-Clause License
7
- Keywords: rclone,api,python,fast,sftp,s3,backblaze
8
- Classifier: Programming Language :: Python :: 3
9
- Requires-Python: >=3.10
10
- Description-Content-Type: text/markdown
11
- License-File: LICENSE
12
- Requires-Dist: pyright>=1.1.393
13
- Requires-Dist: python-dotenv>=1.0.0
14
- Requires-Dist: certifi>=2025.1.31
15
- Requires-Dist: psutil
16
- Requires-Dist: boto3<=1.35.99,>=1.20.1
17
- Requires-Dist: sqlmodel>=0.0.23
18
- Requires-Dist: psycopg2-binary>=2.9.10
19
- Requires-Dist: httpx>=0.28.1
20
- Requires-Dist: download>=0.3.5
21
- Requires-Dist: appdirs>=1.4.4
22
- Requires-Dist: beautifulsoup4>=4.13.3
23
- Dynamic: home-page
24
- Dynamic: license-file
25
-
26
- # rclone-api
27
-
28
-
29
- ![perpetualmaniac_faster_400fd528-df15-4a04-8ad3-3cca786d7bca (2)](https://github.com/user-attachments/assets/65138e38-b115-447c-849a-4adbd27e4b67)
30
-
31
-
32
- <!--
33
- [![Linting](https://github.com/zackees/rclone-api/actions/workflows/lint.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/lint.yml)
34
- [![MacOS_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml)
35
- [![Ubuntu_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml)
36
- [![Win_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_win.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_win.yml)
37
- -->
38
-
39
-
40
- Got a lot of data to transfer quickly? This package is for you.
41
-
42
- This library was built out of necessity to transfer large amounts of AI training data. Aggressive defaults means this api will transfer faster than rclone does in stock settings.
43
-
44
- You can have [rclone](https://rclone.org/) in your path or else the api will download it.
45
-
46
- ## VFS
47
-
48
- There is a virtual file system called `FSPath` which emulates common operators for `pathlib.Path`. You can get an instance of an `FSPath` from an `Rclone` instance using the `rclone.cwd("src:path/to")` function.
49
-
50
- # Install
51
-
52
- `pip install rclone-api`
53
-
54
- pypi link: https://pypi.org/project/rclone-api/
55
-
56
- # Quick
57
-
58
- In addition to providing easy python use for rclone, this package provides additional features:
59
-
60
- * Aggressive default settings for copying / syncing operations for extreme performance.
61
- * Database Support: Dump repo information to an sqlite/postgres/mysql database.
62
- * One repo path -> table.
63
- * Scoped objects for:
64
- * Mounts.
65
- * File servers.
66
- * Enforces correct cleanup
67
- * Mounts are easier - platform specific setup and teardown.
68
- * Resumable multi-part uploads when s3 is the destination.
69
- * Fast diffing src/dst repos as a stream of `list[str]`.
70
- * Find which files need are missing and need to be copied.
71
- * Efficiently build pipelines to select copy strategy based on file size.
72
- * Walk a directory.
73
- * Breath first.
74
- * Depth first.
75
- * Use the HttpServer to slice out byte ranges from extremely large files.
76
-
77
-
78
- ## Example
79
-
80
- ```python
81
-
82
- from rclone_api import Rclone, DirListing, Config
83
-
84
- RCLONE_CONFIG = Config("""
85
- [dst]
86
- type = s3
87
- account = *********
88
- key = ************
89
- """)
90
-
91
-
92
- def test_ls_glob_png(self) -> None:
93
- rclone = Rclone(RCLONE_CONFIG)
94
- path = f"dst:{BUCKET_NAME}/my_data"
95
- listing: DirListing = rclone.ls(path, glob="*.png")
96
- self.assertGreater(len(listing.files), 0)
97
- for file in listing.files:
98
- self.assertIsInstance(file, File)
99
- # test that it ends with .png
100
- self.assertTrue(file.name.endswith(".png"))
101
- # there should be no directories with this glob
102
- self.assertEqual(len(listing.dirs), 0)
103
- ```
104
-
105
- ## API
106
-
107
- ```python
108
-
109
- from rclone_api import Rclone
110
- """
111
- Rclone API - Python interface for the Rclone command-line tool.
112
-
113
- This package provides a high-level API for interacting with Rclone,
114
- allowing file operations across various cloud storage providers.
115
- The API wraps the rclone command-line tool, providing a Pythonic interface
116
- for common operations like copying, listing, and managing remote storage.
117
- """
118
-
119
- # Import core components and utilities
120
- from datetime import datetime
121
- from pathlib import Path
122
- from typing import Generator
123
-
124
- # Import logging utilities
125
- from rclone_api import log
126
-
127
- # Import data structures and models
128
- from .completed_process import CompletedProcess
129
- from .config import Config, Parsed, Section # Configuration handling
130
- from .diff import DiffItem, DiffOption, DiffType # File comparison utilities
131
- from .dir import Dir # Directory representation
132
- from .dir_listing import DirListing # Directory contents representation
133
- from .file import File, FileItem # File representation
134
- from .file_stream import FilesStream # Streaming file listings
135
- from .filelist import FileList # File list utilities
136
- from .fs import FSPath, RemoteFS # Filesystem utilities
137
- from .http_server import HttpFetcher, HttpServer, Range # HTTP serving capabilities
138
-
139
- # Import logging configuration utilities
140
- from .log import configure_logging, setup_default_logging
141
- from .mount import Mount # Mount remote filesystems
142
- from .process import Process # Process management
143
- from .remote import Remote # Remote storage representation
144
- from .rpath import RPath # Remote path utilities
145
- from .s3.types import MultiUploadResult # S3-specific types
146
- from .types import ( # Common types
147
- ListingOption,
148
- Order,
149
- PartInfo,
150
- SizeResult,
151
- SizeSuffix,
152
- )
153
-
154
- # Set up default logging configuration when the package is imported
155
- setup_default_logging()
156
-
157
-
158
- def rclone_verbose(val: bool | None) -> bool:
159
- """
160
- Get or set the global verbosity setting for rclone operations.
161
-
162
- Controls whether rclone commands will produce detailed output.
163
- When enabled, commands will show more information about their operation.
164
-
165
- Args:
166
- val: If provided, sets the verbosity level. If None, returns the current setting.
167
-
168
- Returns:
169
- The current verbosity setting after any change.
170
- """
171
- from rclone_api.rclone_impl import rclone_verbose as _rclone_verbose
172
-
173
- return _rclone_verbose(val)
174
-
175
-
176
- class Rclone:
177
- """
178
- Main interface for interacting with Rclone.
179
-
180
- This class provides methods for all major Rclone operations including
181
- file transfers, listing, mounting, and remote management.
182
-
183
- It serves as the primary entry point for the API, wrapping the underlying
184
- implementation details and providing a clean, consistent interface.
185
- """
186
-
187
- @staticmethod
188
- def upgrade_rclone() -> Path:
189
- """
190
- Upgrade the rclone executable to the latest version.
191
-
192
- Downloads the latest rclone binary and replaces the current one.
193
-
194
- If an external rclone is already in your path then although upgrade_rclone
195
- will download the latest version, it will not affect the rclone selected.
196
-
197
- Returns:
198
- Path to the upgraded rclone executable
199
- """
200
- from rclone_api.util import upgrade_rclone
201
-
202
- return upgrade_rclone()
203
-
204
- def __init__(
205
- self, rclone_conf: Path | Config, rclone_exe: Path | None = None
206
- ) -> None:
207
- """
208
- Initialize the Rclone interface.
209
-
210
- Args:
211
- rclone_conf: Path to rclone config file or Config object
212
- rclone_exe: Optional path to rclone executable. If None, will search in PATH.
213
- """
214
- from rclone_api.rclone_impl import RcloneImpl
215
-
216
- self.impl: RcloneImpl = RcloneImpl(rclone_conf, rclone_exe)
217
-
218
- def webgui(self, other_args: list[str] | None = None) -> Process:
219
- """
220
- Launch the Rclone web GUI.
221
-
222
- Starts the built-in web interface for interacting with rclone.
223
-
224
- Args:
225
- other_args: Additional command-line arguments to pass to rclone
226
-
227
- Returns:
228
- Process object representing the running web GUI
229
- """
230
- return self.impl.webgui(other_args=other_args)
231
-
232
- def filesystem(self, src: str) -> RemoteFS:
233
- """
234
- Get a RealFS object for interacting with the local filesystem.
235
-
236
- Returns:
237
- RealFS object for local filesystem operations
238
- """
239
- return self.impl.filesystem(src=src)
240
-
241
- def cwd(self, src: str) -> FSPath:
242
- """
243
- Get the local root path for a filesystem.
244
-
245
- Args:
246
- src: Source path for the filesystem
247
-
248
- Returns:
249
- FSPath object representing the root of the filesystem
250
- """
251
- return self.impl.cwd(src=src)
252
-
253
- def launch_server(
254
- self,
255
- addr: str,
256
- user: str | None = None,
257
- password: str | None = None,
258
- other_args: list[str] | None = None,
259
- ) -> Process:
260
- """
261
- Launch the Rclone server so it can receive commands.
262
-
263
- Starts an rclone server that can be controlled remotely.
264
-
265
- Args:
266
- addr: Address and port to listen on (e.g., "localhost:5572")
267
- user: Optional username for authentication
268
- password: Optional password for authentication
269
- other_args: Additional command-line arguments
270
-
271
- Returns:
272
- Process object representing the running server
273
- """
274
- return self.impl.launch_server(
275
- addr=addr, user=user, password=password, other_args=other_args
276
- )
277
-
278
- def remote_control(
279
- self,
280
- addr: str,
281
- user: str | None = None,
282
- password: str | None = None,
283
- capture: bool | None = None,
284
- other_args: list[str] | None = None,
285
- ) -> CompletedProcess:
286
- """
287
- Send commands to a running rclone server.
288
-
289
- Args:
290
- addr: Address of the rclone server (e.g., "localhost:5572")
291
- user: Optional username for authentication
292
- password: Optional password for authentication
293
- capture: Whether to capture and return command output
294
- other_args: Additional command-line arguments
295
-
296
- Returns:
297
- CompletedProcess containing the command result
298
- """
299
- return self.impl.remote_control(
300
- addr=addr,
301
- user=user,
302
- password=password,
303
- capture=capture,
304
- other_args=other_args,
305
- )
306
-
307
- def obscure(self, password: str) -> str:
308
- """
309
- Obscure a password for use in rclone config files.
310
-
311
- Converts a plaintext password to rclone's obscured format.
312
- Note that this is not secure encryption, just light obfuscation.
313
-
314
- Args:
315
- password: The plaintext password to obscure
316
-
317
- Returns:
318
- The obscured password string
319
- """
320
- return self.impl.obscure(password=password)
321
-
322
- def ls_stream(
323
- self,
324
- src: str,
325
- max_depth: int = -1,
326
- fast_list: bool = False,
327
- ) -> FilesStream:
328
- """
329
- List files in the given path as a stream of results.
330
-
331
- This method is memory-efficient for large directories as it yields
332
- results incrementally rather than collecting them all at once.
333
-
334
- Args:
335
- src: Remote path to list
336
- max_depth: Maximum recursion depth (-1 for unlimited)
337
- fast_list: Use fast list (only recommended for listing entire repositories or small datasets)
338
-
339
- Returns:
340
- A stream of file entries that can be iterated over
341
- """
342
- return self.impl.ls_stream(src=src, max_depth=max_depth, fast_list=fast_list)
343
-
344
- def save_to_db(
345
- self,
346
- src: str,
347
- db_url: str, # sqalchemy style url, use sqlite:///data.db or mysql://user:pass@localhost/db or postgres://user:pass@localhost/db
348
- max_depth: int = -1,
349
- fast_list: bool = False,
350
- ) -> None:
351
- """
352
- Save files to a database (sqlite, mysql, postgres).
353
-
354
- Lists all files in the source path and stores their metadata in a database.
355
- Useful for creating searchable indexes of remote storage.
356
-
357
- Args:
358
- src: Remote path to list, this will be used to populate an entire table, so always use the root-most path.
359
- db_url: Database URL, like sqlite:///data.db or mysql://user:pass@localhost/db or postgres://user:pass@localhost/db
360
- max_depth: Maximum depth to traverse (-1 for unlimited)
361
- fast_list: Use fast list (only use when getting THE entire data repository from the root/bucket)
362
- """
363
- return self.impl.save_to_db(
364
- src=src, db_url=db_url, max_depth=max_depth, fast_list=fast_list
365
- )
366
-
367
- def ls(
368
- self,
369
- src: Dir | Remote | str | None = None,
370
- max_depth: int | None = None,
371
- glob: str | None = None,
372
- order: Order = Order.NORMAL,
373
- listing_option: ListingOption = ListingOption.ALL,
374
- ) -> DirListing:
375
- """
376
- List files and directories at the specified path.
377
-
378
- Provides a detailed listing with file metadata.
379
-
380
- Args:
381
- src: Path to list (Dir, Remote, or string path)
382
- max_depth: Maximum recursion depth (None for default)
383
- glob: Optional glob pattern to filter results
384
- order: Sorting order for the results
385
- listing_option: What types of entries to include
386
-
387
- Returns:
388
- DirListing object containing the results
389
- """
390
- return self.impl.ls(
391
- src=src,
392
- max_depth=max_depth,
393
- glob=glob,
394
- order=order,
395
- listing_option=listing_option,
396
- )
397
-
398
- def listremotes(self) -> list[Remote]:
399
- """
400
- List all configured remotes.
401
-
402
- Returns a list of all remotes defined in the rclone configuration.
403
-
404
- Returns:
405
- List of Remote objects
406
- """
407
- return self.impl.listremotes()
408
-
409
- def diff(
410
- self,
411
- src: str,
412
- dst: str,
413
- min_size: (
414
- str | None
415
- ) = None, # e. g. "1MB" - see rclone documentation: https://rclone.org/commands/rclone_check/
416
- max_size: (
417
- str | None
418
- ) = None, # e. g. "1GB" - see rclone documentation: https://rclone.org/commands/rclone_check/
419
- diff_option: DiffOption = DiffOption.COMBINED,
420
- fast_list: bool = True,
421
- size_only: bool | None = None,
422
- checkers: int | None = None,
423
- other_args: list[str] | None = None,
424
- ) -> Generator[DiffItem, None, None]:
425
- """
426
- Compare two directories and yield differences.
427
-
428
- Be extra careful with the src and dst values. If you are off by one
429
- parent directory, you will get a huge amount of false diffs.
430
-
431
- Args:
432
- src: Rclone style src path
433
- dst: Rclone style dst path
434
- min_size: Minimum file size to check (e.g., "1MB")
435
- max_size: Maximum file size to check (e.g., "1GB")
436
- diff_option: How to report differences
437
- fast_list: Whether to use fast listing
438
- size_only: Compare only file sizes, not content
439
- checkers: Number of checker threads
440
- other_args: Additional command-line arguments
441
-
442
- Yields:
443
- DiffItem objects representing each difference found
444
- """
445
- return self.impl.diff(
446
- src=src,
447
- dst=dst,
448
- min_size=min_size,
449
- max_size=max_size,
450
- diff_option=diff_option,
451
- fast_list=fast_list,
452
- size_only=size_only,
453
- checkers=checkers,
454
- other_args=other_args,
455
- )
456
-
457
- def walk(
458
- self,
459
- src: Dir | Remote | str,
460
- max_depth: int = -1,
461
- breadth_first: bool = True,
462
- order: Order = Order.NORMAL,
463
- ) -> Generator[DirListing, None, None]:
464
- """
465
- Walk through the given path recursively, yielding directory listings.
466
-
467
- Similar to os.walk(), but for remote storage. Traverses directories
468
- and yields their contents.
469
-
470
- Args:
471
- src: Remote path, Dir, or Remote object to walk through
472
- max_depth: Maximum depth to traverse (-1 for unlimited)
473
- breadth_first: If True, use breadth-first traversal, otherwise depth-first
474
- order: Sorting order for directory entries
475
-
476
- Yields:
477
- DirListing: Directory listing for each directory encountered
478
- """
479
- return self.impl.walk(
480
- src=src, max_depth=max_depth, breadth_first=breadth_first, order=order
481
- )
482
-
483
- def scan_missing_folders(
484
- self,
485
- src: Dir | Remote | str,
486
- dst: Dir | Remote | str,
487
- max_depth: int = -1,
488
- order: Order = Order.NORMAL,
489
- ) -> Generator[Dir, None, None]:
490
- """
491
- Find folders that exist in source but are missing in destination.
492
-
493
- Useful for identifying directories that need to be created before
494
- copying files.
495
-
496
- Args:
497
- src: Source directory or Remote to scan
498
- dst: Destination directory or Remote to compare against
499
- max_depth: Maximum depth to traverse (-1 for unlimited)
500
- order: Sorting order for directory entries
501
-
502
- Yields:
503
- Dir: Each directory that exists in source but not in destination
504
- """
505
- return self.impl.scan_missing_folders(
506
- src=src, dst=dst, max_depth=max_depth, order=order
507
- )
508
-
509
- def cleanup(
510
- self, src: str, other_args: list[str] | None = None
511
- ) -> CompletedProcess:
512
- """
513
- Cleanup any resources used by the Rclone instance.
514
-
515
- Removes temporary files and directories created by rclone.
516
-
517
- Args:
518
- src: Path to clean up
519
- other_args: Additional command-line arguments
520
-
521
- Returns:
522
- CompletedProcess with the result of the cleanup operation
523
- """
524
- return self.impl.cleanup(src=src, other_args=other_args)
525
-
526
- def copy_to(
527
- self,
528
- src: File | str,
529
- dst: File | str,
530
- check: bool | None = None,
531
- verbose: bool | None = None,
532
- other_args: list[str] | None = None,
533
- ) -> CompletedProcess:
534
- """
535
- Copy one file from source to destination.
536
-
537
- Warning - this can be slow for large files or when copying between
538
- different storage providers.
539
-
540
- Args:
541
- src: Rclone style src path
542
- dst: Rclone style dst path
543
- check: Whether to verify the copy with checksums
544
- verbose: Whether to show detailed progress
545
- other_args: Additional command-line arguments
546
-
547
- Returns:
548
- CompletedProcess with the result of the copy operation
549
- """
550
- return self.impl.copy_to(
551
- src=src, dst=dst, check=check, verbose=verbose, other_args=other_args
552
- )
553
-
554
- def copy_files(
555
- self,
556
- src: str,
557
- dst: str,
558
- files: list[str] | Path,
559
- check: bool | None = None,
560
- max_backlog: int | None = None,
561
- verbose: bool | None = None,
562
- checkers: int | None = None,
563
- transfers: int | None = None,
564
- low_level_retries: int | None = None,
565
- retries: int | None = None,
566
- retries_sleep: str | None = None,
567
- metadata: bool | None = None,
568
- timeout: str | None = None,
569
- max_partition_workers: int | None = None,
570
- multi_thread_streams: int | None = None,
571
- other_args: list[str] | None = None,
572
- ) -> list[CompletedProcess]:
573
- """
574
- Copy multiple files from source to destination.
575
-
576
- Efficiently copies a list of files, potentially in parallel.
577
-
578
- Args:
579
- src: Rclone style src path
580
- dst: Rclone style dst path
581
- files: List of file paths relative to src, or Path to a file containing the list
582
- check: Whether to verify copies with checksums
583
- max_backlog: Maximum number of queued transfers
584
- verbose: Whether to show detailed progress
585
- checkers: Number of checker threads
586
- transfers: Number of file transfers to run in parallel
587
- low_level_retries: Number of low-level retries
588
- retries: Number of high-level retries
589
- retries_sleep: Sleep interval between retries (e.g., "10s")
590
- metadata: Whether to preserve metadata
591
- timeout: IO idle timeout (e.g., "5m")
592
- max_partition_workers: Maximum number of partition workers
593
- multi_thread_streams: Number of streams for multi-thread copy
594
- other_args: Additional command-line arguments
595
-
596
- Returns:
597
- List of CompletedProcess objects for each copy operation
598
- """
599
- return self.impl.copy_files(
600
- src=src,
601
- dst=dst,
602
- files=files,
603
- check=check,
604
- max_backlog=max_backlog,
605
- verbose=verbose,
606
- checkers=checkers,
607
- transfers=transfers,
608
- low_level_retries=low_level_retries,
609
- retries=retries,
610
- retries_sleep=retries_sleep,
611
- metadata=metadata,
612
- timeout=timeout,
613
- max_partition_workers=max_partition_workers,
614
- multi_thread_streams=multi_thread_streams,
615
- other_args=other_args,
616
- )
617
-
618
- def copy(
619
- self,
620
- src: Dir | str,
621
- dst: Dir | str,
622
- check: bool | None = None,
623
- transfers: int | None = None,
624
- checkers: int | None = None,
625
- multi_thread_streams: int | None = None,
626
- low_level_retries: int | None = None,
627
- retries: int | None = None,
628
- other_args: list[str] | None = None,
629
- ) -> CompletedProcess:
630
- """
631
- Copy files from source to destination.
632
-
633
- Recursively copies all files from src to dst.
634
-
635
- Args:
636
- src: Rclone style src path
637
- dst: Rclone style dst path
638
- check: Whether to verify copies with checksums
639
- transfers: Number of file transfers to run in parallel
640
- checkers: Number of checker threads
641
- multi_thread_streams: Number of streams for multi-thread copy
642
- low_level_retries: Number of low-level retries
643
- retries: Number of high-level retries
644
- other_args: Additional command-line arguments
645
-
646
- Returns:
647
- CompletedProcess with the result of the copy operation
648
- """
649
- return self.impl.copy(
650
- src=src,
651
- dst=dst,
652
- check=check,
653
- transfers=transfers,
654
- checkers=checkers,
655
- multi_thread_streams=multi_thread_streams,
656
- low_level_retries=low_level_retries,
657
- retries=retries,
658
- other_args=other_args,
659
- )
660
-
661
- def purge(self, src: Dir | str) -> CompletedProcess:
662
- """
663
- Purge a directory.
664
-
665
- Removes a directory and all its contents.
666
-
667
- Args:
668
- src: Rclone style path
669
-
670
- Returns:
671
- CompletedProcess with the result of the purge operation
672
- """
673
- return self.impl.purge(src=src)
674
-
675
- def delete_files(
676
- self,
677
- files: str | File | list[str] | list[File],
678
- check: bool | None = None,
679
- rmdirs=False,
680
- verbose: bool | None = None,
681
- max_partition_workers: int | None = None,
682
- other_args: list[str] | None = None,
683
- ) -> CompletedProcess:
684
- """
685
- Delete files or directories.
686
-
687
- Args:
688
- files: Files to delete (single file/path or list)
689
- check: Whether to verify deletions
690
- rmdirs: Whether to remove empty directories
691
- verbose: Whether to show detailed progress
692
- max_partition_workers: Maximum number of partition workers
693
- other_args: Additional command-line arguments
694
-
695
- Returns:
696
- CompletedProcess with the result of the delete operation
697
- """
698
- return self.impl.delete_files(
699
- files=files,
700
- check=check,
701
- rmdirs=rmdirs,
702
- verbose=verbose,
703
- max_partition_workers=max_partition_workers,
704
- other_args=other_args,
705
- )
706
-
707
- def exists(self, src: Dir | Remote | str | File) -> bool:
708
- """
709
- Check if a file or directory exists.
710
-
711
- Args:
712
- src: Path to check (Dir, Remote, File, or path string)
713
-
714
- Returns:
715
- True if the path exists, False otherwise
716
- """
717
- return self.impl.exists(src=src)
718
-
719
- def is_synced(self, src: str | Dir, dst: str | Dir) -> bool:
720
- """
721
- Check if two directories are in sync.
722
-
723
- Compares the contents of src and dst to determine if they match.
724
-
725
- Args:
726
- src: Source directory (Dir object or path string)
727
- dst: Destination directory (Dir object or path string)
728
-
729
- Returns:
730
- True if the directories are in sync, False otherwise
731
- """
732
- return self.impl.is_synced(src=src, dst=dst)
733
-
734
- def modtime(self, src: str) -> str | Exception:
735
- """
736
- Get the modification time of a file or directory.
737
-
738
- Args:
739
- src: Path to the file or directory
740
-
741
- Returns:
742
- Modification time as a string, or Exception if an error occurred
743
- """
744
- return self.impl.modtime(src=src)
745
-
746
- def modtime_dt(self, src: str) -> datetime | Exception:
747
- """
748
- Get the modification time of a file or directory as a datetime object.
749
-
750
- Args:
751
- src: Path to the file or directory
752
-
753
- Returns:
754
- Modification time as a datetime object, or Exception if an error occurred
755
- """
756
- return self.impl.modtime_dt(src=src)
757
-
758
- def write_text(
759
- self,
760
- text: str,
761
- dst: str,
762
- ) -> Exception | None:
763
- """
764
- Write text to a file.
765
-
766
- Creates or overwrites the file at dst with the given text.
767
-
768
- Args:
769
- text: Text content to write
770
- dst: Destination file path
771
-
772
- Returns:
773
- None if successful, Exception if an error occurred
774
- """
775
- return self.impl.write_text(text=text, dst=dst)
776
-
777
- def write_bytes(
778
- self,
779
- data: bytes,
780
- dst: str,
781
- ) -> Exception | None:
782
- """
783
- Write bytes to a file.
784
-
785
- Creates or overwrites the file at dst with the given binary data.
786
-
787
- Args:
788
- data: Binary content to write
789
- dst: Destination file path
790
-
791
- Returns:
792
- None if successful, Exception if an error occurred
793
- """
794
- return self.impl.write_bytes(data=data, dst=dst)
795
-
796
- def read_bytes(self, src: str) -> bytes | Exception:
797
- """
798
- Read bytes from a file.
799
-
800
- Args:
801
- src: Source file path
802
-
803
- Returns:
804
- File contents as bytes, or Exception if an error occurred
805
- """
806
- return self.impl.read_bytes(src=src)
807
-
808
- def read_text(self, src: str) -> str | Exception:
809
- """
810
- Read text from a file.
811
-
812
- Args:
813
- src: Source file path
814
-
815
- Returns:
816
- File contents as a string, or Exception if an error occurred
817
- """
818
- return self.impl.read_text(src=src)
819
-
820
- def copy_bytes(
821
- self,
822
- src: str,
823
- offset: int | SizeSuffix,
824
- length: int | SizeSuffix,
825
- outfile: Path,
826
- other_args: list[str] | None = None,
827
- ) -> Exception | None:
828
- """
829
- Copy a slice of bytes from the src file to dst.
830
-
831
- Extracts a portion of a file based on offset and length.
832
-
833
- Args:
834
- src: Source file path
835
- offset: Starting position in the source file
836
- length: Number of bytes to copy
837
- outfile: Local file path to write the bytes to
838
- other_args: Additional command-line arguments
839
-
840
- Returns:
841
- None if successful, Exception if an error occurred
842
- """
843
- return self.impl.copy_bytes(
844
- src=src,
845
- offset=offset,
846
- length=length,
847
- outfile=outfile,
848
- other_args=other_args,
849
- )
850
-
851
- def copy_dir(
852
- self, src: str | Dir, dst: str | Dir, args: list[str] | None = None
853
- ) -> CompletedProcess:
854
- """
855
- Copy a directory from source to destination.
856
-
857
- Recursively copies all files and subdirectories.
858
-
859
- Args:
860
- src: Source directory (Dir object or path string)
861
- dst: Destination directory (Dir object or path string)
862
- args: Additional command-line arguments
863
-
864
- Returns:
865
- CompletedProcess with the result of the copy operation
866
- """
867
- # convert src to str, also dst
868
- return self.impl.copy_dir(src=src, dst=dst, args=args)
869
-
870
- def copy_remote(
871
- self, src: Remote, dst: Remote, args: list[str] | None = None
872
- ) -> CompletedProcess:
873
- """
874
- Copy a remote to another remote.
875
-
876
- Copies all contents from one remote storage to another.
877
-
878
- Args:
879
- src: Source remote
880
- dst: Destination remote
881
- args: Additional command-line arguments
882
-
883
- Returns:
884
- CompletedProcess with the result of the copy operation
885
- """
886
- return self.impl.copy_remote(src=src, dst=dst, args=args)
887
-
888
- def copy_file_s3_resumable(
889
- self,
890
- src: str, # src:/Bucket/path/myfile.large.zst
891
- dst: str, # dst:/Bucket/path/myfile.large.zst
892
- part_infos: list[PartInfo] | None = None,
893
- upload_threads: int = 8, # Number of reader and writer threads to use
894
- merge_threads: int = 4, # Number of threads to use for merging the parts
895
- ) -> Exception | None:
896
- """
897
- Copy a large file to S3 with resumable upload capability.
898
-
899
- This method splits the file into parts for parallel upload and can
900
- resume interrupted transfers using a custom algorithm in python.
901
-
902
- Particularly useful for very large files where network interruptions
903
- are likely.
904
-
905
- Args:
906
- src: Source file path (format: remote:bucket/path/file)
907
- dst: Destination file path (format: remote:bucket/path/file)
908
- part_infos: Optional list of part information for resuming uploads
909
- upload_threads: Number of parallel upload threads
910
- merge_threads: Number of threads for merging uploaded parts
911
-
912
- Returns:
913
- None if successful, Exception if an error occurred
914
- """
915
- return self.impl.copy_file_s3_resumable(
916
- src=src,
917
- dst=dst,
918
- part_infos=part_infos,
919
- upload_threads=upload_threads,
920
- merge_threads=merge_threads,
921
- )
922
-
923
- def mount(
924
- self,
925
- src: Remote | Dir | str,
926
- outdir: Path,
927
- allow_writes: bool | None = False,
928
- transfers: int | None = None, # number of writes to perform in parallel
929
- use_links: bool | None = None,
930
- vfs_cache_mode: str | None = None,
931
- verbose: bool | None = None,
932
- cache_dir: Path | None = None,
933
- cache_dir_delete_on_exit: bool | None = None,
934
- log: Path | None = None,
935
- other_args: list[str] | None = None,
936
- ) -> Mount:
937
- """
938
- Mount a remote or directory to a local path.
939
-
940
- Makes remote storage accessible as a local filesystem.
941
-
942
- Args:
943
- src: Remote or directory to mount
944
- outdir: Local path to mount to
945
- allow_writes: Whether to allow write operations
946
- transfers: Number of parallel write operations
947
- use_links: Whether to use symbolic links
948
- vfs_cache_mode: VFS cache mode (e.g., "full", "minimal")
949
- verbose: Whether to show detailed output
950
- cache_dir: Directory to use for caching
951
- cache_dir_delete_on_exit: Whether to delete cache on exit
952
- log: Path to write logs to
953
- other_args: Additional command-line arguments
954
-
955
- Returns:
956
- Mount object representing the mounted filesystem
957
- """
958
- return self.impl.mount(
959
- src=src,
960
- outdir=outdir,
961
- allow_writes=allow_writes,
962
- transfers=transfers,
963
- use_links=use_links,
964
- vfs_cache_mode=vfs_cache_mode,
965
- verbose=verbose,
966
- cache_dir=cache_dir,
967
- cache_dir_delete_on_exit=cache_dir_delete_on_exit,
968
- log=log,
969
- other_args=other_args,
970
- )
971
-
972
- def serve_http(
973
- self,
974
- src: str,
975
- addr: str | None = None,
976
- other_args: list[str] | None = None,
977
- ) -> HttpServer:
978
- """
979
- Serve a remote or directory via HTTP.
980
-
981
- Creates an HTTP server that provides access to the specified remote.
982
- The returned HttpServer object includes a client for fetching files.
983
-
984
- This is useful for providing web access to remote storage or for
985
- accessing remote files from applications that support HTTP but not
986
- the remote's native protocol.
987
-
988
- Args:
989
- src: Remote or directory to serve
990
- addr: Network address and port to serve on (default: localhost:8080)
991
- other_args: Additional arguments to pass to rclone
992
-
993
- Returns:
994
- HttpServer object with methods for accessing the served content
995
- """
996
- return self.impl.serve_http(
997
- src=src, cache_mode="minimal", addr=addr, other_args=other_args
998
- )
999
-
1000
- def size_files(
1001
- self,
1002
- src: str,
1003
- files: list[str],
1004
- fast_list: bool = False, # Recommend that this is False
1005
- other_args: list[str] | None = None,
1006
- check: bool | None = False,
1007
- verbose: bool | None = None,
1008
- ) -> SizeResult | Exception:
1009
- """
1010
- Get the size of a list of files.
1011
-
1012
- Calculates the total size of the specified files.
1013
-
1014
- Args:
1015
- src: Base path for the files
1016
- files: List of file paths relative to src
1017
- fast_list: Whether to use fast listing (not recommended for accuracy)
1018
- other_args: Additional command-line arguments
1019
- check: Whether to verify file integrity
1020
- verbose: Whether to show detailed output
1021
-
1022
- Returns:
1023
- SizeResult with size information, or Exception if an error occurred
1024
-
1025
- Example:
1026
- size_files("remote:bucket", ["path/to/file1", "path/to/file2"])
1027
- """
1028
- return self.impl.size_files(
1029
- src=src,
1030
- files=files,
1031
- fast_list=fast_list,
1032
- other_args=other_args,
1033
- check=check,
1034
- verbose=verbose,
1035
- )
1036
-
1037
- def size_file(self, src: str) -> SizeSuffix | Exception:
1038
- """
1039
- Get the size of a file.
1040
-
1041
- Args:
1042
- src: Path to the file
1043
-
1044
- Returns:
1045
- SizeSuffix object representing the file size, or Exception if an error occurred
1046
- """
1047
- return self.impl.size_file(src=src)
1048
-
1049
-
1050
- # Export public API components
1051
- __all__ = [
1052
- # Main classes
1053
- "Rclone", # Primary interface
1054
- "File", # File representation
1055
- "Config", # Configuration handling
1056
- "Remote", # Remote storage
1057
- "Dir", # Directory representation
1058
- "RPath", # Remote path utilities
1059
- "DirListing", # Directory listing
1060
- "FileList", # File list
1061
- "FileItem", # File item
1062
- "Process", # Process management
1063
- "DiffItem", # Difference item
1064
- "DiffType", # Difference type
1065
- # Functions
1066
- "rclone_verbose", # Verbosity control
1067
- # Data classes and enums
1068
- "CompletedProcess", # Process result
1069
- "DiffOption", # Difference options
1070
- "ListingOption", # Listing options
1071
- "Order", # Sorting order
1072
- "SizeResult", # Size result
1073
- "Parsed", # Parsed configuration
1074
- "Section", # Configuration section
1075
- "MultiUploadResult", # S3 upload result
1076
- "SizeSuffix", # Size with suffix
1077
- # Utilities
1078
- "configure_logging", # Logging configuration
1079
- "log", # Logging utilities
1080
- "HttpServer", # HTTP server
1081
- "Range", # HTTP range
1082
- "HttpFetcher", # HTTP fetcher
1083
- "PartInfo", # Part information for uploads
1084
- ]
1085
- ```
1086
-
1087
-
1088
- # Contributing
1089
-
1090
- ```bash
1091
- git clone https://github.comn/zackees/rclone-api
1092
- cd rclone-api
1093
- ./install
1094
- ./lint
1095
- ./test
1096
- ```
1097
-
1098
- # Windows
1099
-
1100
- This environment requires you to use `git-bash`.
1
+ Metadata-Version: 2.4
2
+ Name: rclone_api
3
+ Version: 1.5.42
4
+ Summary: rclone api in python
5
+ Home-page: https://github.com/zackees/rclone-api
6
+ License: BSD 3-Clause License
7
+ Keywords: rclone,api,python,fast,sftp,s3,backblaze
8
+ Classifier: Programming Language :: Python :: 3
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: pyright>=1.1.393
13
+ Requires-Dist: python-dotenv>=1.0.0
14
+ Requires-Dist: certifi>=2025.1.31
15
+ Requires-Dist: psutil
16
+ Requires-Dist: boto3<=1.35.99,>=1.20.1
17
+ Requires-Dist: sqlmodel>=0.0.23
18
+ Requires-Dist: psycopg2-binary>=2.9.10
19
+ Requires-Dist: httpx>=0.28.1
20
+ Requires-Dist: download>=0.3.5
21
+ Requires-Dist: appdirs>=1.4.4
22
+ Requires-Dist: beautifulsoup4>=4.13.3
23
+ Dynamic: home-page
24
+ Dynamic: license-file
25
+
26
+ # rclone-api
27
+
28
+
29
+ ![perpetualmaniac_faster_400fd528-df15-4a04-8ad3-3cca786d7bca (2)](https://github.com/user-attachments/assets/65138e38-b115-447c-849a-4adbd27e4b67)
30
+
31
+
32
+ <!--
33
+ [![Linting](https://github.com/zackees/rclone-api/actions/workflows/lint.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/lint.yml)
34
+ [![MacOS_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_macos.yml)
35
+ [![Ubuntu_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_ubuntu.yml)
36
+ [![Win_Tests](https://github.com/zackees/rclone-api/actions/workflows/push_win.yml/badge.svg)](https://github.com/zackees/rclone-api/actions/workflows/push_win.yml)
37
+ -->
38
+
39
+
40
+ Got a lot of data to transfer quickly? This package is for you.
41
+
42
+ This library was built out of necessity to transfer large amounts of AI training data. Aggressive defaults means this api will transfer faster than rclone does in stock settings.
43
+
44
+ You can have [rclone](https://rclone.org/) in your path or else the api will download it.
45
+
46
+ ## VFS
47
+
48
+ There is a virtual file system called `FSPath` which emulates common operators for `pathlib.Path`. You can get an instance of an `FSPath` from an `Rclone` instance using the `rclone.cwd("src:path/to")` function.
49
+
50
+ ## ENVS
51
+
52
+ * RCLONE_CONFIG
53
+ * path string of the rclone.conf text file
54
+
55
+ * RCLONE_CONFIG_JSON
56
+ * string content of rclone config.json
57
+
58
+ # Install
59
+
60
+ `pip install rclone-api`
61
+
62
+ pypi link: https://pypi.org/project/rclone-api/
63
+
64
+ # Quick
65
+
66
+ In addition to providing easy python use for rclone, this package provides additional features:
67
+
68
+ * Aggressive default settings for copying / syncing operations for extreme performance.
69
+ * Database Support: Dump repo information to an sqlite/postgres/mysql database.
70
+ * One repo path -> table.
71
+ * Scoped objects for:
72
+ * Mounts.
73
+ * File servers.
74
+ * Enforces correct cleanup
75
+ * Mounts are easier - platform specific setup and teardown.
76
+ * Resumable multi-part uploads when s3 is the destination.
77
+ * Fast diffing src/dst repos as a stream of `list[str]`.
78
+ * Find which files need are missing and need to be copied.
79
+ * Efficiently build pipelines to select copy strategy based on file size.
80
+ * Walk a directory.
81
+ * Breath first.
82
+ * Depth first.
83
+ * Use the HttpServer to slice out byte ranges from extremely large files.
84
+
85
+
86
+ ## Example
87
+
88
+ ```python
89
+
90
+ from rclone_api import Rclone, DirListing, Config
91
+
92
+ RCLONE_CONFIG = Config("""
93
+ [dst]
94
+ type = s3
95
+ account = *********
96
+ key = ************
97
+ """)
98
+
99
+
100
+ def test_ls_glob_png(self) -> None:
101
+ rclone = Rclone(RCLONE_CONFIG)
102
+ path = f"dst:{BUCKET_NAME}/my_data"
103
+ listing: DirListing = rclone.ls(path, glob="*.png")
104
+ self.assertGreater(len(listing.files), 0)
105
+ for file in listing.files:
106
+ self.assertIsInstance(file, File)
107
+ # test that it ends with .png
108
+ self.assertTrue(file.name.endswith(".png"))
109
+ # there should be no directories with this glob
110
+ self.assertEqual(len(listing.dirs), 0)
111
+ ```
112
+
113
+ ## API
114
+
115
+ ```python
116
+
117
+ from rclone_api import Rclone
118
+ """
119
+ Rclone API - Python interface for the Rclone command-line tool.
120
+
121
+ This package provides a high-level API for interacting with Rclone,
122
+ allowing file operations across various cloud storage providers.
123
+ The API wraps the rclone command-line tool, providing a Pythonic interface
124
+ for common operations like copying, listing, and managing remote storage.
125
+ """
126
+
127
+ # Import core components and utilities
128
+ from datetime import datetime
129
+ from pathlib import Path
130
+ from typing import Generator
131
+
132
+ # Import logging utilities
133
+ from rclone_api import log
134
+
135
+ # Import data structures and models
136
+ from .completed_process import CompletedProcess
137
+ from .config import Config, Parsed, Section # Configuration handling
138
+ from .diff import DiffItem, DiffOption, DiffType # File comparison utilities
139
+ from .dir import Dir # Directory representation
140
+ from .dir_listing import DirListing # Directory contents representation
141
+ from .file import File, FileItem # File representation
142
+ from .file_stream import FilesStream # Streaming file listings
143
+ from .filelist import FileList # File list utilities
144
+ from .fs import FSPath, RemoteFS # Filesystem utilities
145
+ from .http_server import HttpFetcher, HttpServer, Range # HTTP serving capabilities
146
+
147
+ # Import logging configuration utilities
148
+ from .log import configure_logging, setup_default_logging
149
+ from .mount import Mount # Mount remote filesystems
150
+ from .process import Process # Process management
151
+ from .remote import Remote # Remote storage representation
152
+ from .rpath import RPath # Remote path utilities
153
+ from .s3.types import MultiUploadResult # S3-specific types
154
+ from .types import ( # Common types
155
+ ListingOption,
156
+ Order,
157
+ PartInfo,
158
+ SizeResult,
159
+ SizeSuffix,
160
+ )
161
+
162
+ # Set up default logging configuration when the package is imported
163
+ setup_default_logging()
164
+
165
+
166
+ def rclone_verbose(val: bool | None) -> bool:
167
+ """
168
+ Get or set the global verbosity setting for rclone operations.
169
+
170
+ Controls whether rclone commands will produce detailed output.
171
+ When enabled, commands will show more information about their operation.
172
+
173
+ Args:
174
+ val: If provided, sets the verbosity level. If None, returns the current setting.
175
+
176
+ Returns:
177
+ The current verbosity setting after any change.
178
+ """
179
+ from rclone_api.rclone_impl import rclone_verbose as _rclone_verbose
180
+
181
+ return _rclone_verbose(val)
182
+
183
+
184
+ class Rclone:
185
+ """
186
+ Main interface for interacting with Rclone.
187
+
188
+ This class provides methods for all major Rclone operations including
189
+ file transfers, listing, mounting, and remote management.
190
+
191
+ It serves as the primary entry point for the API, wrapping the underlying
192
+ implementation details and providing a clean, consistent interface.
193
+ """
194
+
195
+ @staticmethod
196
+ def upgrade_rclone() -> Path:
197
+ """
198
+ Upgrade the rclone executable to the latest version.
199
+
200
+ Downloads the latest rclone binary and replaces the current one.
201
+
202
+ If an external rclone is already in your path then although upgrade_rclone
203
+ will download the latest version, it will not affect the rclone selected.
204
+
205
+ Returns:
206
+ Path to the upgraded rclone executable
207
+ """
208
+ from rclone_api.util import upgrade_rclone
209
+
210
+ return upgrade_rclone()
211
+
212
+ def __init__(
213
+ self, rclone_conf: Path | Config, rclone_exe: Path | None = None
214
+ ) -> None:
215
+ """
216
+ Initialize the Rclone interface.
217
+
218
+ Args:
219
+ rclone_conf: Path to rclone config file or Config object
220
+ rclone_exe: Optional path to rclone executable. If None, will search in PATH.
221
+ """
222
+ from rclone_api.rclone_impl import RcloneImpl
223
+
224
+ self.impl: RcloneImpl = RcloneImpl(rclone_conf, rclone_exe)
225
+
226
+ def webgui(self, other_args: list[str] | None = None) -> Process:
227
+ """
228
+ Launch the Rclone web GUI.
229
+
230
+ Starts the built-in web interface for interacting with rclone.
231
+
232
+ Args:
233
+ other_args: Additional command-line arguments to pass to rclone
234
+
235
+ Returns:
236
+ Process object representing the running web GUI
237
+ """
238
+ return self.impl.webgui(other_args=other_args)
239
+
240
+ def filesystem(self, src: str) -> RemoteFS:
241
+ """
242
+ Get a RealFS object for interacting with the local filesystem.
243
+
244
+ Returns:
245
+ RealFS object for local filesystem operations
246
+ """
247
+ return self.impl.filesystem(src=src)
248
+
249
+ def cwd(self, src: str) -> FSPath:
250
+ """
251
+ Get the local root path for a filesystem.
252
+
253
+ Args:
254
+ src: Source path for the filesystem
255
+
256
+ Returns:
257
+ FSPath object representing the root of the filesystem
258
+ """
259
+ return self.impl.cwd(src=src)
260
+
261
+ def launch_server(
262
+ self,
263
+ addr: str,
264
+ user: str | None = None,
265
+ password: str | None = None,
266
+ other_args: list[str] | None = None,
267
+ ) -> Process:
268
+ """
269
+ Launch the Rclone server so it can receive commands.
270
+
271
+ Starts an rclone server that can be controlled remotely.
272
+
273
+ Args:
274
+ addr: Address and port to listen on (e.g., "localhost:5572")
275
+ user: Optional username for authentication
276
+ password: Optional password for authentication
277
+ other_args: Additional command-line arguments
278
+
279
+ Returns:
280
+ Process object representing the running server
281
+ """
282
+ return self.impl.launch_server(
283
+ addr=addr, user=user, password=password, other_args=other_args
284
+ )
285
+
286
+ def remote_control(
287
+ self,
288
+ addr: str,
289
+ user: str | None = None,
290
+ password: str | None = None,
291
+ capture: bool | None = None,
292
+ other_args: list[str] | None = None,
293
+ ) -> CompletedProcess:
294
+ """
295
+ Send commands to a running rclone server.
296
+
297
+ Args:
298
+ addr: Address of the rclone server (e.g., "localhost:5572")
299
+ user: Optional username for authentication
300
+ password: Optional password for authentication
301
+ capture: Whether to capture and return command output
302
+ other_args: Additional command-line arguments
303
+
304
+ Returns:
305
+ CompletedProcess containing the command result
306
+ """
307
+ return self.impl.remote_control(
308
+ addr=addr,
309
+ user=user,
310
+ password=password,
311
+ capture=capture,
312
+ other_args=other_args,
313
+ )
314
+
315
+ def obscure(self, password: str) -> str:
316
+ """
317
+ Obscure a password for use in rclone config files.
318
+
319
+ Converts a plaintext password to rclone's obscured format.
320
+ Note that this is not secure encryption, just light obfuscation.
321
+
322
+ Args:
323
+ password: The plaintext password to obscure
324
+
325
+ Returns:
326
+ The obscured password string
327
+ """
328
+ return self.impl.obscure(password=password)
329
+
330
+ def ls_stream(
331
+ self,
332
+ src: str,
333
+ max_depth: int = -1,
334
+ fast_list: bool = False,
335
+ ) -> FilesStream:
336
+ """
337
+ List files in the given path as a stream of results.
338
+
339
+ This method is memory-efficient for large directories as it yields
340
+ results incrementally rather than collecting them all at once.
341
+
342
+ Args:
343
+ src: Remote path to list
344
+ max_depth: Maximum recursion depth (-1 for unlimited)
345
+ fast_list: Use fast list (only recommended for listing entire repositories or small datasets)
346
+
347
+ Returns:
348
+ A stream of file entries that can be iterated over
349
+ """
350
+ return self.impl.ls_stream(src=src, max_depth=max_depth, fast_list=fast_list)
351
+
352
+ def save_to_db(
353
+ self,
354
+ src: str,
355
+ db_url: str, # sqalchemy style url, use sqlite:///data.db or mysql://user:pass@localhost/db or postgres://user:pass@localhost/db
356
+ max_depth: int = -1,
357
+ fast_list: bool = False,
358
+ ) -> None:
359
+ """
360
+ Save files to a database (sqlite, mysql, postgres).
361
+
362
+ Lists all files in the source path and stores their metadata in a database.
363
+ Useful for creating searchable indexes of remote storage.
364
+
365
+ Args:
366
+ src: Remote path to list, this will be used to populate an entire table, so always use the root-most path.
367
+ db_url: Database URL, like sqlite:///data.db or mysql://user:pass@localhost/db or postgres://user:pass@localhost/db
368
+ max_depth: Maximum depth to traverse (-1 for unlimited)
369
+ fast_list: Use fast list (only use when getting THE entire data repository from the root/bucket)
370
+ """
371
+ return self.impl.save_to_db(
372
+ src=src, db_url=db_url, max_depth=max_depth, fast_list=fast_list
373
+ )
374
+
375
+ def ls(
376
+ self,
377
+ src: Dir | Remote | str | None = None,
378
+ max_depth: int | None = None,
379
+ glob: str | None = None,
380
+ order: Order = Order.NORMAL,
381
+ listing_option: ListingOption = ListingOption.ALL,
382
+ ) -> DirListing:
383
+ """
384
+ List files and directories at the specified path.
385
+
386
+ Provides a detailed listing with file metadata.
387
+
388
+ Args:
389
+ src: Path to list (Dir, Remote, or string path)
390
+ max_depth: Maximum recursion depth (None for default)
391
+ glob: Optional glob pattern to filter results
392
+ order: Sorting order for the results
393
+ listing_option: What types of entries to include
394
+
395
+ Returns:
396
+ DirListing object containing the results
397
+ """
398
+ return self.impl.ls(
399
+ src=src,
400
+ max_depth=max_depth,
401
+ glob=glob,
402
+ order=order,
403
+ listing_option=listing_option,
404
+ )
405
+
406
+ def listremotes(self) -> list[Remote]:
407
+ """
408
+ List all configured remotes.
409
+
410
+ Returns a list of all remotes defined in the rclone configuration.
411
+
412
+ Returns:
413
+ List of Remote objects
414
+ """
415
+ return self.impl.listremotes()
416
+
417
+ def diff(
418
+ self,
419
+ src: str,
420
+ dst: str,
421
+ min_size: (
422
+ str | None
423
+ ) = None, # e. g. "1MB" - see rclone documentation: https://rclone.org/commands/rclone_check/
424
+ max_size: (
425
+ str | None
426
+ ) = None, # e. g. "1GB" - see rclone documentation: https://rclone.org/commands/rclone_check/
427
+ diff_option: DiffOption = DiffOption.COMBINED,
428
+ fast_list: bool = True,
429
+ size_only: bool | None = None,
430
+ checkers: int | None = None,
431
+ other_args: list[str] | None = None,
432
+ ) -> Generator[DiffItem, None, None]:
433
+ """
434
+ Compare two directories and yield differences.
435
+
436
+ Be extra careful with the src and dst values. If you are off by one
437
+ parent directory, you will get a huge amount of false diffs.
438
+
439
+ Args:
440
+ src: Rclone style src path
441
+ dst: Rclone style dst path
442
+ min_size: Minimum file size to check (e.g., "1MB")
443
+ max_size: Maximum file size to check (e.g., "1GB")
444
+ diff_option: How to report differences
445
+ fast_list: Whether to use fast listing
446
+ size_only: Compare only file sizes, not content
447
+ checkers: Number of checker threads
448
+ other_args: Additional command-line arguments
449
+
450
+ Yields:
451
+ DiffItem objects representing each difference found
452
+ """
453
+ return self.impl.diff(
454
+ src=src,
455
+ dst=dst,
456
+ min_size=min_size,
457
+ max_size=max_size,
458
+ diff_option=diff_option,
459
+ fast_list=fast_list,
460
+ size_only=size_only,
461
+ checkers=checkers,
462
+ other_args=other_args,
463
+ )
464
+
465
+ def walk(
466
+ self,
467
+ src: Dir | Remote | str,
468
+ max_depth: int = -1,
469
+ breadth_first: bool = True,
470
+ order: Order = Order.NORMAL,
471
+ ) -> Generator[DirListing, None, None]:
472
+ """
473
+ Walk through the given path recursively, yielding directory listings.
474
+
475
+ Similar to os.walk(), but for remote storage. Traverses directories
476
+ and yields their contents.
477
+
478
+ Args:
479
+ src: Remote path, Dir, or Remote object to walk through
480
+ max_depth: Maximum depth to traverse (-1 for unlimited)
481
+ breadth_first: If True, use breadth-first traversal, otherwise depth-first
482
+ order: Sorting order for directory entries
483
+
484
+ Yields:
485
+ DirListing: Directory listing for each directory encountered
486
+ """
487
+ return self.impl.walk(
488
+ src=src, max_depth=max_depth, breadth_first=breadth_first, order=order
489
+ )
490
+
491
+ def scan_missing_folders(
492
+ self,
493
+ src: Dir | Remote | str,
494
+ dst: Dir | Remote | str,
495
+ max_depth: int = -1,
496
+ order: Order = Order.NORMAL,
497
+ ) -> Generator[Dir, None, None]:
498
+ """
499
+ Find folders that exist in source but are missing in destination.
500
+
501
+ Useful for identifying directories that need to be created before
502
+ copying files.
503
+
504
+ Args:
505
+ src: Source directory or Remote to scan
506
+ dst: Destination directory or Remote to compare against
507
+ max_depth: Maximum depth to traverse (-1 for unlimited)
508
+ order: Sorting order for directory entries
509
+
510
+ Yields:
511
+ Dir: Each directory that exists in source but not in destination
512
+ """
513
+ return self.impl.scan_missing_folders(
514
+ src=src, dst=dst, max_depth=max_depth, order=order
515
+ )
516
+
517
+ def cleanup(
518
+ self, src: str, other_args: list[str] | None = None
519
+ ) -> CompletedProcess:
520
+ """
521
+ Cleanup any resources used by the Rclone instance.
522
+
523
+ Removes temporary files and directories created by rclone.
524
+
525
+ Args:
526
+ src: Path to clean up
527
+ other_args: Additional command-line arguments
528
+
529
+ Returns:
530
+ CompletedProcess with the result of the cleanup operation
531
+ """
532
+ return self.impl.cleanup(src=src, other_args=other_args)
533
+
534
+ def copy_to(
535
+ self,
536
+ src: File | str,
537
+ dst: File | str,
538
+ check: bool | None = None,
539
+ verbose: bool | None = None,
540
+ other_args: list[str] | None = None,
541
+ ) -> CompletedProcess:
542
+ """
543
+ Copy one file from source to destination.
544
+
545
+ Warning - this can be slow for large files or when copying between
546
+ different storage providers.
547
+
548
+ Args:
549
+ src: Rclone style src path
550
+ dst: Rclone style dst path
551
+ check: Whether to verify the copy with checksums
552
+ verbose: Whether to show detailed progress
553
+ other_args: Additional command-line arguments
554
+
555
+ Returns:
556
+ CompletedProcess with the result of the copy operation
557
+ """
558
+ return self.impl.copy_to(
559
+ src=src, dst=dst, check=check, verbose=verbose, other_args=other_args
560
+ )
561
+
562
+ def copy_files(
563
+ self,
564
+ src: str,
565
+ dst: str,
566
+ files: list[str] | Path,
567
+ check: bool | None = None,
568
+ max_backlog: int | None = None,
569
+ verbose: bool | None = None,
570
+ checkers: int | None = None,
571
+ transfers: int | None = None,
572
+ low_level_retries: int | None = None,
573
+ retries: int | None = None,
574
+ retries_sleep: str | None = None,
575
+ metadata: bool | None = None,
576
+ timeout: str | None = None,
577
+ max_partition_workers: int | None = None,
578
+ multi_thread_streams: int | None = None,
579
+ other_args: list[str] | None = None,
580
+ ) -> list[CompletedProcess]:
581
+ """
582
+ Copy multiple files from source to destination.
583
+
584
+ Efficiently copies a list of files, potentially in parallel.
585
+
586
+ Args:
587
+ src: Rclone style src path
588
+ dst: Rclone style dst path
589
+ files: List of file paths relative to src, or Path to a file containing the list
590
+ check: Whether to verify copies with checksums
591
+ max_backlog: Maximum number of queued transfers
592
+ verbose: Whether to show detailed progress
593
+ checkers: Number of checker threads
594
+ transfers: Number of file transfers to run in parallel
595
+ low_level_retries: Number of low-level retries
596
+ retries: Number of high-level retries
597
+ retries_sleep: Sleep interval between retries (e.g., "10s")
598
+ metadata: Whether to preserve metadata
599
+ timeout: IO idle timeout (e.g., "5m")
600
+ max_partition_workers: Maximum number of partition workers
601
+ multi_thread_streams: Number of streams for multi-thread copy
602
+ other_args: Additional command-line arguments
603
+
604
+ Returns:
605
+ List of CompletedProcess objects for each copy operation
606
+ """
607
+ return self.impl.copy_files(
608
+ src=src,
609
+ dst=dst,
610
+ files=files,
611
+ check=check,
612
+ max_backlog=max_backlog,
613
+ verbose=verbose,
614
+ checkers=checkers,
615
+ transfers=transfers,
616
+ low_level_retries=low_level_retries,
617
+ retries=retries,
618
+ retries_sleep=retries_sleep,
619
+ metadata=metadata,
620
+ timeout=timeout,
621
+ max_partition_workers=max_partition_workers,
622
+ multi_thread_streams=multi_thread_streams,
623
+ other_args=other_args,
624
+ )
625
+
626
+ def copy(
627
+ self,
628
+ src: Dir | str,
629
+ dst: Dir | str,
630
+ check: bool | None = None,
631
+ transfers: int | None = None,
632
+ checkers: int | None = None,
633
+ multi_thread_streams: int | None = None,
634
+ low_level_retries: int | None = None,
635
+ retries: int | None = None,
636
+ other_args: list[str] | None = None,
637
+ ) -> CompletedProcess:
638
+ """
639
+ Copy files from source to destination.
640
+
641
+ Recursively copies all files from src to dst.
642
+
643
+ Args:
644
+ src: Rclone style src path
645
+ dst: Rclone style dst path
646
+ check: Whether to verify copies with checksums
647
+ transfers: Number of file transfers to run in parallel
648
+ checkers: Number of checker threads
649
+ multi_thread_streams: Number of streams for multi-thread copy
650
+ low_level_retries: Number of low-level retries
651
+ retries: Number of high-level retries
652
+ other_args: Additional command-line arguments
653
+
654
+ Returns:
655
+ CompletedProcess with the result of the copy operation
656
+ """
657
+ return self.impl.copy(
658
+ src=src,
659
+ dst=dst,
660
+ check=check,
661
+ transfers=transfers,
662
+ checkers=checkers,
663
+ multi_thread_streams=multi_thread_streams,
664
+ low_level_retries=low_level_retries,
665
+ retries=retries,
666
+ other_args=other_args,
667
+ )
668
+
669
+ def purge(self, src: Dir | str) -> CompletedProcess:
670
+ """
671
+ Purge a directory.
672
+
673
+ Removes a directory and all its contents.
674
+
675
+ Args:
676
+ src: Rclone style path
677
+
678
+ Returns:
679
+ CompletedProcess with the result of the purge operation
680
+ """
681
+ return self.impl.purge(src=src)
682
+
683
+ def delete_files(
684
+ self,
685
+ files: str | File | list[str] | list[File],
686
+ check: bool | None = None,
687
+ rmdirs=False,
688
+ verbose: bool | None = None,
689
+ max_partition_workers: int | None = None,
690
+ other_args: list[str] | None = None,
691
+ ) -> CompletedProcess:
692
+ """
693
+ Delete files or directories.
694
+
695
+ Args:
696
+ files: Files to delete (single file/path or list)
697
+ check: Whether to verify deletions
698
+ rmdirs: Whether to remove empty directories
699
+ verbose: Whether to show detailed progress
700
+ max_partition_workers: Maximum number of partition workers
701
+ other_args: Additional command-line arguments
702
+
703
+ Returns:
704
+ CompletedProcess with the result of the delete operation
705
+ """
706
+ return self.impl.delete_files(
707
+ files=files,
708
+ check=check,
709
+ rmdirs=rmdirs,
710
+ verbose=verbose,
711
+ max_partition_workers=max_partition_workers,
712
+ other_args=other_args,
713
+ )
714
+
715
+ def exists(self, src: Dir | Remote | str | File) -> bool:
716
+ """
717
+ Check if a file or directory exists.
718
+
719
+ Args:
720
+ src: Path to check (Dir, Remote, File, or path string)
721
+
722
+ Returns:
723
+ True if the path exists, False otherwise
724
+ """
725
+ return self.impl.exists(src=src)
726
+
727
+ def is_synced(self, src: str | Dir, dst: str | Dir) -> bool:
728
+ """
729
+ Check if two directories are in sync.
730
+
731
+ Compares the contents of src and dst to determine if they match.
732
+
733
+ Args:
734
+ src: Source directory (Dir object or path string)
735
+ dst: Destination directory (Dir object or path string)
736
+
737
+ Returns:
738
+ True if the directories are in sync, False otherwise
739
+ """
740
+ return self.impl.is_synced(src=src, dst=dst)
741
+
742
+ def modtime(self, src: str) -> str | Exception:
743
+ """
744
+ Get the modification time of a file or directory.
745
+
746
+ Args:
747
+ src: Path to the file or directory
748
+
749
+ Returns:
750
+ Modification time as a string, or Exception if an error occurred
751
+ """
752
+ return self.impl.modtime(src=src)
753
+
754
+ def modtime_dt(self, src: str) -> datetime | Exception:
755
+ """
756
+ Get the modification time of a file or directory as a datetime object.
757
+
758
+ Args:
759
+ src: Path to the file or directory
760
+
761
+ Returns:
762
+ Modification time as a datetime object, or Exception if an error occurred
763
+ """
764
+ return self.impl.modtime_dt(src=src)
765
+
766
+ def write_text(
767
+ self,
768
+ text: str,
769
+ dst: str,
770
+ ) -> Exception | None:
771
+ """
772
+ Write text to a file.
773
+
774
+ Creates or overwrites the file at dst with the given text.
775
+
776
+ Args:
777
+ text: Text content to write
778
+ dst: Destination file path
779
+
780
+ Returns:
781
+ None if successful, Exception if an error occurred
782
+ """
783
+ return self.impl.write_text(text=text, dst=dst)
784
+
785
+ def write_bytes(
786
+ self,
787
+ data: bytes,
788
+ dst: str,
789
+ ) -> Exception | None:
790
+ """
791
+ Write bytes to a file.
792
+
793
+ Creates or overwrites the file at dst with the given binary data.
794
+
795
+ Args:
796
+ data: Binary content to write
797
+ dst: Destination file path
798
+
799
+ Returns:
800
+ None if successful, Exception if an error occurred
801
+ """
802
+ return self.impl.write_bytes(data=data, dst=dst)
803
+
804
+ def read_bytes(self, src: str) -> bytes | Exception:
805
+ """
806
+ Read bytes from a file.
807
+
808
+ Args:
809
+ src: Source file path
810
+
811
+ Returns:
812
+ File contents as bytes, or Exception if an error occurred
813
+ """
814
+ return self.impl.read_bytes(src=src)
815
+
816
+ def read_text(self, src: str) -> str | Exception:
817
+ """
818
+ Read text from a file.
819
+
820
+ Args:
821
+ src: Source file path
822
+
823
+ Returns:
824
+ File contents as a string, or Exception if an error occurred
825
+ """
826
+ return self.impl.read_text(src=src)
827
+
828
+ def copy_bytes(
829
+ self,
830
+ src: str,
831
+ offset: int | SizeSuffix,
832
+ length: int | SizeSuffix,
833
+ outfile: Path,
834
+ other_args: list[str] | None = None,
835
+ ) -> Exception | None:
836
+ """
837
+ Copy a slice of bytes from the src file to dst.
838
+
839
+ Extracts a portion of a file based on offset and length.
840
+
841
+ Args:
842
+ src: Source file path
843
+ offset: Starting position in the source file
844
+ length: Number of bytes to copy
845
+ outfile: Local file path to write the bytes to
846
+ other_args: Additional command-line arguments
847
+
848
+ Returns:
849
+ None if successful, Exception if an error occurred
850
+ """
851
+ return self.impl.copy_bytes(
852
+ src=src,
853
+ offset=offset,
854
+ length=length,
855
+ outfile=outfile,
856
+ other_args=other_args,
857
+ )
858
+
859
+ def copy_dir(
860
+ self, src: str | Dir, dst: str | Dir, args: list[str] | None = None
861
+ ) -> CompletedProcess:
862
+ """
863
+ Copy a directory from source to destination.
864
+
865
+ Recursively copies all files and subdirectories.
866
+
867
+ Args:
868
+ src: Source directory (Dir object or path string)
869
+ dst: Destination directory (Dir object or path string)
870
+ args: Additional command-line arguments
871
+
872
+ Returns:
873
+ CompletedProcess with the result of the copy operation
874
+ """
875
+ # convert src to str, also dst
876
+ return self.impl.copy_dir(src=src, dst=dst, args=args)
877
+
878
+ def copy_remote(
879
+ self, src: Remote, dst: Remote, args: list[str] | None = None
880
+ ) -> CompletedProcess:
881
+ """
882
+ Copy a remote to another remote.
883
+
884
+ Copies all contents from one remote storage to another.
885
+
886
+ Args:
887
+ src: Source remote
888
+ dst: Destination remote
889
+ args: Additional command-line arguments
890
+
891
+ Returns:
892
+ CompletedProcess with the result of the copy operation
893
+ """
894
+ return self.impl.copy_remote(src=src, dst=dst, args=args)
895
+
896
+ def copy_file_s3_resumable(
897
+ self,
898
+ src: str, # src:/Bucket/path/myfile.large.zst
899
+ dst: str, # dst:/Bucket/path/myfile.large.zst
900
+ part_infos: list[PartInfo] | None = None,
901
+ upload_threads: int = 8, # Number of reader and writer threads to use
902
+ merge_threads: int = 4, # Number of threads to use for merging the parts
903
+ ) -> Exception | None:
904
+ """
905
+ Copy a large file to S3 with resumable upload capability.
906
+
907
+ This method splits the file into parts for parallel upload and can
908
+ resume interrupted transfers using a custom algorithm in python.
909
+
910
+ Particularly useful for very large files where network interruptions
911
+ are likely.
912
+
913
+ Args:
914
+ src: Source file path (format: remote:bucket/path/file)
915
+ dst: Destination file path (format: remote:bucket/path/file)
916
+ part_infos: Optional list of part information for resuming uploads
917
+ upload_threads: Number of parallel upload threads
918
+ merge_threads: Number of threads for merging uploaded parts
919
+
920
+ Returns:
921
+ None if successful, Exception if an error occurred
922
+ """
923
+ return self.impl.copy_file_s3_resumable(
924
+ src=src,
925
+ dst=dst,
926
+ part_infos=part_infos,
927
+ upload_threads=upload_threads,
928
+ merge_threads=merge_threads,
929
+ )
930
+
931
+ def mount(
932
+ self,
933
+ src: Remote | Dir | str,
934
+ outdir: Path,
935
+ allow_writes: bool | None = False,
936
+ transfers: int | None = None, # number of writes to perform in parallel
937
+ use_links: bool | None = None,
938
+ vfs_cache_mode: str | None = None,
939
+ verbose: bool | None = None,
940
+ cache_dir: Path | None = None,
941
+ cache_dir_delete_on_exit: bool | None = None,
942
+ log: Path | None = None,
943
+ other_args: list[str] | None = None,
944
+ ) -> Mount:
945
+ """
946
+ Mount a remote or directory to a local path.
947
+
948
+ Makes remote storage accessible as a local filesystem.
949
+
950
+ Args:
951
+ src: Remote or directory to mount
952
+ outdir: Local path to mount to
953
+ allow_writes: Whether to allow write operations
954
+ transfers: Number of parallel write operations
955
+ use_links: Whether to use symbolic links
956
+ vfs_cache_mode: VFS cache mode (e.g., "full", "minimal")
957
+ verbose: Whether to show detailed output
958
+ cache_dir: Directory to use for caching
959
+ cache_dir_delete_on_exit: Whether to delete cache on exit
960
+ log: Path to write logs to
961
+ other_args: Additional command-line arguments
962
+
963
+ Returns:
964
+ Mount object representing the mounted filesystem
965
+ """
966
+ return self.impl.mount(
967
+ src=src,
968
+ outdir=outdir,
969
+ allow_writes=allow_writes,
970
+ transfers=transfers,
971
+ use_links=use_links,
972
+ vfs_cache_mode=vfs_cache_mode,
973
+ verbose=verbose,
974
+ cache_dir=cache_dir,
975
+ cache_dir_delete_on_exit=cache_dir_delete_on_exit,
976
+ log=log,
977
+ other_args=other_args,
978
+ )
979
+
980
+ def serve_http(
981
+ self,
982
+ src: str,
983
+ addr: str | None = None,
984
+ other_args: list[str] | None = None,
985
+ ) -> HttpServer:
986
+ """
987
+ Serve a remote or directory via HTTP.
988
+
989
+ Creates an HTTP server that provides access to the specified remote.
990
+ The returned HttpServer object includes a client for fetching files.
991
+
992
+ This is useful for providing web access to remote storage or for
993
+ accessing remote files from applications that support HTTP but not
994
+ the remote's native protocol.
995
+
996
+ Args:
997
+ src: Remote or directory to serve
998
+ addr: Network address and port to serve on (default: localhost:8080)
999
+ other_args: Additional arguments to pass to rclone
1000
+
1001
+ Returns:
1002
+ HttpServer object with methods for accessing the served content
1003
+ """
1004
+ return self.impl.serve_http(
1005
+ src=src, cache_mode="minimal", addr=addr, other_args=other_args
1006
+ )
1007
+
1008
+ def size_files(
1009
+ self,
1010
+ src: str,
1011
+ files: list[str],
1012
+ fast_list: bool = False, # Recommend that this is False
1013
+ other_args: list[str] | None = None,
1014
+ check: bool | None = False,
1015
+ verbose: bool | None = None,
1016
+ ) -> SizeResult | Exception:
1017
+ """
1018
+ Get the size of a list of files.
1019
+
1020
+ Calculates the total size of the specified files.
1021
+
1022
+ Args:
1023
+ src: Base path for the files
1024
+ files: List of file paths relative to src
1025
+ fast_list: Whether to use fast listing (not recommended for accuracy)
1026
+ other_args: Additional command-line arguments
1027
+ check: Whether to verify file integrity
1028
+ verbose: Whether to show detailed output
1029
+
1030
+ Returns:
1031
+ SizeResult with size information, or Exception if an error occurred
1032
+
1033
+ Example:
1034
+ size_files("remote:bucket", ["path/to/file1", "path/to/file2"])
1035
+ """
1036
+ return self.impl.size_files(
1037
+ src=src,
1038
+ files=files,
1039
+ fast_list=fast_list,
1040
+ other_args=other_args,
1041
+ check=check,
1042
+ verbose=verbose,
1043
+ )
1044
+
1045
+ def size_file(self, src: str) -> SizeSuffix | Exception:
1046
+ """
1047
+ Get the size of a file.
1048
+
1049
+ Args:
1050
+ src: Path to the file
1051
+
1052
+ Returns:
1053
+ SizeSuffix object representing the file size, or Exception if an error occurred
1054
+ """
1055
+ return self.impl.size_file(src=src)
1056
+
1057
+
1058
+ # Export public API components
1059
+ __all__ = [
1060
+ # Main classes
1061
+ "Rclone", # Primary interface
1062
+ "File", # File representation
1063
+ "Config", # Configuration handling
1064
+ "Remote", # Remote storage
1065
+ "Dir", # Directory representation
1066
+ "RPath", # Remote path utilities
1067
+ "DirListing", # Directory listing
1068
+ "FileList", # File list
1069
+ "FileItem", # File item
1070
+ "Process", # Process management
1071
+ "DiffItem", # Difference item
1072
+ "DiffType", # Difference type
1073
+ # Functions
1074
+ "rclone_verbose", # Verbosity control
1075
+ # Data classes and enums
1076
+ "CompletedProcess", # Process result
1077
+ "DiffOption", # Difference options
1078
+ "ListingOption", # Listing options
1079
+ "Order", # Sorting order
1080
+ "SizeResult", # Size result
1081
+ "Parsed", # Parsed configuration
1082
+ "Section", # Configuration section
1083
+ "MultiUploadResult", # S3 upload result
1084
+ "SizeSuffix", # Size with suffix
1085
+ # Utilities
1086
+ "configure_logging", # Logging configuration
1087
+ "log", # Logging utilities
1088
+ "HttpServer", # HTTP server
1089
+ "Range", # HTTP range
1090
+ "HttpFetcher", # HTTP fetcher
1091
+ "PartInfo", # Part information for uploads
1092
+ ]
1093
+ ```
1094
+
1095
+
1096
+ # Contributing
1097
+
1098
+ ```bash
1099
+ git clone https://github.comn/zackees/rclone-api
1100
+ cd rclone-api
1101
+ ./install
1102
+ ./lint
1103
+ ./test
1104
+ ```
1105
+
1106
+ # Windows
1107
+
1108
+ This environment requires you to use `git-bash`.