Files
Lyra/web/src/app/api/library/route.ts
T
Jonathan dcf0bb0696 feat: show real on-disk tracklist in the Library album modal (#9)
The Library modal showed MusicBrainz's canonical tracklist, hiding incomplete
downloads / edition or track-order mismatches, and showed nothing for albums
without an rgMbid. Now it shows the REAL files on disk.

- LibraryItem gains trackNames String[] (migration add_library_track_names):
  the album's on-disk "## Title.ext" audio filenames.
- Captured at import (pipeline._import lists the final album folder) and at scan
  (scan._record_owned now also refreshes trackNames on re-scan via ON CONFLICT
  DO UPDATE + xmax=0 to preserve the newly-imported flag). New shared
  library.list_audio_files() helper.
- /api/library exposes trackNames; the AlbumModal prefers on-disk tracks when
  present (parsed "## Title" → position/title, zero network, labeled "On disk ·
  N tracks"), falling back to the MusicBrainz listing otherwise. Items imported
  before this column show the MB fallback until re-scanned.

worker 221 tests / 7-skip, web 153, tsc + build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 12:55:54 +02:00

33 lines
1.2 KiB
TypeScript

import { prisma } from "@/lib/db";
// Owned albums (LibraryItem) enriched with the release-group MBID + year from the matching
// MonitoredRelease (joined on artist/album), so the Library grid can show cover art + tracks.
export async function GET() {
const [items, releases] = await Promise.all([
prisma.libraryItem.findMany({ orderBy: { importedAt: "desc" } }),
prisma.monitoredRelease.findMany({
select: { artistName: true, album: true, rgMbid: true, firstReleaseDate: true, primaryType: true, artistMbid: true },
}),
]);
const key = (artist: string, album: string) => `${artist}${album}`;
const byKey = new Map(releases.map((r) => [key(r.artistName, r.album), r]));
return Response.json({
albums: items.map((li) => {
const mr = byKey.get(key(li.artist, li.album));
return {
id: li.id,
artist: li.artist,
album: li.album,
format: li.format,
qualityClass: li.qualityClass,
importedAt: li.importedAt,
rgMbid: mr?.rgMbid ?? null,
year: mr?.firstReleaseDate?.slice(0, 4) ?? null,
primaryType: mr?.primaryType ?? null,
artistMbid: mr?.artistMbid ?? null,
trackNames: li.trackNames,
};
}),
});
}