|
| 1 | +# LibreELEC Backupper - AI Coding Agent Instructions |
| 2 | + |
| 3 | +## Project Overview |
| 4 | +**LibreELEC Backupper** is a Kodi addon (service.libreelec.backupper) for automated backup/restore of LibreELEC system configuration, addons, and settings. It supports multiple remote storage protocols (SMB, NFS, FTP, SFTP, WebDAV). |
| 5 | + |
| 6 | +**Key Version:** 1.4.1.5 | **Target:** Kodi 20+ | **Python:** 3.x with xbmc/xbmcaddon modules |
| 7 | + |
| 8 | +--- |
| 9 | + |
| 10 | +## Architecture & Critical Patterns |
| 11 | + |
| 12 | +### Component Structure |
| 13 | +- **`addon.py`** - Main addon entry point, handles UI actions (backup_now, restore, test_connection, browse_remote) |
| 14 | +- **`service.py`** - Background scheduler service that runs backup tasks automatically on schedule |
| 15 | +- **`resources/lib/backup_utils.py`** - Core `BackupManager` class handling backup/restore logic |
| 16 | +- **`resources/lib/remote_browser.py`** - `RemoteBrowser` class for testing connections and browsing remote shares |
| 17 | +- **`resources/lib/email_utils.py`** - `EmailNotifier` for sending backup status notifications |
| 18 | +- **`resources/lib/settings_handler.py`** - Settings action handlers |
| 19 | + |
| 20 | +### Critical Data Flows |
| 21 | +1. **Backup Creation:** `addon.py` (user request) → `BackupManager.create_backup()` → creates tar.gz → uploads to remote via xbmcvfs |
| 22 | +2. **Connection Testing:** `addon.py` (test_connection) → `RemoteBrowser.test_connection_with_params()` → protocol-specific test method |
| 23 | +3. **Restore Operation:** `addon.py` (restore) → `BackupManager.restore_backup()` → downloads from remote → extracts → handles mount operations |
| 24 | + |
| 25 | +### Key Classes & Their Attributes |
| 26 | + |
| 27 | +**RemoteBrowser** (CRITICAL - must always initialize these): |
| 28 | +```python |
| 29 | +self.remote_type # int: 0=SMB, 1=NFS, 2=FTP, 3=SFTP, 4=WebDAV |
| 30 | +self.remote_path # str: server/path or server:/path |
| 31 | +self.username # str: credentials (can be empty) |
| 32 | +self.password # str: credentials (can be empty - THIS IS CRITICAL) |
| 33 | +self.port # str: port number (gets default if empty) |
| 34 | +self.default_ports # dict: protocol→default port mapping |
| 35 | +``` |
| 36 | + |
| 37 | +**BackupManager**: |
| 38 | +```python |
| 39 | +self.backup_dir # str: local backup directory path |
| 40 | +self.profile_path # str: Kodi profile path |
| 41 | +self.addon # xbmcaddon.Addon instance |
| 42 | +self.remote_browser # RemoteBrowser instance |
| 43 | +``` |
| 44 | + |
| 45 | +--- |
| 46 | + |
| 47 | +## Essential Patterns & Conventions |
| 48 | + |
| 49 | +### Logging Standards |
| 50 | +- **ALWAYS** use verbose logging at DEBUG level for detailed information |
| 51 | +- Use the log pattern: `xbmc.log(f"{ADDON_ID}: Message here", xbmc.LOGINFO|LOGDEBUG|LOGERROR)` |
| 52 | +- Log at function entry for debugging flows, connection attempts, and before operations that might fail |
| 53 | +- CRITICAL: Remote connection test failures must log all connection parameters for troubleshooting |
| 54 | + |
| 55 | +### Attribute Initialization Requirement |
| 56 | +**NEVER** assume attributes exist without initializing them. The `RemoteBrowser` class MUST: |
| 57 | +1. Initialize all attributes in `__init__` with default values BEFORE calling `reload_settings()` |
| 58 | +2. Wrap `reload_settings()` in try-except in `__init__` to catch and log errors |
| 59 | +3. In `reload_settings()`, ensure attributes are set even if exceptions occur during settings load |
| 60 | + |
| 61 | +**Example:** |
| 62 | +```python |
| 63 | +def __init__(self): |
| 64 | + # Initialize with defaults FIRST |
| 65 | + self.password = "" |
| 66 | + self.remote_path = "" |
| 67 | + # Then try to reload |
| 68 | + try: |
| 69 | + self.reload_settings() |
| 70 | + except Exception as e: |
| 71 | + xbmc.log(f"Error: {e}", xbmc.LOGERROR) |
| 72 | +``` |
| 73 | + |
| 74 | +### Remote Connection Testing |
| 75 | +- All test methods (`_test_smb_connection`, `_test_nfs_connection`, etc.) must: |
| 76 | + - Log connection parameters at DEBUG level |
| 77 | + - Wrap operations in try-except with detailed error logging |
| 78 | + - Show detailed error messages to users in textviewer dialogs |
| 79 | + - Handle credentials being optional (no password required scenarios) |
| 80 | + |
| 81 | +### Path Formatting |
| 82 | +- **SMB:** `server/share` or `server/share/subpath` (no smb:// prefix in path storage) |
| 83 | +- **NFS:** `server:/export/path` (colon is REQUIRED separating server and path) |
| 84 | +- **FTP/SFTP:** `server/path` |
| 85 | + |
| 86 | +### Settings Handling |
| 87 | +- Always call `xbmc.executebuiltin('UpdateLocalAddons')` after changing settings in code |
| 88 | +- Settings are loaded via `ADDON.getSetting('key')` - returns strings, use int() for type conversion |
| 89 | +- Use `reload_settings()` in RemoteBrowser to ensure fresh values from Kodi |
| 90 | + |
| 91 | +--- |
| 92 | + |
| 93 | +## Common Bug Patterns & Fixes |
| 94 | + |
| 95 | +### Attribute Error: 'RemoteBrowser' object has no attribute 'password' |
| 96 | +**Root Cause:** Attributes not initialized in `__init__` before calling `reload_settings()` |
| 97 | +**Fix:** Initialize all attributes with empty defaults in `__init__` BEFORE `reload_settings()` |
| 98 | + |
| 99 | +### NFS Mount Failures |
| 100 | +**Common Issues:** |
| 101 | +- Path format missing colon (needs `server:/path`, not `server/path`) |
| 102 | +- Missing `-o nolock` option in mount command for better compatibility |
| 103 | +- No temp mount point cleanup between attempts |
| 104 | + |
| 105 | +**Pattern to follow:** |
| 106 | +```python |
| 107 | +mount_options = ["-t", "nfs", "-o", "soft,timeo=10,retrans=2,nolock"] |
| 108 | +subprocess.call(["mount"] + mount_options + [nfs_path, mount_point]) |
| 109 | +``` |
| 110 | + |
| 111 | +### SMB Path Handling |
| 112 | +- When browsing, convert back from protocol format to storage format |
| 113 | +- Store as `server/share`, not `smb://server/share` |
| 114 | +- Convert to full SMB URL only when connecting: `smb://[user:pass@]server/share` |
| 115 | + |
| 116 | +--- |
| 117 | + |
| 118 | +## Development Workflow |
| 119 | + |
| 120 | +### Testing Connection Logic |
| 121 | +Use the included `test_fix.py` for unit testing RemoteBrowser without Kodi: |
| 122 | +```bash |
| 123 | +cd /home/nigel/service.libreelec.backupper |
| 124 | +python3 test_fix.py |
| 125 | +``` |
| 126 | + |
| 127 | +### Key Files to Check Before Changes |
| 128 | +- **`addon.xml`** - For addon version, dependencies, and settings metadata |
| 129 | +- **`resources/settings.xml`** - For available settings names and defaults |
| 130 | +- **`changelog.txt`** - For version history and known issues |
| 131 | +- **`service.py`** - To understand scheduler timing logic if modifying backup triggers |
| 132 | + |
| 133 | +### Adding New Remote Protocol Support |
| 134 | +1. Add protocol option to `addon.xml` settings |
| 135 | +2. Add case handler in `test_connection()` method |
| 136 | +3. Create `_test_protocol_connection()` method with proper logging |
| 137 | +4. Update `reload_settings()` default_ports dict if needed |
| 138 | +5. Update error messages to mention the new protocol |
| 139 | + |
| 140 | +--- |
| 141 | + |
| 142 | +## Kodi/LibreELEC Specifics |
| 143 | + |
| 144 | +### Important Module Constants |
| 145 | +- `xbmc.LOGINFO, LOGDEBUG, LOGERROR, LOGWARNING` - Log levels |
| 146 | +- Settings stored in `xbmc.translatePath(ADDON.getAddonInfo('profile'))` |
| 147 | +- Paths use `xbmcvfs.translatePath()` for cross-platform compatibility |
| 148 | + |
| 149 | +### Remote Browsing with xbmcvfs |
| 150 | +- SMB: `xbmcvfs.listdir("smb://server/share")` returns `(dirs, files)` tuple |
| 151 | +- Local operations use standard `os` module |
| 152 | +- Network operations use `xbmcvfs` for Kodi-compatible path handling |
| 153 | + |
| 154 | +### Dialog Types in Code |
| 155 | +- `xbmcgui.Dialog().ok()` - Simple message dialogs |
| 156 | +- `xbmcgui.Dialog().textviewer()` - Scrollable text display (use for connection test results) |
| 157 | +- `xbmcgui.DialogProgress()` - Progress dialog with `.create()`, `.update()`, `.close()` |
| 158 | + |
| 159 | +--- |
| 160 | + |
| 161 | +## Performance & Reliability Considerations |
| 162 | + |
| 163 | +1. **Connection Tests** - Always provide timeout handling; use low timeouts for remote tests |
| 164 | +2. **Large Backups** - Show progress dialogs; allow user cancellation |
| 165 | +3. **Path Validation** - Validate paths exist and are accessible before creating backups |
| 166 | +4. **Error Recovery** - Clean up temp mount points, temp files on exception |
| 167 | +5. **Settings Persistence** - Always reload settings before operations; settings may change in settings UI |
| 168 | + |
| 169 | +--- |
| 170 | + |
| 171 | +## DO NOT DO |
| 172 | + |
| 173 | +- ❌ Assume attributes exist without initializing them |
| 174 | +- ❌ Use unescaped passwords in logs (log "Set" or "Not Set" instead) |
| 175 | +- ❌ Forget to call `xbmc.executebuiltin('UpdateLocalAddons')` after setting changes |
| 176 | +- ❌ Mix smb:// protocol prefix in stored paths (store as server/share only) |
| 177 | +- ❌ Mount NFS without the `nolock` option |
| 178 | +- ❌ Leave temp mount points or test directories without cleanup |
| 179 | +- ❌ Show detailed error messages without logging the same details to xbmc.log |
0 commit comments