Files
dropbox_helper/README.md
T
jared e363020080
CI / Tests (Linux, Python 3.12) (push) Failing after 4m51s
CI / Tests (Linux, Python 3.11) (push) Failing after 5m19s
CI / Tests (Linux, Python 3.9) (push) Failing after 5m3s
CI / Tests (macOS) (push) Has been cancelled
CI / Tests (Windows) (push) Has been cancelled
feat: initial Dropbox tag sync tool
Cross-platform CLI that mirrors Dropbox web tags to OS-native tags:
- macOS: Finder xattrs (content-preserving, no re-upload)
- Windows: XMP:Subject via ExifTool (re-upload expected once per file)

Includes an interactive wizard that auto-detects the OS and Dropbox
folder, walks the user through read-only token creation, previews
tagged files, applies tags, and installs a background scheduler
(launchd on macOS, Task Scheduler on Windows).

Ships with:
- 47-test suite (pytest) covering config, state DB, run_sync, writers
- Gitea Actions CI for Linux / macOS / Windows with coverage reporting
2026-04-20 14:16:50 -04:00

8.7 KiB

Dropbox Tag Sync

Bring Dropbox web tags down to your computer so you can search for tagged files in Finder (macOS) or File Explorer (Windows) 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.

Quick start

pip install -r requirements.txt
python tagsync.py wizard

That's it. 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 so new tags keep flowing down automatically.

Everything after that is optional — if you want to re-run a sync, check status, or manage the schedule, use the other subcommands below.

Requirements

  • Python 3.9+
  • The packages in requirements.txt (pip install -r requirements.txt)
  • Windows only: ExifTool on your PATH

What's in this repo

File / folder Purpose
tagsync.py Single CLI entry point
tagsync/ Package with all the logic
requirements.txt Python dependencies
README.md This document

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

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

How it works

  1. You create a Dropbox access token with read-only metadata scope. The wizard walks you through this. The token lives in your OS keyring (macOS Keychain or Windows Credential Manager).
  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 SQLite 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, and that shapes what happens under the hood.

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. This is a limitation of the operating system, not the tool.

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. After that first sync, tags are stored in both the cloud and the local copy, and future runs only touch files whose Dropbox tags change again.
  • 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 as each file gets its tags embedded. 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.

Where things are stored

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

Troubleshooting

"not found locally" — the file exists in Dropbox but hasn't synced down yet. Let Dropbox finish syncing, or (on Windows) mark the file as "always keep on this device" if it's online-only.

Windows tags don't show up in Explorer after a run — 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.

"exiftool not found" — install it from https://exiftool.org/ and confirm exiftool -ver works in a terminal.

The state cache got into a weird state — run python tagsync.py reset --state. The next run will re-examine every file; no data is lost.

The token is wrong or revoked — run python tagsync.py reset --token, then python tagsync.py wizard to enter a new one.

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.