foldrive 0.1.0__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.
Files changed (43) hide show
  1. foldrive-0.1.0/LICENSE +21 -0
  2. foldrive-0.1.0/PKG-INFO +400 -0
  3. foldrive-0.1.0/README.md +376 -0
  4. foldrive-0.1.0/pyproject.toml +36 -0
  5. foldrive-0.1.0/setup.cfg +4 -0
  6. foldrive-0.1.0/src/foldrive/__init__.py +1 -0
  7. foldrive-0.1.0/src/foldrive/auth.py +59 -0
  8. foldrive-0.1.0/src/foldrive/autostart.py +178 -0
  9. foldrive-0.1.0/src/foldrive/cli.py +199 -0
  10. foldrive-0.1.0/src/foldrive/commands/__init__.py +1 -0
  11. foldrive-0.1.0/src/foldrive/commands/autostart.py +16 -0
  12. foldrive-0.1.0/src/foldrive/commands/init.py +57 -0
  13. foldrive-0.1.0/src/foldrive/commands/log.py +67 -0
  14. foldrive-0.1.0/src/foldrive/commands/login.py +10 -0
  15. foldrive-0.1.0/src/foldrive/commands/logout.py +8 -0
  16. foldrive-0.1.0/src/foldrive/commands/ls.py +42 -0
  17. foldrive-0.1.0/src/foldrive/commands/pull.py +104 -0
  18. foldrive-0.1.0/src/foldrive/commands/push.py +106 -0
  19. foldrive-0.1.0/src/foldrive/commands/restore.py +90 -0
  20. foldrive-0.1.0/src/foldrive/commands/setup.py +86 -0
  21. foldrive-0.1.0/src/foldrive/commands/status.py +93 -0
  22. foldrive-0.1.0/src/foldrive/commands/sync.py +9 -0
  23. foldrive-0.1.0/src/foldrive/commands/tick.py +91 -0
  24. foldrive-0.1.0/src/foldrive/commands/whoami.py +7 -0
  25. foldrive-0.1.0/src/foldrive/config.py +209 -0
  26. foldrive-0.1.0/src/foldrive/drive.py +166 -0
  27. foldrive-0.1.0/src/foldrive/engine.py +346 -0
  28. foldrive-0.1.0/src/foldrive/executor.py +400 -0
  29. foldrive-0.1.0/src/foldrive/history.py +78 -0
  30. foldrive-0.1.0/src/foldrive/logs.py +17 -0
  31. foldrive-0.1.0/src/foldrive/paths.py +27 -0
  32. foldrive-0.1.0/src/foldrive/progress.py +103 -0
  33. foldrive-0.1.0/src/foldrive/prompts.py +67 -0
  34. foldrive-0.1.0/src/foldrive/scanner.py +113 -0
  35. foldrive-0.1.0/src/foldrive/scheduler.py +26 -0
  36. foldrive-0.1.0/src/foldrive/state.py +47 -0
  37. foldrive-0.1.0/src/foldrive.egg-info/PKG-INFO +400 -0
  38. foldrive-0.1.0/src/foldrive.egg-info/SOURCES.txt +41 -0
  39. foldrive-0.1.0/src/foldrive.egg-info/dependency_links.txt +1 -0
  40. foldrive-0.1.0/src/foldrive.egg-info/entry_points.txt +2 -0
  41. foldrive-0.1.0/src/foldrive.egg-info/requires.txt +5 -0
  42. foldrive-0.1.0/src/foldrive.egg-info/top_level.txt +1 -0
  43. foldrive-0.1.0/tests/test_engine.py +180 -0
foldrive-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Udbhav Sai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,400 @@
1
+ Metadata-Version: 2.4
2
+ Name: foldrive
3
+ Version: 0.1.0
4
+ Summary: Pair any local folder with any Google Drive folder - git-style two-way sync
5
+ Author-email: Udbhav Sai <udbhavsai.k@gmail.com>
6
+ License-Expression: MIT
7
+ Keywords: google-drive,sync,cli,backup
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: End Users/Desktop
10
+ Classifier: Operating System :: Microsoft :: Windows
11
+ Classifier: Operating System :: MacOS
12
+ Classifier: Operating System :: POSIX :: Linux
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: System :: Archiving :: Backup
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: google-api-python-client>=2.100
19
+ Requires-Dist: google-auth-oauthlib>=1.2
20
+ Requires-Dist: google-auth-httplib2>=0.2
21
+ Requires-Dist: send2trash>=1.8
22
+ Requires-Dist: platformdirs>=4.0
23
+ Dynamic: license-file
24
+
25
+ <!-- Header block for project -->
26
+ <hr>
27
+
28
+ <div align="center">
29
+
30
+ <h1 align="center">foldrive</h1>
31
+
32
+ </div>
33
+
34
+ <pre align="center">Pair any local folder with any Google Drive folder, and sync them like git.</pre>
35
+
36
+ <!-- Header block for project -->
37
+
38
+ [![PyPI](https://img.shields.io/pypi/v/foldrive)](https://pypi.org/project/foldrive/) ![License](https://img.shields.io/badge/license-MIT-blue) ![Python](https://img.shields.io/badge/python-3.10%2B-blue) ![Platforms](https://img.shields.io/badge/platforms-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey) [![SLIM](https://img.shields.io/badge/Best%20Practices%20from-SLIM-blue)](https://nasa-ammos.github.io/slim/)
39
+
40
+ ```
41
+ $ foldrive status
42
+ Folder : C:\Users\me\Desktop\6th sem
43
+ Drive : 6th sem
44
+ Local : 336 files Drive: 163 files
45
+
46
+ CONFLICT -> both sides changed 24 file(s)
47
+ new in Drive -> download 25 file(s)
48
+ new locally -> upload 198 file(s)
49
+ identical -> just remember it 114 file(s)
50
+
51
+ 247 change(s) pending. Run `foldrive sync` to apply.
52
+ ```
53
+
54
+ Google's own Drive for Desktop cannot map an arbitrary local folder — say
55
+ `Desktop\7th sem` — to an arbitrary My Drive folder like `My Drive/College/7th sem`.
56
+ It mirrors your whole Drive, or backs folders up into a separate "Computers"
57
+ section that your phone never sees. foldrive exists to fill exactly that gap, with
58
+ git-like explicit control: see what a sync *would* do, then do it.
59
+
60
+ It is built for people who want their files in a specific place on both sides, on a
61
+ schedule they choose, with nothing happening that they can't inspect first.
62
+
63
+ [Report an issue](https://github.com/udbhav07/foldrive/issues) | [Changelog](CHANGELOG.md)
64
+
65
+ ## Features
66
+
67
+ * **Pair any two folders** — one local path, one Drive folder, in either direction.
68
+ * **Preview before acting** — `foldrive status` is read-only and shows every pending change.
69
+ * **Two-way sync** with conflict handling that never loses a version.
70
+ * **Background sync** on a per-folder schedule: Task Scheduler on Windows, cron on macOS and Linux.
71
+ * **Google Docs, Sheets and Slides** support, with four modes per file type.
72
+ * **Deletion safety** — soft deletes, a mass-delete guard, and an optional never-delete side.
73
+ * **Survives interruptions** — progress is checkpointed; re-running never creates duplicates.
74
+ * **Catches up automatically** after being offline, asleep, or unplugged.
75
+
76
+ ## Contents
77
+
78
+ * [Quick Start](#quick-start)
79
+ * [How it behaves](#how-it-behaves)
80
+ * [Changelog](#changelog)
81
+ * [FAQ](#frequently-asked-questions-faq)
82
+ * [Contributing](#contributing)
83
+ * [License](#license)
84
+ * [Support](#support)
85
+
86
+ ## Quick Start
87
+
88
+ ### Requirements
89
+
90
+ * Python 3.10 or newer
91
+ * A Google account
92
+ * Windows, macOS or Linux
93
+
94
+ ### Setup Instructions
95
+
96
+ 1. Install foldrive:
97
+
98
+ ```
99
+ pip install foldrive
100
+ ```
101
+
102
+ <details>
103
+ <summary>Or from source, to develop on it</summary>
104
+
105
+ ```
106
+ git clone https://github.com/udbhav07/foldrive && cd foldrive
107
+ python -m venv .venv && .venv\Scripts\activate # macOS/Linux: source .venv/bin/activate
108
+ pip install -e .
109
+ ```
110
+ </details>
111
+
112
+ 2. Create your own free Google OAuth client — `foldrive setup` prints the steps:
113
+
114
+ ```
115
+ foldrive setup
116
+ ```
117
+
118
+ Briefly: at <https://console.cloud.google.com> create a project, enable the
119
+ **Google Drive API**, configure the **OAuth consent screen** (User type
120
+ *External*, then **Publish app** — in "Testing" Google expires your login every
121
+ 7 days), then **Credentials → Create credentials → OAuth client ID →
122
+ Application type "Desktop app"** and download the JSON.
123
+
124
+ > Choose **Desktop app**, not Web application. A Web client cannot sign in from
125
+ > a terminal.
126
+
127
+ 3. Install the downloaded file and sign in:
128
+
129
+ ```
130
+ foldrive setup ~/Downloads/client_secret_xxxx.json
131
+ foldrive login
132
+ ```
133
+
134
+ You will see Google's "unverified app" warning once — **Advanced → Go to
135
+ foldrive → Allow**. That is expected for personal tools; verification is a paid
136
+ audit intended for commercial apps.
137
+
138
+ ### Run Instructions
139
+
140
+ 1. Link a local folder to a Drive folder (it is created if it doesn't exist):
141
+
142
+ ```
143
+ cd "Desktop/7th sem"
144
+ foldrive init
145
+ ```
146
+
147
+ 2. See what a sync would do — this touches nothing:
148
+
149
+ ```
150
+ foldrive status
151
+ ```
152
+
153
+ 3. Do it. The first sync shows its full plan and asks before transferring anything:
154
+
155
+ ```
156
+ foldrive sync
157
+ ```
158
+
159
+ 4. Optionally, let it run every 5 minutes in the background:
160
+
161
+ ```
162
+ foldrive autostart
163
+ ```
164
+
165
+ ### Usage Examples
166
+
167
+ | Command | What it does |
168
+ |---|---|
169
+ | `foldrive setup` | One-time Google setup; installs your OAuth client file |
170
+ | `foldrive login` / `logout` / `whoami` | Google account sign-in |
171
+ | `foldrive init` | Link the current folder to a Drive folder |
172
+ | `foldrive status` | Read-only preview of pending changes, both directions |
173
+ | `foldrive push` / `pull` / `sync` | Upload / download / both |
174
+ | `foldrive log` | What foldrive has done to this folder |
175
+ | `foldrive restore <path>` | Bring one file back from Drive |
176
+ | `foldrive ls <name>` | List a Drive folder's contents by name |
177
+ | `foldrive autostart` | Register background sync (`--remove`, `--status`) |
178
+
179
+ Flags: `--all` (list every file in `status`), `--yes` (never prompt),
180
+ `--allow-mass-delete` (see [Safety](#safety)), `-n N` (entries in `log`).
181
+
182
+ Checking what the background task did overnight:
183
+
184
+ ```
185
+ $ foldrive log -n 4
186
+ 2026-08-12 03:15:04 downloaded Unit-3 Notes.pdf
187
+ 2026-08-12 03:15:02 uploaded lab/experiment-7.docx
188
+
189
+ 2026-08-11 22:40:11 moved to Recycle Bin old-draft.docx
190
+ ```
191
+
192
+ Recovering a file you deleted locally but kept in Drive:
193
+
194
+ ```
195
+ $ foldrive restore "notes/unit-3.pdf"
196
+ restoring notes/unit-3.pdf
197
+ Restored to /home/me/college/notes/unit-3.pdf
198
+ ```
199
+
200
+ ### Test Instructions
201
+
202
+ ```
203
+ pytest
204
+ ```
205
+
206
+ Unit tests cover the diff engine — the pure logic that decides new / modified /
207
+ deleted / conflict on each side. For manual testing, use a throwaway folder and a
208
+ scratch Drive folder; foldrive moves real files.
209
+
210
+ ## How it behaves
211
+
212
+ ### Configuration
213
+
214
+ `foldrive init` writes `.googledrive.json` into the folder. Everything except the
215
+ Drive folder id is optional.
216
+
217
+ ```json
218
+ {
219
+ "drive_folder_id": "1ddlbDTp...",
220
+ "drive_folder_name": "7th sem",
221
+ "schedule": { "pull_every_minutes": 30, "push_every_minutes": 50 },
222
+ "ignore": [".venv/", "__pycache__/", "node_modules/", "*.tmp", "~$*"],
223
+ "conflict_policy": "ask",
224
+ "conflict_overrides": { "notes/scratch.txt": "local" },
225
+ "delete_policy": { "local": "trash", "drive": "trash" },
226
+ "max_delete_percent": 25,
227
+ "max_delete_minimum": 10,
228
+ "google_native": { "docs": "download_only", "sheets": "skip", "slides": "download_only" }
229
+ }
230
+ ```
231
+
232
+ Ignored by default: `.venv/`, `venv/`, `env/`, `__pycache__/`, `*.pyc`,
233
+ `node_modules/`, `.git/`, `build/`, `dist/`, `*.egg-info/`, `.idea/`, `.vscode/`,
234
+ `~$*`, `*.tmp`, `Thumbs.db`, `desktop.ini`, `.DS_Store`.
235
+
236
+ ### Conflicts
237
+
238
+ A conflict is the same file changed on **both** sides since the last sync. Nothing
239
+ is lost: the newer version keeps the original name, the other is preserved beside
240
+ it as `notes (local copy).docx` or `notes (drive copy).docx`, and both end up on
241
+ both sides. Timestamps within 5 seconds count as a tie — then neither keeps the
242
+ name and both copies are written.
243
+
244
+ In a terminal foldrive asks once per conflict, *before* any transfer starts, so you
245
+ answer in the first few seconds and the rest runs unattended:
246
+
247
+ ```
248
+ [k]eep both [l]ocal wins [d]rive wins [s]kip
249
+ [K]/[L]/[D]/[S] to apply that to all remaining
250
+ ```
251
+
252
+ Scheduled runs never prompt — they always keep both. Set
253
+ `"conflict_policy": "keep_both"` to skip prompting in manual runs, or pin
254
+ individual files with `conflict_overrides`.
255
+
256
+ ### Deletions
257
+
258
+ Deleting on one side deletes on the other, **recoverably** — Recycle Bin locally,
259
+ Drive trash remotely. Set `delete_policy` per side, naming the side files are
260
+ deleted *from*:
261
+
262
+ ```json
263
+ "delete_policy": { "local": "trash", "drive": "never_delete" }
264
+ ```
265
+
266
+ That is the backup setup: local cleanup works normally, Drive keeps everything
267
+ forever. A plain string applies to both sides.
268
+
269
+ ### Google Docs, Sheets and Slides
270
+
271
+ Native files have no bytes and no checksum, so they are handled separately, per type:
272
+
273
+ | Mode | Drive changes | Local changes | Both change |
274
+ |---|---|---|---|
275
+ | `skip` | ignored | ignored | ignored |
276
+ | `download_only` | download over the local file | not uploaded; `status` warns | local kept as a copy, then downloaded |
277
+ | `upload_only` | not downloaded; `status` warns | uploaded back into the same Doc | Drive version kept as a copy |
278
+ | `two_way` | download over the local file | uploaded back into the same Doc | usual conflict rules |
279
+
280
+ A Doc named `Notes` is the local file `Notes.docx`. Uploading writes back into the
281
+ **same** Drive document — same id, share links and version history, no duplicate.
282
+
283
+ Defaults are `docs: download_only`, `slides: download_only`, `sheets: skip`,
284
+ because `.xlsx` mangles cross-sheet formulas, charts and filter views, and `.docx`
285
+ loses comments and suggestion mode. Change detection uses Drive's modification
286
+ time, not a checksum, since exporting the same untouched document twice produces
287
+ different bytes.
288
+
289
+ ### Safety
290
+
291
+ * **First sync never deletes.** Without a snapshot, "deleted" and "never synced"
292
+ are indistinguishable, so foldrive assumes the safer one.
293
+ * **Mass-delete guard.** A run deleting at least `max_delete_minimum` files *and*
294
+ at least `max_delete_percent` of a side is refused, since that usually means the
295
+ other side wrongly looked empty. `--allow-mass-delete` overrides it — but the
296
+ background task never can.
297
+ * **Partial reads stop the sync.** If a folder can't be read, foldrive says which
298
+ one rather than mistaking missing files for deleted ones.
299
+ * **Interruptions are cheap.** Progress is checkpointed every 25 transfers, and
300
+ re-running never duplicates: already-uploaded files come back as *identical*.
301
+ * **Offline is not an error.** Timestamps advance only on success, so an
302
+ interrupted folder stays due and catches up on the next run.
303
+
304
+ ### Background sync
305
+
306
+ ```
307
+ foldrive autostart # every 5 minutes
308
+ foldrive autostart --status
309
+ foldrive autostart --remove
310
+ ```
311
+
312
+ | | How it registers |
313
+ |---|---|
314
+ | Windows | Task Scheduler job, no console window, keeps working on battery |
315
+ | macOS / Linux | a `crontab` line tagged `# foldrive-tick` |
316
+
317
+ `--remove` only touches foldrive's own entry; your other cron jobs are untouched.
318
+ Nothing prints to a terminal, so the rotating log is where you check on it —
319
+ `foldrive.log` in the app folder.
320
+
321
+ > **macOS one-time permission.** Any folder works, but `~/Desktop`, `~/Documents`
322
+ > and `~/Downloads` are protected by macOS, so you allow access once — the same
323
+ > grant Dropbox asks for. **System Settings → Privacy & Security → Full Disk
324
+ > Access → `+`**, then add your terminal app (for commands you run yourself) and
325
+ > `/usr/sbin/cron` (for background sync; press ⌘⇧G to type the hidden path).
326
+
327
+ ### Platform support
328
+
329
+ | | Windows | macOS | Linux |
330
+ |---|---|---|---|
331
+ | Sync | ✅ | ✅ | ✅ |
332
+ | Background sync | ✅ Task Scheduler | ✅ cron | ✅ cron |
333
+
334
+ Config, token and logs live in each OS's standard location: `%APPDATA%\foldrive\`,
335
+ `~/Library/Application Support/foldrive/`, `~/.local/share/foldrive/`.
336
+
337
+ ## Changelog
338
+
339
+ See [CHANGELOG.md](CHANGELOG.md) for a history of changes, and the
340
+ [releases page](https://github.com/udbhav07/foldrive/releases) for versioned releases.
341
+
342
+ ## Frequently Asked Questions (FAQ)
343
+
344
+ 1. **I edited a file in the Drive web UI and foldrive ignored it. Why?**
345
+ - Opening a `.txt` or `.docx` in Drive and editing it usually creates a
346
+ *separate Google Doc* rather than changing the original file. Your original is
347
+ untouched, and that's what foldrive keeps syncing. To really change a file in
348
+ Drive, use *right-click → Manage versions → Upload new version*, or edit it
349
+ locally and let foldrive push it.
350
+
351
+ 2. **Why do I have to create my own Google OAuth client?**
352
+ - A client id carries a shared quota and a 100-user cap for unverified apps.
353
+ Your own client means your own uncontended quota and no dependency on anyone
354
+ else's app staying healthy. It takes about five minutes, once.
355
+
356
+ 3. **Is my data private? What does the client file give access to?**
357
+ - `client_secret.json` identifies the *app* and grants access to nothing by
358
+ itself. Signing in creates `token.json` beside it, which opens *your* Drive
359
+ only. Revoke anytime at <https://myaccount.google.com/permissions>.
360
+
361
+ 4. **Are my files encrypted in Drive?**
362
+ - No — they are stored as ordinary Drive files, so they remain readable on
363
+ drive.google.com and your phone. If you need Google to be unable to read
364
+ them, encrypt the folder with a tool like Cryptomator and sync the encrypted
365
+ folder with foldrive.
366
+
367
+ 5. **Can two computers sync the same Drive folder?**
368
+ - Yes. Each keeps its own snapshot and reconciles independently. Edit the same
369
+ file on both before either syncs and you get the usual conflict copies.
370
+
371
+ 6. **What happens if I delete a file by accident?**
372
+ - Deletions are soft on both sides — Recycle Bin locally, Drive trash remotely.
373
+ `foldrive restore <path>` brings a file back from Drive, and `foldrive log`
374
+ shows what happened and when.
375
+
376
+ 7. **Does it sync continuously, like Drive for Desktop?**
377
+ - No. It runs on your schedule (default: pull every 30 minutes, push every 50),
378
+ or whenever you type a command. That is the tradeoff for control and a small
379
+ footprint.
380
+
381
+ ## Contributing
382
+
383
+ 1. Open an issue describing the change.
384
+ 2. [Fork](https://github.com/udbhav07/foldrive/fork) the repo and work in your fork.
385
+ 3. Run `pytest` and smoke-test against a **throwaway** sync folder — foldrive moves
386
+ real files, and testing against real data is how people lose it.
387
+ 4. Open a pull request describing what you changed and how you verified it.
388
+
389
+ Bug reports are most useful with the relevant lines from `foldrive log` and the
390
+ sync log, plus your `.googledrive.json` with the `drive_folder_id` removed.
391
+
392
+ ## License
393
+
394
+ MIT — see [LICENSE](LICENSE).
395
+
396
+ ## Support
397
+
398
+ Udbhav Sai — [@udbhav07](https://github.com/udbhav07) · <udbhavsai.k@gmail.com>
399
+
400
+ Questions and bugs: [GitHub issues](https://github.com/udbhav07/foldrive/issues).