mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
refactor(lib): relocate the local-library subsystem utils/library -> lib/library
utils/library is the local-index query/browse/sync engine -- feature-free shared infra consumed by the album, search, genre, artist and offline features. With its last feature edges removed (queue resolver, shuffleArray, albumBrowseCatalogChunk, genreBrowsePlayback all relocated earlier this branch), the whole directory is pure infra and belongs under lib/. Whole-directory move (58 files, 76 consumers rewritten) via resolver-based rewrite: cross-cluster relatives absolutized to @/, intra-cluster siblings kept relative. utils/library is gone; lib/library imports zero @/features. Three stale doc comments in the album/search/artist barrels repointed. tsc 0, lint 0, full suite 2353/2353, production build OK.
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@ import { runLegacyOfflineFileMigration } from '@/features/offline';
|
|||||||
import { reconcileLibraryTierForServer } from '@/features/offline';
|
import { reconcileLibraryTierForServer } from '@/features/offline';
|
||||||
import { initMiniPlayerBridgeOnMain } from '@/features/miniPlayer';
|
import { initMiniPlayerBridgeOnMain } from '@/features/miniPlayer';
|
||||||
import { runAdvancedModeMigration } from '../utils/migrations/advancedModeMigration';
|
import { runAdvancedModeMigration } from '../utils/migrations/advancedModeMigration';
|
||||||
import { bootstrapAllIndexedServers } from '../utils/library/librarySession';
|
import { bootstrapAllIndexedServers } from '@/lib/library/librarySession';
|
||||||
import { hydrateQueueFromIndex } from '@/features/playback/store/queueRestore';
|
import { hydrateQueueFromIndex } from '@/features/playback/store/queueRestore';
|
||||||
import { useLibraryAnalysisBackfill } from '../hooks/useLibraryAnalysisBackfill';
|
import { useLibraryAnalysisBackfill } from '../hooks/useLibraryAnalysisBackfill';
|
||||||
import { useCoverArtPrefetch } from '../cover/useCoverArtPrefetch';
|
import { useCoverArtPrefetch } from '../cover/useCoverArtPrefetch';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getGenres } from '@/lib/api/subsonicGenres';
|
import { getGenres } from '@/lib/api/subsonicGenres';
|
||||||
import type { GenreFilterOption } from '../utils/library/albumBrowseLoad';
|
import type { GenreFilterOption } from '@/lib/library/albumBrowseLoad';
|
||||||
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { Check, Filter, X } from 'lucide-react';
|
import { Check, Filter, X } from 'lucide-react';
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ import { copyTextToClipboard } from '../utils/server/serverMagicString';
|
|||||||
import { showToast } from '../utils/ui/toast';
|
import { showToast } from '../utils/ui/toast';
|
||||||
import { formatTrackTime } from '@/lib/format/formatDuration';
|
import { formatTrackTime } from '@/lib/format/formatDuration';
|
||||||
import { formatLastSeen } from '../utils/componentHelpers/userMgmtHelpers';
|
import { formatLastSeen } from '../utils/componentHelpers/userMgmtHelpers';
|
||||||
import { libraryIsReady } from '../utils/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
import {
|
import {
|
||||||
formatQueueMoodLabels,
|
formatQueueMoodLabels,
|
||||||
parseTrackEnrichmentFacts,
|
parseTrackEnrichmentFacts,
|
||||||
resolveQueueBpm,
|
resolveQueueBpm,
|
||||||
type ParsedTrackEnrichment,
|
type ParsedTrackEnrichment,
|
||||||
} from '../utils/library/trackEnrichment';
|
} from '@/lib/library/trackEnrichment';
|
||||||
import i18n from '@/lib/i18n';
|
import i18n from '@/lib/i18n';
|
||||||
|
|
||||||
function formatSize(bytes?: number): string | null {
|
function formatSize(bytes?: number): string | null {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
normalizeAlbumYearToFieldChange,
|
normalizeAlbumYearToFieldChange,
|
||||||
resolveAlbumYearBounds,
|
resolveAlbumYearBounds,
|
||||||
stepAlbumYearField,
|
stepAlbumYearField,
|
||||||
} from '../utils/library/albumYearFilter';
|
} from '@/lib/library/albumYearFilter';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
from: string;
|
from: string;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { CoverCacheStrategy } from '../utils/library/coverStrategy';
|
import type { CoverCacheStrategy } from '@/lib/library/coverStrategy';
|
||||||
import {
|
import {
|
||||||
coverStrategyAllowsLibraryBackfill,
|
coverStrategyAllowsLibraryBackfill,
|
||||||
coverStrategyAllowsRoutePrefetch,
|
coverStrategyAllowsRoutePrefetch,
|
||||||
} from '../utils/library/coverStrategy';
|
} from '@/lib/library/coverStrategy';
|
||||||
|
|
||||||
/** @deprecated Use `coverStrategyAllowsRoutePrefetch` */
|
/** @deprecated Use `coverStrategyAllowsRoutePrefetch` */
|
||||||
export function coverPrefetchStrategyAllowsRoutePrefetch(
|
export function coverPrefetchStrategyAllowsRoutePrefetch(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { coverCacheStats } from '../api/coverCache';
|
import { coverCacheStats } from '../api/coverCache';
|
||||||
import { coverStrategyAllowsRoutePrefetch } from '../utils/library/coverStrategy';
|
import { coverStrategyAllowsRoutePrefetch } from '@/lib/library/coverStrategy';
|
||||||
import { useCoverStrategyStore } from '../store/coverStrategyStore';
|
import { useCoverStrategyStore } from '../store/coverStrategyStore';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { coverPrefetchDrainBatch } from './prefetchRegistry';
|
import { coverPrefetchDrainBatch } from './prefetchRegistry';
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import { sanitizeHtml } from '@/lib/util/sanitizeHtml';
|
|||||||
import { OpenArtistRefInline } from '@/features/artist';
|
import { OpenArtistRefInline } from '@/features/artist';
|
||||||
import { tooltipAttrs } from '@/ui/tooltipAttrs';
|
import { tooltipAttrs } from '@/ui/tooltipAttrs';
|
||||||
import { offlineActionPolicy, type OfflineActionPolicy } from '@/features/offline';
|
import { offlineActionPolicy, type OfflineActionPolicy } from '@/features/offline';
|
||||||
import { deriveAlbumGenreTags } from '@/utils/library/genreTags';
|
import { deriveAlbumGenreTags } from '@/lib/library/genreTags';
|
||||||
import { genreColor } from '@/utils/library/genreColor';
|
import { genreColor } from '@/lib/library/genreColor';
|
||||||
|
|
||||||
/** True when the album artist label means "no single artist" — `getArtistInfo`
|
/** True when the album artist label means "no single artist" — `getArtistInfo`
|
||||||
* has nothing meaningful to return for these, so the Artist Bio entry is hidden.
|
* has nothing meaningful to return for these, so the Artist Bio entry is hidden.
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { ndListLosslessAlbumsPage } from '@/lib/api/navidromeBrowse';
|
|||||||
import AlbumRow from '@/features/album/components/AlbumRow';
|
import AlbumRow from '@/features/album/components/AlbumRow';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
import { runLocalLosslessAlbums } from '@/utils/library/browseTextSearch';
|
import { runLocalLosslessAlbums } from '@/lib/library/browseTextSearch';
|
||||||
import { LOSSLESS_MODE_QUERY } from '@/utils/library/losslessMode';
|
import { LOSSLESS_MODE_QUERY } from '@/lib/library/losslessMode';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
disableArtwork?: boolean;
|
disableArtwork?: boolean;
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import {
|
|||||||
} from '@/cover/coverTraffic';
|
} from '@/cover/coverTraffic';
|
||||||
import { coverEnsureQueueBacklog, coverEnsureResumePump, coverEnsureSubscribeBacklogDrain } from '@/cover/ensureQueue';
|
import { coverEnsureQueueBacklog, coverEnsureResumePump, coverEnsureSubscribeBacklogDrain } from '@/cover/ensureQueue';
|
||||||
import { dedupeById } from '@/lib/util/dedupeById';
|
import { dedupeById } from '@/lib/util/dedupeById';
|
||||||
import { albumBrowseCompScanComplete, albumBrowseCompFilterClientOnly } from '@/utils/library/albumCompilation';
|
import { albumBrowseCompScanComplete, albumBrowseCompFilterClientOnly } from '@/lib/library/albumCompilation';
|
||||||
import type { AlbumCompFilter } from '@/utils/library/albumCompilation';
|
import type { AlbumCompFilter } from '@/lib/library/albumCompilation';
|
||||||
import {
|
import {
|
||||||
albumBrowseHasGenreFilter,
|
albumBrowseHasGenreFilter,
|
||||||
albumBrowseHasServerFilters,
|
albumBrowseHasServerFilters,
|
||||||
@@ -24,12 +24,12 @@ import {
|
|||||||
fetchLocalAlbumCatalogChunk,
|
fetchLocalAlbumCatalogChunk,
|
||||||
type AlbumBrowseQuery,
|
type AlbumBrowseQuery,
|
||||||
type GenreFilterOption,
|
type GenreFilterOption,
|
||||||
} from '@/utils/library/albumBrowseLoad';
|
} from '@/lib/library/albumBrowseLoad';
|
||||||
import { libraryScopeForServer } from '@/lib/api/subsonicClient';
|
import { libraryScopeForServer } from '@/lib/api/subsonicClient';
|
||||||
import {
|
import {
|
||||||
ALBUM_YEAR_FILTER_DEBOUNCE_MS,
|
ALBUM_YEAR_FILTER_DEBOUNCE_MS,
|
||||||
resolveAlbumYearBounds,
|
resolveAlbumYearBounds,
|
||||||
} from '@/utils/library/albumYearFilter';
|
} from '@/lib/library/albumYearFilter';
|
||||||
import { loadOfflineAlbumBrowseInitial } from '@/features/offline';
|
import { loadOfflineAlbumBrowseInitial } from '@/features/offline';
|
||||||
import { useOfflineBrowseReloadToken } from '@/features/offline';
|
import { useOfflineBrowseReloadToken } from '@/features/offline';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
isAlbumDetailPath,
|
isAlbumDetailPath,
|
||||||
useAlbumBrowseSessionStore,
|
useAlbumBrowseSessionStore,
|
||||||
} from '@/features/album/store/albumBrowseSessionStore';
|
} from '@/features/album/store/albumBrowseSessionStore';
|
||||||
import type { AlbumBrowseSort } from '@/utils/library/browseTextSearch';
|
import type { AlbumBrowseSort } from '@/lib/library/browseTextSearch';
|
||||||
import { shouldRestoreAlbumBrowseSession } from '@/utils/navigation/albumDetailNavigation';
|
import { shouldRestoreAlbumBrowseSession } from '@/utils/navigation/albumDetailNavigation';
|
||||||
import { useLiveSearchScopeStore } from '@/store/liveSearchScopeStore';
|
import { useLiveSearchScopeStore } from '@/store/liveSearchScopeStore';
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import {
|
|||||||
ALBUM_YEAR_MAX,
|
ALBUM_YEAR_MAX,
|
||||||
ALBUM_YEAR_MIN,
|
ALBUM_YEAR_MIN,
|
||||||
type AlbumCatalogYearRange,
|
type AlbumCatalogYearRange,
|
||||||
} from '@/utils/library/albumYearFilter';
|
} from '@/lib/library/albumYearFilter';
|
||||||
import { fetchAlbumCatalogYearBounds } from '@/utils/library/albumCatalogYearBounds';
|
import { fetchAlbumCatalogYearBounds } from '@/lib/library/albumCatalogYearBounds';
|
||||||
|
|
||||||
const DEFAULT: AlbumCatalogYearRange = { min: ALBUM_YEAR_MIN, max: ALBUM_YEAR_MAX };
|
const DEFAULT: AlbumCatalogYearRange = { min: ALBUM_YEAR_MIN, max: ALBUM_YEAR_MAX };
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
offlineLocalBrowseEnabled,
|
offlineLocalBrowseEnabled,
|
||||||
} from '@/features/offline';
|
} from '@/features/offline';
|
||||||
import { readDetailServerId } from '@/utils/navigation/detailServerScope';
|
import { readDetailServerId } from '@/utils/navigation/detailServerScope';
|
||||||
import { libraryIsReady } from '@/utils/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
import {
|
import {
|
||||||
shouldAttemptSubsonicForActiveServer,
|
shouldAttemptSubsonicForActiveServer,
|
||||||
shouldAttemptSubsonicForServer,
|
shouldAttemptSubsonicForServer,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
raceBrowseWithLocalFallback,
|
raceBrowseWithLocalFallback,
|
||||||
runLocalBrowseAlbums,
|
runLocalBrowseAlbums,
|
||||||
runNetworkBrowseAlbums,
|
runNetworkBrowseAlbums,
|
||||||
} from '@/utils/library/browseTextSearch';
|
} from '@/lib/library/browseTextSearch';
|
||||||
import { useOfflineBrowseContext } from '@/features/offline';
|
import { useOfflineBrowseContext } from '@/features/offline';
|
||||||
import { offlineLocalBrowseEnabled, searchOfflineLocalAlbums } from '@/features/offline';
|
import { offlineLocalBrowseEnabled, searchOfflineLocalAlbums } from '@/features/offline';
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import { dedupeById } from '@/lib/util/dedupeById';
|
import { dedupeById } from '@/lib/util/dedupeById';
|
||||||
import type { AlbumBrowseSort } from '@/utils/library/albumBrowseSort';
|
import type { AlbumBrowseSort } from '@/lib/library/albumBrowseSort';
|
||||||
import {
|
import {
|
||||||
fetchGenreAlbumPage,
|
fetchGenreAlbumPage,
|
||||||
GENRE_ALBUM_CATALOG_CHUNK,
|
GENRE_ALBUM_CATALOG_CHUNK,
|
||||||
GENRE_ALBUM_FIRST_PAGE,
|
GENRE_ALBUM_FIRST_PAGE,
|
||||||
} from '@/utils/library/genreAlbumBrowse';
|
} from '@/lib/library/genreAlbumBrowse';
|
||||||
import { useClientSliceInfiniteScroll } from '@/hooks/useClientSliceInfiniteScroll';
|
import { useClientSliceInfiniteScroll } from '@/hooks/useClientSliceInfiniteScroll';
|
||||||
import { useInpageScrollSentinel } from '@/hooks/useInpageScrollSentinel';
|
import { useInpageScrollSentinel } from '@/hooks/useInpageScrollSentinel';
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* the album session store, per-album offline status, and album export helpers.
|
* the album session store, per-album offline status, and album export helpers.
|
||||||
*
|
*
|
||||||
* Stays OUT (library-core / cross-cutting, consumed by this feature, not owned):
|
* Stays OUT (library-core / cross-cutting, consumed by this feature, not owned):
|
||||||
* the `utils/library/album*` browse query engine (shared with offline + other
|
* the `lib/library/album*` browse query engine (shared with offline + other
|
||||||
* browse surfaces → M4), `albumDetailNavigation` (cross-cutting nav → M4),
|
* browse surfaces → M4), `albumDetailNavigation` (cross-cutting nav → M4),
|
||||||
* `playAlbum` (playback action → playback), `starredAlbumIndexSync` (index
|
* `playAlbum` (playback action → playback), `starredAlbumIndexSync` (index
|
||||||
* sync, core), the album context-menu items (context-menu subsystem), the
|
* sync, core), the album context-menu items (context-menu subsystem), the
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
|
|||||||
import { albumGridWarmCovers } from '@/cover/layoutSizes';
|
import { albumGridWarmCovers } from '@/cover/layoutSizes';
|
||||||
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
|
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
|
||||||
import LosslessModeBanner from '@/components/LosslessModeBanner';
|
import LosslessModeBanner from '@/components/LosslessModeBanner';
|
||||||
import { isLosslessSuffix } from '@/utils/library/losslessFormats';
|
import { isLosslessSuffix } from '@/lib/library/losslessFormats';
|
||||||
import { isLosslessMode } from '@/utils/library/losslessMode';
|
import { isLosslessMode } from '@/lib/library/losslessMode';
|
||||||
import { readDetailServerId } from '@/utils/navigation/detailServerScope';
|
import { readDetailServerId } from '@/utils/navigation/detailServerScope';
|
||||||
import { useOfflineBrowseContext } from '@/features/offline';
|
import { useOfflineBrowseContext } from '@/features/offline';
|
||||||
import { offlineActionPolicy } from '@/features/offline';
|
import { offlineActionPolicy } from '@/features/offline';
|
||||||
|
|||||||
@@ -43,15 +43,15 @@ import { peekAlbumBrowseScrollRestore } from '@/features/album/store/albumBrowse
|
|||||||
import { readAlbumBrowseRestore } from '@/utils/navigation/albumDetailNavigation';
|
import { readAlbumBrowseRestore } from '@/utils/navigation/albumDetailNavigation';
|
||||||
import { albumArtistDisplayName } from '@/features/album/utils/deriveAlbumHeaderArtistRefs';
|
import { albumArtistDisplayName } from '@/features/album/utils/deriveAlbumHeaderArtistRefs';
|
||||||
import { useAlbumCatalogYearBounds } from '@/features/album/hooks/useAlbumCatalogYearBounds';
|
import { useAlbumCatalogYearBounds } from '@/features/album/hooks/useAlbumCatalogYearBounds';
|
||||||
import type { AlbumBrowseSort } from '@/utils/library/albumBrowseSort';
|
import type { AlbumBrowseSort } from '@/lib/library/albumBrowseSort';
|
||||||
import { LOSSLESS_MODE_QUERY } from '@/utils/library/losslessMode';
|
import { LOSSLESS_MODE_QUERY } from '@/lib/library/losslessMode';
|
||||||
import { resolveAlbumYearBounds } from '@/utils/library/albumYearFilter';
|
import { resolveAlbumYearBounds } from '@/lib/library/albumYearFilter';
|
||||||
import {
|
import {
|
||||||
filterAlbumsByCompilation,
|
filterAlbumsByCompilation,
|
||||||
filterAlbumsByGenres,
|
filterAlbumsByGenres,
|
||||||
filterAlbumsByStarred,
|
filterAlbumsByStarred,
|
||||||
filterAlbumsByYearBounds,
|
filterAlbumsByYearBounds,
|
||||||
} from '@/utils/library/albumBrowseFilters';
|
} from '@/lib/library/albumBrowseFilters';
|
||||||
import { useScopedBrowseSearchQuery } from '@/store/liveSearchScopeStore';
|
import { useScopedBrowseSearchQuery } from '@/store/liveSearchScopeStore';
|
||||||
|
|
||||||
type SortType = AlbumBrowseSort;
|
type SortType = AlbumBrowseSort;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
|||||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import AlbumCard from '@/features/album/components/AlbumCard';
|
import AlbumCard from '@/features/album/components/AlbumCard';
|
||||||
import { LOSSLESS_MODE_QUERY } from '@/utils/library/losslessMode';
|
import { LOSSLESS_MODE_QUERY } from '@/lib/library/losslessMode';
|
||||||
import { ndListLosslessAlbumsPage } from '@/lib/api/navidromeBrowse';
|
import { ndListLosslessAlbumsPage } from '@/lib/api/navidromeBrowse';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
@@ -38,7 +38,7 @@ import {
|
|||||||
runLocalAlbumBrowsePage,
|
runLocalAlbumBrowsePage,
|
||||||
sortSubsonicAlbums,
|
sortSubsonicAlbums,
|
||||||
type AlbumBrowseSort,
|
type AlbumBrowseSort,
|
||||||
} from '@/utils/library/browseTextSearch';
|
} from '@/lib/library/browseTextSearch';
|
||||||
|
|
||||||
/** Local index page size — SQLite is cheap; larger pages than the network walk. */
|
/** Local index page size — SQLite is cheap; larger pages than the network walk. */
|
||||||
const LOCAL_PAGE_SIZE = 30;
|
const LOCAL_PAGE_SIZE = 30;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
|
|||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '@/utils/mix/mixRatingFilter';
|
import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '@/utils/mix/mixRatingFilter';
|
||||||
import { runLocalRandomAlbums, runLocalAlbumsByGenres } from '@/utils/library/browseTextSearch';
|
import { runLocalRandomAlbums, runLocalAlbumsByGenres } from '@/lib/library/browseTextSearch';
|
||||||
import { useOfflineStore } from '@/features/offline';
|
import { useOfflineStore } from '@/features/offline';
|
||||||
import { useDownloadModalStore } from '@/features/offline';
|
import { useDownloadModalStore } from '@/features/offline';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import type { AlbumBrowseSort } from '@/utils/library/browseTextSearch';
|
import type { AlbumBrowseSort } from '@/lib/library/browseTextSearch';
|
||||||
|
|
||||||
export const DEFAULT_ALBUM_BROWSE_SORT: AlbumBrowseSort = 'alphabeticalByName';
|
export const DEFAULT_ALBUM_BROWSE_SORT: AlbumBrowseSort = 'alphabeticalByName';
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
|||||||
import { dedupeById } from '@/lib/util/dedupeById';
|
import { dedupeById } from '@/lib/util/dedupeById';
|
||||||
import { isOfflineBrowseActive } from '@/features/offline';
|
import { isOfflineBrowseActive } from '@/features/offline';
|
||||||
import { loadOfflineAlbumCatalogChunk } from '@/features/offline';
|
import { loadOfflineAlbumCatalogChunk } from '@/features/offline';
|
||||||
import type { AlbumBrowseQuery } from '@/utils/library/albumBrowseTypes';
|
import type { AlbumBrowseQuery } from '@/lib/library/albumBrowseTypes';
|
||||||
import { fetchLocalAlbumCatalogChunk } from '@/utils/library/albumBrowseLoad';
|
import { fetchLocalAlbumCatalogChunk } from '@/lib/library/albumBrowseLoad';
|
||||||
|
|
||||||
export type AlbumCatalogChunk = {
|
export type AlbumCatalogChunk = {
|
||||||
albums: SubsonicAlbum[];
|
albums: SubsonicAlbum[];
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import { loadArtistFromLibraryIndex } from '@/features/offline';
|
|||||||
import { useOfflineBrowseContext } from '@/features/offline';
|
import { useOfflineBrowseContext } from '@/features/offline';
|
||||||
import { loadArtistFromLocalPlayback, offlineLocalBrowseEnabled } from '@/features/offline';
|
import { loadArtistFromLocalPlayback, offlineLocalBrowseEnabled } from '@/features/offline';
|
||||||
import { readDetailServerId } from '@/utils/navigation/detailServerScope';
|
import { readDetailServerId } from '@/utils/navigation/detailServerScope';
|
||||||
import { runLocalArtistLosslessBrowse } from '@/utils/library/browseTextSearch';
|
import { runLocalArtistLosslessBrowse } from '@/lib/library/browseTextSearch';
|
||||||
import { isLosslessSuffix } from '@/utils/library/losslessFormats';
|
import { isLosslessSuffix } from '@/lib/library/losslessFormats';
|
||||||
|
|
||||||
export interface UseArtistDetailDataOptions {
|
export interface UseArtistDetailDataOptions {
|
||||||
/** When true, albums and top tracks are limited to lossless containers (local index preferred). */
|
/** When true, albums and top tracks are limited to lossless containers (local index preferred). */
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { dedupeById } from '@/lib/util/dedupeById';
|
|||||||
import {
|
import {
|
||||||
fetchLocalArtistCatalogChunk,
|
fetchLocalArtistCatalogChunk,
|
||||||
fetchNetworkStarredArtists,
|
fetchNetworkStarredArtists,
|
||||||
} from '@/utils/library/browseTextSearch';
|
} from '@/lib/library/browseTextSearch';
|
||||||
import { useOfflineBrowseContext } from '@/features/offline';
|
import { useOfflineBrowseContext } from '@/features/offline';
|
||||||
import { useOfflineBrowseReloadToken } from '@/features/offline';
|
import { useOfflineBrowseReloadToken } from '@/features/offline';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
runLocalBrowseArtists,
|
runLocalBrowseArtists,
|
||||||
runNetworkBrowseArtists,
|
runNetworkBrowseArtists,
|
||||||
type LibrarySearchSurface,
|
type LibrarySearchSurface,
|
||||||
} from '@/utils/library/browseTextSearch';
|
} from '@/lib/library/browseTextSearch';
|
||||||
import { useOfflineBrowseContext } from '@/features/offline';
|
import { useOfflineBrowseContext } from '@/features/offline';
|
||||||
import { offlineLocalBrowseEnabled, searchOfflineLocalArtists } from '@/features/offline';
|
import { offlineLocalBrowseEnabled, searchOfflineLocalArtists } from '@/features/offline';
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* `pages/*` paths, not re-exported), artist cards/rows/avatars, the detail hero +
|
* `pages/*` paths, not re-exported), artist cards/rows/avatars, the detail hero +
|
||||||
* similar-artists + top-tracks UI, the artist Subsonic API, browse catalog/
|
* similar-artists + top-tracks UI, the artist Subsonic API, browse catalog/
|
||||||
* filter/scroll hooks, per-artist offline status, and the open-artist-ref nav
|
* filter/scroll hooks, per-artist offline status, and the open-artist-ref nav
|
||||||
* cluster. `sortArtistAlbums` moved in from `utils/library/` (artist-only sort,
|
* cluster. `sortArtistAlbums` moved in from `lib/library/` (artist-only sort,
|
||||||
* not the shared index query engine).
|
* not the shared index query engine).
|
||||||
*
|
*
|
||||||
* Stays OUT (other owners): `deriveAlbumHeaderArtistRefs` (album header),
|
* Stays OUT (other owners): `deriveAlbumHeaderArtistRefs` (album header),
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import { artistDetailCoverWarmAlbums } from '@/features/artist/components/topSon
|
|||||||
import { useLibraryCoverPrefetch } from '@/cover/useLibraryCoverPrefetch';
|
import { useLibraryCoverPrefetch } from '@/cover/useLibraryCoverPrefetch';
|
||||||
import { useWarmGridCovers } from '@/hooks/useWarmGridCovers';
|
import { useWarmGridCovers } from '@/hooks/useWarmGridCovers';
|
||||||
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
|
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
|
||||||
import { LOSSLESS_MODE_QUERY } from '@/utils/library/losslessMode';
|
import { LOSSLESS_MODE_QUERY } from '@/lib/library/losslessMode';
|
||||||
import { sortArtistAlbumsByYear } from '@/features/artist/utils/sortArtistAlbums';
|
import { sortArtistAlbumsByYear } from '@/features/artist/utils/sortArtistAlbums';
|
||||||
import { readDetailServerId } from '@/utils/navigation/detailServerScope';
|
import { readDetailServerId } from '@/utils/navigation/detailServerScope';
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import { getAlbumForServer, getSongForServer } from '@/lib/api/subsonicLibrary';
|
|||||||
import type { SubsonicAlbum, SubsonicSong } from '@/lib/api/subsonicTypes';
|
import type { SubsonicAlbum, SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard';
|
import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard';
|
||||||
import { loadAlbumFromLibraryIndex, loadArtistFromLibraryIndex } from '@/features/offline';
|
import { loadAlbumFromLibraryIndex, loadArtistFromLibraryIndex } from '@/features/offline';
|
||||||
import { trackToSong } from '@/utils/library/advancedSearchLocal';
|
import { trackToSong } from '@/lib/library/advancedSearchLocal';
|
||||||
import { libraryIsReady } from '@/utils/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
|
|
||||||
const TOP_SONGS_LIMIT = 5;
|
const TOP_SONGS_LIMIT = 5;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import type { AlbumBrowseQuery } from '@/utils/library/albumBrowseTypes';
|
import type { AlbumBrowseQuery } from '@/lib/library/albumBrowseTypes';
|
||||||
import { isOfflineBrowseActive } from '@/features/offline/utils/offlineBrowseMode';
|
import { isOfflineBrowseActive } from '@/features/offline/utils/offlineBrowseMode';
|
||||||
import {
|
import {
|
||||||
fetchOfflineLocalAlbumCatalogChunk,
|
fetchOfflineLocalAlbumCatalogChunk,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import type { LocalPlaybackEntry, PinnedGroup, PinSource } from '@/store/localPl
|
|||||||
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
||||||
import { findLocalPlaybackEntry, hasLocalLibraryBytes } from '@/store/localPlaybackResolve';
|
import { findLocalPlaybackEntry, hasLocalLibraryBytes } from '@/store/localPlaybackResolve';
|
||||||
import { useOfflineStore, type OfflineAlbumMeta } from '@/features/offline/store/offlineStore';
|
import { useOfflineStore, type OfflineAlbumMeta } from '@/features/offline/store/offlineStore';
|
||||||
import { resolveTrackCoverArtId, trackToSong } from '@/utils/library/advancedSearchLocal';
|
import { resolveTrackCoverArtId, trackToSong } from '@/lib/library/advancedSearchLocal';
|
||||||
import { canonicalQueueServerKey, resolveIndexKey } from '@/utils/server/serverIndexKey';
|
import { canonicalQueueServerKey, resolveIndexKey } from '@/utils/server/serverIndexKey';
|
||||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||||
import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
albumToAlbum,
|
albumToAlbum,
|
||||||
artistToArtist,
|
artistToArtist,
|
||||||
trackToSong,
|
trackToSong,
|
||||||
} from '@/utils/library/advancedSearchLocal';
|
} from '@/lib/library/advancedSearchLocal';
|
||||||
|
|
||||||
export async function loadAlbumFromLibraryIndex(
|
export async function loadAlbumFromLibraryIndex(
|
||||||
serverId: string,
|
serverId: string,
|
||||||
|
|||||||
@@ -9,17 +9,17 @@ import {
|
|||||||
artistToArtist,
|
artistToArtist,
|
||||||
resolveTrackCoverArtId,
|
resolveTrackCoverArtId,
|
||||||
trackToSong,
|
trackToSong,
|
||||||
} from '@/utils/library/advancedSearchLocal';
|
} from '@/lib/library/advancedSearchLocal';
|
||||||
import { albumIsCompilationFromTrackDtos } from '@/utils/library/albumCompilation';
|
import { albumIsCompilationFromTrackDtos } from '@/lib/library/albumCompilation';
|
||||||
import {
|
import {
|
||||||
filterAlbumsByCompilation,
|
filterAlbumsByCompilation,
|
||||||
filterAlbumsByGenres,
|
filterAlbumsByGenres,
|
||||||
filterAlbumsByStarred,
|
filterAlbumsByStarred,
|
||||||
filterAlbumsByYearBounds,
|
filterAlbumsByYearBounds,
|
||||||
} from '@/utils/library/albumBrowseFilters';
|
} from '@/lib/library/albumBrowseFilters';
|
||||||
import type { AlbumBrowseQuery } from '@/utils/library/albumBrowseTypes';
|
import type { AlbumBrowseQuery } from '@/lib/library/albumBrowseTypes';
|
||||||
import { sortSubsonicAlbums } from '@/utils/library/albumBrowseSort';
|
import { sortSubsonicAlbums } from '@/lib/library/albumBrowseSort';
|
||||||
import { isLosslessSuffix } from '@/utils/library/losslessFormats';
|
import { isLosslessSuffix } from '@/lib/library/losslessFormats';
|
||||||
import { entryBelongsToServer } from '@/store/localPlaybackResolve';
|
import { entryBelongsToServer } from '@/store/localPlaybackResolve';
|
||||||
|
|
||||||
function sortBrowsableSongs(songs: SubsonicSong[]): SubsonicSong[] {
|
function sortBrowsableSongs(songs: SubsonicSong[]): SubsonicSong[] {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ vi.mock('@/lib/api/subsonicPlaylists', () => ({
|
|||||||
getPlaylistForServerMock(serverId, playlistId),
|
getPlaylistForServerMock(serverId, playlistId),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('@/utils/library/libraryReady', () => ({
|
vi.mock('@/lib/library/libraryReady', () => ({
|
||||||
libraryIsReady: (serverId: string) => libraryIsReadyMock(serverId),
|
libraryIsReady: (serverId: string) => libraryIsReadyMock(serverId),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import type {
|
|||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard';
|
import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard';
|
||||||
import { isOfflineBrowseActive } from '@/features/offline/utils/offlineBrowseMode';
|
import { isOfflineBrowseActive } from '@/features/offline/utils/offlineBrowseMode';
|
||||||
import { libraryIsReady } from '@/utils/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
import { registerMediaResolver } from '@/store/mediaResolver';
|
import { registerMediaResolver } from '@/store/mediaResolver';
|
||||||
import {
|
import {
|
||||||
loadAlbumFromLibraryIndex,
|
loadAlbumFromLibraryIndex,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { SubsonicPlaylist, SubsonicSong } from '@/lib/api/subsonicTypes';
|
|||||||
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
import type { PinnedGroup } from '@/store/localPlaybackStore';
|
import type { PinnedGroup } from '@/store/localPlaybackStore';
|
||||||
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
||||||
import { trackToSong } from '@/utils/library/advancedSearchLocal';
|
import { trackToSong } from '@/lib/library/advancedSearchLocal';
|
||||||
import { isManualOfflinePlaylist } from '@/features/offline/utils/pinnedOfflineSync';
|
import { isManualOfflinePlaylist } from '@/features/offline/utils/pinnedOfflineSync';
|
||||||
import {
|
import {
|
||||||
hasLocalLibraryBytes,
|
hasLocalLibraryBytes,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { isActiveServerReachable } from '@/utils/network/activeServerReachabilit
|
|||||||
import {
|
import {
|
||||||
albumToAlbum,
|
albumToAlbum,
|
||||||
trackToSong,
|
trackToSong,
|
||||||
} from '@/utils/library/advancedSearchLocal';
|
} from '@/lib/library/advancedSearchLocal';
|
||||||
import { dedupeById } from '@/lib/util/dedupeById';
|
import { dedupeById } from '@/lib/util/dedupeById';
|
||||||
import { isOfflineBrowseActive } from '@/features/offline/utils/offlineBrowseMode';
|
import { isOfflineBrowseActive } from '@/features/offline/utils/offlineBrowseMode';
|
||||||
import { favoritesServerIds } from '@/features/offline/utils/favoritesOfflineBrowse';
|
import { favoritesServerIds } from '@/features/offline/utils/favoritesOfflineBrowse';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
|||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import type { OfflineAlbumMeta } from '@/features/offline/store/offlineStore';
|
import type { OfflineAlbumMeta } from '@/features/offline/store/offlineStore';
|
||||||
import { useOfflineStore } from '@/features/offline/store/offlineStore';
|
import { useOfflineStore } from '@/features/offline/store/offlineStore';
|
||||||
import { trackToSong } from '@/utils/library/advancedSearchLocal';
|
import { trackToSong } from '@/lib/library/advancedSearchLocal';
|
||||||
import { isActiveServerReachable, onActiveServerBecameReachable } from '@/utils/network/activeServerReachability';
|
import { isActiveServerReachable, onActiveServerBecameReachable } from '@/utils/network/activeServerReachability';
|
||||||
import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard';
|
import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
} from '@/features/playback/store/playListenSession';
|
} from '@/features/playback/store/playListenSession';
|
||||||
import { onPlaySessionRecorded } from '@/features/playback/store/playSessionRecorded';
|
import { onPlaySessionRecorded } from '@/features/playback/store/playSessionRecorded';
|
||||||
|
|
||||||
vi.mock('@/utils/library/libraryReady', () => ({
|
vi.mock('@/lib/library/libraryReady', () => ({
|
||||||
libraryIsReady: vi.fn(async () => true),
|
libraryIsReady: vi.fn(async () => true),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ describe('playListenSession', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('skips when library is not ready', async () => {
|
it('skips when library is not ready', async () => {
|
||||||
const { libraryIsReady } = await import('@/utils/library/libraryReady');
|
const { libraryIsReady } = await import('@/lib/library/libraryReady');
|
||||||
vi.mocked(libraryIsReady).mockResolvedValueOnce(false);
|
vi.mocked(libraryIsReady).mockResolvedValueOnce(false);
|
||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
await playListenSessionOpen(testTrack, 'server-1');
|
await playListenSessionOpen(testTrack, 'server-1');
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
libraryRecordPlaySession,
|
libraryRecordPlaySession,
|
||||||
type PlaySessionEndReason,
|
type PlaySessionEndReason,
|
||||||
} from '@/lib/api/library';
|
} from '@/lib/api/library';
|
||||||
import { libraryIsReady } from '@/utils/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||||
import { emitPlaySessionRecorded } from '@/features/playback/store/playSessionRecorded';
|
import { emitPlaySessionRecorded } from '@/features/playback/store/playSessionRecorded';
|
||||||
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import type { QueueItemRef, Track } from '@/features/playback/store/playerStoreT
|
|||||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
||||||
import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
|
import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
|
||||||
import { trackToSong } from '@/utils/library/advancedSearchLocal';
|
import { trackToSong } from '@/lib/library/advancedSearchLocal';
|
||||||
import { libraryIsReady } from '@/utils/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queue track resolver (thin-state phase 2). Resolves `QueueItemRef`s to full
|
* Queue track resolver (thin-state phase 2). Resolves `QueueItemRef`s to full
|
||||||
|
|||||||
@@ -22,23 +22,23 @@ vi.mock('@/lib/api/subsonicClient', () => ({
|
|||||||
libraryScopeForServer: vi.fn(() => 'music'),
|
libraryScopeForServer: vi.fn(() => 'music'),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('@/utils/library/libraryReady', () => ({
|
vi.mock('@/lib/library/libraryReady', () => ({
|
||||||
libraryIsReady: vi.fn(),
|
libraryIsReady: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Spread the real leaf module so other consumers pulled in transitively (the
|
// Spread the real leaf module so other consumers pulled in transitively (the
|
||||||
// album barrel reaches this via the artist↔album edge → useGenreAlbumBrowse needs
|
// album barrel reaches this via the artist↔album edge → useGenreAlbumBrowse needs
|
||||||
// GENRE_ALBUM_FIRST_PAGE); only fetchGenreAlbumTotal is stubbed here.
|
// GENRE_ALBUM_FIRST_PAGE); only fetchGenreAlbumTotal is stubbed here.
|
||||||
vi.mock('@/utils/library/genreAlbumBrowse', async (importOriginal) => ({
|
vi.mock('@/lib/library/genreAlbumBrowse', async (importOriginal) => ({
|
||||||
...(await importOriginal<typeof import('@/utils/library/genreAlbumBrowse')>()),
|
...(await importOriginal<typeof import('@/lib/library/genreAlbumBrowse')>()),
|
||||||
fetchGenreAlbumTotal: vi.fn(),
|
fetchGenreAlbumTotal: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { libraryAdvancedSearch, libraryGetGenreAlbumCounts } from '@/lib/api/library';
|
import { libraryAdvancedSearch, libraryGetGenreAlbumCounts } from '@/lib/api/library';
|
||||||
import { fetchAllSongsByGenre, getGenres } from '@/lib/api/subsonicGenres';
|
import { fetchAllSongsByGenre, getGenres } from '@/lib/api/subsonicGenres';
|
||||||
import { fetchGenreAlbumTotal } from '@/utils/library/genreAlbumBrowse';
|
import { fetchGenreAlbumTotal } from '@/lib/library/genreAlbumBrowse';
|
||||||
import { resetGenreCatalogCountsCacheForTests } from '@/utils/library/genreCatalogCountsCache';
|
import { resetGenreCatalogCountsCacheForTests } from '@/lib/library/genreCatalogCountsCache';
|
||||||
import { libraryIsReady } from '@/utils/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
|
|
||||||
describe('genreBrowsePlayback', () => {
|
describe('genreBrowsePlayback', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import { libraryScopeForServer } from '@/lib/api/subsonicClient';
|
|||||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||||
import { shuffleArray } from '@/lib/util/shuffleArray';
|
import { shuffleArray } from '@/lib/util/shuffleArray';
|
||||||
import { trackToSong } from '@/utils/library/advancedSearchLocal';
|
import { trackToSong } from '@/lib/library/advancedSearchLocal';
|
||||||
import { type AlbumBrowseSort } from '@/utils/library/albumBrowseSort';
|
import { type AlbumBrowseSort } from '@/lib/library/albumBrowseSort';
|
||||||
import {
|
import {
|
||||||
genreCatalogCacheKey,
|
genreCatalogCacheKey,
|
||||||
getInflightGenreCatalog,
|
getInflightGenreCatalog,
|
||||||
@@ -17,9 +17,9 @@ import {
|
|||||||
peekGenreCatalogCache,
|
peekGenreCatalogCache,
|
||||||
trackInflightGenreCatalog,
|
trackInflightGenreCatalog,
|
||||||
writeGenreCatalogCache,
|
writeGenreCatalogCache,
|
||||||
} from '@/utils/library/genreCatalogCountsCache';
|
} from '@/lib/library/genreCatalogCountsCache';
|
||||||
import { fetchGenreAlbumTotal } from '@/utils/library/genreAlbumBrowse';
|
import { fetchGenreAlbumTotal } from '@/lib/library/genreAlbumBrowse';
|
||||||
import { libraryIsReady } from '@/utils/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
|
|
||||||
/** Drop genres with no indexed albums/tracks (stale server list or orphan rows). */
|
/** Drop genres with no indexed albums/tracks (stale server list or orphan rows). */
|
||||||
export function filterGenresWithContent(genres: SubsonicGenre[]): SubsonicGenre[] {
|
export function filterGenresWithContent(genres: SubsonicGenre[]): SubsonicGenre[] {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
} from '@/utils/componentHelpers/queuePanelHelpers';
|
} from '@/utils/componentHelpers/queuePanelHelpers';
|
||||||
import { loudnessGainPlaceholderUntilCacheDb } from '@/features/playback/utils/audio/loudnessPlaceholder';
|
import { loudnessGainPlaceholderUntilCacheDb } from '@/features/playback/utils/audio/loudnessPlaceholder';
|
||||||
import { effectiveLoudnessPreAnalysisAttenuationDb } from '@/utils/audio/loudnessPreAnalysisSlider';
|
import { effectiveLoudnessPreAnalysisAttenuationDb } from '@/utils/audio/loudnessPreAnalysisSlider';
|
||||||
import { formatQueueBpmTech, formatQueueMoodLabels } from '@/utils/library/trackEnrichment';
|
import { formatQueueBpmTech, formatQueueMoodLabels } from '@/lib/library/trackEnrichment';
|
||||||
import { useQueueTrackEnrichment } from '@/features/queue/hooks/useQueueTrackEnrichment';
|
import { useQueueTrackEnrichment } from '@/features/queue/hooks/useQueueTrackEnrichment';
|
||||||
import { QueueLufsTargetMenu } from '@/features/queue/components/QueueLufsTargetMenu';
|
import { QueueLufsTargetMenu } from '@/features/queue/components/QueueLufsTargetMenu';
|
||||||
import { PlaybackBufferingOverlay } from '@/components/playback/PlaybackBufferingOverlay';
|
import { PlaybackBufferingOverlay } from '@/components/playback/PlaybackBufferingOverlay';
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
OXIMEDIA_MOOD_UI_ENABLED,
|
OXIMEDIA_MOOD_UI_ENABLED,
|
||||||
parseTrackEnrichmentFacts,
|
parseTrackEnrichmentFacts,
|
||||||
type ParsedTrackEnrichment,
|
type ParsedTrackEnrichment,
|
||||||
} from '@/utils/library/trackEnrichment';
|
} from '@/lib/library/trackEnrichment';
|
||||||
import { libraryIsReady } from '@/utils/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
import { normalizeAnalysisTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
import { normalizeAnalysisTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
||||||
|
|
||||||
const EMPTY: ParsedTrackEnrichment = {
|
const EMPTY: ParsedTrackEnrichment = {
|
||||||
|
|||||||
@@ -9,17 +9,17 @@ import {
|
|||||||
mergeLiveSearchResults,
|
mergeLiveSearchResults,
|
||||||
runLocalLiveSearch,
|
runLocalLiveSearch,
|
||||||
runNetworkLiveSearch,
|
runNetworkLiveSearch,
|
||||||
} from '@/utils/library/liveSearchLocal';
|
} from '@/lib/library/liveSearchLocal';
|
||||||
import { raceLiveSearch } from '@/utils/library/searchRace';
|
import { raceLiveSearch } from '@/lib/library/searchRace';
|
||||||
import { libraryIsReady } from '@/utils/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
import {
|
import {
|
||||||
emitLiveSearchDebug,
|
emitLiveSearchDebug,
|
||||||
searchHitCounts,
|
searchHitCounts,
|
||||||
searchResultSamples,
|
searchResultSamples,
|
||||||
} from '@/utils/library/liveSearchDebug';
|
} from '@/lib/library/liveSearchDebug';
|
||||||
import {
|
import {
|
||||||
logLibrarySearch,
|
logLibrarySearch,
|
||||||
} from '@/utils/library/libraryDevLog';
|
} from '@/lib/library/libraryDevLog';
|
||||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { useNavigateToAlbum } from '@/features/album';
|
import { useNavigateToAlbum } from '@/features/album';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* search/browse page (lazy via the deep path `pages/SearchBrowsePage`), live
|
* search/browse page (lazy via the deep path `pages/SearchBrowsePage`), live
|
||||||
* search scope UI/state, and the share-link search/queue-preview surfaces.
|
* search scope UI/state, and the share-link search/queue-preview surfaces.
|
||||||
*
|
*
|
||||||
* The local-index query engine (`utils/library/*search*`), the shared Subsonic
|
* The local-index query engine (`lib/library/*search*`), the shared Subsonic
|
||||||
* search API, the cross-cutting `liveSearchScopeStore`, and the
|
* search API, the cross-cutting `liveSearchScopeStore`, and the
|
||||||
* `advancedSearch*` session/scroll state live outside this feature
|
* `advancedSearch*` session/scroll state live outside this feature
|
||||||
* (library-core / cross-cutting) — this feature consumes them.
|
* (library-core / cross-cutting) — this feature consumes them.
|
||||||
|
|||||||
@@ -42,19 +42,19 @@ import {
|
|||||||
runNetworkAdvancedTextSearch,
|
runNetworkAdvancedTextSearch,
|
||||||
runNetworkAdvancedYearAlbums,
|
runNetworkAdvancedYearAlbums,
|
||||||
tryRunLocalAdvancedSearch,
|
tryRunLocalAdvancedSearch,
|
||||||
} from '@/utils/library/advancedSearchLocal';
|
} from '@/lib/library/advancedSearchLocal';
|
||||||
import { isLosslessSuffix } from '@/utils/library/losslessFormats';
|
import { isLosslessSuffix } from '@/lib/library/losslessFormats';
|
||||||
import { LOSSLESS_MODE_QUERY } from '@/utils/library/losslessMode';
|
import { LOSSLESS_MODE_QUERY } from '@/lib/library/losslessMode';
|
||||||
import { OXIMEDIA_MOOD_SEARCH_ENABLED } from '@/utils/library/trackEnrichment';
|
import { OXIMEDIA_MOOD_SEARCH_ENABLED } from '@/lib/library/trackEnrichment';
|
||||||
import { raceSearchSources } from '@/utils/library/searchRace';
|
import { raceSearchSources } from '@/lib/library/searchRace';
|
||||||
import { logLibrarySearch } from '@/utils/library/libraryDevLog';
|
import { logLibrarySearch } from '@/lib/library/libraryDevLog';
|
||||||
import {
|
import {
|
||||||
browseRaceCountsFullSearch,
|
browseRaceCountsFullSearch,
|
||||||
loadMoreLocalBrowseSongs,
|
loadMoreLocalBrowseSongs,
|
||||||
raceBrowseWithLocalFallback,
|
raceBrowseWithLocalFallback,
|
||||||
runLocalBrowseFullSearch,
|
runLocalBrowseFullSearch,
|
||||||
runNetworkBrowseFullSearch,
|
runNetworkBrowseFullSearch,
|
||||||
} from '@/utils/library/browseTextSearch';
|
} from '@/lib/library/browseTextSearch';
|
||||||
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
import { MOOD_GROUP_IDS } from '@/config/moodGroups';
|
import { MOOD_GROUP_IDS } from '@/config/moodGroups';
|
||||||
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
|
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import {
|
|||||||
ADVANCED_PARALLELISM_MAX,
|
ADVANCED_PARALLELISM_MAX,
|
||||||
ADVANCED_PARALLELISM_MIN,
|
ADVANCED_PARALLELISM_MIN,
|
||||||
type AnalyticsStrategy,
|
type AnalyticsStrategy,
|
||||||
} from '@/utils/library/analysisStrategy';
|
} from '@/lib/library/analysisStrategy';
|
||||||
|
|
||||||
type ClearTarget = {
|
type ClearTarget = {
|
||||||
serverId: string;
|
serverId: string;
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
|||||||
import { showToast } from '@/utils/ui/toast';
|
import { showToast } from '@/utils/ui/toast';
|
||||||
import { formatBytes } from '@/lib/format/formatBytes';
|
import { formatBytes } from '@/lib/format/formatBytes';
|
||||||
import { clearImageCache, getImageCacheSize } from '@/utils/imageCache';
|
import { clearImageCache, getImageCacheSize } from '@/utils/imageCache';
|
||||||
import { wakeLibraryCoverBackfill } from '@/utils/library/coverBackfillWake';
|
import { wakeLibraryCoverBackfill } from '@/lib/library/coverBackfillWake';
|
||||||
import {
|
import {
|
||||||
COVER_CACHE_STRATEGIES,
|
COVER_CACHE_STRATEGIES,
|
||||||
type CoverCacheStrategy,
|
type CoverCacheStrategy,
|
||||||
} from '@/utils/library/coverStrategy';
|
} from '@/lib/library/coverStrategy';
|
||||||
|
|
||||||
type ClearTarget =
|
type ClearTarget =
|
||||||
| { kind: 'image' }
|
| { kind: 'image' }
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { SyncStateDto } from '@/lib/api/library';
|
|||||||
import {
|
import {
|
||||||
libraryStatusDisplayTrackCount,
|
libraryStatusDisplayTrackCount,
|
||||||
libraryStatusIsReady,
|
libraryStatusIsReady,
|
||||||
} from '@/utils/library/libraryReady';
|
} from '@/lib/library/libraryReady';
|
||||||
import type { LibraryServerConnection } from '@/hooks/useLibraryIndexSync';
|
import type { LibraryServerConnection } from '@/hooks/useLibraryIndexSync';
|
||||||
|
|
||||||
interface ServerLibraryIndexControlsProps {
|
interface ServerLibraryIndexControlsProps {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { formatServerSoftware, isNavidromeAudiomuseSoftwareEligible, type Instan
|
|||||||
import { buildCapabilityContext } from '@/serverCapabilities/context';
|
import { buildCapabilityContext } from '@/serverCapabilities/context';
|
||||||
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
import { libraryDeleteServerData, librarySyncClearSession } from '@/lib/api/library';
|
import { libraryDeleteServerData, librarySyncClearSession } from '@/lib/api/library';
|
||||||
import { bootstrapIndexedServer } from '@/utils/library/librarySession';
|
import { bootstrapIndexedServer } from '@/lib/library/librarySession';
|
||||||
import { useLibraryIndexSync } from '@/hooks/useLibraryIndexSync';
|
import { useLibraryIndexSync } from '@/hooks/useLibraryIndexSync';
|
||||||
import ServerLibraryIndexControls from '@/features/settings/components/ServerLibraryIndexControls';
|
import ServerLibraryIndexControls from '@/features/settings/components/ServerLibraryIndexControls';
|
||||||
import type { ServerProfile } from '@/store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
logLibrarySync,
|
logLibrarySync,
|
||||||
normalizeIngestMetrics,
|
normalizeIngestMetrics,
|
||||||
timed,
|
timed,
|
||||||
} from '../../utils/library/libraryDevLog';
|
} from '@/lib/library/libraryDevLog';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DevTools: log library sync progress + idle with ingest strategy from status.
|
* DevTools: log library sync progress + idle with ingest strategy from status.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
import { librarySqlServerId } from '../api/coverCache';
|
import { librarySqlServerId } from '../api/coverCache';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { useAnalysisStrategyStore } from '../store/analysisStrategyStore';
|
import { useAnalysisStrategyStore } from '../store/analysisStrategyStore';
|
||||||
import { DEFAULT_ADVANCED_PARALLELISM } from '../utils/library/analysisStrategy';
|
import { DEFAULT_ADVANCED_PARALLELISM } from '@/lib/library/analysisStrategy';
|
||||||
import { serverIndexKeyForProfile } from '../utils/server/serverIndexKey';
|
import { serverIndexKeyForProfile } from '../utils/server/serverIndexKey';
|
||||||
|
|
||||||
const DISABLED_CONFIGURE = {
|
const DISABLED_CONFIGURE = {
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import {
|
|||||||
libraryCoverBackfillSetBaseUrl,
|
libraryCoverBackfillSetBaseUrl,
|
||||||
librarySqlServerId,
|
librarySqlServerId,
|
||||||
} from '../api/coverCache';
|
} from '../api/coverCache';
|
||||||
import { coverStrategyAllowsLibraryBackfill } from '../utils/library/coverStrategy';
|
import { coverStrategyAllowsLibraryBackfill } from '@/lib/library/coverStrategy';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { useCoverStrategyStore } from '../store/coverStrategyStore';
|
import { useCoverStrategyStore } from '../store/coverStrategyStore';
|
||||||
import { subscribeLibraryCoverBackfillWake } from '../utils/library/coverBackfillWake';
|
import { subscribeLibraryCoverBackfillWake } from '@/lib/library/coverBackfillWake';
|
||||||
import { serverIndexKeyForProfile } from '../utils/server/serverIndexKey';
|
import { serverIndexKeyForProfile } from '../utils/server/serverIndexKey';
|
||||||
import { subscribeConnectCache } from '../utils/server/serverEndpoint';
|
import { subscribeConnectCache } from '../utils/server/serverEndpoint';
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import {
|
|||||||
bootstrapAllIndexedServers,
|
bootstrapAllIndexedServers,
|
||||||
bootstrapIndexedServer,
|
bootstrapIndexedServer,
|
||||||
type BindServerResult,
|
type BindServerResult,
|
||||||
} from '../utils/library/librarySession';
|
} from '@/lib/library/librarySession';
|
||||||
import { enqueueLibrarySync } from '../utils/library/librarySyncQueue';
|
import { enqueueLibrarySync } from '@/lib/library/librarySyncQueue';
|
||||||
import { syncIngestDisplayCount } from '../utils/library/libraryReady';
|
import { syncIngestDisplayCount } from '@/lib/library/libraryReady';
|
||||||
|
|
||||||
export type LibraryServerConnection = 'online' | 'offline' | 'unknown';
|
export type LibraryServerConnection = 'online' | 'offline' | 'unknown';
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ vi.mock('@/lib/api/navidromeBrowse', () => ({
|
|||||||
ndListSongs: vi.fn(async () => []),
|
ndListSongs: vi.fn(async () => []),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../utils/library/advancedSearchLocal', () => ({
|
vi.mock('@/lib/library/advancedSearchLocal', () => ({
|
||||||
runLocalSongBrowse: vi.fn(async () => []),
|
runLocalSongBrowse: vi.fn(async () => []),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ vi.mock('@/features/offline/hooks/useOfflineBrowseReloadToken', () => ({
|
|||||||
useOfflineBrowseReloadToken: () => undefined,
|
useOfflineBrowseReloadToken: () => undefined,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('../utils/library/browseTextSearch', () => ({
|
vi.mock('@/lib/library/browseTextSearch', () => ({
|
||||||
BROWSE_TEXT_DEBOUNCE_NETWORK_MS: 10,
|
BROWSE_TEXT_DEBOUNCE_NETWORK_MS: 10,
|
||||||
BROWSE_TEXT_DEBOUNCE_RACE_MS: 10,
|
BROWSE_TEXT_DEBOUNCE_RACE_MS: 10,
|
||||||
browseRaceCountsSongs: vi.fn(),
|
browseRaceCountsSongs: vi.fn(),
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { searchSongsPaged } from '@/lib/api/subsonicSearch';
|
|||||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { ndListSongs } from '@/lib/api/navidromeBrowse';
|
import { ndListSongs } from '@/lib/api/navidromeBrowse';
|
||||||
import { runLocalSongBrowse } from '../utils/library/advancedSearchLocal';
|
import { runLocalSongBrowse } from '@/lib/library/advancedSearchLocal';
|
||||||
import {
|
import {
|
||||||
BROWSE_TEXT_DEBOUNCE_NETWORK_MS,
|
BROWSE_TEXT_DEBOUNCE_NETWORK_MS,
|
||||||
BROWSE_TEXT_DEBOUNCE_RACE_MS,
|
BROWSE_TEXT_DEBOUNCE_RACE_MS,
|
||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
raceBrowseWithLocalFallback,
|
raceBrowseWithLocalFallback,
|
||||||
runLocalBrowseSongPage,
|
runLocalBrowseSongPage,
|
||||||
runNetworkBrowseSongPage,
|
runNetworkBrowseSongPage,
|
||||||
} from '../utils/library/browseTextSearch';
|
} from '@/lib/library/browseTextSearch';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { useLibraryIndexStore } from '../store/libraryIndexStore';
|
import { useLibraryIndexStore } from '../store/libraryIndexStore';
|
||||||
import { useOfflineBrowseContext } from '@/features/offline';
|
import { useOfflineBrowseContext } from '@/features/offline';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { SubsonicAlbum, SubsonicArtist, SubsonicSong } from '@/lib/api/subsonicTypes';
|
import type { SubsonicAlbum, SubsonicArtist, SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { parseItemGenres } from '@/utils/library/genreTags';
|
import { parseItemGenres } from '@/lib/library/genreTags';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { ndLogin } from '@/lib/api/navidromeAdmin';
|
import { ndLogin } from '@/lib/api/navidromeAdmin';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { api, apiForServer } from '@/lib/api/subsonicClient';
|
import { api, apiForServer } from '@/lib/api/subsonicClient';
|
||||||
import type { PlaybackReportState, SubsonicNowPlaying } from '@/lib/api/subsonicTypes';
|
import type { PlaybackReportState, SubsonicNowPlaying } from '@/lib/api/subsonicTypes';
|
||||||
import { patchLibraryTrackOnUse } from '@/utils/library/patchOnUse';
|
import { patchLibraryTrackOnUse } from '@/lib/library/patchOnUse';
|
||||||
import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard';
|
import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard';
|
||||||
|
|
||||||
async function scrobbleOnServer(
|
async function scrobbleOnServer(
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { api, libraryFilterParams } from '@/lib/api/subsonicClient';
|
import { api, libraryFilterParams } from '@/lib/api/subsonicClient';
|
||||||
import { searchQueryIsFtsSafe } from '@/utils/library/searchQueryFtsSafe';
|
import { searchQueryIsFtsSafe } from '@/lib/library/searchQueryFtsSafe';
|
||||||
import type {
|
import type {
|
||||||
SearchResults,
|
SearchResults,
|
||||||
SubsonicAlbum,
|
SubsonicAlbum,
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { api, apiForServer, libraryFilterParams, libraryFilterParamsForServer } from '@/lib/api/subsonicClient';
|
import { api, apiForServer, libraryFilterParams, libraryFilterParamsForServer } from '@/lib/api/subsonicClient';
|
||||||
import { invalidateEntityUserRatingCaches } from '@/lib/api/subsonicRatings';
|
import { invalidateEntityUserRatingCaches } from '@/lib/api/subsonicRatings';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { patchLibraryTrackOnUse, type StarPatchMeta } from '@/utils/library/patchOnUse';
|
import { patchLibraryTrackOnUse, type StarPatchMeta } from '@/lib/library/patchOnUse';
|
||||||
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
import {
|
import {
|
||||||
invalidateStarredAlbumBrowse,
|
invalidateStarredAlbumBrowse,
|
||||||
refreshStarredAlbumIndexFromServer,
|
refreshStarredAlbumIndexFromServer,
|
||||||
} from '@/utils/library/starredAlbumIndexSync';
|
} from '@/lib/library/starredAlbumIndexSync';
|
||||||
import type {
|
import type {
|
||||||
EntityRatingSupportLevel,
|
EntityRatingSupportLevel,
|
||||||
StarredResults,
|
StarredResults,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { genreTagsFor } from '@/utils/library/genreTags';
|
import { genreTagsFor } from '@/lib/library/genreTags';
|
||||||
import { getArtists } from '@/lib/api/subsonicArtists';
|
import { getArtists } from '@/lib/api/subsonicArtists';
|
||||||
import { getAlbumList, getRandomSongs } from '@/lib/api/subsonicLibrary';
|
import { getAlbumList, getRandomSongs } from '@/lib/api/subsonicLibrary';
|
||||||
import type {
|
import type {
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import type { SubsonicGenre } from '@/lib/api/subsonicTypes';
|
import type { SubsonicGenre } from '@/lib/api/subsonicTypes';
|
||||||
import { resolveServerIdForIndexKey } from '../server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
||||||
|
|
||||||
/** Fresh hits skip SQLite entirely. */
|
/** Fresh hits skip SQLite entirely. */
|
||||||
const FRESH_TTL_MS = 60 * 60 * 1000;
|
const FRESH_TTL_MS = 60 * 60 * 1000;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { coverEnsureQueueBacklog } from '../../cover/ensureQueue';
|
import { coverEnsureQueueBacklog } from '@/cover/ensureQueue';
|
||||||
|
|
||||||
/** Target in-flight + queued ensures ≈ workers × multiplier (mirror analysis backfill). */
|
/** Target in-flight + queued ensures ≈ workers × multiplier (mirror analysis backfill). */
|
||||||
export const LIBRARY_COVER_BACKLOG_DEPTH_MULTIPLIER = 3;
|
export const LIBRARY_COVER_BACKLOG_DEPTH_MULTIPLIER = 3;
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
* index when it's enabled and synced enough for trustworthy results.
|
* index when it's enabled and synced enough for trustworthy results.
|
||||||
*/
|
*/
|
||||||
import { libraryGetStatus, type SyncStateDto } from '@/lib/api/library';
|
import { libraryGetStatus, type SyncStateDto } from '@/lib/api/library';
|
||||||
import { useLibraryIndexStore } from '../../store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
|
|
||||||
/** Spec §9.3 — shared by Live Search, Advanced Search, browse, … */
|
/** Spec §9.3 — shared by Live Search, Advanced Search, browse, … */
|
||||||
export function libraryStatusIsReady(status: SyncStateDto): boolean {
|
export function libraryStatusIsReady(status: SyncStateDto): boolean {
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user