refactor(api): co-locate server-protocol clients into lib/api

Move the Subsonic + Navidrome protocol clients and the library IPC facade
(subsonic*, navidrome*, library.ts + tests) from src/api/ into src/lib/api/,
the feature-free infra layer (plan M4, decision §10.3: shared client core → lib/).
Pure move: deep-path import specifiers @/api/* -> @/lib/api/* across ~150
consumers; no behaviour change. Domain REST (lyrics/events) and cover/analysis
infra stay in src/api/ pending their own M4 placement.

tsc 0, lint 0/0, full suite 319 files / 2353 tests green.
This commit is contained in:
Psychotoxical
2026-06-30 07:53:25 +02:00
parent 429e632598
commit 7e9cb60763
392 changed files with 652 additions and 652 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
import { api } from '@/api/subsonicClient';
import type { AlbumInfo } from '@/api/subsonicTypes';
import { api } from '@/lib/api/subsonicClient';
import type { AlbumInfo } from '@/lib/api/subsonicTypes';
export async function getAlbumInfo2(albumId: string): Promise<AlbumInfo | null> {
try {
+1 -1
View File
@@ -1,4 +1,4 @@
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import React, { memo, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import { useNavigateToAlbum } from '@/features/album/hooks/useNavigateToAlbum';
@@ -3,7 +3,7 @@ import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
import AlbumHeader from '@/features/album/components/AlbumHeader';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
const navigate = vi.fn();
@@ -1,4 +1,4 @@
import type { EntityRatingSupportLevel, SubsonicItemGenre, SubsonicOpenArtistRef, SubsonicSong } from '@/api/subsonicTypes';
import type { EntityRatingSupportLevel, SubsonicItemGenre, SubsonicOpenArtistRef, SubsonicSong } from '@/lib/api/subsonicTypes';
import { type CSSProperties, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { useNavigate } from 'react-router-dom';
+1 -1
View File
@@ -1,4 +1,4 @@
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import React, { useRef, useState, useEffect, useLayoutEffect, useMemo } from 'react';
import AlbumCard from '@/features/album/components/AlbumCard';
import { ChevronLeft, ChevronRight, ArrowRight } from 'lucide-react';
@@ -1,4 +1,4 @@
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import type { Track } from '@/store/playerStoreTypes';
import React, { useMemo, useState, useEffect } from 'react';
import { useTracklistColumns } from '@/utils/useTracklistColumns';
@@ -1,6 +1,6 @@
import React from 'react';
import { AudioLines } from 'lucide-react';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import type { Track } from '@/store/playerStoreTypes';
import { songToTrack } from '@/utils/playback/songToTrack';
import { formatLongDuration } from '@/utils/format/formatDuration';
@@ -1,7 +1,7 @@
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ndListLosslessAlbumsPage } from '@/api/navidromeBrowse';
import { ndListLosslessAlbumsPage } from '@/lib/api/navidromeBrowse';
import AlbumRow from '@/features/album/components/AlbumRow';
import { useAuthStore } from '@/store/authStore';
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
+1 -1
View File
@@ -3,7 +3,7 @@ import { AudioLines, ChevronRight, Heart, Play, Square } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import type { ColDef } from '@/utils/useTracklistColumns';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import type { Track } from '@/store/playerStoreTypes';
import { songToTrack } from '@/utils/playback/songToTrack';
import { useSelectionStore } from '@/store/selectionStore';
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
// Keep pagination termination rules aligned with the hook implementation.
function resolveHasMoreAfterPage(
@@ -5,7 +5,7 @@ import {
useRef,
useState,
} from 'react';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import {
coverTrafficBeginGridPagination,
coverTrafficEndGridPagination,
@@ -25,7 +25,7 @@ import {
type AlbumBrowseQuery,
type GenreFilterOption,
} from '@/utils/library/albumBrowseLoad';
import { libraryScopeForServer } from '@/api/subsonicClient';
import { libraryScopeForServer } from '@/lib/api/subsonicClient';
import {
ALBUM_YEAR_FILTER_DEBOUNCE_MS,
resolveAlbumYearBounds,
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import { useAuthStore } from '@/store/authStore';
import {
loadAlbumFromLibraryIndex,
@@ -1,5 +1,5 @@
import { useMemo, useState } from 'react';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
export type AlbumSortKey = 'natural' | 'title' | 'artist' | 'album' | 'favorite' | 'rating' | 'duration' | 'playCount' | 'lastPlayed' | 'bpm';
@@ -1,6 +1,6 @@
import { useEffect, useRef, useState, type RefObject } from 'react';
import { useLocation, useNavigationType, type NavigationType } from 'react-router-dom';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import {
DEFAULT_ALBUM_BROWSE_RETURN_FILTERS,
type AlbumBrowseReturnFilters,
@@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useRef } from 'react';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { useSelectionStore } from '@/store/selectionStore';
import { useDragDrop } from '@/contexts/DragDropContext';
import { songToTrack } from '@/utils/playback/songToTrack';
@@ -1,4 +1,4 @@
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import { useEffect, useRef, useState } from 'react';
import {
BROWSE_TEXT_DEBOUNCE_NETWORK_MS,
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import { dedupeById } from '@/utils/dedupeById';
import type { AlbumBrowseSort } from '@/utils/library/albumBrowseSort';
import {
+4 -4
View File
@@ -1,9 +1,9 @@
import { buildDownloadUrl } from '@/api/subsonicStreamUrl';
import { setRating, star, unstar } from '@/api/subsonicStarRating';
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
import { setRating, star, unstar } from '@/lib/api/subsonicStarRating';
import { queueSongStar, queueSongRating } from '@/store/pendingStarSync';
import { getAlbumForServer } from '@/api/subsonicLibrary';
import { getAlbumForServer } from '@/lib/api/subsonicLibrary';
import { getArtistInfo } from '@/features/artist';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { songToTrack } from '@/utils/playback/songToTrack';
import { shuffleArray } from '@/utils/playback/shuffleArray';
import React, { useEffect, useState, useCallback, useMemo } from 'react';
+1 -1
View File
@@ -1,4 +1,4 @@
import { buildDownloadUrl } from '@/api/subsonicStreamUrl';
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
import { resolveAlbum } from '@/features/offline';
import { songToTrack } from '@/utils/playback/songToTrack';
import { useState, useEffect, useLayoutEffect, useRef, useMemo } from 'react';
+2 -2
View File
@@ -1,5 +1,5 @@
import { search } from '@/api/subsonicSearch';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import { search } from '@/lib/api/subsonicSearch';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import React, { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { ChevronLeft } from 'lucide-react';
+3 -3
View File
@@ -1,11 +1,11 @@
import { buildDownloadUrl } from '@/api/subsonicStreamUrl';
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
import { resolveAlbum } from '@/features/offline';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import { songToTrack } from '@/utils/playback/songToTrack';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import AlbumCard from '@/features/album/components/AlbumCard';
import { LOSSLESS_MODE_QUERY } from '@/utils/library/losslessMode';
import { ndListLosslessAlbumsPage } from '@/api/navidromeBrowse';
import { ndListLosslessAlbumsPage } from '@/lib/api/navidromeBrowse';
import { useTranslation } from 'react-i18next';
import { useAuthStore } from '@/store/authStore';
import { useOfflineStore } from '@/features/offline';
+4 -4
View File
@@ -1,8 +1,8 @@
import { buildDownloadUrl } from '@/api/subsonicStreamUrl';
import { getAlbumsByGenre } from '@/api/subsonicGenres';
import { getAlbumList } from '@/api/subsonicLibrary';
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
import { getAlbumsByGenre } from '@/lib/api/subsonicGenres';
import { getAlbumList } from '@/lib/api/subsonicLibrary';
import { resolveAlbum } from '@/features/offline';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import { dedupeById } from '@/utils/dedupeById';
import { shuffleArray } from '@/utils/playback/shuffleArray';
import React, { useEffect, useLayoutEffect, useState, useCallback, useRef } from 'react';
@@ -1,5 +1,5 @@
import { describe, expect, it, beforeEach } from 'vitest';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import {
DEFAULT_ALBUM_BROWSE_RETURN_FILTERS,
DEFAULT_ALBUM_BROWSE_SORT,
@@ -1,5 +1,5 @@
import { create } from 'zustand';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import type { AlbumBrowseSort } from '@/utils/library/browseTextSearch';
export const DEFAULT_ALBUM_BROWSE_SORT: AlbumBrowseSort = 'alphabeticalByName';
@@ -4,7 +4,7 @@ import {
deriveAlbumArtistRefs,
deriveAlbumHeaderArtistRefs,
} from '@/features/album/utils/deriveAlbumHeaderArtistRefs';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import { makeSubsonicSong } from '@/test/helpers/factories';
const baseAlbum = (): SubsonicAlbum => ({
@@ -1,4 +1,4 @@
import type { SubsonicAlbum, SubsonicOpenArtistRef, SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicAlbum, SubsonicOpenArtistRef, SubsonicSong } from '@/lib/api/subsonicTypes';
import { coerceOpenArtistRefs } from '@/features/artist';
function nonEmpty(refs: SubsonicOpenArtistRef[]): refs is SubsonicOpenArtistRef[] {
+1 -1
View File
@@ -1,4 +1,4 @@
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { coverArtRef } from '@/cover/ref';
+2 -2
View File
@@ -1,7 +1,7 @@
import { getAlbumList } from '@/api/subsonicLibrary';
import { getAlbumList } from '@/lib/api/subsonicLibrary';
import { coverArtRef } from '@/cover/ref';
import { loadCoverBlobForExport } from '@/cover/integrations/export';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import { albumArtistDisplayName } from '@/features/album/utils/deriveAlbumHeaderArtistRefs';
import { writeFile } from '@tauri-apps/plugin-fs';
import { downloadDir, join } from '@tauri-apps/api/path';
@@ -1,19 +1,19 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
vi.mock('@/api/subsonicClient', () => ({
vi.mock('@/lib/api/subsonicClient', () => ({
api: vi.fn(),
apiForServer: vi.fn(),
libraryFilterParams: () => ({}),
libraryFilterParamsForServer: () => ({}),
}));
vi.mock('@/api/subsonicLibrary', () => ({
vi.mock('@/lib/api/subsonicLibrary', () => ({
filterSongsToActiveLibrary: async (songs: unknown[]) => songs,
filterSongsToServerLibrary: async (songs: unknown[]) => songs,
similarSongsRequestCount: (count: number) => count,
}));
import { api } from '@/api/subsonicClient';
import { api } from '@/lib/api/subsonicClient';
import { fetchSimilarTracksRouted } from '@/features/artist/api/subsonicArtists';
import { useAuthStore } from '@/store/authStore';
+4 -4
View File
@@ -1,7 +1,7 @@
import { useAuthStore } from '@/store/authStore';
import { api, apiForServer, libraryFilterParams, libraryFilterParamsForServer } from '@/api/subsonicClient';
import { filterSongsToServerLibrary } from '@/api/subsonicLibrary';
import { filterSongsToActiveLibrary, similarSongsRequestCount } from '@/api/subsonicLibrary';
import { api, apiForServer, libraryFilterParams, libraryFilterParamsForServer } from '@/lib/api/subsonicClient';
import { filterSongsToServerLibrary } from '@/lib/api/subsonicLibrary';
import { filterSongsToActiveLibrary, similarSongsRequestCount } from '@/lib/api/subsonicLibrary';
import {
FEATURE_AUDIOMUSE_SIMILAR_TRACKS,
OP_SIMILAR_TRACKS,
@@ -12,7 +12,7 @@ import type {
SubsonicArtist,
SubsonicArtistInfo,
SubsonicSong,
} from '@/api/subsonicTypes';
} from '@/lib/api/subsonicTypes';
export async function getArtists(): Promise<SubsonicArtist[]> {
type ArtistIndexEntry = { artist?: SubsonicArtist | SubsonicArtist[] };
@@ -1,5 +1,5 @@
import React from 'react';
import type { SubsonicArtist } from '@/api/subsonicTypes';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import { ArtistCoverArtImage } from '@/cover/ArtistCoverArtImage';
import {
COVER_DENSE_ARTIST_LIST_CSS_PX,
@@ -1,4 +1,4 @@
import type { SubsonicArtist } from '@/api/subsonicTypes';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import React, { useMemo } from 'react';
import { Users } from 'lucide-react';
import { useTranslation } from 'react-i18next';
@@ -5,7 +5,7 @@ import {
ArrowLeft, Camera, Check, HardDriveDownload, Heart,
Loader2, Play, Radio, Share2, Shuffle, Users,
} from 'lucide-react';
import type { SubsonicAlbum, SubsonicArtist, SubsonicArtistInfo } from '@/api/subsonicTypes';
import type { SubsonicAlbum, SubsonicArtist, SubsonicArtistInfo } from '@/lib/api/subsonicTypes';
import { useOfflineStore } from '@/features/offline';
import { useAuthStore } from '@/store/authStore';
import { useThemeStore } from '@/store/themeStore';
@@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { ChevronDown, ChevronUp } from 'lucide-react';
import type { SubsonicArtist } from '@/api/subsonicTypes';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import { useIsMobile } from '@/hooks/useIsMobile';
interface Props {
@@ -1,7 +1,7 @@
import React, { Fragment } from 'react';
import { useTranslation } from 'react-i18next';
import { AudioLines, ChevronRight, Play, Square } from 'lucide-react';
import type { SubsonicAlbum, SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicAlbum, SubsonicSong } from '@/lib/api/subsonicTypes';
import { usePlayerStore } from '@/store/playerStore';
import { previewInputFromSong, usePreviewStore } from '@/store/previewStore';
import { useOrbitSongRowBehavior } from '@/features/orbit';
+1 -1
View File
@@ -1,4 +1,4 @@
import type { SubsonicArtist } from '@/api/subsonicTypes';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import React, { useRef, useState, useEffect, useLayoutEffect } from 'react';
import ArtistCardLocal from '@/features/artist/components/ArtistCardLocal';
import { ChevronLeft, ChevronRight, ArrowRight } from 'lucide-react';
@@ -1,7 +1,7 @@
import React from 'react';
import { Check } from 'lucide-react';
import type { TFunction } from 'i18next';
import type { SubsonicArtist } from '@/api/subsonicTypes';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import type { PlayerState } from '@/store/playerStoreTypes';
import { ARTISTS_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
@@ -1,7 +1,7 @@
import React from 'react';
import type { Virtualizer } from '@tanstack/react-virtual';
import type { TFunction } from 'i18next';
import type { SubsonicArtist } from '@/api/subsonicTypes';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import type { PlayerState } from '@/store/playerStoreTypes';
import { OTHER_BUCKET, type ArtistListFlatRow } from '@/features/artist/utils/artistsHelpers';
import { ArtistRowAvatar } from '@/features/artist/components/ArtistAvatars';
@@ -1,5 +1,5 @@
import React, { Fragment } from 'react';
import type { SubsonicOpenArtistRef } from '@/api/subsonicTypes';
import type { SubsonicOpenArtistRef } from '@/lib/api/subsonicTypes';
interface Props {
refs: SubsonicOpenArtistRef[];
@@ -1,4 +1,4 @@
import type { SubsonicAlbum, SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicAlbum, SubsonicSong } from '@/lib/api/subsonicTypes';
export type TopSongAlbumCoverSource = Pick<SubsonicAlbum, 'id' | 'coverArt' | 'name'>;
@@ -13,13 +13,13 @@ import { renderHook, act, waitFor } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { SubsonicArtistInfo } from '@/api/subsonicTypes';
import type { SubsonicArtistInfo } from '@/lib/api/subsonicTypes';
vi.mock('@/features/artist/api/subsonicArtists');
vi.mock('@/api/subsonicSearch');
vi.mock('@/lib/api/subsonicSearch');
import { getArtist, getArtistInfo, getTopSongs } from '@/features/artist/api/subsonicArtists';
import { search } from '@/api/subsonicSearch';
import { search } from '@/lib/api/subsonicSearch';
import { useArtistDetailData } from '@/features/artist/hooks/useArtistDetailData';
const mockArtistInfo = vi.mocked(getArtistInfo) as unknown as {
@@ -1,10 +1,10 @@
import { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { search } from '@/api/subsonicSearch';
import { search } from '@/lib/api/subsonicSearch';
import { getArtist, getArtistForServer, getArtistInfo, getTopSongs } from '@/features/artist/api/subsonicArtists';
import type {
SubsonicAlbum, SubsonicArtist, SubsonicArtistInfo, SubsonicSong,
} from '@/api/subsonicTypes';
} from '@/lib/api/subsonicTypes';
import { useAuthStore } from '@/store/authStore';
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
import { loadArtistFromLibraryIndex } from '@/features/offline';
@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from 'react';
import { getArtistInfoForServer } from '@/features/artist/api/subsonicArtists';
import type { SubsonicArtistInfo, SubsonicOpenArtistRef } from '@/api/subsonicTypes';
import type { SubsonicArtistInfo, SubsonicOpenArtistRef } from '@/lib/api/subsonicTypes';
import { makeCache } from '@/utils/cache/nowPlayingCache';
const artistInfoCache = makeCache<SubsonicArtistInfo | null>();
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { getMusicNetworkRuntime } from '@/music-network';
import { search } from '@/api/subsonicSearch';
import type { SubsonicArtist, SubsonicArtistInfo } from '@/api/subsonicTypes';
import { search } from '@/lib/api/subsonicSearch';
import type { SubsonicArtist, SubsonicArtistInfo } from '@/lib/api/subsonicTypes';
import { useAuthStore } from '@/store/authStore';
export interface ArtistSimilarArtistsResult {
@@ -1,5 +1,5 @@
import { getArtists } from '@/features/artist/api/subsonicArtists';
import type { SubsonicArtist } from '@/api/subsonicTypes';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import { useCallback, useEffect, useRef, useState } from 'react';
import { dedupeById } from '@/utils/dedupeById';
import {
@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import type { SubsonicArtist } from '@/api/subsonicTypes';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import { usePlayerStore } from '@/store/playerStore';
import { ALL_SENTINEL, artistLetterBucket, compareBuckets, type ArtistListFlatRow } from '@/features/artist/utils/artistsHelpers';
@@ -1,4 +1,4 @@
import type { SubsonicArtist } from '@/api/subsonicTypes';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import { useEffect, useRef, useState } from 'react';
import {
BROWSE_TEXT_DEBOUNCE_NETWORK_MS,
+1 -1
View File
@@ -1,6 +1,6 @@
import { useCoverArt } from '@/cover/useCoverArt';
import { useArtistCoverRef } from '@/cover/useLibraryCoverRef';
import type { SubsonicArtist, SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicArtist, SubsonicAlbum } from '@/lib/api/subsonicTypes';
import { useEffect, useState, Fragment, useMemo } from 'react';
import { useParams, useSearchParams } from 'react-router-dom';
import { AlbumCard } from '@/features/album';
+1 -1
View File
@@ -1,4 +1,4 @@
import type { SubsonicArtist } from '@/api/subsonicTypes';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
export const ALL_SENTINEL = 'ALL';
/** Catch-all bucket for names that start with neither an AZ letter nor a digit
+1 -1
View File
@@ -1,4 +1,4 @@
import type { SubsonicOpenArtistRef } from '@/api/subsonicTypes';
import type { SubsonicOpenArtistRef } from '@/lib/api/subsonicTypes';
/** Subsonic JSON may return one ref object instead of a one-element array. */
export function coerceOpenArtistRefs(
@@ -1,8 +1,8 @@
import type React from 'react';
import type { TFunction } from 'i18next';
import { uploadArtistImage } from '@/features/playlist';
import { setRating, star, unstar } from '@/api/subsonicStarRating';
import type { SubsonicArtist } from '@/api/subsonicTypes';
import { setRating, star, unstar } from '@/lib/api/subsonicStarRating';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import { useAuthStore } from '@/store/authStore';
import { copyEntityShareLink } from '@/utils/share/copyEntityShareLink';
import { invalidateCoverArt } from '@/utils/imageCache';
@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import * as offlineMediaResolve from '@/features/offline';
import { fetchArtistDetailTracks, runArtistDetailPlayTopSong } from '@/features/artist/utils/runArtistDetailPlay';
@@ -1,6 +1,6 @@
import type { TFunction } from 'i18next';
import { getSimilarSongs2, getTopSongs } from '@/features/artist/api/subsonicArtists';
import type { SubsonicAlbum, SubsonicArtist, SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicAlbum, SubsonicArtist, SubsonicSong } from '@/lib/api/subsonicTypes';
import type { Track } from '@/store/playerStoreTypes';
import { songToTrack } from '@/utils/playback/songToTrack';
import { runBulkPlayAll, runBulkShuffle } from '@/utils/playback/runBulkPlay';
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import { sortArtistAlbumsByYear } from '@/features/artist/utils/sortArtistAlbums';
const album = (id: string, name: string, year?: number): SubsonicAlbum => ({
@@ -1,4 +1,4 @@
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
export type ArtistAlbumYearOrder = 'yearDesc' | 'yearAsc';
@@ -6,7 +6,7 @@ import {
} from 'lucide-react';
import type {
SubsonicAlbum, SubsonicArtist, SubsonicPlaylist,
} from '@/api/subsonicTypes';
} from '@/lib/api/subsonicTypes';
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
import type { SourceTab } from '@/features/deviceSync/utils/deviceSyncHelpers';
import BrowserRow from '@/features/deviceSync/components/BrowserRow';
@@ -1,11 +1,11 @@
import { useCallback, useEffect, useState } from 'react';
import { getPlaylists } from '@/features/playlist';
import { getArtists, getArtist } from '@/features/artist';
import { getAlbumList } from '@/api/subsonicLibrary';
import { search as searchSubsonic } from '@/api/subsonicSearch';
import { getAlbumList } from '@/lib/api/subsonicLibrary';
import { search as searchSubsonic } from '@/lib/api/subsonicSearch';
import type {
SubsonicAlbum, SubsonicArtist, SubsonicPlaylist,
} from '@/api/subsonicTypes';
} from '@/lib/api/subsonicTypes';
import type { SourceTab } from '@/features/deviceSync/utils/deviceSyncHelpers';
export interface DeviceSyncBrowserResult {
+1 -1
View File
@@ -1,4 +1,4 @@
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import React, { useState, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useDeviceSyncStore, DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
@@ -1,4 +1,4 @@
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
export type SourceTab = 'playlists' | 'albums' | 'artists';
@@ -1,7 +1,7 @@
import type { TFunction } from 'i18next';
import { invoke } from '@tauri-apps/api/core';
import { buildDownloadUrl } from '@/api/subsonicStreamUrl';
import type { SubsonicSong } from '@/api/subsonicTypes';
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { useDeviceSyncStore, type DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
import { useDeviceSyncJobStore } from '@/features/deviceSync/store/deviceSyncJobStore';
import { showToast } from '@/utils/ui/toast';
@@ -37,7 +37,7 @@ export async function runDeviceSyncSummaryPrompt(deps: RunDeviceSyncSummaryDeps)
setPreSyncOpen(true);
try {
const { getClient } = await import('@/api/subsonicClient');
const { getClient } = await import('@/lib/api/subsonicClient');
const { baseUrl, params } = getClient();
const payload = await invoke<SyncDelta>('calculate_sync_payload', {
sources,
@@ -1,7 +1,7 @@
import type React from 'react';
import { invoke } from '@tauri-apps/api/core';
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { applyLegacyTemplate } from '@/features/deviceSync/utils/deviceSyncLegacyTemplate';
import { trackToSyncInfo } from '@/features/deviceSync/utils/deviceSyncHelpers';
import { fetchTracksForSource } from '@/utils/playback/fetchTracksForSource';
@@ -2,7 +2,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { AudioLines, ChevronRight, Play, Square, X } from 'lucide-react';
import type { ColDef } from '@/utils/useTracklistColumns';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { codecLabel } from '@/utils/componentHelpers/playlistDetailHelpers';
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
import i18n from '@/i18n';
@@ -1,7 +1,7 @@
import React, { useMemo, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { ListPlus, Play, SlidersHorizontal, X } from 'lucide-react';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { usePlayerStore } from '@/store/playerStore';
import { useSelectionStore } from '@/store/selectionStore';
import { songToTrack } from '@/utils/playback/songToTrack';
@@ -5,7 +5,7 @@ import { TracklistColumnPicker } from '@/components/albumTrackList/TracklistColu
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import type { ColDef } from '@/utils/useTracklistColumns';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { usePlayerStore } from '@/store/playerStore';
import { previewInputFromSong, usePreviewStore } from '@/store/previewStore';
import { useSelectionStore } from '@/store/selectionStore';
@@ -1,7 +1,7 @@
import React, { useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Cast, ChevronLeft, ChevronRight, Heart, X } from 'lucide-react';
import type { InternetRadioStation } from '@/api/subsonicTypes';
import type { InternetRadioStation } from '@/lib/api/subsonicTypes';
import { CoverArtImage } from '@/cover/CoverArtImage';
import { albumCoverRef } from '@/cover/ref';
import { coverArtIdFromRadio } from '@/cover/ids';
@@ -1,9 +1,9 @@
import { useEffect, useMemo, useState } from 'react';
import { getInternetRadioStations } from '@/features/radio';
import { getStarred } from '@/api/subsonicStarRating';
import { getStarred } from '@/lib/api/subsonicStarRating';
import type {
InternetRadioStation, SubsonicAlbum, SubsonicArtist, SubsonicSong,
} from '@/api/subsonicTypes';
} from '@/lib/api/subsonicTypes';
import { useAuthStore } from '@/store/authStore';
import { usePlayerStore } from '@/store/playerStore';
import type { TopFavoriteArtist } from '@/features/favorites/components/TopFavoriteArtists';
@@ -1,5 +1,5 @@
import React, { useCallback, useEffect, useRef } from 'react';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { useSelectionStore } from '@/store/selectionStore';
export interface FavoritesSelectionResult {
@@ -1,6 +1,6 @@
import React, { useMemo } from 'react';
import { ArrowDown, ArrowUp } from 'lucide-react';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { usePlayerStore } from '@/store/playerStore';
const CURRENT_YEAR = new Date().getFullYear();
@@ -1,4 +1,4 @@
import { star, unstar } from '@/api/subsonicStarRating';
import { star, unstar } from '@/lib/api/subsonicStarRating';
import { useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { emit } from '@tauri-apps/api/event';
@@ -9,7 +9,7 @@
*/
import { beforeEach, describe, expect, it, vi } from 'vitest';
vi.mock('@/api/subsonic', () => ({
vi.mock('@/lib/api/subsonic', () => ({
savePlayQueue: vi.fn(async () => undefined),
getPlayQueue: vi.fn(async () => ({ songs: [], current: undefined, position: 0 })),
buildStreamUrl: vi.fn((id: string) => `https://mock/stream/${id}`),
@@ -4,7 +4,7 @@ import { usePlayerStore } from '@/store/playerStore';
import { useAuthStore } from '@/store/authStore';
import { setTransitionMode, type TransitionMode } from '@/utils/playback/playbackTransition';
import { resolveQueueTrack } from '@/utils/library/queueTrackView';
import type { SubsonicOpenArtistRef } from '@/api/subsonicTypes';
import type { SubsonicOpenArtistRef } from '@/lib/api/subsonicTypes';
import type { Track } from '@/store/playerStoreTypes';
export const MINI_WINDOW_LABEL = 'mini';
@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest';
import { nowPlayingPresence, NOW_PLAYING_IDLE_MINUTES } from '@/features/nowPlaying/api/nowPlayingPresence';
import type { SubsonicNowPlaying, PlaybackReportState } from '@/api/subsonicTypes';
import type { SubsonicNowPlaying, PlaybackReportState } from '@/lib/api/subsonicTypes';
// The function only reads `state` and `minutesAgo`; cast a minimal fixture.
function entry(partial: { state?: PlaybackReportState; minutesAgo?: number }): SubsonicNowPlaying {
@@ -1,4 +1,4 @@
import type { SubsonicNowPlaying } from '@/api/subsonicTypes';
import type { SubsonicNowPlaying } from '@/lib/api/subsonicTypes';
/**
* Derived liveness of a now-playing entry, surfaced as the indicator dot in the
@@ -1,7 +1,7 @@
import React, { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Disc3, ExternalLink, Star } from 'lucide-react';
import type { SubsonicAlbum, SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicAlbum, SubsonicSong } from '@/lib/api/subsonicTypes';
import { formatTotalDuration } from '@/utils/componentHelpers/nowPlayingHelpers';
import { formatTrackTime } from '@/utils/format/formatDuration';
@@ -8,7 +8,7 @@ import { describe, it, expect, vi } from 'vitest';
import { fireEvent } from '@testing-library/react';
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
import ArtistCard from '@/features/nowPlaying/components/ArtistCard';
import type { SubsonicArtistInfo } from '@/api/subsonicTypes';
import type { SubsonicArtistInfo } from '@/lib/api/subsonicTypes';
const infoWithImage: SubsonicArtistInfo = {
biography: 'Some bio text here.',
@@ -1,7 +1,7 @@
import React, { memo, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ExternalLink } from 'lucide-react';
import type { SubsonicArtistInfo } from '@/api/subsonicTypes';
import type { SubsonicArtistInfo } from '@/lib/api/subsonicTypes';
import { isRealArtistImage, sanitizeHtml } from '@/utils/componentHelpers/nowPlayingHelpers';
import CachedImage from '@/ui/CachedImage';
@@ -1,7 +1,7 @@
import React, { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Disc3, ExternalLink, Music } from 'lucide-react';
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import { AlbumCoverArtImage } from '@/cover/AlbumCoverArtImage';
import { COVER_DENSE_RAIL_CELL_CSS_PX } from '@/cover/layoutSizes';
+1 -1
View File
@@ -4,7 +4,7 @@ import { Headphones, Heart, MicVocal, Music, Star } from 'lucide-react';
import { CoverArtImage } from '@/cover/CoverArtImage';
import type { CoverArtRef } from '@/cover/types';
import type { ArtistStats, TrackStats } from '@/music-network';
import type { SubsonicOpenArtistRef } from '@/api/subsonicTypes';
import type { SubsonicOpenArtistRef } from '@/lib/api/subsonicTypes';
import { OpenArtistRefInline } from '@/features/artist';
import { formatTrackTime } from '@/utils/format/formatDuration';
import { useEnrichmentPrimaryLabel } from '@/hooks/useEnrichmentPrimaryLabel';
@@ -1,6 +1,6 @@
import { TrackCoverArtImage } from '@/cover/TrackCoverArtImage';
import { getNowPlaying } from '@/api/subsonicScrobble';
import type { SubsonicNowPlaying } from '@/api/subsonicTypes';
import { getNowPlaying } from '@/lib/api/subsonicScrobble';
import type { SubsonicNowPlaying } from '@/lib/api/subsonicTypes';
import React, { useState, useEffect, useRef, useCallback, useLayoutEffect } from 'react';
import { createPortal } from 'react-dom';
import { PlayCircle, Pause, User, Radio, RefreshCw } from 'lucide-react';
@@ -1,6 +1,6 @@
import { getSongForServer } from '@/api/subsonicLibrary';
import { getSongForServer } from '@/lib/api/subsonicLibrary';
import { getArtistInfoForServer } from '@/features/artist';
import type { SubsonicArtistInfo, SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicArtistInfo, SubsonicSong } from '@/lib/api/subsonicTypes';
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Info } from 'lucide-react';
@@ -1,7 +1,7 @@
import React, { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { ExternalLink, Play, TrendingUp } from 'lucide-react';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { formatTrackTime } from '@/utils/format/formatDuration';
interface TopSongsCardProps {
@@ -11,10 +11,10 @@
*/
import { renderHook, act, waitFor } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { SubsonicArtistInfo, SubsonicSong, SubsonicAlbum, SubsonicArtist } from '@/api/subsonicTypes';
import type { SubsonicArtistInfo, SubsonicSong, SubsonicAlbum, SubsonicArtist } from '@/lib/api/subsonicTypes';
vi.mock('@/features/artist');
vi.mock('@/api/subsonicLibrary');
vi.mock('@/lib/api/subsonicLibrary');
vi.mock('@/api/bandsintown');
vi.mock('@/utils/network/subsonicNetworkGuard', () => ({
shouldAttemptSubsonicForServer: vi.fn(() => true),
@@ -22,7 +22,7 @@ vi.mock('@/utils/network/subsonicNetworkGuard', () => ({
import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard';
import { getArtistForServer, getArtistInfoForServer, getTopSongsForServer } from '@/features/artist';
import { getAlbumForServer, getSongForServer } from '@/api/subsonicLibrary';
import { getAlbumForServer, getSongForServer } from '@/lib/api/subsonicLibrary';
import { fetchBandsintownEvents } from '@/api/bandsintown';
import { useNowPlayingFetchers, type NowPlayingFetchersDeps } from '@/features/nowPlaying/hooks/useNowPlayingFetchers';
@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import { getArtistInfoForServer } from '@/features/artist';
import type { SubsonicAlbum, SubsonicArtistInfo, SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicAlbum, SubsonicArtistInfo, SubsonicSong } from '@/lib/api/subsonicTypes';
import { resolveNpAlbum, resolveNpDiscography, resolveNpSongMeta, resolveNpTopSongs } from '@/features/nowPlaying/utils/nowPlayingMetadataResolve';
import { fetchBandsintownEvents, type BandsintownEvent } from '@/api/bandsintown';
import type { ArtistStats, TrackStats } from '@/music-network';
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from 'react';
import { queueSongStar } from '@/store/pendingStarSync';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import type { Track } from '@/store/playerStoreTypes';
import type { TrackStats } from '@/music-network';
import { getMusicNetworkRuntime } from '@/music-network';
+1 -1
View File
@@ -2,7 +2,7 @@ import { useCoverArt } from '@/cover/useCoverArt';
import { albumCoverRef } from '@/cover/ref';
import { usePlaybackTrackCoverRef } from '@/cover/useLibraryCoverRef';
import { coverArtIdFromRadio } from '@/cover/ids';
import type { SubsonicArtistInfo, SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicArtistInfo, SubsonicSong } from '@/lib/api/subsonicTypes';
import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
import { usePlaybackLibraryNavigate } from '@/hooks/usePlaybackLibraryNavigate';
import { usePlaybackServerId } from '@/hooks/usePlaybackServerId';
@@ -8,9 +8,9 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { onInvoke } from '@/test/mocks/tauri';
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
import type { LibraryAdvancedSearchResponse } from '@/api/library';
import type { LibraryAdvancedSearchResponse } from '@/lib/api/library';
import * as subsonicArtists from '@/features/artist';
import * as subsonicLibrary from '@/api/subsonicLibrary';
import * as subsonicLibrary from '@/lib/api/subsonicLibrary';
// Network reachability is decided by the guard; mock it so we can test both arms.
vi.mock('@/utils/network/subsonicNetworkGuard', () => ({
@@ -16,10 +16,10 @@
* `artistInfo` (bio / similar) has no index source and stays network-only it
* is intentionally absent here.
*/
import { libraryGetTrack, libraryGetTracksByAlbum } from '@/api/library';
import { libraryGetTrack, libraryGetTracksByAlbum } from '@/lib/api/library';
import { getArtistForServer, getTopSongsForServer } from '@/features/artist';
import { getAlbumForServer, getSongForServer } from '@/api/subsonicLibrary';
import type { SubsonicAlbum, SubsonicSong } from '@/api/subsonicTypes';
import { getAlbumForServer, getSongForServer } from '@/lib/api/subsonicLibrary';
import type { SubsonicAlbum, SubsonicSong } from '@/lib/api/subsonicTypes';
import { shouldAttemptSubsonicForServer } from '@/utils/network/subsonicNetworkGuard';
import { loadAlbumFromLibraryIndex, loadArtistFromLibraryIndex } from '@/features/offline';
import { trackToSong } from '@/utils/library/advancedSearchLocal';
+4 -4
View File
@@ -1,8 +1,8 @@
import { libraryUpsertSongsFromApi } from '@/api/library';
import { buildStreamUrl } from '@/api/subsonicStreamUrl';
import { getAlbum } from '@/api/subsonicLibrary';
import { libraryUpsertSongsFromApi } from '@/lib/api/library';
import { buildStreamUrl } from '@/lib/api/subsonicStreamUrl';
import { getAlbum } from '@/lib/api/subsonicLibrary';
import { getArtist } from '@/features/artist';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import { invoke } from '@tauri-apps/api/core';
@@ -32,11 +32,11 @@ const libraryAdvancedSearchMock = vi.fn();
const libraryGetTracksByAlbumMock = vi.fn();
const libraryGetTracksBatchChunkedMock = vi.fn();
vi.mock('@/api/subsonicLibrary', () => ({
vi.mock('@/lib/api/subsonicLibrary', () => ({
getAlbumForServer: (...args: unknown[]) => getAlbumForServerMock(...args),
}));
vi.mock('@/api/library', () => ({
vi.mock('@/lib/api/library', () => ({
libraryAdvancedSearch: (...args: unknown[]) => libraryAdvancedSearchMock(...args),
libraryGetTracksByAlbum: (...args: unknown[]) => libraryGetTracksByAlbumMock(...args),
libraryGetTracksBatchChunked: (...args: unknown[]) => libraryGetTracksBatchChunkedMock(...args),
@@ -1,5 +1,5 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { useAuthStore } from '@/store/authStore';
import { useOfflineJobStore } from '@/features/offline/store/offlineJobStore';
import { FAVORITES_OFFLINE_JOB_ID } from '@/features/offline/utils/favoritesOfflineConstants';
@@ -20,11 +20,11 @@ vi.mock('@/utils/network/activeServerReachability', () => ({
isActiveServerReachable: () => isActiveServerReachableMock(),
}));
vi.mock('@/api/subsonicStarRating', () => ({
vi.mock('@/lib/api/subsonicStarRating', () => ({
getStarredForServer: (serverId: string) => getStarredForServerMock(serverId),
}));
vi.mock('@/api/subsonicLibrary', () => ({
vi.mock('@/lib/api/subsonicLibrary', () => ({
getAlbumForServer: vi.fn(async () => ({ songs: [] })),
}));
@@ -1,10 +1,10 @@
import { libraryUpsertSongsFromApi } from '@/api/library';
import { libraryUpsertSongsFromApi } from '@/lib/api/library';
import { librarySqlServerId } from '@/api/coverCache';
import { getAlbumForServer } from '@/api/subsonicLibrary';
import { getAlbumForServer } from '@/lib/api/subsonicLibrary';
import { getArtistForServer } from '@/features/artist';
import { getStarredForServer } from '@/api/subsonicStarRating';
import { buildStreamUrlForServer } from '@/api/subsonicStreamUrl';
import type { SubsonicSong } from '@/api/subsonicTypes';
import { getStarredForServer } from '@/lib/api/subsonicStarRating';
import { buildStreamUrlForServer } from '@/lib/api/subsonicStreamUrl';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { invoke } from '@tauri-apps/api/core';
import i18n from '@/i18n';
import { useAuthStore } from '@/store/authStore';
@@ -1,5 +1,5 @@
import { invoke } from '@tauri-apps/api/core';
import { libraryGetTracksBatch } from '@/api/library';
import { libraryGetTracksBatch } from '@/lib/api/library';
import { useAuthStore } from '@/store/authStore';
import { useOfflineStore, type OfflineAlbumMeta } from '@/features/offline/store/offlineStore';
import { useLocalPlaybackStore, type LocalPlaybackEntry, type PinSource } from '@/store/localPlaybackStore';
@@ -1,6 +1,6 @@
import { libraryUpsertSongsFromApi } from '@/api/library';
import { libraryUpsertSongsFromApi } from '@/lib/api/library';
import { librarySqlServerId } from '@/api/coverCache';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { invoke } from '@tauri-apps/api/core';
import { useAuthStore } from '@/store/authStore';
import type { LocalPlaybackEntry, PinSource } from '@/store/localPlaybackStore';
@@ -1,4 +1,4 @@
import type { SubsonicAlbum } from '@/api/subsonicTypes';
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
import type { AlbumBrowseQuery } from '@/utils/library/albumBrowseTypes';
import { isOfflineBrowseActive } from '@/features/offline/utils/offlineBrowseMode';
import {
@@ -16,7 +16,7 @@ import {
offlineTrackCount,
type OfflineLibraryCard,
} from '@/features/offline/utils/offlineLibraryHelpers';
import * as libraryApi from '@/api/library';
import * as libraryApi from '@/lib/api/library';
import { coverStorageKey } from '@/cover/storageKeys';
import { resolveCoverDisplayTier } from '@/cover/tiers';
@@ -24,8 +24,8 @@ vi.mock('@/utils/server/switchActiveServer', () => ({
switchActiveServer: vi.fn(async () => true),
}));
vi.mock('@/api/library', async importOriginal => {
const actual = await importOriginal<typeof import('@/api/library')>();
vi.mock('@/lib/api/library', async importOriginal => {
const actual = await importOriginal<typeof import('@/lib/api/library')>();
const libraryGetTracksBatch = vi.fn();
return {
...actual,
@@ -1,6 +1,6 @@
import type { LibraryTrackDto } from '@/api/library';
import { libraryGetTrack, libraryGetTracksBatchChunked } from '@/api/library';
import type { SubsonicSong } from '@/api/subsonicTypes';
import type { LibraryTrackDto } from '@/lib/api/library';
import { libraryGetTrack, libraryGetTracksBatchChunked } from '@/lib/api/library';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import type { CoverServerScope } from '@/cover/types';
import { useAuthStore } from '@/store/authStore';
import type { LocalPlaybackEntry, PinnedGroup, PinSource } from '@/store/localPlaybackStore';
@@ -1,9 +1,9 @@
import { libraryAdvancedSearch, libraryGetTracksByAlbum } from '@/api/library';
import { libraryAdvancedSearch, libraryGetTracksByAlbum } from '@/lib/api/library';
import type {
SubsonicAlbum,
SubsonicArtist,
SubsonicSong,
} from '@/api/subsonicTypes';
} from '@/lib/api/subsonicTypes';
import {
albumToAlbum,
artistToArtist,
@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { LibraryTrackDto } from '@/api/library';
import type { LibraryTrackDto } from '@/lib/api/library';
import { useAuthStore } from '@/store/authStore';
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
@@ -13,7 +13,7 @@ const { libraryGetTracksBatchChunkedMock } = vi.hoisted(() => ({
libraryGetTracksBatchChunkedMock: vi.fn(async (): Promise<LibraryTrackDto[]> => []),
}));
vi.mock('@/api/library', () => ({
vi.mock('@/lib/api/library', () => ({
libraryGetTracksBatchChunked: libraryGetTracksBatchChunkedMock,
libraryGetTracksByAlbum: vi.fn(async () => []),
libraryAdvancedSearch: vi.fn(async () => ({ albums: [], artists: [], tracks: [] })),
@@ -1,6 +1,6 @@
import type { LibraryTrackDto } from '@/api/library';
import { libraryAdvancedSearch, libraryGetTracksBatchChunked, libraryGetTracksByAlbum } from '@/api/library';
import type { SubsonicAlbum, SubsonicArtist, SubsonicSong } from '@/api/subsonicTypes';
import type { LibraryTrackDto } from '@/lib/api/library';
import { libraryAdvancedSearch, libraryGetTracksBatchChunked, libraryGetTracksByAlbum } from '@/lib/api/library';
import type { SubsonicAlbum, SubsonicArtist, SubsonicSong } from '@/lib/api/subsonicTypes';
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
import type { LocalPlaybackEntry } from '@/store/localPlaybackStore';
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
@@ -49,7 +49,7 @@ vi.mock('@/utils/network/subsonicNetworkGuard', () => ({
shouldAttemptSubsonicForServerMock(serverId, trackId),
}));
vi.mock('@/api/subsonicLibrary', () => ({
vi.mock('@/lib/api/subsonicLibrary', () => ({
getAlbumForServer: (serverId: string, albumId: string) => getAlbumForServerMock(serverId, albumId),
}));

Some files were not shown because too many files have changed in this diff Show More