feat(local-library): Plex consolidation in chunked albums page query

Plex cache lives in a separate SQLite database
(`<data_dir>/qbz/plex_cache.db`), which is why `v2_library_get_albums_page`
previously only saw local_tracks and the LocalLibraryView chunked
path had to fall back to the legacy virtualizer when Plex was on.
That fallback meant Plex-dominant users (which is the common case)
got none of the chunked-store / recycling-pool perf work.

Now consolidated server-side via SQL `ATTACH DATABASE` + `UNION ALL`:

Backend (qbz-library):

  - `get_albums_metadata_page` accepts `plex_cache_path: Option<&Path>`.
    When the file exists it `ATTACH`es the plex DB to the connection,
    runs a CTE that aggregates `plex_cache.plex_cache_tracks` by
    `album_key` into the same column shape as local_aggregated, and
    UNIONs both before the filter/sort/pagination step. DETACHes at
    the end so subsequent queries don't see stale attachment.
    Plex group_key prefixed with `'plex:'` so it doesn't collide with
    local group keys; `'plex'` literal in the `source` column drives
    the frontend's SourceBadge mapping.
  - Schema differences smoothed via `CAST` in the plex arm:
    `duration_ms / 1000 → total_duration` (matches local seconds),
    `sampling_rate_hz → sample_rate` (INTEGER → REAL).
  - `count_albums_metadata_for_page` mirrors the same UNION so total
    is consistent across empty-page edge cases.

Backend (Tauri command):

  - `v2_library_get_albums_page` accepts an `include_plex` bool.
    When true, resolves the plex cache path the same way
    `plex/mod.rs::open_plex_cache_db` does and passes it through.

Frontend:

  - Chunked store's `LocalAlbumsQueryParams` carries an `includePlex`
    flag, included in the `paramsEqual` check so toggling Plex
    visibility invalidates the chunk cache.
  - LocalLibraryView drops the `!isPlexLibraryEnabled()` guard from
    both the `$effect` that calls `setParams` and the render
    condition that picks chunked vs legacy. The new path serves
    Plex-only, local-only, and mixed libraries the same way.

cc
This commit is contained in:
vicrodh committed 2026-05-12 15:19:18 -06:00
1 parent 2e58c889b1
commit 1aea33cf19
4 files changed
+155 -16

No files matched your search

+17
View File
@@ -2584,6 +2584,10 @@ pub async fn v2_library_get_albums_metadata(
/// recycling-grid pool + chunked store on the frontend: caller asks for
/// `[offset, offset+limit)` and receives that page plus the total count
/// of albums matching the same filter (drives scrollbar pre-allocation).
///
/// When `include_plex` is true and the `plex_cache.db` cache file
/// exists in the user's data dir, the result is the UNION of local and
/// plex sources — sort, filter, and pagination apply to both at once.
#[tauri::command]
pub async fn v2_library_get_albums_page(
offset: u64,
@@ -2592,6 +2596,7 @@ pub async fn v2_library_get_albums_page(
sort_by: Option<String>,
sort_dir: Option<String>,
exclude_network_folders: Option<bool>,
include_plex: Option<bool>,
state: State<'_, LibraryState>,
download_settings_state: State<'_, DownloadSettingsState>,
) -> Result<qbz_library::AlbumsMetadataPage, String> {
@@ -2603,6 +2608,17 @@ pub async fn v2_library_get_albums_page(
.map(|s| s.show_in_library)
.unwrap_or(false);
// Resolve the plex_cache.db path the same way `plex/mod.rs`
// does (`dirs::data_dir() + qbz/plex_cache.db`). The qbz-library
// crate doesn't know about Plex — passing `None` here keeps the
// local-only behavior; passing the path lets the SQL ATTACH + UNION
// light up.
let plex_path: Option<std::path::PathBuf> = if include_plex.unwrap_or(false) {
dirs::data_dir().map(|d| d.join("qbz").join("plex_cache.db"))
} else {
None
};
let guard__ = state.db.lock().await;
let db = guard__
.as_ref()
@@ -2616,6 +2632,7 @@ pub async fn v2_library_get_albums_page(
sort_dir.as_deref().unwrap_or("asc"),
include_qobuz,
exclude_network_folders.unwrap_or(false),
plex_path.as_deref(),
)
.map_err(|e| e.to_string())
}