CLI to sync notes between Joplin and Obsidian (bidirectional).
Project description
notebridge
Just like the old saying "eating at one house, sleeping at another," I can't give up the features of either Joplin or Obsidian, so I created this command-line tool to help you achieve bidirectional synchronization between Joplin and Obsidian notes. This is currently a rough initial version.
Tool Introduction
notebridge allows you to easily synchronize all note content between Joplin and Obsidian on Windows, including:
- Titles
- Body text
- Tags
- Attachments (images, PDFs, etc.)
- Folder structure
- Selective sync support (certain notebooks can be synced one-way or skipped)
- Sync direction control (bidirectional, unidirectional)
- Reliable ID-based identification: Uses
notebridge_idto ensure note consistency, unaffected by filename changes
You just need to enter one command in the terminal, and the tool will automatically keep both sides in sync.
Installation
Option 1: From PyPI (recommended)
- Install Python (3.8+ recommended).
- Run:
pip install joplin-obsidian-bridge - Put
config.jsonin the current working directory, or in:- Windows:
%APPDATA%\notebridge\config.json - macOS/Linux:
~/.config/notebridge/config.json
- Windows:
- Run:
job syncorpython -m notebridge sync
Option 2: From source
- Install Python (3.8+ recommended).
- Clone the repo and run:
pip install -r requirements.txt - Put
config.jsonin the project folder and run:python notebridge.py sync
Configuration
Before first use, simple configuration is required:
- Joplin Settings
- Open Joplin, go to Settings → Web Clipper → Enable Web Clipper service.
- Note down the "Port" and "Token".
- Obsidian Settings
- Find your Obsidian vault folder path.
- Create Config File
- Create
config.jsonin the current working directory or the path above, with example content:{ "joplin": { "api_base": "http://127.0.0.1:41184", "token": "your_joplin_token" }, "obsidian": { "vault_path": "C:/Users/your_username/Obsidian_vault_path" }, "sync_rules": { "joplin_to_obsidian_only": ["Work Notes", "Temp Notes"], "obsidian_to_joplin_only": ["Personal Diary"], "skip_sync": ["Drafts", "Test"], "bidirectional": ["Study Notes", "Project Docs"] } }
- Create
Usage
Commands below use job (after pip install). From source, replace job with python notebridge.py.
Basic Sync Commands
# Preview sync plan (no actual sync)
job sync
# Execute bidirectional sync
job sync --force
# Sync only from Joplin to Obsidian
job sync --force --joplin-to-obsidian
# Sync only from Obsidian to Joplin
job sync --force --obsidian-to-joplin
# Manual confirmation mode sync (recommended, prevents duplicate headers)
job sync-manual
# Manual confirmation one-way sync
job sync-manual --joplin-to-obsidian
job sync-manual --obsidian-to-joplin
Additional Feature Commands
# Check for duplicate notes (ultra-fast version, greatly improved performance)
job check-duplicates
# Quick title similarity check (recommended, manual decision)
job quick-title-check
# Clean Joplin imports from Obsidian
job clean-joplin-imports
# Performance test comparison (new vs old algorithm)
job test-duplicates
# Run tests (tags & attachments sync logic, no real Joplin/Obsidian needed)
python -m unittest tests.test_tags_and_attachments -v
# Interactive duplicate cleaning (recommended)
job interactive-clean
# Auto-clean duplicate notes and sync IDs
job clean-duplicates
# Fix missing attachments in Obsidian
job fix-attachments
Sync Mode Explanation
Automatic Sync Mode
- Bidirectional sync (default): Mutual sync between Joplin and Obsidian
- Joplin → Obsidian: Only sync from Joplin to Obsidian, suitable for initial import
- Obsidian → Joplin: Only sync from Obsidian to Joplin, suitable for backup
Manual Confirmation Mode (Recommended)
Why is manual confirmation mode recommended?
- ✅ Prevents duplicate headers: Automatically checks and fixes duplicate sync info headers before each sync
- ✅ Prevents reverse sync: Automatically detects note source, avoids syncing notes back to their origin (e.g., Obsidian → Joplin → Obsidian)
- ✅ Intelligent deletion detection: Uses sync time and cached state to automatically determine if a note was deleted or is new
- ✅ Enhanced deletion handling: Provides options to delete locally or copy to the other side
- ✅ Complete control: Shows details for each note before sync, you decide whether to sync
- ✅ Safe and reliable: Can view note content, sync status, duplicate headers, note source, etc. anytime
- ✅ Flexible operations: Supports skip single, skip all, exit, etc.
Use Cases:
- Recommended for first-time sync
- First sync after resolving duplicate header issues
- When uncertain which notes need syncing
- When careful inspection of each note is needed
Selective Sync Configuration
In config.json, you can configure sync rules for different notebooks (supports wildcard pattern matching):
joplin_to_obsidian_only: Only sync from Joplin to Obsidianobsidian_to_joplin_only: Only sync from Obsidian to Joplinskip_sync: Skip syncbidirectional: Bidirectional sync (default)
Wildcard Support
All sync rules support wildcard pattern matching:
*matches any number of characters, e.g.,"Conflict*"matches all notebooks starting with Conflict?matches a single character, e.g.,"Test?"matches "Test1", "Test2", etc.
Example configuration:
{
"sync_rules": {
"joplin_to_obsidian_only": ["Work Notes", "Project*"],
"obsidian_to_joplin_only": ["Personal Diary", "Backup*"],
"skip_sync": ["Conflict*", "Temp*", "Draft*"],
"bidirectional": ["Important*", "Study*"]
}
}
Smart Duplicate Detection and Cleaning
Quick Title Similarity Check (Recommended)
job quick-title-check
- ⚡ Blazing fast: Only checks title similarity, extremely fast
- 🎯 Manual decision: You have complete control over what's duplicate
- 📝 Content preview: Shows note content preview for easy judgment
- 🔧 Adjustable threshold: Can set similarity threshold (70%-90%)
- 📊 Detailed comparison: Can view full content comparison
- 🛡️ Safe confirmation: Requires confirmation before deletion, prevents accidental deletion
Clean Joplin Imports (Recommended)
job clean-joplin-imports
- 🔍 Smart detection: Automatically identifies notes from Joplin in Obsidian
- 📊 Status analysis: Distinguishes unmodified, modified, and orphaned notes
- 🎯 Flexible selection: Can choose to delete all, only unmodified, or only orphaned
- 🛡️ Safe operation: Requires confirmation before deletion, prevents accidental deletion
- 💡 Re-sync: Can re-sync after cleaning to avoid duplicates
Ultra-fast Duplicate Check (Fully Automatic)
job check-duplicates
- 🚀 Layered detection algorithm: Uses 5-layer detection strategy, 3-5x performance improvement
- 🔍 Smart content preprocessing: More thorough removal of headers, markdown syntax, HTML tags, etc.
- 💾 Caching mechanism: Avoids redundant calculations, greatly improves detection speed
- 🎯 Advanced similarity calculation: Specifically handles "same content after removing headers" cases
- 📊 Detailed statistics report: Provides performance stats, duplicate rate analysis, etc.
- 🔧 Multiple duplicate type detection: ID duplicates, content hash duplicates, title similarity, content similarity, post-header-removal duplicates
Performance Testing
job test-duplicates
- Compare new vs old algorithm performance
- Show detection result differences
- Provide performance improvement multiplier
Interactive Cleaning (Recommended)
job interactive-clean
- Smart duplicate note detection
- Multiple cleaning strategy options
- Content comparison preview support
- Confirm each deletion individually, safe and reliable
Auto Cleaning
job clean-duplicates
- Auto-clean duplicate sync IDs in all notes
- Auto-find and delete duplicate notes
- Ensure clean, conflict-free note library
FAQ
- Q: Will content be lost during sync? A: The tool tries its best to avoid content loss. If both sides modify the same note simultaneously, the newest version will be kept.
- Q: What content is supported for sync? A: Supports titles, body text, tags, attachments, folder structure, etc.
- Q: Do I need to keep Joplin running? A: Yes, and the Web Clipper service must be enabled.
- Q: How are sync conflicts handled? A: The tool automatically selects the newest version based on timestamps, avoiding manual conflict resolution.
- Q: Can I sync only some notes?
A: Yes, by configuring
sync_rulesyou can precisely control which notebooks sync and how. - Q: Will manual file deletion during program execution cause errors? A: No, the program has optimized error handling to automatically skip non-existent files and continue.
- Q: What if I encounter permission issues? A: The program automatically detects permission errors and skips problematic files without interrupting the entire sync process.
- Q: What improvements does the new duplicate detection algorithm have? A: The new algorithm uses a 5-layer detection strategy with 3-5x performance improvement, more accurately detecting "same content after removing headers" duplicates.
- Q: What if duplicate checking is too slow?
A: The new version has greatly optimized performance using caching and layered detection with significant speed improvements. If still slow, run
job test-duplicatesto view performance comparison. - Q: How to detect "same content after removing headers" duplicates? A: The new algorithm specifically adds a 5th layer detection using advanced similarity calculation to accurately identify such duplicates.
- Q: What if one-way sync rules don't take effect? A: The latest version has fixed one-way sync rule filtering issues. The program now correctly checks sync rules for each note, ensuring only allowed direction notes are synced. If issues persist, check sync rule settings in config file.
Latest Updates
v1.4.0 - Intelligent Deletion Detection and Sync Time Updates
- ✅ Smart deletion vs new note detection
- Automatically determines via sync time records
- Uses sync state cache (sync_state.json) to check if both sides existed in last sync
- Shows detailed judgment basis (sync time, last state, source)
- Dynamically identifies deletion candidates and adds to deletion list
- ✅ Enhanced deletion handling options
- Delete locally: Keep in sync with other side (real deletion)
- Copy to other side: Restore note to other side (accidental deletion or re-need)
- Skip: Don't handle for now
- Shows confidence and judgment basis
- ✅ Fix sync time updates on re-sync
- sync_joplin_to_obsidian: Updates sync time on re-sync
- sync_obsidian_to_joplin_with_notebook_id: Updates sync time on re-sync
- Bidirectional sync updates: Updates sync time on both sides
- Smart bidirectional sync updates: Updates sync time on both sides
- Manual confirmation sync updates: Updates sync time on both sides
- ✅ Improved duplicate header handling
- Prioritize headers with sync time
- Preserve complete sync info (id, time, source, version)
- Handle empty values to avoid losing valid info
- Fix sync info being incorrectly cleaned
v1.3.0 - Manual Confirmation Mode, Complete Solution for Duplicate Headers and Reverse Sync
- ✅ New manual confirmation sync mode: Human confirmation required before each note sync, complete control
- ✅ Fixed sync rule checking in manual confirmation mode: Manual mode now strictly follows configured sync rules
- Matched note pairs: Check if specified direction sync is allowed
- New notes: Check if notebook/folder sync is allowed
- Auto-skip non-compliant notes with reason shown
- ✅ Smart reverse sync prevention: Automatically detects note source, avoids syncing unmodified notes back to origin
- Note from Obsidian → Joplin, if unmodified in Joplin, won't sync back to Obsidian
- Note from Joplin → Obsidian, if unmodified in Obsidian, won't sync back to Joplin
- Only truly modified notes sync, intelligently determined by timestamps
- ✅ Fixed sync info format issues:
- Joplin uses HTML comment format:
<!-- notebridge_id: xxx --> - Obsidian uses YAML frontmatter format: In note properties
- Auto-converts format during sync, no longer mixed use
- Joplin uses HTML comment format:
- ✅ Dual-end sync info writeback: After successful sync, both sides have correctly formatted sync info
- Joplin → Obsidian, Joplin side also adds sync info (HTML comments)
- Obsidian → Joplin, Obsidian side also adds sync info (YAML format)
- Forced writeback ensures no duplicate syncs
- ✅ Enhanced image link handling: Supports both HTML and Markdown format images
- Supports
<img src=":/resource_id"/>format (HTML) - Supports
format (Markdown) - Auto-downloads resources and converts to Obsidian local paths
- Preserves original size info (as comments)
- Supports
- ✅ Fixed sync info field missing issues:
- Ensures extracted sync info contains all required fields
- Missing fields use defaults (
notebridge_versiondefaults to'1') - Avoids
'notebridge_version'and other field missing errors during sync
- ✅ Auto-skip empty and invalid notes:
- Auto-skips notes with empty titles (possibly deleted)
- Auto-skips notes with empty content
- Avoids syncing invalid or deleted notes
- ✅ Auto-detect and fix duplicate headers: Automatically checks and fixes duplicate sync info headers during sync
- ✅ Enhanced sync info cleaning logic: Thoroughly cleans mixed HTML comment and YAML format duplicate info
- ✅ Added preventive check command:
prevent-duplicate-headersfor regular duplicate header checks - ✅ Fixed timestamp issues: Avoids generating future timestamps
Tags and Attachments Sync Fix (Issue #1)
- ✅ Tags sync: Joplin → Obsidian writes note tags to YAML frontmatter
tags; Obsidian → Joplin reads frontmatter and inline#tagsand creates/assigns tags in Joplin - ✅ Attachments: Clear error messages when resource download fails; case-insensitive resource ID; ensure
attachmentsdirectory exists before writing
v1.2.0 - Fix One-way Sync Rule Filtering Issues
- ✅ Fixed one-way sync rules not taking effect: Program now correctly checks each note's sync rules, ensuring only allowed direction notes sync
- ✅ Enhanced sync rule checking: Validates sync rules for each note during sync execution
- ✅ Improved sync reporting: Added statistics for skips due to one-way sync restrictions
- ✅ Added test script:
test_sync_rules.pyfor validating sync rule logic
Reverse Sync Problem Solution (Smart Judgment, No Manual Work)
What is the reverse sync problem?
- After syncing from Obsidian to Joplin, if unmodified in Joplin, shouldn't sync back to Obsidian
- After syncing from Joplin to Obsidian, if unmodified in Obsidian, shouldn't sync back to Joplin
Smart judgment logic (automatic, no manual work):
- Detect note source (
notebridge_sourcefield) - Compare sync timestamps on both sides
- If timestamps match → Not modified → Auto-skip
- If timestamps differ → Modified → Allow sync
Application scenarios:
- ✅ Scenario 1: Note from Obsidian, unmodified in Joplin → Auto-skip
- ✅ Scenario 2: Note from Obsidian, modified in Joplin → Allow sync
- ✅ Scenario 3: Note from Joplin, unmodified in Obsidian → Auto-skip
- ✅ Scenario 4: Note from Joplin, modified in Obsidian → Allow sync
Duplicate Header Problem Solution
- Immediate fix: Run
job fix-duplicate-headersto fix existing duplicate headers - Preventive measures:
- Use manual confirmation mode sync:
job sync-manual - Auto-check and fix duplicate headers before each sync
- Regular preventive checks:
job prevent-duplicate-headers
- Use manual confirmation mode sync:
- Root solution:
- Improved sync info addition logic, thoroughly cleans old sync info
- Added duplicate header check in
update_obsidian_notefunction - Fixed timestamp generation logic
- New smart reverse sync detection: Auto-skips unmodified reverse syncs
Advanced Usage & Development Plans
- Support scheduled auto-sync
- Support sync history versions
- Support more customization options
Suggestions or issues are welcome!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file joplin_obsidian_bridge-0.2.0-py3-none-any.whl.
File metadata
- Download URL: joplin_obsidian_bridge-0.2.0-py3-none-any.whl
- Upload date:
- Size: 85.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e30a7134a24d12c5c40993a4ca605a2b406cb1ee6815d1315d3bdeb2d153896f
|
|
| MD5 |
4b57e6a4bc97f5ab4dca1bed51522171
|
|
| BLAKE2b-256 |
19b11f073ac913d7d952259cf628c49cecc2606d8b8135822d9911995368929e
|