Skip to content

feat(library): deep rescan for the whole library, not just one folder - #460

Merged
InstaZDLL merged 4 commits into
mainfrom
fix/457-deep-rescan-library
Jul 30, 2026
Merged

feat(library): deep rescan for the whole library, not just one folder#460
InstaZDLL merged 4 commits into
mainfrom
fix/457-deep-rescan-library

Conversation

@InstaZDLL

@InstaZDLL InstaZDLL commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Fixes #457@jo-el414 changed genres on several albums in Mp3tag, hit Rescan the library, and the genres never updated. An earlier artist rename through the same workflow had worked, which is what made it look inconsistent.

Cause

The scan fast path skips any file whose (mtime, size) is unchanged. Mp3tag preserves the file's modification date, and an ID3 rewrite usually fits inside the existing padding so the size doesn't move either — the file looks untouched, and its new tags are never read.

That also explains why the artist rename worked: CelineCéline grows the tag by a byte, which likely pushed it past the padding and changed the file size.

Why the existing fix didn't help

The bypass already existed: scan_folder(deep: true), added for this exact root cause in #366, reachable from the magnifier button on a folder row.

But rescan_library hard-coded deep = false:

scan_folder_inner(&pool, &artwork_dir, folder_id, Some(&app), false).await

So the library-wide button — the one you actually reach for after retagging a batch of albums — could never see mtime-preserving edits. The escape hatch existed but not where the problem is met.

Change

deep threaded through rescan_library (defaulting to false, so existing callers such as Settings are untouched), plus a second header button next to Rescan mirroring the per-folder one.

A separate control rather than a modifier on the existing button: a full re-read of the library is markedly slower, so it should be chosen deliberately, not triggered by a stray modifier key. New i18n keys in all 17 locales.

Docs

docs/features/library.md asserted:

A tag edit that rewrites the audio file is already detected, since it moves that file's mtime

That is precisely the assumption this bug disproves. It now carries the caveat and documents both deep-rescan entry points, including the interaction with Split this artist (a deep pass re-reads tags authoritatively and undoes an in-place split).

Verification

cargo check -p waveflow --all-targets, bun run typecheck, bun run lint, prettier — all clean (exit codes read directly).

Not visually verified — the app doesn't run outside the Tauri shell here. Worth confirming on the reporter's own case: retag a genre in Mp3tag with "preserve modification time" on, then use the new button.

Note on scope

This makes the workaround reachable; it doesn't make a normal rescan notice mtime-preserving edits. Doing that would mean hashing every file on every scan — the cost the fast path exists to avoid. If it turns out users hit this often, the middle ground would be hashing only files whose tag-region size changed, but that's a bigger change than this bug warrants.

Summary by CodeRabbit

  • Nouvelles fonctionnalités
    • Ajout d’un mode re-scan approfondi (“Deep pass”) qui relit tous les fichiers, même si la date de modification/taille n’a pas changé.
    • Disponible par dossier et pour toute la bibliothèque, avec exécution en séquence et affichage d’une progression dédiée.
    • Les boutons de re-scan sont désormais mieux coordonnés pour éviter les lancements concurrents.
  • Documentation
    • Mise à jour de la section “Deep rescan” : coût, mode opt-in, et interaction avec Split this artist.
  • Traductions
    • Ajout des libellés “deep rescan” et enrichissement de l’affichage de fin avec le compte des erreurs.

Reported in #457: genre edits made in Mp3tag never showed up after
"Rescan the library", while an earlier artist rename had worked.

The scan fast path skips a file whose `(mtime, size)` is unchanged.
Mp3tag preserves the modification date, and an ID3 rewrite usually fits
in the existing padding so the size doesn't move either — the file looks
untouched and its new tags are never read. (The artist rename likely
grew the tag past its padding, changing the size.)

The bypass for this already existed — `scan_folder(deep: true)`, added
for the same root cause in #366 — but only per folder. `rescan_library`
hard-coded `false`, so the library-wide button users actually reach for
could never see these edits.

Thread `deep` through `rescan_library` and add a second header button
next to Rescan, mirroring the per-folder one. Separate control rather
than a modifier on the existing button: a full re-read is markedly
slower, so it should be chosen deliberately. i18n across all 17 locales.

Docs: library.md claimed a tag edit "is already detected, since it moves
that file's mtime". That is the assumption this bug disproves, so it now
states the caveat and documents both deep-rescan entry points.

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

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0e5b471f-af83-4a31-8626-4d313b07b0c6

📥 Commits

Reviewing files that changed from the base of the PR and between 0f867fe and 7b9d39f.

📒 Files selected for processing (1)
  • src/components/common/ScanProgressToast.tsx

📝 Walkthrough

Walkthrough

Le PR ajoute un rescan profond optionnel, disponible par dossier et pour toutes les bibliothèques. Le paramètre est propagé jusqu’au scanner backend, avec prévention des chevauchements, progression localisée et documentation du comportement.

Changes

Rescan profond

Layer / File(s) Summary
Propagation du mode profond
src/lib/tauri/library.ts, src/hooks/useLibrary.ts, src/contexts/LibraryContext.tsx, src-tauri/crates/app/src/commands/library.rs
Le paramètre optionnel deep est transmis à scan_folder_inner, avec false par défaut.
Orchestration des rescans
src/components/views/LibraryView.tsx
Un bouton global lance les rescans profonds séquentiels et les actions globales ou par dossier sont mutuellement désactivées pendant un scan.
Progression et traductions
src/components/common/ScanProgressToast.tsx, src/i18n/locales/*.json
Le toast affiche les erreurs finales et les locales ajoutent les libellés du rescan profond et des erreurs.
Documentation du comportement
docs/features/library.md
La documentation décrit le re-hachage complet, les points d’entrée, l’exclusion mutuelle et l’effet possible sur les splits d’artistes.

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

Sequence Diagram(s)

sequenceDiagram
  participant LibraryView
  participant LibraryContext
  participant TauriAPI
  participant Scanner
  participant ScanProgressToast
  LibraryView->>LibraryContext: rescanLibrary(libraryId, true)
  LibraryContext->>TauriAPI: rescan_library({ libraryId, deep: true })
  TauriAPI->>Scanner: scan_folder_inner(..., deep)
  Scanner-->>TauriAPI: RescanSummary
  TauriAPI-->>LibraryContext: refresh()
  Scanner-->>ScanProgressToast: progression et erreurs
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning La refonte du toast de fin de scan et des messages d’erreur semble hors du besoin #457 centré sur le deep rescan. Isolez les changements de notifications/progression du scan dans une PR séparée ou ajoutez-les explicitement au scope de #457.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Le titre résume correctement l’ajout principal : un deep rescan pour toute la bibliothèque, pas seulement un dossier.
Linked Issues check ✅ Passed Le PR implémente bien #457 avec un deep rescan bibliothèque, le passage de deep dans rescanLibrary et l’UI dédiée.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/457-deep-rescan-library

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 `@docs/features/library.md`:
- Around line 35-38: Update the “Two entry points” documentation in
docs/features/library.md to distinguish the locations: document the per-folder
magnifier under My music → Folders, and document the whole-library button next
to Rescan in the My music header independently of the active tab.

In `@src/components/views/LibraryView.tsx`:
- Around line 153-155: Unify the rescan locking in LibraryView by deriving a
shared isAnyRescanActive state from both isDeepRescanning and
deepRescanFolderId, then use it for the global and folder rescan controls,
including the referenced handlers or disabled states. Ensure either rescan mode
disables both actions and prevents concurrent scan_folder_inner writes.
- Around line 445-451: Modify the deep-rescan loop in LibraryView so each
rescanLibrary(lib.id, true) call has its own error handling, allowing subsequent
libraries to continue after one fails. Track or report the failed library
errors, while retaining the final setIsDeepRescanning(false) cleanup and
avoiding a loop-level catch that aborts iteration.
🪄 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: 009d081c-d820-4cc6-8647-1224d2d8c42d

📥 Commits

Reviewing files that changed from the base of the PR and between d35d640 and c4f25ac.

📒 Files selected for processing (23)
  • docs/features/library.md
  • src-tauri/crates/app/src/commands/library.rs
  • src/components/views/LibraryView.tsx
  • src/contexts/LibraryContext.tsx
  • src/hooks/useLibrary.ts
  • 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 docs/features/library.md Outdated
Comment thread src/components/views/LibraryView.tsx
Comment thread src/components/views/LibraryView.tsx
Three review findings on #460.

**Concurrent scans were possible.** The two header buttons only guarded
against each other, leaving the per-folder deep-rescan button free to
start a second scan alongside a library-wide one. `scan_folder_inner`
writes and SQLite takes one writer at a time, so that is a real
overlap, not just a confusing UI. All three controls now derive their
disabled state and their early-return guard from one
`isAnyRescanActive`.

**One failing library stranded the rest.** The loop wrapped every
iteration in a single try, so a drive that went away aborted the pass
for the libraries after it. Errors are handled per library now. The
plain rescan loop had the identical flaw right next to it and gets the
same treatment — leaving one of two adjacent loops with the bug would
be worse than the small extra diff.

**Docs placed both entry points in the same screen.** The per-folder
magnifier is under My music → Folders, but the library-wide button sits
in the My music header and is reachable from any tab.

Refs #457
@InstaZDLL InstaZDLL added size: l 200-500 lines and removed size: m 50-200 lines labels Jul 29, 2026

@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: 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/components/views/LibraryView.tsx`:
- Around line 456-464: Update the library rescan loops around rescanLibrary so
they inspect each returned RescanSummary.errors, not only Promise rejections.
Aggregate or display the number of errors for each library, while preserving the
existing catch handling for rejected rescans and continuing to process other
libraries.
🪄 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: b0eb8912-f459-4081-b1cf-a8377dfa2a32

📥 Commits

Reviewing files that changed from the base of the PR and between c4f25ac and bca21df.

📒 Files selected for processing (2)
  • docs/features/library.md
  • src/components/views/LibraryView.tsx

Comment thread src/components/views/LibraryView.tsx
Review finding on #460. `rescan_library` folds per-folder failures into
`summary.errors` and resolves anyway, so awaiting it only catches an
outright rejection: a pass where half the folders failed looked exactly
like a clean one, in both loops.

Both now read the summary and log the aggregate folder-error count.

The count was invisible in the UI too. `scan:progress` has carried
`errors` all along and the toast never rendered it, so a partially
failed scan announced "Scan complete" with no qualification. It now
appends the failed-file count in amber when there is one — which covers
every scan path, not just this button.

New i18n key across all 17 locales, with the CLDR plural categories
Russian and Arabic need (a missing `_other` on ru is what makes i18next
fall back to English).

Refs #457

@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: 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/components/common/ScanProgressToast.tsx`:
- Around line 112-119: Update the completed-scan rendering in ScanProgressToast
so done && errors > 0 uses scanProgress.doneErrors as the primary completion
message instead of the normal success title/subtitle. Also switch the completion
icon and visual styling from success green to an error or warning treatment for
partial failures, while preserving the existing clean-success appearance when
errors is zero.
🪄 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: 40960c12-1890-40b7-8bd3-0783a4ac5a1e

📥 Commits

Reviewing files that changed from the base of the PR and between bca21df and 0f867fe.

📒 Files selected for processing (19)
  • src/components/common/ScanProgressToast.tsx
  • 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

Comment thread src/components/common/ScanProgressToast.tsx Outdated
Review finding on #460. Appending an amber line under a green checkmark
and "Scan complete" still presents a partially failed scan as a clean
one — the headline is what people read.

When the scan finishes with errors, the failure count becomes the
headline, the icon switches to an amber warning, and the title takes the
amber tone. The added/updated/skipped line stays underneath as context,
and a clean scan looks exactly as before.

Refs #457
@InstaZDLL
InstaZDLL merged commit 9e6cd30 into main Jul 30, 2026
14 checks passed
@InstaZDLL
InstaZDLL deleted the fix/457-deep-rescan-library branch July 30, 2026 05:03
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: docs Docs, README, assets scope: frontend React/Vite frontend (src/) scope: i18n Translations (src/i18n/) size: l 200-500 lines type: feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: scan for update genre tag did not view in UI after use "Rescan the library".

1 participant