Skip to content

feat(library): allow a custom picture per genre#426

Merged
InstaZDLL merged 2 commits into
mainfrom
feat/genre-custom-picture
Jul 22, 2026
Merged

feat(library): allow a custom picture per genre#426
InstaZDLL merged 2 commits into
mainfrom
feat/genre-custom-picture

Conversation

@InstaZDLL

@InstaZDLL InstaZDLL commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Summary

  • Genre tiles (library grid + detail page header) rendered a fixed generic icon with no way to customize it.
  • Adds a manual per-genre picture, mirroring the existing artist/album manual-image override pattern: new genre.artwork_id column, set_genre_artwork_from_file / clear_genre_artwork commands (magic-byte validated jpg/png/webp, hash-addressed in the shared per-profile artwork/ dir, source = 'manual'), and a GenreImagePickerModal opened from a Pencil overlay on both the grid tile and the detail header.
  • list_genres / get_genre_detail now resolve and ship the artwork path (same slim-row + artwork_base economy as albums/artists).
  • i18n: genreImagePicker.* propagated to all 17 locales.

Test plan

  • cargo check --workspace --all-targets — green
  • bun run typecheck — green
  • bun run lint — green
  • cargo test --workspace — blocked locally by disk space (link step: "No space left on device"), not a code issue; relying on CI for this run
  • Manual: set a genre picture from the detail page, confirm it shows on the grid tile too; clear it, confirm it falls back to the placeholder icon

Closes #424

https://claude.ai/code/session_01F9cgAgcbjFFe74k9vTEcjV

Summary by CodeRabbit

  • Nouvelles fonctionnalités
    • Ajout de photos personnalisées pour les genres de la bibliothèque.
    • Possibilité de sélectionner, remplacer ou supprimer une image (JPG/PNG/WEBP) et affichage des artworks sur les tuiles et la page de détail.
    • Bouton d’édition (icône crayon) ouvrant une modale dédiée pour chaque genre.
  • Documentation
    • Documentation mise à jour pour la gestion des images de genre.
  • Accessibilité et traductions
    • Nouvelles chaînes i18n pour la modale d’édition de genre, avec libellés et texte d’accessibilité.

Genre tiles and the genre detail header rendered a fixed generic icon
with no way to customize it. Adds a manual per-genre picture, mirroring
the existing artist/album manual-image override pattern: a new
genre.artwork_id column, set_genre_artwork_from_file / clear_genre_artwork
commands (magic-byte validated, hash-addressed in the shared per-profile
artwork dir), and a GenreImagePickerModal opened from a Pencil overlay on
both the library grid tile and the detail page header.

Closes #424
@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: xl > 500 lines labels Jul 22, 2026
@coderabbitai

coderabbitai Bot commented Jul 22, 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: 035164d6-0838-4db4-bb1c-4035f7a794a2

📥 Commits

Reviewing files that changed from the base of the PR and between 38e4e35 and 2654d73.

📒 Files selected for processing (4)
  • src-tauri/crates/app/src/commands/browse.rs
  • src/components/common/GenreImagePickerModal.tsx
  • src/components/views/LibraryView.tsx
  • src/lib/tauri/browse.ts

📝 Walkthrough

Walkthrough

Cette évolution ajoute l’artwork manuel des genres avec persistance SQL, commandes Tauri, chemins d’image, modale de sélection/suppression, affichage dans la bibliothèque et le détail, ainsi que les traductions associées.

Changes

Artwork manuel des genres

Layer / File(s) Summary
Contrat backend et persistance
src-tauri/crates/app/src/commands/browse.rs, src-tauri/crates/app/src/commands/deezer.rs, src-tauri/crates/app/src/lib.rs, src-tauri/migrations/profile/*
Les réponses de genres et de détail incluent les métadonnées d’artwork, la relation genre.artwork_id est ajoutée et les commandes Tauri permettent de définir ou supprimer l’image.
Pont Tauri et contrats frontend
src/lib/tauri/browse.ts, src/lib/tauri/detail.ts
Les réponses backend sont converties en chemins d’artwork frontend, et les opérations de définition et suppression sont exposées.
Sélection et affichage dans l’interface
src/components/common/GenreImagePickerModal.tsx, src/components/views/GenreDetailView.tsx, src/components/views/LibraryView.tsx, src/i18n/locales/*, docs/features/library.md
La modale gère la sélection et la suppression, le détail fournit l’édition, la bibliothèque affiche l’artwork, et les traductions et la documentation sont ajoutées.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GenreDetailView
  participant GenreImagePickerModal
  participant browse_ts
  participant BackendTauri
  GenreDetailView->>GenreImagePickerModal: ouvre la modale d’artwork
  GenreImagePickerModal->>browse_ts: appelle setGenreArtworkFromFile
  browse_ts->>BackendTauri: invoque la commande backend
  BackendTauri-->>GenreImagePickerModal: retourne le résultat
  GenreImagePickerModal-->>GenreDetailView: déclenche le rafraîchissement
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.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 est clair, en convention commit, et décrit bien l’ajout d’une image personnalisable par genre.
Description check ✅ Passed La description couvre le résumé, les tests, le lien vers l’issue et la mise à jour i18n, malgré l’absence de checklist formelle.
Linked Issues check ✅ Passed La PR répond à #424 : image manuelle par genre, modal de sélection, persistance locale et chemins d’artwork renvoyés.
Out of Scope Changes check ✅ Passed Les changements visibles restent alignés avec l’objectif : backend, UI, i18n et doc pour l’artwork manuel des genres.
✨ 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 feat/genre-custom-picture

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: 4

🤖 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/browse.rs`:
- Around line 717-735: Update the artwork file flow around create_dir_all, read,
hashing, and write to run inside a tokio::task::spawn_blocking closure. Replace
unbounded std::fs::read with bounded reading of at most MAX_IMAGE_BYTES + 1
bytes, reject inputs exceeding MAX_IMAGE_BYTES, then preserve format detection,
hashing, and deduplicated target writing within the blocking task.

In `@src/components/common/GenreImagePickerModal.tsx`:
- Around line 32-33: Reset the error state when the modal transitions to open by
updating the isOpen handling around the error state in GenreImagePickerModal.
Ensure setError clears the previous message on each isOpen=true transition,
while preserving error updates during the current application attempt.

In `@src/components/views/LibraryView.tsx`:
- Around line 2208-2218: Update the Artwork instance in the genre button to use
an empty alt value instead of genre.name, keeping the adjacent genre.name text
as the sole accessible label for both the loaded image and placeholder.

In `@src/lib/tauri/browse.ts`:
- Around line 322-330: Update the invoke calls in setGenreArtworkFromFile and
clearGenreArtwork to use the explicit void generic, preserving their
Promise<void> return contracts.
🪄 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: cbf62939-cc35-4a02-bbd0-eef401a48e51

📥 Commits

Reviewing files that changed from the base of the PR and between 25d48de and 38e4e35.

📒 Files selected for processing (27)
  • docs/features/library.md
  • src-tauri/crates/app/src/commands/browse.rs
  • src-tauri/crates/app/src/commands/deezer.rs
  • src-tauri/crates/app/src/lib.rs
  • src-tauri/migrations/profile/20260721103000_genre_artwork.sql
  • src/components/common/GenreImagePickerModal.tsx
  • src/components/views/GenreDetailView.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
  • src/lib/tauri/browse.ts
  • src/lib/tauri/detail.ts

Comment thread src-tauri/crates/app/src/commands/browse.rs Outdated
Comment thread src/components/common/GenreImagePickerModal.tsx
Comment thread src/components/views/LibraryView.tsx
Comment thread src/lib/tauri/browse.ts Outdated
- Bound the manual genre-picture read to MAX_IMAGE_BYTES + 1 and move
  create_dir_all/read/hash/write off the async runtime via
  spawn_blocking, instead of an unbounded std::fs::read on the tokio
  worker thread.
- Reset GenreImagePickerModal's error state when it closes, so a stale
  message from a previous attempt doesn't linger on reopen.
- Genre grid tile: alt="" on the Artwork image since the adjacent
  genre name text is already the button's accessible label — avoids
  double announcement.
- setGenreArtworkFromFile/clearGenreArtwork: explicit invoke<void>
  generic, matching the sibling clearArtistArtwork/clearAlbumMotionArtwork
  wrappers.
@InstaZDLL
InstaZDLL merged commit 8884335 into main Jul 22, 2026
14 checks passed
@InstaZDLL
InstaZDLL deleted the feat/genre-custom-picture branch July 22, 2026 08:44
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: xl > 500 lines type: feat New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: allow a custom picture per tag/genre

1 participant