albums: one page for every record, and streamed history rows that actually open

Three things were wrong with opening an album, and only one of them was the
one that got reported.

The reported half: clicking a local record in the sidebar's history went to
the old in-library album view. Local albums carry no catalog identity of any
kind -- `albums.mbid` is null and is not even in the DTO -- so there was
nothing to route with. openAlbumByName already existed for exactly this
(the now-playing links use it, and it requires BOTH artist and title to match
normalized, which is the namesake guard); it now reports whether it resolved
and caches settled answers, so a second click is instant.

The unreported half, and the reason "half of them are not working": every
STREAMED row was dead. catalog_album_id is TEXT in SQLite, so it arrives as a
string, and `catalog_album` takes a u64 -- the invoke was rejected outright
with "invalid type: string ... expected u64". Terrestrials, OK Human and 9 all
failed silently on click. The id is coerced in checkedAlbumPayload now, where
both other string sources (page seeds) pass through too.

And the part that made the first half unfixable: a record no catalog carries
had nowhere to go. Panda Bear's "A ? of WHEN" is not on Deezer, so no amount
of resolving would ever open it. Local albums now render on the SAME page,
composed from the library's own data -- its tracks, durations, bitrates and
the cover already on disk -- under a namespaced `lib:` id that can never
collide with a Deezer one. Playback stays local: Play and any row queue the
files you already have rather than sending a record you own back through the
resolver. Save is not offered (it is already saved), previews are absent
(there is nothing to preview), and the attribution footer credits your
library rather than claiming Deezer + MusicBrainz.

With that, the old view has no callers and is deleted: the album-page block,
its CSS, and the members that only it used. Nothing anywhere routes to it.
Verified live: all six history rows open the new page, a Home card opens it,
Play queues 14 local FLACs without streaming, and the back arrow returns.
This commit is contained in:
KeelrLeek committed 2026-08-01 07:43:50 -05:00
1 parent 3c5bccf1b6
commit dde8c7510b
9 files changed
+252 -365

No files matched your search

+4 -3
View File
@@ -1,5 +1,5 @@
<script>
import { getArt, ui } from "../lib/state.svelte.js";
import { getArt, openLibraryAlbum } from "../lib/state.svelte.js";
import Icon from "./Icon.svelte";
let { album, onselect = null } = $props();
@@ -15,8 +15,9 @@
onselect(album);
return;
}
ui.view = "library";
ui.albumId = album.id;
// One album page for everything — the old in-library album view is
// gone, so a local record opens the same page a catalog record does.
void openLibraryAlbum(album.id);
}
</script>
+6 -2
View File
@@ -125,8 +125,12 @@
<style>
.glassbar {
--bar-h: 46px;
/* Sticky rather than absolute, so the bar pins to the top of the pane
/* --bar-h is INHERITED from the scroll pane (App.svelte), not declared
here: pages need the same number to decide whether to clear the bar
or run their artwork under it, and two copies would drift. Safe
because this component is only ever mounted inside that pane — see
the note at the top of the file.
Sticky rather than absolute, so the bar pins to the top of the pane
and stays there as the page scrolls, and `top` pins it flush against
the very top of the pane over that padding.
+14 -2
View File
@@ -12,6 +12,8 @@
toggleSidebar,
goToView,
openCatalogAlbum,
openAlbumByName,
openLibraryAlbum,
getArt,
} from "../lib/state.svelte.js";
import * as api from "../lib/api.js";
@@ -137,7 +139,7 @@
return !!a.art_path && a.art_path === playingArt;
}
function openRecent(a) {
async function openRecent(a) {
if (a.kind === "catalog" && a.catalog_album_id) {
// The catalog page, not the old library album view — streamed
// records live in the catalog. The seed paints title/artist/cover
@@ -152,7 +154,17 @@
});
return;
}
goToView("library", { albumId: a.album_id });
// A library row has no catalog id to open with — local albums carry no
// catalog identity at all (`albums.mbid` is null and never surfaced) —
// so the record is resolved by artist + title, where BOTH must match
// normalized or nothing opens. That namesake guard lives in
// openAlbumByName and is the reason this can't just take a first hit.
//
// Either way it is the SAME album page: a record no catalog has (Panda
// Bear's "A ? of WHEN", verified live) opens from the library's own
// data instead. There is no second album view to fall back to.
if (await openAlbumByName(a.artist, a.title)) return;
void openLibraryAlbum(a.album_id);
}
$effect(() => {