mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-08 13:00:13 +02:00
feat: sync play queue for navidrome/subsonic (#1335)
--------- Co-authored-by: jeffvli <jeffvictorli@gmail.com>
This commit is contained in:
@@ -141,6 +141,20 @@ export const contract = c.router({
|
||||
200: ssType._response.getPlaylists,
|
||||
},
|
||||
},
|
||||
getPlayQueue: {
|
||||
method: 'GET',
|
||||
path: 'getPlayQueue.view',
|
||||
responses: {
|
||||
200: ssType._response.playQueue,
|
||||
},
|
||||
},
|
||||
getPlayQueueByIndex: {
|
||||
method: 'GET',
|
||||
path: 'getPlayQueueByIndex.view',
|
||||
responses: {
|
||||
200: ssType._response.playQueueByIndex,
|
||||
},
|
||||
},
|
||||
getRandomSongList: {
|
||||
method: 'GET',
|
||||
path: 'getRandomSongs.view',
|
||||
@@ -227,6 +241,22 @@ export const contract = c.router({
|
||||
200: ssType._response.removeFavorite,
|
||||
},
|
||||
},
|
||||
savePlayQueue: {
|
||||
method: 'GET',
|
||||
path: 'savePlayQueue.view',
|
||||
query: ssType._parameters.saveQueue,
|
||||
responses: {
|
||||
200: ssType._response.saveQueue,
|
||||
},
|
||||
},
|
||||
savePlayQueueByIndex: {
|
||||
method: 'GET',
|
||||
path: 'savePlayQueueByIndex.view',
|
||||
query: ssType._parameters.savePlayQueueByIndex,
|
||||
responses: {
|
||||
200: ssType._response.saveQueue,
|
||||
},
|
||||
},
|
||||
scrobble: {
|
||||
method: 'GET',
|
||||
path: 'scrobble.view',
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
ssType,
|
||||
SubsonicExtensions,
|
||||
} from '/@/shared/api/subsonic/subsonic-types';
|
||||
import { sortAlbumArtistList, sortAlbumList, sortSongList } from '/@/shared/api/utils';
|
||||
import { hasFeature, sortAlbumArtistList, sortAlbumList, sortSongList } from '/@/shared/api/utils';
|
||||
import {
|
||||
AlbumListSort,
|
||||
GenreListSort,
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
SongListSort,
|
||||
SortOrder,
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { ServerFeatures } from '/@/shared/types/features-types';
|
||||
import { ServerFeature, ServerFeatures } from '/@/shared/types/features-types';
|
||||
|
||||
const ALBUM_LIST_SORT_MAPPING: Record<AlbumListSort, AlbumListSortType | undefined> = {
|
||||
[AlbumListSort.ALBUM_ARTIST]: AlbumListSortType.ALPHABETICAL_BY_ARTIST,
|
||||
@@ -913,6 +913,44 @@ export const SubsonicController: InternalControllerEndpoint = {
|
||||
totalRecordCount: items.length,
|
||||
};
|
||||
},
|
||||
getPlayQueue: async ({ apiClientProps }) => {
|
||||
if (hasFeature(apiClientProps.server, ServerFeature.SERVER_PLAY_QUEUE)) {
|
||||
const res = await ssApiClient(apiClientProps).getPlayQueueByIndex();
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to get random songs');
|
||||
}
|
||||
|
||||
const { changed, changedBy, currentIndex, entry, position, username } =
|
||||
res.body.playQueueByIndex;
|
||||
|
||||
return {
|
||||
changed,
|
||||
changedBy,
|
||||
currentIndex: currentIndex ?? 0,
|
||||
entry: entry?.map((song) => ssNormalize.song(song, apiClientProps.server)) || [],
|
||||
positionMs: position ?? 0,
|
||||
username,
|
||||
};
|
||||
} else {
|
||||
const res = await ssApiClient(apiClientProps).getPlayQueue();
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to get random songs');
|
||||
}
|
||||
|
||||
const { changed, changedBy, current, entry, position, username } = res.body.playQueue;
|
||||
|
||||
return {
|
||||
changed,
|
||||
changedBy,
|
||||
currentIndex: current ? entry.findIndex((item) => item.id === current) : 0,
|
||||
entry: entry?.map((song) => ssNormalize.song(song, apiClientProps.server)) || [],
|
||||
positionMs: position ?? 0,
|
||||
username,
|
||||
};
|
||||
}
|
||||
},
|
||||
getRandomSongList: async (args) => {
|
||||
const { apiClientProps, query } = args;
|
||||
|
||||
@@ -967,6 +1005,7 @@ export const SubsonicController: InternalControllerEndpoint = {
|
||||
final.splice(0, 0, { label: 'all artists', value: '' });
|
||||
return final;
|
||||
},
|
||||
|
||||
getServerInfo: async (args) => {
|
||||
const { apiClientProps } = args;
|
||||
|
||||
@@ -1003,6 +1042,10 @@ export const SubsonicController: InternalControllerEndpoint = {
|
||||
features.osFormPost = [1];
|
||||
}
|
||||
|
||||
if (subsonicFeatures[SubsonicExtensions.INDEX_BASED_QUEUE]) {
|
||||
features.serverPlayQueue = [1];
|
||||
}
|
||||
|
||||
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion };
|
||||
},
|
||||
getSimilarSongs: async (args) => {
|
||||
@@ -1586,6 +1629,36 @@ export const SubsonicController: InternalControllerEndpoint = {
|
||||
|
||||
return null;
|
||||
},
|
||||
savePlayQueue: async ({ apiClientProps, query }) => {
|
||||
if (hasFeature(apiClientProps.server, ServerFeature.SERVER_PLAY_QUEUE)) {
|
||||
const res = await ssApiClient(apiClientProps).savePlayQueueByIndex({
|
||||
query: {
|
||||
currentIndex: query.currentIndex !== undefined ? query.currentIndex : undefined,
|
||||
id: query.songs,
|
||||
position: query.positionMs,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to save play queue');
|
||||
}
|
||||
} else {
|
||||
const res = await ssApiClient(apiClientProps).savePlayQueue({
|
||||
query: {
|
||||
current:
|
||||
query.currentIndex !== undefined && query.currentIndex < query.songs.length
|
||||
? query.songs[query.currentIndex]
|
||||
: undefined,
|
||||
id: query.songs,
|
||||
position: query.positionMs,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to save play queue');
|
||||
}
|
||||
}
|
||||
},
|
||||
scrobble: async (args) => {
|
||||
const { apiClientProps, query } = args;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user