mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
refactor(playback): move the audio engine into features/playback
Relocate the playback/queue/transport/audio-output engine out of the type-first
store/ + utils/playback/ + utils/audio/ dirs into a cohesive src/features/playback/,
structure-preserving:
store/<x> -> features/playback/store/<x>
store/audioListenerSetup/<x> -> features/playback/store/audioListenerSetup/<x>
utils/playback/<x> -> features/playback/utils/playback/<x>
utils/audio/<x> -> features/playback/utils/audio/<x>
184 files moved (107 source + 77 tests), 365 consumers rewritten. Pure move — no
behavior change, no state-split (the playerStore state-split stays a separate M5
question). Enabled by this session's decouple seams (artist/offline/orbit/auth →
core registries), so the engine carries no inbound core->feature inversion: store/
now holds only the 50 cross-cutting global stores (auth family, the seams, library
index, UI/settings stores).
KEPT OUT of the move (would re-create global->engine edges): the 3 pure config
helpers utils/audio/{loudnessPreAnalysisSlider,hiResCrossfadeResample} +
utils/playback/autodjOverlapCap (authStore + settings UI read them — they stay in
utils/). Ambiguous view-state stores (eqStore, queueToolbarStore,
playerBarLayoutStore) stay global (no engine imports).
Consumers use DEEP paths (@/features/playback/...), no barrel — matches the lib/
approach and avoids barrel-mock-collapse across the 140 usePlayerStore consumers.
Two tolerated type-only core->feature edges remain (localPlaybackStore->QueueItemRef,
localPlaybackMigration->HotCacheEntry, both erased).
tsc 0, lint 0, full suite 319/2353 green, iron-rule clean (no runtime store->feature
import). Behavior-touching only via the prerequisite bridge seam (already QA-flagged);
the move itself is pure.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useAuthStore } from './store/authStore';
|
||||
import { usePlayerStore } from './store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useLyricsStore } from './store/lyricsStore';
|
||||
import { useThemeStore } from './store/themeStore';
|
||||
import { useInstalledThemesStore } from './store/installedThemesStore';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { makeTrack } from '../test/helpers/factories';
|
||||
import { resetAllStores } from '../test/helpers/storeReset';
|
||||
import { invokeMock, onInvoke } from '../test/mocks/tauri';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useThemeStore } from '../store/themeStore';
|
||||
import { coverIndexKeyFromRef, coverStorageKeyFromRef } from '../cover/storageKeys';
|
||||
import { connectBaseUrlForServer } from '../utils/server/serverEndpoint';
|
||||
import { serverIndexKeyForProfile } from '../utils/server/serverIndexKey';
|
||||
import { getPlaybackServerId } from '../utils/playback/playbackServer';
|
||||
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||
import { restBaseFromUrl } from '@/lib/api/subsonicClient';
|
||||
import type { CoverArtRef, CoverArtTier } from '../cover/types';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { Suspense, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { ensurePlaybackServerActive } from '../utils/playback/playbackServer';
|
||||
import { ensurePlaybackServerActive } from '@/features/playback/utils/playback/playbackServer';
|
||||
import { navigatePathWithAlbumReturnTo, shouldSkipMainScrollResetOnRouteChange } from '../utils/navigation/albumDetailNavigation';
|
||||
import { getCurrentWebview } from '@tauri-apps/api/webview';
|
||||
import { PanelRight } from 'lucide-react';
|
||||
@@ -58,13 +58,13 @@ import { IS_LINUX, IS_MACOS } from '@/lib/util/platform';
|
||||
import { useConnectionStatus } from '../hooks/useConnectionStatus';
|
||||
import { useIdlePlayQueuePull } from '../hooks/useIdlePlayQueuePull';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import '../store/previewPlayerVolumeSync';
|
||||
import '../store/queueResolverBridge';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import '@/features/playback/store/previewPlayerVolumeSync';
|
||||
import '@/features/playback/store/queueResolverBridge';
|
||||
import { useThemeStore } from '../store/themeStore';
|
||||
import { useFontStore } from '../store/fontStore';
|
||||
import { useEqStore } from '../store/eqStore';
|
||||
import { usePlaybackRateStore } from '../store/playbackRateStore';
|
||||
import { usePlaybackRateStore } from '@/features/playback/store/playbackRateStore';
|
||||
import { usePlaybackRateOrbitSync } from '@/features/orbit';
|
||||
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
|
||||
import {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
import { initAudioListeners } from '../store/initAudioListeners';
|
||||
import '../store/playbackEngineBridgeRegister'; // installs the playback-engine bridge at boot
|
||||
import { initAudioListeners } from '@/features/playback/store/initAudioListeners';
|
||||
import '@/features/playback/store/playbackEngineBridgeRegister'; // installs the playback-engine bridge at boot
|
||||
import { lazy, Suspense, useEffect, useState } from 'react';
|
||||
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
* payload) and the failure-tolerant path (no throw when invoke or storage
|
||||
* misbehaves).
|
||||
*/
|
||||
import { installQueueUndoHotkey } from '../store/queueUndoHotkey';
|
||||
import { installQueueUndoHotkey } from '@/features/playback/store/queueUndoHotkey';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
vi.mock('@tauri-apps/api/core', () => ({
|
||||
invoke: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../store/queueUndoHotkey', () => ({
|
||||
vi.mock('@/features/playback/store/queueUndoHotkey', () => ({
|
||||
installQueueUndoHotkey: vi.fn(),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { installQueueUndoHotkey } from '../store/queueUndoHotkey';
|
||||
import { installQueueUndoHotkey } from '@/features/playback/store/queueUndoHotkey';
|
||||
import { configureStartupSplash } from './startupSplash';
|
||||
import { setupMusicNetworkRuntime } from './musicNetworkBridge';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
|
||||
@@ -2,8 +2,8 @@ import { getArtist, getArtistInfo } from '@/lib/api/subsonicArtists';
|
||||
import { filterAlbumsToActiveLibrary } from '@/lib/api/subsonicLibrary';
|
||||
import { resolveAlbum, resolveMediaServerId } from '@/features/offline';
|
||||
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||
import { songToTrack } from '../utils/playback/songToTrack';
|
||||
import { shuffleArray } from '../utils/playback/shuffleArray';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { shuffleArray } from '@/features/playback/utils/playback/shuffleArray';
|
||||
import React, { memo, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -18,9 +18,9 @@ import {
|
||||
writeBecauseYouLikeCache,
|
||||
type BecauseYouLikeAnchor,
|
||||
} from '../store/becauseYouLikeCache';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { playAlbum, playAlbumShuffled } from '../utils/playback/playAlbum';
|
||||
import { playAlbum, playAlbumShuffled } from '@/features/playback/utils/playback/playAlbum';
|
||||
import { useLongPressAction } from '@/lib/hooks/useLongPressAction';
|
||||
import { LongPressWaveOverlay } from './LongPressWaveOverlay';
|
||||
import { formatHumanHoursMinutes } from '@/lib/format/formatHumanDuration';
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState } from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Disc3, Search, Music4, AudioLines, MoreHorizontal } from 'lucide-react';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { MobileSearchOverlay } from '@/features/search';
|
||||
import MobileMoreOverlay from './MobileMoreOverlay';
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ vi.mock('@/features/offline/hooks/useOfflineBrowseContext', () => ({
|
||||
|
||||
import ContextMenu from './ContextMenu';
|
||||
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { resetAllStores } from '@/test/helpers/storeReset';
|
||||
import { makeTrack, makeServer, seedQueue } from '@/test/helpers/factories';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import type { Track } from '../store/playerStoreTypes';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Save, Trash2, RotateCcw } from 'lucide-react';
|
||||
import CustomSelect from '@/ui/CustomSelect';
|
||||
import { useEqStore, EQ_BANDS, BUILTIN_PRESETS } from '../store/eqStore';
|
||||
import { useThemeStore } from '../store/themeStore';
|
||||
import { drawCurve } from '../utils/audio/eqCurve';
|
||||
import { drawCurve } from '@/features/playback/utils/audio/eqCurve';
|
||||
import VerticalFader from './equalizer/VerticalFader';
|
||||
import AutoEqSection from './equalizer/AutoEqSection';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getRandomAlbums } from '@/lib/api/subsonicLibrary';
|
||||
import { resolveAlbum, resolveMediaServerId } from '@/features/offline';
|
||||
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||
import { songToTrack } from '../utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
|
||||
import { useNavigateToAlbum } from '@/features/album';
|
||||
import { Play, ListPlus, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
@@ -11,7 +11,7 @@ import { useArtistBanner, useArtistFanart } from '../cover/useArtistFanart';
|
||||
import { useCoverArt } from '../cover/useCoverArt';
|
||||
import { useHeroBackdrop } from '../cover/useHeroBackdrop';
|
||||
import { useCachedUrl } from '@/ui/CachedImage';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useIsMobile } from '@/lib/hooks/useIsMobile';
|
||||
import { useWindowVisibility } from '@/lib/hooks/useWindowVisibility';
|
||||
@@ -19,7 +19,7 @@ import { useAuthStore } from '../store/authStore';
|
||||
import { useThemeStore } from '../store/themeStore';
|
||||
import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '../utils/mix/mixRatingFilter';
|
||||
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
|
||||
import { playAlbum, playAlbumShuffled } from '../utils/playback/playAlbum';
|
||||
import { playAlbum, playAlbumShuffled } from '@/features/playback/utils/playback/playAlbum';
|
||||
import { useLongPressAction } from '@/lib/hooks/useLongPressAction';
|
||||
import { LongPressWaveOverlay } from './LongPressWaveOverlay';
|
||||
import { albumArtistDisplayName, deriveAlbumArtistRefs } from '@/features/album';
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { getPlaybackProgressSnapshot, subscribePlaybackProgress } from '../store/playbackProgress';
|
||||
import { getPlaybackProgressSnapshot, subscribePlaybackProgress } from '@/features/playback/store/playbackProgress';
|
||||
import { useEffect, useRef, useCallback } from 'react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import type { LrcLine } from '../api/lrclib';
|
||||
import { useLyrics, type WordLyricsLine } from '../hooks/useLyrics';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { Track } from '../store/playerStoreTypes';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { EaseScroller, targetForFraction } from '../utils/ui/easeScroll';
|
||||
import OverlayScrollArea from '@/ui/OverlayScrollArea';
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { queueSongStar } from '../store/pendingStarSync';
|
||||
import { queueSongStar } from '@/features/playback/store/pendingStarSync';
|
||||
import { usePlaybackCoverArt } from '../hooks/usePlaybackCoverArt';
|
||||
import { usePlaybackTrackCoverRef } from '../cover/useLibraryCoverRef';
|
||||
import type { Track } from '../store/playerStoreTypes';
|
||||
import { getPlaybackProgressSnapshot, subscribePlaybackProgress } from '../store/playbackProgress';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { getPlaybackProgressSnapshot, subscribePlaybackProgress } from '@/features/playback/store/playbackProgress';
|
||||
import React, { useState, useCallback, useRef, useEffect, useSyncExternalStore, CSSProperties } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { usePlaybackLibraryNavigate } from '../hooks/usePlaybackLibraryNavigate';
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
Shuffle, Repeat, Repeat1, Heart, Music, MicVocal, ListMusic, X,
|
||||
Moon, Sunrise,
|
||||
} from 'lucide-react';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useCachedUrl } from '@/ui/CachedImage';
|
||||
import { OpenArtistRefInline } from '@/features/artist';
|
||||
import { formatTrackTime } from '@/lib/format/formatDuration';
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { X, Moon, Sunrise } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
parsePlaybackDelayCustomMinutes,
|
||||
scheduleDelayMsFromSeconds,
|
||||
scheduleSecondsFromCustomMinutes,
|
||||
} from '../utils/playback/playbackScheduleDelay';
|
||||
} from '@/features/playback/utils/playback/playbackScheduleDelay';
|
||||
|
||||
/** One tap = schedule; custom minutes still covers any duration. */
|
||||
const PRESET_SECONDS = [30, 60, 120, 300, 600, 900, 1800, 3600] as const;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { formatPlaybackScheduleRemaining } from '../utils/format/playbackScheduleFormat';
|
||||
import { useWindowVisibility } from '@/lib/hooks/useWindowVisibility';
|
||||
|
||||
@@ -39,7 +39,7 @@ vi.mock('@/lib/api/subsonic', () => ({
|
||||
|
||||
import PlayerBar from './PlayerBar';
|
||||
import { renderWithProviders } from '@/test/helpers/renderWithProviders';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { resetAllStores } from '@/test/helpers/storeReset';
|
||||
import { makeTrack, seedQueue } from '@/test/helpers/factories';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { queueSongStar } from '../store/pendingStarSync';
|
||||
import { queueSongStar } from '@/features/playback/store/pendingStarSync';
|
||||
import { coverArtIdFromRadio } from '../cover/ids';
|
||||
import { resolvePlaybackTrackCoverArtId } from '../cover/resolveCoverArtId';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
PictureInPicture2, Ellipsis,
|
||||
} from 'lucide-react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useThemeStore } from '../store/themeStore';
|
||||
@@ -20,10 +20,10 @@ import { useRadioMprisSync } from '@/features/radio';
|
||||
import { usePlaybackDelayPress } from '../hooks/usePlaybackDelayPress';
|
||||
import PlaybackDelayModal from './PlaybackDelayModal';
|
||||
import { usePlaybackScheduleRemaining } from '../utils/format/playbackScheduleFormat';
|
||||
import { usePreviewStore } from '../store/previewStore';
|
||||
import { usePreviewStore } from '@/features/playback/store/previewStore';
|
||||
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
|
||||
import { coerceOpenArtistRefs } from '@/lib/api/openArtistRefs';
|
||||
import { resolveTrackArtistRefs } from '../utils/playback/trackArtistRefs';
|
||||
import { resolveTrackArtistRefs } from '@/features/playback/utils/playback/trackArtistRefs';
|
||||
import { PlayerTrackInfo } from './playerBar/PlayerTrackInfo';
|
||||
import { PlayerTransportControls } from './playerBar/PlayerTransportControls';
|
||||
import { PlayerSeekbarSection } from './playerBar/PlayerSeekbarSection';
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { songToTrack } from '../utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { Play, ListPlus, Star, Disc3 } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { CoverArtImage } from '../cover/CoverArtImage';
|
||||
import { useCoverArt } from '../cover/useCoverArt';
|
||||
import { useTrackCoverRef } from '../cover/useLibraryCoverRef';
|
||||
import { COVER_DENSE_RAIL_CELL_CSS_PX } from '../cover/layoutSizes';
|
||||
import { enqueueAndPlay } from '../utils/playback/playSong';
|
||||
import { enqueueAndPlay } from '@/features/playback/utils/playback/playSong';
|
||||
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import { useNavigateToAlbum } from '@/features/album';
|
||||
import { useNavigateToArtist } from '@/features/artist';
|
||||
import { OpenArtistRefInline } from '@/features/artist';
|
||||
import { resolveTrackArtistRefs } from '../utils/playback/trackArtistRefs';
|
||||
import { resolveTrackArtistRefs } from '@/features/playback/utils/playback/trackArtistRefs';
|
||||
|
||||
interface SongCardProps {
|
||||
song: SubsonicSong;
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { X } from 'lucide-react';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { ndGetSongPath } from '@/lib/api/navidromeAdmin';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { songToTrack } from '../utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import React, { memo } from 'react';
|
||||
import { useNavigateToAlbum } from '@/features/album';
|
||||
import { useNavigateToArtist } from '@/features/artist';
|
||||
import { Play, ListPlus } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { enqueueAndPlay } from '../utils/playback/playSong';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { enqueueAndPlay } from '@/features/playback/utils/playback/playSong';
|
||||
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import { formatTrackTime } from '@/lib/format/formatDuration';
|
||||
import { resolveTrackArtistRefs } from '../utils/playback/trackArtistRefs';
|
||||
import { resolveTrackArtistRefs } from '@/features/playback/utils/playback/trackArtistRefs';
|
||||
import { tooltipAttrs } from '@/ui/tooltipAttrs';
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||
import { Minus, Square, X } from 'lucide-react';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { IS_MACOS } from '@/lib/util/platform';
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { resolveAlbum, resolveMediaServerId } from '@/features/offline';
|
||||
import { star, unstar } from '@/lib/api/subsonicStarRating';
|
||||
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||
import { songToTrack } from '../../utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import StarRating from '../StarRating';
|
||||
import { AlbumToPlaylistSubmenu } from './AlbumArtistToPlaylistSubmenu';
|
||||
import { MultiAlbumToPlaylistSubmenu } from './MultiAlbumToPlaylistSubmenu';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Play, Radio, Heart, ChevronRight, User, Disc3, ListMusic, Info, Sparkles, Star, Trash2, Share2 } from 'lucide-react';
|
||||
import { queueSongStar } from '../../store/pendingStarSync';
|
||||
import { queueSongStar } from '@/features/playback/store/pendingStarSync';
|
||||
import { getMusicNetworkRuntime, useEnrichmentPrimary } from '../../music-network';
|
||||
import type { Track } from '../../store/playerStoreTypes';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { renderPresetIcon } from '../settings/musicNetwork/presetIcon';
|
||||
import StarRating from '../StarRating';
|
||||
|
||||
@@ -3,12 +3,12 @@ import { Play, ListPlus, Radio, Heart, ChevronRight, ChevronsRight, User, Disc3,
|
||||
import { useNavigateToAlbum } from '@/features/album';
|
||||
import { useNavigateToArtist } from '@/features/artist';
|
||||
import { resolveAlbum, resolveMediaServerId, resolvePlaylist } from '@/features/offline';
|
||||
import { queueSongStar } from '../../store/pendingStarSync';
|
||||
import { queueSongStar } from '@/features/playback/store/pendingStarSync';
|
||||
import { getMusicNetworkRuntime, useEnrichmentPrimary } from '../../music-network';
|
||||
import type { Track } from '../../store/playerStoreTypes';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { usePlaylistStore } from '@/features/playlist';
|
||||
import { songToTrack } from '../../utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { showToast } from '../../utils/ui/toast';
|
||||
import { suggestOrbitTrack, hostEnqueueToOrbit, evaluateOrbitSuggestGate, OrbitSuggestBlockedError } from '@/features/orbit';
|
||||
import { renderPresetIcon } from '../settings/musicNetwork/presetIcon';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type React from 'react';
|
||||
import type { SubsonicAlbum, SubsonicArtist } from '@/lib/api/subsonicTypes';
|
||||
import type { QueueItemRef, Track } from '../../store/playerStoreTypes';
|
||||
import type { QueueItemRef, Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import type { EntityShareKind } from '../../utils/share/shareLink';
|
||||
import type { OfflineActionPolicy } from '@/features/offline';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ChevronRight, Folder, FolderOpen, Music } from 'lucide-react';
|
||||
import type { SubsonicDirectoryEntry } from '@/lib/api/subsonicTypes';
|
||||
import type { Track } from '../../store/playerStoreTypes';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import {
|
||||
folderBrowserHasKeyModifiers, isFolderBrowserArrowKey,
|
||||
type Column,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { memo, useEffect, useRef } from 'react';
|
||||
import { getPlaybackProgressSnapshot, subscribePlaybackProgress } from '../../store/playbackProgress';
|
||||
import { getPlaybackProgressSnapshot, subscribePlaybackProgress } from '@/features/playback/store/playbackProgress';
|
||||
import {
|
||||
formatPlaybarClock,
|
||||
formatPlaybarToggleClock,
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useCallback } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { PlaybackRateControls } from '@/features/settings';
|
||||
import { usePlaybackRateStore } from '../../store/playbackRateStore';
|
||||
import { usePlaybackRateStore } from '@/features/playback/store/playbackRateStore';
|
||||
import { useOrbitStore } from '@/features/orbit';
|
||||
import {
|
||||
clampPlaybackPitch,
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
playbackPitchStep,
|
||||
playbackSpeedStep,
|
||||
varispeedSpeedFromSemitones,
|
||||
} from '../../utils/audio/playbackRateHelpers';
|
||||
} from '@/features/playback/utils/audio/playbackRateHelpers';
|
||||
import { isOrbitPlaybackSyncActive } from '@/features/orbit';
|
||||
import { usePlayerBarAnchoredPopover } from '../../hooks/usePlayerBarAnchoredPopover';
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Cast, Heart, Maximize2, Music } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { queueSongRating } from '../../store/pendingStarSync';
|
||||
import { queueSongRating } from '@/features/playback/store/pendingStarSync';
|
||||
import type { InternetRadioStation, SubsonicOpenArtistRef } from '@/lib/api/subsonicTypes';
|
||||
import type { PlayerState, Track } from '../../store/playerStoreTypes';
|
||||
import type { PlayerState, Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import type { RadioMetadata } from '@/features/radio';
|
||||
import type { PreviewingTrack } from '../../store/previewStore';
|
||||
import type { PreviewingTrack } from '@/features/playback/store/previewStore';
|
||||
import { CoverArtImage } from '../../cover/CoverArtImage';
|
||||
import { albumCoverRef } from '../../cover/ref';
|
||||
import { useAlbumCoverRef } from '../../cover/useLibraryCoverRef';
|
||||
@@ -13,7 +13,7 @@ import MarqueeText from '../MarqueeText';
|
||||
import { OpenArtistRefInline } from '@/features/artist';
|
||||
import StarRating from '../StarRating';
|
||||
import { PlaybackBufferingOverlay } from '../playback/PlaybackBufferingOverlay';
|
||||
import { usePlayerStore } from '../../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useEnrichmentPrimaryLabel } from '../../hooks/useEnrichmentPrimaryLabel';
|
||||
import { useEnrichmentPrimaryIcon } from '../../hooks/useEnrichmentPrimaryIcon';
|
||||
import { renderPresetIcon } from '../settings/musicNetwork/presetIcon';
|
||||
|
||||
@@ -2,9 +2,9 @@ import React from 'react';
|
||||
import { Blend, Moon, Pause, Play, Repeat, Repeat1, SkipBack, SkipForward, Square, Sunrise } from 'lucide-react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { PlayerState } from '../../store/playerStoreTypes';
|
||||
import { useAutodjTransitionUi } from '../../store/autodjTransitionUi';
|
||||
import { usePreviewStore } from '../../store/previewStore';
|
||||
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
|
||||
import { useAutodjTransitionUi } from '@/features/playback/store/autodjTransitionUi';
|
||||
import { usePreviewStore } from '@/features/playback/store/previewStore';
|
||||
import PlaybackScheduleBadge from '../PlaybackScheduleBadge';
|
||||
import { usePlaybackDelayPress } from '../../hooks/usePlaybackDelayPress';
|
||||
import { usePlaybackScheduleRemaining } from '../../utils/format/playbackScheduleFormat';
|
||||
|
||||
@@ -2,8 +2,8 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AudioLines, ChevronRight, Heart, Play, Square } from 'lucide-react';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import type { Track } from '../../store/playerStoreTypes';
|
||||
import { previewInputFromSong, usePreviewStore } from '../../store/previewStore';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { previewInputFromSong, usePreviewStore } from '@/features/playback/store/previewStore';
|
||||
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
||||
import { formatRandomMixDuration } from '../../utils/componentHelpers/randomMixHelpers';
|
||||
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { AlbumCoverArtImage } from '../../cover/AlbumCoverArtImage';
|
||||
import { getRandomSongs } from '@/lib/api/subsonicLibrary';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { songToTrack } from '../../utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Play, ListPlus, RefreshCw, Sparkles } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { usePlayerStore } from '../../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import SongRail from '../SongRail';
|
||||
import { playSongNow } from '../../utils/playback/playSong';
|
||||
import { playSongNow } from '@/features/playback/utils/playback/playSong';
|
||||
import { ndListSongs, ndInvalidateSongsCache } from '@/lib/api/navidromeBrowse';
|
||||
import { usePerfProbeFlags } from '../../utils/perf/perfFlags';
|
||||
import { useNavigateToAlbum } from '@/features/album';
|
||||
import { useNavigateToArtist } from '@/features/artist';
|
||||
import { OpenArtistRefInline } from '@/features/artist';
|
||||
import { resolveTrackArtistRefs } from '../../utils/playback/trackArtistRefs';
|
||||
import { resolveTrackArtistRefs } from '@/features/playback/utils/playback/trackArtistRefs';
|
||||
|
||||
const RANDOM_RAIL_SIZE = 18;
|
||||
const RATED_RAIL_FETCH = 60;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { queueSongStar } from '../store/pendingStarSync';
|
||||
import { queueSongStar } from '@/features/playback/store/pendingStarSync';
|
||||
import { getSong } from '@/lib/api/subsonicLibrary';
|
||||
import { songToTrack } from '../utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||
import i18n from '@/lib/i18n';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePreviewStore } from '../store/previewStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { usePreviewStore } from '@/features/playback/store/previewStore';
|
||||
import { useLyricsStore } from '../store/lyricsStore';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
import type { ActionContext, ShortcutSlot, ShortcutActionMeta } from '@/config/shortcutTypes';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { queueSongRating } from '../store/pendingStarSync';
|
||||
import { queueSongRating } from '@/features/playback/store/pendingStarSync';
|
||||
import i18n from '@/lib/i18n';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { showToast } from '../utils/ui/toast';
|
||||
import { playByOpaqueId } from '../utils/playback/playByOpaqueId';
|
||||
import { playByOpaqueId } from '@/features/playback/utils/playback/playByOpaqueId';
|
||||
import type { ActionContext, CliContext } from '@/config/shortcutTypes';
|
||||
import {
|
||||
SHORTCUT_ACTION_REGISTRY,
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
buildCoverArtUrl,
|
||||
buildCoverArtUrlForServer,
|
||||
} from '@/lib/api/subsonicStreamUrl';
|
||||
import { getPlaybackServerId } from '../utils/playback/playbackServer';
|
||||
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { connectBaseUrlForServer } from '../utils/server/serverEndpoint';
|
||||
import type { CoverArtRef, CoverArtTier } from './types';
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { getPlaybackServerId } from '../utils/playback/playbackServer';
|
||||
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { coverServerScopeForServerId } from './serverScope';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
|
||||
@@ -26,7 +26,7 @@ vi.mock('../store/authStore', () => ({
|
||||
useAuthStore: { getState: () => mockState },
|
||||
}));
|
||||
|
||||
vi.mock('../utils/playback/playbackServer', () => ({
|
||||
vi.mock('@/features/playback/utils/playback/playbackServer', () => ({
|
||||
getPlaybackServerId: () => null,
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getPlaybackServerId } from '../utils/playback/playbackServer';
|
||||
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { findServerByIdOrIndexKey } from '../utils/server/serverLookup';
|
||||
import {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
|
||||
const TICK_MS = 60_000;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useMemo, useState, type Dispatch, type SetStateAction } from 'react';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import {
|
||||
albumCoverRef,
|
||||
albumCoverRefForPlayback,
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from './ref';
|
||||
import { coverServerScopeForServerId } from './serverScope';
|
||||
import { resolveServerIdForIndexKey } from '../utils/server/serverLookup';
|
||||
import { sameQueueTrackId } from '../utils/playback/queueIdentity';
|
||||
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
||||
import {
|
||||
resolveAlbumCoverRefFromLibrary,
|
||||
resolveArtistCoverRefFromLibrary,
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { CoverArtHandle } from './types';
|
||||
import { albumCoverRef } from './ref';
|
||||
import { usePlaybackCoverArt } from './usePlaybackCoverArt';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { makeTrack } from '../test/helpers/factories';
|
||||
import { resetAllStores } from '../test/helpers/storeReset';
|
||||
import { toQueueItemRefs } from '../utils/library/queueItemRef';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { resolvePlaybackCoverScope } from './ref';
|
||||
import type { CoverArtHandle, CoverArtRef } from './types';
|
||||
import { useCoverArt } from './useCoverArt';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { usePlayerStore } from '../store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
|
||||
/** Cover art for playback queue — uses queue server when it differs from browsed server. */
|
||||
export function usePlaybackCoverArt(
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { useNavigateToAlbum } from '@/features/album/hooks/useNavigateToAlbum';
|
||||
import { Play, ListPlus, HardDriveDownload, Check } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
||||
import { isOfflinePinComplete } from '@/features/offline';
|
||||
@@ -16,7 +16,7 @@ import { COVER_DENSE_GRID_MIN_CELL_CSS_PX } from '@/cover/layoutSizes';
|
||||
import { resolveCoverDisplayTier } from '@/cover/tiers';
|
||||
import { acquireUrl } from '@/utils/imageCache/urlPool';
|
||||
import { OpenArtistRefInline } from '@/features/artist';
|
||||
import { fetchAlbumTracks, playAlbum, playAlbumShuffled } from '@/utils/playback/playAlbum';
|
||||
import { fetchAlbumTracks, playAlbum, playAlbumShuffled } from '@/features/playback/utils/playback/playAlbum';
|
||||
import { useLongPressAction } from '@/lib/hooks/useLongPressAction';
|
||||
import { LongPressWaveOverlay } from '@/components/LongPressWaveOverlay';
|
||||
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import type { Track } from '@/store/playerStoreTypes';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import React, { useMemo, useState, useEffect } from 'react';
|
||||
import { useTracklistColumns } from '@/utils/useTracklistColumns';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useIsMobile } from '@/lib/hooks/useIsMobile';
|
||||
import { useSelectionStore } from '@/store/selectionStore';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { AudioLines } from 'lucide-react';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import type { Track } from '@/store/playerStoreTypes';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { formatLongDuration } from '@/lib/format/formatDuration';
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -4,18 +4,18 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import type { ColDef } from '@/utils/useTracklistColumns';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import type { Track } from '@/store/playerStoreTypes';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { useSelectionStore } from '@/store/selectionStore';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { previewInputFromSong, usePreviewStore } from '@/store/previewStore';
|
||||
import { previewInputFromSong, usePreviewStore } from '@/features/playback/store/previewStore';
|
||||
import StarRating from '@/components/StarRating';
|
||||
import { codecLabel, type ColKey } from '@/features/album/utils/albumTrackListHelpers';
|
||||
import { formatLongDuration } from '@/lib/format/formatDuration';
|
||||
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
|
||||
import i18n from '@/lib/i18n';
|
||||
import { offlineActionPolicy, type OfflineActionPolicy } from '@/features/offline';
|
||||
import { resolveTrackArtistRefs } from '@/utils/playback/trackArtistRefs';
|
||||
import { resolveTrackArtistRefs } from '@/features/playback/utils/playback/trackArtistRefs';
|
||||
|
||||
type ContextMenuFn = (
|
||||
x: number,
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef } from 'react';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { useSelectionStore } from '@/store/selectionStore';
|
||||
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
|
||||
interface UseAlbumTrackListSelectionArgs {
|
||||
songs: SubsonicSong[];
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
|
||||
import { setRating, star, unstar } from '@/lib/api/subsonicStarRating';
|
||||
import { queueSongStar, queueSongRating } from '@/store/pendingStarSync';
|
||||
import { queueSongStar, queueSongRating } from '@/features/playback/store/pendingStarSync';
|
||||
import { getAlbumForServer } from '@/lib/api/subsonicLibrary';
|
||||
import { getArtistInfo } from '@/lib/api/subsonicArtists';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { shuffleArray } from '@/utils/playback/shuffleArray';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { shuffleArray } from '@/features/playback/utils/playback/shuffleArray';
|
||||
import React, { useEffect, useState, useCallback, useMemo } from 'react';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import { useAlbumDetailData } from '@/features/album/hooks/useAlbumDetailData';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
|
||||
import { resolveAlbum } from '@/features/offline';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { useState, useEffect, useLayoutEffect, useRef, useMemo } from 'react';
|
||||
import AlbumCard from '@/features/album/components/AlbumCard';
|
||||
import { albumGridWarmCovers, coverDisplayCssPxForAlbumGrid } from '@/cover/layoutSizes';
|
||||
@@ -17,7 +17,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { useOfflineStore } from '@/features/offline';
|
||||
import { useDownloadModalStore } from '@/features/offline';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { join } from '@tauri-apps/api/path';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
|
||||
import { resolveAlbum } from '@/features/offline';
|
||||
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/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';
|
||||
@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useOfflineStore } from '@/features/offline';
|
||||
import { useDownloadModalStore } from '@/features/offline';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useZipDownloadStore } from '@/features/offline';
|
||||
import { useRangeSelection } from '@/lib/hooks/useRangeSelection';
|
||||
import { useMainstageInpageHeaderTight } from '@/hooks/useMainstageInpageHeaderTight';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { getAlbumList } from '@/lib/api/subsonicLibrary';
|
||||
import { resolveAlbum } from '@/features/offline';
|
||||
import type { SubsonicAlbum } from '@/lib/api/subsonicTypes';
|
||||
import { dedupeById } from '@/lib/util/dedupeById';
|
||||
import { shuffleArray } from '@/utils/playback/shuffleArray';
|
||||
import { shuffleArray } from '@/features/playback/utils/playback/shuffleArray';
|
||||
import React, { useEffect, useLayoutEffect, useState, useCallback, useRef } from 'react';
|
||||
import { RefreshCw, Download, HardDriveDownload } from 'lucide-react';
|
||||
import SelectionToggleButton from '@/components/SelectionToggleButton';
|
||||
|
||||
@@ -2,10 +2,10 @@ import React, { Fragment } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AudioLines, ChevronRight, Play, Square } from 'lucide-react';
|
||||
import type { SubsonicAlbum, SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { previewInputFromSong, usePreviewStore } from '@/store/previewStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { previewInputFromSong, usePreviewStore } from '@/features/playback/store/previewStore';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { formatTrackTime } from '@/lib/format/formatDuration';
|
||||
import ArtistTopTrackCover from '@/features/artist/components/ArtistTopTrackCover';
|
||||
import { topSongAlbumForCover } from '@/features/artist/components/topSongAlbumForCover';
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Check } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
|
||||
import type { PlayerState } from '@/store/playerStoreTypes';
|
||||
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
|
||||
import { ARTISTS_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
|
||||
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
|
||||
import { ArtistCardAvatar } from '@/features/artist/components/ArtistAvatars';
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import type { Virtualizer } from '@tanstack/react-virtual';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
|
||||
import type { PlayerState } from '@/store/playerStoreTypes';
|
||||
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
|
||||
import { OTHER_BUCKET, type ArtistListFlatRow } from '@/features/artist/utils/artistsHelpers';
|
||||
import { ArtistRowAvatar } from '@/features/artist/components/ArtistAvatars';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMemo } from 'react';
|
||||
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { ALL_SENTINEL, artistLetterBucket, compareBuckets, type ArtistListFlatRow } from '@/features/artist/utils/artistsHelpers';
|
||||
|
||||
interface UseArtistsFilteringArgs {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import { AlbumCard } from '@/features/album';
|
||||
import { ArrowDownUp } from 'lucide-react';
|
||||
import { open } from '@tauri-apps/plugin-shell';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useArtistLayoutStore, type ArtistSectionId } from '@/features/artist/store/artistLayoutStore';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { LayoutGrid, List, Images } from 'lucide-react';
|
||||
import SelectionToggleButton from '@/components/SelectionToggleButton';
|
||||
import StarFilterButton from '@/components/StarFilterButton';
|
||||
import OverlayScrollArea from '@/ui/OverlayScrollArea';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { TFunction } from 'i18next';
|
||||
import { getSimilarSongs2, getTopSongs } from '@/lib/api/subsonicArtists';
|
||||
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';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { runBulkPlayAll, runBulkShuffle } from '@/features/playback/utils/playback/runBulkPlay';
|
||||
import { resolveAlbum, resolveMediaServerId } from '@/features/offline';
|
||||
|
||||
/** Ordered artist discography tracks for play-all / shuffle (network or local bytes). */
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useDeviceSyncJobStore } from '@/features/deviceSync/store/deviceSyncJob
|
||||
import { useDeviceSyncStore } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import { trackToSyncInfo } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
import { fetchTracksForSource } from '@/utils/playback/fetchTracksForSource';
|
||||
import { fetchTracksForSource } from '@/features/playback/utils/playback/fetchTracksForSource';
|
||||
|
||||
export function useDeviceSyncJobEvents(
|
||||
t: TFunction,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { fetchTracksForSource } from '@/utils/playback/fetchTracksForSource';
|
||||
import { fetchTracksForSource } from '@/features/playback/utils/playback/fetchTracksForSource';
|
||||
import { trackToSyncInfo, type SyncStatus } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncStore';
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useDeviceSyncStore, type DeviceSyncSource } from '@/features/deviceSync
|
||||
import { useDeviceSyncJobStore } from '@/features/deviceSync/store/deviceSyncJobStore';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import { trackToSyncInfo, uuid } from '@/features/deviceSync/utils/deviceSyncHelpers';
|
||||
import { fetchTracksForSource } from '@/utils/playback/fetchTracksForSource';
|
||||
import { fetchTracksForSource } from '@/features/playback/utils/playback/fetchTracksForSource';
|
||||
|
||||
export interface SyncDelta {
|
||||
addBytes: number;
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { DeviceSyncSource } from '@/features/deviceSync/store/deviceSyncSto
|
||||
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';
|
||||
import { fetchTracksForSource } from '@/features/playback/utils/playback/fetchTracksForSource';
|
||||
import { IS_WINDOWS } from '@/lib/util/platform';
|
||||
|
||||
export type MigrationPhase = 'closed' | 'loading' | 'preview' | 'executing' | 'done' | 'nothing';
|
||||
|
||||
@@ -9,7 +9,7 @@ import i18n from '@/lib/i18n';
|
||||
import { formatTrackTime } from '@/lib/format/formatDuration';
|
||||
import StarRating from '@/components/StarRating';
|
||||
import { OpenArtistRefInline } from '@/features/artist';
|
||||
import { resolveTrackArtistRefs } from '@/utils/playback/trackArtistRefs';
|
||||
import { resolveTrackArtistRefs } from '@/features/playback/utils/playback/trackArtistRefs';
|
||||
|
||||
export interface FavoriteSongRowCallbacks {
|
||||
activate: (song: SubsonicSong, index: number, e: React.MouseEvent) => void;
|
||||
|
||||
@@ -2,9 +2,9 @@ import React, { useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ListPlus, Play, SlidersHorizontal, X } from 'lucide-react';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useSelectionStore } from '@/store/selectionStore';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { AddToPlaylistSubmenu } from '@/components/ContextMenu';
|
||||
import GenreFilterBar from '@/components/GenreFilterBar';
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import type { ColDef } from '@/utils/useTracklistColumns';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { previewInputFromSong, usePreviewStore } from '@/store/previewStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { previewInputFromSong, usePreviewStore } from '@/features/playback/store/previewStore';
|
||||
import { useSelectionStore } from '@/store/selectionStore';
|
||||
import { useThemeStore } from '@/store/themeStore';
|
||||
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { appendServerQuery } from '@/utils/navigation/detailServerScope';
|
||||
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
|
||||
import { useElementClientHeightById } from '@/lib/hooks/useResizeClientHeight';
|
||||
|
||||
@@ -5,7 +5,7 @@ import type {
|
||||
InternetRadioStation, SubsonicAlbum, SubsonicArtist, SubsonicSong,
|
||||
} from '@/lib/api/subsonicTypes';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import type { TopFavoriteArtist } from '@/features/favorites/components/TopFavoriteArtists';
|
||||
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
|
||||
import { isActiveServerReachable } from '@/utils/network/activeServerReachability';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { ArrowDown, ArrowUp } from 'lucide-react';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
|
||||
const CURRENT_YEAR = new Date().getFullYear();
|
||||
const MIN_YEAR = 1950;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { queueSongStar, queueSongRating } from '@/store/pendingStarSync';
|
||||
import { queueSongStar, queueSongRating } from '@/features/playback/store/pendingStarSync';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { useTracklistColumns, type ColDef } from '@/utils/useTracklistColumns';
|
||||
import { TopFavoriteArtistsRow } from '@/features/favorites/components/TopFavoriteArtists';
|
||||
@@ -11,7 +11,7 @@ import { useFavoritesSelection } from '@/features/favorites/hooks/useFavoritesSe
|
||||
import { useBulkPlPickerOutsideClick } from '@/hooks/useBulkPlPickerOutsideClick';
|
||||
import { AlbumRow } from '@/features/album';
|
||||
import { ArtistRow } from '@/features/artist';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelectionStore } from '@/store/selectionStore';
|
||||
import FavoritesOfflineHeader from '@/features/favorites/components/FavoritesOfflineHeader';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useLyrics, type WordLyricsLine } from '@/hooks/useLyrics';
|
||||
import { useWordLyricsSync } from '@/hooks/useWordLyricsSync';
|
||||
import { getPlaybackProgressSnapshot, subscribePlaybackProgress } from '@/store/playbackProgress';
|
||||
import { getPlaybackProgressSnapshot, subscribePlaybackProgress } from '@/features/playback/store/playbackProgress';
|
||||
import type { LrcLine } from '@/api/lrclib';
|
||||
import type { Track } from '@/store/playerStoreTypes';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { EaseScroller, targetForFraction } from '@/utils/ui/easeScroll';
|
||||
|
||||
// Fullscreen synced lyrics.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { memo, useRef } from 'react';
|
||||
import { Moon, Pause, Play, Sunrise } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { usePlaybackDelayPress } from '@/hooks/usePlaybackDelayPress';
|
||||
import { usePlaybackScheduleRemaining } from '@/utils/format/playbackScheduleFormat';
|
||||
import PlaybackDelayModal from '@/components/PlaybackDelayModal';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { memo, useMemo, useSyncExternalStore } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { X } from 'lucide-react';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import type { Track } from '@/store/playerStoreTypes';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { resolveQueueTrack } from '@/utils/library/queueTrackView';
|
||||
import {
|
||||
getQueueResolverVersion,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { memo, useEffect, useRef } from 'react';
|
||||
import { getPlaybackProgressSnapshot, subscribePlaybackProgress } from '@/store/playbackProgress';
|
||||
import { getPlaybackProgressSnapshot, subscribePlaybackProgress } from '@/features/playback/store/playbackProgress';
|
||||
import { formatTrackTime } from '@/lib/format/formatDuration';
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,8 +4,8 @@ import {
|
||||
Shuffle, ListMusic, ChevronDown, Star, MicVocal,
|
||||
} from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { queueSongStar, queueSongRating } from '@/store/pendingStarSync';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { queueSongStar, queueSongRating } from '@/features/playback/store/pendingStarSync';
|
||||
import { useAlbumCoverRef, useArtistCoverRef } from '@/cover/useLibraryCoverRef';
|
||||
import { usePlaybackCoverArt } from '@/hooks/usePlaybackCoverArt';
|
||||
import { useCachedUrl } from '@/ui/CachedImage';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import { emit } from '@tauri-apps/api/event';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { registerQueueDragHitTest } from '@/lib/dnd/DragDropContext';
|
||||
import MiniContextMenu from '@/features/miniPlayer/components/MiniContextMenu';
|
||||
import type { MiniSyncPayload, MiniControlAction, MiniTrackInfo } from '@/features/miniPlayer/utils/miniPlayerBridge';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { emit } from '@tauri-apps/api/event';
|
||||
import { Blend, Infinity as InfinityIcon, ListMusic, MoveRight, Shuffle, Volume2, VolumeX, Waves } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { MiniSyncPayload } from '@/features/miniPlayer/utils/miniPlayerBridge';
|
||||
import { getTransitionMode } from '@/utils/playback/playbackTransition';
|
||||
import { getTransitionMode } from '@/features/playback/utils/playback/playbackTransition';
|
||||
|
||||
interface Props {
|
||||
state: MiniSyncPayload;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { emit } from '@tauri-apps/api/event';
|
||||
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
|
||||
interface Args {
|
||||
queueOpen: boolean;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||
import { listen, emitTo } from '@tauri-apps/api/event';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { setTransitionMode, type TransitionMode } from '@/utils/playback/playbackTransition';
|
||||
import { setTransitionMode, type TransitionMode } from '@/features/playback/utils/playback/playbackTransition';
|
||||
import { resolveQueueTrack } from '@/utils/library/queueTrackView';
|
||||
import type { SubsonicOpenArtistRef } from '@/lib/api/subsonicTypes';
|
||||
import type { Track } from '@/store/playerStoreTypes';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
|
||||
export const MINI_WINDOW_LABEL = 'mini';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { resolveQueueTrack } from '@/utils/library/queueTrackView';
|
||||
import type { MiniSyncPayload, MiniTrackInfo } from '@/features/miniPlayer/utils/miniPlayerBridge';
|
||||
import type { Track } from '@/store/playerStoreTypes';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
|
||||
/** Half-width of the mini initial-snapshot queue window (matches the bridge). */
|
||||
const MINI_SNAPSHOT_HALF = 100;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { createPortal } from 'react-dom';
|
||||
import { PlayCircle, Pause, User, Radio, RefreshCw } from 'lucide-react';
|
||||
import { nowPlayingPresence } from '@/features/nowPlaying/api/nowPlayingPresence';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@ import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 're
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Info } from 'lucide-react';
|
||||
import { open as shellOpen } from '@tauri-apps/plugin-shell';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePlaybackServerId } from '@/hooks/usePlaybackServerId';
|
||||
import { fetchBandsintownEvents, type BandsintownEvent } from '@/api/bandsintown';
|
||||
import CachedImage from '@/ui/CachedImage';
|
||||
import OverlayScrollArea from '@/ui/OverlayScrollArea';
|
||||
import { primaryTrackArtistRef } from '@/utils/playback/trackArtistRefs';
|
||||
import { primaryTrackArtistRef } from '@/features/playback/utils/playback/trackArtistRefs';
|
||||
|
||||
const TOUR_LIMIT = 5;
|
||||
const BIO_CLAMP_LINES = 4;
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Cast, Clock, Radio, SkipForward, Users } from 'lucide-react';
|
||||
import type { useRadioMetadata } from '@/features/radio';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { formatTrackTime } from '@/lib/format/formatDuration';
|
||||
|
||||
type NonNullStoreField<K extends keyof ReturnType<typeof usePlayerStore.getState>> =
|
||||
|
||||
@@ -5,7 +5,7 @@ import { coverIndexKeyFromRef } from '@/cover/storageKeys';
|
||||
import { useNowPlayingPrewarm } from '@/features/nowPlaying/hooks/useNowPlayingPrewarm';
|
||||
import { prewarmNowPlayingFetchers } from '@/features/nowPlaying/hooks/useNowPlayingFetchers';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { makeTrack } from '@/test/helpers/factories';
|
||||
import { resetAllStores } from '@/test/helpers/storeReset';
|
||||
import { toQueueItemRefs } from '@/utils/library/queueItemRef';
|
||||
|
||||
@@ -10,9 +10,9 @@ import { coverArtIdFromRadio } from '@/cover/ids';
|
||||
import type { CoverArtRef } from '@/cover/types';
|
||||
import { prewarmNowPlayingFetchers } from '@/features/nowPlaying/hooks/useNowPlayingFetchers';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { usePlaybackServerId } from '@/hooks/usePlaybackServerId';
|
||||
import { primaryTrackArtistRef } from '@/utils/playback/trackArtistRefs';
|
||||
import { primaryTrackArtistRef } from '@/features/playback/utils/playback/trackArtistRefs';
|
||||
|
||||
const NOW_PLAYING_COVER_CSS_PX = 800;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { queueSongStar } from '@/store/pendingStarSync';
|
||||
import { queueSongStar } from '@/features/playback/store/pendingStarSync';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import type { Track } from '@/store/playerStoreTypes';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import type { TrackStats } from '@/music-network';
|
||||
import { getMusicNetworkRuntime } from '@/music-network';
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import { usePlaybackLibraryNavigate } from '@/hooks/usePlaybackLibraryNavigate';
|
||||
import { usePlaybackServerId } from '@/hooks/usePlaybackServerId';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Music, EyeOff, LayoutGrid, RotateCcw, Eye } from 'lucide-react';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useLyricsStore } from '@/store/lyricsStore';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { useRadioMetadata } from '@/features/radio';
|
||||
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
||||
import OverlayScrollArea from '@/ui/OverlayScrollArea';
|
||||
@@ -38,7 +38,7 @@ import DiscographyCard from '@/features/nowPlaying/components/DiscographyCard';
|
||||
import { useNowPlayingFetchers } from '@/features/nowPlaying/hooks/useNowPlayingFetchers';
|
||||
import { useNowPlayingStarLove } from '@/features/nowPlaying/hooks/useNowPlayingStarLove';
|
||||
import { useArtistInfoBatch } from '@/features/artist';
|
||||
import { primaryTrackArtistRef, resolveTrackArtistRefs } from '@/utils/playback/trackArtistRefs';
|
||||
import { primaryTrackArtistRef, resolveTrackArtistRefs } from '@/features/playback/utils/playback/trackArtistRefs';
|
||||
import type { ArtistCardTab } from '@/features/nowPlaying/components/ArtistCard';
|
||||
|
||||
// ─── Main Page ────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useOfflineStore } from '@/features/offline/store/offlineStore';
|
||||
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { AlbumCoverArtImage } from '@/cover/AlbumCoverArtImage';
|
||||
import { coverServerScopeForServerId } from '@/cover/serverScope';
|
||||
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
|
||||
@@ -33,7 +33,7 @@ import {
|
||||
type OfflineLibraryCard,
|
||||
} from '@/features/offline/utils/offlineLibraryHelpers';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import { shuffleArray } from '@/utils/playback/shuffleArray';
|
||||
import { shuffleArray } from '@/features/playback/utils/playback/shuffleArray';
|
||||
import { getMediaDir } from '@/utils/media/mediaDir';
|
||||
import { canonicalQueueServerKey, resolveIndexKey } from '@/utils/server/serverIndexKey';
|
||||
import { reconcileAllLibraryTiersFromDisk } from '@/features/offline/utils/libraryTierReconcile';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { findLocalPlaybackEntry, hasLocalLibraryBytes } from '@/store/localPlayb
|
||||
import { useOfflineStore, type OfflineAlbumMeta } from '@/features/offline/store/offlineStore';
|
||||
import { resolveTrackCoverArtId, trackToSong } from '@/utils/library/advancedSearchLocal';
|
||||
import { canonicalQueueServerKey, resolveIndexKey } from '@/utils/server/serverIndexKey';
|
||||
import type { Track } from '@/store/playerStoreTypes';
|
||||
import type { Track } from '@/features/playback/store/playerStoreTypes';
|
||||
import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '@/utils/server/serverLookup';
|
||||
import { serverIndexKeyForProfile } from '@/utils/server/serverIndexKey';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { createPortal } from 'react-dom';
|
||||
import { Copy, Trash2 } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { showToast } from '@/utils/ui/toast';
|
||||
import { computeOrbitDriftMs } from '@/features/orbit/utils/orbit';
|
||||
import {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { getSong } from '@/lib/api/subsonicLibrary';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { X, RefreshCw, Shuffle, Settings2, Share2, HelpCircle, Activity } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { useHelpModalStore } from '@/store/helpModalStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import {
|
||||
endOrbitSession,
|
||||
leaveOrbitSession,
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from '@/features/orbit/utils/orbit';
|
||||
import { randomOrbitSessionName } from '@/features/orbit/utils/orbitNames';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { isLanUrl, serverShareBaseUrl } from '@/utils/server/serverEndpoint';
|
||||
import { ORBIT_DEFAULT_MAX_USERS } from '@/features/orbit/api/orbit';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { getSong } from '@/lib/api/subsonicLibrary';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import {
|
||||
readOrbitState,
|
||||
applyOrbitTransitionSettings,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { getSong } from '@/lib/api/subsonicLibrary';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { useEffect } from 'react';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import {
|
||||
writeOrbitState,
|
||||
sweepGuestOutboxes,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { usePlaybackRateStore } from '@/store/playbackRateStore';
|
||||
import { usePlaybackRateStore } from '@/features/playback/store/playbackRateStore';
|
||||
|
||||
/** Re-sync playback rate when Orbit enters or leaves shared playback. */
|
||||
export function usePlaybackRateOrbitSync() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createPlaylist, deletePlaylist, getPlaylist, getPlaylists, updatePlaylist } from '@/lib/api/subsonicPlaylists';
|
||||
import { getSong } from '@/lib/api/subsonicLibrary';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import {
|
||||
orbitOutboxPlaylistName,
|
||||
type OrbitQueueItem,
|
||||
|
||||
@@ -23,10 +23,10 @@ vi.mock('@/features/orbit/utils/remote', () => ({
|
||||
}));
|
||||
vi.mock('@/features/orbit/store/orbitStore', () => ({ useOrbitStore: { getState: () => orbitStore } }));
|
||||
vi.mock('@/store/authStore', () => ({ useAuthStore: { getState: () => ({}) } }));
|
||||
vi.mock('@/store/playerStore', () => ({ usePlayerStore: { getState: () => ({ enqueue: vi.fn() }) } }));
|
||||
vi.mock('@/features/playback/store/playerStore', () => ({ usePlayerStore: { getState: () => ({ enqueue: vi.fn() }) } }));
|
||||
vi.mock('@/lib/api/subsonicPlaylists', () => ({ createPlaylist: vi.fn(), deletePlaylist: vi.fn() }));
|
||||
vi.mock('@/lib/api/subsonicLibrary', () => ({ getSong: vi.fn() }));
|
||||
vi.mock('@/utils/playback/songToTrack', () => ({ songToTrack: vi.fn() }));
|
||||
vi.mock('@/features/playback/utils/playback/songToTrack', () => ({ songToTrack: vi.fn() }));
|
||||
|
||||
import { updateOrbitSettings } from '@/features/orbit/utils/host';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createPlaylist, deletePlaylist } from '@/lib/api/subsonicPlaylists';
|
||||
import { getSong } from '@/lib/api/subsonicLibrary';
|
||||
import { songToTrack } from '@/utils/playback/songToTrack';
|
||||
import { songToTrack } from '@/features/playback/utils/playback/songToTrack';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { useOrbitStore } from '@/features/orbit/store/orbitStore';
|
||||
import { usePlayerStore } from '@/store/playerStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import {
|
||||
makeInitialOrbitState,
|
||||
orbitOutboxPlaylistName,
|
||||
|
||||
+18
-18
@@ -1,29 +1,29 @@
|
||||
import { playbackReportStart } from './playbackReportSession';
|
||||
import { playbackReportStart } from '@/features/playback/store/playbackReportSession';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { getPlaybackServerId } from '../utils/playback/playbackServer';
|
||||
import { getPlaybackSourceKind } from '../utils/playback/resolvePlaybackUrl';
|
||||
import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer';
|
||||
import { getPlaybackSourceKind } from '@/features/playback/utils/playback/resolvePlaybackUrl';
|
||||
import {
|
||||
bumpPlayGeneration,
|
||||
getPlayGeneration,
|
||||
setIsAudioPaused,
|
||||
} from './engineState';
|
||||
import { clearPreloadingIds } from './gaplessPreloadState';
|
||||
import { deriveNormalizationSnapshot } from './normalizationSnapshot';
|
||||
import type { PlayerState, QueueItemRef } from './playerStoreTypes';
|
||||
import { resolveQueueTrack } from '../utils/library/queueTrackView';
|
||||
import { seedQueueResolver } from '../utils/library/queueTrackResolver';
|
||||
import { canonicalQueueServerKey } from '../utils/server/serverIndexKey';
|
||||
import { sameQueueTrackId } from '../utils/playback/queueIdentity';
|
||||
import { queueUndoRestoreAudioEngine } from './queueUndoAudioRestore';
|
||||
} from '@/features/playback/store/engineState';
|
||||
import { clearPreloadingIds } from '@/features/playback/store/gaplessPreloadState';
|
||||
import { deriveNormalizationSnapshot } from '@/features/playback/store/normalizationSnapshot';
|
||||
import type { PlayerState, QueueItemRef } from '@/features/playback/store/playerStoreTypes';
|
||||
import { resolveQueueTrack } from '@/utils/library/queueTrackView';
|
||||
import { seedQueueResolver } from '@/utils/library/queueTrackResolver';
|
||||
import { canonicalQueueServerKey } from '@/utils/server/serverIndexKey';
|
||||
import { sameQueueTrackId } from '@/features/playback/utils/playback/queueIdentity';
|
||||
import { queueUndoRestoreAudioEngine } from '@/features/playback/store/queueUndoAudioRestore';
|
||||
import {
|
||||
setPendingQueueListScrollTop,
|
||||
type QueueUndoSnapshot,
|
||||
} from './queueUndo';
|
||||
import { refreshLoudnessForTrack } from './loudnessRefresh';
|
||||
import { refreshWaveformForTrack } from '@/store/waveformRefresh';
|
||||
import { stopRadio } from './radioPlayer';
|
||||
import { clearAllPlaybackScheduleTimers } from './scheduleTimers';
|
||||
import { syncUserQueueMutationToServer } from './queueSync';
|
||||
} from '@/features/playback/store/queueUndo';
|
||||
import { refreshLoudnessForTrack } from '@/features/playback/store/loudnessRefresh';
|
||||
import { refreshWaveformForTrack } from '@/features/playback/store/waveformRefresh';
|
||||
import { stopRadio } from '@/features/playback/store/radioPlayer';
|
||||
import { clearAllPlaybackScheduleTimers } from '@/features/playback/store/scheduleTimers';
|
||||
import { syncUserQueueMutationToServer } from '@/features/playback/store/queueSync';
|
||||
|
||||
type SetState = (
|
||||
partial: Partial<PlayerState> | ((state: PlayerState) => Partial<PlayerState>),
|
||||
+9
-9
@@ -1,5 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { QueueItemRef } from './playerStoreTypes';
|
||||
import type { QueueItemRef } from '@/features/playback/store/playerStoreTypes';
|
||||
|
||||
const getPlayQueueForServerMock = vi.fn();
|
||||
|
||||
@@ -7,11 +7,11 @@ vi.mock('@/lib/api/subsonicPlayQueue', () => ({
|
||||
getPlayQueueForServer: (...args: unknown[]) => getPlayQueueForServerMock(...args),
|
||||
}));
|
||||
|
||||
vi.mock('../utils/server/serverLookup', () => ({
|
||||
vi.mock('@/utils/server/serverLookup', () => ({
|
||||
resolveServerIdForIndexKey: (id: string) => id,
|
||||
}));
|
||||
|
||||
vi.mock('../utils/playback/songToTrack', () => ({
|
||||
vi.mock('@/features/playback/utils/playback/songToTrack', () => ({
|
||||
songToTrack: (s: { id: string }) => ({
|
||||
id: s.id,
|
||||
title: s.id,
|
||||
@@ -23,15 +23,15 @@ vi.mock('../utils/playback/songToTrack', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('./pausedRestorePrepare', () => ({
|
||||
vi.mock('@/features/playback/store/pausedRestorePrepare', () => ({
|
||||
preparePausedRestoreOnStartup: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@/store/waveformRefresh', () => ({
|
||||
vi.mock('@/features/playback/store/waveformRefresh', () => ({
|
||||
refreshWaveformForTrack: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('./queueSyncUiState', () => ({
|
||||
vi.mock('@/features/playback/store/queueSyncUiState', () => ({
|
||||
clearQueueHandoffPending: vi.fn(),
|
||||
}));
|
||||
|
||||
@@ -43,7 +43,7 @@ const playerState = {
|
||||
isPlaying: false,
|
||||
};
|
||||
|
||||
vi.mock('./playerStore', () => ({
|
||||
vi.mock('@/features/playback/store/playerStore', () => ({
|
||||
usePlayerStore: {
|
||||
getState: () => playerState,
|
||||
setState: (partial: Partial<typeof playerState>) => {
|
||||
@@ -52,13 +52,13 @@ vi.mock('./playerStore', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
import { applyServerPlayQueue } from './applyServerPlayQueue';
|
||||
import { applyServerPlayQueue } from '@/features/playback/store/applyServerPlayQueue';
|
||||
import {
|
||||
_resetQueuePlaybackIdleForTest,
|
||||
getIdlePullGeneration,
|
||||
isIdleQueuePullSuspended,
|
||||
touchQueueMutationClock,
|
||||
} from './queuePlaybackIdle';
|
||||
} from '@/features/playback/store/queuePlaybackIdle';
|
||||
|
||||
describe('applyServerPlayQueue idle guards', () => {
|
||||
beforeEach(() => {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user