mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-08 04:50:12 +02:00
feat: sync play queue for navidrome/subsonic (#1335)
--------- Co-authored-by: jeffvli <jeffvictorli@gmail.com>
This commit is contained in:
@@ -14,6 +14,8 @@ import {
|
||||
} from '/@/shared/types/domain-types';
|
||||
import { ServerListItem, ServerType } from '/@/shared/types/types';
|
||||
|
||||
const TICKS_PER_MS = 10000;
|
||||
|
||||
const getAlbumArtistCoverArtUrl = (args: {
|
||||
baseUrl: string;
|
||||
item: z.infer<typeof jfType._response.albumArtist>;
|
||||
@@ -221,7 +223,7 @@ const normalizeSong = (
|
||||
createdAt: item.DateCreated,
|
||||
discNumber: (item.ParentIndexNumber && item.ParentIndexNumber) || 1,
|
||||
discSubtitle: null,
|
||||
duration: item.RunTimeTicks / 10000,
|
||||
duration: item.RunTimeTicks / TICKS_PER_MS,
|
||||
explicitStatus: null,
|
||||
gain:
|
||||
item.NormalizationGain !== undefined
|
||||
@@ -294,7 +296,7 @@ const normalizeAlbum = (
|
||||
backdropImageUrl: null,
|
||||
comment: null,
|
||||
createdAt: item.DateCreated,
|
||||
duration: item.RunTimeTicks / 10000,
|
||||
duration: item.RunTimeTicks / TICKS_PER_MS,
|
||||
explicitStatus: null,
|
||||
genres:
|
||||
item.GenreItems?.map((entry) => ({
|
||||
@@ -363,7 +365,7 @@ const normalizeAlbumArtist = (
|
||||
albumCount: item.AlbumCount ?? null,
|
||||
backgroundImageUrl: null,
|
||||
biography: item.Overview || null,
|
||||
duration: item.RunTimeTicks / 10000,
|
||||
duration: item.RunTimeTicks / TICKS_PER_MS,
|
||||
genres: item.GenreItems?.map((entry) => ({
|
||||
_itemType: LibraryItem.GENRE,
|
||||
_serverId: server?.id || '',
|
||||
@@ -409,7 +411,7 @@ const normalizePlaylist = (
|
||||
_serverId: server?.id || '',
|
||||
_serverType: ServerType.JELLYFIN,
|
||||
description: item.Overview || null,
|
||||
duration: item.RunTimeTicks / 10000,
|
||||
duration: item.RunTimeTicks / TICKS_PER_MS,
|
||||
genres: item.GenreItems?.map((entry) => ({
|
||||
_itemType: LibraryItem.GENRE,
|
||||
_serverId: server?.id || '',
|
||||
|
||||
@@ -253,6 +253,7 @@ const sessionInfo = z.object({
|
||||
CanSeek: z.boolean(),
|
||||
IsMuted: z.boolean(),
|
||||
IsPaused: z.boolean(),
|
||||
PositionTicks: z.number().optional(),
|
||||
RepeatMode: z.string(),
|
||||
}),
|
||||
RemoteEndPoint: z.string(),
|
||||
@@ -801,6 +802,28 @@ const folderParameters = z.object({
|
||||
SortOrder: z.enum(sortOrderValues).optional(),
|
||||
});
|
||||
|
||||
const queueItem = z.object({
|
||||
Id: z.string(),
|
||||
PlaylistItemId: z.string().optional(),
|
||||
});
|
||||
|
||||
const saveQueueParameters = scrobbleParameters.merge(
|
||||
z.object({
|
||||
NowPlayingQueue: z.array(queueItem),
|
||||
PlaylistItemId: z.string().optional(),
|
||||
}),
|
||||
);
|
||||
|
||||
const getQueueParameters = z.object({});
|
||||
|
||||
const getSessions = z.array(
|
||||
sessionInfo.merge(
|
||||
z.object({
|
||||
PlaylistItemId: z.string().optional(),
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
export const jfType = {
|
||||
_enum: {
|
||||
albumArtistList: albumArtistListSort,
|
||||
@@ -825,10 +848,12 @@ export const jfType = {
|
||||
filterList: filterListParameters,
|
||||
folder: folderParameters,
|
||||
genreList: genreListParameters,
|
||||
getQueue: getQueueParameters,
|
||||
musicFolderList: musicFolderListParameters,
|
||||
playlistDetail: playlistDetailParameters,
|
||||
playlistList: playlistListParameters,
|
||||
removeFromPlaylist: removeFromPlaylistParameters,
|
||||
saveQueue: saveQueueParameters,
|
||||
scrobble: scrobbleParameters,
|
||||
search: searchParameters,
|
||||
similarArtistList: similarArtistListParameters,
|
||||
@@ -853,6 +878,7 @@ export const jfType = {
|
||||
folderList,
|
||||
genre,
|
||||
genreList,
|
||||
getSessions,
|
||||
lyrics,
|
||||
moveItem,
|
||||
musicFolder,
|
||||
|
||||
@@ -676,6 +676,25 @@ const tagListParameters = optionalPaginationParameters.extend({
|
||||
tag_value: z.string().optional(), // Search
|
||||
});
|
||||
|
||||
const saveQueueParameters = z.object({
|
||||
current: z.number().optional(),
|
||||
ids: z.array(z.string()).optional(),
|
||||
position: z.number().optional(),
|
||||
});
|
||||
|
||||
const saveQueue = z.null();
|
||||
|
||||
const queue = z.object({
|
||||
changedBy: z.string(),
|
||||
createdAt: z.string(),
|
||||
current: z.number(),
|
||||
id: z.string(),
|
||||
items: z.array(song),
|
||||
position: z.number(),
|
||||
updatedAt: z.string(),
|
||||
userId: z.string(),
|
||||
});
|
||||
|
||||
export const ndType = {
|
||||
_enum: {
|
||||
albumArtistList: NDAlbumArtistListSort,
|
||||
@@ -696,6 +715,7 @@ export const ndType = {
|
||||
moveItem: moveItemParameters,
|
||||
playlistList: playlistListParameters,
|
||||
removeFromPlaylist: removeFromPlaylistParameters,
|
||||
saveQueue: saveQueueParameters,
|
||||
shareItem: shareItemParameters,
|
||||
songList: songListParameters,
|
||||
tagList: tagListParameters,
|
||||
@@ -719,7 +739,9 @@ export const ndType = {
|
||||
playlistList,
|
||||
playlistSong,
|
||||
playlistSongList,
|
||||
queue,
|
||||
removeFromPlaylist,
|
||||
saveQueue,
|
||||
shareItem,
|
||||
song,
|
||||
songList,
|
||||
|
||||
@@ -356,6 +356,7 @@ const similarSongs = z.object({
|
||||
|
||||
export enum SubsonicExtensions {
|
||||
FORM_POST = 'formPost',
|
||||
INDEX_BASED_QUEUE = 'indexBasedQueue',
|
||||
SONG_LYRICS = 'songLyrics',
|
||||
TRANSCODE_OFFSET = 'transcodeOffset',
|
||||
}
|
||||
@@ -617,6 +618,42 @@ const getIndexesParameters = z.object({
|
||||
musicFolderId: z.string().optional(),
|
||||
});
|
||||
|
||||
const saveQueueParameters = z.object({
|
||||
current: z.string().optional(),
|
||||
id: z.string().array(),
|
||||
position: z.number().optional(),
|
||||
});
|
||||
|
||||
const savePlayQueueByIndexParameters = z.object({
|
||||
currentIndex: z.number().optional(),
|
||||
id: z.string().array().optional(),
|
||||
position: z.number().optional(),
|
||||
});
|
||||
|
||||
const saveQueue = z.null();
|
||||
|
||||
const playQueue = z.object({
|
||||
playQueue: z.object({
|
||||
changed: z.string(),
|
||||
changedBy: z.string(),
|
||||
current: z.string().optional(),
|
||||
entry: song.array(),
|
||||
position: z.number().optional(),
|
||||
username: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
const playQueueByIndex = z.object({
|
||||
playQueueByIndex: z.object({
|
||||
changed: z.string(),
|
||||
changedBy: z.string(),
|
||||
currentIndex: z.number().optional(),
|
||||
entry: song.array().optional(),
|
||||
position: z.number().optional(),
|
||||
username: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const ssType = {
|
||||
_parameters: {
|
||||
albumInfo: albumInfoParameters,
|
||||
@@ -641,6 +678,8 @@ export const ssType = {
|
||||
getStarred: getStarredParameters,
|
||||
randomSongList: randomSongListParameters,
|
||||
removeFavorite: removeFavoriteParameters,
|
||||
savePlayQueueByIndex: savePlayQueueByIndexParameters,
|
||||
saveQueue: saveQueueParameters,
|
||||
scrobble: scrobbleParameters,
|
||||
search3: search3Parameters,
|
||||
setRating: setRatingParameters,
|
||||
@@ -681,8 +720,11 @@ export const ssType = {
|
||||
ping,
|
||||
playlist,
|
||||
playlistListEntry,
|
||||
playQueue,
|
||||
playQueueByIndex,
|
||||
randomSongList,
|
||||
removeFavorite,
|
||||
saveQueue,
|
||||
scrobble,
|
||||
search3,
|
||||
serverInfo,
|
||||
|
||||
@@ -49,6 +49,18 @@ export const hasFeature = (server: null | ServerListItem, feature: ServerFeature
|
||||
return (server.features[feature]?.length || 0) > 0;
|
||||
};
|
||||
|
||||
export const hasFeatureWithVersion = (
|
||||
server: null | ServerListItem,
|
||||
feature: ServerFeature,
|
||||
version: number,
|
||||
): boolean => {
|
||||
if (!server || !server.features) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (server.features[feature] ?? []).includes(version);
|
||||
};
|
||||
|
||||
export type VersionInfo = ReadonlyArray<
|
||||
[string, Partial<Record<ServerFeature, readonly number[]>>]
|
||||
>;
|
||||
|
||||
Reference in New Issue
Block a user