feat(sync): library + liked + rating hooks (Phase 1.f.desktop.5)#200
Conversation
Extends the canonical-id sync infrastructure to three new entities, all mirroring patterns already proven on playlist in 4b. - library: same UUID-canonical-id pattern as playlist. Migration 20260603000001 plants the column, backfills existing rows, installs the AFTER INSERT / BEFORE UPDATE triggers and seeds sync_id_map. Outbound hooks in commands/library.rs wrap each CRUD command in a tx with enqueue_op_in_tx. Apply branch in sync::apply::apply_library_op mirrors apply_playlist_op. - liked_track: no canonical column needed — track.file_hash (BLAKE3) is already a stable cross-device key. toggle_like_track enqueues with entity_id = file_hash; apply_liked_track_op resolves hash → local track id via canonical::local_track_for_hash. A hash that hasn't been scanned locally lands Ignored. - track_rating: same hash-keyed routing. set_track_rating wraps the rating UPDATE + outbox enqueue in one tx. Apply validates the 0-5 range and rejects out-of-range as Ignored. Core: extracted insert_conn / update_conn / delete_conn for library following the same pattern as playlist atomicity refactor (#195). Trait methods now delegate to the *_conn helpers. 5 new unit tests in sync::apply (library insert, liked apply, liked miss, rating apply, rating out-of-range). Signed-off-by: InstaZDLL <github.105mh@8shield.net>
📝 WalkthroughWalkthroughCe PR étend l'outbox transactionnel aux entités library, liked_track et track_rating : migration canonical_id, helpers repo transactionnels, handlers d'application d'op distantes, et commandes mises à jour pour écrire dans des transactions atomiques qui enfilent des ops outbox puis notifient le drain. ChangesSynchronisation transactionnelle pour Library et Track
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labelstype: feat, size: xl, scope: backend Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src-tauri/crates/app/src/commands/track.rs`:
- Around line 625-638: The current branch uses INSERT OR IGNORE which can
silently do nothing (FK violation) but still sets now_liked = true; change the
logic to inspect the query result's rows_affected() for both the DELETE and
INSERT executed on &mut *tx (the queries acting on liked_track with
bind(track_id) and bind(now)) and derive now_liked = rows_affected() > 0 instead
of unconditionally true/false; alternatively, validate that the referenced track
exists in track before attempting the INSERT and set now_liked based on the
actual insert outcome (use the execute() result to decide).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 3caad7bc-f4b3-4716-8a04-beba2c851813
📒 Files selected for processing (7)
src-tauri/crates/app/src/commands/library.rssrc-tauri/crates/app/src/commands/track.rssrc-tauri/crates/app/src/sync/apply.rssrc-tauri/crates/app/src/sync/canonical.rssrc-tauri/crates/core/src/repository/sqlite/library.rssrc-tauri/crates/core/src/repository/sqlite/playlist.rssrc-tauri/migrations/profile/20260603000001_sync_library_canonical_id.sql
INSERT OR IGNORE silently no-ops on FK violation (track removed between the UI render and the IPC call), but the previous code set now_liked = true unconditionally — the UI would render a heart against a row that no longer exists in liked_track. Same for a DELETE that matched 0 rows under a concurrent unlike. Now both branches read rows_affected and gate the outbox enqueue on did_change so a phantom op isn't sent for a no-op write. Signed-off-by: InstaZDLL <github.105mh@8shield.net>
Summary
Extends the canonical-id sync loop from 4b (#199) to three new entities. Same patterns, no new architecture.
commands/library.rs, brancheapply_library_op).entity_id = track.file_hash(BLAKE3 already cross-device-stable), pas de canonical column ni mapping table.Architecture invariants (rappel)
entity write + canonical lookup + enqueue_op_in_tx.state.drain.notify()aprèstx.commit().apply_remote_op_in_txbypassenqueue_op_in_tx(anti-ping-pong). Tx per op :observe_remote_conn + apply + cursor advance. Echoes (device_idmatch) = Skipped, mapping miss / unknown entity / malformed = Ignored.File hash routing
Pour
liked_trackettrack_rating, leentity_idest le BLAKE3 file_hash directement. Trade-off : si le fichier n'a pas été scanné sur device B, l'op est Ignored (le user verra le like / rating apparaître au prochain rescan du même fichier). Pas de mapping table pour track parce que le contenu du fichier EST le canonical key naturel.What lands
Migration
20260603000001_sync_library_canonical_id.sql—library.canonical_id TEXT+ UUIDv4 backfill (avecrandom() & 3bitmask, leçon CR de 4b) + UNIQUE index + AFTER INSERT/BEFORE UPDATE triggers + sync_id_map seed.Core —
library::{insert_conn, update_conn, delete_conn}extraits (même pattern que playlist atomicity refactor #195). Trait methods délèguent.sync::canonical — 3 nouvelles ENTITY_* constantes +
ensure_local_library/set_canonical_library(mirror playlist) +local_track_for_hash/file_hash_for_local_trackpour le hash routing.Outbound hooks :
commands/library.rs::create_library/update_library/delete_library— tx + enqueuecommands/track.rs::toggle_like_track— tx + enqueueliked_track(insert/delete)commands/track.rs::set_track_rating— tx + enqueuetrack_rating(set/delete)Apply — 3 nouveaux dispatchers (
apply_library_op,apply_liked_track_op,apply_track_rating_op) routés depuisapply_remote_op_in_tx.Tests — 5 nouveaux tests dans
sync::apply:applies_remote_library_insertapplies_liked_track_via_file_hashliked_track_for_unknown_hash_is_ignoredapplies_track_rating_via_file_hashtrack_rating_out_of_range_is_ignoredOut of scope
commands/edit.rs::update_track_tags) — le filesystem est source of truth, rescan résout. Cross-device tag sync est une autre feature.Test plan
Summary by CodeRabbit
Notes de version
Nouvelles Fonctionnalités
Améliorations
Migration