Add internet radio (#1384)

This commit is contained in:
Jeff
2025-12-13 21:26:33 -08:00
committed by GitHub
parent f61d34c340
commit 7ed847fecb
46 changed files with 2229 additions and 118 deletions
@@ -7,6 +7,7 @@ import {
ExplicitStatus,
Folder,
Genre,
InternetRadioStation,
LibraryItem,
Playlist,
RelatedArtist,
@@ -391,11 +392,23 @@ const normalizeFolder = (
};
};
const normalizeInternetRadioStation = (
item: z.infer<typeof ssType._response.internetRadioStation>,
): InternetRadioStation => {
return {
homepageUrl: item.homepageUrl || null,
id: item.id,
name: item.name,
streamUrl: item.streamUrl,
};
};
export const ssNormalize = {
album: normalizeAlbum,
albumArtist: normalizeAlbumArtist,
folder: normalizeFolder,
genre: normalizeGenre,
internetRadioStation: normalizeInternetRadioStation,
playlist: normalizePlaylist,
song: normalizeSong,
};
+46
View File
@@ -654,6 +654,44 @@ const playQueueByIndex = z.object({
}),
});
const internetRadioStation = z.object({
homepageUrl: z.string().optional(),
id: z.string(),
name: z.string(),
streamUrl: z.string(),
});
const deleteInternetRadioStationParameters = z.object({
id: z.string(),
});
const deleteInternetRadioStation = z.null();
const createInternetRadioStationParameters = z.object({
homepageUrl: z.string().optional(),
name: z.string(),
streamUrl: z.string(),
});
const createInternetRadioStation = z.null();
const updateInternetRadioStationParameters = z.object({
homepageUrl: z.string().optional(),
id: z.string(),
name: z.string(),
streamUrl: z.string(),
});
const updateInternetRadioStation = z.null();
const getInternetRadioStations = z.object({
internetRadioStations: z
.object({
internetRadioStation: z.array(internetRadioStation),
})
.optional(),
});
export const ssType = {
_parameters: {
albumInfo: albumInfoParameters,
@@ -661,7 +699,9 @@ export const ssType = {
artistInfo: artistInfoParameters,
authenticate: authenticateParameters,
createFavorite: createFavoriteParameters,
createInternetRadioStation: createInternetRadioStationParameters,
createPlaylist: createPlaylistParameters,
deleteInternetRadioStation: deleteInternetRadioStationParameters,
deletePlaylist: deletePlaylistParameters,
getAlbum: getAlbumParameters,
getAlbumList2: getAlbumList2Parameters,
@@ -686,6 +726,7 @@ export const ssType = {
similarSongs: similarSongsParameters,
structuredLyrics: structuredLyricsParameters,
topSongsList: topSongsListParameters,
updateInternetRadioStation: updateInternetRadioStationParameters,
updatePlaylist: updatePlaylistParameters,
user: userParameters,
},
@@ -701,7 +742,9 @@ export const ssType = {
authenticate,
baseResponse,
createFavorite,
createInternetRadioStation,
createPlaylist,
deleteInternetRadioStation,
directory,
genre,
getAlbum,
@@ -710,12 +753,14 @@ export const ssType = {
getArtists,
getGenres,
getIndexes,
getInternetRadioStations,
getMusicDirectory,
getPlaylist,
getPlaylists,
getSong,
getSongsByGenre,
getStarred,
internetRadioStation,
musicFolderList,
ping,
playlist,
@@ -733,6 +778,7 @@ export const ssType = {
song,
structuredLyrics,
topSongsList,
updateInternetRadioStation,
user,
},
};
+29
View File
@@ -12,7 +12,9 @@ import {
AlbumArtistListSort,
AlbumListSort,
ArtistListSort,
InternetRadioStation,
LibraryItem,
RadioListSort,
ServerListItem,
Song,
SongListSort,
@@ -365,6 +367,7 @@ export const sortAlbumArtistList = (
return results;
};
export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder: SortOrder) => {
let results = albums;
@@ -414,3 +417,29 @@ export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder:
return results;
};
export const sortRadioList = (
stations: InternetRadioStation[],
sortBy: RadioListSort,
sortOrder: SortOrder,
) => {
let results = stations;
const order = sortOrder === SortOrder.ASC ? 'asc' : 'desc';
switch (sortBy) {
case RadioListSort.ID:
results = [...results];
if (order === 'desc') {
results.reverse();
}
break;
case RadioListSort.NAME:
results = orderBy(results, [(v) => v.name.toLowerCase()], [order]);
break;
default:
break;
}
return results;
};
+80 -2
View File
@@ -29,6 +29,7 @@ export enum LibraryItem {
PLAYLIST = 'playlist',
PLAYLIST_SONG = 'playlistSong',
QUEUE_SONG = 'queueSong',
RADIO_STATION = 'radioStation',
SONG = 'song',
}
@@ -861,8 +862,6 @@ export const artistListSortMap: ArtistListSortMap = {
},
};
// Artist Detail
export enum PlaylistListSort {
DURATION = 'duration',
NAME = 'name',
@@ -872,6 +871,11 @@ export enum PlaylistListSort {
UPDATED_AT = 'updatedAt',
}
export enum RadioListSort {
ID = 'id',
NAME = 'name',
}
export type AddToPlaylistArgs = BaseEndpointArgs & {
body: AddToPlaylistBody;
query: AddToPlaylistQuery;
@@ -888,6 +892,18 @@ export type AddToPlaylistQuery = {
// Add to playlist
export type AddToPlaylistResponse = null | undefined;
export type CreateInternetRadioStationArgs = BaseEndpointArgs & {
body: CreateInternetRadioStationBody;
};
export type CreateInternetRadioStationBody = {
homepageUrl?: string;
name: string;
streamUrl: string;
};
export type CreateInternetRadioStationResponse = null | undefined;
export type CreatePlaylistArgs = BaseEndpointArgs & { body: CreatePlaylistBody };
export type CreatePlaylistBody = {
@@ -903,6 +919,16 @@ export type CreatePlaylistBody = {
// Create Playlist
export type CreatePlaylistResponse = undefined | { id: string };
export type DeleteInternetRadioStationArgs = BaseEndpointArgs & {
query: DeleteInternetRadioStationQuery;
};
export type DeleteInternetRadioStationQuery = {
id: string;
};
export type DeleteInternetRadioStationResponse = null | undefined;
export type DeletePlaylistArgs = BaseEndpointArgs & {
query: DeletePlaylistQuery;
};
@@ -922,6 +948,17 @@ export type FavoriteQuery = {
// Favorite
export type FavoriteResponse = null | undefined;
export type GetInternetRadioStationsArgs = BaseEndpointArgs;
export type GetInternetRadioStationsResponse = InternetRadioStation[];
export type InternetRadioStation = {
homepageUrl?: null | string;
id: string;
name: string;
streamUrl: string;
};
export type PlaylistListArgs = BaseEndpointArgs & { query: PlaylistListQuery };
export type PlaylistListCountArgs = BaseEndpointArgs & { query: ListCountQuery<PlaylistListQuery> };
@@ -989,6 +1026,23 @@ export type ShareItemBody = {
// Sharing
export type ShareItemResponse = undefined | { id: string };
export type UpdateInternetRadioStationArgs = BaseEndpointArgs & {
body: UpdateInternetRadioStationBody;
query: UpdateInternetRadioStationQuery;
};
export type UpdateInternetRadioStationBody = {
homepageUrl?: string;
name: string;
streamUrl: string;
};
export type UpdateInternetRadioStationQuery = {
id: string;
};
export type UpdateInternetRadioStationResponse = null | undefined;
export type UpdatePlaylistArgs = BaseEndpointArgs & {
body: UpdatePlaylistBody;
query: UpdatePlaylistQuery;
@@ -1265,8 +1319,14 @@ export type ControllerEndpoint = {
body: { legacy?: boolean; password: string; username: string },
) => Promise<AuthenticationResponse>;
createFavorite: (args: FavoriteArgs) => Promise<FavoriteResponse>;
createInternetRadioStation: (
args: CreateInternetRadioStationArgs,
) => Promise<CreateInternetRadioStationResponse>;
createPlaylist: (args: CreatePlaylistArgs) => Promise<CreatePlaylistResponse>;
deleteFavorite: (args: FavoriteArgs) => Promise<FavoriteResponse>;
deleteInternetRadioStation: (
args: DeleteInternetRadioStationArgs,
) => Promise<DeleteInternetRadioStationResponse>;
deletePlaylist: (args: DeletePlaylistArgs) => Promise<DeletePlaylistResponse>;
getAlbumArtistDetail: (args: AlbumArtistDetailArgs) => Promise<AlbumArtistDetailResponse>;
getAlbumArtistList: (args: AlbumArtistListArgs) => Promise<AlbumArtistListResponse>;
@@ -1280,6 +1340,9 @@ export type ControllerEndpoint = {
getDownloadUrl: (args: DownloadArgs) => string;
getFolder: (args: FolderArgs) => Promise<FolderResponse>;
getGenreList: (args: GenreListArgs) => Promise<GenreListResponse>;
getInternetRadioStations: (
args: GetInternetRadioStationsArgs,
) => Promise<GetInternetRadioStationsResponse>;
getLyrics?: (args: LyricsArgs) => Promise<LyricsResponse>;
getMusicFolderList: (args: MusicFolderListArgs) => Promise<MusicFolderListResponse>;
getPlaylistDetail: (args: PlaylistDetailArgs) => Promise<PlaylistDetailResponse>;
@@ -1309,6 +1372,9 @@ export type ControllerEndpoint = {
search: (args: SearchArgs) => Promise<SearchResponse>;
setRating?: (args: SetRatingArgs) => Promise<RatingResponse>;
shareItem?: (args: ShareItemArgs) => Promise<ShareItemResponse>;
updateInternetRadioStation: (
args: UpdateInternetRadioStationArgs,
) => Promise<UpdateInternetRadioStationResponse>;
updatePlaylist: (args: UpdatePlaylistArgs) => Promise<UpdatePlaylistResponse>;
};
@@ -1351,10 +1417,16 @@ export type InternalControllerEndpoint = {
body: { legacy?: boolean; password: string; username: string },
) => Promise<AuthenticationResponse>;
createFavorite: (args: ReplaceApiClientProps<FavoriteArgs>) => Promise<FavoriteResponse>;
createInternetRadioStation: (
args: ReplaceApiClientProps<CreateInternetRadioStationArgs>,
) => Promise<CreateInternetRadioStationResponse>;
createPlaylist: (
args: ReplaceApiClientProps<CreatePlaylistArgs>,
) => Promise<CreatePlaylistResponse>;
deleteFavorite: (args: ReplaceApiClientProps<FavoriteArgs>) => Promise<FavoriteResponse>;
deleteInternetRadioStation: (
args: ReplaceApiClientProps<DeleteInternetRadioStationArgs>,
) => Promise<DeleteInternetRadioStationResponse>;
deletePlaylist: (
args: ReplaceApiClientProps<DeletePlaylistArgs>,
) => Promise<DeletePlaylistResponse>;
@@ -1377,6 +1449,9 @@ export type InternalControllerEndpoint = {
getDownloadUrl: (args: ReplaceApiClientProps<DownloadArgs>) => string;
getFolder: (args: ReplaceApiClientProps<FolderArgs>) => Promise<FolderResponse>;
getGenreList: (args: ReplaceApiClientProps<GenreListArgs>) => Promise<GenreListResponse>;
getInternetRadioStations: (
args: ReplaceApiClientProps<GetInternetRadioStationsArgs>,
) => Promise<GetInternetRadioStationsResponse>;
getLyrics?: (args: ReplaceApiClientProps<LyricsArgs>) => Promise<LyricsResponse>;
getMusicFolderList: (
args: ReplaceApiClientProps<MusicFolderListArgs>,
@@ -1423,6 +1498,9 @@ export type InternalControllerEndpoint = {
search: (args: ReplaceApiClientProps<SearchArgs>) => Promise<SearchResponse>;
setRating?: (args: ReplaceApiClientProps<SetRatingArgs>) => Promise<RatingResponse>;
shareItem?: (args: ReplaceApiClientProps<ShareItemArgs>) => Promise<ShareItemResponse>;
updateInternetRadioStation: (
args: ReplaceApiClientProps<UpdateInternetRadioStationArgs>,
) => Promise<UpdateInternetRadioStationResponse>;
updatePlaylist: (
args: ReplaceApiClientProps<UpdatePlaylistArgs>,
) => Promise<UpdatePlaylistResponse>;
+1
View File
@@ -28,6 +28,7 @@ export enum ItemListKey {
PLAYLIST = LibraryItem.PLAYLIST,
PLAYLIST_SONG = LibraryItem.PLAYLIST_SONG,
QUEUE_SONG = LibraryItem.QUEUE_SONG,
RADIO = 'radio',
SIDE_QUEUE = 'sideQueue',
SONG = LibraryItem.SONG,
}