add native nd radio endpoints, support radio station images

This commit is contained in:
jeffvli
2026-04-02 18:26:26 -07:00
parent fbf82c1ef0
commit db06e7f601
18 changed files with 789 additions and 90 deletions
@@ -8,6 +8,7 @@ import {
AlbumArtist,
ExplicitStatus,
Genre,
InternetRadioStation,
LibraryItem,
Playlist,
RelatedArtist,
@@ -543,10 +544,28 @@ const normalizeUser = (item: z.infer<typeof ndType._response.user>): User => {
};
};
const normalizeInternetRadioStation = (
item: z.infer<typeof ndType._response.radioStation>,
): InternetRadioStation => {
const homepageUrl = item.homePageUrl?.trim() ? item.homePageUrl : null;
const imageId = item.uploadedImage ? `ra-${item.id}&square=true&_=${item.updatedAt}` : item.id;
return {
homepageUrl,
id: item.id,
imageId,
imageUrl: null,
name: item.name,
streamUrl: item.streamUrl,
uploadedImage: item.uploadedImage || null,
};
};
export const ndNormalize = {
album: normalizeAlbum,
albumArtist: normalizeAlbumArtist,
genre: normalizeGenre,
internetRadioStation: normalizeInternetRadioStation,
playlist: normalizePlaylist,
song: normalizeSong,
user: normalizeUser,
@@ -660,6 +660,12 @@ const updatePlaylist = playlist;
const updatePlaylistParameters = createPlaylistParameters.partial();
const updateInternetRadioStationParameters = z.object({
homePageUrl: z.string().optional(),
name: z.string(),
streamUrl: z.string(),
});
const uploadPlaylistImage = z.object({
status: z.string(),
});
@@ -672,8 +678,14 @@ const deletePlaylistImage = z.object({
status: z.string(),
});
const uploadInternetRadioStationImage = uploadPlaylistImage;
const uploadInternetRadioStationImageParameters = uploadPlaylistImageParameters;
const deleteInternetRadioStationImage = deletePlaylistImage;
const deletePlaylist = z.null();
const deleteInternetRadioStation = deletePlaylist;
const addToPlaylist = z.object({
added: z.number(),
});
@@ -748,12 +760,35 @@ const queue = z.object({
userId: z.string(),
});
export enum NDRadioListSort {
NAME = 'name',
}
const radioStation = z.object({
createdAt: z.string(),
homePageUrl: z.string().optional(),
id: z.string(),
name: z.string(),
streamUrl: z.string(),
updatedAt: z.string(),
uploadedImage: z.string().optional(),
});
const radioList = z.array(radioStation);
const updateInternetRadioStation = radioStation;
const radioListParameters = optionalPaginationParameters.extend({
_sort: z.nativeEnum(NDRadioListSort).optional(),
});
export const ndType = {
_enum: {
albumArtistList: NDAlbumArtistListSort,
albumList: NDAlbumListSort,
genreList: genreListSort,
playlistList: NDPlaylistListSort,
radioList: NDRadioListSort,
songList: NDSongListSort,
tagList: NDTagListSort,
userList: ndUserListSort,
@@ -767,12 +802,15 @@ export const ndType = {
genreList: genreListParameters,
moveItem: moveItemParameters,
playlistList: playlistListParameters,
radioList: radioListParameters,
removeFromPlaylist: removeFromPlaylistParameters,
saveQueue: saveQueueParameters,
shareItem: shareItemParameters,
songList: songListParameters,
tagList: tagListParameters,
updateInternetRadioStation: updateInternetRadioStationParameters,
updatePlaylist: updatePlaylistParameters,
uploadInternetRadioStationImage: uploadInternetRadioStationImageParameters,
uploadPlaylistImage: uploadPlaylistImageParameters,
userList: userListParameters,
},
@@ -784,6 +822,8 @@ export const ndType = {
albumList,
authenticate,
createPlaylist,
deleteInternetRadioStation,
deleteInternetRadioStationImage,
deletePlaylist,
deletePlaylistImage,
error,
@@ -795,13 +835,17 @@ export const ndType = {
playlistSong,
playlistSongList,
queue,
radioList,
radioStation,
removeFromPlaylist,
saveQueue,
shareItem,
song,
songList,
tagList,
updateInternetRadioStation,
updatePlaylist,
uploadInternetRadioStationImage,
uploadPlaylistImage,
user,
userList,
@@ -432,6 +432,8 @@ const normalizeInternetRadioStation = (
return {
homepageUrl: item.homepageUrl || null,
id: item.id,
imageId: item.coverArt?.toString() || null,
imageUrl: null,
name: item.name,
streamUrl: item.streamUrl,
};
@@ -755,6 +755,7 @@ const playQueueByIndex = z.object({
});
const internetRadioStation = z.object({
coverArt: z.string().optional(),
homepageUrl: z.string().optional(),
id: z.string(),
name: z.string(),
+41 -1
View File
@@ -959,6 +959,16 @@ export type DeleteInternetRadioStationArgs = BaseEndpointArgs & {
query: DeleteInternetRadioStationQuery;
};
export type DeleteInternetRadioStationImageArgs = BaseEndpointArgs & {
query: DeleteInternetRadioStationImageQuery;
};
export type DeleteInternetRadioStationImageQuery = {
id: string;
};
export type DeleteInternetRadioStationImageResponse = boolean;
export type DeleteInternetRadioStationQuery = {
id: string;
};
@@ -999,10 +1009,13 @@ export type GetInternetRadioStationsArgs = BaseEndpointArgs;
export type GetInternetRadioStationsResponse = InternetRadioStation[];
export type InternetRadioStation = {
homepageUrl?: null | string;
homepageUrl: null | string;
id: string;
imageId?: null | string;
imageUrl?: null | string;
name: string;
streamUrl: string;
uploadedImage?: null | string;
};
export type PlaylistListArgs = BaseEndpointArgs & { query: PlaylistListQuery };
@@ -1117,6 +1130,21 @@ export type UpdatePlaylistQuery = {
// Update Playlist
export type UpdatePlaylistResponse = null | undefined;
export type UploadInternetRadioStationImageArgs = BaseEndpointArgs & {
body: UploadInternetRadioStationImageBody;
query: UploadInternetRadioStationImageQuery;
};
export type UploadInternetRadioStationImageBody = {
image: Uint8Array;
};
export type UploadInternetRadioStationImageQuery = {
id: string;
};
export type UploadInternetRadioStationImageResponse = boolean;
export type UploadPlaylistImageArgs = BaseEndpointArgs & {
body: UploadPlaylistImageBody;
query: UploadPlaylistImageQuery;
@@ -1415,6 +1443,9 @@ export type ControllerEndpoint = {
deleteInternetRadioStation: (
args: DeleteInternetRadioStationArgs,
) => Promise<DeleteInternetRadioStationResponse>;
deleteInternetRadioStationImage?: (
args: DeleteInternetRadioStationImageArgs,
) => Promise<DeleteInternetRadioStationImageResponse>;
deletePlaylist: (args: DeletePlaylistArgs) => Promise<DeletePlaylistResponse>;
deletePlaylistImage?: (args: DeletePlaylistImageArgs) => Promise<DeletePlaylistImageResponse>;
getAlbumArtistDetail: (args: AlbumArtistDetailArgs) => Promise<AlbumArtistDetailResponse>;
@@ -1470,6 +1501,9 @@ export type ControllerEndpoint = {
args: UpdateInternetRadioStationArgs,
) => Promise<UpdateInternetRadioStationResponse>;
updatePlaylist: (args: UpdatePlaylistArgs) => Promise<UpdatePlaylistResponse>;
uploadInternetRadioStationImage?: (
args: UploadInternetRadioStationImageArgs,
) => Promise<UploadInternetRadioStationImageResponse>;
uploadPlaylistImage?: (args: UploadPlaylistImageArgs) => Promise<UploadPlaylistImageResponse>;
};
@@ -1540,6 +1574,9 @@ export type InternalControllerEndpoint = {
deleteInternetRadioStation: (
args: ReplaceApiClientProps<DeleteInternetRadioStationArgs>,
) => Promise<DeleteInternetRadioStationResponse>;
deleteInternetRadioStationImage?: (
args: ReplaceApiClientProps<DeleteInternetRadioStationImageArgs>,
) => Promise<DeleteInternetRadioStationImageResponse>;
deletePlaylist: (
args: ReplaceApiClientProps<DeletePlaylistArgs>,
) => Promise<DeletePlaylistResponse>;
@@ -1630,6 +1667,9 @@ export type InternalControllerEndpoint = {
updatePlaylist: (
args: ReplaceApiClientProps<UpdatePlaylistArgs>,
) => Promise<UpdatePlaylistResponse>;
uploadInternetRadioStationImage?: (
args: ReplaceApiClientProps<UploadInternetRadioStationImageArgs>,
) => Promise<UploadInternetRadioStationImageResponse>;
uploadPlaylistImage?: (
args: ReplaceApiClientProps<UploadPlaylistImageArgs>,
) => Promise<UploadPlaylistImageResponse>;
+1
View File
@@ -3,6 +3,7 @@
export enum ServerFeature {
ALBUM_YES_NO_RATING_FILTER = 'albumYesNoRatingFilter',
BFR = 'bfr',
INTERNET_RADIO_IMAGE_UPLOAD = 'internetRadioImageUpload',
LYRICS_MULTIPLE_STRUCTURED = 'lyricsMultipleStructured',
LYRICS_SINGLE_STRUCTURED = 'lyricsSingleStructured',
MUSIC_FOLDER_MULTISELECT = 'musicFolderMultiselect',