mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-01 02:13:33 +00:00
Implement Navidrome sharing (#575)
* add share item feature * take care of (mostly) everything * bugfixes * allow clicking on notification to open url * readd the missing modal after router migration * remove unnecessary extension --------- Co-authored-by: Kendall Garner <17521368+kgarner7@users.noreply.github.com>
This commit is contained in:
parent
0d03b66fe5
commit
cb2597d2c8
14 changed files with 303 additions and 4 deletions
|
|
@ -8,6 +8,7 @@ import type {
|
|||
AlbumArtistDetailArgs,
|
||||
AlbumArtistListArgs,
|
||||
SetRatingArgs,
|
||||
ShareItemArgs,
|
||||
GenreListArgs,
|
||||
CreatePlaylistArgs,
|
||||
DeletePlaylistArgs,
|
||||
|
|
@ -55,6 +56,7 @@ import type {
|
|||
SimilarSongsArgs,
|
||||
Song,
|
||||
ServerType,
|
||||
ShareItemResponse,
|
||||
} from '/@/renderer/api/types';
|
||||
import { DeletePlaylistResponse, RandomSongListArgs } from './types';
|
||||
import { ndController } from '/@/renderer/api/navidrome/navidrome-controller';
|
||||
|
|
@ -102,6 +104,7 @@ export type ControllerEndpoint = Partial<{
|
|||
scrobble: (args: ScrobbleArgs) => Promise<ScrobbleResponse>;
|
||||
search: (args: SearchArgs) => Promise<SearchResponse>;
|
||||
setRating: (args: SetRatingArgs) => Promise<RatingResponse>;
|
||||
shareItem: (args: ShareItemArgs) => Promise<ShareItemResponse>;
|
||||
updatePlaylist: (args: UpdatePlaylistArgs) => Promise<UpdatePlaylistResponse>;
|
||||
}>;
|
||||
|
||||
|
|
@ -149,6 +152,7 @@ const endpoints: ApiController = {
|
|||
scrobble: jfController.scrobble,
|
||||
search: jfController.search,
|
||||
setRating: undefined,
|
||||
shareItem: undefined,
|
||||
updatePlaylist: jfController.updatePlaylist,
|
||||
},
|
||||
navidrome: {
|
||||
|
|
@ -188,6 +192,7 @@ const endpoints: ApiController = {
|
|||
scrobble: ssController.scrobble,
|
||||
search: ssController.search3,
|
||||
setRating: ssController.setRating,
|
||||
shareItem: ndController.shareItem,
|
||||
updatePlaylist: ndController.updatePlaylist,
|
||||
},
|
||||
subsonic: {
|
||||
|
|
@ -223,6 +228,7 @@ const endpoints: ApiController = {
|
|||
scrobble: ssController.scrobble,
|
||||
search: ssController.search3,
|
||||
setRating: undefined,
|
||||
shareItem: undefined,
|
||||
updatePlaylist: undefined,
|
||||
},
|
||||
};
|
||||
|
|
@ -457,6 +463,15 @@ const updateRating = async (args: SetRatingArgs) => {
|
|||
)?.(args);
|
||||
};
|
||||
|
||||
const shareItem = async (args: ShareItemArgs) => {
|
||||
return (
|
||||
apiController(
|
||||
'shareItem',
|
||||
args.apiClientProps.server?.type,
|
||||
) as ControllerEndpoint['shareItem']
|
||||
)?.(args);
|
||||
};
|
||||
|
||||
const getTopSongList = async (args: TopSongListArgs) => {
|
||||
return (
|
||||
apiController(
|
||||
|
|
@ -555,6 +570,7 @@ export const controller = {
|
|||
removeFromPlaylist,
|
||||
scrobble,
|
||||
search,
|
||||
shareItem,
|
||||
updatePlaylist,
|
||||
updateRating,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ export enum ServerFeature {
|
|||
LYRICS_MULTIPLE_STRUCTURED = 'lyricsMultipleStructured',
|
||||
LYRICS_SINGLE_STRUCTURED = 'lyricsSingleStructured',
|
||||
PLAYLISTS_SMART = 'playlistsSmart',
|
||||
SHARING_ALBUM_SONG = 'sharingAlbumSong',
|
||||
}
|
||||
|
||||
export type ServerFeatures = Partial<Record<ServerFeature, boolean>>;
|
||||
|
|
|
|||
|
|
@ -157,6 +157,16 @@ export const contract = c.router({
|
|||
500: resultWithHeaders(ndType._response.error),
|
||||
},
|
||||
},
|
||||
shareItem: {
|
||||
body: ndType._parameters.shareItem,
|
||||
method: 'POST',
|
||||
path: 'share',
|
||||
responses: {
|
||||
200: resultWithHeaders(ndType._response.shareItem),
|
||||
404: resultWithHeaders(ndType._response.error),
|
||||
500: resultWithHeaders(ndType._response.error),
|
||||
},
|
||||
},
|
||||
updatePlaylist: {
|
||||
body: ndType._parameters.updatePlaylist,
|
||||
method: 'PUT',
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ import {
|
|||
genreListSortMap,
|
||||
ServerInfo,
|
||||
ServerInfoArgs,
|
||||
ShareItemArgs,
|
||||
ShareItemResponse,
|
||||
SimilarSongsArgs,
|
||||
Song,
|
||||
} from '../types';
|
||||
|
|
@ -484,7 +486,10 @@ const removeFromPlaylist = async (
|
|||
return null;
|
||||
};
|
||||
|
||||
// The order should be in decreasing version, as the highest version match
|
||||
// will automatically consider all lower versions matched
|
||||
const VERSION_INFO: Array<[string, Record<string, number[]>]> = [
|
||||
['0.49.3', { [ServerFeature.SHARING_ALBUM_SONG]: [1] }],
|
||||
['0.48.0', { [ServerFeature.PLAYLISTS_SMART]: [1] }],
|
||||
];
|
||||
|
||||
|
|
@ -544,11 +549,34 @@ const getServerInfo = async (args: ServerInfoArgs): Promise<ServerInfo> => {
|
|||
const features: ServerFeatures = {
|
||||
lyricsMultipleStructured: !!navidromeFeatures[SubsonicExtensions.SONG_LYRICS],
|
||||
playlistsSmart: !!navidromeFeatures[ServerFeature.PLAYLISTS_SMART],
|
||||
sharingAlbumSong: !!navidromeFeatures[ServerFeature.SHARING_ALBUM_SONG],
|
||||
};
|
||||
|
||||
return { features, id: apiClientProps.server?.id, version: ping.body.serverVersion! };
|
||||
};
|
||||
|
||||
const shareItem = async (args: ShareItemArgs): Promise<ShareItemResponse> => {
|
||||
const { body, apiClientProps } = args;
|
||||
|
||||
const res = await ndApiClient(apiClientProps).shareItem({
|
||||
body: {
|
||||
description: body.description,
|
||||
downloadable: body.downloadable,
|
||||
expires: body.expires,
|
||||
resourceIds: body.resourceIds,
|
||||
resourceType: body.resourceType,
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status !== 200) {
|
||||
throw new Error('Failed to share item');
|
||||
}
|
||||
|
||||
return {
|
||||
id: res.body.data.id,
|
||||
};
|
||||
};
|
||||
|
||||
const getSimilarSongs = async (args: SimilarSongsArgs): Promise<Song[]> => {
|
||||
const { apiClientProps, query } = args;
|
||||
|
||||
|
|
@ -620,5 +648,6 @@ export const ndController = {
|
|||
getSongList,
|
||||
getUserList,
|
||||
removeFromPlaylist,
|
||||
shareItem,
|
||||
updatePlaylist,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -343,6 +343,18 @@ const removeFromPlaylistParameters = z.object({
|
|||
id: z.array(z.string()),
|
||||
});
|
||||
|
||||
const shareItem = z.object({
|
||||
id: z.string(),
|
||||
});
|
||||
|
||||
const shareItemParameters = z.object({
|
||||
description: z.string(),
|
||||
downloadable: z.boolean(),
|
||||
expires: z.number(),
|
||||
resourceIds: z.string(),
|
||||
resourceType: z.string(),
|
||||
});
|
||||
|
||||
export const ndType = {
|
||||
_enum: {
|
||||
albumArtistList: ndAlbumArtistListSort,
|
||||
|
|
@ -361,6 +373,7 @@ export const ndType = {
|
|||
genreList: genreListParameters,
|
||||
playlistList: playlistListParameters,
|
||||
removeFromPlaylist: removeFromPlaylistParameters,
|
||||
shareItem: shareItemParameters,
|
||||
songList: songListParameters,
|
||||
updatePlaylist: updatePlaylistParameters,
|
||||
userList: userListParameters,
|
||||
|
|
@ -382,6 +395,7 @@ export const ndType = {
|
|||
playlistSong,
|
||||
playlistSongList,
|
||||
removeFromPlaylist,
|
||||
shareItem,
|
||||
song,
|
||||
songList,
|
||||
updatePlaylist,
|
||||
|
|
|
|||
|
|
@ -766,6 +766,19 @@ export type RatingQuery = {
|
|||
|
||||
export type SetRatingArgs = { query: RatingQuery; serverId?: string } & BaseEndpointArgs;
|
||||
|
||||
// Sharing
|
||||
export type ShareItemResponse = { id: string } | undefined;
|
||||
|
||||
export type ShareItemBody = {
|
||||
description: string;
|
||||
downloadable: boolean;
|
||||
expires: number;
|
||||
resourceIds: string;
|
||||
resourceType: string;
|
||||
};
|
||||
|
||||
export type ShareItemArgs = { body: ShareItemBody; serverId?: string } & BaseEndpointArgs;
|
||||
|
||||
// Add to playlist
|
||||
export type AddToPlaylistResponse = null | undefined;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue