feat: implement duplicate title normalized filter

This commit is contained in:
Tiberiu Ichim 2026-02-22 16:46:14 +02:00
parent aa85106681
commit ead215e777
13 changed files with 276 additions and 1 deletions

View file

@ -0,0 +1,28 @@
# Normalized Title Filter Specification
## Overview
Added a "Duplicate Title" filter option to the frontend library controls. To support performant querying of duplicate titles across potentially massive libraries, a new `titleNormalized` concept was introduced to the backend models and database schema, allowing for fast `$Op.in` and `COUNT() > 1` matching via SQL.
## Verification Plan
1. Start server and ensure no migration errors occur
2. Verify existing books/podcasts have their `titleNormalized` fields backfilled
3. Select "Duplicate Title" in the library filter
4. Verify results contain books/podcasts that are identical or differ only by punctuation/casing
## Architectural Decisions
- **Pre-computed database fields**: Chosen over in-memory string similarity calculations to maintain $O(1)$ query complexity at filter time instead of $O(N^2)$ Node.js looping.
- **`getNormalizedTitle` Utility**: Centralized normalization to strip all non-unicode letter characters (`/[^\p{L}]/gu`), numbers, spaces, and ignore common sorting prefixes.
- **SQLite Triggers**: Implemented via migrations to automatically keep `libraryItems.titleNormalized` in sync with updates to `books.title` and `podcasts.title`.
## Traceability
| File | Changes |
| :--- | :--- |
| `server/utils/index.js` | Added `getNormalizedTitle` utility function. |
| `server/models/Book.js` | Added `titleNormalized` property and updated saving logic. |
| `server/models/Podcast.js` | Added `titleNormalized` property and updated saving logic. |
| `server/models/LibraryItem.js` | Added `titleNormalized` property, index, and hook copying from media. |
| `server/migrations/v2.32.9-*-columns.js` | Added migration to alter tables, backfill data, add hooks & triggers. |
| `server/utils/queries/libraryItemsBookFilters.js` | Added 'duplicates' filter handler to SQL mapping. |
| `server/utils/queries/libraryItemsPodcastFilters.js` | Added 'duplicates' filter handler to SQL mapping. |
| `client/components/controls/LibraryFilterSelect.vue` | Added "Duplicate Title" options to dropdown items. |
| `client/strings/en-us.json` | Added `LabelDuplicateTitle` translation string. |

View file

@ -0,0 +1,42 @@
# Player Keyboard Shortcuts Specification
## Overview
This feature introduces new keyboard shortcuts to the audio player, enabling improved playback control for power users. This expands on the existing standard keyboard interactions to include dedicated major jumps (60 seconds) and chapter navigation mapping.
## New Keyboard Shortcuts
The following hotkey definitions have been added or updated in the `AudioPlayer` context:
| Context | Action | Shortcut | Description |
| :--- | :--- | :--- | :--- |
| AudioPlayer | `JUMP_FORWARD_ALT` | `Shift + Right Arrow` | Alternative shortcut for the user's standard jump forward amount. |
| AudioPlayer | `JUMP_BACKWARD_ALT` | `Shift + Left Arrow` | Alternative shortcut for the user's standard jump backward amount. |
| AudioPlayer | `JUMP_FORWARD_MAJOR` | `Ctrl + Shift + Right Arrow` | Hardcoded 60-second jump forward. |
| AudioPlayer | `JUMP_BACKWARD_MAJOR`| `Ctrl + Shift + Left Arrow` | Hardcoded 60-second jump backward. |
| AudioPlayer | `NEXT_CHAPTER` | `Shift + Up Arrow` | Skips directly to the start of the next chapter. |
| AudioPlayer | `PREV_CHAPTER` | `Shift + Down Arrow` | Skips back to the previous chapter mark (or restarts the chapter). |
| AudioPlayer | `INCREASE_PLAYBACK_RATE` | `]` (BracketRight) | Previously `Shift + Up Arrow`. Now moved to accommodate chapter navigation. |
| AudioPlayer | `DECREASE_PLAYBACK_RATE` | `[` (BracketLeft) | Previously `Shift + Down Arrow`. Now moved to accommodate chapter navigation. |
## Implementation Details
### Modifier Parsing
- Upgraded the hotkey parsing logic within `getHotkeyName(e)` in `client/layouts/default.vue` and `client/pages/share/_slug.vue` to actively capture `ctrlKey` and `altKey` modifier combinations, aligning with the pattern used elsewhere in the UI.
### Player Core
- `jumpForwardMajor` and `jumpBackwardMajor` logic was integrated directly into `client/components/player/PlayerUi.vue` using its `seek` primitive capability, computing relative to the local `currentTime` and `duration`.
- Mapped the chapter navigation constants (`NEXT_CHAPTER` and `PREV_CHAPTER`) directly to existing handler functions (`goToNext()` and `prevChapter()`), keeping modifications self-contained.
## Files Modified
| File Location | Category | Reason for Change |
| :--- | :--- | :--- |
| `AGENTS.md` | **Documentation** | Document how AI agents can lookup previous artifact specifications in `artifacts/index.md`. |
| `artifacts/docs/ux_power_user_shortcuts.md` | **Documentation** | Added Audio Player Shortcuts section. |
| `client/plugins/constants.js` | **Frontend** | Add `BracketLeft` and `BracketRight` to KeyNames registry; redefine `Hotkeys.AudioPlayer` with the new combinations. |
| `client/components/player/PlayerUi.vue` | **Frontend** | Implement the jump magnitude logic and intercept the actual shortcut emissions. |
| `client/layouts/default.vue` | **Frontend** | Add robust modifier flag interpretation in the main view wrapper. |
| `client/pages/share/_slug.vue` | **Frontend** | Replicate the exact same modifier key evaluation logic for the standalone unauthenticated player widget. |
## Edge Cases Addressed
- **Duration Boundary checks**: Hardcoded jumps (60 sec) use bounds checking to prevent seeking below position 0, or past the overall duration of the target media.
- **Playback Rate Shortcut conflict**: Addressed by cleanly replacing the default array to brackets `[` and `]` making intuitive sense for increasing and decreasing numeric magnitudes.

View file

@ -26,6 +26,7 @@ This index provides a quick reference for specification and documentation files
| **2026-02-22** | [centralized_keyboard_shortcuts.md](2026-02-22/centralized_keyboard_shortcuts.md) | Specification for centralizing keyboard shortcut definitions into a single configuration file. |
| **2026-02-22** | [match_default_behavior.md](2026-02-22/match_default_behavior.md) | Specification for the new default "Direct Apply" match behavior and Review & Edit button. |
| **2026-02-22** | [player_keyboard_shortcuts.md](2026-02-22/player_keyboard_shortcuts.md) | Specification for new player keyboard shortcuts including major skip and chapter jumps. |
| **2026-02-22** | [normalized_title_filter.md](2026-02-22/normalized_title_filter.md) | Specification for the Normalized Title / Duplicate Title filter implementations and database columns. |
| **General** | [docs/consolidate_feature.md](docs/consolidate_feature.md) | Comprehensive documentation for the "Consolidate" feature, including conflict resolution and technical details. |
| **General** | [docs/item_restructuring_guide.md](docs/item_restructuring_guide.md) | Guide for Moving, Merging, and Splitting (Promoting) library items. |
| **General** | [docs/metadata_management_tools.md](docs/metadata_management_tools.md) | Documentation for Reset Metadata and Batch Reset operations. |