Import favorites from a Hot Tub backup
Settings gains a file picker that reads an exported Hot Tub database and merges its favorites into this client's. The file never leaves the device: sqlite.js is a small read-only reader -- header, schema, table b-trees, record decoding, and the overflow pages that real rows here spill onto -- which is all it takes to walk one table, and avoids putting a wasm SQLite behind a CDN fetch. The two sides don't agree on what identifies a video. The app keys one by a hash it computes locally (a 64-hex string); the server, and so this client, keys it as something like "reddit-1rdudss". So the merge matches on normalized URL: entries already saved here are left exactly as they are, keeping the server id that makes a listing card's heart light up, and only genuinely new videos are appended. That means an imported favorite has no server id, so hearts now also match by URL (`data-fav-url` on the card, feed slide and favorites bar). Without it an imported favorite would look unsaved on its own card, and clicking the heart would file a second copy of the same video. Only the columns a favorite needs are read. `allFormats` is deliberately left behind: it holds resolved, signed URLs, which is exactly what favorites must not store (they expire -- see App.favorites.normalize). Tests (scratchpad): the reader checked against Python's sqlite3 on a real 12MB backup -- table list, every table's row count, all 515 favorites with their fields and order, and the 25 longest records byte-for-byte, which is where a wrong overflow split shows up; and the Settings control driven end-to-end, covering the merge, an existing favorite keeping its id, a re-import adding nothing, and a listing card recognising an imported favorite and unfavoriting it cleanly. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBDkEXP4htyXTCZUwMLphd
This commit is contained in:
@@ -133,6 +133,17 @@
|
||||
</div>
|
||||
<div id="sources-list" class="sources-list"></div>
|
||||
</div>
|
||||
<div class="sidebar-section">
|
||||
<h4 class="sidebar-subtitle">Hot Tub Backup</h4>
|
||||
<div class="setting-item">
|
||||
<label for="import-favorites-btn">Import Favorites</label>
|
||||
<input id="import-favorites-file" type="file" accept=".sqlite3,.sqlite,.db" hidden>
|
||||
<button id="import-favorites-btn" class="btn-secondary" type="button">Choose backup file…</button>
|
||||
<p id="import-favorites-status" class="setting-note" role="status" aria-live="polite">
|
||||
Reads the favorites out of an exported Hot Tub database. The file stays on this device.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
@@ -191,6 +202,8 @@
|
||||
<script src="static/js/customPlayer.js"></script>
|
||||
<script src="static/js/player.js"></script>
|
||||
<script src="static/js/favorites.js"></script>
|
||||
<script src="static/js/sqlite.js"></script>
|
||||
<script src="static/js/hottubBackup.js"></script>
|
||||
<script src="static/js/videos.js"></script>
|
||||
<script src="static/js/feed.js"></script>
|
||||
<script src="static/js/ui.js"></script>
|
||||
|
||||
Reference in New Issue
Block a user