mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
refactor(lib): extract clean server + navigation infra to lib/ (decouple-knot files stay in utils/ pending seam)
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
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 { serverIndexKeyFromUrl } from '../utils/server/serverIndexKey';
|
import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
|
||||||
|
|
||||||
export interface AnalysisBackfillQueueStatsDto {
|
export interface AnalysisBackfillQueueStatsDto {
|
||||||
queued: number;
|
queued: number;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { invoke } from '@tauri-apps/api/core';
|
|||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { useThemeStore } from '../store/themeStore';
|
import { useThemeStore } from '../store/themeStore';
|
||||||
import { coverIndexKeyFromRef, coverStorageKeyFromRef } from '../cover/storageKeys';
|
import { coverIndexKeyFromRef, coverStorageKeyFromRef } from '../cover/storageKeys';
|
||||||
import { connectBaseUrlForServer } from '../utils/server/serverEndpoint';
|
import { connectBaseUrlForServer } from '@/lib/server/serverEndpoint';
|
||||||
import { serverIndexKeyForProfile } from '../utils/server/serverIndexKey';
|
import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||||
import { restBaseFromUrl } from '@/lib/api/subsonicClient';
|
import { restBaseFromUrl } from '@/lib/api/subsonicClient';
|
||||||
import type { CoverArtRef, CoverArtTier } from '../cover/types';
|
import type { CoverArtRef, CoverArtTier } from '../cover/types';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import type { ServerProfile } from '@/store/authStoreTypes';
|
|||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { switchActiveServer } from '@/utils/server/switchActiveServer';
|
import { switchActiveServer } from '@/utils/server/switchActiveServer';
|
||||||
import { showToast } from '@/lib/dom/toast';
|
import { showToast } from '@/lib/dom/toast';
|
||||||
import { serverListDisplayLabel } from '@/utils/server/serverDisplayName';
|
import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
status: ConnectionStatus;
|
status: ConnectionStatus;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { libraryGenreTagsInspect, libraryGenreTagsRun } from '@/lib/api/library'
|
|||||||
import { migrationInspect, migrationRun, type ServerIndexMapping } from '@/api/migration';
|
import { migrationInspect, migrationRun, type ServerIndexMapping } from '@/api/migration';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useMigrationStore } from '@/store/migrationStore';
|
import { useMigrationStore } from '@/store/migrationStore';
|
||||||
import { serverIndexKeyFromUrl } from '@/utils/server/serverIndexKey';
|
import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
|
||||||
import { rewriteFrontendStoreKeys } from '@/utils/server/rewriteFrontendStoreKeys';
|
import { rewriteFrontendStoreKeys } from '@/utils/server/rewriteFrontendStoreKeys';
|
||||||
|
|
||||||
const MIGRATION_DONE_FLAG = 'psysonic-server-key-migration-v1';
|
const MIGRATION_DONE_FLAG = 'psysonic-server-key-migration-v1';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import type { EntitySharePayloadV1 } from '../utils/share/shareLink';
|
import type { EntitySharePayloadV1 } from '../utils/share/shareLink';
|
||||||
import { decodeSharePayloadFromText } from '../utils/share/shareLink';
|
import { decodeSharePayloadFromText } from '../utils/share/shareLink';
|
||||||
import { decodeServerMagicStringFromText } from '../utils/server/serverMagicString';
|
import { decodeServerMagicStringFromText } from '@/lib/server/serverMagicString';
|
||||||
import { applySharePastePayload, applySharePasteQueue } from '../utils/share/applySharePaste';
|
import { applySharePastePayload, applySharePasteQueue } from '../utils/share/applySharePaste';
|
||||||
import { shareQueueServerContext } from '../utils/share/shareServerOriginLabel';
|
import { shareQueueServerContext } from '../utils/share/shareServerOriginLabel';
|
||||||
import { showToast } from '@/lib/dom/toast';
|
import { showToast } from '@/lib/dom/toast';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { ndGetSongPath } from '@/lib/api/navidromeAdmin';
|
|||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { useLibraryIndexStore } from '../store/libraryIndexStore';
|
import { useLibraryIndexStore } from '../store/libraryIndexStore';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { copyTextToClipboard } from '../utils/server/serverMagicString';
|
import { copyTextToClipboard } from '@/lib/server/serverMagicString';
|
||||||
import { showToast } from '@/lib/dom/toast';
|
import { showToast } from '@/lib/dom/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';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
} from '@/lib/api/subsonicStreamUrl';
|
} from '@/lib/api/subsonicStreamUrl';
|
||||||
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { connectBaseUrlForServer } from '../utils/server/serverEndpoint';
|
import { connectBaseUrlForServer } from '@/lib/server/serverEndpoint';
|
||||||
import type { CoverArtRef, CoverArtTier } from './types';
|
import type { CoverArtRef, CoverArtTier } from './types';
|
||||||
|
|
||||||
/** Builds ephemeral getCoverArt URL — NOT a cache key */
|
/** Builds ephemeral getCoverArt URL — NOT a cache key */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { buildCoverArtUrlForServer } from '@/lib/api/subsonicStreamUrl';
|
import { buildCoverArtUrlForServer } from '@/lib/api/subsonicStreamUrl';
|
||||||
import { serverShareBaseUrl } from '../../utils/server/serverEndpoint';
|
import { serverShareBaseUrl } from '@/lib/server/serverEndpoint';
|
||||||
import { useAuthStore } from '../../store/authStore';
|
import { useAuthStore } from '../../store/authStore';
|
||||||
import type { CoverArtRef } from '../types';
|
import type { CoverArtRef } from '../types';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { findServerByIdOrIndexKey } from '../utils/server/serverLookup';
|
import { findServerByIdOrIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { COVER_SCOPE_ACTIVE, type CoverServerScope } from './types';
|
import { COVER_SCOPE_ACTIVE, type CoverServerScope } from './types';
|
||||||
|
|
||||||
/** Explicit server bucket for cover disk/IDB — use when entity carries `serverId` (e.g. cross-server favorites). */
|
/** Explicit server bucket for cover disk/IDB — use when entity carries `serverId` (e.g. cross-server favorites). */
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { findServerByIdOrIndexKey } from '../utils/server/serverLookup';
|
import { findServerByIdOrIndexKey } from '@/lib/server/serverLookup';
|
||||||
import {
|
import {
|
||||||
resolveIndexKey,
|
resolveIndexKey,
|
||||||
serverIndexKeyForProfile,
|
serverIndexKeyForProfile,
|
||||||
serverIndexKeyFromUrl,
|
serverIndexKeyFromUrl,
|
||||||
} from '../utils/server/serverIndexKey';
|
} from '@/lib/server/serverIndexKey';
|
||||||
import type { CoverArtRef, CoverArtTier, CoverServerScope } from './types';
|
import type { CoverArtRef, CoverArtTier, CoverServerScope } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ 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 '@/lib/library/coverBackfillWake';
|
import { subscribeLibraryCoverBackfillWake } from '@/lib/library/coverBackfillWake';
|
||||||
import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import { subscribeConnectCache } from '@/utils/server/serverEndpoint';
|
import { subscribeConnectCache } from '@/lib/server/serverEndpoint';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Library cover warm-up — configure session in Rust; full pass runs natively.
|
* Library cover warm-up — configure session in Rust; full pass runs natively.
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
resolvePlaybackCoverScope,
|
resolvePlaybackCoverScope,
|
||||||
} from './ref';
|
} from './ref';
|
||||||
import { coverServerScopeForServerId } from './serverScope';
|
import { coverServerScopeForServerId } from './serverScope';
|
||||||
import { resolveServerIdForIndexKey } from '../utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
||||||
import {
|
import {
|
||||||
resolveAlbumCoverRefFromLibrary,
|
resolveAlbumCoverRefFromLibrary,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
|||||||
import { isAlbumRecentlyAdded } from '@/features/album/utils/albumRecency';
|
import { isAlbumRecentlyAdded } from '@/features/album/utils/albumRecency';
|
||||||
import { albumArtistDisplayName, deriveAlbumArtistRefs } from '@/features/album/utils/deriveAlbumHeaderArtistRefs';
|
import { albumArtistDisplayName, deriveAlbumArtistRefs } from '@/features/album/utils/deriveAlbumHeaderArtistRefs';
|
||||||
import { coverServerScopeForServerId } from '@/cover/serverScope';
|
import { coverServerScopeForServerId } from '@/cover/serverScope';
|
||||||
import { appendServerQuery } from '@/utils/navigation/detailServerScope';
|
import { appendServerQuery } from '@/lib/navigation/detailServerScope';
|
||||||
|
|
||||||
interface AlbumCardProps {
|
interface AlbumCardProps {
|
||||||
album: SubsonicAlbum;
|
album: SubsonicAlbum;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import {
|
|||||||
loadArtistFromLocalPlayback,
|
loadArtistFromLocalPlayback,
|
||||||
offlineLocalBrowseEnabled,
|
offlineLocalBrowseEnabled,
|
||||||
} from '@/features/offline';
|
} from '@/features/offline';
|
||||||
import { readDetailServerId } from '@/utils/navigation/detailServerScope';
|
import { readDetailServerId } from '@/lib/navigation/detailServerScope';
|
||||||
import { libraryIsReady } from '@/lib/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
import {
|
import {
|
||||||
shouldAttemptSubsonicForActiveServer,
|
shouldAttemptSubsonicForActiveServer,
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
|
|||||||
import LosslessModeBanner from '@/ui/LosslessModeBanner';
|
import LosslessModeBanner from '@/ui/LosslessModeBanner';
|
||||||
import { isLosslessSuffix } from '@/lib/library/losslessFormats';
|
import { isLosslessSuffix } from '@/lib/library/losslessFormats';
|
||||||
import { isLosslessMode } from '@/lib/library/losslessMode';
|
import { isLosslessMode } from '@/lib/library/losslessMode';
|
||||||
import { readDetailServerId } from '@/utils/navigation/detailServerScope';
|
import { readDetailServerId } from '@/lib/navigation/detailServerScope';
|
||||||
import { useOfflineBrowseContext } from '@/features/offline';
|
import { useOfflineBrowseContext } from '@/features/offline';
|
||||||
import { offlineActionPolicy } from '@/features/offline';
|
import { offlineActionPolicy } from '@/features/offline';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useArtistCoverRef } from '@/cover/useLibraryCoverRef';
|
|||||||
import { COVER_DENSE_GRID_MIN_CELL_CSS_PX } from '@/cover/layoutSizes';
|
import { COVER_DENSE_GRID_MIN_CELL_CSS_PX } from '@/cover/layoutSizes';
|
||||||
import { useNavigateToArtist } from '@/features/artist/hooks/useNavigateToArtist';
|
import { useNavigateToArtist } from '@/features/artist/hooks/useNavigateToArtist';
|
||||||
import { coverServerScopeForServerId } from '@/cover/serverScope';
|
import { coverServerScopeForServerId } from '@/cover/serverScope';
|
||||||
import { appendServerQuery } from '@/utils/navigation/detailServerScope';
|
import { appendServerQuery } from '@/lib/navigation/detailServerScope';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
artist: SubsonicArtist;
|
artist: SubsonicArtist;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useConnectionStatus } from '@/hooks/useConnectionStatus';
|
|||||||
import { loadArtistFromLibraryIndex } from '@/features/offline';
|
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 '@/lib/navigation/detailServerScope';
|
||||||
import { runLocalArtistLosslessBrowse } from '@/lib/library/browseTextSearch';
|
import { runLocalArtistLosslessBrowse } from '@/lib/library/browseTextSearch';
|
||||||
import { isLosslessSuffix } from '@/lib/library/losslessFormats';
|
import { isLosslessSuffix } from '@/lib/library/losslessFormats';
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import { useWarmGridCovers } from '@/cover/useWarmGridCovers';
|
|||||||
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
|
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
|
||||||
import { LOSSLESS_MODE_QUERY } from '@/lib/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 '@/lib/navigation/detailServerScope';
|
||||||
|
|
||||||
|
|
||||||
export default function ArtistDetail() {
|
export default function ArtistDetail() {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event';
|
|||||||
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
|
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
|
||||||
import { resetAuthStore } from '@/test/helpers/storeReset';
|
import { resetAuthStore } from '@/test/helpers/storeReset';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { encodeServerMagicString } from '@/utils/server/serverMagicString';
|
import { encodeServerMagicString } from '@/lib/server/serverMagicString';
|
||||||
|
|
||||||
vi.mock('@/lib/api/subsonic', () => ({
|
vi.mock('@/lib/api/subsonic', () => ({
|
||||||
pingWithCredentialsForProfile: vi.fn(async () => ({
|
pingWithCredentialsForProfile: vi.fn(async () => ({
|
||||||
@@ -16,7 +16,7 @@ vi.mock('@/lib/api/subsonic', () => ({
|
|||||||
scheduleInstantMixProbeForServer: vi.fn(),
|
scheduleInstantMixProbeForServer: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('@/utils/server/syncServerHttpContext', () => ({
|
vi.mock('@/lib/server/syncServerHttpContext', () => ({
|
||||||
syncServerHttpContextForProfile: vi.fn(async () => undefined),
|
syncServerHttpContextForProfile: vi.fn(async () => undefined),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import {
|
|||||||
DEFAULT_CUSTOM_HEADERS_APPLY_TO,
|
DEFAULT_CUSTOM_HEADERS_APPLY_TO,
|
||||||
serverCustomHeadersFromForm,
|
serverCustomHeadersFromForm,
|
||||||
validateCustomHeaders,
|
validateCustomHeaders,
|
||||||
} from '@/utils/server/serverHttpHeaders';
|
} from '@/lib/server/serverHttpHeaders';
|
||||||
import { syncServerHttpContextForProfile } from '@/utils/server/syncServerHttpContext';
|
import { syncServerHttpContextForProfile } from '@/lib/server/syncServerHttpContext';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import i18n from '@/lib/i18n';
|
import i18n from '@/lib/i18n';
|
||||||
import CustomSelect from '@/ui/CustomSelect';
|
import CustomSelect from '@/ui/CustomSelect';
|
||||||
@@ -19,8 +19,8 @@ import {
|
|||||||
DECODED_PASSWORD_VISUAL_MASK,
|
DECODED_PASSWORD_VISUAL_MASK,
|
||||||
encodeServerMagicString,
|
encodeServerMagicString,
|
||||||
type ServerMagicPayload,
|
type ServerMagicPayload,
|
||||||
} from '@/utils/server/serverMagicString';
|
} from '@/lib/server/serverMagicString';
|
||||||
import { shortHostFromServerUrl, serverListDisplayLabel } from '@/utils/server/serverDisplayName';
|
import { shortHostFromServerUrl, serverListDisplayLabel } from '@/lib/server/serverDisplayName';
|
||||||
|
|
||||||
const PsysonicLogo = () => (
|
const PsysonicLogo = () => (
|
||||||
<img src="/logo-psysonic.png" width="64" height="64" alt="Psysonic" style={{ borderRadius: 18 }} />
|
<img src="/logo-psysonic.png" width="64" height="64" alt="Psysonic" style={{ borderRadius: 18 }} />
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { useThemeStore } from '@/store/themeStore';
|
|||||||
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
||||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||||
import { songToTrack } from '@/lib/media/songToTrack';
|
import { songToTrack } from '@/lib/media/songToTrack';
|
||||||
import { appendServerQuery } from '@/utils/navigation/detailServerScope';
|
import { appendServerQuery } from '@/lib/navigation/detailServerScope';
|
||||||
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
|
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
|
||||||
import { useElementClientHeightById } from '@/lib/hooks/useResizeClientHeight';
|
import { useElementClientHeightById } from '@/lib/hooks/useResizeClientHeight';
|
||||||
import { SORTABLE_COLUMNS } from '@/features/favorites/hooks/useFavoritesSongFiltering';
|
import { SORTABLE_COLUMNS } from '@/features/favorites/hooks/useFavoritesSongFiltering';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
|||||||
import { fetchGenreCatalog, filterGenresWithContent } from '@/features/playback/utils/playback/genreBrowsePlayback';
|
import { fetchGenreCatalog, filterGenresWithContent } from '@/features/playback/utils/playback/genreBrowsePlayback';
|
||||||
import { libraryScopeForServer } from '@/lib/api/subsonicClient';
|
import { libraryScopeForServer } from '@/lib/api/subsonicClient';
|
||||||
import { peekGenreCatalogCache } from '@/lib/library/genreCatalogCountsCache';
|
import { peekGenreCatalogCache } from '@/lib/library/genreCatalogCountsCache';
|
||||||
import { resolveIndexKey } from '@/utils/server/serverIndexKey';
|
import { resolveIndexKey } from '@/lib/server/serverIndexKey';
|
||||||
import { genreColor } from '@/lib/library/genreColor';
|
import { genreColor } from '@/lib/library/genreColor';
|
||||||
|
|
||||||
const SCROLL_KEY = 'genres-scroll';
|
const SCROLL_KEY = 'genres-scroll';
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import {
|
|||||||
import { showToast } from '@/lib/dom/toast';
|
import { showToast } from '@/lib/dom/toast';
|
||||||
import { shuffleArray } from '@/lib/util/shuffleArray';
|
import { shuffleArray } from '@/lib/util/shuffleArray';
|
||||||
import { getMediaDir } from '@/lib/media/mediaDir';
|
import { getMediaDir } from '@/lib/media/mediaDir';
|
||||||
import { canonicalQueueServerKey, resolveIndexKey } from '@/utils/server/serverIndexKey';
|
import { canonicalQueueServerKey, resolveIndexKey } from '@/lib/server/serverIndexKey';
|
||||||
import { reconcileAllLibraryTiersFromDisk } from '@/features/offline/utils/libraryTierReconcile';
|
import { reconcileAllLibraryTiersFromDisk } from '@/features/offline/utils/libraryTierReconcile';
|
||||||
import {
|
import {
|
||||||
inferPinSourcesFromLibraryIndex,
|
inferPinSourcesFromLibraryIndex,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
pendingOfflinePinSongs,
|
pendingOfflinePinSongs,
|
||||||
} from '@/features/offline/utils/offlineLibraryHelpers';
|
} from '@/features/offline/utils/offlineLibraryHelpers';
|
||||||
import { librarySqlServerId } from '@/api/coverCache';
|
import { librarySqlServerId } from '@/api/coverCache';
|
||||||
import { resolveIndexKey, serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import { isSmartPlaylistName } from '@/utils/componentHelpers/playlistDetailHelpers';
|
import { isSmartPlaylistName } from '@/utils/componentHelpers/playlistDetailHelpers';
|
||||||
import {
|
import {
|
||||||
enqueueOfflinePin,
|
enqueueOfflinePin,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { cancelledDownloads, useOfflineJobStore } from '@/features/offline/store
|
|||||||
import { useFavoritesOfflineSyncStore } from '@/features/offline/store/favoritesOfflineSyncStore';
|
import { useFavoritesOfflineSyncStore } from '@/features/offline/store/favoritesOfflineSyncStore';
|
||||||
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
||||||
import { getMediaDir } from '@/lib/media/mediaDir';
|
import { getMediaDir } from '@/lib/media/mediaDir';
|
||||||
import { resolveIndexKey, serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import { FAVORITES_OFFLINE_JOB_ID } from '@/features/offline/utils/favoritesOfflineConstants';
|
import { FAVORITES_OFFLINE_JOB_ID } from '@/features/offline/utils/favoritesOfflineConstants';
|
||||||
import { isActiveServerReachable } from '@/utils/network/activeServerReachability';
|
import { isActiveServerReachable } from '@/utils/network/activeServerReachability';
|
||||||
import { favoritesServerIds } from '@/features/offline/utils/favoritesOfflineBrowse';
|
import { favoritesServerIds } from '@/features/offline/utils/favoritesOfflineBrowse';
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import { useLocalPlaybackStore, type LocalPlaybackEntry, type PinSource } from '
|
|||||||
import { localPlaybackEntryKey } from '@/store/localPlaybackKeys';
|
import { localPlaybackEntryKey } from '@/store/localPlaybackKeys';
|
||||||
import { importLegacyLocalPlayback } from '@/store/localPlaybackMigration';
|
import { importLegacyLocalPlayback } from '@/store/localPlaybackMigration';
|
||||||
import { getMediaDir } from '@/lib/media/mediaDir';
|
import { getMediaDir } from '@/lib/media/mediaDir';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { resolveIndexKey } from '@/utils/server/serverIndexKey';
|
import { resolveIndexKey } from '@/lib/server/serverIndexKey';
|
||||||
|
|
||||||
interface LegacyOfflineMigrationResult {
|
interface LegacyOfflineMigrationResult {
|
||||||
trackId: string;
|
trackId: string;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useAuthStore } from '@/store/authStore';
|
|||||||
import type { LocalPlaybackEntry, PinSource } from '@/store/localPlaybackStore';
|
import type { LocalPlaybackEntry, PinSource } from '@/store/localPlaybackStore';
|
||||||
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
||||||
import { getMediaDir } from '@/lib/media/mediaDir';
|
import { getMediaDir } from '@/lib/media/mediaDir';
|
||||||
import { resolveIndexKey, serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import {
|
import {
|
||||||
entryBelongsToServer,
|
entryBelongsToServer,
|
||||||
findLocalPlaybackEntry,
|
findLocalPlaybackEntry,
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ 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 '@/lib/library/advancedSearchLocal';
|
import { resolveTrackCoverArtId, trackToSong } from '@/lib/library/advancedSearchLocal';
|
||||||
import { canonicalQueueServerKey, resolveIndexKey } from '@/utils/server/serverIndexKey';
|
import { canonicalQueueServerKey, resolveIndexKey } from '@/lib/server/serverIndexKey';
|
||||||
import type { Track } from '@/lib/media/trackTypes';
|
import type { Track } from '@/lib/media/trackTypes';
|
||||||
import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
|
|
||||||
export interface OfflineLibraryCard {
|
export interface OfflineLibraryCard {
|
||||||
serverIndexKey: string;
|
serverIndexKey: string;
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import {
|
|||||||
isActiveServerReachable,
|
isActiveServerReachable,
|
||||||
onActiveServerBecameReachable,
|
onActiveServerBecameReachable,
|
||||||
} from '@/utils/network/activeServerReachability';
|
} from '@/utils/network/activeServerReachability';
|
||||||
import { resolveIndexKey, serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { findLocalPlaybackEntry } from '@/store/localPlaybackResolve';
|
import { findLocalPlaybackEntry } from '@/store/localPlaybackResolve';
|
||||||
import { enqueueOfflinePin } from '@/features/offline/utils/offlinePinQueue';
|
import { enqueueOfflinePin } from '@/features/offline/utils/offlinePinQueue';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useOfflineStore } from '@/features/offline/store/offlineStore';
|
|||||||
import { trackToSong } from '@/lib/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 '@/lib/server/serverLookup';
|
||||||
import { isOfflinePinComplete } from '@/features/offline/utils/offlineLibraryHelpers';
|
import { isOfflinePinComplete } from '@/features/offline/utils/offlineLibraryHelpers';
|
||||||
import { resolveAlbumForServer } from '@/features/offline/utils/offlineMediaResolve';
|
import { resolveAlbumForServer } from '@/features/offline/utils/offlineMediaResolve';
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { buildOrbitShareLink } from '@/features/orbit/utils/orbit';
|
import { buildOrbitShareLink } from '@/features/orbit/utils/orbit';
|
||||||
import { serverShareBaseUrl } from '@/utils/server/serverEndpoint';
|
import { serverShareBaseUrl } from '@/lib/server/serverEndpoint';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
anchorRef: React.RefObject<HTMLElement | null>;
|
anchorRef: React.RefObject<HTMLElement | null>;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
import { randomOrbitSessionName } from '@/features/orbit/utils/orbitNames';
|
import { randomOrbitSessionName } from '@/features/orbit/utils/orbitNames';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||||
import { isLanUrl, serverShareBaseUrl } from '@/utils/server/serverEndpoint';
|
import { isLanUrl, serverShareBaseUrl } from '@/lib/server/serverEndpoint';
|
||||||
import { ORBIT_DEFAULT_MAX_USERS } from '@/features/orbit/api/orbit';
|
import { ORBIT_DEFAULT_MAX_USERS } from '@/features/orbit/api/orbit';
|
||||||
|
|
||||||
interface Props { onClose: () => void; }
|
interface Props { onClose: () => void; }
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import type { QueueItemRef } from '@/lib/media/trackTypes';
|
|||||||
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
|
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
|
||||||
import { resolveQueueTrack } from '@/features/playback/store/queueTrackView';
|
import { resolveQueueTrack } from '@/features/playback/store/queueTrackView';
|
||||||
import { seedQueueResolver } from '@/features/playback/store/queueTrackResolver';
|
import { seedQueueResolver } from '@/features/playback/store/queueTrackResolver';
|
||||||
import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
|
import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
|
||||||
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
||||||
import { queueUndoRestoreAudioEngine } from '@/features/playback/store/queueUndoAudioRestore';
|
import { queueUndoRestoreAudioEngine } from '@/features/playback/store/queueUndoAudioRestore';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ vi.mock('@/lib/api/subsonicPlayQueue', () => ({
|
|||||||
getPlayQueueForServer: (...args: unknown[]) => getPlayQueueForServerMock(...args),
|
getPlayQueueForServer: (...args: unknown[]) => getPlayQueueForServerMock(...args),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('@/utils/server/serverLookup', () => ({
|
vi.mock('@/lib/server/serverLookup', () => ({
|
||||||
resolveServerIdForIndexKey: (id: string) => id,
|
resolveServerIdForIndexKey: (id: string) => id,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { getPlayQueueForServer, type PlayQueueResult } from '@/lib/api/subsonicPlayQueue';
|
import { getPlayQueueForServer, type PlayQueueResult } from '@/lib/api/subsonicPlayQueue';
|
||||||
import { songToTrack } from '@/lib/media/songToTrack';
|
import { songToTrack } from '@/lib/media/songToTrack';
|
||||||
import { bindQueueServerId } from '@/features/playback/utils/playback/playbackServer';
|
import { bindQueueServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { toQueueItemRefs } from '@/features/playback/store/queueItemRef';
|
import { toQueueItemRefs } from '@/features/playback/store/queueItemRef';
|
||||||
import { seedQueueResolver } from '@/features/playback/store/queueTrackResolver';
|
import { seedQueueResolver } from '@/features/playback/store/queueTrackResolver';
|
||||||
import type { Track } from '@/lib/media/trackTypes';
|
import type { Track } from '@/lib/media/trackTypes';
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ const hoisted = vi.hoisted(() => {
|
|||||||
|
|
||||||
vi.mock('@tauri-apps/api/core', () => ({ invoke: hoisted.invokeMock }));
|
vi.mock('@tauri-apps/api/core', () => ({ invoke: hoisted.invokeMock }));
|
||||||
vi.mock('@/lib/api/subsonicStreamUrl', () => ({ buildStreamUrl: hoisted.buildStreamUrlMock }));
|
vi.mock('@/lib/api/subsonicStreamUrl', () => ({ buildStreamUrl: hoisted.buildStreamUrlMock }));
|
||||||
vi.mock('@/utils/server/redactSubsonicUrl', () => ({ redactSubsonicUrlForLog: hoisted.redactMock }));
|
vi.mock('@/lib/server/redactSubsonicUrl', () => ({ redactSubsonicUrlForLog: hoisted.redactMock }));
|
||||||
vi.mock('@/store/authStore', () => ({ useAuthStore: { getState: () => hoisted.auth } }));
|
vi.mock('@/store/authStore', () => ({ useAuthStore: { getState: () => hoisted.auth } }));
|
||||||
vi.mock('@/features/playback/store/playerStore', () => ({
|
vi.mock('@/features/playback/store/playerStore', () => ({
|
||||||
usePlayerStore: {
|
usePlayerStore: {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { buildStreamUrl } from '@/lib/api/subsonicStreamUrl';
|
import { buildStreamUrl } from '@/lib/api/subsonicStreamUrl';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { getPlaybackIndexKey } from '@/features/playback/utils/playback/playbackServer';
|
import { getPlaybackIndexKey } from '@/features/playback/utils/playback/playbackServer';
|
||||||
import { redactSubsonicUrlForLog } from '@/utils/server/redactSubsonicUrl';
|
import { redactSubsonicUrlForLog } from '@/lib/server/redactSubsonicUrl';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||||
import { emitNormalizationDebug } from '@/features/playback/store/normalizationDebug';
|
import { emitNormalizationDebug } from '@/features/playback/store/normalizationDebug';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { findLocalPlaybackUrl } from '@/store/localPlaybackResolve';
|
import { findLocalPlaybackUrl } from '@/store/localPlaybackResolve';
|
||||||
import { resolvePlaybackUrl, type PlaybackSourceKind } from '@/features/playback/utils/playback/resolvePlaybackUrl';
|
import { resolvePlaybackUrl, type PlaybackSourceKind } from '@/features/playback/utils/playback/resolvePlaybackUrl';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { emitPlaybackProgress } from '@/features/playback/store/playbackProgress
|
|||||||
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
|
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
|
||||||
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
|
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
|
||||||
import { toQueueItemRefs } from '@/features/playback/store/queueItemRef';
|
import { toQueueItemRefs } from '@/features/playback/store/queueItemRef';
|
||||||
import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
|
import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
|
||||||
import { readInitialQueueVisibility } from '@/features/playback/store/queueVisibilityStorage';
|
import { readInitialQueueVisibility } from '@/features/playback/store/queueVisibilityStorage';
|
||||||
import { createNetworkLoveActions } from '@/features/playback/store/networkLoveActions';
|
import { createNetworkLoveActions } from '@/features/playback/store/networkLoveActions';
|
||||||
import { createMiscActions } from '@/features/playback/store/miscActions';
|
import { createMiscActions } from '@/features/playback/store/miscActions';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
|
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
|
||||||
import { stampTrackServerId } from '@/lib/media/trackServerScope';
|
import { stampTrackServerId } from '@/lib/media/trackServerScope';
|
||||||
import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
|
import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derive thin `QueueItemRef`s from a `Track[]` queue (thin-state). Per-item
|
* Derive thin `QueueItemRef`s from a `Track[]` queue (thin-state). Per-item
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||||
import type { QueueItemRef } from '@/lib/media/trackTypes';
|
import type { QueueItemRef } from '@/lib/media/trackTypes';
|
||||||
import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
|
import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
|
||||||
import { resolveBatch } from './queueTrackResolver';
|
import { resolveBatch } from './queueTrackResolver';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { getSongForServer } from '@/lib/api/subsonicLibrary';
|
|||||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||||
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
|
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
|
||||||
import { songToTrack } from '@/lib/media/songToTrack';
|
import { songToTrack } from '@/lib/media/songToTrack';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
|
import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
|
||||||
import { trackToSong } from '@/lib/library/advancedSearchLocal';
|
import { trackToSong } from '@/lib/library/advancedSearchLocal';
|
||||||
import { libraryIsReady } from '@/lib/library/libraryReady';
|
import { libraryIsReady } from '@/lib/library/libraryReady';
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import { usePlayerStore } from '@/features/playback/store/playerStore';
|
|||||||
import { switchActiveServer } from '@/utils/server/switchActiveServer';
|
import { switchActiveServer } from '@/utils/server/switchActiveServer';
|
||||||
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
||||||
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
|
import type { QueueItemRef, Track } from '@/lib/media/trackTypes';
|
||||||
import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
import {
|
import {
|
||||||
canonicalQueueServerKey,
|
canonicalQueueServerKey,
|
||||||
resolveIndexKey,
|
resolveIndexKey,
|
||||||
serverIndexKeyForProfile,
|
serverIndexKeyForProfile,
|
||||||
serverIndexKeyFromUrl,
|
serverIndexKeyFromUrl,
|
||||||
} from '@/utils/server/serverIndexKey';
|
} from '@/lib/server/serverIndexKey';
|
||||||
import {
|
import {
|
||||||
activeServerProfileId,
|
activeServerProfileId,
|
||||||
isMultiServerQueue,
|
isMultiServerQueue,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { buildStreamUrlForServer } from '@/lib/api/subsonicStreamUrl';
|
import { buildStreamUrlForServer } from '@/lib/api/subsonicStreamUrl';
|
||||||
import { findLocalPlaybackUrl } from '@/store/localPlaybackResolve';
|
import { findLocalPlaybackUrl } from '@/store/localPlaybackResolve';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { getPlaybackCacheServerKey, getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
import { getPlaybackCacheServerKey, getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||||
|
|
||||||
/** Same resolution order as {@link resolvePlaybackUrl} — for UI hints only. */
|
/** Same resolution order as {@link resolvePlaybackUrl} — for UI hints only. */
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { QueueItemRef } from '@/lib/media/trackTypes';
|
import type { QueueItemRef } from '@/lib/media/trackTypes';
|
||||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { activeServerProfileId, profileIdFromQueueRef } from '@/lib/media/trackServerScope';
|
import { activeServerProfileId, profileIdFromQueueRef } from '@/lib/media/trackServerScope';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { usePlaybackLibraryNavigate } from '@/features/playback/hooks/usePlaybackLibraryNavigate';
|
import { usePlaybackLibraryNavigate } from '@/features/playback/hooks/usePlaybackLibraryNavigate';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { encodeSharePayload } from '@/utils/share/shareLink';
|
import { encodeSharePayload } from '@/utils/share/shareLink';
|
||||||
import { serverShareBaseUrl } from '@/utils/server/serverEndpoint';
|
import { serverShareBaseUrl } from '@/lib/server/serverEndpoint';
|
||||||
import { copyTextToClipboard } from '@/utils/server/serverMagicString';
|
import { copyTextToClipboard } from '@/lib/server/serverMagicString';
|
||||||
import { showToast } from '@/lib/dom/toast';
|
import { showToast } from '@/lib/dom/toast';
|
||||||
import { useThemeStore } from '@/store/themeStore';
|
import { useThemeStore } from '@/store/themeStore';
|
||||||
import { useLyricsStore } from '@/store/lyricsStore';
|
import { useLyricsStore } from '@/store/lyricsStore';
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ import {
|
|||||||
resolveLiveSearchScopeGhost,
|
resolveLiveSearchScopeGhost,
|
||||||
} from '@/features/search/components/liveSearchScope';
|
} from '@/features/search/components/liveSearchScope';
|
||||||
import { useLiveSearchScopeStore } from '@/store/liveSearchScopeStore';
|
import { useLiveSearchScopeStore } from '@/store/liveSearchScopeStore';
|
||||||
import { resolveIndexKey } from '@/utils/server/serverIndexKey';
|
import { resolveIndexKey } from '@/lib/server/serverIndexKey';
|
||||||
|
|
||||||
type LiveSearchSource = 'local' | 'network';
|
type LiveSearchSource = 'local' | 'network';
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import type { ServerProfile } from '@/store/authStoreTypes';
|
|||||||
import { findServerIdForShareUrl } from '@/utils/share/shareLink';
|
import { findServerIdForShareUrl } from '@/utils/share/shareLink';
|
||||||
import { shareServerOriginLabel } from '@/utils/share/shareServerOriginLabel';
|
import { shareServerOriginLabel } from '@/utils/share/shareServerOriginLabel';
|
||||||
import { parseShareSearchText } from '@/utils/share/shareSearch';
|
import { parseShareSearchText } from '@/utils/share/shareSearch';
|
||||||
import { serverIndexKeyFromUrl } from '@/utils/server/serverIndexKey';
|
import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
|
||||||
import { useShareSearchPreview } from '@/features/search/hooks/useShareSearchPreview';
|
import { useShareSearchPreview } from '@/features/search/hooks/useShareSearchPreview';
|
||||||
|
|
||||||
export function useShareSearch(query: string, onSuccess?: () => void) {
|
export function useShareSearch(query: string, onSuccess?: () => void) {
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ import {
|
|||||||
registerAdvancedSearchSessionProvider,
|
registerAdvancedSearchSessionProvider,
|
||||||
resolveAdvancedSearchLeaveSnapshot,
|
resolveAdvancedSearchLeaveSnapshot,
|
||||||
type AdvancedSearchLeaveSnapshot,
|
type AdvancedSearchLeaveSnapshot,
|
||||||
} from '@/utils/navigation/advancedSearchScrollSnapshot';
|
} from '@/lib/navigation/advancedSearchScrollSnapshot';
|
||||||
import { restoreMainViewportScroll } from '@/utils/navigation/restoreMainViewportScroll';
|
import { restoreMainViewportScroll } from '@/lib/navigation/restoreMainViewportScroll';
|
||||||
import {
|
import {
|
||||||
loadMoreLocalSongs,
|
loadMoreLocalSongs,
|
||||||
runNetworkAdvancedTextSearch,
|
runNetworkAdvancedTextSearch,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { screen } from '@testing-library/react';
|
|||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
|
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
|
||||||
import { AddServerForm } from '@/features/settings/components/AddServerForm';
|
import { AddServerForm } from '@/features/settings/components/AddServerForm';
|
||||||
import { encodeServerMagicString } from '@/utils/server/serverMagicString';
|
import { encodeServerMagicString } from '@/lib/server/serverMagicString';
|
||||||
|
|
||||||
// resolve_host_addresses Tauri command — hint-only, must not block save.
|
// resolve_host_addresses Tauri command — hint-only, must not block save.
|
||||||
vi.mock('@/api/network', () => ({
|
vi.mock('@/api/network', () => ({
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ import {
|
|||||||
DEFAULT_CUSTOM_HEADERS_APPLY_TO,
|
DEFAULT_CUSTOM_HEADERS_APPLY_TO,
|
||||||
serverCustomHeadersFromForm,
|
serverCustomHeadersFromForm,
|
||||||
validateCustomHeaders,
|
validateCustomHeaders,
|
||||||
} from '@/utils/server/serverHttpHeaders';
|
} from '@/lib/server/serverHttpHeaders';
|
||||||
import { CustomHttpHeadersEditor } from '@/features/settings/components/CustomHttpHeadersEditor';
|
import { CustomHttpHeadersEditor } from '@/features/settings/components/CustomHttpHeadersEditor';
|
||||||
import {
|
import {
|
||||||
decodeServerMagicString,
|
decodeServerMagicString,
|
||||||
encodeServerMagicString,
|
encodeServerMagicString,
|
||||||
DECODED_PASSWORD_VISUAL_MASK,
|
DECODED_PASSWORD_VISUAL_MASK,
|
||||||
type ServerMagicPayload,
|
type ServerMagicPayload,
|
||||||
} from '@/utils/server/serverMagicString';
|
} from '@/lib/server/serverMagicString';
|
||||||
import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
|
import { shortHostFromServerUrl } from '@/lib/server/serverDisplayName';
|
||||||
import { isLanUrl } from '@/utils/server/serverEndpoint';
|
import { isLanUrl } from '@/lib/server/serverEndpoint';
|
||||||
import { resolveHostAddresses } from '@/api/network';
|
import { resolveHostAddresses } from '@/api/network';
|
||||||
|
|
||||||
type FormState = {
|
type FormState = {
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import {
|
|||||||
} from '@/api/analysis';
|
} from '@/api/analysis';
|
||||||
import { libraryGetTracksBatch, type LibraryTrackDto, type TrackRefDto } from '@/lib/api/library';
|
import { libraryGetTracksBatch, type LibraryTrackDto, type TrackRefDto } from '@/lib/api/library';
|
||||||
import { buildStreamUrlForServer } from '@/lib/api/subsonicStreamUrl';
|
import { buildStreamUrlForServer } from '@/lib/api/subsonicStreamUrl';
|
||||||
import { serverListDisplayLabel } from '@/utils/server/serverDisplayName';
|
import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
|
||||||
import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import { showToast } from '@/lib/dom/toast';
|
import { showToast } from '@/lib/dom/toast';
|
||||||
import {
|
import {
|
||||||
ANALYTICS_STRATEGIES,
|
ANALYTICS_STRATEGIES,
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import {
|
|||||||
libraryCoverProgress,
|
libraryCoverProgress,
|
||||||
} from '@/api/coverCache';
|
} from '@/api/coverCache';
|
||||||
import { clearDiskSrcCacheForServer } from '@/cover/diskSrcCache';
|
import { clearDiskSrcCacheForServer } from '@/cover/diskSrcCache';
|
||||||
import { serverListDisplayLabel } from '@/utils/server/serverDisplayName';
|
import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
|
||||||
import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import { showToast } from '@/lib/dom/toast';
|
import { showToast } from '@/lib/dom/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';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { open as openUrl } from '@tauri-apps/plugin-shell';
|
import { open as openUrl } from '@tauri-apps/plugin-shell';
|
||||||
import { AlertTriangle, CheckCircle2, Info, Lock, LogOut, Pencil, Plus, Power, Server, Sparkles, Wifi, WifiOff } from 'lucide-react';
|
import { AlertTriangle, CheckCircle2, Info, Lock, LogOut, Pencil, Plus, Power, Server, Sparkles, Wifi, WifiOff } from 'lucide-react';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { formatServerSoftware, isNavidromeAudiomuseSoftwareEligible, type InstantMixProbeResult, type SubsonicServerIdentity } from '@/utils/server/subsonicServerIdentity';
|
import { formatServerSoftware, isNavidromeAudiomuseSoftwareEligible, type InstantMixProbeResult, type SubsonicServerIdentity } from '@/lib/server/subsonicServerIdentity';
|
||||||
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';
|
||||||
@@ -16,24 +16,24 @@ import { pingWithCredentialsForProfile, scheduleInstantMixProbeForServer } from
|
|||||||
import {
|
import {
|
||||||
clearServerHttpContext,
|
clearServerHttpContext,
|
||||||
syncServerHttpContextForProfile,
|
syncServerHttpContextForProfile,
|
||||||
} from '@/utils/server/syncServerHttpContext';
|
} from '@/lib/server/syncServerHttpContext';
|
||||||
import { type ServerMagicPayload } from '@/utils/server/serverMagicString';
|
import { type ServerMagicPayload } from '@/lib/server/serverMagicString';
|
||||||
import { ensureConnectUrlResolved, invalidateReachableEndpointCache } from '@/utils/server/serverEndpoint';
|
import { ensureConnectUrlResolved, invalidateReachableEndpointCache } from '@/lib/server/serverEndpoint';
|
||||||
import {
|
import {
|
||||||
verifySameServerEndpoints,
|
verifySameServerEndpoints,
|
||||||
type VerifySameServerResult,
|
type VerifySameServerResult,
|
||||||
} from '@/utils/server/serverFingerprint';
|
} from '@/lib/server/serverFingerprint';
|
||||||
import {
|
import {
|
||||||
indexKeyRemapForUrlChange,
|
indexKeyRemapForUrlChange,
|
||||||
runIndexKeyRemigration,
|
runIndexKeyRemigration,
|
||||||
} from '@/utils/server/serverUrlRemigration';
|
} from '@/lib/server/serverUrlRemigration';
|
||||||
import { useConfirmModalStore } from '@/store/confirmModalStore';
|
import { useConfirmModalStore } from '@/store/confirmModalStore';
|
||||||
import { showToast } from '@/lib/dom/toast';
|
import { showToast } from '@/lib/dom/toast';
|
||||||
import { FEATURE_AUDIOMUSE_SIMILAR_TRACKS } from '@/serverCapabilities/catalog';
|
import { FEATURE_AUDIOMUSE_SIMILAR_TRACKS } from '@/serverCapabilities/catalog';
|
||||||
import { isFeatureActiveForServer, resolveFeatureForServer } from '@/serverCapabilities/storeView';
|
import { isFeatureActiveForServer, resolveFeatureForServer } from '@/serverCapabilities/storeView';
|
||||||
import type { ResolvedCapability } from '@/serverCapabilities/types';
|
import type { ResolvedCapability } from '@/serverCapabilities/types';
|
||||||
import { serverIdentityLabel, serverListDisplayLabel, serverSettingsEntryTitle } from '@/utils/server/serverDisplayName';
|
import { serverIdentityLabel, serverListDisplayLabel, serverSettingsEntryTitle } from '@/lib/server/serverDisplayName';
|
||||||
import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import { switchActiveServer } from '@/utils/server/switchActiveServer';
|
import { switchActiveServer } from '@/utils/server/switchActiveServer';
|
||||||
import { AddServerForm } from '@/features/settings/components/AddServerForm';
|
import { AddServerForm } from '@/features/settings/components/AddServerForm';
|
||||||
import { ServerCapabilityHeaderBadge } from '@/features/settings/components/ServerCapabilityHeaderBadge';
|
import { ServerCapabilityHeaderBadge } from '@/features/settings/components/ServerCapabilityHeaderBadge';
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import {
|
|||||||
copyTextToClipboard,
|
copyTextToClipboard,
|
||||||
encodeServerMagicString,
|
encodeServerMagicString,
|
||||||
magicPayloadAddressFields,
|
magicPayloadAddressFields,
|
||||||
} from '@/utils/server/serverMagicString';
|
} from '@/lib/server/serverMagicString';
|
||||||
import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
|
import { shortHostFromServerUrl } from '@/lib/server/serverDisplayName';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
|
|
||||||
export interface UserFormState {
|
export interface UserFormState {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
copyTextToClipboard,
|
copyTextToClipboard,
|
||||||
encodeServerMagicString,
|
encodeServerMagicString,
|
||||||
magicPayloadAddressFields,
|
magicPayloadAddressFields,
|
||||||
} from '@/utils/server/serverMagicString';
|
} from '@/lib/server/serverMagicString';
|
||||||
import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
|
import { shortHostFromServerUrl } from '@/lib/server/serverDisplayName';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import { showToast } from '@/lib/dom/toast';
|
|||||||
import {
|
import {
|
||||||
copyTextToClipboard,
|
copyTextToClipboard,
|
||||||
encodeServerMagicString,
|
encodeServerMagicString,
|
||||||
} from '@/utils/server/serverMagicString';
|
} from '@/lib/server/serverMagicString';
|
||||||
import { shortHostFromServerUrl } from '@/utils/server/serverDisplayName';
|
import { shortHostFromServerUrl } from '@/lib/server/serverDisplayName';
|
||||||
import type { UserFormState } from '@/features/settings/components/UserForm';
|
import type { UserFormState } from '@/features/settings/components/UserForm';
|
||||||
|
|
||||||
interface UseUserMgmtActionsArgs {
|
interface UseUserMgmtActionsArgs {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { searchSettings, type SettingsSearchHit } from '@/features/settings/comp
|
|||||||
import { type Tab, resolveTab } from '@/features/settings/components/settingsTabs';
|
import { type Tab, resolveTab } from '@/features/settings/components/settingsTabs';
|
||||||
import { UserManagementSection } from '@/features/settings/components/UserManagementSection';
|
import { UserManagementSection } from '@/features/settings/components/UserManagementSection';
|
||||||
import { ndLogin } from '@/lib/api/navidromeAdmin';
|
import { ndLogin } from '@/lib/api/navidromeAdmin';
|
||||||
import { type ServerMagicPayload } from '@/utils/server/serverMagicString';
|
import { type ServerMagicPayload } from '@/lib/server/serverMagicString';
|
||||||
|
|
||||||
|
|
||||||
export default function Settings() {
|
export default function Settings() {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { useAuthStore } from '@/store/authStore';
|
|||||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||||
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
|
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
|
||||||
import { useNavidromeAdminRole } from '@/hooks/useNavidromeAdminRole';
|
import { useNavidromeAdminRole } from '@/hooks/useNavidromeAdminRole';
|
||||||
import { serverListDisplayLabel } from '@/utils/server/serverDisplayName';
|
import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
|
||||||
import { findServerByIdOrIndexKey } from '@/utils/server/serverLookup';
|
import { findServerByIdOrIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { PerfProbeMetricSection } from '@/features/sidebar/components/perfProbe/PerfProbeMetricCard';
|
import { PerfProbeMetricSection } from '@/features/sidebar/components/perfProbe/PerfProbeMetricCard';
|
||||||
import PerfProbeDetailList from '@/features/sidebar/components/perfProbe/PerfProbeDetailList';
|
import PerfProbeDetailList from '@/features/sidebar/components/perfProbe/PerfProbeDetailList';
|
||||||
import PerfProbeStatusBadge, { type PerfProbeBadgeTone } from '@/features/sidebar/components/perfProbe/PerfProbeStatusBadge';
|
import PerfProbeStatusBadge, { type PerfProbeBadgeTone } from '@/features/sidebar/components/perfProbe/PerfProbeStatusBadge';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import type { NavidromeAdminRole } from '@/hooks/useNavidromeAdminRole';
|
import type { NavidromeAdminRole } from '@/hooks/useNavidromeAdminRole';
|
||||||
import { isNavidromeServer, formatServerSoftware } from '@/utils/server/subsonicServerIdentity';
|
import { isNavidromeServer, formatServerSoftware } from '@/lib/server/subsonicServerIdentity';
|
||||||
import { FEATURE_AUDIOMUSE_SIMILAR_TRACKS, OP_SIMILAR_TRACKS } from '@/serverCapabilities/catalog';
|
import { FEATURE_AUDIOMUSE_SIMILAR_TRACKS, OP_SIMILAR_TRACKS } from '@/serverCapabilities/catalog';
|
||||||
import {
|
import {
|
||||||
isFeatureActiveForServer,
|
isFeatureActiveForServer,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useAuthStore } from '@/store/authStore';
|
|||||||
import {
|
import {
|
||||||
invalidateReachableEndpointCache,
|
invalidateReachableEndpointCache,
|
||||||
type PickReachableResult,
|
type PickReachableResult,
|
||||||
} from '@/utils/server/serverEndpoint';
|
} from '@/lib/server/serverEndpoint';
|
||||||
|
|
||||||
vi.mock('@/lib/api/subsonic', () => ({
|
vi.mock('@/lib/api/subsonic', () => ({
|
||||||
pingWithCredentials: vi.fn(),
|
pingWithCredentials: vi.fn(),
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { useState, useEffect, useCallback, useRef, useMemo, useSyncExternalStore } from 'react';
|
import { useState, useEffect, useCallback, useRef, useMemo, useSyncExternalStore } from 'react';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { scheduleInstantMixProbeForServer } from '@/lib/api/subsonic';
|
import { scheduleInstantMixProbeForServer } from '@/lib/api/subsonic';
|
||||||
import { serverListDisplayLabel } from '../utils/server/serverDisplayName';
|
import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
|
||||||
import {
|
import {
|
||||||
ensureConnectUrlResolved,
|
ensureConnectUrlResolved,
|
||||||
invalidateReachableEndpointCache,
|
invalidateReachableEndpointCache,
|
||||||
isLanUrl,
|
isLanUrl,
|
||||||
type ServerEndpointKind,
|
type ServerEndpointKind,
|
||||||
} from '../utils/server/serverEndpoint';
|
} from '@/lib/server/serverEndpoint';
|
||||||
import {
|
import {
|
||||||
getConnectionStatus,
|
getConnectionStatus,
|
||||||
setActiveServerReachable,
|
setActiveServerReachable,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { ndLogin } from '@/lib/api/navidromeAdmin';
|
import { ndLogin } from '@/lib/api/navidromeAdmin';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { isNavidromeServer } from '../utils/server/subsonicServerIdentity';
|
import { isNavidromeServer } from '@/lib/server/subsonicServerIdentity';
|
||||||
|
|
||||||
export type NavidromeAdminRole = 'idle' | 'checking' | 'admin' | 'user' | 'na' | 'error';
|
export type NavidromeAdminRole = 'idle' | 'checking' | 'admin' | 'user' | 'na' | 'error';
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { usePlayerStore } from '@/features/playback/store/playerStore';
|
|||||||
import { collectPlaybackMiddlePriorityTrackIds } from '@/features/playback/store/loudnessBackfillWindow';
|
import { collectPlaybackMiddlePriorityTrackIds } from '@/features/playback/store/loudnessBackfillWindow';
|
||||||
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||||
import { analysisSetPlaybackPriorityHints } from '../api/analysis';
|
import { analysisSetPlaybackPriorityHints } from '../api/analysis';
|
||||||
import { serverIndexKeyFromUrl } from '../utils/server/serverIndexKey';
|
import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
|
||||||
import { hotCacheFrontendDebug } from './helpers';
|
import { hotCacheFrontendDebug } from './helpers';
|
||||||
|
|
||||||
let analysisPruneTimer: ReturnType<typeof setTimeout> | null = null;
|
let analysisPruneTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
|
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { serverIndexKeyFromUrl } from '@/utils/server/serverIndexKey';
|
import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
|
|
||||||
// ── DTO mirrors (camelCase, matching the Rust `#[serde(rename_all = "camelCase")]`) ─
|
// ── DTO mirrors (camelCase, matching the Rust `#[serde(rename_all = "camelCase")]`) ─
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ vi.mock('@/lib/api/subsonicOpenSubsonic', () => ({
|
|||||||
import { fetchOpenSubsonicExtensionsWithCredentials } from '@/lib/api/subsonicOpenSubsonic';
|
import { fetchOpenSubsonicExtensionsWithCredentials } from '@/lib/api/subsonicOpenSubsonic';
|
||||||
import { scheduleInstantMixProbeForServer } from '@/lib/api/subsonic';
|
import { scheduleInstantMixProbeForServer } from '@/lib/api/subsonic';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import type { SubsonicServerIdentity } from '@/utils/server/subsonicServerIdentity';
|
import type { SubsonicServerIdentity } from '@/lib/server/subsonicServerIdentity';
|
||||||
|
|
||||||
const fetchMock = vi.mocked(fetchOpenSubsonicExtensionsWithCredentials);
|
const fetchMock = vi.mocked(fetchOpenSubsonicExtensionsWithCredentials);
|
||||||
const SID = 'srv-probe';
|
const SID = 'srv-probe';
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import axios from 'axios';
|
|||||||
import md5 from 'md5';
|
import md5 from 'md5';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import type { ServerProfile } from '@/store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
import { headersForServerRequest } from '@/utils/server/serverHttpHeaders';
|
import { headersForServerRequest } from '@/lib/server/serverHttpHeaders';
|
||||||
import { findServerByIdOrIndexKey } from '@/utils/server/serverLookup';
|
import { findServerByIdOrIndexKey } from '@/lib/server/serverLookup';
|
||||||
import {
|
import {
|
||||||
type InstantMixProbeResult,
|
type InstantMixProbeResult,
|
||||||
type SubsonicServerIdentity,
|
type SubsonicServerIdentity,
|
||||||
} from '@/utils/server/subsonicServerIdentity';
|
} from '@/lib/server/subsonicServerIdentity';
|
||||||
import { fetchOpenSubsonicExtensionsWithCredentials } from '@/lib/api/subsonicOpenSubsonic';
|
import { fetchOpenSubsonicExtensionsWithCredentials } from '@/lib/api/subsonicOpenSubsonic';
|
||||||
import { buildCapabilityContext } from '@/serverCapabilities/context';
|
import { buildCapabilityContext } from '@/serverCapabilities/context';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import md5 from 'md5';
|
|||||||
import { version } from '@/../package.json';
|
import { version } from '@/../package.json';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import type { ServerProfile } from '@/store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
import { connectBaseUrlForServer } from '@/utils/server/serverEndpoint';
|
import { connectBaseUrlForServer } from '@/lib/server/serverEndpoint';
|
||||||
import { headersForServerRequest } from '@/utils/server/serverHttpHeaders';
|
import { headersForServerRequest } from '@/lib/server/serverHttpHeaders';
|
||||||
import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
|
|
||||||
export const SUBSONIC_CLIENT = `psysonic/${version}`;
|
export const SUBSONIC_CLIENT = `psysonic/${version}`;
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { coverStorageKeyFromRef } from '@/cover/storageKeys';
|
|||||||
import { coverEntryToRef, resolveAlbumCoverEntry } from '@/cover/resolveEntry';
|
import { coverEntryToRef, resolveAlbumCoverEntry } from '@/cover/resolveEntry';
|
||||||
import type { CoverArtTier } from '@/cover/types';
|
import type { CoverArtTier } from '@/cover/types';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { connectBaseUrlForServer } from '@/utils/server/serverEndpoint';
|
import { connectBaseUrlForServer } from '@/lib/server/serverEndpoint';
|
||||||
import { findServerByIdOrIndexKey } from '@/utils/server/serverLookup';
|
import { findServerByIdOrIndexKey } from '@/lib/server/serverLookup';
|
||||||
import { restBaseFromUrl, SUBSONIC_CLIENT, secureRandomSalt } from '@/lib/api/subsonicClient';
|
import { restBaseFromUrl, SUBSONIC_CLIENT, secureRandomSalt } from '@/lib/api/subsonicClient';
|
||||||
|
|
||||||
function coverArtQueryParams(username: string, password: string, id: string, size: number): URLSearchParams {
|
function coverArtQueryParams(username: string, password: string, id: string, size: number): URLSearchParams {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SubsonicServerIdentity } from '@/utils/server/subsonicServerIdentity';
|
import type { SubsonicServerIdentity } from '@/lib/server/subsonicServerIdentity';
|
||||||
|
|
||||||
/** OpenSubsonic `ItemGenre` on songs/albums (atomic genres from the server). */
|
/** OpenSubsonic `ItemGenre` on songs/albums (atomic genres from the server). */
|
||||||
export interface SubsonicItemGenre {
|
export interface SubsonicItemGenre {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { SubsonicGenre } from '@/lib/api/subsonicTypes';
|
import type { SubsonicGenre } from '@/lib/api/subsonicTypes';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/lib/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;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ 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 '@/lib/library/analysisStrategy';
|
import { DEFAULT_ADVANCED_PARALLELISM } from '@/lib/library/analysisStrategy';
|
||||||
import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
|
|
||||||
const DISABLED_CONFIGURE = {
|
const DISABLED_CONFIGURE = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
import { showToast } from '@/lib/dom/toast';
|
import { showToast } from '@/lib/dom/toast';
|
||||||
import { resolveIndexKey, serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import {
|
import {
|
||||||
libraryGetStatus,
|
libraryGetStatus,
|
||||||
librarySyncCancel,
|
librarySyncCancel,
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import { enqueueLibrarySync, queueInitialSyncIfNeeded } from './librarySyncQueue
|
|||||||
import type { ServerProfile } from '@/store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
import { ensureConnectUrlResolved } from '@/utils/server/serverEndpoint';
|
import { ensureConnectUrlResolved } from '@/lib/server/serverEndpoint';
|
||||||
import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import { syncServerHttpContextForProfile } from '@/utils/server/syncServerHttpContext';
|
import { syncServerHttpContextForProfile } from '@/lib/server/syncServerHttpContext';
|
||||||
import { libraryDevEnabled, logLibraryStatus, logLibrarySync, timed } from './libraryDevLog';
|
import { libraryDevEnabled, logLibraryStatus, logLibrarySync, timed } from './libraryDevLog';
|
||||||
|
|
||||||
export type BindServerResult = 'bound' | 'offline' | 'error';
|
export type BindServerResult = 'bound' | 'offline' | 'error';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Track, QueueItemRef } from '@/lib/media/trackTypes';
|
import type { Track, QueueItemRef } from '@/lib/media/trackTypes';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
|
import { canonicalQueueServerKey } from '@/lib/server/serverIndexKey';
|
||||||
import { resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
import { resolveServerIdForIndexKey } from '@/lib/server/serverLookup';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pure server-scope helpers for the shared media model: stamp the owning server
|
* Pure server-scope helpers for the shared media model: stamp the owning server
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
// @vitest-environment jsdom
|
// @vitest-environment jsdom
|
||||||
import { afterEach, describe, expect, it } from 'vitest';
|
import { afterEach, describe, expect, it } from 'vitest';
|
||||||
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../../constants/appScroll';
|
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
|
||||||
import {
|
import {
|
||||||
clearAdvancedSearchLeaveSnapshots,
|
clearAdvancedSearchLeaveSnapshots,
|
||||||
peekPersistedAdvancedSearchLeaveSnapshot,
|
peekPersistedAdvancedSearchLeaveSnapshot,
|
||||||
@@ -9,8 +9,8 @@ import {
|
|||||||
registerAdvancedSearchSessionProvider,
|
registerAdvancedSearchSessionProvider,
|
||||||
resolveAdvancedSearchLeaveSnapshot,
|
resolveAdvancedSearchLeaveSnapshot,
|
||||||
saveAdvancedSearchLeaveSnapshot,
|
saveAdvancedSearchLeaveSnapshot,
|
||||||
} from './advancedSearchScrollSnapshot';
|
} from '@/lib/navigation/advancedSearchScrollSnapshot';
|
||||||
import { useAdvancedSearchSessionStore } from '../../store/advancedSearchSessionStore';
|
import { useAdvancedSearchSessionStore } from '@/store/advancedSearchSessionStore';
|
||||||
|
|
||||||
describe('advancedSearchScrollSnapshot', () => {
|
describe('advancedSearchScrollSnapshot', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../../constants/appScroll';
|
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
|
||||||
import {
|
import {
|
||||||
useAdvancedSearchSessionStore,
|
useAdvancedSearchSessionStore,
|
||||||
type AdvancedSearchSessionStash,
|
type AdvancedSearchSessionStash,
|
||||||
} from '../../store/advancedSearchSessionStore';
|
} from '@/store/advancedSearchSessionStore';
|
||||||
|
|
||||||
export type AdvancedSearchLeaveSnapshot = {
|
export type AdvancedSearchLeaveSnapshot = {
|
||||||
scrollTop: number;
|
scrollTop: number;
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import { beforeEach, describe, expect, it } from 'vitest';
|
import { beforeEach, describe, expect, it } from 'vitest';
|
||||||
import { useAuthStore } from '../../store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { appendServerQuery, readDetailServerId } from './detailServerScope';
|
import { appendServerQuery, readDetailServerId } from '@/lib/navigation/detailServerScope';
|
||||||
|
|
||||||
describe('detailServerScope', () => {
|
describe('detailServerScope', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useAuthStore } from '../../store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
|
|
||||||
/** Resolve `?server=` on album/artist detail routes; falls back when absent or unknown. */
|
/** Resolve `?server=` on album/artist detail routes; falls back when absent or unknown. */
|
||||||
export function readDetailServerId(
|
export function readDetailServerId(
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../../constants/appScroll';
|
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
|
||||||
|
|
||||||
const SAFETY_TIMEOUT_MS = 3000;
|
const SAFETY_TIMEOUT_MS = 3000;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ServerProfile } from '../../store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
|
|
||||||
/** Normalized Subsonic root URL for a server profile (same shape as `getBaseUrl`). */
|
/** Normalized Subsonic root URL for a server profile (same shape as `getBaseUrl`). */
|
||||||
export function serverProfileBaseUrl(server: Pick<ServerProfile, 'url'>): string {
|
export function serverProfileBaseUrl(server: Pick<ServerProfile, 'url'>): string {
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import type { ServerProfile } from '../../store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import { serverIdentityLabel, serverListDisplayLabel, serverSettingsEntryTitle, shortHostFromServerUrl } from './serverDisplayName';
|
import { serverIdentityLabel, serverListDisplayLabel, serverSettingsEntryTitle, shortHostFromServerUrl } from '@/lib/server/serverDisplayName';
|
||||||
|
|
||||||
function srv(p: Partial<ServerProfile> & Pick<ServerProfile, 'id'>): ServerProfile {
|
function srv(p: Partial<ServerProfile> & Pick<ServerProfile, 'id'>): ServerProfile {
|
||||||
return {
|
return {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ServerProfile } from '../../store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
/** Host (+ port) from a server base URL, e.g. `https://music.one.com/foo` → `music.one.com`. */
|
/** Host (+ port) from a server base URL, e.g. `https://music.one.com/foo` → `music.one.com`. */
|
||||||
export function shortHostFromServerUrl(urlRaw?: string | null): string {
|
export function shortHostFromServerUrl(urlRaw?: string | null): string {
|
||||||
const t = typeof urlRaw === 'string' ? urlRaw.trim() : '';
|
const t = typeof urlRaw === 'string' ? urlRaw.trim() : '';
|
||||||
@@ -17,8 +17,8 @@ import {
|
|||||||
serverAddressEndpoints,
|
serverAddressEndpoints,
|
||||||
serverShareBaseUrl,
|
serverShareBaseUrl,
|
||||||
subscribeConnectCache,
|
subscribeConnectCache,
|
||||||
} from './serverEndpoint';
|
} from '@/lib/server/serverEndpoint';
|
||||||
import type { ServerProfile } from '../../store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
|
|
||||||
function makeProfile(overrides: Partial<ServerProfile> = {}): ServerProfile {
|
function makeProfile(overrides: Partial<ServerProfile> = {}): ServerProfile {
|
||||||
return {
|
return {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { pingWithCredentialsForProfile } from '@/lib/api/subsonic';
|
import { pingWithCredentialsForProfile } from '@/lib/api/subsonic';
|
||||||
import type { PingWithCredentialsResult } from '@/lib/api/subsonicTypes';
|
import type { PingWithCredentialsResult } from '@/lib/api/subsonicTypes';
|
||||||
import type { ServerProfile } from '../../store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
import { serverProfileBaseUrl } from './serverBaseUrl';
|
import { serverProfileBaseUrl } from '@/lib/server/serverBaseUrl';
|
||||||
|
|
||||||
export type ServerEndpointKind = 'local' | 'public';
|
export type ServerEndpointKind = 'local' | 'public';
|
||||||
|
|
||||||
+1
-1
@@ -16,7 +16,7 @@ import {
|
|||||||
fetchServerFingerprint,
|
fetchServerFingerprint,
|
||||||
verifySameServerEndpoints,
|
verifySameServerEndpoints,
|
||||||
type ServerFingerprint,
|
type ServerFingerprint,
|
||||||
} from './serverFingerprint';
|
} from '@/lib/server/serverFingerprint';
|
||||||
|
|
||||||
function makeFingerprint(overrides: Partial<ServerFingerprint> = {}): ServerFingerprint {
|
function makeFingerprint(overrides: Partial<ServerFingerprint> = {}): ServerFingerprint {
|
||||||
return {
|
return {
|
||||||
@@ -22,9 +22,9 @@ import {
|
|||||||
SUBSONIC_CLIENT,
|
SUBSONIC_CLIENT,
|
||||||
type ServerHttpHeaderProfile,
|
type ServerHttpHeaderProfile,
|
||||||
} from '@/lib/api/subsonicClient';
|
} from '@/lib/api/subsonicClient';
|
||||||
import type { ServerProfile } from '../../store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
import { allNormalizedAddresses } from './serverEndpoint';
|
import { allNormalizedAddresses } from '@/lib/server/serverEndpoint';
|
||||||
import { headersForServerRequest } from './serverHttpHeaders';
|
import { headersForServerRequest } from '@/lib/server/serverHttpHeaders';
|
||||||
|
|
||||||
export type ServerFingerprint = {
|
export type ServerFingerprint = {
|
||||||
ping: {
|
ping: {
|
||||||
+1
-1
@@ -4,7 +4,7 @@ import {
|
|||||||
requestBaseUrlFromHttpUrl,
|
requestBaseUrlFromHttpUrl,
|
||||||
serverCustomHeadersFromForm,
|
serverCustomHeadersFromForm,
|
||||||
validateCustomHeaders,
|
validateCustomHeaders,
|
||||||
} from './serverHttpHeaders';
|
} from '@/lib/server/serverHttpHeaders';
|
||||||
|
|
||||||
describe('requestBaseUrlFromHttpUrl', () => {
|
describe('requestBaseUrlFromHttpUrl', () => {
|
||||||
it('strips /rest/ path and query from stream URLs', () => {
|
it('strips /rest/ path and query from stream URLs', () => {
|
||||||
@@ -4,9 +4,9 @@ import type {
|
|||||||
CustomHeadersFieldError,
|
CustomHeadersFieldError,
|
||||||
CustomHeadersValidationResult,
|
CustomHeadersValidationResult,
|
||||||
ServerProfile,
|
ServerProfile,
|
||||||
} from '../../store/authStoreTypes';
|
} from '@/store/authStoreTypes';
|
||||||
import { serverIndexKeyForProfile } from './serverIndexKey';
|
import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||||
import { normalizeServerBaseUrl, serverAddressEndpoints, type ServerEndpointKind } from './serverEndpoint';
|
import { normalizeServerBaseUrl, serverAddressEndpoints, type ServerEndpointKind } from '@/lib/server/serverEndpoint';
|
||||||
|
|
||||||
export const DEFAULT_CUSTOM_HEADERS_APPLY_TO: CustomHeadersApplyTo = 'public';
|
export const DEFAULT_CUSTOM_HEADERS_APPLY_TO: CustomHeadersApplyTo = 'public';
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { ServerProfile } from '../../store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
import { useAuthStore } from '../../store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { serverProfileBaseUrl } from './serverBaseUrl';
|
import { serverProfileBaseUrl } from '@/lib/server/serverBaseUrl';
|
||||||
|
|
||||||
/** Stable index key derived from a server URL (host + optional path, no scheme). */
|
/** Stable index key derived from a server URL (host + optional path, no scheme). */
|
||||||
export function serverIndexKeyFromUrl(urlRaw: string): string {
|
export function serverIndexKeyFromUrl(urlRaw: string): string {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useAuthStore } from '../../store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { rewriteFrontendStoreKeys } from './rewriteFrontendStoreKeys';
|
import { rewriteFrontendStoreKeys } from '@/utils/server/rewriteFrontendStoreKeys';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Legacy compatibility shim. The migration lifecycle now runs through
|
* Legacy compatibility shim. The migration lifecycle now runs through
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useAuthStore } from '../../store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import type { ServerProfile } from '../../store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
import { serverIndexKeyForProfile, serverIndexKeyFromUrl } from './serverIndexKey';
|
import { serverIndexKeyForProfile, serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
|
||||||
|
|
||||||
export function findServerByIdOrIndexKey(serverIdOrKey: string): ServerProfile | undefined {
|
export function findServerByIdOrIndexKey(serverIdOrKey: string): ServerProfile | undefined {
|
||||||
const servers = useAuthStore.getState().servers;
|
const servers = useAuthStore.getState().servers;
|
||||||
+1
-1
@@ -6,7 +6,7 @@ import {
|
|||||||
decodeServerMagicString,
|
decodeServerMagicString,
|
||||||
decodeServerMagicStringFromText,
|
decodeServerMagicStringFromText,
|
||||||
encodeServerMagicString,
|
encodeServerMagicString,
|
||||||
} from './serverMagicString';
|
} from '@/lib/server/serverMagicString';
|
||||||
|
|
||||||
describe('DECODED_PASSWORD_VISUAL_MASK', () => {
|
describe('DECODED_PASSWORD_VISUAL_MASK', () => {
|
||||||
it('has fixed length independent of real passwords', () => {
|
it('has fixed length independent of real passwords', () => {
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { ServerProfile } from '../../store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
import { normalizeServerBaseUrl, serverShareBaseUrl } from './serverEndpoint';
|
import { normalizeServerBaseUrl, serverShareBaseUrl } from '@/lib/server/serverEndpoint';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefix for server invite strings (Subsonic credentials). Same family as library
|
* Prefix for server invite strings (Subsonic credentials). Same family as library
|
||||||
+1
-1
@@ -11,7 +11,7 @@ import { invoke } from '@tauri-apps/api/core';
|
|||||||
import {
|
import {
|
||||||
indexKeyRemapForUrlChange,
|
indexKeyRemapForUrlChange,
|
||||||
runIndexKeyRemigration,
|
runIndexKeyRemigration,
|
||||||
} from './serverUrlRemigration';
|
} from '@/lib/server/serverUrlRemigration';
|
||||||
|
|
||||||
function inspectStub(needs = true) {
|
function inspectStub(needs = true) {
|
||||||
return {
|
return {
|
||||||
@@ -28,15 +28,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import type { ServerProfile } from '../../store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
import {
|
import {
|
||||||
migrationInspect,
|
migrationInspect,
|
||||||
migrationRun,
|
migrationRun,
|
||||||
type MigrationInspectReport,
|
type MigrationInspectReport,
|
||||||
type MigrationRunResult,
|
type MigrationRunResult,
|
||||||
} from '../../api/migration';
|
} from '@/api/migration';
|
||||||
import { rewriteFrontendStoreKeysForRemap } from './rewriteFrontendStoreKeys';
|
import { rewriteFrontendStoreKeysForRemap } from '@/utils/server/rewriteFrontendStoreKeys';
|
||||||
import { serverIndexKeyFromUrl } from './serverIndexKey';
|
import { serverIndexKeyFromUrl } from '@/lib/server/serverIndexKey';
|
||||||
|
|
||||||
export type IndexKeyRemap = { oldKey: string; newKey: string };
|
export type IndexKeyRemap = { oldKey: string; newKey: string };
|
||||||
|
|
||||||
+1
-1
@@ -4,7 +4,7 @@ import {
|
|||||||
isNavidromeAudiomuseSoftwareEligible,
|
isNavidromeAudiomuseSoftwareEligible,
|
||||||
isNavidromeServer,
|
isNavidromeServer,
|
||||||
parseLeadingSemver,
|
parseLeadingSemver,
|
||||||
} from './subsonicServerIdentity';
|
} from '@/lib/server/subsonicServerIdentity';
|
||||||
|
|
||||||
describe('parseLeadingSemver', () => {
|
describe('parseLeadingSemver', () => {
|
||||||
it('parses Navidrome-style version strings', () => {
|
it('parses Navidrome-style version strings', () => {
|
||||||
+1
-1
@@ -4,7 +4,7 @@ const invokeMock = vi.hoisted(() => vi.fn(async () => undefined));
|
|||||||
|
|
||||||
vi.mock('@tauri-apps/api/core', () => ({ invoke: invokeMock }));
|
vi.mock('@tauri-apps/api/core', () => ({ invoke: invokeMock }));
|
||||||
|
|
||||||
import { syncServerHttpContextForProfile } from './syncServerHttpContext';
|
import { syncServerHttpContextForProfile } from '@/lib/server/syncServerHttpContext';
|
||||||
|
|
||||||
const server = {
|
const server = {
|
||||||
id: 'app-uuid-1',
|
id: 'app-uuid-1',
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user