CI / Tests (Linux, Python 3.12) (push) Successful in 9m24s
Adds tagsync.ps1, a self-contained PowerShell rewrite of the full tagsync tool. Uses Invoke-RestMethod for Dropbox REST calls, DPAPI- encrypted file for token storage, JSON for state, and native Register-ScheduledTask for scheduling. Only external dependency is exiftool.exe (same as the Python version). Updates README to lead with the PowerShell quick start, with Python instructions in a separate section below.
233 lines
8.5 KiB
Markdown
233 lines
8.5 KiB
Markdown
# Dropbox Tag Sync
|
|
|
|
Bring Dropbox web tags down to your computer so you can search for tagged
|
|
files in File Explorer (Windows) or Finder (macOS) as if they were
|
|
first-class local tags.
|
|
|
|
## The problem this solves
|
|
|
|
Dropbox lets you add **tags** to files from its web interface. Those tags
|
|
live in Dropbox's cloud metadata — they are *not* stored inside the files
|
|
themselves. The Dropbox desktop app syncs file **contents**, but it does
|
|
not copy tags onto your local files. As a result, tags you rely on for
|
|
search and organization show up on dropbox.com but are invisible to
|
|
Finder, Spotlight, File Explorer, and Windows Search.
|
|
|
|
This tool closes that gap. It connects to Dropbox, reads each file's
|
|
tags, and writes them to your local files in a form your operating
|
|
system treats as a first-class tag.
|
|
|
|
---
|
|
|
|
## Windows — PowerShell (no Python required)
|
|
|
|
**Requirements:** Windows 10/11, PowerShell 5.1+, [ExifTool](https://exiftool.org/) on your PATH.
|
|
|
|
### Quick start
|
|
|
|
```powershell
|
|
.\tagsync.ps1 wizard
|
|
```
|
|
|
|
If you get an execution policy error, run this first (once, per machine):
|
|
|
|
```powershell
|
|
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
|
|
```
|
|
|
|
### Commands
|
|
|
|
All commands are run as `.\tagsync.ps1 <command>`.
|
|
|
|
| Command | What it does |
|
|
|---|---|
|
|
| `wizard` | Interactive setup (run this first) |
|
|
| `run` | One-shot sync using saved settings |
|
|
| `run -DryRun` | Show what would change without touching files |
|
|
| `run -Verbose` | Show every file action during sync |
|
|
| `status` | Show config, token presence, and schedule state |
|
|
| `schedule install` | Install the background scheduled task |
|
|
| `schedule install -Interval 30` | Install, running every 30 minutes |
|
|
| `schedule uninstall` | Remove the background scheduled task |
|
|
| `schedule status` | Show whether the scheduled task is installed |
|
|
| `reset -All` | Clear stored token, settings, and local state |
|
|
| `reset -Token` | Clear only the token |
|
|
|
|
### Where things are stored (Windows / PowerShell)
|
|
|
|
| Item | Location |
|
|
|---|---|
|
|
| Token | `~\.dropbox_tag_sync\token.dpapi` (DPAPI-encrypted, current user only) |
|
|
| Settings | `~\.dropbox_tag_sync\config.json` |
|
|
| Local state cache | `~\.dropbox_tag_sync\state.json` |
|
|
| Scheduled task | Task Scheduler, name: `DropboxTagSync` |
|
|
|
|
### Troubleshooting (Windows / PowerShell)
|
|
|
|
**"exiftool not found"** — install from <https://exiftool.org/>, place
|
|
`exiftool.exe` on your PATH (e.g. `C:\Windows\`), then confirm
|
|
`exiftool -ver` works in a terminal.
|
|
|
|
**Windows tags don't show in Explorer** — Explorer's Tags column may
|
|
be hidden; right-click a column header and add **Tags**. Windows Search
|
|
may need a few minutes to re-index.
|
|
|
|
**"not found locally"** — the file exists in Dropbox but hasn't synced
|
|
down yet. Let Dropbox finish syncing, or mark the file as "always keep
|
|
on this device" if it is online-only.
|
|
|
|
**Token wrong or revoked** — run `.\tagsync.ps1 reset -Token`, then
|
|
`.\tagsync.ps1 wizard` to enter a new one.
|
|
|
|
**State got into a weird state** — run `.\tagsync.ps1 reset -State`.
|
|
The next run will re-examine every file; no data is lost.
|
|
|
|
---
|
|
|
|
## macOS / Windows — Python
|
|
|
|
**Requirements:** Python 3.9+, packages in `requirements.txt`, and
|
|
(Windows only) [ExifTool](https://exiftool.org/) on your PATH.
|
|
|
|
### Quick start
|
|
|
|
```
|
|
pip install -r requirements.txt
|
|
python tagsync.py wizard
|
|
```
|
|
|
|
The wizard detects your OS, finds your Dropbox folder, walks you through
|
|
creating a read-only Dropbox access token, previews what will be tagged,
|
|
applies the tags, and offers to install a background scheduler.
|
|
|
|
### Commands
|
|
|
|
All commands are run as `python tagsync.py <command>`.
|
|
|
|
| Command | What it does |
|
|
|---|---|
|
|
| `wizard` | Interactive setup (run this first) |
|
|
| `run` | Perform a one-shot sync using saved settings |
|
|
| `run --dry-run` | Show what would change without touching any files |
|
|
| `status` | Show config, token presence, and schedule state |
|
|
| `schedule install` | Install the background scheduled task |
|
|
| `schedule install --interval 30` | Install, running every 30 minutes |
|
|
| `schedule uninstall` | Remove the background scheduled task |
|
|
| `schedule status` | Show whether the scheduler is installed and loaded |
|
|
| `reset --all` | Clear stored token, settings, and local state DB |
|
|
| `reset --token` | Clear only the token |
|
|
|
|
### What's in this repo
|
|
|
|
| File / folder | Purpose |
|
|
|---|---|
|
|
| `tagsync.ps1` | PowerShell entry point (Windows, no Python) |
|
|
| `tagsync.py` | Python CLI entry point |
|
|
| `tagsync/` | Python package with all the logic |
|
|
| `requirements.txt` | Python dependencies |
|
|
|
|
Inside `tagsync/`:
|
|
|
|
| File | Purpose |
|
|
|---|---|
|
|
| `wizard.py` | Interactive setup flow |
|
|
| `core.py` | Dropbox listing, tag fetching, sync loop |
|
|
| `config.py` | Settings file + secure token storage |
|
|
| `detect.py` | OS / Dropbox folder / dependency detection |
|
|
| `platform_macos.py` | Finder-xattr writer + launchd scheduler |
|
|
| `platform_windows.py` | ExifTool-based XMP writer + Task Scheduler |
|
|
|
|
### Where things are stored (Python)
|
|
|
|
| Item | Location |
|
|
|---|---|
|
|
| Token (preferred) | macOS Keychain / Windows Credential Manager |
|
|
| Token (fallback) | `~/.dropbox_tag_sync/token` (mode 0600) |
|
|
| Settings | `~/.dropbox_tag_sync/config.json` |
|
|
| Local state cache | `~/.dropbox_tag_sync/state.db` |
|
|
| Log (macOS) | `~/Library/Logs/dropbox-tag-sync.log` |
|
|
| launchd plist | `~/Library/LaunchAgents/com.dropboxtagsync.agent.plist` |
|
|
| Windows task | Task Scheduler, name: `DropboxTagSync` |
|
|
|
|
---
|
|
|
|
## How it works
|
|
|
|
1. You create a Dropbox access token with **read-only** metadata scope.
|
|
The wizard walks you through this.
|
|
2. The tool lists every file in your Dropbox and asks Dropbox which
|
|
tags each file has.
|
|
3. For files whose tags have changed since the last run, it writes
|
|
those tags to the local copy.
|
|
4. A small state cache remembers what was written so subsequent runs
|
|
only touch files whose tags actually changed.
|
|
|
|
Nothing is ever deleted from Dropbox. The tool only **reads** from
|
|
Dropbox and **writes** to local files on your machine.
|
|
|
|
## The important platform difference
|
|
|
|
macOS and Windows store "file tags" in fundamentally different ways.
|
|
|
|
### macOS — clean and invisible
|
|
|
|
macOS has a universal, built-in tag system. Tags are stored in an
|
|
**extended attribute** (`xattr`) alongside the file, not inside it.
|
|
The tool writes the xattr directly. Consequences:
|
|
|
|
- Every file type can be tagged (text, zip, source code, anything).
|
|
- The file's contents are untouched, so Dropbox does **not** see the
|
|
file as modified and does **not** re-upload it.
|
|
- The file's modification time does not change.
|
|
|
|
### Windows — embeds tags into the file
|
|
|
|
Windows has no universal tag store. The "Tags" field in Explorer is
|
|
actually the **XMP:Subject** metadata field embedded inside the file.
|
|
|
|
Consequences:
|
|
|
|
- Only file formats that support XMP (images, PDFs, Office docs, most
|
|
audio and video) can be tagged. The tool skips unsupported formats
|
|
by default and can optionally write a `.tags.json` sidecar next to
|
|
them instead.
|
|
- Because the tag lives inside the file, writing it **does** modify
|
|
the file. Dropbox will notice and re-upload the file once.
|
|
- If Dropbox Smart Sync leaves some files online-only, tagging one
|
|
would trigger a download. The tool can skip online-only files (on
|
|
by default in the wizard).
|
|
|
|
> **Plan for the first Windows run.** If there are many tagged files,
|
|
> the first run will queue a large number of re-uploads. Schedule it
|
|
> overnight, on a good connection, and expect a one-time spike in
|
|
> Dropbox activity. Subsequent runs are quiet.
|
|
|
|
## One-time Dropbox app setup
|
|
|
|
The wizard will open this page and walk you through it, but for
|
|
reference:
|
|
|
|
1. Go to <https://www.dropbox.com/developers/apps> and click **Create app**.
|
|
2. Choose:
|
|
- **Scoped access**
|
|
- **Full Dropbox**
|
|
- A name of your choosing (e.g. `tag-sync`)
|
|
3. Open the **Permissions** tab and enable `files.metadata.read`.
|
|
4. Click **Submit**.
|
|
5. On the **Settings** tab, scroll to **OAuth 2 → Generated access token**
|
|
and click **Generate**. Paste that token into the wizard.
|
|
|
|
The generated token only has permission to read file metadata. It
|
|
cannot modify anything in Dropbox even if it leaked.
|
|
|
|
## Safety summary
|
|
|
|
- **Read-only** with Dropbox (token scope: `files.metadata.read`).
|
|
- **Non-destructive on macOS**: only extended attributes are written.
|
|
- **Content-modifying on Windows**: rewrites the XMP metadata section
|
|
of supported files (one-time re-upload per tagged file is expected).
|
|
- **Idempotent**: safe to run as often as you like.
|
|
- **Resumable**: if interrupted, the next run picks up where it left off.
|
|
- **One-way**: tags flow cloud → local only. Tagging on the web is
|
|
still the source of truth.
|