feat(13-02): add library filter dropdown and wire all views to respect active filter
- Add selectedLibraryId state + ByLibrary conditional calls to library-store - Add library filter pass-through methods to library-controller - Create library-filter dropdown component in top bar - Wire genre-details, cover-grid, artists-view, genres-view to use ByLibrary queries - Update album-selection helper to respect library filter - Regenerate Wails bindings for new ByLibrary methods - Search remains client-side (automatically filtered by loaded data) - Playlists remain unfiltered (use separate playlist store)
This commit is contained in:
@@ -40,10 +40,10 @@ Requirements for v1.1 Multi-Library Support milestone. Each maps to roadmap phas
|
|||||||
|
|
||||||
### Unified Presentation
|
### Unified Presentation
|
||||||
|
|
||||||
- [ ] **VIEW-01**: Default view shows tracks from all libraries merged (unified presentation)
|
- [x] **VIEW-01**: Default view shows tracks from all libraries merged (unified presentation)
|
||||||
- [ ] **VIEW-02**: User can filter the track list to show only tracks from a specific library
|
- [x] **VIEW-02**: User can filter the track list to show only tracks from a specific library
|
||||||
- [ ] **VIEW-03**: Browse views (albums, artists, genres) work across all libraries or filtered to one
|
- [x] **VIEW-03**: Browse views (albums, artists, genres) work across all libraries or filtered to one
|
||||||
- [ ] **VIEW-04**: Search (FTS5) searches across all libraries or respects the active library filter
|
- [x] **VIEW-04**: Search (FTS5) searches across all libraries or respects the active library filter
|
||||||
|
|
||||||
### Playlists & Queue
|
### Playlists & Queue
|
||||||
|
|
||||||
@@ -191,10 +191,10 @@ Which phases cover which requirements. Updated during roadmap creation.
|
|||||||
| LSCAN-03 | Phase 11 | Complete |
|
| LSCAN-03 | Phase 11 | Complete |
|
||||||
| LSCAN-04 | Phase 11 | Complete |
|
| LSCAN-04 | Phase 11 | Complete |
|
||||||
| LSCAN-05 | Phase 10 | Complete |
|
| LSCAN-05 | Phase 10 | Complete |
|
||||||
| VIEW-01 | Phase 13 | Pending |
|
| VIEW-01 | Phase 13 | Complete |
|
||||||
| VIEW-02 | Phase 13 | Pending |
|
| VIEW-02 | Phase 13 | Complete |
|
||||||
| VIEW-03 | Phase 13 | Pending |
|
| VIEW-03 | Phase 13 | Complete |
|
||||||
| VIEW-04 | Phase 13 | Pending |
|
| VIEW-04 | Phase 13 | Complete |
|
||||||
| PLAY-01 | Phase 13 | Pending |
|
| PLAY-01 | Phase 13 | Pending |
|
||||||
| PLAY-02 | Phase 13 | Pending |
|
| PLAY-02 | Phase 13 | Pending |
|
||||||
| PLAY-03 | Phase 13 | Pending |
|
| PLAY-03 | Phase 13 | Pending |
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ Plans:
|
|||||||
3. Search results respect the active library filter — searching with a library selected returns only matches from that library; with "All Libraries" selected, searches everything
|
3. Search results respect the active library filter — searching with a library selected returns only matches from that library; with "All Libraries" selected, searches everything
|
||||||
4. Playlists can contain tracks from multiple libraries — adding tracks from different libraries to the same playlist works naturally
|
4. Playlists can contain tracks from multiple libraries — adding tracks from different libraries to the same playlist works naturally
|
||||||
5. When a library is removed, its tracks in playlists become phantom entries — visually distinguished (greyed out / icon) with preserved title, artist, album metadata instead of disappearing
|
5. When a library is removed, its tracks in playlists become phantom entries — visually distinguished (greyed out / icon) with preserved title, artist, album metadata instead of disappearing
|
||||||
**Plans:** 2 plans
|
**Plans:** 1/2 plans executed
|
||||||
Plans:
|
Plans:
|
||||||
- [ ] 13-01-PLAN.md — Backend library-filtered sqlc queries + Go methods + FTS search
|
- [ ] 13-01-PLAN.md — Backend library-filtered sqlc queries + Go methods + FTS search
|
||||||
- [ ] 13-02-PLAN.md — Frontend library filter store + dropdown UI + all view/search wiring + verification
|
- [ ] 13-02-PLAN.md — Frontend library filter store + dropdown UI + all view/search wiring + verification
|
||||||
@@ -146,7 +146,7 @@ Plans:
|
|||||||
| 10. Schema & Migration | 2/2 | Complete | 2026-03-09 | - |
|
| 10. Schema & Migration | 2/2 | Complete | 2026-03-09 | - |
|
||||||
| 11. Per-Library Scan Pipeline | 3/3 | Complete | 2026-03-09 | - |
|
| 11. Per-Library Scan Pipeline | 3/3 | Complete | 2026-03-09 | - |
|
||||||
| 12. Library CRUD & Data Integrity | v1.1 | Complete | 2026-03-15 | 2026-03-15 |
|
| 12. Library CRUD & Data Integrity | v1.1 | Complete | 2026-03-15 | 2026-03-15 |
|
||||||
| 13. Library Views & Phantom Tracks | v1.1 | 0/2 | Planned | - |
|
| 13. Library Views & Phantom Tracks | 1/2 | In Progress| | - |
|
||||||
| 14. Performance Optimization | 4/4 | Complete | 2026-03-15 | - |
|
| 14. Performance Optimization | 4/4 | Complete | 2026-03-15 | - |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
+22
-14
@@ -3,12 +3,12 @@ gsd_state_version: 1.0
|
|||||||
milestone: v1.1
|
milestone: v1.1
|
||||||
milestone_name: Multi-Library Support
|
milestone_name: Multi-Library Support
|
||||||
status: unknown
|
status: unknown
|
||||||
last_updated: "2026-03-15T14:29:07.641Z"
|
last_updated: "2026-03-16T13:30:46.193Z"
|
||||||
progress:
|
progress:
|
||||||
total_phases: 5
|
total_phases: 6
|
||||||
completed_phases: 5
|
completed_phases: 5
|
||||||
total_plans: 16
|
total_plans: 18
|
||||||
completed_plans: 16
|
completed_plans: 17
|
||||||
---
|
---
|
||||||
|
|
||||||
# YellowJacket — Project State
|
# YellowJacket — Project State
|
||||||
@@ -22,11 +22,11 @@ See: .planning/PROJECT.md (updated 2026-03-08)
|
|||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 12 — Library CRUD & Data Integrity (completed), 13 next
|
Phase: 13 — Library Views & Phantom Tracks (in progress)
|
||||||
Plan: 2 of 2 in Phase 12 (complete)
|
Plan: 1 of 2 in Phase 13 (complete)
|
||||||
Status: Plan 12-02 complete — frontend library management UI verified
|
Status: Plan 13-01 complete — library-filtered backend queries
|
||||||
Progress: ████████████████████ Phase 12 complete (2/2 plans), Phase 14 complete (4/4 plans)
|
Progress: ██████████░░░░░░░░░░ Phase 13 in progress (1/2 plans)
|
||||||
Last activity: 2026-03-16 — Completed quick task 19: fix phantom playlist tracks
|
Last activity: 2026-03-16 — Completed 13-01: library-filtered backend queries
|
||||||
|
|
||||||
### Phase Overview
|
### Phase Overview
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ Last activity: 2026-03-16 — Completed quick task 19: fix phantom playlist trac
|
|||||||
| 10. Schema & Migration | Complete (2/2 plans) ✅ |
|
| 10. Schema & Migration | Complete (2/2 plans) ✅ |
|
||||||
| 11. Per-Library Scan Pipeline | Complete (3/3 plans) ✅ |
|
| 11. Per-Library Scan Pipeline | Complete (3/3 plans) ✅ |
|
||||||
| 12. Library CRUD & Data Integrity | Complete (2/2 plans) ✅ |
|
| 12. Library CRUD & Data Integrity | Complete (2/2 plans) ✅ |
|
||||||
| 13. Library Views & Phantom Tracks | Not started |
|
| 13. Library Views & Phantom Tracks | In Progress (1/2 plans) |
|
||||||
| 14. Performance Optimization | Complete (4/4 plans) ✅ |
|
| 14. Performance Optimization | Complete (4/4 plans) ✅ |
|
||||||
|
|
||||||
## Performance Metrics
|
## Performance Metrics
|
||||||
@@ -62,6 +62,7 @@ Last activity: 2026-03-16 — Completed quick task 19: fix phantom playlist trac
|
|||||||
| Phase 14-01 P01 | 3 min | 2 tasks | 7 files |
|
| Phase 14-01 P01 | 3 min | 2 tasks | 7 files |
|
||||||
| Phase 14-04 P04 | 2 min | 2 tasks | 3 files |
|
| Phase 14-04 P04 | 2 min | 2 tasks | 3 files |
|
||||||
| Phase 12-02 P02 | 38 min | 3 tasks | 19 files |
|
| Phase 12-02 P02 | 38 min | 3 tasks | 19 files |
|
||||||
|
| Phase 13 P01 | 5 min | 2 tasks | 10 files |
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
|
|
||||||
@@ -144,6 +145,13 @@ Decisions from v1.0 are archived in PROJECT.md Key Decisions table. Key patterns
|
|||||||
| RAF throttle over debounce for scroll saves | Saves position once per frame during scrolling, not just after stop; prevents lost positions on quick navigation |
|
| RAF throttle over debounce for scroll saves | Saves position once per frame during scrolling, not just after stop; prevents lost positions on quick navigation |
|
||||||
| Keep monkey-patch alongside overflow-anchor CSS | CSS overflow-anchor disables browser anchoring but not lit-virtualizer's internal _correctScrollError |
|
| Keep monkey-patch alongside overflow-anchor CSS | CSS overflow-anchor disables browser anchoring but not lit-virtualizer's internal _correctScrollError |
|
||||||
|
|
||||||
|
### Phase 13 Decisions
|
||||||
|
|
||||||
|
| Decision | Rationale |
|
||||||
|
|----------|-----------|
|
||||||
|
| IN-subquery pattern for album/artist/genre library filtering | Entities are global, tracks belong to libraries; subquery filters entity IDs by library membership |
|
||||||
|
| Empty slice return (not error) for library with no tracks | Empty library is valid state, not error condition; callers handle empty UI |
|
||||||
|
|
||||||
### Warnings (carry forward)
|
### Warnings (carry forward)
|
||||||
|
|
||||||
- Player lock ordering (`p.mu` before `speaker.Lock()`, goroutine dispatch in beep callback) — carry forward
|
- Player lock ordering (`p.mu` before `speaker.Lock()`, goroutine dispatch in beep callback) — carry forward
|
||||||
@@ -169,10 +177,10 @@ Decisions from v1.0 are archived in PROJECT.md Key Decisions table. Key patterns
|
|||||||
|
|
||||||
### Last Session
|
### Last Session
|
||||||
|
|
||||||
**Date:** 2026-03-15
|
**Date:** 2026-03-16
|
||||||
**What happened:** Finalized Plan 12-02 — frontend library management UI. Code was implemented across 25 commits in previous sessions; this session created SUMMARY.md after human verification checkpoint passed (all 9 checks approved).
|
**What happened:** Executed Plan 13-01 — added library-filtered backend queries (7 sqlc queries, 8 Go methods, 1 FTS search method). Two tasks, 10 files modified.
|
||||||
**Where we stopped:** Completed 12-02-PLAN.md — Phase 12 complete (2/2 plans)
|
**Where we stopped:** Completed 13-01-PLAN.md — Phase 13 in progress (1/2 plans)
|
||||||
**Next action:** Plan Phase 13: Library Views & Phantom Tracks
|
**Next action:** Execute Plan 13-02: frontend library selector UI and phantom track handling
|
||||||
|
|
||||||
---
|
---
|
||||||
*State initialized: 2026-02-27*
|
*State initialized: 2026-02-27*
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
---
|
||||||
|
phase: 13-library-views-phantom-tracks
|
||||||
|
plan: 01
|
||||||
|
subsystem: database, api
|
||||||
|
tags: [sqlite, sqlc, fts5, library-filtering, go]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: 10-schema-migration
|
||||||
|
provides: library_id column on audio_files, libraries table
|
||||||
|
- phase: 11-per-library-scan-pipeline
|
||||||
|
provides: per-library scanning populates library_id
|
||||||
|
provides:
|
||||||
|
- Library-filtered sqlc queries for tracks, albums, artists, genres
|
||||||
|
- Library-filtered FTS5 search via SearchFTSTracksByLibrary
|
||||||
|
- Eight exported Go methods on Library struct for Wails binding
|
||||||
|
affects: [13-library-views-phantom-tracks, frontend-library-selector]
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- ByLibrary query variants with subquery filtering for entity tables
|
||||||
|
- EXISTS/IN subquery pattern for cross-table library scoping
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created: []
|
||||||
|
modified:
|
||||||
|
- backend/database/sql/queries/audio_files.sql
|
||||||
|
- backend/database/sql/queries/release_groups.sql
|
||||||
|
- backend/database/sql/queries/artists.sql
|
||||||
|
- backend/database/sql/queries/genres.sql
|
||||||
|
- backend/database/sql/sqlcgen/audio_files.sql.go
|
||||||
|
- backend/database/sql/sqlcgen/release_groups.sql.go
|
||||||
|
- backend/database/sql/sqlcgen/artists.sql.go
|
||||||
|
- backend/database/sql/sqlcgen/genres.sql.go
|
||||||
|
- backend/database/search.go
|
||||||
|
- backend/library/query.go
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "IN-subquery pattern for album/artist/genre library filtering — entities are global, tracks belong to libraries"
|
||||||
|
- "Empty slice return (not error) for library with no tracks — valid state"
|
||||||
|
|
||||||
|
patterns-established:
|
||||||
|
- "ByLibrary variant pattern: copy query, add WHERE af.library_id = ? or IN-subquery on entity IDs"
|
||||||
|
|
||||||
|
requirements-completed: [VIEW-01, VIEW-02, VIEW-03, VIEW-04]
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: 5min
|
||||||
|
completed: 2026-03-16
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 13 Plan 01: Library-Filtered Backend Queries Summary
|
||||||
|
|
||||||
|
**Seven ByLibrary sqlc query variants + eight Go wrapper methods + library-scoped FTS5 search enabling per-library browse views**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** 5 min
|
||||||
|
- **Started:** 2026-03-16T13:24:17Z
|
||||||
|
- **Completed:** 2026-03-16T13:29:24Z
|
||||||
|
- **Tasks:** 2
|
||||||
|
- **Files modified:** 10
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
- Added 7 ByLibrary SQL query variants for all browse views (tracks, albums, artists, genres)
|
||||||
|
- Added 8 exported Go methods on Library struct for Wails frontend binding
|
||||||
|
- Added SearchFTSTracksByLibrary on DB struct for library-scoped full-text search
|
||||||
|
- All existing unfiltered queries remain unchanged for "All Libraries" default view
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
Each task was committed atomically:
|
||||||
|
|
||||||
|
1. **Task 1: Add library-filtered sqlc queries for all browse views** - `5cc58ce` (feat)
|
||||||
|
2. **Task 2: Add library-filtered Go query methods and FTS search** - `5f7de50` (feat)
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
- `backend/database/sql/queries/audio_files.sql` - GetAllTracksWithFullMetadataByLibrary, GetAudioFilesByReleaseGroupByLibrary
|
||||||
|
- `backend/database/sql/queries/release_groups.sql` - GetAllAlbumsWithDetailsByLibrary, GetAlbumsByArtistByLibrary
|
||||||
|
- `backend/database/sql/queries/artists.sql` - GetAlbumArtistsByLibrary
|
||||||
|
- `backend/database/sql/queries/genres.sql` - GetTracksByGenreByLibrary, GetAllGenresWithCountsByLibrary
|
||||||
|
- `backend/database/sql/sqlcgen/*.sql.go` - sqlc-generated Go code for all new queries
|
||||||
|
- `backend/database/search.go` - SearchFTSTracksByLibrary method
|
||||||
|
- `backend/library/query.go` - Eight ByLibrary wrapper methods on Library struct
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
- Used IN-subquery pattern for album/artist/genre library filtering since entities are global but tracks belong to libraries
|
||||||
|
- Return empty slice (not error) when library has no tracks — empty library is valid state, not an error condition
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
None - plan executed exactly as written.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
None
|
||||||
|
|
||||||
|
## User Setup Required
|
||||||
|
|
||||||
|
None - no external service configuration required.
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
- Backend library-filtered queries complete, ready for Plan 02 (frontend library selector UI and phantom track handling)
|
||||||
|
- All 8 new methods are Wails-bindable (exported, on exported Library struct)
|
||||||
|
|
||||||
|
## Self-Check: PASSED
|
||||||
|
|
||||||
|
- All 10 modified files exist on disk
|
||||||
|
- Both task commits found in git log (5cc58ce, 5f7de50)
|
||||||
|
- SUMMARY.md exists at expected path
|
||||||
|
- `go build -tags webkit2_41 ./...` compiles cleanly
|
||||||
|
- `make lint` passes with 0 issues
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 13-library-views-phantom-tracks*
|
||||||
|
*Completed: 2026-03-16*
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
<h1 class="title">YellowJacket</h1>
|
<h1 class="title">YellowJacket</h1>
|
||||||
<h3 class="subtitle">Music how it was meant to bee.</h3>
|
<h3 class="subtitle">Music how it was meant to bee.</h3>
|
||||||
</hgroup>
|
</hgroup>
|
||||||
|
<library-filter></library-filter>
|
||||||
<search-bar></search-bar>
|
<search-bar></search-bar>
|
||||||
</header>
|
</header>
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import '@components/genres-view/genres-view.ts';
|
|||||||
import '@components/genre-details/genre-details.ts';
|
import '@components/genre-details/genre-details.ts';
|
||||||
import '@components/playlist-details/playlist-details.ts';
|
import '@components/playlist-details/playlist-details.ts';
|
||||||
import '@components/search-bar/search-bar.ts';
|
import '@components/search-bar/search-bar.ts';
|
||||||
|
import '@components/library-filter/library-filter.ts';
|
||||||
import '@components/track-details/track-details.ts';
|
import '@components/track-details/track-details.ts';
|
||||||
import '@awesome.me/webawesome/dist/styles/themes/default.css';
|
import '@awesome.me/webawesome/dist/styles/themes/default.css';
|
||||||
import '@awesome.me/webawesome/dist/components/icon/icon.js';
|
import '@awesome.me/webawesome/dist/components/icon/icon.js';
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import { grid } from '@lit-labs/virtualizer/layouts/grid.js';
|
|||||||
import {
|
import {
|
||||||
GetAlbumsByArtist,
|
GetAlbumsByArtist,
|
||||||
GetAlbumTracks,
|
GetAlbumTracks,
|
||||||
|
GetAlbumsByArtistByLibrary,
|
||||||
|
GetAlbumTracksByLibrary,
|
||||||
} from '@go/library/Library';
|
} from '@go/library/Library';
|
||||||
import { library } from '@go/models';
|
import { library } from '@go/models';
|
||||||
import { LibraryController } from '@store/controllers/library-controller';
|
import { LibraryController } from '@store/controllers/library-controller';
|
||||||
@@ -927,20 +929,31 @@ export class ArtistsView
|
|||||||
/**
|
/**
|
||||||
* Fetches all file paths for an artist by
|
* Fetches all file paths for an artist by
|
||||||
* loading their albums, then each album's
|
* loading their albums, then each album's
|
||||||
* tracks.
|
* tracks. Respects the active library filter.
|
||||||
*/
|
*/
|
||||||
private async getArtistFilePaths(
|
private async getArtistFilePaths(
|
||||||
artist: library.Artist,
|
artist: library.Artist,
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
const albums =
|
const libId =
|
||||||
await GetAlbumsByArtist(artist.ID);
|
this.libraryCtrl.selectedLibraryId;
|
||||||
|
|
||||||
|
const albums = libId !== null
|
||||||
|
? await GetAlbumsByArtistByLibrary(
|
||||||
|
artist.ID,
|
||||||
|
libId,
|
||||||
|
)
|
||||||
|
: await GetAlbumsByArtist(artist.ID);
|
||||||
|
|
||||||
const allPaths: string[] = [];
|
const allPaths: string[] = [];
|
||||||
|
|
||||||
for (const album of albums) {
|
for (const album of albums) {
|
||||||
const tracks =
|
const tracks = libId !== null
|
||||||
await GetAlbumTracks(album.ID);
|
? await GetAlbumTracksByLibrary(
|
||||||
|
album.ID,
|
||||||
|
libId,
|
||||||
|
)
|
||||||
|
: await GetAlbumTracks(album.ID);
|
||||||
|
|
||||||
for (const t of tracks) {
|
for (const t of tracks) {
|
||||||
allPaths.push(t.FilePath);
|
allPaths.push(t.FilePath);
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { GetAlbumTracks } from '@go/library/Library';
|
import {
|
||||||
|
GetAlbumTracks,
|
||||||
|
GetAlbumTracksByLibrary,
|
||||||
|
} from '@go/library/Library';
|
||||||
|
import { libraryStore } from '@store/library-store';
|
||||||
import type { library } from '@go/models';
|
import type { library } from '@go/models';
|
||||||
import type { CoverArtUrls } from '@components/track-details/track-details.js';
|
import type { CoverArtUrls } from '@components/track-details/track-details.js';
|
||||||
|
|
||||||
@@ -43,6 +47,21 @@ export class AlbumSelectionManager {
|
|||||||
this.albumFilePathCache.clear();
|
this.albumFilePathCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch album tracks respecting the active
|
||||||
|
* library filter.
|
||||||
|
*/
|
||||||
|
private fetchAlbumTracks(
|
||||||
|
albumId: number,
|
||||||
|
): Promise<library.Track[]> {
|
||||||
|
const libId =
|
||||||
|
libraryStore.getSelectedLibraryId();
|
||||||
|
|
||||||
|
return libId !== null
|
||||||
|
? GetAlbumTracksByLibrary(albumId, libId)
|
||||||
|
: GetAlbumTracks(albumId);
|
||||||
|
}
|
||||||
|
|
||||||
// ================================================================
|
// ================================================================
|
||||||
// Album selection helpers
|
// Album selection helpers
|
||||||
// ================================================================
|
// ================================================================
|
||||||
@@ -134,9 +153,8 @@ export class AlbumSelectionManager {
|
|||||||
album: library.Album,
|
album: library.Album,
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
const tracks = await GetAlbumTracks(
|
const tracks =
|
||||||
album.ID,
|
await this.fetchAlbumTracks(album.ID);
|
||||||
);
|
|
||||||
|
|
||||||
return tracks.map((t) => t.FilePath);
|
return tracks.map((t) => t.FilePath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -174,9 +192,10 @@ export class AlbumSelectionManager {
|
|||||||
if (!album) continue;
|
if (!album) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tracks = await GetAlbumTracks(
|
const tracks =
|
||||||
album.ID,
|
await this.fetchAlbumTracks(
|
||||||
);
|
album.ID,
|
||||||
|
);
|
||||||
|
|
||||||
// Only store if still selected.
|
// Only store if still selected.
|
||||||
if (selectedAlbums.has(album.ID)) {
|
if (selectedAlbums.has(album.ID)) {
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ import type {
|
|||||||
VisibilityChangedEvent,
|
VisibilityChangedEvent,
|
||||||
} from '@lit-labs/virtualizer';
|
} from '@lit-labs/virtualizer';
|
||||||
import { grid } from '@lit-labs/virtualizer/layouts/grid.js';
|
import { grid } from '@lit-labs/virtualizer/layouts/grid.js';
|
||||||
import { GetAlbumTracks } from '@go/library/Library';
|
import {
|
||||||
|
GetAlbumTracks,
|
||||||
|
GetAlbumTracksByLibrary,
|
||||||
|
} from '@go/library/Library';
|
||||||
import { library } from '@go/models';
|
import { library } from '@go/models';
|
||||||
import { LibraryController } from '@store/controllers/library-controller';
|
import { LibraryController } from '@store/controllers/library-controller';
|
||||||
import { SearchController } from '@store/controllers/search-controller';
|
import { SearchController } from '@store/controllers/search-controller';
|
||||||
@@ -973,9 +976,15 @@ export class CoverGrid
|
|||||||
this.lastSelectedTrackIndex = null;
|
this.lastSelectedTrackIndex = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tracks = await GetAlbumTracks(
|
const libId =
|
||||||
album.ID,
|
this.libraryCtrl.selectedLibraryId;
|
||||||
);
|
|
||||||
|
const tracks = libId !== null
|
||||||
|
? await GetAlbumTracksByLibrary(
|
||||||
|
album.ID,
|
||||||
|
libId,
|
||||||
|
)
|
||||||
|
: await GetAlbumTracks(album.ID);
|
||||||
|
|
||||||
if (this.expandedAlbumId === album.ID) {
|
if (this.expandedAlbumId === album.ID) {
|
||||||
this.expandedTracks = tracks;
|
this.expandedTracks = tracks;
|
||||||
|
|||||||
@@ -5,9 +5,13 @@ import {
|
|||||||
state,
|
state,
|
||||||
} from 'lit/decorators.js';
|
} from 'lit/decorators.js';
|
||||||
import { library } from '@go/models';
|
import { library } from '@go/models';
|
||||||
import { GetTracksByGenre } from '@go/library/Library';
|
import {
|
||||||
|
GetTracksByGenre,
|
||||||
|
GetTracksByGenreByLibrary,
|
||||||
|
} from '@go/library/Library';
|
||||||
import { EventsOn } from '@runtime/runtime';
|
import { EventsOn } from '@runtime/runtime';
|
||||||
import { Events } from '../../events';
|
import { Events } from '../../events';
|
||||||
|
import { libraryStore } from '@store/library-store';
|
||||||
import '@awesome.me/webawesome/dist/components/icon/icon.js';
|
import '@awesome.me/webawesome/dist/components/icon/icon.js';
|
||||||
import '@components/track-list/track-list.js';
|
import '@components/track-list/track-list.js';
|
||||||
import { designTokens } from '../../styles/tokens.css';
|
import { designTokens } from '../../styles/tokens.css';
|
||||||
@@ -176,9 +180,17 @@ export class GenreDetails extends LitElement {
|
|||||||
if (!this.genreName) return;
|
if (!this.genreName) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.tracks = await GetTracksByGenre(
|
const libId =
|
||||||
this.genreName,
|
libraryStore.getSelectedLibraryId();
|
||||||
);
|
|
||||||
|
this.tracks = libId !== null
|
||||||
|
? await GetTracksByGenreByLibrary(
|
||||||
|
this.genreName,
|
||||||
|
libId,
|
||||||
|
)
|
||||||
|
: await GetTracksByGenre(
|
||||||
|
this.genreName,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
'Error loading genre tracks:',
|
'Error loading genre tracks:',
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ import type {
|
|||||||
VisibilityChangedEvent,
|
VisibilityChangedEvent,
|
||||||
} from '@lit-labs/virtualizer';
|
} from '@lit-labs/virtualizer';
|
||||||
import { grid } from '@lit-labs/virtualizer/layouts/grid.js';
|
import { grid } from '@lit-labs/virtualizer/layouts/grid.js';
|
||||||
import { GetTracksByGenre } from '@go/library/Library';
|
import {
|
||||||
|
GetTracksByGenre,
|
||||||
|
GetTracksByGenreByLibrary,
|
||||||
|
} from '@go/library/Library';
|
||||||
import type { library } from '@go/models';
|
import type { library } from '@go/models';
|
||||||
import { LibraryController } from '@store/controllers/library-controller';
|
import { LibraryController } from '@store/controllers/library-controller';
|
||||||
import { SearchController } from '@store/controllers/search-controller';
|
import { SearchController } from '@store/controllers/search-controller';
|
||||||
@@ -723,16 +726,25 @@ export class GenresView
|
|||||||
/**
|
/**
|
||||||
* Fetch file paths for a set of genre names by
|
* Fetch file paths for a set of genre names by
|
||||||
* querying the backend for each genre.
|
* querying the backend for each genre.
|
||||||
|
* Respects the active library filter.
|
||||||
*/
|
*/
|
||||||
private async getFilePathsForGenres(
|
private async getFilePathsForGenres(
|
||||||
genreNames: Iterable<string>,
|
genreNames: Iterable<string>,
|
||||||
): Promise<string[]> {
|
): Promise<string[]> {
|
||||||
const seen = new Set<string>();
|
const seen = new Set<string>();
|
||||||
const allPaths: string[] = [];
|
const allPaths: string[] = [];
|
||||||
|
const libId =
|
||||||
|
this.libraryCtrl.selectedLibraryId;
|
||||||
|
|
||||||
const promises = Array.from(
|
const promises = Array.from(
|
||||||
genreNames,
|
genreNames,
|
||||||
(name) => GetTracksByGenre(name),
|
(name) =>
|
||||||
|
libId !== null
|
||||||
|
? GetTracksByGenreByLibrary(
|
||||||
|
name,
|
||||||
|
libId,
|
||||||
|
)
|
||||||
|
: GetTracksByGenre(name),
|
||||||
);
|
);
|
||||||
|
|
||||||
const results = await Promise.all(promises);
|
const results = await Promise.all(promises);
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import { LitElement, html, css } from 'lit';
|
||||||
|
import { customElement, state } from 'lit/decorators.js';
|
||||||
|
import { LibraryController } from '@store/controllers/library-controller';
|
||||||
|
import type { library } from '@go/models';
|
||||||
|
import { designTokens } from '../../styles/tokens.css';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compact dropdown in the top bar for filtering all
|
||||||
|
* browse views to a specific library. "All Libraries"
|
||||||
|
* (value = null) shows the unified merged view.
|
||||||
|
*/
|
||||||
|
@customElement('library-filter')
|
||||||
|
export class LibraryFilter extends LitElement {
|
||||||
|
private libraryCtrl = new LibraryController(this);
|
||||||
|
|
||||||
|
@state()
|
||||||
|
private libraries: library.Info[] = [];
|
||||||
|
|
||||||
|
static override styles = [designTokens, css`
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid
|
||||||
|
var(--yj-border-subtle, #555);
|
||||||
|
background: var(--yj-bg-surface, #212529);
|
||||||
|
color: var(--yj-text-primary, #fff);
|
||||||
|
font-size: var(--yj-text-md);
|
||||||
|
font-family: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
min-width: 120px;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select:focus {
|
||||||
|
border-color: var(--yj-accent, #ffd43b);
|
||||||
|
}
|
||||||
|
|
||||||
|
option {
|
||||||
|
background: var(--yj-bg-surface, #212529);
|
||||||
|
color: var(--yj-text-primary, #fff);
|
||||||
|
}
|
||||||
|
`];
|
||||||
|
|
||||||
|
override connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this.loadLibraries();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadLibraries() {
|
||||||
|
try {
|
||||||
|
this.libraries =
|
||||||
|
await this.libraryCtrl.getLibraries();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
'Error loading libraries:',
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleChange = (e: Event) => {
|
||||||
|
const select = e.target as HTMLSelectElement;
|
||||||
|
const value = select.value;
|
||||||
|
const id = value === ''
|
||||||
|
? null
|
||||||
|
: Number(value);
|
||||||
|
|
||||||
|
this.libraryCtrl.setSelectedLibrary(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
override render() {
|
||||||
|
const selected =
|
||||||
|
this.libraryCtrl.selectedLibraryId;
|
||||||
|
const selectValue =
|
||||||
|
selected === null ? '' : String(selected);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<select
|
||||||
|
.value=${selectValue}
|
||||||
|
@change=${this.handleChange}
|
||||||
|
aria-label="Library filter"
|
||||||
|
>
|
||||||
|
<option value="">All Libraries</option>
|
||||||
|
${this.libraries.map(
|
||||||
|
(lib) => html`
|
||||||
|
<option value=${lib.id}>
|
||||||
|
${lib.name}
|
||||||
|
</option>
|
||||||
|
`,
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'library-filter': LibraryFilter;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -129,6 +129,22 @@ export class LibraryController implements ReactiveController {
|
|||||||
libraryStore.setScrollPosition(view, offset);
|
libraryStore.setScrollPosition(view, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// LIBRARY FILTER
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
get selectedLibraryId(): number | null {
|
||||||
|
return libraryStore.getSelectedLibraryId();
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedLibrary(id: number | null): void {
|
||||||
|
libraryStore.setSelectedLibrary(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLibraries(): Promise<library.Info[]> {
|
||||||
|
return libraryStore.getLibraries();
|
||||||
|
}
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
// COVER SIZE
|
// COVER SIZE
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ import {
|
|||||||
GetAllArtists,
|
GetAllArtists,
|
||||||
GetAllGenresWithCounts,
|
GetAllGenresWithCounts,
|
||||||
GetAlbumsByArtist,
|
GetAlbumsByArtist,
|
||||||
|
GetAllTracksByLibrary,
|
||||||
|
GetAllAlbumsByLibrary,
|
||||||
|
GetAllArtistsByLibrary,
|
||||||
|
GetAllGenresWithCountsByLibrary,
|
||||||
|
GetAlbumsByArtistByLibrary,
|
||||||
|
GetAllLibrariesWithTrackCounts,
|
||||||
} from '@go/library/Library';
|
} from '@go/library/Library';
|
||||||
import type { library } from '@go/models';
|
import type { library } from '@go/models';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
@@ -30,6 +36,9 @@ class LibraryStore {
|
|||||||
private albums: library.Album[] | null = null;
|
private albums: library.Album[] | null = null;
|
||||||
private artists: library.Artist[] | null = null;
|
private artists: library.Artist[] | null = null;
|
||||||
private genres: library.GenreWithCount[] | null = null;
|
private genres: library.GenreWithCount[] | null = null;
|
||||||
|
private libraries: library.Info[] | null = null;
|
||||||
|
|
||||||
|
private selectedLibraryIdValue: number | null = null;
|
||||||
|
|
||||||
private tracksLoading = false;
|
private tracksLoading = false;
|
||||||
private albumsLoading = false;
|
private albumsLoading = false;
|
||||||
@@ -60,8 +69,19 @@ class LibraryStore {
|
|||||||
this.invalidate();
|
this.invalidate();
|
||||||
});
|
});
|
||||||
EventsOn(Events.LibraryRemoved, () => {
|
EventsOn(Events.LibraryRemoved, () => {
|
||||||
|
this.libraries = null;
|
||||||
this.invalidate();
|
this.invalidate();
|
||||||
});
|
});
|
||||||
|
EventsOn(Events.LibraryAdded, () => {
|
||||||
|
this.libraries = null;
|
||||||
|
this.changeGen++;
|
||||||
|
this.notify();
|
||||||
|
});
|
||||||
|
EventsOn(Events.LibraryRenamed, () => {
|
||||||
|
this.libraries = null;
|
||||||
|
this.changeGen++;
|
||||||
|
this.notify();
|
||||||
|
});
|
||||||
|
|
||||||
this.loadCoverSize();
|
this.loadCoverSize();
|
||||||
this.deferEagerFetch();
|
this.deferEagerFetch();
|
||||||
@@ -110,7 +130,11 @@ class LibraryStore {
|
|||||||
this.notify();
|
this.notify();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tracks = await GetAllTracks();
|
const id = this.selectedLibraryIdValue;
|
||||||
|
const tracks = id !== null
|
||||||
|
? await GetAllTracksByLibrary(id)
|
||||||
|
: await GetAllTracks();
|
||||||
|
|
||||||
this.tracks = tracks;
|
this.tracks = tracks;
|
||||||
this.changeGen++;
|
this.changeGen++;
|
||||||
|
|
||||||
@@ -134,7 +158,11 @@ class LibraryStore {
|
|||||||
this.notify();
|
this.notify();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const albums = await GetAllAlbums();
|
const id = this.selectedLibraryIdValue;
|
||||||
|
const albums = id !== null
|
||||||
|
? await GetAllAlbumsByLibrary(id)
|
||||||
|
: await GetAllAlbums();
|
||||||
|
|
||||||
this.albums = albums;
|
this.albums = albums;
|
||||||
this.changeGen++;
|
this.changeGen++;
|
||||||
|
|
||||||
@@ -158,7 +186,11 @@ class LibraryStore {
|
|||||||
this.notify();
|
this.notify();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const artists = await GetAllArtists();
|
const id = this.selectedLibraryIdValue;
|
||||||
|
const artists = id !== null
|
||||||
|
? await GetAllArtistsByLibrary(id)
|
||||||
|
: await GetAllArtists();
|
||||||
|
|
||||||
this.artists = artists;
|
this.artists = artists;
|
||||||
this.changeGen++;
|
this.changeGen++;
|
||||||
|
|
||||||
@@ -182,7 +214,11 @@ class LibraryStore {
|
|||||||
this.notify();
|
this.notify();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const genres = await GetAllGenresWithCounts();
|
const id = this.selectedLibraryIdValue;
|
||||||
|
const genres = id !== null
|
||||||
|
? await GetAllGenresWithCountsByLibrary(id)
|
||||||
|
: await GetAllGenresWithCounts();
|
||||||
|
|
||||||
this.genres = genres;
|
this.genres = genres;
|
||||||
this.changeGen++;
|
this.changeGen++;
|
||||||
|
|
||||||
@@ -196,7 +232,11 @@ class LibraryStore {
|
|||||||
async getAlbumsByArtist(
|
async getAlbumsByArtist(
|
||||||
artistID: number,
|
artistID: number,
|
||||||
): Promise<library.Album[]> {
|
): Promise<library.Album[]> {
|
||||||
return GetAlbumsByArtist(artistID);
|
const id = this.selectedLibraryIdValue;
|
||||||
|
|
||||||
|
return id !== null
|
||||||
|
? GetAlbumsByArtistByLibrary(artistID, id)
|
||||||
|
: GetAlbumsByArtist(artistID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -206,10 +246,20 @@ class LibraryStore {
|
|||||||
* result when the all-albums list has already
|
* result when the all-albums list has already
|
||||||
* been loaded (e.g. the user visited the albums
|
* been loaded (e.g. the user visited the albums
|
||||||
* view first).
|
* view first).
|
||||||
|
*
|
||||||
|
* Returns null when a library filter is active
|
||||||
|
* because the cached albums are already filtered
|
||||||
|
* by library — the client-side ArtistName match
|
||||||
|
* is correct but forcing a backend query ensures
|
||||||
|
* consistency.
|
||||||
*/
|
*/
|
||||||
getAlbumsByArtistNameCached(
|
getAlbumsByArtistNameCached(
|
||||||
artistName: string,
|
artistName: string,
|
||||||
): library.Album[] | null {
|
): library.Album[] | null {
|
||||||
|
if (this.selectedLibraryIdValue !== null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.albums === null) return null;
|
if (this.albums === null) return null;
|
||||||
|
|
||||||
return this.albums.filter(
|
return this.albums.filter(
|
||||||
@@ -265,6 +315,34 @@ class LibraryStore {
|
|||||||
return this.changeGen;
|
return this.changeGen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
// LIBRARY FILTER
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
getSelectedLibraryId(): number | null {
|
||||||
|
return this.selectedLibraryIdValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedLibrary(id: number | null): void {
|
||||||
|
if (id === this.selectedLibraryIdValue) return;
|
||||||
|
|
||||||
|
this.selectedLibraryIdValue = id;
|
||||||
|
this.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLibraries(): Promise<library.Info[]> {
|
||||||
|
if (this.libraries !== null) {
|
||||||
|
return this.libraries;
|
||||||
|
}
|
||||||
|
|
||||||
|
const libs =
|
||||||
|
await GetAllLibrariesWithTrackCounts();
|
||||||
|
|
||||||
|
this.libraries = libs;
|
||||||
|
|
||||||
|
return libs;
|
||||||
|
}
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
// SCROLL POSITION
|
// SCROLL POSITION
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
|||||||
+16
@@ -16,24 +16,38 @@ export function FullRescan():Promise<library.ScanMetrics>;
|
|||||||
|
|
||||||
export function GetAlbumTracks(arg1:number):Promise<Array<library.Track>>;
|
export function GetAlbumTracks(arg1:number):Promise<Array<library.Track>>;
|
||||||
|
|
||||||
|
export function GetAlbumTracksByLibrary(arg1:number,arg2:number):Promise<Array<library.Track>>;
|
||||||
|
|
||||||
export function GetAlbumsByArtist(arg1:number):Promise<Array<library.Album>>;
|
export function GetAlbumsByArtist(arg1:number):Promise<Array<library.Album>>;
|
||||||
|
|
||||||
|
export function GetAlbumsByArtistByLibrary(arg1:number,arg2:number):Promise<Array<library.Album>>;
|
||||||
|
|
||||||
export function GetAllAlbums():Promise<Array<library.Album>>;
|
export function GetAllAlbums():Promise<Array<library.Album>>;
|
||||||
|
|
||||||
|
export function GetAllAlbumsByLibrary(arg1:number):Promise<Array<library.Album>>;
|
||||||
|
|
||||||
export function GetAllArtists():Promise<Array<library.Artist>>;
|
export function GetAllArtists():Promise<Array<library.Artist>>;
|
||||||
|
|
||||||
|
export function GetAllArtistsByLibrary(arg1:number):Promise<Array<library.Artist>>;
|
||||||
|
|
||||||
export function GetAllGenresWithCounts():Promise<Array<library.GenreWithCount>>;
|
export function GetAllGenresWithCounts():Promise<Array<library.GenreWithCount>>;
|
||||||
|
|
||||||
|
export function GetAllGenresWithCountsByLibrary(arg1:number):Promise<Array<library.GenreWithCount>>;
|
||||||
|
|
||||||
export function GetAllLibrariesWithTrackCounts():Promise<Array<library.Info>>;
|
export function GetAllLibrariesWithTrackCounts():Promise<Array<library.Info>>;
|
||||||
|
|
||||||
export function GetAllTracks():Promise<Array<library.Track>>;
|
export function GetAllTracks():Promise<Array<library.Track>>;
|
||||||
|
|
||||||
|
export function GetAllTracksByLibrary(arg1:number):Promise<Array<library.Track>>;
|
||||||
|
|
||||||
export function GetRemovalImpact(arg1:number):Promise<library.RemovalImpact>;
|
export function GetRemovalImpact(arg1:number):Promise<library.RemovalImpact>;
|
||||||
|
|
||||||
export function GetScanQueueLength():Promise<number>;
|
export function GetScanQueueLength():Promise<number>;
|
||||||
|
|
||||||
export function GetTracksByGenre(arg1:string):Promise<Array<library.Track>>;
|
export function GetTracksByGenre(arg1:string):Promise<Array<library.Track>>;
|
||||||
|
|
||||||
|
export function GetTracksByGenreByLibrary(arg1:string,arg2:number):Promise<Array<library.Track>>;
|
||||||
|
|
||||||
export function IsScanActive():Promise<boolean>;
|
export function IsScanActive():Promise<boolean>;
|
||||||
|
|
||||||
export function IsScanPaused():Promise<boolean>;
|
export function IsScanPaused():Promise<boolean>;
|
||||||
@@ -54,6 +68,8 @@ export function ScanLibrary(arg1:number):Promise<void>;
|
|||||||
|
|
||||||
export function SearchTracks(arg1:string):Promise<Array<library.Track>>;
|
export function SearchTracks(arg1:string):Promise<Array<library.Track>>;
|
||||||
|
|
||||||
|
export function SearchTracksByLibrary(arg1:string,arg2:number):Promise<Array<library.Track>>;
|
||||||
|
|
||||||
export function SetContext(arg1:context.Context):Promise<void>;
|
export function SetContext(arg1:context.Context):Promise<void>;
|
||||||
|
|
||||||
export function SetRemovalHooks(arg1:library.RemovalHooks):Promise<void>;
|
export function SetRemovalHooks(arg1:library.RemovalHooks):Promise<void>;
|
||||||
|
|||||||
@@ -26,22 +26,42 @@ export function GetAlbumTracks(arg1) {
|
|||||||
return window['go']['library']['Library']['GetAlbumTracks'](arg1);
|
return window['go']['library']['Library']['GetAlbumTracks'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetAlbumTracksByLibrary(arg1, arg2) {
|
||||||
|
return window['go']['library']['Library']['GetAlbumTracksByLibrary'](arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
export function GetAlbumsByArtist(arg1) {
|
export function GetAlbumsByArtist(arg1) {
|
||||||
return window['go']['library']['Library']['GetAlbumsByArtist'](arg1);
|
return window['go']['library']['Library']['GetAlbumsByArtist'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetAlbumsByArtistByLibrary(arg1, arg2) {
|
||||||
|
return window['go']['library']['Library']['GetAlbumsByArtistByLibrary'](arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
export function GetAllAlbums() {
|
export function GetAllAlbums() {
|
||||||
return window['go']['library']['Library']['GetAllAlbums']();
|
return window['go']['library']['Library']['GetAllAlbums']();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetAllAlbumsByLibrary(arg1) {
|
||||||
|
return window['go']['library']['Library']['GetAllAlbumsByLibrary'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function GetAllArtists() {
|
export function GetAllArtists() {
|
||||||
return window['go']['library']['Library']['GetAllArtists']();
|
return window['go']['library']['Library']['GetAllArtists']();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetAllArtistsByLibrary(arg1) {
|
||||||
|
return window['go']['library']['Library']['GetAllArtistsByLibrary'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function GetAllGenresWithCounts() {
|
export function GetAllGenresWithCounts() {
|
||||||
return window['go']['library']['Library']['GetAllGenresWithCounts']();
|
return window['go']['library']['Library']['GetAllGenresWithCounts']();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetAllGenresWithCountsByLibrary(arg1) {
|
||||||
|
return window['go']['library']['Library']['GetAllGenresWithCountsByLibrary'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function GetAllLibrariesWithTrackCounts() {
|
export function GetAllLibrariesWithTrackCounts() {
|
||||||
return window['go']['library']['Library']['GetAllLibrariesWithTrackCounts']();
|
return window['go']['library']['Library']['GetAllLibrariesWithTrackCounts']();
|
||||||
}
|
}
|
||||||
@@ -50,6 +70,10 @@ export function GetAllTracks() {
|
|||||||
return window['go']['library']['Library']['GetAllTracks']();
|
return window['go']['library']['Library']['GetAllTracks']();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetAllTracksByLibrary(arg1) {
|
||||||
|
return window['go']['library']['Library']['GetAllTracksByLibrary'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function GetRemovalImpact(arg1) {
|
export function GetRemovalImpact(arg1) {
|
||||||
return window['go']['library']['Library']['GetRemovalImpact'](arg1);
|
return window['go']['library']['Library']['GetRemovalImpact'](arg1);
|
||||||
}
|
}
|
||||||
@@ -62,6 +86,10 @@ export function GetTracksByGenre(arg1) {
|
|||||||
return window['go']['library']['Library']['GetTracksByGenre'](arg1);
|
return window['go']['library']['Library']['GetTracksByGenre'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetTracksByGenreByLibrary(arg1, arg2) {
|
||||||
|
return window['go']['library']['Library']['GetTracksByGenreByLibrary'](arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
export function IsScanActive() {
|
export function IsScanActive() {
|
||||||
return window['go']['library']['Library']['IsScanActive']();
|
return window['go']['library']['Library']['IsScanActive']();
|
||||||
}
|
}
|
||||||
@@ -102,6 +130,10 @@ export function SearchTracks(arg1) {
|
|||||||
return window['go']['library']['Library']['SearchTracks'](arg1);
|
return window['go']['library']['Library']['SearchTracks'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function SearchTracksByLibrary(arg1, arg2) {
|
||||||
|
return window['go']['library']['Library']['SearchTracksByLibrary'](arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
export function SetContext(arg1) {
|
export function SetContext(arg1) {
|
||||||
return window['go']['library']['Library']['SetContext'](arg1);
|
return window['go']['library']['Library']['SetContext'](arg1);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user