add filepath replacement setting (#1402)

This commit is contained in:
jeffvli
2026-01-01 21:46:58 -08:00
parent e406b27170
commit 6aeec1e89c
13 changed files with 594 additions and 165 deletions
+5
View File
@@ -83,6 +83,7 @@
"edit": "edit", "edit": "edit",
"enable": "enable", "enable": "enable",
"expand": "expand", "expand": "expand",
"example": "example",
"externalLinks": "external links", "externalLinks": "external links",
"faster": "faster", "faster": "faster",
"favorite": "favorite", "favorite": "favorite",
@@ -872,6 +873,10 @@
"neteaseTranslation": "Enable NetEase translations", "neteaseTranslation": "Enable NetEase translations",
"notify": "enable song notifications", "notify": "enable song notifications",
"notify_description": "show notifications when changing the current song", "notify_description": "show notifications when changing the current song",
"pathReplace": "file path replacement",
"pathReplace_description": "replace your server's default filepath",
"pathReplace_optionRemovePrefix": "remove prefix",
"pathReplace_optionAddPrefix": "add prefix",
"passwordStore_description": "what password/secret store to use. change this if you are having issues storing passwords", "passwordStore_description": "what password/secret store to use. change this if you are having issues storing passwords",
"passwordStore": "passwords/secret store", "passwordStore": "passwords/secret store",
"playerFilters": "Filter songs from the queue", "playerFilters": "Filter songs from the queue",
+113 -66
View File
@@ -3,7 +3,7 @@ import { JellyfinController } from '/@/renderer/api/jellyfin/jellyfin-controller
import { NavidromeController } from '/@/renderer/api/navidrome/navidrome-controller'; import { NavidromeController } from '/@/renderer/api/navidrome/navidrome-controller';
import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller'; import { SubsonicController } from '/@/renderer/api/subsonic/subsonic-controller';
import { mergeMusicFolderId } from '/@/renderer/api/utils-music-folder'; import { mergeMusicFolderId } from '/@/renderer/api/utils-music-folder';
import { getServerById, useAuthStore } from '/@/renderer/store'; import { getServerById, useAuthStore, useSettingsStore } from '/@/renderer/store';
import { toast } from '/@/shared/components/toast/toast'; import { toast } from '/@/shared/components/toast/toast';
import { import {
AuthenticationResponse, AuthenticationResponse,
@@ -60,6 +60,22 @@ const apiController = <K extends keyof ControllerEndpoint>(
return controllerFn; return controllerFn;
}; };
const getPathReplaceSettings = () => {
const { pathReplace, pathReplaceWith } = useSettingsStore.getState().general;
return { pathReplace, pathReplaceWith };
};
const addContext = <T extends { apiClientProps: any; context?: any }>(args: T): T => {
const pathSettings = getPathReplaceSettings();
return {
...args,
context: {
...(args.context || {}),
...pathSettings,
},
};
};
export interface GeneralController extends Omit<Required<ControllerEndpoint>, 'authenticate'> { export interface GeneralController extends Omit<Required<ControllerEndpoint>, 'authenticate'> {
authenticate: ( authenticate: (
url: string, url: string,
@@ -81,7 +97,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'addToPlaylist', 'addToPlaylist',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
authenticate(url, body, type) { authenticate(url, body, type) {
return apiController('authenticate', type)(url, body); return apiController('authenticate', type)(url, body);
@@ -98,7 +114,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'createFavorite', 'createFavorite',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
createInternetRadioStation(args) { createInternetRadioStation(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -112,7 +128,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'createInternetRadioStation', 'createInternetRadioStation',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
createPlaylist(args) { createPlaylist(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -126,7 +142,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'createPlaylist', 'createPlaylist',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
deleteFavorite(args) { deleteFavorite(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -140,7 +156,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'deleteFavorite', 'deleteFavorite',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
deleteInternetRadioStation(args) { deleteInternetRadioStation(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -154,7 +170,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'deleteInternetRadioStation', 'deleteInternetRadioStation',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
deletePlaylist(args) { deletePlaylist(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -168,7 +184,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'deletePlaylist', 'deletePlaylist',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getAlbumArtistDetail(args) { getAlbumArtistDetail(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -182,7 +198,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getAlbumArtistDetail', 'getAlbumArtistDetail',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getAlbumArtistList(args) { getAlbumArtistList(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -196,11 +212,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getAlbumArtistList', 'getAlbumArtistList',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getAlbumArtistListCount(args) { getAlbumArtistListCount(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -214,11 +232,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getAlbumArtistListCount', 'getAlbumArtistListCount',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getAlbumDetail(args) { getAlbumDetail(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -232,7 +252,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getAlbumDetail', 'getAlbumDetail',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getAlbumInfo(args) { getAlbumInfo(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -246,7 +266,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getAlbumInfo', 'getAlbumInfo',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getAlbumList(args) { getAlbumList(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -260,11 +280,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getAlbumList', 'getAlbumList',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getAlbumListCount(args) { getAlbumListCount(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -278,11 +300,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getAlbumListCount', 'getAlbumListCount',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getArtistList(args) { getArtistList(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -296,11 +320,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getArtistList', 'getArtistList',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getArtistListCount(args) { getArtistListCount(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -314,11 +340,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getArtistListCount', 'getArtistListCount',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getArtistRadio(args) { getArtistRadio(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -332,7 +360,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getArtistRadio', 'getArtistRadio',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getDownloadUrl(args) { getDownloadUrl(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -346,7 +374,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getDownloadUrl', 'getDownloadUrl',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getFolder(args) { getFolder(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -360,11 +388,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getFolder', 'getFolder',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getGenreList(args) { getGenreList(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -378,11 +408,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getGenreList', 'getGenreList',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getImageUrl(args) { getImageUrl(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -395,7 +427,12 @@ export const controller: GeneralController = {
apiController( apiController(
'getImageUrl', 'getImageUrl',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }) || null )?.(
addContext({
...args,
apiClientProps: { ...args.apiClientProps, server },
}),
) || null
); );
}, },
getInternetRadioStations(args) { getInternetRadioStations(args) {
@@ -409,7 +446,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getInternetRadioStations', 'getInternetRadioStations',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getLyrics(args) { getLyrics(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -423,7 +460,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getLyrics', 'getLyrics',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getMusicFolderList(args) { getMusicFolderList(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -437,7 +474,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getMusicFolderList', 'getMusicFolderList',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getPlaylistDetail(args) { getPlaylistDetail(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -451,7 +488,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getPlaylistDetail', 'getPlaylistDetail',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getPlaylistList(args) { getPlaylistList(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -465,7 +502,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getPlaylistList', 'getPlaylistList',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getPlaylistListCount(args) { getPlaylistListCount(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -479,7 +516,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getPlaylistListCount', 'getPlaylistListCount',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getPlaylistSongList(args) { getPlaylistSongList(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -493,7 +530,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getPlaylistSongList', 'getPlaylistSongList',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getPlayQueue(args) { getPlayQueue(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -507,7 +544,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getPlayQueue', 'getPlayQueue',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getRandomSongList(args) { getRandomSongList(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -521,11 +558,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getRandomSongList', 'getRandomSongList',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getRoles(args) { getRoles(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -539,7 +578,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getRoles', 'getRoles',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getServerInfo(args) { getServerInfo(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -553,7 +592,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getServerInfo', 'getServerInfo',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getSimilarSongs(args) { getSimilarSongs(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -567,11 +606,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getSimilarSongs', 'getSimilarSongs',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getSongDetail(args) { getSongDetail(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -585,7 +626,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getSongDetail', 'getSongDetail',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getSongList(args) { getSongList(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -599,11 +640,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getSongList', 'getSongList',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getSongListCount(args) { getSongListCount(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -617,11 +660,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getSongListCount', 'getSongListCount',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
getStreamUrl(args) { getStreamUrl(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -633,7 +678,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getStreamUrl', 'getStreamUrl',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getStructuredLyrics(args) { getStructuredLyrics(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -647,7 +692,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getStructuredLyrics', 'getStructuredLyrics',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getTagList(args) { getTagList(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -661,7 +706,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getTagList', 'getTagList',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getTopSongs(args) { getTopSongs(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -675,7 +720,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getTopSongs', 'getTopSongs',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getUserInfo(args) { getUserInfo(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -689,7 +734,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getUserInfo', 'getUserInfo',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
getUserList(args) { getUserList(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -703,7 +748,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getUserList', 'getUserList',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
movePlaylistItem(args) { movePlaylistItem(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -717,7 +762,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'movePlaylistItem', 'movePlaylistItem',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
removeFromPlaylist(args) { removeFromPlaylist(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -731,7 +776,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'removeFromPlaylist', 'removeFromPlaylist',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
replacePlaylist(args) { replacePlaylist(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -745,7 +790,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'replacePlaylist', 'replacePlaylist',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
savePlayQueue(args) { savePlayQueue(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -759,7 +804,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'savePlayQueue', 'savePlayQueue',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
scrobble(args) { scrobble(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -773,7 +818,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'scrobble', 'scrobble',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
search(args) { search(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -787,11 +832,13 @@ export const controller: GeneralController = {
return apiController( return apiController(
'search', 'search',
server.type, server.type,
)?.({ )?.(
addContext({
...args, ...args,
apiClientProps: { ...args.apiClientProps, server }, apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server), query: mergeMusicFolderId(args.query, server),
}); }),
);
}, },
setRating(args) { setRating(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -805,7 +852,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'setRating', 'setRating',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
shareItem(args) { shareItem(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -819,7 +866,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'shareItem', 'shareItem',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
updateInternetRadioStation(args) { updateInternetRadioStation(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -833,7 +880,7 @@ export const controller: GeneralController = {
return apiController( return apiController(
'updateInternetRadioStation', 'updateInternetRadioStation',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
updatePlaylist(args) { updatePlaylist(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -847,6 +894,6 @@ export const controller: GeneralController = {
return apiController( return apiController(
'updatePlaylist', 'updatePlaylist',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.(addContext({ ...args, apiClientProps: { ...args.apiClientProps, server } }));
}, },
}; };
@@ -447,7 +447,14 @@ export const JellyfinController: InternalControllerEndpoint = {
throw new Error('Failed to get artist radio songs'); throw new Error('Failed to get artist radio songs');
} }
return res.body.Items.map((song) => jfNormalize.song(song, apiClientProps.server)); return res.body.Items.map((song) =>
jfNormalize.song(
song,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
);
}, },
getDownloadUrl: (args) => { getDownloadUrl: (args) => {
const { apiClientProps, query } = args; const { apiClientProps, query } = args;
@@ -858,7 +865,14 @@ export const JellyfinController: InternalControllerEndpoint = {
} }
return { return {
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server)), items: res.body.Items.map((item) =>
jfNormalize.song(
item,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
),
startIndex: 0, startIndex: 0,
totalRecordCount: res.body.TotalRecordCount, totalRecordCount: res.body.TotalRecordCount,
}; };
@@ -911,7 +925,14 @@ export const JellyfinController: InternalControllerEndpoint = {
} }
return { return {
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server)), items: res.body.Items.map((item) =>
jfNormalize.song(
item,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
),
startIndex: 0, startIndex: 0,
totalRecordCount: res.body.Items.length || 0, totalRecordCount: res.body.Items.length || 0,
}; };
@@ -961,7 +982,14 @@ export const JellyfinController: InternalControllerEndpoint = {
if (res.status === 200 && res.body.Items.length) { if (res.status === 200 && res.body.Items.length) {
const results = res.body.Items.reduce<Song[]>((acc, song) => { const results = res.body.Items.reduce<Song[]>((acc, song) => {
if (song.Id !== query.songId) { if (song.Id !== query.songId) {
acc.push(jfNormalize.song(song, apiClientProps.server)); acc.push(
jfNormalize.song(
song,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
);
} }
return acc; return acc;
@@ -990,7 +1018,14 @@ export const JellyfinController: InternalControllerEndpoint = {
return mix.body.Items.reduce<Song[]>((acc, song) => { return mix.body.Items.reduce<Song[]>((acc, song) => {
if (song.Id !== query.songId) { if (song.Id !== query.songId) {
acc.push(jfNormalize.song(song, apiClientProps.server)); acc.push(
jfNormalize.song(
song,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
);
} }
return acc; return acc;
@@ -1010,7 +1045,12 @@ export const JellyfinController: InternalControllerEndpoint = {
throw new Error('Failed to get song detail'); throw new Error('Failed to get song detail');
} }
return jfNormalize.song(res.body, apiClientProps.server); return jfNormalize.song(
res.body,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
);
}, },
getSongList: async (args) => { getSongList: async (args) => {
const { apiClientProps, query } = args; const { apiClientProps, query } = args;
@@ -1122,7 +1162,14 @@ export const JellyfinController: InternalControllerEndpoint = {
} }
return { return {
items: items.map((item) => jfNormalize.song(item, apiClientProps.server)), items: items.map((item) =>
jfNormalize.song(
item,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
),
startIndex: query.startIndex, startIndex: query.startIndex,
totalRecordCount, totalRecordCount,
}; };
@@ -1221,7 +1268,14 @@ export const JellyfinController: InternalControllerEndpoint = {
} }
return { return {
items: res.body.Items.map((item) => jfNormalize.song(item, apiClientProps.server)), items: res.body.Items.map((item) =>
jfNormalize.song(
item,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
),
startIndex: 0, startIndex: 0,
totalRecordCount: res.body.TotalRecordCount, totalRecordCount: res.body.TotalRecordCount,
}; };
@@ -1306,7 +1360,12 @@ export const JellyfinController: InternalControllerEndpoint = {
} }
const existingSongs = existingSongsRes.body.Items.map((item) => const existingSongs = existingSongsRes.body.Items.map((item) =>
jfNormalize.song(item, apiClientProps.server), jfNormalize.song(
item,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
); );
// 2. Get playlist detail to get the name // 2. Get playlist detail to get the name
@@ -1546,7 +1605,14 @@ export const JellyfinController: InternalControllerEndpoint = {
jfNormalize.albumArtist(item, apiClientProps.server), jfNormalize.albumArtist(item, apiClientProps.server),
), ),
albums: albums.map((item) => jfNormalize.album(item, apiClientProps.server)), albums: albums.map((item) => jfNormalize.album(item, apiClientProps.server)),
songs: songs.map((item) => jfNormalize.song(item, apiClientProps.server)), songs: songs.map((item) =>
jfNormalize.song(
item,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
),
}; };
}, },
updateInternetRadioStation: async (args) => { updateInternetRadioStation: async (args) => {
@@ -268,7 +268,7 @@ export const NavidromeController: InternalControllerEndpoint = {
query: { ...query, limit: 1, startIndex: 0 }, query: { ...query, limit: 1, startIndex: 0 },
}).then((result) => result!.totalRecordCount!), }).then((result) => result!.totalRecordCount!),
getAlbumDetail: async (args) => { getAlbumDetail: async (args) => {
const { apiClientProps, query } = args; const { apiClientProps, context, query } = args;
const albumRes = await ndApiClient(apiClientProps).getAlbumDetail({ const albumRes = await ndApiClient(apiClientProps).getAlbumDetail({
params: { params: {
@@ -294,6 +294,8 @@ export const NavidromeController: InternalControllerEndpoint = {
return ndNormalize.album( return ndNormalize.album(
{ ...albumRes.body.data, songs: songsData.body.data }, { ...albumRes.body.data, songs: songsData.body.data },
apiClientProps.server, apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
); );
}, },
getAlbumInfo: async (args) => { getAlbumInfo: async (args) => {
@@ -317,7 +319,7 @@ export const NavidromeController: InternalControllerEndpoint = {
}; };
}, },
getAlbumList: async (args) => { getAlbumList: async (args) => {
const { apiClientProps, query } = args; const { apiClientProps, context, query } = args;
const genres = hasFeature(apiClientProps.server, ServerFeature.BFR) const genres = hasFeature(apiClientProps.server, ServerFeature.BFR)
? query.genreIds ? query.genreIds
@@ -352,7 +354,14 @@ export const NavidromeController: InternalControllerEndpoint = {
} }
return { return {
items: res.body.data.map((album) => ndNormalize.album(album, apiClientProps.server)), items: res.body.data.map((album) =>
ndNormalize.album(
album,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
),
startIndex: query?.startIndex || 0, startIndex: query?.startIndex || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0), totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
}; };
@@ -560,7 +569,14 @@ export const NavidromeController: InternalControllerEndpoint = {
} }
return { return {
items: res.body.data.map((item) => ndNormalize.song(item, apiClientProps.server)), items: res.body.data.map((item) =>
ndNormalize.song(
item,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
),
startIndex: 0, startIndex: 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0), totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
}; };
@@ -577,7 +593,14 @@ export const NavidromeController: InternalControllerEndpoint = {
const { changedBy, current, items, position, updatedAt } = res.body.data; const { changedBy, current, items, position, updatedAt } = res.body.data;
const entries = items.map((song) => ndNormalize.song(song, apiClientProps.server)); const entries = items.map((song) =>
ndNormalize.song(
song,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
);
return { return {
changed: updatedAt, changed: updatedAt,
@@ -668,7 +691,12 @@ export const NavidromeController: InternalControllerEndpoint = {
throw new Error('Failed to get song detail'); throw new Error('Failed to get song detail');
} }
return ndNormalize.song(res.body.data, apiClientProps.server); return ndNormalize.song(
res.body.data,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
);
}, },
getSongList: async (args) => { getSongList: async (args) => {
const { apiClientProps, query } = args; const { apiClientProps, query } = args;
@@ -696,7 +724,14 @@ export const NavidromeController: InternalControllerEndpoint = {
} }
return { return {
items: res.body.data.map((song) => ndNormalize.song(song, apiClientProps.server)), items: res.body.data.map((song) =>
ndNormalize.song(
song,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
),
startIndex: query?.startIndex || 0, startIndex: query?.startIndex || 0,
totalRecordCount: Number(res.body.headers.get('x-total-count') || 0), totalRecordCount: Number(res.body.headers.get('x-total-count') || 0),
}; };
@@ -847,7 +882,12 @@ export const NavidromeController: InternalControllerEndpoint = {
} }
const existingSongs = existingSongsRes.body.data.map((item) => const existingSongs = existingSongsRes.body.data.map((item) =>
ndNormalize.song(item, apiClientProps.server), ndNormalize.song(
item,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
); );
// 2. Get playlist detail to get the name // 2. Get playlist detail to get the name
+179 -36
View File
@@ -282,7 +282,14 @@ export const SubsonicController: InternalControllerEndpoint = {
return { return {
...ssNormalize.albumArtist(artist, apiClientProps.server), ...ssNormalize.albumArtist(artist, apiClientProps.server),
albums: artist.album?.map((album) => ssNormalize.album(album, apiClientProps.server)), albums: artist.album?.map((album) =>
ssNormalize.album(
album,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
),
similarArtists: similarArtists:
artistInfo?.similarArtist?.map((artist) => artistInfo?.similarArtist?.map((artist) =>
ssNormalize.albumArtist(artist, apiClientProps.server), ssNormalize.albumArtist(artist, apiClientProps.server),
@@ -327,6 +334,7 @@ export const SubsonicController: InternalControllerEndpoint = {
getAlbumArtistListCount: (args) => getAlbumArtistListCount: (args) =>
SubsonicController.getAlbumArtistList({ SubsonicController.getAlbumArtistList({
...args, ...args,
context: args.context,
query: { ...args.query, startIndex: 0 }, query: { ...args.query, startIndex: 0 },
}).then((res) => res!.totalRecordCount!), }).then((res) => res!.totalRecordCount!),
getAlbumDetail: async (args) => { getAlbumDetail: async (args) => {
@@ -342,7 +350,12 @@ export const SubsonicController: InternalControllerEndpoint = {
throw new Error('Failed to get album detail'); throw new Error('Failed to get album detail');
} }
return ssNormalize.album(res.body.album, apiClientProps.server); return ssNormalize.album(
res.body.album,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
);
}, },
getAlbumList: async (args) => { getAlbumList: async (args) => {
const { apiClientProps, query } = args; const { apiClientProps, query } = args;
@@ -367,7 +380,12 @@ export const SubsonicController: InternalControllerEndpoint = {
const results = const results =
res.body.searchResult3?.album?.map((album) => res.body.searchResult3?.album?.map((album) =>
ssNormalize.album(album, apiClientProps.server), ssNormalize.album(
album,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
) || []; ) || [];
return { return {
@@ -402,7 +420,14 @@ export const SubsonicController: InternalControllerEndpoint = {
return artist.body.artist.album ?? []; return artist.body.artist.album ?? [];
}); });
const items = albums.map((album) => ssNormalize.album(album, apiClientProps.server)); const items = albums.map((album) =>
ssNormalize.album(
album,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
);
return { return {
items: sortAlbumList(items, query.sortBy, query.sortOrder), items: sortAlbumList(items, query.sortBy, query.sortOrder),
@@ -424,7 +449,12 @@ export const SubsonicController: InternalControllerEndpoint = {
const allResults = const allResults =
res.body.starred?.album?.map((album) => res.body.starred?.album?.map((album) =>
ssNormalize.album(album, apiClientProps.server), ssNormalize.album(
album,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
) || []; ) || [];
return sortAndPaginate(allResults, { return sortAndPaginate(allResults, {
@@ -489,7 +519,12 @@ export const SubsonicController: InternalControllerEndpoint = {
return { return {
items: items:
res.body.albumList2.album?.map((album) => res.body.albumList2.album?.map((album) =>
ssNormalize.album(album, apiClientProps.server), ssNormalize.album(
album,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
) || [], ) || [],
startIndex: query.startIndex, startIndex: query.startIndex,
totalRecordCount: null, totalRecordCount: null,
@@ -682,10 +717,11 @@ export const SubsonicController: InternalControllerEndpoint = {
getArtistListCount: async (args) => getArtistListCount: async (args) =>
SubsonicController.getArtistList({ SubsonicController.getArtistList({
...args, ...args,
context: args.context,
query: { ...args.query, startIndex: 0 }, query: { ...args.query, startIndex: 0 },
}).then((res) => res!.totalRecordCount!), }).then((res) => res!.totalRecordCount!),
getArtistRadio: async (args) => { getArtistRadio: async (args) => {
const { apiClientProps, query } = args; const { apiClientProps, context, query } = args;
const res = await ssApiClient(apiClientProps).getSimilarSongs2({ const res = await ssApiClient(apiClientProps).getSimilarSongs2({
query: { query: {
@@ -703,7 +739,12 @@ export const SubsonicController: InternalControllerEndpoint = {
} }
return res.body.similarSongs2.song.map((song) => return res.body.similarSongs2.song.map((song) =>
ssNormalize.song(song, apiClientProps.server), ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
); );
}, },
getDownloadUrl: (args) => { getDownloadUrl: (args) => {
@@ -717,7 +758,7 @@ export const SubsonicController: InternalControllerEndpoint = {
'&c=Feishin' '&c=Feishin'
); );
}, },
getFolder: async ({ apiClientProps, query }) => { getFolder: async ({ apiClientProps, context, query }) => {
const sortOrder = (query.sortOrder?.toLowerCase() ?? 'asc') as 'asc' | 'desc'; const sortOrder = (query.sortOrder?.toLowerCase() ?? 'asc') as 'asc' | 'desc';
const isRootFolderId = /^\d+$/.test(query.id); const isRootFolderId = /^\d+$/.test(query.id);
@@ -749,7 +790,14 @@ export const SubsonicController: InternalControllerEndpoint = {
}); });
} }
let folders = items.map((item) => ssNormalize.folder(item, apiClientProps.server)); let folders = items.map((item) =>
ssNormalize.folder(
item,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
);
folders = orderBy(folders, [(v) => v.name.toLowerCase()], [sortOrder]); folders = orderBy(folders, [(v) => v.name.toLowerCase()], [sortOrder]);
@@ -777,7 +825,12 @@ export const SubsonicController: InternalControllerEndpoint = {
throw new Error('Failed to get folder'); throw new Error('Failed to get folder');
} }
const folder = ssNormalize.folder(directoryRes.body.directory, apiClientProps.server); const folder = ssNormalize.folder(
directoryRes.body.directory,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
);
let filteredFolders = folder.children?.folders || []; let filteredFolders = folder.children?.folders || [];
let filteredSongs = folder.children?.songs || []; let filteredSongs = folder.children?.songs || [];
@@ -991,7 +1044,7 @@ export const SubsonicController: InternalControllerEndpoint = {
return results.length; return results.length;
}, },
getPlaylistSongList: async ({ apiClientProps, query }) => { getPlaylistSongList: async ({ apiClientProps, context, query }) => {
const res = await ssApiClient(apiClientProps).getPlaylist({ const res = await ssApiClient(apiClientProps).getPlaylist({
query: { query: {
id: query.id, id: query.id,
@@ -1003,8 +1056,14 @@ export const SubsonicController: InternalControllerEndpoint = {
} }
const items = const items =
res.body.playlist.entry?.map((song) => ssNormalize.song(song, apiClientProps.server)) || res.body.playlist.entry?.map((song) =>
[]; ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
) || [];
return { return {
items, items,
@@ -1012,7 +1071,7 @@ export const SubsonicController: InternalControllerEndpoint = {
totalRecordCount: items.length, totalRecordCount: items.length,
}; };
}, },
getPlayQueue: async ({ apiClientProps }) => { getPlayQueue: async ({ apiClientProps, context }) => {
if (hasFeature(apiClientProps.server, ServerFeature.SERVER_PLAY_QUEUE)) { if (hasFeature(apiClientProps.server, ServerFeature.SERVER_PLAY_QUEUE)) {
const res = await ssApiClient(apiClientProps).getPlayQueueByIndex(); const res = await ssApiClient(apiClientProps).getPlayQueueByIndex();
@@ -1027,7 +1086,15 @@ export const SubsonicController: InternalControllerEndpoint = {
changed, changed,
changedBy, changedBy,
currentIndex: currentIndex ?? 0, currentIndex: currentIndex ?? 0,
entry: entry?.map((song) => ssNormalize.song(song, apiClientProps.server)) || [], entry:
entry?.map((song) =>
ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
) || [],
positionMs: position ?? 0, positionMs: position ?? 0,
username, username,
}; };
@@ -1044,14 +1111,22 @@ export const SubsonicController: InternalControllerEndpoint = {
changed, changed,
changedBy, changedBy,
currentIndex: current ? entry.findIndex((item) => item.id === current) : 0, currentIndex: current ? entry.findIndex((item) => item.id === current) : 0,
entry: entry?.map((song) => ssNormalize.song(song, apiClientProps.server)) || [], entry:
entry?.map((song) =>
ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
) || [],
positionMs: position ?? 0, positionMs: position ?? 0,
username, username,
}; };
} }
}, },
getRandomSongList: async (args) => { getRandomSongList: async (args) => {
const { apiClientProps, query } = args; const { apiClientProps, context, query } = args;
const res = await ssApiClient(apiClientProps).getRandomSongList({ const res = await ssApiClient(apiClientProps).getRandomSongList({
query: { query: {
@@ -1069,7 +1144,12 @@ export const SubsonicController: InternalControllerEndpoint = {
const results = res.body.randomSongs?.song || []; const results = res.body.randomSongs?.song || [];
const normalizedResults = results.map((song) => const normalizedResults = results.map((song) =>
ssNormalize.song(song, apiClientProps.server), ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
); );
return { return {
@@ -1147,7 +1227,7 @@ export const SubsonicController: InternalControllerEndpoint = {
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion }; return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion };
}, },
getSimilarSongs: async (args) => { getSimilarSongs: async (args) => {
const { apiClientProps, query } = args; const { apiClientProps, context, query } = args;
const res = await ssApiClient(apiClientProps).getSimilarSongs({ const res = await ssApiClient(apiClientProps).getSimilarSongs({
query: { query: {
@@ -1166,14 +1246,21 @@ export const SubsonicController: InternalControllerEndpoint = {
return res.body.similarSongs.song.reduce<Song[]>((acc, song) => { return res.body.similarSongs.song.reduce<Song[]>((acc, song) => {
if (song.id !== query.songId) { if (song.id !== query.songId) {
acc.push(ssNormalize.song(song, apiClientProps.server)); acc.push(
ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
);
} }
return acc; return acc;
}, []); }, []);
}, },
getSongDetail: async (args) => { getSongDetail: async (args) => {
const { apiClientProps, query } = args; const { apiClientProps, context, query } = args;
const res = await ssApiClient(apiClientProps).getSong({ const res = await ssApiClient(apiClientProps).getSong({
query: { query: {
@@ -1185,9 +1272,14 @@ export const SubsonicController: InternalControllerEndpoint = {
throw new Error('Failed to get song detail'); throw new Error('Failed to get song detail');
} }
return ssNormalize.song(res.body.song, apiClientProps.server); return ssNormalize.song(
res.body.song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
);
}, },
getSongList: async ({ apiClientProps, query }) => { getSongList: async ({ apiClientProps, context, query }) => {
const fromAlbumPromises: Promise<ServerInferResponses<typeof contract.getAlbum>>[] = []; const fromAlbumPromises: Promise<ServerInferResponses<typeof contract.getAlbum>>[] = [];
const artistDetailPromises: Promise<ServerInferResponses<typeof contract.getArtist>>[] = []; const artistDetailPromises: Promise<ServerInferResponses<typeof contract.getArtist>>[] = [];
@@ -1212,7 +1304,12 @@ export const SubsonicController: InternalControllerEndpoint = {
return { return {
items: items:
res.body.searchResult3?.song?.map((song) => res.body.searchResult3?.song?.map((song) =>
ssNormalize.song(song, apiClientProps.server), ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
) || [], ) || [],
startIndex: query.startIndex, startIndex: query.startIndex,
totalRecordCount: null, totalRecordCount: null,
@@ -1236,7 +1333,15 @@ export const SubsonicController: InternalControllerEndpoint = {
const results = res.body.songsByGenre?.song || []; const results = res.body.songsByGenre?.song || [];
return { return {
items: results.map((song) => ssNormalize.song(song, apiClientProps.server)) || [], items:
results.map((song) =>
ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
) || [],
startIndex: 0, startIndex: 0,
totalRecordCount: null, totalRecordCount: null,
}; };
@@ -1255,7 +1360,12 @@ export const SubsonicController: InternalControllerEndpoint = {
const allResults = const allResults =
(res.body.starred?.song || []).map((song) => (res.body.starred?.song || []).map((song) =>
ssNormalize.song(song, apiClientProps.server), ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
) || []; ) || [];
return sortAndPaginate(allResults, { return sortAndPaginate(allResults, {
@@ -1331,7 +1441,15 @@ export const SubsonicController: InternalControllerEndpoint = {
} }
return { return {
items: results.map((song) => ssNormalize.song(song, apiClientProps.server)) || [], items:
results.map((song) =>
ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
) || [],
startIndex: 0, startIndex: 0,
totalRecordCount: results.length, totalRecordCount: results.length,
}; };
@@ -1357,7 +1475,12 @@ export const SubsonicController: InternalControllerEndpoint = {
return { return {
items: items:
res.body.searchResult3?.song?.map((song) => res.body.searchResult3?.song?.map((song) =>
ssNormalize.song(song, apiClientProps.server), ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
) || [], ) || [],
startIndex: 0, startIndex: 0,
totalRecordCount: null, totalRecordCount: null,
@@ -1596,7 +1719,7 @@ export const SubsonicController: InternalControllerEndpoint = {
}); });
}, },
getTopSongs: async (args) => { getTopSongs: async (args) => {
const { apiClientProps, query } = args; const { apiClientProps, context, query } = args;
const res = await ssApiClient(apiClientProps).getTopSongsList({ const res = await ssApiClient(apiClientProps).getTopSongsList({
query: { query: {
@@ -1612,7 +1735,12 @@ export const SubsonicController: InternalControllerEndpoint = {
return { return {
items: items:
res.body.topSongs?.song?.map((song) => res.body.topSongs?.song?.map((song) =>
ssNormalize.song(song, apiClientProps.server), ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
) || [], ) || [],
startIndex: 0, startIndex: 0,
totalRecordCount: res.body.topSongs?.song?.length || 0, totalRecordCount: res.body.topSongs?.song?.length || 0,
@@ -1652,7 +1780,7 @@ export const SubsonicController: InternalControllerEndpoint = {
return null; return null;
}, },
replacePlaylist: async (args) => { replacePlaylist: async (args) => {
const { apiClientProps, body, query } = args; const { apiClientProps, body, context, query } = args;
// 1. Fetch existing songs from the playlist // 1. Fetch existing songs from the playlist
const existingSongsRes = await ssApiClient(apiClientProps).getPlaylist({ const existingSongsRes = await ssApiClient(apiClientProps).getPlaylist({
@@ -1667,7 +1795,12 @@ export const SubsonicController: InternalControllerEndpoint = {
const existingSongs = const existingSongs =
existingSongsRes.body.playlist.entry?.map((song) => existingSongsRes.body.playlist.entry?.map((song) =>
ssNormalize.song(song, apiClientProps.server), ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
) || []; ) || [];
// 2. Get playlist detail to get the name // 2. Get playlist detail to get the name
@@ -1779,7 +1912,7 @@ export const SubsonicController: InternalControllerEndpoint = {
return null; return null;
}, },
search: async (args) => { search: async (args) => {
const { apiClientProps, query } = args; const { apiClientProps, context, query } = args;
const res = await ssApiClient(apiClientProps).search3({ const res = await ssApiClient(apiClientProps).search3({
query: { query: {
@@ -1803,10 +1936,20 @@ export const SubsonicController: InternalControllerEndpoint = {
ssNormalize.albumArtist(artist, apiClientProps.server), ssNormalize.albumArtist(artist, apiClientProps.server),
), ),
albums: (res.body.searchResult3?.album || []).map((album) => albums: (res.body.searchResult3?.album || []).map((album) =>
ssNormalize.album(album, apiClientProps.server), ssNormalize.album(
album,
apiClientProps.server,
args.context?.pathReplace,
args.context?.pathReplaceWith,
),
), ),
songs: (res.body.searchResult3?.song || []).map((song) => songs: (res.body.searchResult3?.song || []).map((song) =>
ssNormalize.song(song, apiClientProps.server), ssNormalize.song(
song,
apiClientProps.server,
context?.pathReplace,
context?.pathReplaceWith,
),
), ),
}; };
}, },
@@ -12,6 +12,7 @@ import {
ArtistSettings, ArtistSettings,
} from '/@/renderer/features/settings/components/general/artist-settings'; } from '/@/renderer/features/settings/components/general/artist-settings';
import { HomeSettings } from '/@/renderer/features/settings/components/general/home-settings'; import { HomeSettings } from '/@/renderer/features/settings/components/general/home-settings';
import { PathSettings } from '/@/renderer/features/settings/components/general/path-settings';
import { import {
SettingOption, SettingOption,
SettingsSection, SettingsSection,
@@ -610,6 +611,7 @@ export const ApplicationSettings = () => {
<HomeSettings /> <HomeSettings />
<ArtistSettings /> <ArtistSettings />
<ArtistReleaseTypeSettings /> <ArtistReleaseTypeSettings />
<PathSettings />
</> </>
} }
options={options} options={options}
@@ -0,0 +1,83 @@
import { useQuery } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { songsQueries } from '/@/renderer/features/songs/api/songs-api';
import {
useCurrentServerId,
useGeneralSettings,
useSettingsStore,
useSettingsStoreActions,
} from '/@/renderer/store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Code } from '/@/shared/components/code/code';
import { Group } from '/@/shared/components/group/group';
import { Stack } from '/@/shared/components/stack/stack';
import { TextInput } from '/@/shared/components/text-input/text-input';
import { Text } from '/@/shared/components/text/text';
import { Played } from '/@/shared/types/domain-types';
export const PathSettings = () => {
const { t } = useTranslation();
const serverId = useCurrentServerId();
const randomSong = useQuery({
...songsQueries.random({
query: { limit: 1, played: Played.All },
serverId,
}),
gcTime: Infinity,
staleTime: Infinity,
});
const { pathReplace, pathReplaceWith } = useGeneralSettings();
const { setSettings } = useSettingsStoreActions();
return (
<Stack>
<Group>
<Text>{t('setting.pathReplace', { postProcess: 'sentenceCase' })}</Text>
<ActionIcon
icon="refresh"
loading={randomSong.isFetching}
onClick={() => randomSong.refetch()}
size="xs"
variant="transparent"
/>
</Group>
<Code>
<Text isMuted size="md">
{randomSong.data?.items[0]?.path || ''}
</Text>
</Code>
<Group grow>
<TextInput
onChange={(e) =>
setSettings({
general: {
...useSettingsStore.getState().general,
pathReplace: e.currentTarget.value,
},
})
}
placeholder={t('setting.pathReplace_optionRemovePrefix', {
postProcess: 'sentenceCase',
})}
value={pathReplace}
/>
<TextInput
onChange={(e) =>
setSettings({
general: {
...useSettingsStore.getState().general,
pathReplaceWith: e.currentTarget.value,
},
})
}
placeholder={t('setting.pathReplace_optionAddPrefix', {
postProcess: 'sentenceCase',
})}
value={pathReplaceWith}
/>
</Group>
</Stack>
);
};
+4
View File
@@ -400,6 +400,8 @@ export const GeneralSettingsSchema = z.object({
musicBrainz: z.boolean(), musicBrainz: z.boolean(),
nativeAspectRatio: z.boolean(), nativeAspectRatio: z.boolean(),
passwordStore: z.string().optional(), passwordStore: z.string().optional(),
pathReplace: z.string(),
pathReplaceWith: z.string(),
playButtonBehavior: z.nativeEnum(Play), playButtonBehavior: z.nativeEnum(Play),
playerbarOpenDrawer: z.boolean(), playerbarOpenDrawer: z.boolean(),
playerbarSlider: PlayerbarSliderSchema, playerbarSlider: PlayerbarSliderSchema,
@@ -953,6 +955,8 @@ const initialState: SettingsState = {
musicBrainz: true, musicBrainz: true,
nativeAspectRatio: false, nativeAspectRatio: false,
passwordStore: undefined, passwordStore: undefined,
pathReplace: '',
pathReplaceWith: '',
playButtonBehavior: Play.NOW, playButtonBehavior: Play.NOW,
playerbarOpenDrawer: false, playerbarOpenDrawer: false,
playerbarSlider: { playerbarSlider: {
@@ -1,6 +1,7 @@
import { z } from 'zod'; import { z } from 'zod';
import { jfType } from '/@/shared/api/jellyfin/jellyfin-types'; import { jfType } from '/@/shared/api/jellyfin/jellyfin-types';
import { replacePathPrefix } from '/@/shared/api/utils';
import { import {
Album, Album,
AlbumArtist, AlbumArtist,
@@ -108,6 +109,8 @@ const getPlaylistImageId = (item: z.infer<typeof jfType._response.playlist>): nu
const normalizeSong = ( const normalizeSong = (
item: z.infer<typeof jfType._response.song>, item: z.infer<typeof jfType._response.song>,
server: null | ServerListItem, server: null | ServerListItem,
pathReplace?: string,
pathReplaceWith?: string,
): Song => { ): Song => {
let bitRate = 0; let bitRate = 0;
let channels: null | number = null; let channels: null | number = null;
@@ -208,7 +211,10 @@ const normalizeSong = (
mbzTrackId: item.ProviderIds?.MusicBrainzTrack || null, mbzTrackId: item.ProviderIds?.MusicBrainzTrack || null,
name: item.Name, name: item.Name,
participants: getPeople(item), participants: getPeople(item),
path, path:
path && (pathReplace || pathReplaceWith)
? replacePathPrefix(path, pathReplace || '', pathReplaceWith || '')
: path,
peak: null, peak: null,
playCount: (item.UserData && item.UserData.PlayCount) || 0, playCount: (item.UserData && item.UserData.PlayCount) || 0,
playlistItemId: item.PlaylistItemId, playlistItemId: item.PlaylistItemId,
@@ -2,6 +2,7 @@ import z from 'zod';
import { ndType } from '/@/shared/api/navidrome/navidrome-types'; import { ndType } from '/@/shared/api/navidrome/navidrome-types';
import { ssType } from '/@/shared/api/subsonic/subsonic-types'; import { ssType } from '/@/shared/api/subsonic/subsonic-types';
import { replacePathPrefix } from '/@/shared/api/utils';
import { import {
Album, Album,
AlbumArtist, AlbumArtist,
@@ -139,6 +140,8 @@ const getArtists = (
const normalizeSong = ( const normalizeSong = (
item: z.infer<typeof ndType._response.playlistSong> | z.infer<typeof ndType._response.song>, item: z.infer<typeof ndType._response.playlistSong> | z.infer<typeof ndType._response.song>,
server?: null | ServerListItem, server?: null | ServerListItem,
pathReplace?: string,
pathReplaceWith?: string,
): Song => { ): Song => {
let id; let id;
let playlistItemId; let playlistItemId;
@@ -202,7 +205,7 @@ const normalizeSong = (
name: item.title, name: item.title,
// Thankfully, Windows is merciful and allows a mix of separators. So, we can use the // Thankfully, Windows is merciful and allows a mix of separators. So, we can use the
// POSIX separator here instead // POSIX separator here instead
path: (item.libraryPath ? item.libraryPath + '/' : '') + item.path, path: item.path ? replacePathPrefix(item.path, pathReplace, pathReplaceWith) : null,
peak: peak:
item.rgAlbumPeak || item.rgTrackPeak item.rgAlbumPeak || item.rgTrackPeak
? { album: item.rgAlbumPeak, track: item.rgTrackPeak } ? { album: item.rgAlbumPeak, track: item.rgTrackPeak }
@@ -267,6 +270,8 @@ const normalizeAlbum = (
songs?: z.infer<typeof ndType._response.songList>; songs?: z.infer<typeof ndType._response.songList>;
}, },
server?: null | ServerListItem, server?: null | ServerListItem,
pathReplace?: string,
pathReplaceWith?: string,
): Album => { ): Album => {
return { return {
...parseAlbumTags(item), ...parseAlbumTags(item),
@@ -309,7 +314,9 @@ const normalizeAlbum = (
releaseYear: item.maxYear || null, releaseYear: item.maxYear || null,
size: item.size, size: item.size,
songCount: item.songCount, songCount: item.songCount,
songs: item.songs ? item.songs.map((song) => normalizeSong(song, server)) : undefined, songs: item.songs
? item.songs.map((song) => normalizeSong(song, server, pathReplace, pathReplaceWith))
: undefined,
tags: item.tags || null, tags: item.tags || null,
updatedAt: item.updatedAt, updatedAt: item.updatedAt,
userFavorite: item.starred || false, userFavorite: item.starred || false,
+13 -3
View File
@@ -1,6 +1,7 @@
import { z } from 'zod'; import { z } from 'zod';
import { ssType } from '/@/shared/api/subsonic/subsonic-types'; import { ssType } from '/@/shared/api/subsonic/subsonic-types';
import { replacePathPrefix } from '/@/shared/api/utils';
import { import {
Album, Album,
AlbumArtist, AlbumArtist,
@@ -117,6 +118,8 @@ const getGenres = (
const normalizeSong = ( const normalizeSong = (
item: z.infer<typeof ssType._response.song>, item: z.infer<typeof ssType._response.song>,
server?: null | ServerListItemWithCredential, server?: null | ServerListItemWithCredential,
pathReplace?: string,
pathReplaceWith?: string,
): Song => { ): Song => {
return { return {
_itemType: LibraryItem.SONG, _itemType: LibraryItem.SONG,
@@ -162,7 +165,10 @@ const normalizeSong = (
mbzTrackId: null, mbzTrackId: null,
name: item.title, name: item.title,
participants: getParticipants(item), participants: getParticipants(item),
path: item.path, path:
pathReplace || pathReplaceWith
? replacePathPrefix(item.path || '', pathReplace, pathReplaceWith)
: item.path,
peak: peak:
item.replayGain && (item.replayGain.albumPeak || item.replayGain.trackPeak) item.replayGain && (item.replayGain.albumPeak || item.replayGain.trackPeak)
? { ? {
@@ -243,6 +249,8 @@ const getReleaseType = (
const normalizeAlbum = ( const normalizeAlbum = (
item: z.infer<typeof ssType._response.album> | z.infer<typeof ssType._response.albumListEntry>, item: z.infer<typeof ssType._response.album> | z.infer<typeof ssType._response.albumListEntry>,
server?: null | ServerListItemWithCredential, server?: null | ServerListItemWithCredential,
pathReplace?: string,
pathReplaceWith?: string,
): Album => { ): Album => {
return { return {
_itemType: LibraryItem.ALBUM, _itemType: LibraryItem.ALBUM,
@@ -286,7 +294,7 @@ const normalizeAlbum = (
songCount: item.songCount, songCount: item.songCount,
songs: songs:
(item as z.infer<typeof ssType._response.album>).song?.map((song) => (item as z.infer<typeof ssType._response.album>).song?.map((song) =>
normalizeSong(song, server), normalizeSong(song, server, pathReplace, pathReplaceWith),
) || [], ) || [],
tags: null, tags: null,
updatedAt: item.created, updatedAt: item.created,
@@ -341,6 +349,8 @@ const normalizeGenre = (
const normalizeFolder = ( const normalizeFolder = (
item: z.infer<typeof ssType._response.directory>, item: z.infer<typeof ssType._response.directory>,
server?: null | ServerListItemWithCredential, server?: null | ServerListItemWithCredential,
pathReplace?: string,
pathReplaceWith?: string,
): Folder => { ): Folder => {
const results = item.child?.reduce( const results = item.child?.reduce(
(acc: { folders: Folder[]; songs: Song[] }, item) => { (acc: { folders: Folder[]; songs: Song[] }, item) => {
@@ -350,7 +360,7 @@ const normalizeFolder = (
const folder = normalizeFolder(item, server); const folder = normalizeFolder(item, server);
acc.folders.push(folder); acc.folders.push(folder);
} else { } else {
const song = normalizeSong(item, server); const song = normalizeSong(item, server, pathReplace, pathReplaceWith);
acc.songs.push(song); acc.songs.push(song);
} }
+8
View File
@@ -471,3 +471,11 @@ export const sortRadioList = (
return results; return results;
}; };
export const replacePathPrefix = (path: string, replacePrefix?: string, addPrefix?: string) => {
if (replacePrefix && path.startsWith(replacePrefix)) {
return path.slice(replacePrefix.length);
}
return addPrefix ? addPrefix + path : path;
};
+8
View File
@@ -403,6 +403,10 @@ type BaseEndpointArgs = {
serverId: string; serverId: string;
signal?: AbortSignal; signal?: AbortSignal;
}; };
context?: {
pathReplace?: string;
pathReplaceWith?: string;
};
}; };
type GenreListSortMap = { type GenreListSortMap = {
@@ -1652,4 +1656,8 @@ type BaseEndpointArgsWithServer = {
serverId: string; serverId: string;
signal?: AbortSignal; signal?: AbortSignal;
}; };
context?: {
pathReplace?: string;
pathReplaceWith?: string;
};
}; };