mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
more
This commit is contained in:
@@ -60,7 +60,7 @@ export type Encoding = {
|
|||||||
mimeType: string
|
mimeType: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Track = {
|
export type TrackSummary = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
encoding: Encoding,
|
encoding: Encoding,
|
||||||
@@ -68,9 +68,12 @@ export type Track = {
|
|||||||
number: number | undefined;
|
number: number | undefined;
|
||||||
genre: Genre | undefined;
|
genre: Genre | undefined;
|
||||||
coverArt: BUrn | undefined;
|
coverArt: BUrn | undefined;
|
||||||
album: AlbumSummary;
|
|
||||||
artist: ArtistSummary;
|
artist: ArtistSummary;
|
||||||
rating: Rating;
|
rating: Rating;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Track = TrackSummary & {
|
||||||
|
album: AlbumSummary;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RadioStation = {
|
export type RadioStation = {
|
||||||
@@ -129,6 +132,18 @@ export const albumToAlbumSummary = (it: Album): AlbumSummary => ({
|
|||||||
coverArt: it.coverArt
|
coverArt: it.coverArt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const trackToTrackSummary = (it: Track): TrackSummary => ({
|
||||||
|
id: it.id,
|
||||||
|
name: it.name,
|
||||||
|
encoding: it.encoding,
|
||||||
|
duration: it.duration,
|
||||||
|
number: it.number,
|
||||||
|
genre: it.genre,
|
||||||
|
coverArt: it.coverArt,
|
||||||
|
artist: it.artist,
|
||||||
|
rating: it.rating
|
||||||
|
});
|
||||||
|
|
||||||
export const playlistToPlaylistSummary = (it: Playlist): PlaylistSummary => ({
|
export const playlistToPlaylistSummary = (it: Playlist): PlaylistSummary => ({
|
||||||
id: it.id,
|
id: it.id,
|
||||||
name: it.name,
|
name: it.name,
|
||||||
@@ -199,8 +214,8 @@ export interface MusicLibrary {
|
|||||||
deletePlaylist(id: string): Promise<boolean>
|
deletePlaylist(id: string): Promise<boolean>
|
||||||
addToPlaylist(playlistId: string, trackId: string): Promise<boolean>
|
addToPlaylist(playlistId: string, trackId: string): Promise<boolean>
|
||||||
removeFromPlaylist(playlistId: string, indicies: number[]): Promise<boolean>
|
removeFromPlaylist(playlistId: string, indicies: number[]): Promise<boolean>
|
||||||
similarSongs(id: string): Promise<Track[]>;
|
similarSongs(id: string): Promise<TrackSummary[]>;
|
||||||
topSongs(artistId: string): Promise<Track[]>;
|
topSongs(artistId: string): Promise<TrackSummary[]>;
|
||||||
radioStation(id: string): Promise<RadioStation>
|
radioStation(id: string): Promise<RadioStation>
|
||||||
radioStations(): Promise<RadioStation[]>
|
radioStations(): Promise<RadioStation[]>
|
||||||
}
|
}
|
||||||
|
|||||||
141
src/subsonic.ts
141
src/subsonic.ts
@@ -12,9 +12,9 @@ import {
|
|||||||
Track,
|
Track,
|
||||||
CoverArt,
|
CoverArt,
|
||||||
AlbumQueryType,
|
AlbumQueryType,
|
||||||
PlaylistSummary,
|
|
||||||
Encoding,
|
Encoding,
|
||||||
albumToAlbumSummary,
|
albumToAlbumSummary,
|
||||||
|
TrackSummary,
|
||||||
} from "./music_library";
|
} from "./music_library";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
@@ -170,24 +170,34 @@ export type GetAlbumResponse = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type playlist = {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
coverArt: string | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetPlaylistResponse = {
|
export type GetPlaylistResponse = {
|
||||||
// todo: isnt the type here a composite? playlistSummary && { entry: song[]; }
|
// todo: isnt the type here a composite? playlistSummary && { entry: song[]; }
|
||||||
playlist: {
|
playlist: {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
coverArt: string | undefined;
|
|
||||||
entry: song[];
|
entry: song[];
|
||||||
|
|
||||||
|
// todo: this is an ND specific field?
|
||||||
|
coverArt: string | undefined;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetPlaylistsResponse = {
|
export type GetPlaylistsResponse = {
|
||||||
playlists: { playlist: playlist[] };
|
playlists: {
|
||||||
|
playlist: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
//owner: string,
|
||||||
|
//public: boolean,
|
||||||
|
//created: string,
|
||||||
|
//changed: string,
|
||||||
|
//songCount: int,
|
||||||
|
//duration: int,
|
||||||
|
|
||||||
|
// todo: this is an ND specific field.
|
||||||
|
coverArt: string | undefined;
|
||||||
|
}[]
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GetSimilarSongsResponse = {
|
export type GetSimilarSongsResponse = {
|
||||||
@@ -280,11 +290,10 @@ export const artistImageURN = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const asTrack = (
|
export const asTrackSummary = (
|
||||||
album: AlbumSummary,
|
|
||||||
song: song,
|
song: song,
|
||||||
customPlayers: CustomPlayers
|
customPlayers: CustomPlayers
|
||||||
): Track => ({
|
): TrackSummary => ({
|
||||||
id: song.id,
|
id: song.id,
|
||||||
name: song.title,
|
name: song.title,
|
||||||
encoding: pipe(
|
encoding: pipe(
|
||||||
@@ -300,7 +309,6 @@ export const asTrack = (
|
|||||||
number: song.track || 0,
|
number: song.track || 0,
|
||||||
genre: maybeAsGenre(song.genre),
|
genre: maybeAsGenre(song.genre),
|
||||||
coverArt: coverArtURN(song.coverArt),
|
coverArt: coverArtURN(song.coverArt),
|
||||||
album: album,
|
|
||||||
artist: {
|
artist: {
|
||||||
id: song.artistId,
|
id: song.artistId,
|
||||||
name: song.artist ? song.artist : "?",
|
name: song.artist ? song.artist : "?",
|
||||||
@@ -317,6 +325,15 @@ export const asTrack = (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const asTrack = (
|
||||||
|
album: AlbumSummary,
|
||||||
|
song: song,
|
||||||
|
customPlayers: CustomPlayers
|
||||||
|
): Track => ({
|
||||||
|
...asTrackSummary(song, customPlayers),
|
||||||
|
album: album,
|
||||||
|
});
|
||||||
|
|
||||||
export const asAlbumSummary = (album: album): AlbumSummary => ({
|
export const asAlbumSummary = (album: album): AlbumSummary => ({
|
||||||
id: album.id,
|
id: album.id,
|
||||||
name: album.name,
|
name: album.name,
|
||||||
@@ -327,13 +344,6 @@ export const asAlbumSummary = (album: album): AlbumSummary => ({
|
|||||||
coverArt: coverArtURN(album.coverArt),
|
coverArt: coverArtURN(album.coverArt),
|
||||||
});
|
});
|
||||||
|
|
||||||
// coverArtURN
|
|
||||||
export const asPlayListSummary = (playlist: playlist): PlaylistSummary => ({
|
|
||||||
id: playlist.id,
|
|
||||||
name: playlist.name,
|
|
||||||
coverArt: coverArtURN(playlist.coverArt),
|
|
||||||
});
|
|
||||||
|
|
||||||
export const asGenre = (genreName: string) => ({
|
export const asGenre = (genreName: string) => ({
|
||||||
id: b64Encode(genreName),
|
id: b64Encode(genreName),
|
||||||
name: genreName,
|
name: genreName,
|
||||||
@@ -611,7 +621,6 @@ export class Subsonic {
|
|||||||
tracks: y
|
tracks: y
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
getArtist = (
|
getArtist = (
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
@@ -764,5 +773,93 @@ export class Subsonic {
|
|||||||
"accept-ranges": stream.headers["accept-ranges"],
|
"accept-ranges": stream.headers["accept-ranges"],
|
||||||
},
|
},
|
||||||
stream: stream.data,
|
stream: stream.data,
|
||||||
}))
|
}));
|
||||||
|
|
||||||
|
playlists = (credentials: Credentials) =>
|
||||||
|
this.getJSON<GetPlaylistsResponse>(credentials, "/rest/getPlaylists")
|
||||||
|
.then(({ playlists }) => (playlists.playlist || []).map( it => ({
|
||||||
|
id: it.id,
|
||||||
|
name: it.name,
|
||||||
|
coverArt: coverArtURN(it.coverArt),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
playlist = (credentials: Credentials, id: string) =>
|
||||||
|
this.getJSON<GetPlaylistResponse>(credentials, "/rest/getPlaylist", {
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
.then(({ playlist }) => {
|
||||||
|
let trackNumber = 1;
|
||||||
|
return {
|
||||||
|
id: playlist.id,
|
||||||
|
name: playlist.name,
|
||||||
|
coverArt: coverArtURN(playlist.coverArt),
|
||||||
|
entries: (playlist.entry || []).map((entry) => ({
|
||||||
|
...asTrack(
|
||||||
|
{
|
||||||
|
id: entry.albumId!,
|
||||||
|
name: entry.album!,
|
||||||
|
year: entry.year,
|
||||||
|
genre: maybeAsGenre(entry.genre),
|
||||||
|
artistName: entry.artist,
|
||||||
|
artistId: entry.artistId,
|
||||||
|
coverArt: coverArtURN(entry.coverArt),
|
||||||
|
},
|
||||||
|
entry,
|
||||||
|
this.customPlayers
|
||||||
|
),
|
||||||
|
number: trackNumber++,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
createPlayList = (credentials: Credentials, name: string) =>
|
||||||
|
this.getJSON<GetPlaylistResponse>(credentials, "/rest/createPlaylist", {
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
.then(({ playlist }) => ({
|
||||||
|
id: playlist.id,
|
||||||
|
name: playlist.name,
|
||||||
|
coverArt: coverArtURN(playlist.coverArt),
|
||||||
|
}));
|
||||||
|
|
||||||
|
deletePlayList = (credentials: Credentials, id: string) =>
|
||||||
|
this.getJSON<SubsonicResponse>(credentials, "/rest/deletePlaylist", {
|
||||||
|
id,
|
||||||
|
})
|
||||||
|
.then(it => it.status == "ok");
|
||||||
|
|
||||||
|
updatePlaylist = (
|
||||||
|
credentials: Credentials,
|
||||||
|
playlistId: string,
|
||||||
|
changes : Partial<{ songIdToAdd: string | undefined, songIndexToRemove: number[] | undefined }> = {}
|
||||||
|
) =>
|
||||||
|
this.getJSON<SubsonicResponse>(credentials, "/rest/updatePlaylist", {
|
||||||
|
playlistId,
|
||||||
|
...changes
|
||||||
|
})
|
||||||
|
.then(it => it.status == "ok");
|
||||||
|
|
||||||
|
getSimilarSongs2 = (credentials: Credentials, id: string) =>
|
||||||
|
this.getJSON<GetSimilarSongsResponse>(
|
||||||
|
credentials,
|
||||||
|
"/rest/getSimilarSongs2",
|
||||||
|
//todo: remove this hard coded 50?
|
||||||
|
{ id, count: 50 }
|
||||||
|
)
|
||||||
|
.then((it) =>
|
||||||
|
(it.similarSongs2.song || []).map(it => asTrackSummary(it, this.customPlayers))
|
||||||
|
);
|
||||||
|
|
||||||
|
getTopSongs = (credentials: Credentials, artist: string) =>
|
||||||
|
this.getJSON<GetTopSongsResponse>(
|
||||||
|
credentials,
|
||||||
|
"/rest/getTopSongs",
|
||||||
|
//todo: remove this hard coded 50?
|
||||||
|
{ artist, count: 50 }
|
||||||
|
)
|
||||||
|
.then((it) =>
|
||||||
|
(it.topSongs.song || []).map(it => asTrackSummary(it, this.customPlayers))
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,24 +15,15 @@ import {
|
|||||||
Artist,
|
Artist,
|
||||||
AuthFailure,
|
AuthFailure,
|
||||||
AuthSuccess,
|
AuthSuccess,
|
||||||
albumToAlbumSummary,
|
|
||||||
} from "./music_library";
|
} from "./music_library";
|
||||||
import {
|
import {
|
||||||
Subsonic,
|
Subsonic,
|
||||||
CustomPlayers,
|
CustomPlayers,
|
||||||
asTrack,
|
|
||||||
PingResponse,
|
PingResponse,
|
||||||
NO_CUSTOM_PLAYERS,
|
NO_CUSTOM_PLAYERS,
|
||||||
asToken,
|
asToken,
|
||||||
parseToken,
|
parseToken,
|
||||||
artistImageURN,
|
artistImageURN,
|
||||||
GetPlaylistsResponse,
|
|
||||||
GetPlaylistResponse,
|
|
||||||
asPlayListSummary,
|
|
||||||
coverArtURN,
|
|
||||||
maybeAsGenre,
|
|
||||||
GetSimilarSongsResponse,
|
|
||||||
GetTopSongsResponse,
|
|
||||||
GetInternetRadioStationsResponse,
|
GetInternetRadioStationsResponse,
|
||||||
asYear,
|
asYear,
|
||||||
isValidImage
|
isValidImage
|
||||||
@@ -292,112 +283,31 @@ export class SubsonicMusicLibrary implements MusicLibrary {
|
|||||||
);
|
);
|
||||||
|
|
||||||
playlists = async () =>
|
playlists = async () =>
|
||||||
this.subsonic
|
this.subsonic.playlists(this.credentials);
|
||||||
.getJSON<GetPlaylistsResponse>(this.credentials, "/rest/getPlaylists")
|
|
||||||
.then(({ playlists }) =>
|
|
||||||
(playlists.playlist || []).map(asPlayListSummary)
|
|
||||||
);
|
|
||||||
|
|
||||||
playlist = async (id: string) =>
|
playlist = async (id: string) =>
|
||||||
this.subsonic
|
this.subsonic.playlist(this.credentials, id);
|
||||||
.getJSON<GetPlaylistResponse>(this.credentials, "/rest/getPlaylist", {
|
|
||||||
id,
|
|
||||||
})
|
|
||||||
.then(({ playlist }) => {
|
|
||||||
let trackNumber = 1;
|
|
||||||
return {
|
|
||||||
id: playlist.id,
|
|
||||||
name: playlist.name,
|
|
||||||
coverArt: coverArtURN(playlist.coverArt),
|
|
||||||
entries: (playlist.entry || []).map((entry) => ({
|
|
||||||
...asTrack(
|
|
||||||
{
|
|
||||||
id: entry.albumId!,
|
|
||||||
name: entry.album!,
|
|
||||||
year: entry.year,
|
|
||||||
genre: maybeAsGenre(entry.genre),
|
|
||||||
artistName: entry.artist,
|
|
||||||
artistId: entry.artistId,
|
|
||||||
coverArt: coverArtURN(entry.coverArt),
|
|
||||||
},
|
|
||||||
entry,
|
|
||||||
this.customPlayers
|
|
||||||
),
|
|
||||||
number: trackNumber++,
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
createPlaylist = async (name: string) =>
|
createPlaylist = async (name: string) =>
|
||||||
this.subsonic
|
this.subsonic.createPlayList(this.credentials, name);
|
||||||
.getJSON<GetPlaylistResponse>(this.credentials, "/rest/createPlaylist", {
|
|
||||||
name,
|
|
||||||
})
|
|
||||||
.then(({ playlist }) => ({
|
|
||||||
id: playlist.id,
|
|
||||||
name: playlist.name,
|
|
||||||
coverArt: coverArtURN(playlist.coverArt),
|
|
||||||
}));
|
|
||||||
|
|
||||||
deletePlaylist = async (id: string) =>
|
deletePlaylist = async (id: string) =>
|
||||||
this.subsonic
|
this.subsonic.deletePlayList(this.credentials, id);
|
||||||
.getJSON<GetPlaylistResponse>(this.credentials, "/rest/deletePlaylist", {
|
|
||||||
id,
|
|
||||||
})
|
|
||||||
.then((_) => true);
|
|
||||||
|
|
||||||
addToPlaylist = async (playlistId: string, trackId: string) =>
|
addToPlaylist = async (playlistId: string, trackId: string) =>
|
||||||
this.subsonic
|
this.subsonic.updatePlaylist(this.credentials, playlistId, { songIdToAdd: trackId });
|
||||||
.getJSON<GetPlaylistResponse>(this.credentials, "/rest/updatePlaylist", {
|
|
||||||
playlistId,
|
|
||||||
songIdToAdd: trackId,
|
|
||||||
})
|
|
||||||
.then((_) => true);
|
|
||||||
|
|
||||||
removeFromPlaylist = async (playlistId: string, indicies: number[]) =>
|
removeFromPlaylist = async (playlistId: string, indicies: number[]) =>
|
||||||
this.subsonic
|
this.subsonic.updatePlaylist(this.credentials, playlistId, { songIndexToRemove: indicies });
|
||||||
.getJSON<GetPlaylistResponse>(this.credentials, "/rest/updatePlaylist", {
|
|
||||||
playlistId,
|
|
||||||
songIndexToRemove: indicies,
|
|
||||||
})
|
|
||||||
.then((_) => true);
|
|
||||||
|
|
||||||
similarSongs = async (id: string) =>
|
similarSongs = async (id: string) =>
|
||||||
this.subsonic
|
this.subsonic.getSimilarSongs2(this.credentials, id)
|
||||||
.getJSON<GetSimilarSongsResponse>(
|
|
||||||
this.credentials,
|
|
||||||
"/rest/getSimilarSongs2",
|
|
||||||
{ id, count: 50 }
|
|
||||||
)
|
|
||||||
.then((it) => it.similarSongs2.song || [])
|
|
||||||
.then((songs) =>
|
|
||||||
Promise.all(
|
|
||||||
songs.map((song) =>
|
|
||||||
this.subsonic
|
|
||||||
.getAlbum(this.credentials, song.albumId!)
|
|
||||||
.then((album) => asTrack(albumToAlbumSummary(album), song, this.customPlayers))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
topSongs = async (artistId: string) =>
|
topSongs = async (artistId: string) =>
|
||||||
this.subsonic.getArtist(this.credentials, artistId).then(({ name }) =>
|
this.subsonic.getArtist(this.credentials, artistId)
|
||||||
this.subsonic
|
.then(({ name }) =>
|
||||||
.getJSON<GetTopSongsResponse>(this.credentials, "/rest/getTopSongs", {
|
this.subsonic.getTopSongs(this.credentials, name)
|
||||||
artist: name,
|
);
|
||||||
count: 50,
|
|
||||||
})
|
|
||||||
.then((it) => it.topSongs.song || [])
|
|
||||||
.then((songs) =>
|
|
||||||
Promise.all(
|
|
||||||
songs.map((song) =>
|
|
||||||
this.subsonic
|
|
||||||
.getAlbum(this.credentials, song.albumId!)
|
|
||||||
.then((album) => asTrack(albumToAlbumSummary(album), song, this.customPlayers))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
radioStations = async () =>
|
radioStations = async () =>
|
||||||
this.subsonic
|
this.subsonic
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import {
|
|||||||
SimilarArtist,
|
SimilarArtist,
|
||||||
AlbumSummary,
|
AlbumSummary,
|
||||||
RadioStation,
|
RadioStation,
|
||||||
ArtistSummary
|
ArtistSummary,
|
||||||
|
TrackSummary
|
||||||
} from "../src/music_library";
|
} from "../src/music_library";
|
||||||
|
|
||||||
import { b64Encode } from "../src/b64";
|
import { b64Encode } from "../src/b64";
|
||||||
@@ -178,12 +179,11 @@ export const SAMPLE_GENRES = [
|
|||||||
];
|
];
|
||||||
export const randomGenre = () => SAMPLE_GENRES[randomInt(SAMPLE_GENRES.length)];
|
export const randomGenre = () => SAMPLE_GENRES[randomInt(SAMPLE_GENRES.length)];
|
||||||
|
|
||||||
export function aTrack(fields: Partial<Track> = {}): Track {
|
export function aTrackSummary(fields: Partial<TrackSummary> = {}): TrackSummary {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
const artist = fields.artist || anArtistSummary();
|
const artist = fields.artist || anArtistSummary();
|
||||||
const genre = fields.genre || randomGenre();
|
const genre = fields.genre || randomGenre();
|
||||||
const rating = { love: false, stars: Math.floor(Math.random() * 5) };
|
const rating = { love: false, stars: Math.floor(Math.random() * 5) };
|
||||||
const album = fields.album || anAlbumSummary({ artistId: artist.id, artistName: artist.name, genre })
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
name: `Track ${id}`,
|
name: `Track ${id}`,
|
||||||
@@ -195,12 +195,21 @@ export function aTrack(fields: Partial<Track> = {}): Track {
|
|||||||
number: randomInt(100),
|
number: randomInt(100),
|
||||||
genre,
|
genre,
|
||||||
artist,
|
artist,
|
||||||
album,
|
|
||||||
coverArt: { system: "subsonic", resource: `art:${uuid()}`},
|
coverArt: { system: "subsonic", resource: `art:${uuid()}`},
|
||||||
rating,
|
rating,
|
||||||
...fields,
|
...fields,
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export function aTrack(fields: Partial<Track> = {}): Track {
|
||||||
|
const summary = aTrackSummary(fields);
|
||||||
|
const album = fields.album || anAlbumSummary({ artistId: summary.artist.id, artistName: summary.artist.name, genre: summary.genre })
|
||||||
|
return {
|
||||||
|
...summary,
|
||||||
|
album,
|
||||||
|
...fields
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export function anAlbumSummary(fields: Partial<AlbumSummary> = {}): AlbumSummary {
|
export function anAlbumSummary(fields: Partial<AlbumSummary> = {}): AlbumSummary {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
@@ -213,7 +222,7 @@ export function anAlbumSummary(fields: Partial<AlbumSummary> = {}): AlbumSummary
|
|||||||
artistId: `Artist ${uuid()}`,
|
artistId: `Artist ${uuid()}`,
|
||||||
artistName: `Artist ${randomstring.generate()}`,
|
artistName: `Artist ${randomstring.generate()}`,
|
||||||
...fields
|
...fields
|
||||||
}
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function anAlbum(fields: Partial<Album> = {}): Album {
|
export function anAlbum(fields: Partial<Album> = {}): Album {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
CustomPlayers,
|
CustomPlayers,
|
||||||
PingResponse,
|
PingResponse,
|
||||||
images,
|
images,
|
||||||
|
|
||||||
} from "../src/subsonic";
|
} from "../src/subsonic";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -42,6 +41,7 @@ import {
|
|||||||
AuthFailure,
|
AuthFailure,
|
||||||
RadioStation,
|
RadioStation,
|
||||||
AlbumSummary,
|
AlbumSummary,
|
||||||
|
trackToTrackSummary,
|
||||||
} from "../src/music_library";
|
} from "../src/music_library";
|
||||||
import {
|
import {
|
||||||
aGenre,
|
aGenre,
|
||||||
@@ -4216,21 +4216,18 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
const track1 = aTrack({
|
const track1 = aTrack({
|
||||||
id: "track1",
|
id: "track1",
|
||||||
artist: artistToArtistSummary(artist1),
|
artist: artistToArtistSummary(artist1),
|
||||||
album: albumToAlbumSummary(album1),
|
album: album1,
|
||||||
genre: pop,
|
genre: pop,
|
||||||
});
|
});
|
||||||
|
|
||||||
mockGET
|
mockGET
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getSimilarSongsJson([track1])))
|
Promise.resolve(ok(getSimilarSongsJson([track1])))
|
||||||
)
|
|
||||||
.mockImplementationOnce(() =>
|
|
||||||
Promise.resolve(ok(getAlbumJson(album1)))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.similarSongs(id);
|
const result = await subsonic.similarSongs(id);
|
||||||
|
|
||||||
expect(result).toEqual([track1]);
|
expect(result).toEqual([trackToTrackSummary(track1)]);
|
||||||
|
|
||||||
expect(mockGET).toHaveBeenCalledWith(
|
expect(mockGET).toHaveBeenCalledWith(
|
||||||
url.append({ pathname: "/rest/getSimilarSongs2" }).href(),
|
url.append({ pathname: "/rest/getSimilarSongs2" }).href(),
|
||||||
@@ -4288,20 +4285,15 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
mockGET
|
mockGET
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getSimilarSongsJson([track1, track2, track3])))
|
Promise.resolve(ok(getSimilarSongsJson([track1, track2, track3])))
|
||||||
)
|
|
||||||
.mockImplementationOnce(() =>
|
|
||||||
Promise.resolve(ok(getAlbumJson(album1)))
|
|
||||||
)
|
|
||||||
.mockImplementationOnce(() =>
|
|
||||||
Promise.resolve(ok(getAlbumJson(album2)))
|
|
||||||
)
|
|
||||||
.mockImplementationOnce(() =>
|
|
||||||
Promise.resolve(ok(getAlbumJson(album1)))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.similarSongs(id);
|
const result = await subsonic.similarSongs(id);
|
||||||
|
|
||||||
expect(result).toEqual([track1, track2, track3]);
|
expect(result).toEqual([
|
||||||
|
trackToTrackSummary(track1),
|
||||||
|
trackToTrackSummary(track2),
|
||||||
|
trackToTrackSummary(track3),
|
||||||
|
]);
|
||||||
|
|
||||||
expect(mockGET).toHaveBeenCalledWith(
|
expect(mockGET).toHaveBeenCalledWith(
|
||||||
url.append({ pathname: "/rest/getSimilarSongs2" }).href(),
|
url.append({ pathname: "/rest/getSimilarSongs2" }).href(),
|
||||||
@@ -4390,14 +4382,13 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getTopSongsJson([track1])))
|
Promise.resolve(ok(getTopSongsJson([track1])))
|
||||||
)
|
|
||||||
.mockImplementationOnce(() =>
|
|
||||||
Promise.resolve(ok(getAlbumJson(album1)))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.topSongs(artistId);
|
const result = await subsonic.topSongs(artistId);
|
||||||
|
|
||||||
expect(result).toEqual([track1]);
|
expect(result).toEqual([
|
||||||
|
trackToTrackSummary(track1)
|
||||||
|
]);
|
||||||
|
|
||||||
expect(mockGET).toHaveBeenCalledWith(
|
expect(mockGET).toHaveBeenCalledWith(
|
||||||
url.append({ pathname: "/rest/getTopSongs" }).href(),
|
url.append({ pathname: "/rest/getTopSongs" }).href(),
|
||||||
@@ -4452,20 +4443,15 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getTopSongsJson([track1, track2, track3])))
|
Promise.resolve(ok(getTopSongsJson([track1, track2, track3])))
|
||||||
)
|
|
||||||
.mockImplementationOnce(() =>
|
|
||||||
Promise.resolve(ok(getAlbumJson(album1)))
|
|
||||||
)
|
|
||||||
.mockImplementationOnce(() =>
|
|
||||||
Promise.resolve(ok(getAlbumJson(album2)))
|
|
||||||
)
|
|
||||||
.mockImplementationOnce(() =>
|
|
||||||
Promise.resolve(ok(getAlbumJson(album1)))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.topSongs(artistId);
|
const result = await subsonic.topSongs(artistId);
|
||||||
|
|
||||||
expect(result).toEqual([track1, track2, track3]);
|
expect(result).toEqual([
|
||||||
|
trackToTrackSummary(track1),
|
||||||
|
trackToTrackSummary(track2),
|
||||||
|
trackToTrackSummary(track3),
|
||||||
|
]);
|
||||||
|
|
||||||
expect(mockGET).toHaveBeenCalledWith(
|
expect(mockGET).toHaveBeenCalledWith(
|
||||||
url.append({ pathname: "/rest/getTopSongs" }).href(),
|
url.append({ pathname: "/rest/getTopSongs" }).href(),
|
||||||
|
|||||||
Reference in New Issue
Block a user