Save reference artwork to Raindrop.io on your phone — your PC automatically downloads it into Eagle, with source links and folders.
English | 日本語
Found something inspiring on Instagram? Save it to Raindrop.io from the share sheet, and the next time you open your PC it's already in your Eagle library — with the source URL recorded and carousel posts grouped into folders. No more opening links in a browser and downloading by hand.
flowchart LR
A["Phone<br>Instagram → Share<br>→ Raindrop"] --> B["PC: swoop<br>checks every 5 min"]
B --> C["gallery-dl<br>downloads media"]
C --> D["Eagle API<br>imports with source URL"]
B -. "tags results<br>swoop:done" .-> A
- Nothing to do on your phone except saving to Raindrop, as you already do
- Source URL is recorded in Eagle's URL field — jump back to the original post anytime
- Automatic organization: everything lands in one parent folder (default:
Raindrop); multi-image carousel posts get their own subfolder, single items go directly inside. Open the parent folder to see folder cards for carousels and plain items for singles - No resident process: runs for a few seconds at login and every 5 minutes (launchd / Task Scheduler), zero footprint in between
- Eagle can be closed: downloads proceed in the background and are imported on a later cycle once Eagle is open
- Visible results: native notifications plus
swoop:done/swoop:failedtags on the Raindrop bookmarks (visible from the phone app too) - Beyond Instagram: any of the hundreds of sites gallery-dl supports (Pixiv, X/Twitter, ArtStation, …) work the same way
- Robust: idempotent delivery (no duplicates even after crashes or interruptions), retry management, and an inter-process lock
Note: Automated media downloading is a gray area under most sites' terms of service. Use this for personal reference collecting at your own risk. For Instagram, a secondary account is strongly recommended (see Risks).
| Category | Requirement |
|---|---|
| PC | macOS / Windows / Linux, Python 3.11+ |
| Apps | Eagle 4.0+ (for its local API), ffmpeg (for Instagram video merging) |
| Accounts | Raindrop.io (free plan is fine), login cookies for target sites (read directly from your browser) |
The CLI messages are currently Japanese only.
brew install ffmpeg
git clone https://github.com/arkrtm/swoop.git ~/swoop
cd ~/swoop && python3 -m venv .venv && .venv/bin/pip install .
mkdir -p ~/.local/bin && ln -s ~/swoop/.venv/bin/swoop ~/.local/bin/swoop
# if ~/.local/bin is not on your PATH (swoop: command not found):
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zprofile && exec zsh
swoop setup # interactive wizard (token, cookies, Eagle integration)
swoop doctor # health check
swoop run -v # one manual syncThen set up scheduled runs and you're done.
- Install the Raindrop app on your phone and create a collection for saving (e.g.
references) - Confirm you can save into that collection from the share sheet of Instagram etc.
- Get an API token: at https://app.raindrop.io/settings/integrations click “+ Create new app” (any name), open the app, and create a Test token
A test token can only access your own account; no OAuth setup is needed.
Instagram effectively requires a login. Two options:
- Read directly from your browser (recommended): just be logged in to the site in your PC browser. In
swoop setup, answer with the browser name (firefox / chrome / safari, …). Firefox is the most reliable; Chrome on macOS may ask for keychain permission once - Cookie file: export cookies in Netscape format with a browser extension (“Get cookies.txt LOCALLY” for Chrome, “Export Cookies” for Firefox) and give the wizard its path. Keep the file writable — gallery-dl writes refreshed session cookies back to it
Install Eagle and open the target library. That's it — swoop talks to Eagle's local API (localhost:41595, started automatically while Eagle runs). No watch-folder configuration needed.
If you sync your Eagle library across PCs via Google Drive etc., never open the same library on two machines at once (per Eagle's official guidance), and run swoop on one machine only.
swoop setupThe wizard asks four things, validating each as you go: the Raindrop token, the collection name, the cookie source, and the Eagle library (auto-detected when Eagle is running) plus the parent folder name.
Configuration lives at ~/.config/swoop/config.toml (override with -c <path> or the SWOOP_CONFIG env var). Prefer editing by hand? swoop init writes a commented template instead.
[run] since is set to the installation date automatically — bookmarks saved before that date are ignored, so a large pre-existing backlog won't suddenly download. To backfill, set since to an earlier date and run swoop run --full.
Run swoop run at login and every 5 minutes:
macOS (launchd):
# edit USERNAME and paths in deploy/com.swoop.plist first
cp deploy/com.swoop.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.swoop.plist
tail -f ~/Library/Logs/swoop.logIt runs once right after login (catching up on everything saved while the PC was off) and then every 5 minutes. launchd automatically skips a tick while the previous run is still going.
Windows: install first (PowerShell):
winget install ffmpeg git.git python.python.3.12
git clone https://github.com/arkrtm/swoop.git $env:USERPROFILE\swoop
cd $env:USERPROFILE\swoop
py -m venv .venv
.venv\Scripts\pip install .
.venv\Scripts\swoop.exe setupThen create a Task Scheduler task triggered at logon, repeating every 5 minutes indefinitely, running %USERPROFILE%\swoop\.venv\Scripts\swoop.exe with argument run. (The author runs swoop on macOS; Windows support is best-effort.)
Linux: use a systemd user timer or cron to run swoop run periodically.
Manual swoop run whenever you feel like it works fine too.
Phone: save to your Raindrop collection from the share sheet. That's all.
Within 5 minutes your PC downloads the media into Eagle's parent folder (default: Raindrop). While Eagle is closed, downloads are staged and held; they're imported on a later cycle once the library is open.
| Command | Description |
|---|---|
swoop run |
Sync once (--full rescans the whole collection, ignoring the incremental cutoff) |
swoop daemon |
Long-running poller (for VPS use; prefer launchd/Task Scheduler on a PC) |
swoop status |
Show processing state and failures |
swoop retry --failed |
Reset all failed items for retry (swoop retry <ID> for one) |
swoop doctor |
Health check for config, connections, and external tools |
swoop setup / swoop init |
Interactive config wizard / write a template |
Common options: -c <path> for the config file, -v for debug logging.
- Failed downloads retry automatically; after
max_attempts(default 3) the bookmark is taggedswoop:failed(with a notification). Useswoop retryto try again - Partially retrievable posts (e.g. a carousel with one deleted slide) keep whatever was downloaded
- Filenames are
Bookmark title [identifier].ext(numbered when multiple). The identifier is the Instagram shortcode (recoverable to the post URL) orrd+ the Raindrop ID for other sites (e.g.[rd123456]) - Scheduled and manual runs can overlap safely — an inter-process lock serializes them
- The source of truth for "already processed" is the tag on Raindrop, so reinstalling or switching PCs never causes re-downloads
Save whatever you like to the collection:
- Sites supported by gallery-dl (Pixiv, X/Twitter, ArtStation, Tumblr, …) are handled identically. With browser-cookie mode, sites you're logged in to in the browser generally work without extra setup
- Unsupported sites end up tagged
swoop:failedafter retries, with a notification — save those manually as before
- Automated downloading is a gray area under most sites' terms. Personal reference use, at your own risk
- Use a secondary Instagram account: automation can trigger warnings or locks. Log the spare account into your browser (or a separate profile) and point swoop at it
- Don't lower the request interval (default 6–12 s randomized), and keep volumes reasonable
- Datacenter IPs (VPS) are known to get blocked by Instagram quickly — run on a home PC
- Everything runs locally. Network traffic goes only to the Raindrop API, the sites being downloaded from, and Eagle on localhost
- The Raindrop token is stored in plain text in
~/.config/swoop/config.toml— considerchmod 600, or pass it via theRAINDROP_TOKENenv var instead - Browser cookies are read locally by gallery-dl at download time and are never stored or sent anywhere else
If you want downloads to happen while your PC stays off for days, swoop can run on a VPS and deliver via rclone to a Google Drive folder watched by Eagle's auto-import. In this mode the Eagle API is unavailable, so imports have no source URL and no folder grouping:
- Set
[deliver] mode = "rclone"andremote = "gdrive:EagleInbox/<LibraryName>"(headless auth:rclone config, thenrclone authorize "drive"on a machine with a browser) - On the PC, set Google Drive to mirror mode and point Eagle's auto-import at the synced
EagleInboxfolder. Eagle only imports from the subfolder named after the library (auto-created), so the remote must point at that subfolder - Export cookies as a Netscape file onto the VPS (keep it writable)
- Use
swoop daemonwithdeploy/swoop.service(systemd)
Not recommended for Instagram due to datacenter-IP blocking.
See config.example.toml for a fully commented example.
| Key | Default | Description |
|---|---|---|
work_dir |
~/.local/state/swoop |
Working directory for staging and state |
[raindrop] token |
(env RAINDROP_TOKEN) |
API test token |
[raindrop] collection |
references |
Collection name or numeric ID to watch |
[download] gallery_dl |
gallery-dl |
gallery-dl executable (auto-detected inside the venv) |
[download] cookies |
— | Path to a Netscape-format cookie file |
[download] sleep_request |
6.0-12.0 |
Delay between requests (seconds; range = randomized) |
[download] timeout |
600 |
Per-bookmark download timeout (seconds) |
[download] max_files |
25 |
Max files per bookmark (protects against profile URLs) |
[download] extra_args |
[] |
Extra gallery-dl arguments (e.g. --cookies-from-browser) |
[deliver] mode |
eagle |
eagle (recommended) / local / rclone |
[deliver] eagle_url |
http://localhost:41595 |
Eagle API address |
[deliver] eagle_library |
— | Expected library name (held while another library is open; empty = no check) |
[deliver] eagle_folder |
— | Parent folder collecting all imports (empty = library root) |
[deliver] local_dir |
— | Destination folder for mode = "local" |
[deliver] remote |
— | rclone remote:path for mode = "rclone" |
[deliver] rclone |
rclone |
Path to the rclone executable |
[deliver] timeout |
600 |
Per-delivery timeout (seconds) |
[deliver] extra_args |
[] |
Extra rclone arguments |
[run] interval |
300 |
Polling interval for daemon mode (seconds, ≥ 60) |
[run] since |
— | Ignore bookmarks saved before this date (UTC) |
[run] max_attempts |
3 |
Total attempts before giving up |
[run] tag_done / tag_failed |
swoop:done / swoop:failed |
Tag names applied on Raindrop |
[run] name_max_len |
80 |
Max filename length (UTF-8 bytes, 20–180) |
[run] notify |
all |
Native notifications: all / failures / off |
| Symptom | Fix |
|---|---|
swoop command not found |
Besides the symlink, ~/.local/bin must be on your PATH (the export PATH line in the quick start), or use the full path ~/swoop/.venv/bin/swoop |
| Nothing appears in Eagle | Make sure Eagle is running with the configured library open (swoop doctor); a different library holds imports |
Downloads fail with login required |
Cookies expired — log in to the site again in your browser (or re-export the file) |
| Chrome cookies unreadable | Allow the macOS keychain prompt, or switch to Firefox / a cookie file |
| Persistent 429 / 401 | Rate limited — pause for a few days, increase sleep_request |
| Only videos fail | Check ffmpeg (swoop doctor), or add "-o", "extractor.instagram.videos=merged" to [download] extra_args |
| An item is wrongly marked failed | swoop retry --failed |
| A bookmark moved into the collection isn't processed | Incremental scans cut off at the last confirmed position; run swoop run --full (daemon mode does this automatically every 24 h) |
Q. The “All” view in Eagle shows everything flat.
A. That's Eagle's design. Open the parent folder (default Raindrop) in the sidebar instead — carousels appear as folder cards, singles as items.
Q. Will items I delete in Eagle get re-imported?
A. No. The processed marker is the tag on Raindrop. To re-import intentionally, use swoop retry <ID>.
Q. Multiple PCs? A. Run swoop on one PC only. Sync the Eagle library itself (Google Drive etc.) to see results on other machines — never open the same library on two machines at once.
Q. Rename the parent folder?
A. Change eagle_folder in the config and rename the folder in Eagle to match (they're matched by name).
Q. I saved something but nothing happened.
A. Check swoop status and tail ~/Library/Logs/swoop.log (macOS). Bookmarks saved before the [run] since date are ignored by design.
launchctl unload ~/Library/LaunchAgents/com.swoop.plist # macOS
rm ~/Library/LaunchAgents/com.swoop.plist
rm ~/Library/Logs/swoop.log
rm ~/.local/bin/swoop
rm -rf ~/swoop ~/.config/swoop ~/.local/state/swoopThe swoop:done / swoop:failed tags left on Raindrop can be bulk-deleted from Raindrop's tag settings.
git clone https://github.com/arkrtm/swoop.git && cd swoop
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytestsrc/swoop/
cli.py # commands (run / setup / daemon / doctor / status / retry / init)
config.py # TOML config loading & validation
raindrop.py # Raindrop REST API client
pipeline.py # one sync cycle (fetch → download → deliver → tag)
downloader.py # gallery-dl wrapper
deliver.py # Eagle API / local / rclone delivery
eagle.py # Eagle API client
naming.py # safe filename generation
state.py # processing state (state.json)
setup_wizard.py # interactive setup wizard
notify.py # native notifications (macOS / Windows / Linux)
deploy/ # launchd (macOS) / systemd (VPS) templates
Bug reports and suggestions welcome in Issues.
