Skip to content

feat(scan): add a per-folder deep rescan action#419

Merged
InstaZDLL merged 2 commits into
mainfrom
fix/deep-rescan-folder
Jul 20, 2026
Merged

feat(scan): add a per-folder deep rescan action#419
InstaZDLL merged 2 commits into
mainfrom
fix/deep-rescan-folder

Conversation

@InstaZDLL

@InstaZDLL InstaZDLL commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

Addresses the remaining half of issue #366's Symptom A (not auto-closing — this needs runtime confirmation, see Test plan).

The scanner's Phase 1 fast path in scan_folder_inner skips a file whose (mtime, size) matches the stored row — cheap, and what makes re-scans of a large library fast. But it means a tag or embedded-picture edit made by an external tool that preserves the file's mtime (Mp3tag has a "preserve modification time" option, among others) is invisible: the file never reaches the hash/re-extract step that would actually notice the change.

refresh_folder_covers already closed the sidecar cover.jpg half of this symptom (a prior PR). This closes the embedded tag/picture half — the part still open.

Changes

  • scan_folder_inner gains a deep: bool parameter. When true, Phase 1's (mtime, size) check is skipped entirely, so every file in the folder is re-hashed and re-read (hash_file's doc comment already notes a tag rewrite shifts bytes in the hashed window, so the hash-compare step downstream correctly picks up the edit — Phase 1 was the only thing standing in the way).
  • Every existing caller (fs-watcher, scan-on-start, rescan_library, import_paths) passes false, so normal scans are unaffected.
  • New optional deep argument on the scan_folder command, wired to a per-folder action (spinning refresh icon) in the library's "Dossiers" tab — the only place a user targets an individual folder today.
  • Deliberately scoped to per-folder, not a rescan_library/global Settings option: the reported problem is "I edited one album's tags," not "make every scan slow by default."
  • i18n key (library.folderList.deepRescan) propagated to all 17 locales.

Test plan

  • cargo check --workspace --all-targets — clean
  • cargo test -p waveflow --lib — 120/120 passing
  • bun run typecheck — clean
  • bun run lint — clean
  • Manual: edit a track's genre with an external tool that preserves mtime, confirm the normal rescan still misses it, confirm the new deep-rescan action picks it up — not done in this session (no such external-tool edit scenario reproduced locally)

Summary by CodeRabbit

  • Nouvelles fonctionnalités

    • Ajout d’un bouton de rescan approfondi pour chaque dossier de bibliothèque.
    • Le rescan approfondi relit et réanalyse tous les fichiers afin de détecter les changements de métadonnées ou de pochettes, même sans modification de date.
    • Un indicateur de progression s’affiche pendant l’opération, et les rescans simultanés sont empêchés.
  • Améliorations

    • Les libellés de cette fonctionnalité sont désormais disponibles dans les langues prises en charge.
    • Les scans habituels conservent leur comportement rapide.

Symptom A of issue #366: the scanner's fast path keys purely on
(mtime, size) at scan.rs's Phase 1, so a tag or embedded-picture edit
made by an external tool that preserves the file's mtime (Mp3tag's
'preserve modification time' option, among others) is never noticed —
the file never even reaches the hash/re-extract step that would
actually catch it. refresh_folder_covers already closed the sidecar
cover.jpg half of this issue; this closes the remaining half.

scan_folder_inner gains a deep: bool parameter that bypasses the
Phase 1 fast path entirely when true, so every file in the folder is
re-hashed and re-read. Every existing caller (the fs-watcher, the
scan-on-start path, rescan_library, import_paths) passes false to
keep normal re-scans fast; only the new scan_folder(folderId, deep)
argument, wired to a per-folder action in the library's 'Dossiers'
tab, can opt in. Deliberately scoped to per-folder rather than adding
a matching deep option to rescan_library/import_paths or a global
Settings card -- the reported problem is 'I edited one album's tags',
not 'I need every scan to be slow by default'.

i18n key propagated to all 17 locales.

Refs #366
@InstaZDLL InstaZDLL added scope: frontend React/Vite frontend (src/) scope: backend Rust/Tauri backend (src-tauri/) scope: i18n Translations (src/i18n/) type: feat New feature size: m 50-200 lines labels Jul 20, 2026
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@InstaZDLL, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 11 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5ddf3f3a-3922-468a-b4df-e77bc5f2d388

📥 Commits

Reviewing files that changed from the base of the PR and between 8d20ea4 and e14e80f.

📒 Files selected for processing (2)
  • src-tauri/crates/app/src/commands/scan.rs
  • src/components/views/LibraryView.tsx
📝 Walkthrough

Walkthrough

Ajout d’un rescan approfondi par dossier, transmis de l’interface au backend. Ce mode relit et re-hache tous les fichiers, sans utiliser le filtrage mtime/taille. Les chemins de scan existants conservent le mode standard.

Changes

Rescan approfondi

Layer / File(s) Summary
Mode deep du moteur de scan
src-tauri/crates/app/src/commands/scan.rs
scan_folder et scan_folder_inner acceptent le mode deep, qui envoie tous les fichiers vers l’extraction au lieu d’appliquer le raccourci mtime/taille.
Propagation vers les entrées de scan
src/lib/tauri/library.ts, src-tauri/crates/app/src/commands/library.rs, src-tauri/crates/app/src/lib.rs, src-tauri/crates/app/src/watcher.rs
L’API transmet deep; les rescan, import, démarrage et surveillance passent explicitement false.
Commande UI et traductions
src/components/views/LibraryView.tsx, src/i18n/locales/*
Un bouton par dossier lance le rescan approfondi, bloque les lancements concurrents et affiche son état via deepRescan, traduit dans les locales disponibles.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  actor Bibliothèque
  participant LibraryView
  participant scanFolder
  participant scan_folder
  participant scan_folder_inner
  Bibliothèque->>LibraryView: Cliquer sur deep rescan
  LibraryView->>scanFolder: scanFolder(folderId, true)
  scanFolder->>scan_folder: invoke(folderId, deep)
  scan_folder->>scan_folder_inner: deep.unwrap_or(false)
  scan_folder_inner-->>LibraryView: ScanSummary
  LibraryView-->>Bibliothèque: Terminer le spinner
Loading

Possibly related PRs

  • InstaZDLL/WaveFlow#385 : modifie la logique de scan autour du chemin basé sur (mtime, size) dans scan_folder_inner.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Le titre décrit clairement le changement principal et respecte le format Conventional Commits.
Description check ✅ Passed La description couvre le besoin, les changements et les tests, avec la portée per-folder et les locales mises à jour.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/deep-rescan-folder

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 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/scan.rs`:
- Around line 553-579: Update the Phase 1 loop around `to_extract` and
`existing_meta` to remove each discovered path from `existing_meta`
unconditionally before the `!deep` fast-path check. Preserve the existing
metadata comparison and skip behavior, while ensuring deep scans and later
extraction failures cannot cause present files to be marked unavailable.

In `@src/components/views/LibraryView.tsx`:
- Around line 448-459: Update handleDeepRescanFolder so it refreshes the
LibraryContext after scanFolder completes, using the existing listFolders
refresh mechanism or the library:rescanned Tauri event. Preserve the current
guard, error logging, and finally cleanup while ensuring the last-scan date and
track count update in the view.
- Around line 2374-2393: Update the deep-rescan button’s className in the folder
list so it forces opacity-100 when deepRescanFolderId equals folder.id, while
preserving the existing opacity-0 and group-hover behavior for other folders.
Use the existing deepRescanFolderId state and folder.id comparison already used
for aria-busy and the spinner.
🪄 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: dfb9c167-7409-46ec-b1c0-572313865339

📥 Commits

Reviewing files that changed from the base of the PR and between 27a3e97 and 8d20ea4.

📒 Files selected for processing (23)
  • src-tauri/crates/app/src/commands/library.rs
  • src-tauri/crates/app/src/commands/scan.rs
  • src-tauri/crates/app/src/lib.rs
  • src-tauri/crates/app/src/watcher.rs
  • src/components/views/LibraryView.tsx
  • src/i18n/locales/ar.json
  • src/i18n/locales/de.json
  • src/i18n/locales/en.json
  • src/i18n/locales/es.json
  • src/i18n/locales/fr.json
  • src/i18n/locales/hi.json
  • src/i18n/locales/id.json
  • src/i18n/locales/it.json
  • src/i18n/locales/ja.json
  • src/i18n/locales/ko.json
  • src/i18n/locales/nl.json
  • src/i18n/locales/pt-BR.json
  • src/i18n/locales/pt.json
  • src/i18n/locales/ru.json
  • src/i18n/locales/tr.json
  • src/i18n/locales/zh-CN.json
  • src/i18n/locales/zh-TW.json
  • src/lib/tauri/library.ts

Comment thread src-tauri/crates/app/src/commands/scan.rs
Comment thread src/components/views/LibraryView.tsx
Comment thread src/components/views/LibraryView.tsx
Three review findings on the deep-rescan PR, all confirmed:

1. Phase 1 only removed a walked path from existing_meta on the
   fast-path skip branch; the consumer loop's own removal only fires
   on a SUCCESSFUL extraction. A file that's on disk but fails
   extraction (a lock, a transient I/O error, a malformed rewrite
   mid-scan) stayed in existing_meta and got swept into
   is_available = 0 by the end of the scan -- despite being present.
   Deep mode makes this far more likely to bite, since every file in
   the folder is now on that path instead of just the ones that
   happened to change. Fixed by removing each walked path
   unconditionally, before the deep/fast-path branch.

2. handleDeepRescanFolder never refreshed anything after
   scanFolder(..., true) resolved -- scan_folder doesn't emit
   library:rescanned (only folder removal and tag edits do), so a
   folder's last-scan date and track count stayed stale in the UI
   until some unrelated action refreshed them. Now refetches via the
   same listFolders(null) the mount effect already uses.

3. The deep-rescan button's row icon was opacity-0 unconditionally,
   only reaching opacity-100 on :hover -- so the spinner for the
   folder actually being rescanned was invisible the moment the mouse
   left that row, unlike the watch-toggle and delete buttons which
   both stay visible while active. Now forces opacity-100 when
   deepRescanFolderId matches the row.
@InstaZDLL

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@InstaZDLL
InstaZDLL merged commit 5de21d6 into main Jul 20, 2026
14 checks passed
@InstaZDLL
InstaZDLL deleted the fix/deep-rescan-folder branch July 20, 2026 08:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: backend Rust/Tauri backend (src-tauri/) scope: frontend React/Vite frontend (src/) scope: i18n Translations (src/i18n/) size: m 50-200 lines type: feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant