mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
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:
@@ -5,7 +5,7 @@ import { coverIndexKeyFromRef, coverStorageKeyFromRef } from '../cover/storageKe
|
|||||||
import { connectBaseUrlForServer } from '../utils/server/serverEndpoint';
|
import { connectBaseUrlForServer } from '../utils/server/serverEndpoint';
|
||||||
import { serverIndexKeyForProfile } from '../utils/server/serverIndexKey';
|
import { serverIndexKeyForProfile } from '../utils/server/serverIndexKey';
|
||||||
import { getPlaybackServerId } from '../utils/playback/playbackServer';
|
import { getPlaybackServerId } from '../utils/playback/playbackServer';
|
||||||
import { restBaseFromUrl } from './subsonicClient';
|
import { restBaseFromUrl } from '@/lib/api/subsonicClient';
|
||||||
import type { CoverArtRef, CoverArtTier } from '../cover/types';
|
import type { CoverArtRef, CoverArtTier } from '../cover/types';
|
||||||
|
|
||||||
/** Library SQLite `track.server_id` uses host index keys, not auth profile UUIDs. */
|
/** Library SQLite `track.server_id` uses host index keys, not auth profile UUIDs. */
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { getArtist, getArtistInfo } from '@/features/artist';
|
import { getArtist, getArtistInfo } from '@/features/artist';
|
||||||
import { filterAlbumsToActiveLibrary } from '../api/subsonicLibrary';
|
import { filterAlbumsToActiveLibrary } from '@/lib/api/subsonicLibrary';
|
||||||
import { resolveAlbum, resolveMediaServerId } from '@/features/offline';
|
import { resolveAlbum, resolveMediaServerId } from '@/features/offline';
|
||||||
import type { SubsonicAlbum } from '../api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import { songToTrack } from '../utils/playback/songToTrack';
|
import { songToTrack } from '../utils/playback/songToTrack';
|
||||||
import { shuffleArray } from '../utils/playback/shuffleArray';
|
import { shuffleArray } from '../utils/playback/shuffleArray';
|
||||||
import React, { memo, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
import React, { memo, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import type { ServerProfile } from '@/store/authStoreTypes';
|
import type { ServerProfile } from '@/store/authStoreTypes';
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
vi.mock('@/api/subsonic', () => ({
|
vi.mock('@/lib/api/subsonic', () => ({
|
||||||
savePlayQueue: vi.fn(async () => undefined),
|
savePlayQueue: vi.fn(async () => undefined),
|
||||||
getPlayQueue: vi.fn(async () => ({ songs: [], current: undefined, position: 0 })),
|
getPlayQueue: vi.fn(async () => ({ songs: [], current: undefined, position: 0 })),
|
||||||
buildStreamUrl: vi.fn((id: string) => `https://mock/stream/${id}`),
|
buildStreamUrl: vi.fn((id: string) => `https://mock/stream/${id}`),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getGenres } from '../api/subsonicGenres';
|
import { getGenres } from '@/lib/api/subsonicGenres';
|
||||||
import type { GenreFilterOption } from '../utils/library/albumBrowseLoad';
|
import type { GenreFilterOption } from '../utils/library/albumBrowseLoad';
|
||||||
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { getRandomAlbums } from '../api/subsonicLibrary';
|
import { getRandomAlbums } from '@/lib/api/subsonicLibrary';
|
||||||
import { resolveAlbum, resolveMediaServerId } from '@/features/offline';
|
import { resolveAlbum, resolveMediaServerId } from '@/features/offline';
|
||||||
import type { SubsonicAlbum } from '../api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import { songToTrack } from '../utils/playback/songToTrack';
|
import { songToTrack } from '../utils/playback/songToTrack';
|
||||||
import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
|
import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
|
||||||
import { useNavigateToAlbum } from '@/features/album';
|
import { useNavigateToAlbum } from '@/features/album';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getSong } from '../api/subsonicLibrary';
|
import { getSong } from '@/lib/api/subsonicLibrary';
|
||||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { Check, X, Inbox } from 'lucide-react';
|
import { Check, X, Inbox } from 'lucide-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import SongRow, { SongListHeader } from './SongRow';
|
import SongRow, { SongListHeader } from './SongRow';
|
||||||
import { useInpageScrollSentinel } from '../hooks/useInpageScrollSentinel';
|
import { useInpageScrollSentinel } from '../hooks/useInpageScrollSentinel';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
vi.mock('@/api/subsonic', () => ({
|
vi.mock('@/lib/api/subsonic', () => ({
|
||||||
savePlayQueue: vi.fn(async () => undefined),
|
savePlayQueue: vi.fn(async () => undefined),
|
||||||
getPlayQueue: vi.fn(async () => ({ songs: [], current: undefined, position: 0 })),
|
getPlayQueue: vi.fn(async () => ({ songs: [], current: undefined, position: 0 })),
|
||||||
pingWithCredentials: vi.fn(async () => ({
|
pingWithCredentials: vi.fn(async () => ({
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
vi.mock('@/api/subsonic', () => ({
|
vi.mock('@/lib/api/subsonic', () => ({
|
||||||
savePlayQueue: vi.fn(async () => undefined),
|
savePlayQueue: vi.fn(async () => undefined),
|
||||||
getPlayQueue: vi.fn(async () => ({ songs: [], current: undefined, position: 0 })),
|
getPlayQueue: vi.fn(async () => ({ songs: [], current: undefined, position: 0 })),
|
||||||
buildStreamUrl: vi.fn((id: string) => `https://mock/stream/${id}`),
|
buildStreamUrl: vi.fn((id: string) => `https://mock/stream/${id}`),
|
||||||
|
|||||||
@@ -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 SongCard from './SongCard';
|
import SongCard from './SongCard';
|
||||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
|
|
||||||
const navigateToArtist = vi.fn();
|
const navigateToArtist = vi.fn();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { songToTrack } from '../utils/playback/songToTrack';
|
import { songToTrack } from '../utils/playback/songToTrack';
|
||||||
import React, { memo, useMemo } from 'react';
|
import React, { memo, useMemo } from 'react';
|
||||||
import { Play, ListPlus, Star, Disc3 } from 'lucide-react';
|
import { Play, ListPlus, Star, Disc3 } from 'lucide-react';
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { getSong } from '../api/subsonicLibrary';
|
import { getSong } from '@/lib/api/subsonicLibrary';
|
||||||
import { libraryGetFacts } from '../api/library';
|
import { libraryGetFacts } from '@/lib/api/library';
|
||||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
import { usePlayerStore } from '../store/playerStore';
|
import { usePlayerStore } from '../store/playerStore';
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
import { ndGetSongPath } from '../api/navidromeAdmin';
|
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';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import React, { useRef, useState, useEffect, useMemo } from 'react';
|
import React, { useRef, useState, useEffect, useMemo } from 'react';
|
||||||
import { ChevronLeft, ChevronRight, RefreshCw } from 'lucide-react';
|
import { ChevronLeft, ChevronRight, RefreshCw } from 'lucide-react';
|
||||||
import SongCard from './SongCard';
|
import SongCard from './SongCard';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { songToTrack } from '../utils/playback/songToTrack';
|
import { songToTrack } from '../utils/playback/songToTrack';
|
||||||
import React, { memo } from 'react';
|
import React, { memo } from 'react';
|
||||||
import { useNavigateToAlbum } from '@/features/album';
|
import { useNavigateToAlbum } from '@/features/album';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 're
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ListMusic, Plus } from 'lucide-react';
|
import { ListMusic, Plus } from 'lucide-react';
|
||||||
import { getPlaylist, updatePlaylist } from '@/features/playlist';
|
import { getPlaylist, updatePlaylist } from '@/features/playlist';
|
||||||
import type { SubsonicPlaylist } from '../../api/subsonicTypes';
|
import type { SubsonicPlaylist } from '@/lib/api/subsonicTypes';
|
||||||
import { usePlaylistStore } from '@/features/playlist';
|
import { usePlaylistStore } from '@/features/playlist';
|
||||||
import { showToast } from '../../utils/ui/toast';
|
import { showToast } from '../../utils/ui/toast';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Play, ListPlus, Heart, Download, ChevronRight, ChevronsRight, User, ListMusic, Star, Share2 } from 'lucide-react';
|
import { Play, ListPlus, Heart, Download, ChevronRight, ChevronsRight, User, ListMusic, Star, Share2 } from 'lucide-react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { resolveAlbum, resolveMediaServerId } from '@/features/offline';
|
import { resolveAlbum, resolveMediaServerId } from '@/features/offline';
|
||||||
import { star, unstar } from '../../api/subsonicStarRating';
|
import { star, unstar } from '@/lib/api/subsonicStarRating';
|
||||||
import type { SubsonicAlbum } from '../../api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import { songToTrack } from '../../utils/playback/songToTrack';
|
import { songToTrack } from '../../utils/playback/songToTrack';
|
||||||
import StarRating from '../StarRating';
|
import StarRating from '../StarRating';
|
||||||
import { AlbumToPlaylistSubmenu } from './AlbumArtistToPlaylistSubmenu';
|
import { AlbumToPlaylistSubmenu } from './AlbumArtistToPlaylistSubmenu';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Radio, Heart, ChevronRight, ListMusic, Star, Share2 } from 'lucide-react';
|
import { Radio, Heart, ChevronRight, ListMusic, Star, Share2 } from 'lucide-react';
|
||||||
import { star, unstar } from '../../api/subsonicStarRating';
|
import { star, unstar } from '@/lib/api/subsonicStarRating';
|
||||||
import type { SubsonicArtist } from '../../api/subsonicTypes';
|
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
|
||||||
import StarRating from '../StarRating';
|
import StarRating from '../StarRating';
|
||||||
import { ArtistToPlaylistSubmenu } from './AlbumArtistToPlaylistSubmenu';
|
import { ArtistToPlaylistSubmenu } from './AlbumArtistToPlaylistSubmenu';
|
||||||
import { MultiArtistToPlaylistSubmenu } from './MultiArtistToPlaylistSubmenu';
|
import { MultiArtistToPlaylistSubmenu } from './MultiArtistToPlaylistSubmenu';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 're
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ListMusic, Plus } from 'lucide-react';
|
import { ListMusic, Plus } from 'lucide-react';
|
||||||
import { resolveAlbum, resolveMediaServerId, resolvePlaylist } from '@/features/offline';
|
import { resolveAlbum, resolveMediaServerId, resolvePlaylist } from '@/features/offline';
|
||||||
import type { SubsonicPlaylist } from '../../api/subsonicTypes';
|
import type { SubsonicPlaylist } from '@/lib/api/subsonicTypes';
|
||||||
import { usePlaylistStore } from '@/features/playlist';
|
import { usePlaylistStore } from '@/features/playlist';
|
||||||
import { showToast } from '../../utils/ui/toast';
|
import { showToast } from '../../utils/ui/toast';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { ListMusic, Plus } from 'lucide-react';
|
import { ListMusic, Plus } from 'lucide-react';
|
||||||
import { resolveAlbum, resolveArtist, resolveMediaServerId, resolvePlaylist } from '@/features/offline';
|
import { resolveAlbum, resolveArtist, resolveMediaServerId, resolvePlaylist } from '@/features/offline';
|
||||||
import { getPlaylists } from '@/features/playlist';
|
import { getPlaylists } from '@/features/playlist';
|
||||||
import type { SubsonicPlaylist } from '../../api/subsonicTypes';
|
import type { SubsonicPlaylist } from '@/lib/api/subsonicTypes';
|
||||||
import { usePlaylistStore } from '@/features/playlist';
|
import { usePlaylistStore } from '@/features/playlist';
|
||||||
import { showToast } from '../../utils/ui/toast';
|
import { showToast } from '../../utils/ui/toast';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Play, ChevronRight, FolderTree, ListMusic, Trash2 } from 'lucide-react';
|
import { Play, ChevronRight, FolderTree, ListMusic, Trash2 } from 'lucide-react';
|
||||||
import type { SubsonicPlaylist } from '../../api/subsonicTypes';
|
import type { SubsonicPlaylist } from '@/lib/api/subsonicTypes';
|
||||||
import { usePlaylistStore } from '@/features/playlist';
|
import { usePlaylistStore } from '@/features/playlist';
|
||||||
import { MultiPlaylistToPlaylistSubmenu, SinglePlaylistToPlaylistSubmenu } from './PlaylistToPlaylistSubmenus';
|
import { MultiPlaylistToPlaylistSubmenu, SinglePlaylistToPlaylistSubmenu } from './PlaylistToPlaylistSubmenus';
|
||||||
import MoveToFolderSubmenu from './MoveToFolderSubmenu';
|
import MoveToFolderSubmenu from './MoveToFolderSubmenu';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import type { SubsonicAlbum, SubsonicArtist } from '../../api/subsonicTypes';
|
import type { SubsonicAlbum, SubsonicArtist } from '@/lib/api/subsonicTypes';
|
||||||
import type { QueueItemRef, Track } from '../../store/playerStoreTypes';
|
import type { QueueItemRef, Track } from '../../store/playerStoreTypes';
|
||||||
import type { EntityShareKind } from '../../utils/share/shareLink';
|
import type { EntityShareKind } from '../../utils/share/shareLink';
|
||||||
import type { OfflineActionPolicy } from '@/features/offline';
|
import type { OfflineActionPolicy } from '@/features/offline';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ChevronRight, Folder, FolderOpen, Music } from 'lucide-react';
|
import { ChevronRight, Folder, FolderOpen, Music } from 'lucide-react';
|
||||||
import type { SubsonicDirectoryEntry } from '../../api/subsonicTypes';
|
import type { SubsonicDirectoryEntry } from '@/lib/api/subsonicTypes';
|
||||||
import type { Track } from '../../store/playerStoreTypes';
|
import type { Track } from '../../store/playerStoreTypes';
|
||||||
import {
|
import {
|
||||||
folderBrowserHasKeyModifiers, isFolderBrowserArrowKey,
|
folderBrowserHasKeyModifiers, isFolderBrowserArrowKey,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Cast, Heart, Maximize2, Music } from 'lucide-react';
|
import { Cast, Heart, Maximize2, Music } from 'lucide-react';
|
||||||
import type { TFunction } from 'i18next';
|
import type { TFunction } from 'i18next';
|
||||||
import { queueSongRating } from '../../store/pendingStarSync';
|
import { queueSongRating } from '../../store/pendingStarSync';
|
||||||
import type { InternetRadioStation, SubsonicOpenArtistRef } from '../../api/subsonicTypes';
|
import type { InternetRadioStation, SubsonicOpenArtistRef } from '@/lib/api/subsonicTypes';
|
||||||
import type { PlayerState, Track } from '../../store/playerStoreTypes';
|
import type { PlayerState, Track } from '../../store/playerStoreTypes';
|
||||||
import type { RadioMetadata } from '@/features/radio';
|
import type { RadioMetadata } from '@/features/radio';
|
||||||
import type { PreviewingTrack } from '../../store/previewStore';
|
import type { PreviewingTrack } from '../../store/previewStore';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
|
|||||||
import { Play, X, Trash2, ListPlus } from 'lucide-react';
|
import { Play, X, Trash2, ListPlus } from 'lucide-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { getPlaylists, deletePlaylist } from '@/features/playlist';
|
import { getPlaylists, deletePlaylist } from '@/features/playlist';
|
||||||
import type { SubsonicPlaylist } from '../../api/subsonicTypes';
|
import type { SubsonicPlaylist } from '@/lib/api/subsonicTypes';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AudioLines, ChevronRight, Heart, Play, Square } from 'lucide-react';
|
import { AudioLines, ChevronRight, Heart, Play, Square } from 'lucide-react';
|
||||||
import type { SubsonicSong } from '../../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import type { Track } from '../../store/playerStoreTypes';
|
import type { Track } from '../../store/playerStoreTypes';
|
||||||
import { previewInputFromSong, usePreviewStore } from '../../store/previewStore';
|
import { previewInputFromSong, usePreviewStore } from '../../store/previewStore';
|
||||||
import { useDragDrop } from '../../contexts/DragDropContext';
|
import { useDragDrop } from '../../contexts/DragDropContext';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SubsonicSong } from '../../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import PagedSongList from '../PagedSongList';
|
import PagedSongList from '../PagedSongList';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { AlbumCoverArtImage } from '../../cover/AlbumCoverArtImage';
|
import { AlbumCoverArtImage } from '../../cover/AlbumCoverArtImage';
|
||||||
import { getRandomSongs } from '../../api/subsonicLibrary';
|
import { getRandomSongs } from '@/lib/api/subsonicLibrary';
|
||||||
import type { SubsonicSong } from '../../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { songToTrack } from '../../utils/playback/songToTrack';
|
import { songToTrack } from '../../utils/playback/songToTrack';
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Play, ListPlus, RefreshCw, Sparkles } from 'lucide-react';
|
import { Play, ListPlus, RefreshCw, Sparkles } from 'lucide-react';
|
||||||
@@ -9,7 +9,7 @@ import { useAuthStore } from '../../store/authStore';
|
|||||||
import { usePlayerStore } from '../../store/playerStore';
|
import { usePlayerStore } from '../../store/playerStore';
|
||||||
import SongRail from '../SongRail';
|
import SongRail from '../SongRail';
|
||||||
import { playSongNow } from '../../utils/playback/playSong';
|
import { playSongNow } from '../../utils/playback/playSong';
|
||||||
import { ndListSongs, ndInvalidateSongsCache } from '../../api/navidromeBrowse';
|
import { ndListSongs, ndInvalidateSongsCache } from '@/lib/api/navidromeBrowse';
|
||||||
import { usePerfProbeFlags } from '../../utils/perf/perfFlags';
|
import { usePerfProbeFlags } from '../../utils/perf/perfFlags';
|
||||||
import { useNavigateToAlbum } from '@/features/album';
|
import { useNavigateToAlbum } from '@/features/album';
|
||||||
import { useNavigateToArtist } from '@/features/artist';
|
import { useNavigateToArtist } from '@/features/artist';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { queueSongStar } from '../store/pendingStarSync';
|
import { queueSongStar } from '../store/pendingStarSync';
|
||||||
import { getSong } from '../api/subsonicLibrary';
|
import { getSong } from '@/lib/api/subsonicLibrary';
|
||||||
import { songToTrack } from '../utils/playback/songToTrack';
|
import { songToTrack } from '../utils/playback/songToTrack';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { CoverArtImage, type CoverArtImageProps } from './CoverArtImage';
|
import { CoverArtImage, type CoverArtImageProps } from './CoverArtImage';
|
||||||
import { useTrackCoverRef } from './useLibraryCoverRef';
|
import { useTrackCoverRef } from './useLibraryCoverRef';
|
||||||
import { COVER_SCOPE_ACTIVE, type CoverServerScope } from './types';
|
import { COVER_SCOPE_ACTIVE, type CoverServerScope } from './types';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useMemo, useState, type CSSProperties, type ImgHTMLAttributes } from 'react';
|
import { useEffect, useMemo, useState, type CSSProperties, type ImgHTMLAttributes } from 'react';
|
||||||
import type { SubsonicArtistInfo } from '../api/subsonicTypes';
|
import type { SubsonicArtistInfo } from '@/lib/api/subsonicTypes';
|
||||||
import { isRealArtistImage } from '../utils/componentHelpers/nowPlayingHelpers';
|
import { isRealArtistImage } from '../utils/componentHelpers/nowPlayingHelpers';
|
||||||
import { CoverArtImage } from './CoverArtImage';
|
import { CoverArtImage } from './CoverArtImage';
|
||||||
import type { CoverArtRef, CoverSurfaceKind } from './types';
|
import type { CoverArtRef, CoverSurfaceKind } from './types';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
buildCoverArtUrl,
|
buildCoverArtUrl,
|
||||||
buildCoverArtUrlForServer,
|
buildCoverArtUrlForServer,
|
||||||
} from '../api/subsonicStreamUrl';
|
} from '@/lib/api/subsonicStreamUrl';
|
||||||
import { getPlaybackServerId } from '../utils/playback/playbackServer';
|
import { getPlaybackServerId } from '../utils/playback/playbackServer';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { connectBaseUrlForServer } from '../utils/server/serverEndpoint';
|
import { connectBaseUrlForServer } from '../utils/server/serverEndpoint';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { buildCoverArtUrlForServer } from '../../api/subsonicStreamUrl';
|
import { buildCoverArtUrlForServer } from '@/lib/api/subsonicStreamUrl';
|
||||||
import { serverShareBaseUrl } from '../../utils/server/serverEndpoint';
|
import { serverShareBaseUrl } from '../../utils/server/serverEndpoint';
|
||||||
import { useAuthStore } from '../../store/authStore';
|
import { useAuthStore } from '../../store/authStore';
|
||||||
import type { CoverArtRef } from '../types';
|
import type { CoverArtRef } from '../types';
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import { getPlaybackServerId } from '../utils/playback/playbackServer';
|
import { getPlaybackServerId } from '../utils/playback/playbackServer';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { coverServerScopeForServerId } from './serverScope';
|
import { coverServerScopeForServerId } from './serverScope';
|
||||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import type { CoverArtId, CoverArtRef, CoverCacheKind, CoverServerScope } from './types';
|
import type { CoverArtId, CoverArtRef, CoverCacheKind, CoverServerScope } from './types';
|
||||||
import {
|
import {
|
||||||
albumHasDistinctDiscCovers,
|
albumHasDistinctDiscCovers,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
* stay in sync with `resolve_album_cover` / `resolve_artist_cover` there.
|
* stay in sync with `resolve_album_cover` / `resolve_artist_cover` there.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { SubsonicAlbum, SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicAlbum, SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import type { CoverArtRef, CoverCacheKind, CoverServerScope } from './types';
|
import type { CoverArtRef, CoverCacheKind, CoverServerScope } from './types';
|
||||||
|
|
||||||
/** Resolved cover identity — maps 1:1 to Rust `CoverEntry`. */
|
/** Resolved cover identity — maps 1:1 to Rust `CoverEntry`. */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { coverPrefetchRegister } from './prefetchRegistry';
|
import { coverPrefetchRegister } from './prefetchRegistry';
|
||||||
import {
|
import {
|
||||||
resolveAlbumCoverRefsFromLibrary,
|
resolveAlbumCoverRefsFromLibrary,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useMemo, useState, type Dispatch, type SetStateAction } from 'react';
|
import { useEffect, useMemo, useState, type Dispatch, type SetStateAction } from 'react';
|
||||||
import type { SubsonicSong } from '../api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { usePlayerStore } from '../store/playerStore';
|
import { usePlayerStore } from '../store/playerStore';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { coverCachePeekBatch } from '../api/coverCache';
|
import { coverCachePeekBatch } from '../api/coverCache';
|
||||||
import type { SubsonicAlbum } from '../api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import { coverEnsureQueued, ensureArtistBackdropQueued } from './ensureQueue';
|
import { coverEnsureQueued, ensureArtistBackdropQueued } from './ensureQueue';
|
||||||
import { getDiskSrcForGrid, rememberGridDiskSrc } from './diskSrcLookup';
|
import { getDiskSrcForGrid, rememberGridDiskSrc } from './diskSrcLookup';
|
||||||
import { albumCoverRef, artistCoverRef } from './ref';
|
import { albumCoverRef, artistCoverRef } from './ref';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { api } from '@/api/subsonicClient';
|
import { api } from '@/lib/api/subsonicClient';
|
||||||
import type { AlbumInfo } from '@/api/subsonicTypes';
|
import type { AlbumInfo } from '@/lib/api/subsonicTypes';
|
||||||
|
|
||||||
export async function getAlbumInfo2(albumId: string): Promise<AlbumInfo | null> {
|
export async function getAlbumInfo2(albumId: string): Promise<AlbumInfo | null> {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SubsonicAlbum } from '@/api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import React, { memo, useMemo } from 'react';
|
import React, { memo, useMemo } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useNavigateToAlbum } from '@/features/album/hooks/useNavigateToAlbum';
|
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 userEvent from '@testing-library/user-event';
|
||||||
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
|
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
|
||||||
import AlbumHeader from '@/features/album/components/AlbumHeader';
|
import AlbumHeader from '@/features/album/components/AlbumHeader';
|
||||||
import type { SubsonicSong } from '@/api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
|
|
||||||
const navigate = vi.fn();
|
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 { type CSSProperties, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|||||||
@@ -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 React, { useRef, useState, useEffect, useLayoutEffect, useMemo } from 'react';
|
||||||
import AlbumCard from '@/features/album/components/AlbumCard';
|
import AlbumCard from '@/features/album/components/AlbumCard';
|
||||||
import { ChevronLeft, ChevronRight, ArrowRight } from 'lucide-react';
|
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 type { Track } from '@/store/playerStoreTypes';
|
||||||
import React, { useMemo, useState, useEffect } from 'react';
|
import React, { useMemo, useState, useEffect } from 'react';
|
||||||
import { useTracklistColumns } from '@/utils/useTracklistColumns';
|
import { useTracklistColumns } from '@/utils/useTracklistColumns';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AudioLines } from 'lucide-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 type { Track } from '@/store/playerStoreTypes';
|
||||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||||
import { formatLongDuration } from '@/utils/format/formatDuration';
|
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 React, { useEffect, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ndListLosslessAlbumsPage } from '@/api/navidromeBrowse';
|
import { ndListLosslessAlbumsPage } from '@/lib/api/navidromeBrowse';
|
||||||
import AlbumRow from '@/features/album/components/AlbumRow';
|
import AlbumRow from '@/features/album/components/AlbumRow';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { AudioLines, ChevronRight, Heart, Play, Square } from 'lucide-react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import type { ColDef } from '@/utils/useTracklistColumns';
|
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 type { Track } from '@/store/playerStoreTypes';
|
||||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||||
import { useSelectionStore } from '@/store/selectionStore';
|
import { useSelectionStore } from '@/store/selectionStore';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
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.
|
// Keep pagination termination rules aligned with the hook implementation.
|
||||||
function resolveHasMoreAfterPage(
|
function resolveHasMoreAfterPage(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import type { SubsonicAlbum } from '@/api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import {
|
import {
|
||||||
coverTrafficBeginGridPagination,
|
coverTrafficBeginGridPagination,
|
||||||
coverTrafficEndGridPagination,
|
coverTrafficEndGridPagination,
|
||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
type AlbumBrowseQuery,
|
type AlbumBrowseQuery,
|
||||||
type GenreFilterOption,
|
type GenreFilterOption,
|
||||||
} from '@/utils/library/albumBrowseLoad';
|
} from '@/utils/library/albumBrowseLoad';
|
||||||
import { libraryScopeForServer } from '@/api/subsonicClient';
|
import { libraryScopeForServer } from '@/lib/api/subsonicClient';
|
||||||
import {
|
import {
|
||||||
ALBUM_YEAR_FILTER_DEBOUNCE_MS,
|
ALBUM_YEAR_FILTER_DEBOUNCE_MS,
|
||||||
resolveAlbumYearBounds,
|
resolveAlbumYearBounds,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
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 { useAuthStore } from '@/store/authStore';
|
||||||
import {
|
import {
|
||||||
loadAlbumFromLibraryIndex,
|
loadAlbumFromLibraryIndex,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo, useState } from 'react';
|
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';
|
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 { useEffect, useRef, useState, type RefObject } from 'react';
|
||||||
import { useLocation, useNavigationType, type NavigationType } from 'react-router-dom';
|
import { useLocation, useNavigationType, type NavigationType } from 'react-router-dom';
|
||||||
import type { SubsonicAlbum } from '@/api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import {
|
import {
|
||||||
DEFAULT_ALBUM_BROWSE_RETURN_FILTERS,
|
DEFAULT_ALBUM_BROWSE_RETURN_FILTERS,
|
||||||
type AlbumBrowseReturnFilters,
|
type AlbumBrowseReturnFilters,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback, useEffect, useRef } from 'react';
|
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 { useSelectionStore } from '@/store/selectionStore';
|
||||||
import { useDragDrop } from '@/contexts/DragDropContext';
|
import { useDragDrop } from '@/contexts/DragDropContext';
|
||||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
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 { useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
BROWSE_TEXT_DEBOUNCE_NETWORK_MS,
|
BROWSE_TEXT_DEBOUNCE_NETWORK_MS,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
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 { dedupeById } from '@/utils/dedupeById';
|
||||||
import type { AlbumBrowseSort } from '@/utils/library/albumBrowseSort';
|
import type { AlbumBrowseSort } from '@/utils/library/albumBrowseSort';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { buildDownloadUrl } from '@/api/subsonicStreamUrl';
|
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
|
||||||
import { setRating, star, unstar } from '@/api/subsonicStarRating';
|
import { setRating, star, unstar } from '@/lib/api/subsonicStarRating';
|
||||||
import { queueSongStar, queueSongRating } from '@/store/pendingStarSync';
|
import { queueSongStar, queueSongRating } from '@/store/pendingStarSync';
|
||||||
import { getAlbumForServer } from '@/api/subsonicLibrary';
|
import { getAlbumForServer } from '@/lib/api/subsonicLibrary';
|
||||||
import { getArtistInfo } from '@/features/artist';
|
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 { songToTrack } from '@/utils/playback/songToTrack';
|
||||||
import { shuffleArray } from '@/utils/playback/shuffleArray';
|
import { shuffleArray } from '@/utils/playback/shuffleArray';
|
||||||
import React, { useEffect, useState, useCallback, useMemo } from 'react';
|
import React, { useEffect, useState, useCallback, useMemo } from 'react';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { buildDownloadUrl } from '@/api/subsonicStreamUrl';
|
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
|
||||||
import { resolveAlbum } from '@/features/offline';
|
import { resolveAlbum } from '@/features/offline';
|
||||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||||
import { useState, useEffect, useLayoutEffect, useRef, useMemo } from 'react';
|
import { useState, useEffect, useLayoutEffect, useRef, useMemo } from 'react';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { search } from '@/api/subsonicSearch';
|
import { search } from '@/lib/api/subsonicSearch';
|
||||||
import type { SubsonicAlbum } from '@/api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { ChevronLeft } from 'lucide-react';
|
import { ChevronLeft } from 'lucide-react';
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { buildDownloadUrl } from '@/api/subsonicStreamUrl';
|
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
|
||||||
import { resolveAlbum } from '@/features/offline';
|
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 { songToTrack } from '@/utils/playback/songToTrack';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import AlbumCard from '@/features/album/components/AlbumCard';
|
import AlbumCard from '@/features/album/components/AlbumCard';
|
||||||
import { LOSSLESS_MODE_QUERY } from '@/utils/library/losslessMode';
|
import { LOSSLESS_MODE_QUERY } from '@/utils/library/losslessMode';
|
||||||
import { ndListLosslessAlbumsPage } from '@/api/navidromeBrowse';
|
import { ndListLosslessAlbumsPage } from '@/lib/api/navidromeBrowse';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useOfflineStore } from '@/features/offline';
|
import { useOfflineStore } from '@/features/offline';
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { buildDownloadUrl } from '@/api/subsonicStreamUrl';
|
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
|
||||||
import { getAlbumsByGenre } from '@/api/subsonicGenres';
|
import { getAlbumsByGenre } from '@/lib/api/subsonicGenres';
|
||||||
import { getAlbumList } from '@/api/subsonicLibrary';
|
import { getAlbumList } from '@/lib/api/subsonicLibrary';
|
||||||
import { resolveAlbum } from '@/features/offline';
|
import { resolveAlbum } from '@/features/offline';
|
||||||
import type { SubsonicAlbum } from '@/api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import { dedupeById } from '@/utils/dedupeById';
|
import { dedupeById } from '@/utils/dedupeById';
|
||||||
import { shuffleArray } from '@/utils/playback/shuffleArray';
|
import { shuffleArray } from '@/utils/playback/shuffleArray';
|
||||||
import React, { useEffect, useLayoutEffect, useState, useCallback, useRef } from 'react';
|
import React, { useEffect, useLayoutEffect, useState, useCallback, useRef } from 'react';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it, beforeEach } from 'vitest';
|
import { describe, expect, it, beforeEach } from 'vitest';
|
||||||
import type { SubsonicAlbum } from '@/api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import {
|
import {
|
||||||
DEFAULT_ALBUM_BROWSE_RETURN_FILTERS,
|
DEFAULT_ALBUM_BROWSE_RETURN_FILTERS,
|
||||||
DEFAULT_ALBUM_BROWSE_SORT,
|
DEFAULT_ALBUM_BROWSE_SORT,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import type { SubsonicAlbum } from '@/api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import type { AlbumBrowseSort } from '@/utils/library/browseTextSearch';
|
import type { AlbumBrowseSort } from '@/utils/library/browseTextSearch';
|
||||||
|
|
||||||
export const DEFAULT_ALBUM_BROWSE_SORT: AlbumBrowseSort = 'alphabeticalByName';
|
export const DEFAULT_ALBUM_BROWSE_SORT: AlbumBrowseSort = 'alphabeticalByName';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
deriveAlbumArtistRefs,
|
deriveAlbumArtistRefs,
|
||||||
deriveAlbumHeaderArtistRefs,
|
deriveAlbumHeaderArtistRefs,
|
||||||
} from '@/features/album/utils/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';
|
import { makeSubsonicSong } from '@/test/helpers/factories';
|
||||||
|
|
||||||
const baseAlbum = (): SubsonicAlbum => ({
|
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';
|
import { coerceOpenArtistRefs } from '@/features/artist';
|
||||||
|
|
||||||
function nonEmpty(refs: SubsonicOpenArtistRef[]): refs is SubsonicOpenArtistRef[] {
|
function nonEmpty(refs: SubsonicOpenArtistRef[]): refs is SubsonicOpenArtistRef[] {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SubsonicAlbum } from '@/api/subsonicTypes';
|
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { renderToStaticMarkup } from 'react-dom/server';
|
import { renderToStaticMarkup } from 'react-dom/server';
|
||||||
import { coverArtRef } from '@/cover/ref';
|
import { coverArtRef } from '@/cover/ref';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { getAlbumList } from '@/api/subsonicLibrary';
|
import { getAlbumList } from '@/lib/api/subsonicLibrary';
|
||||||
import { coverArtRef } from '@/cover/ref';
|
import { coverArtRef } from '@/cover/ref';
|
||||||
import { loadCoverBlobForExport } from '@/cover/integrations/export';
|
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 { albumArtistDisplayName } from '@/features/album/utils/deriveAlbumHeaderArtistRefs';
|
||||||
import { writeFile } from '@tauri-apps/plugin-fs';
|
import { writeFile } from '@tauri-apps/plugin-fs';
|
||||||
import { downloadDir, join } from '@tauri-apps/api/path';
|
import { downloadDir, join } from '@tauri-apps/api/path';
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
vi.mock('@/api/subsonicClient', () => ({
|
vi.mock('@/lib/api/subsonicClient', () => ({
|
||||||
api: vi.fn(),
|
api: vi.fn(),
|
||||||
apiForServer: vi.fn(),
|
apiForServer: vi.fn(),
|
||||||
libraryFilterParams: () => ({}),
|
libraryFilterParams: () => ({}),
|
||||||
libraryFilterParamsForServer: () => ({}),
|
libraryFilterParamsForServer: () => ({}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock('@/api/subsonicLibrary', () => ({
|
vi.mock('@/lib/api/subsonicLibrary', () => ({
|
||||||
filterSongsToActiveLibrary: async (songs: unknown[]) => songs,
|
filterSongsToActiveLibrary: async (songs: unknown[]) => songs,
|
||||||
filterSongsToServerLibrary: async (songs: unknown[]) => songs,
|
filterSongsToServerLibrary: async (songs: unknown[]) => songs,
|
||||||
similarSongsRequestCount: (count: number) => count,
|
similarSongsRequestCount: (count: number) => count,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { api } from '@/api/subsonicClient';
|
import { api } from '@/lib/api/subsonicClient';
|
||||||
import { fetchSimilarTracksRouted } from '@/features/artist/api/subsonicArtists';
|
import { fetchSimilarTracksRouted } from '@/features/artist/api/subsonicArtists';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { api, apiForServer, libraryFilterParams, libraryFilterParamsForServer } from '@/api/subsonicClient';
|
import { api, apiForServer, libraryFilterParams, libraryFilterParamsForServer } from '@/lib/api/subsonicClient';
|
||||||
import { filterSongsToServerLibrary } from '@/api/subsonicLibrary';
|
import { filterSongsToServerLibrary } from '@/lib/api/subsonicLibrary';
|
||||||
import { filterSongsToActiveLibrary, similarSongsRequestCount } from '@/api/subsonicLibrary';
|
import { filterSongsToActiveLibrary, similarSongsRequestCount } from '@/lib/api/subsonicLibrary';
|
||||||
import {
|
import {
|
||||||
FEATURE_AUDIOMUSE_SIMILAR_TRACKS,
|
FEATURE_AUDIOMUSE_SIMILAR_TRACKS,
|
||||||
OP_SIMILAR_TRACKS,
|
OP_SIMILAR_TRACKS,
|
||||||
@@ -12,7 +12,7 @@ import type {
|
|||||||
SubsonicArtist,
|
SubsonicArtist,
|
||||||
SubsonicArtistInfo,
|
SubsonicArtistInfo,
|
||||||
SubsonicSong,
|
SubsonicSong,
|
||||||
} from '@/api/subsonicTypes';
|
} from '@/lib/api/subsonicTypes';
|
||||||
|
|
||||||
export async function getArtists(): Promise<SubsonicArtist[]> {
|
export async function getArtists(): Promise<SubsonicArtist[]> {
|
||||||
type ArtistIndexEntry = { artist?: SubsonicArtist | SubsonicArtist[] };
|
type ArtistIndexEntry = { artist?: SubsonicArtist | SubsonicArtist[] };
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { SubsonicArtist } from '@/api/subsonicTypes';
|
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
|
||||||
import { ArtistCoverArtImage } from '@/cover/ArtistCoverArtImage';
|
import { ArtistCoverArtImage } from '@/cover/ArtistCoverArtImage';
|
||||||
import {
|
import {
|
||||||
COVER_DENSE_ARTIST_LIST_CSS_PX,
|
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 React, { useMemo } from 'react';
|
||||||
import { Users } from 'lucide-react';
|
import { Users } from 'lucide-react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
ArrowLeft, Camera, Check, HardDriveDownload, Heart,
|
ArrowLeft, Camera, Check, HardDriveDownload, Heart,
|
||||||
Loader2, Play, Radio, Share2, Shuffle, Users,
|
Loader2, Play, Radio, Share2, Shuffle, Users,
|
||||||
} from 'lucide-react';
|
} 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 { useOfflineStore } from '@/features/offline';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useThemeStore } from '@/store/themeStore';
|
import { useThemeStore } from '@/store/themeStore';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { Fragment } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
import type { SubsonicArtist } from '@/api/subsonicTypes';
|
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
|
||||||
import { useIsMobile } from '@/hooks/useIsMobile';
|
import { useIsMobile } from '@/hooks/useIsMobile';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AudioLines, ChevronRight, Play, Square } from 'lucide-react';
|
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 { usePlayerStore } from '@/store/playerStore';
|
||||||
import { previewInputFromSong, usePreviewStore } from '@/store/previewStore';
|
import { previewInputFromSong, usePreviewStore } from '@/store/previewStore';
|
||||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||||
|
|||||||
@@ -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 React, { useRef, useState, useEffect, useLayoutEffect } from 'react';
|
||||||
import ArtistCardLocal from '@/features/artist/components/ArtistCardLocal';
|
import ArtistCardLocal from '@/features/artist/components/ArtistCardLocal';
|
||||||
import { ChevronLeft, ChevronRight, ArrowRight } from 'lucide-react';
|
import { ChevronLeft, ChevronRight, ArrowRight } from 'lucide-react';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Check } from 'lucide-react';
|
import { Check } from 'lucide-react';
|
||||||
import type { TFunction } from 'i18next';
|
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 type { PlayerState } from '@/store/playerStoreTypes';
|
||||||
import { ARTISTS_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
|
import { ARTISTS_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
|
||||||
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
|
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { Virtualizer } from '@tanstack/react-virtual';
|
import type { Virtualizer } from '@tanstack/react-virtual';
|
||||||
import type { TFunction } from 'i18next';
|
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 type { PlayerState } from '@/store/playerStoreTypes';
|
||||||
import { OTHER_BUCKET, type ArtistListFlatRow } from '@/features/artist/utils/artistsHelpers';
|
import { OTHER_BUCKET, type ArtistListFlatRow } from '@/features/artist/utils/artistsHelpers';
|
||||||
import { ArtistRowAvatar } from '@/features/artist/components/ArtistAvatars';
|
import { ArtistRowAvatar } from '@/features/artist/components/ArtistAvatars';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import type { SubsonicOpenArtistRef } from '@/api/subsonicTypes';
|
import type { SubsonicOpenArtistRef } from '@/lib/api/subsonicTypes';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
refs: SubsonicOpenArtistRef[];
|
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'>;
|
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 React from 'react';
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
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('@/features/artist/api/subsonicArtists');
|
||||||
vi.mock('@/api/subsonicSearch');
|
vi.mock('@/lib/api/subsonicSearch');
|
||||||
|
|
||||||
import { getArtist, getArtistInfo, getTopSongs } from '@/features/artist/api/subsonicArtists';
|
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';
|
import { useArtistDetailData } from '@/features/artist/hooks/useArtistDetailData';
|
||||||
|
|
||||||
const mockArtistInfo = vi.mocked(getArtistInfo) as unknown as {
|
const mockArtistInfo = vi.mocked(getArtistInfo) as unknown as {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
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 { getArtist, getArtistForServer, getArtistInfo, getTopSongs } from '@/features/artist/api/subsonicArtists';
|
||||||
import type {
|
import type {
|
||||||
SubsonicAlbum, SubsonicArtist, SubsonicArtistInfo, SubsonicSong,
|
SubsonicAlbum, SubsonicArtist, SubsonicArtistInfo, SubsonicSong,
|
||||||
} from '@/api/subsonicTypes';
|
} from '@/lib/api/subsonicTypes';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
|
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
|
||||||
import { loadArtistFromLibraryIndex } from '@/features/offline';
|
import { loadArtistFromLibraryIndex } from '@/features/offline';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { getArtistInfoForServer } from '@/features/artist/api/subsonicArtists';
|
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';
|
import { makeCache } from '@/utils/cache/nowPlayingCache';
|
||||||
|
|
||||||
const artistInfoCache = makeCache<SubsonicArtistInfo | null>();
|
const artistInfoCache = makeCache<SubsonicArtistInfo | null>();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { getMusicNetworkRuntime } from '@/music-network';
|
import { getMusicNetworkRuntime } from '@/music-network';
|
||||||
import { search } from '@/api/subsonicSearch';
|
import { search } from '@/lib/api/subsonicSearch';
|
||||||
import type { SubsonicArtist, SubsonicArtistInfo } from '@/api/subsonicTypes';
|
import type { SubsonicArtist, SubsonicArtistInfo } from '@/lib/api/subsonicTypes';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
|
|
||||||
export interface ArtistSimilarArtistsResult {
|
export interface ArtistSimilarArtistsResult {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { getArtists } from '@/features/artist/api/subsonicArtists';
|
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 { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { dedupeById } from '@/utils/dedupeById';
|
import { dedupeById } from '@/utils/dedupeById';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import type { SubsonicArtist } from '@/api/subsonicTypes';
|
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
|
||||||
import { usePlayerStore } from '@/store/playerStore';
|
import { usePlayerStore } from '@/store/playerStore';
|
||||||
import { ALL_SENTINEL, artistLetterBucket, compareBuckets, type ArtistListFlatRow } from '@/features/artist/utils/artistsHelpers';
|
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 { useEffect, useRef, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
BROWSE_TEXT_DEBOUNCE_NETWORK_MS,
|
BROWSE_TEXT_DEBOUNCE_NETWORK_MS,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useCoverArt } from '@/cover/useCoverArt';
|
import { useCoverArt } from '@/cover/useCoverArt';
|
||||||
import { useArtistCoverRef } from '@/cover/useLibraryCoverRef';
|
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 { useEffect, useState, Fragment, useMemo } from 'react';
|
||||||
import { useParams, useSearchParams } from 'react-router-dom';
|
import { useParams, useSearchParams } from 'react-router-dom';
|
||||||
import { AlbumCard } from '@/features/album';
|
import { AlbumCard } from '@/features/album';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { SubsonicArtist } from '@/api/subsonicTypes';
|
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
|
||||||
|
|
||||||
export const ALL_SENTINEL = 'ALL';
|
export const ALL_SENTINEL = 'ALL';
|
||||||
/** Catch-all bucket for names that start with neither an A–Z letter nor a digit
|
/** Catch-all bucket for names that start with neither an A–Z letter nor a digit
|
||||||
|
|||||||
@@ -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. */
|
/** Subsonic JSON may return one ref object instead of a one-element array. */
|
||||||
export function coerceOpenArtistRefs(
|
export function coerceOpenArtistRefs(
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import type { TFunction } from 'i18next';
|
import type { TFunction } from 'i18next';
|
||||||
import { uploadArtistImage } from '@/features/playlist';
|
import { uploadArtistImage } from '@/features/playlist';
|
||||||
import { setRating, star, unstar } from '@/api/subsonicStarRating';
|
import { setRating, star, unstar } from '@/lib/api/subsonicStarRating';
|
||||||
import type { SubsonicArtist } from '@/api/subsonicTypes';
|
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { copyEntityShareLink } from '@/utils/share/copyEntityShareLink';
|
import { copyEntityShareLink } from '@/utils/share/copyEntityShareLink';
|
||||||
import { invalidateCoverArt } from '@/utils/imageCache';
|
import { invalidateCoverArt } from '@/utils/imageCache';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
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 * as offlineMediaResolve from '@/features/offline';
|
||||||
import { fetchArtistDetailTracks, runArtistDetailPlayTopSong } from '@/features/artist/utils/runArtistDetailPlay';
|
import { fetchArtistDetailTracks, runArtistDetailPlayTopSong } from '@/features/artist/utils/runArtistDetailPlay';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { TFunction } from 'i18next';
|
import type { TFunction } from 'i18next';
|
||||||
import { getSimilarSongs2, getTopSongs } from '@/features/artist/api/subsonicArtists';
|
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 type { Track } from '@/store/playerStoreTypes';
|
||||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||||
import { runBulkPlayAll, runBulkShuffle } from '@/utils/playback/runBulkPlay';
|
import { runBulkPlayAll, runBulkShuffle } from '@/utils/playback/runBulkPlay';
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
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';
|
import { sortArtistAlbumsByYear } from '@/features/artist/utils/sortArtistAlbums';
|
||||||
|
|
||||||
const album = (id: string, name: string, year?: number): SubsonicAlbum => ({
|
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';
|
export type ArtistAlbumYearOrder = 'yearDesc' | 'yearAsc';
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import type {
|
import type {
|
||||||
SubsonicAlbum, SubsonicArtist, SubsonicPlaylist,
|
SubsonicAlbum, SubsonicArtist, SubsonicPlaylist,
|
||||||
} from '@/api/subsonicTypes';
|
} from '@/lib/api/subsonicTypes';
|
||||||
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||||
import type { SourceTab } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
import type { SourceTab } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||||
import BrowserRow from '@/features/deviceSync/components/BrowserRow';
|
import BrowserRow from '@/features/deviceSync/components/BrowserRow';
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { getPlaylists } from '@/features/playlist';
|
import { getPlaylists } from '@/features/playlist';
|
||||||
import { getArtists, getArtist } from '@/features/artist';
|
import { getArtists, getArtist } from '@/features/artist';
|
||||||
import { getAlbumList } from '@/api/subsonicLibrary';
|
import { getAlbumList } from '@/lib/api/subsonicLibrary';
|
||||||
import { search as searchSubsonic } from '@/api/subsonicSearch';
|
import { search as searchSubsonic } from '@/lib/api/subsonicSearch';
|
||||||
import type {
|
import type {
|
||||||
SubsonicAlbum, SubsonicArtist, SubsonicPlaylist,
|
SubsonicAlbum, SubsonicArtist, SubsonicPlaylist,
|
||||||
} from '@/api/subsonicTypes';
|
} from '@/lib/api/subsonicTypes';
|
||||||
import type { SourceTab } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
import type { SourceTab } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||||
|
|
||||||
export interface DeviceSyncBrowserResult {
|
export interface DeviceSyncBrowserResult {
|
||||||
|
|||||||
@@ -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 React, { useState, useCallback, useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDeviceSyncStore, DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
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';
|
export type SourceTab = 'playlists' | 'albums' | 'artists';
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { TFunction } from 'i18next';
|
import type { TFunction } from 'i18next';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { buildDownloadUrl } from '@/api/subsonicStreamUrl';
|
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
|
||||||
import type { SubsonicSong } from '@/api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { useDeviceSyncStore, type DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
import { useDeviceSyncStore, type DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||||
import { useDeviceSyncJobStore } from '@/features/deviceSync/store/deviceSyncJobStore';
|
import { useDeviceSyncJobStore } from '@/features/deviceSync/store/deviceSyncJobStore';
|
||||||
import { showToast } from '@/utils/ui/toast';
|
import { showToast } from '@/utils/ui/toast';
|
||||||
@@ -37,7 +37,7 @@ export async function runDeviceSyncSummaryPrompt(deps: RunDeviceSyncSummaryDeps)
|
|||||||
setPreSyncOpen(true);
|
setPreSyncOpen(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { getClient } = await import('@/api/subsonicClient');
|
const { getClient } = await import('@/lib/api/subsonicClient');
|
||||||
const { baseUrl, params } = getClient();
|
const { baseUrl, params } = getClient();
|
||||||
const payload = await invoke<SyncDelta>('calculate_sync_payload', {
|
const payload = await invoke<SyncDelta>('calculate_sync_payload', {
|
||||||
sources,
|
sources,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
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 { applyLegacyTemplate } from '@/features/deviceSync/utils/deviceSyncLegacyTemplate';
|
||||||
import { trackToSyncInfo } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
import { trackToSyncInfo } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||||
import { fetchTracksForSource } from '@/utils/playback/fetchTracksForSource';
|
import { fetchTracksForSource } from '@/utils/playback/fetchTracksForSource';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { AudioLines, ChevronRight, Play, Square, X } from 'lucide-react';
|
import { AudioLines, ChevronRight, Play, Square, X } from 'lucide-react';
|
||||||
import type { ColDef } from '@/utils/useTracklistColumns';
|
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 { codecLabel } from '@/utils/componentHelpers/playlistDetailHelpers';
|
||||||
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
|
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
|
||||||
import i18n from '@/i18n';
|
import i18n from '@/i18n';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useMemo, useRef } from 'react';
|
import React, { useMemo, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ListPlus, Play, SlidersHorizontal, X } from 'lucide-react';
|
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 { usePlayerStore } from '@/store/playerStore';
|
||||||
import { useSelectionStore } from '@/store/selectionStore';
|
import { useSelectionStore } from '@/store/selectionStore';
|
||||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user