refactor song path replacement

- path replacement during runtime instead of during API normalization
- fix Navidrome API path not appending libraryPath which caused inconsistency between ND and Subsonic paths
This commit is contained in:
jeffvli
2026-06-19 22:02:12 -07:00
parent 36624350f6
commit 61cc87e0b7
15 changed files with 119 additions and 398 deletions
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
import { ContextMenu } from '/@/shared/components/context-menu/context-menu';
import { toast } from '/@/shared/components/toast/toast';
import { resolveSongPath } from '/@/renderer/utils/resolve-song-path';
import { QueueSong, Song } from '/@/shared/types/domain-types';
interface ShowInFileExplorerActionProps {
@@ -21,12 +22,13 @@ export const ShowInFileExplorerAction = ({ items }: ShowInFileExplorerActionProp
}
const firstItem = items[0];
if (!firstItem?.path) {
const resolvedPath = resolveSongPath(firstItem?.path);
if (!resolvedPath) {
return;
}
try {
await utils.openItem(firstItem.path);
await utils.openItem(resolvedPath);
} catch (error) {
toast.error({
message: (error as Error).message,
@@ -1,6 +1,7 @@
import isElectron from 'is-electron';
import { useTranslation } from 'react-i18next';
import { useResolvedSongPath } from '/@/renderer/utils/resolve-song-path';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { CopyButton } from '/@/shared/components/copy-button/copy-button';
import { Group } from '/@/shared/components/group/group';
@@ -17,12 +18,13 @@ export type SongPathProps = {
export const SongPath = ({ path }: SongPathProps) => {
const { t } = useTranslation();
const resolvedPath = useResolvedSongPath(path);
if (!path) return null;
if (!resolvedPath) return null;
return (
<Group>
<CopyButton timeout={2000} value={path}>
<CopyButton timeout={2000} value={resolvedPath}>
{({ copied, copy }) => (
<Tooltip
label={t(
@@ -42,7 +44,7 @@ export const SongPath = ({ path }: SongPathProps) => {
<ActionIcon
icon="externalLink"
onClick={() => {
util.openItem(path).catch((error) => {
util.openItem(resolvedPath).catch((error) => {
toast.error({
message: (error as Error).message,
title: t('error.openError'),
@@ -53,7 +55,7 @@ export const SongPath = ({ path }: SongPathProps) => {
/>
</Tooltip>
)}
<Text style={{ userSelect: 'all' }}>{path}</Text>
<Text style={{ userSelect: 'all' }}>{resolvedPath}</Text>
</Group>
);
};
+2 -1
View File
@@ -6,6 +6,7 @@ import { folderQueries } from '/@/renderer/features/folders/api/folder-api';
import { PlayerFilter, useSettingsStore } from '/@/renderer/store';
import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message';
import { resolveSongPath } from '/@/renderer/utils/resolve-song-path';
import { sortSongList } from '/@/shared/api/utils';
import {
PlaylistSongListQuery,
@@ -351,7 +352,7 @@ const getSongFieldValue = (song: Song, field: string): boolean | null | number |
case 'note':
return song.comment || '';
case 'path':
return song.path || '';
return resolveSongPath(song.path) || '';
case 'playCount':
return song.playCount;
case 'rating':
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next';
import { songsQueries } from '/@/renderer/features/songs/api/songs-api';
import { useCurrentServerId, useGeneralSettings, useSettingsStoreActions } from '/@/renderer/store';
import { useResolvedSongPath } from '/@/renderer/utils/resolve-song-path';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Code } from '/@/shared/components/code/code';
import { Group } from '/@/shared/components/group/group';
@@ -27,6 +28,7 @@ export const PathSettings = memo(() => {
const { pathReplace, pathReplaceWith } = useGeneralSettings();
const { setSettings } = useSettingsStoreActions();
const resolvedPreviewPath = useResolvedSongPath(randomSong.data?.items[0]?.path);
const [localPathReplace, setLocalPathReplace] = useState(pathReplace);
const [localPathReplaceWith, setLocalPathReplaceWith] = useState(pathReplaceWith);
@@ -45,8 +47,6 @@ export const PathSettings = memo(() => {
pathReplace: value,
},
});
randomSong.refetch();
}, 500);
const debouncedSetPathReplaceWith = useDebouncedCallback((value: string) => {
@@ -55,8 +55,6 @@ export const PathSettings = memo(() => {
pathReplaceWith: value,
},
});
randomSong.refetch();
}, 500);
return (
@@ -73,7 +71,7 @@ export const PathSettings = memo(() => {
</Group>
<Code>
<Text isMuted size="md">
{randomSong.data?.items[0]?.path || ''}
{resolvedPreviewPath || ''}
</Text>
</Code>
<Group grow>