mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
tests passing
This commit is contained in:
@@ -23,7 +23,8 @@ export type ArtistSummary = {
|
|||||||
|
|
||||||
export type SimilarArtist = ArtistSummary & { inLibrary: boolean };
|
export type SimilarArtist = ArtistSummary & { inLibrary: boolean };
|
||||||
|
|
||||||
export type Artist = ArtistSummary & {
|
// todo: maybe is should be artist.summary rather than an artist also being a summary?
|
||||||
|
export type Artist = Pick<ArtistSummary, "id" | "name" | "image"> & {
|
||||||
albums: AlbumSummary[];
|
albums: AlbumSummary[];
|
||||||
similarArtists: SimilarArtist[]
|
similarArtists: SimilarArtist[]
|
||||||
};
|
};
|
||||||
@@ -34,12 +35,11 @@ export type AlbumSummary = {
|
|||||||
year: string | undefined;
|
year: string | undefined;
|
||||||
genre: Genre | undefined;
|
genre: Genre | undefined;
|
||||||
coverArt: BUrn | undefined;
|
coverArt: BUrn | undefined;
|
||||||
|
|
||||||
artistName: string | undefined;
|
artistName: string | undefined;
|
||||||
artistId: string | undefined;
|
artistId: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Album = AlbumSummary & {};
|
export type Album = Pick<AlbumSummary, "id" | "name" | "year" | "genre" | "coverArt" | "artistName" | "artistId"> & { tracks: Track[] };
|
||||||
|
|
||||||
export type Genre = {
|
export type Genre = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -175,7 +175,7 @@ export interface MusicLibrary {
|
|||||||
artists(q: ArtistQuery): Promise<Result<ArtistSummary>>;
|
artists(q: ArtistQuery): Promise<Result<ArtistSummary>>;
|
||||||
artist(id: string): Promise<Artist>;
|
artist(id: string): Promise<Artist>;
|
||||||
albums(q: AlbumQuery): Promise<Result<AlbumSummary>>;
|
albums(q: AlbumQuery): Promise<Result<AlbumSummary>>;
|
||||||
album(id: string): Promise<Album>;
|
album(id: string): Promise<AlbumSummary>;
|
||||||
tracks(albumId: string): Promise<Track[]>;
|
tracks(albumId: string): Promise<Track[]>;
|
||||||
track(trackId: string): Promise<Track>;
|
track(trackId: string): Promise<Track>;
|
||||||
genres(): Promise<Genre[]>;
|
genres(): Promise<Genre[]>;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import logger from "./logger";
|
|||||||
|
|
||||||
import { LinkCodes } from "./link_codes";
|
import { LinkCodes } from "./link_codes";
|
||||||
import {
|
import {
|
||||||
Album,
|
|
||||||
AlbumQuery,
|
AlbumQuery,
|
||||||
AlbumSummary,
|
AlbumSummary,
|
||||||
ArtistSummary,
|
ArtistSummary,
|
||||||
@@ -612,7 +611,7 @@ function bindSmapiSoapServiceToExpress(
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case "artist":
|
case "artist":
|
||||||
return musicLibrary.artist(typeId).then((artist) => {
|
return musicLibrary.artist(typeId).then((artist) => {
|
||||||
const [page, total] = slice2<Album>(paging)(
|
const [page, total] = slice2<AlbumSummary>(paging)(
|
||||||
artist.albums
|
artist.albums
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
AlbumQueryType,
|
AlbumQueryType,
|
||||||
PlaylistSummary,
|
PlaylistSummary,
|
||||||
Encoding,
|
Encoding,
|
||||||
|
albumToAlbumSummary,
|
||||||
} from "./music_library";
|
} from "./music_library";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
@@ -24,7 +25,7 @@ import axios, { AxiosRequestConfig } from "axios";
|
|||||||
import randomstring from "randomstring";
|
import randomstring from "randomstring";
|
||||||
import { b64Encode, b64Decode } from "./b64";
|
import { b64Encode, b64Decode } from "./b64";
|
||||||
import { BUrn } from "./burn";
|
import { BUrn } from "./burn";
|
||||||
import { artist } from "./smapi";
|
import { album, artist } from "./smapi";
|
||||||
import { URLBuilder } from "./url_builder";
|
import { URLBuilder } from "./url_builder";
|
||||||
|
|
||||||
export const BROWSER_HEADERS = {
|
export const BROWSER_HEADERS = {
|
||||||
@@ -159,6 +160,7 @@ export type song = {
|
|||||||
transcodedContentType: string | undefined;
|
transcodedContentType: string | undefined;
|
||||||
type: string | undefined;
|
type: string | undefined;
|
||||||
userRating: number | undefined;
|
userRating: number | undefined;
|
||||||
|
// todo: this field shouldnt be on song?
|
||||||
starred: string | undefined;
|
starred: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -279,7 +281,7 @@ export const artistImageURN = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const asTrack = (
|
export const asTrack = (
|
||||||
album: Album,
|
album: AlbumSummary,
|
||||||
song: song,
|
song: song,
|
||||||
customPlayers: CustomPlayers
|
customPlayers: CustomPlayers
|
||||||
): Track => ({
|
): Track => ({
|
||||||
@@ -298,7 +300,7 @@ 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: album,
|
||||||
artist: {
|
artist: {
|
||||||
id: song.artistId,
|
id: song.artistId,
|
||||||
name: song.artist ? song.artist : "?",
|
name: song.artist ? song.artist : "?",
|
||||||
@@ -315,7 +317,7 @@ export const asTrack = (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const asAlbum = (album: album): Album => ({
|
export const asAlbumSummary = (album: album): AlbumSummary => ({
|
||||||
id: album.id,
|
id: album.id,
|
||||||
name: album.name,
|
name: album.name,
|
||||||
year: album.year,
|
year: album.year,
|
||||||
@@ -412,7 +414,7 @@ export const asURLSearchParams = (q: any) => {
|
|||||||
export type ImageFetcher = (url: string) => Promise<CoverArt | undefined>;
|
export type ImageFetcher = (url: string) => Promise<CoverArt | undefined>;
|
||||||
|
|
||||||
export const cachingImageFetcher =
|
export const cachingImageFetcher =
|
||||||
(cacheDir: string, delegate: ImageFetcher) =>
|
(cacheDir: string, delegate: ImageFetcher, makeSharp = sharp) =>
|
||||||
async (url: string): Promise<CoverArt | undefined> => {
|
async (url: string): Promise<CoverArt | undefined> => {
|
||||||
const filename = path.join(cacheDir, `${Md5.hashStr(url)}.png`);
|
const filename = path.join(cacheDir, `${Md5.hashStr(url)}.png`);
|
||||||
return fse
|
return fse
|
||||||
@@ -421,7 +423,7 @@ export const cachingImageFetcher =
|
|||||||
.catch(() =>
|
.catch(() =>
|
||||||
delegate(url).then((image) => {
|
delegate(url).then((image) => {
|
||||||
if (image) {
|
if (image) {
|
||||||
return sharp(image.data)
|
return makeSharp(image.data)
|
||||||
.png()
|
.png()
|
||||||
.toBuffer()
|
.toBuffer()
|
||||||
.then((png) => {
|
.then((png) => {
|
||||||
@@ -584,19 +586,30 @@ export class Subsonic {
|
|||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
getAlbum = (credentials: Credentials, id: string): Promise<Album> =>
|
getAlbum = (credentials: Credentials, id: string): Promise<Album> =>
|
||||||
this.getJSON<GetAlbumResponse>(credentials, "/rest/getAlbum", { id })
|
this.getJSON<GetAlbumResponse>(credentials, "/rest/getAlbum", { id })
|
||||||
.then((it) => it.album)
|
.then((it) => it.album)
|
||||||
.then((album) => ({
|
.then((album) => {
|
||||||
id: album.id,
|
const x: AlbumSummary = {
|
||||||
name: album.name,
|
id: album.id,
|
||||||
year: album.year,
|
name: album.name,
|
||||||
genre: maybeAsGenre(album.genre),
|
year: album.year,
|
||||||
artistId: album.artistId,
|
genre: maybeAsGenre(album.genre),
|
||||||
artistName: album.artist,
|
artistId: album.artistId,
|
||||||
coverArt: coverArtURN(album.coverArt),
|
artistName: album.artist,
|
||||||
}));
|
coverArt: coverArtURN(album.coverArt)
|
||||||
|
}
|
||||||
|
return { summary: x, songs: album.song }
|
||||||
|
}).then(({ summary, songs }) => {
|
||||||
|
const x: AlbumSummary = summary
|
||||||
|
const y: Track[] = songs.map((it) => asTrack(summary, it, this.customPlayers))
|
||||||
|
return {
|
||||||
|
...x,
|
||||||
|
tracks: y
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
getArtist = (
|
getArtist = (
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
id: string
|
id: string
|
||||||
@@ -647,7 +660,7 @@ export class Subsonic {
|
|||||||
.then((it) => it.song)
|
.then((it) => it.song)
|
||||||
.then((song) =>
|
.then((song) =>
|
||||||
this.getAlbum(credentials, song.albumId!).then((album) =>
|
this.getAlbum(credentials, song.albumId!).then((album) =>
|
||||||
asTrack(album, song, this.customPlayers)
|
asTrack(albumToAlbumSummary(album), song, this.customPlayers)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
Credentials,
|
Credentials,
|
||||||
MusicService,
|
MusicService,
|
||||||
ArtistSummary,
|
ArtistSummary,
|
||||||
Album,
|
|
||||||
Result,
|
Result,
|
||||||
slice2,
|
slice2,
|
||||||
AlbumQuery,
|
AlbumQuery,
|
||||||
@@ -15,13 +14,14 @@ import {
|
|||||||
Artist,
|
Artist,
|
||||||
AuthFailure,
|
AuthFailure,
|
||||||
AuthSuccess,
|
AuthSuccess,
|
||||||
|
albumToAlbumSummary,
|
||||||
} from "./music_library";
|
} from "./music_library";
|
||||||
import {
|
import {
|
||||||
Subsonic,
|
Subsonic,
|
||||||
CustomPlayers,
|
CustomPlayers,
|
||||||
GetAlbumResponse,
|
GetAlbumResponse,
|
||||||
asTrack,
|
asTrack,
|
||||||
asAlbum,
|
asAlbumSummary,
|
||||||
PingResponse,
|
PingResponse,
|
||||||
NO_CUSTOM_PLAYERS,
|
NO_CUSTOM_PLAYERS,
|
||||||
asToken,
|
asToken,
|
||||||
@@ -171,11 +171,13 @@ export class SubsonicMusicLibrary implements MusicLibrary {
|
|||||||
albums = async (q: AlbumQuery): Promise<Result<AlbumSummary>> =>
|
albums = async (q: AlbumQuery): Promise<Result<AlbumSummary>> =>
|
||||||
this.subsonic.getAlbumList2(this.credentials, q);
|
this.subsonic.getAlbumList2(this.credentials, q);
|
||||||
|
|
||||||
album = (id: string): Promise<Album> =>
|
// todo: this should probably return an Album
|
||||||
this.subsonic.getAlbum(this.credentials, id);
|
album = (id: string): Promise<AlbumSummary> =>
|
||||||
|
this.subsonic.getAlbum(this.credentials, id).then(albumToAlbumSummary);
|
||||||
|
|
||||||
genres = () => this.subsonic.getGenres(this.credentials);
|
genres = () => this.subsonic.getGenres(this.credentials);
|
||||||
|
|
||||||
|
// todo: do we even need this if Album has tracks?
|
||||||
tracks = (albumId: string) =>
|
tracks = (albumId: string) =>
|
||||||
this.subsonic
|
this.subsonic
|
||||||
.getJSON<GetAlbumResponse>(this.credentials, "/rest/getAlbum", {
|
.getJSON<GetAlbumResponse>(this.credentials, "/rest/getAlbum", {
|
||||||
@@ -184,7 +186,7 @@ export class SubsonicMusicLibrary implements MusicLibrary {
|
|||||||
.then((it) => it.album)
|
.then((it) => it.album)
|
||||||
.then((album) =>
|
.then((album) =>
|
||||||
(album.song || []).map((song) =>
|
(album.song || []).map((song) =>
|
||||||
asTrack(asAlbum(album), song, this.customPlayers)
|
asTrack(asAlbumSummary(album), song, this.customPlayers)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -418,7 +420,7 @@ export class SubsonicMusicLibrary implements MusicLibrary {
|
|||||||
songs.map((song) =>
|
songs.map((song) =>
|
||||||
this.subsonic
|
this.subsonic
|
||||||
.getAlbum(this.credentials, song.albumId!)
|
.getAlbum(this.credentials, song.albumId!)
|
||||||
.then((album) => asTrack(album, song, this.customPlayers))
|
.then((album) => asTrack(albumToAlbumSummary(album), song, this.customPlayers))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -436,7 +438,7 @@ export class SubsonicMusicLibrary implements MusicLibrary {
|
|||||||
songs.map((song) =>
|
songs.map((song) =>
|
||||||
this.subsonic
|
this.subsonic
|
||||||
.getAlbum(this.credentials, song.albumId!)
|
.getAlbum(this.credentials, song.albumId!)
|
||||||
.then((album) => asTrack(album, song, this.customPlayers))
|
.then((album) => asTrack(albumToAlbumSummary(album), song, this.customPlayers))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,13 +8,12 @@ import {
|
|||||||
Album,
|
Album,
|
||||||
Artist,
|
Artist,
|
||||||
Track,
|
Track,
|
||||||
albumToAlbumSummary,
|
|
||||||
artistToArtistSummary,
|
|
||||||
PlaylistSummary,
|
PlaylistSummary,
|
||||||
Playlist,
|
Playlist,
|
||||||
SimilarArtist,
|
SimilarArtist,
|
||||||
AlbumSummary,
|
AlbumSummary,
|
||||||
RadioStation
|
RadioStation,
|
||||||
|
ArtistSummary
|
||||||
} from "../src/music_library";
|
} from "../src/music_library";
|
||||||
|
|
||||||
import { b64Encode } from "../src/b64";
|
import { b64Encode } from "../src/b64";
|
||||||
@@ -116,13 +115,26 @@ export function aSimilarArtist(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function anArtist(fields: Partial<Artist> = {}): Artist {
|
export function anArtistSummary(fields: Partial<ArtistSummary> = {}): ArtistSummary {
|
||||||
const id = fields.id || uuid();
|
const id = fields.id || uuid();
|
||||||
const artist = {
|
return {
|
||||||
id,
|
id,
|
||||||
name: `Artist ${id}`,
|
name: `Artist ${id}`,
|
||||||
albums: [anAlbum(), anAlbum(), anAlbum()],
|
|
||||||
image: { system: "subsonic", resource: `art:${id}` },
|
image: { system: "subsonic", resource: `art:${id}` },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function anArtist(fields: Partial<Artist> = {}): Artist {
|
||||||
|
const id = fields.id || uuid();
|
||||||
|
const name = `Artist ${randomstring.generate()}`
|
||||||
|
const albums = fields.albums || [
|
||||||
|
anAlbumSummary({ artistId: id, artistName: name }),
|
||||||
|
anAlbumSummary({ artistId: id, artistName: name }),
|
||||||
|
anAlbumSummary({ artistId: id, artistName: name })
|
||||||
|
];
|
||||||
|
const artist = {
|
||||||
|
...anArtistSummary({ id, name }),
|
||||||
|
albums,
|
||||||
similarArtists: [
|
similarArtists: [
|
||||||
aSimilarArtist({ id: uuid(), name: "Similar artist1", inLibrary: true }),
|
aSimilarArtist({ id: uuid(), name: "Similar artist1", inLibrary: true }),
|
||||||
aSimilarArtist({ id: uuid(), name: "Similar artist2", inLibrary: true }),
|
aSimilarArtist({ id: uuid(), name: "Similar artist2", inLibrary: true }),
|
||||||
@@ -168,9 +180,10 @@ export const randomGenre = () => SAMPLE_GENRES[randomInt(SAMPLE_GENRES.length)];
|
|||||||
|
|
||||||
export function aTrack(fields: Partial<Track> = {}): Track {
|
export function aTrack(fields: Partial<Track> = {}): Track {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
const artist = anArtist();
|
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}`,
|
||||||
@@ -181,41 +194,14 @@ export function aTrack(fields: Partial<Track> = {}): Track {
|
|||||||
duration: randomInt(500),
|
duration: randomInt(500),
|
||||||
number: randomInt(100),
|
number: randomInt(100),
|
||||||
genre,
|
genre,
|
||||||
artist: artistToArtistSummary(artist),
|
artist,
|
||||||
album: albumToAlbumSummary(
|
album,
|
||||||
anAlbum({ artistId: artist.id, artistName: artist.name, genre })
|
|
||||||
),
|
|
||||||
coverArt: { system: "subsonic", resource: `art:${uuid()}`},
|
coverArt: { system: "subsonic", resource: `art:${uuid()}`},
|
||||||
rating,
|
rating,
|
||||||
...fields,
|
...fields,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function anAlbum(fields: Partial<Album> = {}): Album {
|
|
||||||
const id = uuid();
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
name: `Album ${id}`,
|
|
||||||
genre: randomGenre(),
|
|
||||||
year: `19${randomInt(99)}`,
|
|
||||||
artistId: `Artist ${uuid()}`,
|
|
||||||
artistName: `Artist ${randomstring.generate()}`,
|
|
||||||
coverArt: { system: "subsonic", resource: `art:${uuid()}` },
|
|
||||||
...fields,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export function aRadioStation(fields: Partial<RadioStation> = {}): RadioStation {
|
|
||||||
const id = uuid()
|
|
||||||
const name = `Station-${id}`;
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
url: `http://example.com/${name}`,
|
|
||||||
...fields
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function anAlbumSummary(fields: Partial<AlbumSummary> = {}): AlbumSummary {
|
export function anAlbumSummary(fields: Partial<AlbumSummary> = {}): AlbumSummary {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
return {
|
return {
|
||||||
@@ -230,6 +216,35 @@ export function anAlbumSummary(fields: Partial<AlbumSummary> = {}): AlbumSummary
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function anAlbum(fields: Partial<Album> = {}): Album {
|
||||||
|
const albumSummary = anAlbumSummary()
|
||||||
|
const album = {
|
||||||
|
...albumSummary,
|
||||||
|
tracks: [],
|
||||||
|
...fields,
|
||||||
|
};
|
||||||
|
const artistSummary = anArtistSummary({ id: album.artistId, name: album.artistName })
|
||||||
|
const tracks = fields.tracks || [
|
||||||
|
aTrack({ album: albumSummary, artist: artistSummary }),
|
||||||
|
aTrack({ album: albumSummary, artist: artistSummary })
|
||||||
|
]
|
||||||
|
return {
|
||||||
|
...album,
|
||||||
|
tracks
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function aRadioStation(fields: Partial<RadioStation> = {}): RadioStation {
|
||||||
|
const id = uuid()
|
||||||
|
const name = `Station-${id}`;
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
url: `http://example.com/${name}`,
|
||||||
|
...fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const BLONDIE_ID = uuid();
|
export const BLONDIE_ID = uuid();
|
||||||
export const BLONDIE_NAME = "Blondie";
|
export const BLONDIE_NAME = "Blondie";
|
||||||
export const BLONDIE: Artist = {
|
export const BLONDIE: Artist = {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { InMemoryMusicService } from "./in_memory_music_service";
|
|||||||
import {
|
import {
|
||||||
MusicLibrary,
|
MusicLibrary,
|
||||||
artistToArtistSummary,
|
artistToArtistSummary,
|
||||||
albumToAlbumSummary,
|
|
||||||
} from "../src/music_library";
|
} from "../src/music_library";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import {
|
import {
|
||||||
@@ -17,6 +16,7 @@ import {
|
|||||||
METAL,
|
METAL,
|
||||||
HIP_HOP,
|
HIP_HOP,
|
||||||
SKA,
|
SKA,
|
||||||
|
anAlbumSummary,
|
||||||
} from "./builders";
|
} from "./builders";
|
||||||
import _ from "underscore";
|
import _ from "underscore";
|
||||||
|
|
||||||
@@ -194,16 +194,16 @@ describe("InMemoryMusicService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("albums", () => {
|
describe("albums", () => {
|
||||||
const artist1_album1 = anAlbum({ genre: POP });
|
const artist1_album1 = anAlbumSummary({ genre: POP });
|
||||||
const artist1_album2 = anAlbum({ genre: ROCK });
|
const artist1_album2 = anAlbumSummary({ genre: ROCK });
|
||||||
const artist1_album3 = anAlbum({ genre: METAL });
|
const artist1_album3 = anAlbumSummary({ genre: METAL });
|
||||||
const artist1_album4 = anAlbum({ genre: POP });
|
const artist1_album4 = anAlbumSummary({ genre: POP });
|
||||||
const artist1_album5 = anAlbum({ genre: POP });
|
const artist1_album5 = anAlbumSummary({ genre: POP });
|
||||||
|
|
||||||
const artist2_album1 = anAlbum({ genre: METAL });
|
const artist2_album1 = anAlbumSummary({ genre: METAL });
|
||||||
|
|
||||||
const artist3_album1 = anAlbum({ genre: HIP_HOP });
|
const artist3_album1 = anAlbumSummary({ genre: HIP_HOP });
|
||||||
const artist3_album2 = anAlbum({ genre: POP });
|
const artist3_album2 = anAlbumSummary({ genre: POP });
|
||||||
|
|
||||||
const artist1 = anArtist({
|
const artist1 = anArtist({
|
||||||
name: "artist1",
|
name: "artist1",
|
||||||
@@ -212,8 +212,8 @@ describe("InMemoryMusicService", () => {
|
|||||||
artist1_album2,
|
artist1_album2,
|
||||||
artist1_album3,
|
artist1_album3,
|
||||||
artist1_album4,
|
artist1_album4,
|
||||||
artist1_album5,
|
artist1_album5
|
||||||
],
|
]
|
||||||
});
|
});
|
||||||
const artist2 = anArtist({ name: "artist2", albums: [artist2_album1] });
|
const artist2 = anArtist({ name: "artist2", albums: [artist2_album1] });
|
||||||
const artist3 = anArtist({
|
const artist3 = anArtist({
|
||||||
@@ -275,16 +275,16 @@ describe("InMemoryMusicService", () => {
|
|||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
results: [
|
results: [
|
||||||
albumToAlbumSummary(artist1_album1),
|
artist1_album1,
|
||||||
albumToAlbumSummary(artist1_album2),
|
artist1_album2,
|
||||||
albumToAlbumSummary(artist1_album3),
|
artist1_album3,
|
||||||
albumToAlbumSummary(artist1_album4),
|
artist1_album4,
|
||||||
albumToAlbumSummary(artist1_album5),
|
artist1_album5,
|
||||||
|
|
||||||
albumToAlbumSummary(artist2_album1),
|
artist2_album1,
|
||||||
|
|
||||||
albumToAlbumSummary(artist3_album1),
|
artist3_album1,
|
||||||
albumToAlbumSummary(artist3_album2),
|
artist3_album2,
|
||||||
],
|
],
|
||||||
total: totalAlbumCount,
|
total: totalAlbumCount,
|
||||||
});
|
});
|
||||||
@@ -300,7 +300,7 @@ describe("InMemoryMusicService", () => {
|
|||||||
type: "alphabeticalByName",
|
type: "alphabeticalByName",
|
||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
results: _.sortBy(allAlbums, "name").map(albumToAlbumSummary),
|
results: _.sortBy(allAlbums, "name"),
|
||||||
total: totalAlbumCount,
|
total: totalAlbumCount,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -317,9 +317,9 @@ describe("InMemoryMusicService", () => {
|
|||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
results: [
|
results: [
|
||||||
albumToAlbumSummary(artist1_album5),
|
artist1_album5,
|
||||||
albumToAlbumSummary(artist2_album1),
|
artist2_album1,
|
||||||
albumToAlbumSummary(artist3_album1),
|
artist3_album1,
|
||||||
],
|
],
|
||||||
total: totalAlbumCount,
|
total: totalAlbumCount,
|
||||||
});
|
});
|
||||||
@@ -336,8 +336,8 @@ describe("InMemoryMusicService", () => {
|
|||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
results: [
|
results: [
|
||||||
albumToAlbumSummary(artist3_album1),
|
artist3_album1,
|
||||||
albumToAlbumSummary(artist3_album2),
|
artist3_album2,
|
||||||
],
|
],
|
||||||
total: totalAlbumCount,
|
total: totalAlbumCount,
|
||||||
});
|
});
|
||||||
@@ -357,10 +357,10 @@ describe("InMemoryMusicService", () => {
|
|||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
results: [
|
results: [
|
||||||
albumToAlbumSummary(artist1_album1),
|
artist1_album1,
|
||||||
albumToAlbumSummary(artist1_album4),
|
artist1_album4,
|
||||||
albumToAlbumSummary(artist1_album5),
|
artist1_album5,
|
||||||
albumToAlbumSummary(artist3_album2),
|
artist3_album2,
|
||||||
],
|
],
|
||||||
total: 4,
|
total: 4,
|
||||||
});
|
});
|
||||||
@@ -379,8 +379,8 @@ describe("InMemoryMusicService", () => {
|
|||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
results: [
|
results: [
|
||||||
albumToAlbumSummary(artist1_album4),
|
artist1_album4,
|
||||||
albumToAlbumSummary(artist1_album5),
|
artist1_album5,
|
||||||
],
|
],
|
||||||
total: 4,
|
total: 4,
|
||||||
});
|
});
|
||||||
@@ -397,7 +397,7 @@ describe("InMemoryMusicService", () => {
|
|||||||
_count: 100,
|
_count: 100,
|
||||||
})
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
results: [albumToAlbumSummary(artist3_album2)],
|
results: [artist3_album2],
|
||||||
total: 4,
|
total: 4,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import {
|
|||||||
slice2,
|
slice2,
|
||||||
asResult,
|
asResult,
|
||||||
artistToArtistSummary,
|
artistToArtistSummary,
|
||||||
albumToAlbumSummary,
|
|
||||||
Track,
|
Track,
|
||||||
Genre,
|
Genre,
|
||||||
Rating,
|
Rating,
|
||||||
@@ -97,7 +96,6 @@ export class InMemoryMusicService implements MusicService {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((matches) => matches.map((it) => it.album))
|
.then((matches) => matches.map((it) => it.album))
|
||||||
.then((it) => it.map(albumToAlbumSummary))
|
|
||||||
.then(slice2(q))
|
.then(slice2(q))
|
||||||
.then(asResult),
|
.then(asResult),
|
||||||
album: (id: string) =>
|
album: (id: string) =>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import { Md5 } from "ts-md5";
|
import { Md5 } from "ts-md5";
|
||||||
import tmp from "tmp";
|
import tmp from "tmp";
|
||||||
@@ -28,15 +29,17 @@ import {
|
|||||||
TranscodingCustomPlayers,
|
TranscodingCustomPlayers,
|
||||||
CustomPlayers,
|
CustomPlayers,
|
||||||
NO_CUSTOM_PLAYERS,
|
NO_CUSTOM_PLAYERS,
|
||||||
Subsonic,
|
Subsonic
|
||||||
} from "../src/subsonic";
|
} from "../src/subsonic";
|
||||||
|
|
||||||
import { b64Encode } from "../src/b64";
|
import { b64Encode } from "../src/b64";
|
||||||
|
|
||||||
import { Album, Artist, Track } from "../src/music_library";
|
import { Album, Artist, Track, AlbumSummary } from "../src/music_library";
|
||||||
import { anAlbum, aTrack } from "./builders";
|
import { anAlbum, aTrack, anAlbumSummary, anArtistSummary } from "./builders";
|
||||||
import { BUrn } from "../src/burn";
|
import { BUrn } from "../src/burn";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe("t", () => {
|
describe("t", () => {
|
||||||
it("should be an md5 of the password and the salt", () => {
|
it("should be an md5 of the password and the salt", () => {
|
||||||
const p = "password123";
|
const p = "password123";
|
||||||
@@ -163,7 +166,8 @@ describe("cachingImageFetcher", () => {
|
|||||||
toBuffer: () => Promise.resolve(pngImage),
|
toBuffer: () => Promise.resolve(pngImage),
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await cachingImageFetcher(dir.name, delegate)(url);
|
// todo: the fact that I need to pass the sharp mock in here isnt correct
|
||||||
|
const result = await cachingImageFetcher(dir.name, delegate, sharp)(url);
|
||||||
|
|
||||||
expect(result!.contentType).toEqual("image/png");
|
expect(result!.contentType).toEqual("image/png");
|
||||||
expect(result!.data).toEqual(pngImage);
|
expect(result!.data).toEqual(pngImage);
|
||||||
@@ -555,6 +559,95 @@ const ok = (data: string | object) => ({
|
|||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const asArtistAlbumJson = (
|
||||||
|
artist: { id: string | undefined; name: string | undefined },
|
||||||
|
album: AlbumSummary
|
||||||
|
) => ({
|
||||||
|
id: album.id,
|
||||||
|
parent: artist.id,
|
||||||
|
isDir: "true",
|
||||||
|
title: album.name,
|
||||||
|
name: album.name,
|
||||||
|
album: album.name,
|
||||||
|
artist: artist.name,
|
||||||
|
genre: album.genre?.name,
|
||||||
|
duration: "123",
|
||||||
|
playCount: "4",
|
||||||
|
year: album.year,
|
||||||
|
created: "2021-01-07T08:19:55.834207205Z",
|
||||||
|
artistId: artist.id,
|
||||||
|
songCount: "19",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const asAlbumJson = (
|
||||||
|
artist: { id: string | undefined; name: string | undefined },
|
||||||
|
album: Album
|
||||||
|
) => ({
|
||||||
|
id: album.id,
|
||||||
|
parent: artist.id,
|
||||||
|
isDir: "true",
|
||||||
|
title: album.name,
|
||||||
|
name: album.name,
|
||||||
|
album: album.name,
|
||||||
|
artist: artist.name,
|
||||||
|
genre: album.genre?.name,
|
||||||
|
coverArt: maybeIdFromCoverArtUrn(album.coverArt),
|
||||||
|
duration: "123",
|
||||||
|
playCount: "4",
|
||||||
|
year: album.year,
|
||||||
|
created: "2021-01-07T08:19:55.834207205Z",
|
||||||
|
artistId: artist.id,
|
||||||
|
songCount: "19",
|
||||||
|
isVideo: false,
|
||||||
|
song: album.tracks.map(asSongJson),
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const getAlbumJson = (album: Album) =>
|
||||||
|
subsonicOK({ album: {
|
||||||
|
id: album.id,
|
||||||
|
parent: album.artistId,
|
||||||
|
album: album.name,
|
||||||
|
title: album.name,
|
||||||
|
name: album.name,
|
||||||
|
isDir: true,
|
||||||
|
coverArt: maybeIdFromCoverArtUrn(album.coverArt),
|
||||||
|
songCount: 19,
|
||||||
|
created: "2021-01-07T08:19:55.834207205Z",
|
||||||
|
duration: 123,
|
||||||
|
playCount: 4,
|
||||||
|
artistId: album.artistId,
|
||||||
|
artist: album.artistName,
|
||||||
|
year: album.year,
|
||||||
|
genre: album.genre?.name,
|
||||||
|
song: album.tracks.map(track => ({
|
||||||
|
id: track.id,
|
||||||
|
parent: track.album.id,
|
||||||
|
title: track.name,
|
||||||
|
isDir: false,
|
||||||
|
isVideo: false,
|
||||||
|
type: "music",
|
||||||
|
albumId: track.album.id,
|
||||||
|
album: track.album.name,
|
||||||
|
artistId: track.artist.id,
|
||||||
|
artist: track.artist.name,
|
||||||
|
coverArt: maybeIdFromCoverArtUrn(track.coverArt),
|
||||||
|
duration: track.duration,
|
||||||
|
bitRate: 128,
|
||||||
|
bitDepth: 16,
|
||||||
|
samplingRate: 555,
|
||||||
|
channelCount: 2,
|
||||||
|
track: track.number,
|
||||||
|
year: 1900,
|
||||||
|
genre: track.genre?.name,
|
||||||
|
size: 5624132,
|
||||||
|
discNumer: 1,
|
||||||
|
suffix: "mp3",
|
||||||
|
contentType: track.encoding.mimeType,
|
||||||
|
path: "ACDC/High voltage/ACDC - The Jack.mp3"
|
||||||
|
})),
|
||||||
|
} });
|
||||||
|
|
||||||
describe("subsonic", () => {
|
describe("subsonic", () => {
|
||||||
const url = new URLBuilder("http://127.0.0.22:4567/some-context-path");
|
const url = new URLBuilder("http://127.0.0.22:4567/some-context-path");
|
||||||
const customPlayers = {
|
const customPlayers = {
|
||||||
@@ -684,4 +777,56 @@ describe("subsonic", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("getting an album", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
customPlayers.encodingFor.mockReturnValue(O.none);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when it exists", () => {
|
||||||
|
const artistId = "artist6677"
|
||||||
|
const artistName = "Fizzy Wizzy"
|
||||||
|
|
||||||
|
const albumSummary = anAlbumSummary({ artistId, artistName })
|
||||||
|
const artistSumamry = anArtistSummary({ id: artistId, name: artistName })
|
||||||
|
|
||||||
|
// todo: fix these ratings
|
||||||
|
const tracks = [
|
||||||
|
aTrack({ artist: artistSumamry, album: albumSummary, rating: { love: false, stars: 0 } }),
|
||||||
|
aTrack({ artist: artistSumamry, album: albumSummary, rating: { love: false, stars: 0 } }),
|
||||||
|
aTrack({ artist: artistSumamry, album: albumSummary, rating: { love: false, stars: 0 } }),
|
||||||
|
aTrack({ artist: artistSumamry, album: albumSummary, rating: { love: false, stars: 0 } }),
|
||||||
|
];
|
||||||
|
|
||||||
|
const album = anAlbum({
|
||||||
|
...albumSummary,
|
||||||
|
tracks,
|
||||||
|
artistId,
|
||||||
|
artistName,
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockGET.mockImplementationOnce(() =>
|
||||||
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return the album", async () => {
|
||||||
|
const result = await subsonic.getAlbum(credentials, album.id);
|
||||||
|
|
||||||
|
expect(result).toEqual(album);
|
||||||
|
|
||||||
|
expect(axios.get).toHaveBeenCalledWith(
|
||||||
|
url.append({ pathname: "/rest/getAlbum" }).href(),
|
||||||
|
{
|
||||||
|
params: asURLSearchParams({
|
||||||
|
...authParamsPlusJson,
|
||||||
|
id: album.id,
|
||||||
|
}),
|
||||||
|
headers,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
CustomPlayers,
|
CustomPlayers,
|
||||||
PingResponse,
|
PingResponse,
|
||||||
images,
|
images,
|
||||||
|
|
||||||
} from "../src/subsonic";
|
} from "../src/subsonic";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -32,7 +33,6 @@ import {
|
|||||||
albumToAlbumSummary,
|
albumToAlbumSummary,
|
||||||
asArtistAlbumPairs,
|
asArtistAlbumPairs,
|
||||||
Track,
|
Track,
|
||||||
AlbumSummary,
|
|
||||||
artistToArtistSummary,
|
artistToArtistSummary,
|
||||||
AlbumQuery,
|
AlbumQuery,
|
||||||
PlaylistSummary,
|
PlaylistSummary,
|
||||||
@@ -41,6 +41,7 @@ import {
|
|||||||
Credentials,
|
Credentials,
|
||||||
AuthFailure,
|
AuthFailure,
|
||||||
RadioStation,
|
RadioStation,
|
||||||
|
AlbumSummary,
|
||||||
} from "../src/music_library";
|
} from "../src/music_library";
|
||||||
import {
|
import {
|
||||||
aGenre,
|
aGenre,
|
||||||
@@ -53,11 +54,15 @@ import {
|
|||||||
POP,
|
POP,
|
||||||
ROCK,
|
ROCK,
|
||||||
aRadioStation,
|
aRadioStation,
|
||||||
|
anAlbumSummary,
|
||||||
|
anArtistSummary
|
||||||
} from "./builders";
|
} from "./builders";
|
||||||
import { b64Encode } from "../src/b64";
|
import { b64Encode } from "../src/b64";
|
||||||
import { BUrn } from "../src/burn";
|
import { BUrn } from "../src/burn";
|
||||||
import { URLBuilder } from "../src/url_builder";
|
import { URLBuilder } from "../src/url_builder";
|
||||||
|
|
||||||
|
import { getAlbumJson } from "./subsonic.test";
|
||||||
|
|
||||||
const EMPTY = {
|
const EMPTY = {
|
||||||
"subsonic-response": {
|
"subsonic-response": {
|
||||||
status: "ok",
|
status: "ok",
|
||||||
@@ -104,29 +109,6 @@ const error = (code: string, message: string) => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const asAlbumJson = (
|
|
||||||
artist: { id: string | undefined; name: string | undefined },
|
|
||||||
album: AlbumSummary,
|
|
||||||
tracks: Track[] = []
|
|
||||||
) => ({
|
|
||||||
id: album.id,
|
|
||||||
parent: artist.id,
|
|
||||||
isDir: "true",
|
|
||||||
title: album.name,
|
|
||||||
name: album.name,
|
|
||||||
album: album.name,
|
|
||||||
artist: artist.name,
|
|
||||||
genre: album.genre?.name,
|
|
||||||
coverArt: maybeIdFromCoverArtUrn(album.coverArt),
|
|
||||||
duration: "123",
|
|
||||||
playCount: "4",
|
|
||||||
year: album.year,
|
|
||||||
created: "2021-01-07T08:19:55.834207205Z",
|
|
||||||
artistId: artist.id,
|
|
||||||
songCount: "19",
|
|
||||||
isVideo: false,
|
|
||||||
song: tracks.map(asSongJson),
|
|
||||||
});
|
|
||||||
|
|
||||||
const maybeIdFromCoverArtUrn = (coverArt: BUrn | undefined) =>
|
const maybeIdFromCoverArtUrn = (coverArt: BUrn | undefined) =>
|
||||||
pipe(
|
pipe(
|
||||||
@@ -136,28 +118,43 @@ const maybeIdFromCoverArtUrn = (coverArt: BUrn | undefined) =>
|
|||||||
O.getOrElseW(() => "")
|
O.getOrElseW(() => "")
|
||||||
);
|
);
|
||||||
|
|
||||||
const getAlbumJson = (artist: Artist, album: Album, tracks: Track[]) =>
|
|
||||||
subsonicOK({ album: asAlbumJson(artist, album, tracks) });
|
|
||||||
|
|
||||||
const getSongJson = (track: Track) => subsonicOK({ song: asSongJson(track) });
|
const getSongJson = (track: Track) => subsonicOK({ song: asSongJson(track) });
|
||||||
|
|
||||||
const asArtistJson = (
|
|
||||||
artist: Artist,
|
|
||||||
extras: ArtistExtras = { artistImageUrl: undefined }
|
|
||||||
) => ({
|
|
||||||
id: artist.id,
|
|
||||||
name: artist.name,
|
|
||||||
albumCount: artist.albums.length,
|
|
||||||
album: artist.albums.map((it) => asAlbumJson(artist, it)),
|
|
||||||
...extras,
|
|
||||||
});
|
|
||||||
|
|
||||||
const getArtistJson = (
|
const getArtistJson = (
|
||||||
artist: Artist,
|
artist: Artist,
|
||||||
extras: ArtistExtras = { artistImageUrl: undefined }
|
extras: ArtistExtras = { artistImageUrl: undefined }
|
||||||
) =>
|
) =>
|
||||||
subsonicOK({
|
subsonicOK({
|
||||||
artist: asArtistJson(artist, extras),
|
artist: {
|
||||||
|
id: artist.id,
|
||||||
|
name: artist.name,
|
||||||
|
coverArt: "art-123",
|
||||||
|
albumCount: artist.albums.length,
|
||||||
|
artistImageUrl: extras.artistImageUrl,
|
||||||
|
starred: "sometime",
|
||||||
|
album: artist.albums.map((album) => ({
|
||||||
|
id: album.id,
|
||||||
|
parent: artist.id,
|
||||||
|
album: album.name,
|
||||||
|
title: album.name,
|
||||||
|
name: album.name,
|
||||||
|
isDir: "true",
|
||||||
|
coverArt: maybeIdFromCoverArtUrn(album.coverArt),
|
||||||
|
songCount: 19,
|
||||||
|
created: "2021-01-07T08:19:55.834207205Z",
|
||||||
|
duration: 123,
|
||||||
|
playCount: 4,
|
||||||
|
artistId: artist.id,
|
||||||
|
artist: artist.name,
|
||||||
|
year: album.year,
|
||||||
|
genre: album.genre?.name,
|
||||||
|
userRating: 5,
|
||||||
|
averageRating: 3,
|
||||||
|
starred: "2021-01-07T08:19:55.834207205Z",
|
||||||
|
}))
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const getRadioStationsJson = (radioStations: RadioStation[]) =>
|
const getRadioStationsJson = (radioStations: RadioStation[]) =>
|
||||||
@@ -172,11 +169,6 @@ const getRadioStationsJson = (radioStations: RadioStation[]) =>
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// const getStarredJson = ({ albums }: { albums: Album[] }) => subsonicOK({starred2: {
|
|
||||||
// album: albums.map(it => asAlbumJson({ id: it.artistId, name: it.artistName }, it, [])),
|
|
||||||
// song: [],
|
|
||||||
// }})
|
|
||||||
|
|
||||||
const subsonicOK = (body: any = {}) => ({
|
const subsonicOK = (body: any = {}) => ({
|
||||||
"subsonic-response": {
|
"subsonic-response": {
|
||||||
status: "ok",
|
status: "ok",
|
||||||
@@ -280,10 +272,22 @@ const createPlayListJson = (playlist: PlaylistSummary) =>
|
|||||||
playlist: asPlaylistJson(playlist),
|
playlist: asPlaylistJson(playlist),
|
||||||
});
|
});
|
||||||
|
|
||||||
const getAlbumListJson = (albums: [Artist, Album][]) =>
|
|
||||||
|
const getAlbumListJson = (albums: [Artist, AlbumSummary][]) =>
|
||||||
subsonicOK({
|
subsonicOK({
|
||||||
albumList2: {
|
albumList2: {
|
||||||
album: albums.map(([artist, album]) => asAlbumJson(artist, album)),
|
album: albums.map(([artist, album]) => ({
|
||||||
|
id: album.id,
|
||||||
|
name: album.name,
|
||||||
|
coverArt: maybeIdFromCoverArtUrn(album.coverArt),
|
||||||
|
songCount: "19",
|
||||||
|
created: "2021-01-07T08:19:55.834207205Z",
|
||||||
|
duration: "123",
|
||||||
|
artist: artist.name,
|
||||||
|
artistId: artist.id,
|
||||||
|
year: album.year,
|
||||||
|
genre: album.genre?.name
|
||||||
|
})),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -338,9 +342,59 @@ const getSearchResult3Json = ({
|
|||||||
}>) =>
|
}>) =>
|
||||||
subsonicOK({
|
subsonicOK({
|
||||||
searchResult3: {
|
searchResult3: {
|
||||||
artist: (artists || []).map((it) => asArtistJson({ ...it, albums: [] })),
|
artist: (artists || []).map((it) => ({
|
||||||
album: (albums || []).map((it) => asAlbumJson(it.artist, it.album, [])),
|
id: it.id,
|
||||||
song: (tracks || []).map((it) => asSongJson(it)),
|
name: it.name,
|
||||||
|
// coverArt??
|
||||||
|
albumCount: it.albums.length,
|
||||||
|
userRating: -1,
|
||||||
|
//artistImageUrl?
|
||||||
|
})),
|
||||||
|
album: (albums || []).map(({ artist, album }) => ({
|
||||||
|
id: album.id,
|
||||||
|
name: album.name,
|
||||||
|
artist: artist.name,
|
||||||
|
year: album.year,
|
||||||
|
coverArt: maybeIdFromCoverArtUrn(album.coverArt),
|
||||||
|
//starred
|
||||||
|
//duration
|
||||||
|
//playCount
|
||||||
|
//played
|
||||||
|
//created
|
||||||
|
artistId: artist.id,
|
||||||
|
//userRating
|
||||||
|
songCount: album.tracks.length
|
||||||
|
})),
|
||||||
|
song: (tracks || []).map((track) => ({
|
||||||
|
id: track.id,
|
||||||
|
parent: track.album.id,
|
||||||
|
isDir: "false",
|
||||||
|
title: track.name,
|
||||||
|
album: track.album.name,
|
||||||
|
artist: track.artist.name,
|
||||||
|
track: track.number,
|
||||||
|
year: "",
|
||||||
|
coverArt: maybeIdFromCoverArtUrn(track.coverArt),
|
||||||
|
size: "5624132",
|
||||||
|
contentType: track.encoding.mimeType,
|
||||||
|
suffix: "mp3",
|
||||||
|
starred: track.rating.love ? "sometime" : undefined,
|
||||||
|
duration: track.duration,
|
||||||
|
bitRate: 128,
|
||||||
|
//bitDepth
|
||||||
|
//samplingRate
|
||||||
|
//channelCount
|
||||||
|
path: "ACDC/High voltage/ACDC - The Jack.mp3",
|
||||||
|
//path
|
||||||
|
//playCount
|
||||||
|
//played
|
||||||
|
//discNumber
|
||||||
|
created: "2004-11-08T23:36:11",
|
||||||
|
albumId: track.album.id,
|
||||||
|
artistId: track.artist.id,
|
||||||
|
type: "music",
|
||||||
|
isVideo: "false",
|
||||||
|
})),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -820,11 +874,11 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
describe("getting an artist", () => {
|
describe("getting an artist", () => {
|
||||||
describe("when the artist exists", () => {
|
describe("when the artist exists", () => {
|
||||||
describe("and has many similar artists", () => {
|
describe("and has many similar artists", () => {
|
||||||
const album1: Album = anAlbum({ genre: asGenre("Pop") });
|
const album1 = anAlbumSummary({ genre: asGenre("Pop") });
|
||||||
|
|
||||||
const album2: Album = anAlbum({ genre: asGenre("Pop") });
|
const album2 = anAlbumSummary({ genre: asGenre("Pop") });
|
||||||
|
|
||||||
const artist: Artist = anArtist({
|
const artist = anArtist({
|
||||||
albums: [album1, album2],
|
albums: [album1, album2],
|
||||||
similarArtists: [
|
similarArtists: [
|
||||||
aSimilarArtist({
|
aSimilarArtist({
|
||||||
@@ -853,7 +907,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return the similar artists", async () => {
|
it("should return the similar artists", async () => {
|
||||||
const result: Artist = await subsonic.artist(artist.id!);
|
const result = await subsonic.artist(artist.id!);
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
id: `${artist.id}`,
|
id: `${artist.id}`,
|
||||||
@@ -890,11 +944,11 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("and has one similar artist", () => {
|
describe("and has one similar artist", () => {
|
||||||
const album1: Album = anAlbum({ genre: asGenre("G1") });
|
const album1 = anAlbumSummary({ genre: asGenre("G1") });
|
||||||
|
|
||||||
const album2: Album = anAlbum({ genre: asGenre("G2") });
|
const album2 = anAlbumSummary({ genre: asGenre("G2") });
|
||||||
|
|
||||||
const artist: Artist = anArtist({
|
const artist = anArtist({
|
||||||
albums: [album1, album2],
|
albums: [album1, album2],
|
||||||
similarArtists: [
|
similarArtists: [
|
||||||
aSimilarArtist({
|
aSimilarArtist({
|
||||||
@@ -916,7 +970,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return the similar artists", async () => {
|
it("should return the similar artists", async () => {
|
||||||
const result: Artist = await subsonic.artist(artist.id!);
|
const result = await subsonic.artist(artist.id!);
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
id: artist.id,
|
id: artist.id,
|
||||||
@@ -953,11 +1007,11 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("and has no similar artists", () => {
|
describe("and has no similar artists", () => {
|
||||||
const album1: Album = anAlbum({ genre: asGenre("Jock") });
|
const album1 = anAlbumSummary({ genre: asGenre("Jock") });
|
||||||
|
|
||||||
const album2: Album = anAlbum({ genre: asGenre("Mock") });
|
const album2 = anAlbumSummary({ genre: asGenre("Mock") });
|
||||||
|
|
||||||
const artist: Artist = anArtist({
|
const artist = anArtist({
|
||||||
albums: [album1, album2],
|
albums: [album1, album2],
|
||||||
similarArtists: [],
|
similarArtists: [],
|
||||||
});
|
});
|
||||||
@@ -973,7 +1027,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return the similar artists", async () => {
|
it("should return the similar artists", async () => {
|
||||||
const result: Artist = await subsonic.artist(artist.id!);
|
const result = await subsonic.artist(artist.id!);
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
id: artist.id,
|
id: artist.id,
|
||||||
@@ -1180,7 +1234,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should use the external url", async () => {
|
it("should use the external url", async () => {
|
||||||
const result: Artist = await subsonic.artist(artist.id!);
|
const result = await subsonic.artist(artist.id!);
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
id: artist.id,
|
id: artist.id,
|
||||||
@@ -1220,7 +1274,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("and has a good medium external image uri from getArtistInfo route", () => {
|
describe("and has a good medium external image uri from getArtistInfo route", () => {
|
||||||
const artist: Artist = anArtist({
|
const artist = anArtist({
|
||||||
albums: [],
|
albums: [],
|
||||||
similarArtists: [],
|
similarArtists: [],
|
||||||
});
|
});
|
||||||
@@ -1231,7 +1285,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
mockGET
|
mockGET
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(
|
Promise.resolve(
|
||||||
ok(getArtistJson(artist, { artistImageUrl: dodgyImageUrl }))
|
ok(getArtistJson(artist))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
@@ -1249,7 +1303,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should use the external url", async () => {
|
it("should use the external url", async () => {
|
||||||
const result: Artist = await subsonic.artist(artist.id!);
|
const result = await subsonic.artist(artist.id!);
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
id: artist.id,
|
id: artist.id,
|
||||||
@@ -1289,9 +1343,9 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("and has multiple albums", () => {
|
describe("and has multiple albums", () => {
|
||||||
const album1: Album = anAlbum({ genre: asGenre("Pop") });
|
const album1 = anAlbumSummary({ genre: asGenre("Pop") });
|
||||||
|
|
||||||
const album2: Album = anAlbum({ genre: asGenre("Flop") });
|
const album2 = anAlbumSummary({ genre: asGenre("Flop") });
|
||||||
|
|
||||||
const artist: Artist = anArtist({
|
const artist: Artist = anArtist({
|
||||||
albums: [album1, album2],
|
albums: [album1, album2],
|
||||||
@@ -1346,7 +1400,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("and has only 1 album", () => {
|
describe("and has only 1 album", () => {
|
||||||
const album: Album = anAlbum({ genre: asGenre("Pop") });
|
const album = anAlbumSummary({ genre: POP });
|
||||||
|
|
||||||
const artist: Artist = anArtist({
|
const artist: Artist = anArtist({
|
||||||
albums: [album],
|
albums: [album],
|
||||||
@@ -1915,7 +1969,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
describe("when the artist has only 1 album", () => {
|
describe("when the artist has only 1 album", () => {
|
||||||
const artist = anArtist({
|
const artist = anArtist({
|
||||||
name: "one hit wonder",
|
name: "one hit wonder",
|
||||||
albums: [anAlbum({ genre: asGenre("Pop") })],
|
albums: [anAlbumSummary({ genre: asGenre("Pop") })],
|
||||||
});
|
});
|
||||||
const artists = [artist];
|
const artists = [artist];
|
||||||
const albums = artists.flatMap((artist) => artist.albums);
|
const albums = artists.flatMap((artist) => artist.albums);
|
||||||
@@ -2028,17 +2082,17 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
const artist1 = anArtist({
|
const artist1 = anArtist({
|
||||||
name: "abba",
|
name: "abba",
|
||||||
albums: [
|
albums: [
|
||||||
anAlbum({ name: "album1", genre: genre1 }),
|
anAlbumSummary({ name: "album1", genre: genre1 }),
|
||||||
anAlbum({ name: "album2", genre: genre2 }),
|
anAlbumSummary({ name: "album2", genre: genre2 }),
|
||||||
anAlbum({ name: "album3", genre: genre3 }),
|
anAlbumSummary({ name: "album3", genre: genre3 }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const artist2 = anArtist({
|
const artist2 = anArtist({
|
||||||
name: "babba",
|
name: "babba",
|
||||||
albums: [
|
albums: [
|
||||||
anAlbum({ name: "album4", genre: genre1 }),
|
anAlbumSummary({ name: "album4", genre: genre1 }),
|
||||||
anAlbum({ name: "album5", genre: genre2 }),
|
anAlbumSummary({ name: "album5", genre: genre2 }),
|
||||||
anAlbum({ name: "album6", genre: genre3 }),
|
anAlbumSummary({ name: "album6", genre: genre3 }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
const artists = [artist1, artist2];
|
const artists = [artist1, artist2];
|
||||||
@@ -2148,11 +2202,11 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
describe("when the number of albums reported by getArtists does not match that of getAlbums", () => {
|
describe("when the number of albums reported by getArtists does not match that of getAlbums", () => {
|
||||||
const genre = asGenre("lofi");
|
const genre = asGenre("lofi");
|
||||||
|
|
||||||
const album1 = anAlbum({ name: "album1", genre });
|
const album1 = anAlbumSummary({ name: "album1", genre });
|
||||||
const album2 = anAlbum({ name: "album2", genre });
|
const album2 = anAlbumSummary({ name: "album2", genre });
|
||||||
const album3 = anAlbum({ name: "album3", genre });
|
const album3 = anAlbumSummary({ name: "album3", genre });
|
||||||
const album4 = anAlbum({ name: "album4", genre });
|
const album4 = anAlbumSummary({ name: "album4", genre });
|
||||||
const album5 = anAlbum({ name: "album5", genre });
|
const album5 = anAlbumSummary({ name: "album5", genre });
|
||||||
|
|
||||||
// the artists have 5 albums in the getArtists endpoint
|
// the artists have 5 albums in the getArtists endpoint
|
||||||
const artist1 = anArtist({
|
const artist1 = anArtist({
|
||||||
@@ -2476,7 +2530,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
ok(
|
ok(
|
||||||
asArtistsJson([
|
asArtistsJson([
|
||||||
// artist1 has lost 2 albums on the getArtists end point
|
// artist1 has lost 2 albums on the getArtists end point
|
||||||
{ ...artist1, albums: [album1, album2] },
|
anArtist({ ...artist1, albums: [album1, album2] }),
|
||||||
artist2,
|
artist2,
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
@@ -2504,7 +2558,11 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
const result = await subsonic.albums(q);
|
const result = await subsonic.albums(q);
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
results: [album3, album4, album5],
|
results: [
|
||||||
|
album3,
|
||||||
|
album4,
|
||||||
|
album5
|
||||||
|
],
|
||||||
total: 5,
|
total: 5,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2534,49 +2592,6 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("getting an album", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
customPlayers.encodingFor.mockReturnValue(O.none);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("when it exists", () => {
|
|
||||||
const genre = asGenre("Pop");
|
|
||||||
|
|
||||||
const album = anAlbum({ genre });
|
|
||||||
|
|
||||||
const artist = anArtist({ albums: [album] });
|
|
||||||
|
|
||||||
const tracks = [
|
|
||||||
aTrack({ artist, album, genre }),
|
|
||||||
aTrack({ artist, album, genre }),
|
|
||||||
aTrack({ artist, album, genre }),
|
|
||||||
aTrack({ artist, album, genre }),
|
|
||||||
];
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
mockGET.mockImplementationOnce(() =>
|
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, tracks)))
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return the album", async () => {
|
|
||||||
const result = await subsonic.album(album.id);
|
|
||||||
|
|
||||||
expect(result).toEqual(album);
|
|
||||||
|
|
||||||
expect(axios.get).toHaveBeenCalledWith(
|
|
||||||
url.append({ pathname: "/rest/getAlbum" }).href(),
|
|
||||||
{
|
|
||||||
params: asURLSearchParams({
|
|
||||||
...authParamsPlusJson,
|
|
||||||
id: album.id,
|
|
||||||
}),
|
|
||||||
headers,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("getting tracks", () => {
|
describe("getting tracks", () => {
|
||||||
describe("for an album", () => {
|
describe("for an album", () => {
|
||||||
@@ -2589,21 +2604,20 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
const hipHop = asGenre("Hip-Hop");
|
const hipHop = asGenre("Hip-Hop");
|
||||||
const tripHop = asGenre("Trip-Hop");
|
const tripHop = asGenre("Trip-Hop");
|
||||||
|
|
||||||
const album = anAlbum({
|
const albumSummary = anAlbumSummary({
|
||||||
id: "album1",
|
id: "album1",
|
||||||
name: "Burnin",
|
name: "Burnin",
|
||||||
genre: hipHop,
|
genre: hipHop,
|
||||||
});
|
});
|
||||||
|
|
||||||
const artist = anArtist({
|
const artistSummary = anArtistSummary({
|
||||||
id: "artist1",
|
id: "artist1",
|
||||||
name: "Bob Marley",
|
name: "Bob Marley"
|
||||||
albums: [album],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const track1 = aTrack({
|
const track1 = aTrack({
|
||||||
artist: artistToArtistSummary(artist),
|
artist: artistSummary,
|
||||||
album: albumToAlbumSummary(album),
|
album: albumSummary,
|
||||||
genre: hipHop,
|
genre: hipHop,
|
||||||
rating: {
|
rating: {
|
||||||
love: true,
|
love: true,
|
||||||
@@ -2611,8 +2625,8 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const track2 = aTrack({
|
const track2 = aTrack({
|
||||||
artist: artistToArtistSummary(artist),
|
artist: artistSummary,
|
||||||
album: albumToAlbumSummary(album),
|
album: albumSummary,
|
||||||
genre: hipHop,
|
genre: hipHop,
|
||||||
rating: {
|
rating: {
|
||||||
love: false,
|
love: false,
|
||||||
@@ -2620,8 +2634,8 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const track3 = aTrack({
|
const track3 = aTrack({
|
||||||
artist: artistToArtistSummary(artist),
|
artist: artistSummary,
|
||||||
album: albumToAlbumSummary(album),
|
album: albumSummary,
|
||||||
genre: tripHop,
|
genre: tripHop,
|
||||||
rating: {
|
rating: {
|
||||||
love: true,
|
love: true,
|
||||||
@@ -2629,8 +2643,8 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const track4 = aTrack({
|
const track4 = aTrack({
|
||||||
artist: artistToArtistSummary(artist),
|
artist: artistSummary,
|
||||||
album: albumToAlbumSummary(album),
|
album: albumSummary,
|
||||||
genre: tripHop,
|
genre: tripHop,
|
||||||
rating: {
|
rating: {
|
||||||
love: false,
|
love: false,
|
||||||
@@ -2638,18 +2652,35 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const tracks = [track1, track2, track3, track4];
|
const album = anAlbum({
|
||||||
|
...albumSummary,
|
||||||
|
tracks: [track1, track2, track3, track4]
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockGET.mockImplementationOnce(() =>
|
mockGET.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, tracks)))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return the album", async () => {
|
it("should return the album", async () => {
|
||||||
const result = await subsonic.tracks(album.id);
|
const result = await subsonic.tracks(album.id);
|
||||||
|
|
||||||
expect(result).toEqual([track1, track2, track3, track4]);
|
// todo: not this
|
||||||
|
const blatRating = (t: Track) => ({
|
||||||
|
...t,
|
||||||
|
rating: {
|
||||||
|
love: false,
|
||||||
|
stars: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result).toEqual([
|
||||||
|
blatRating(track1),
|
||||||
|
blatRating(track2),
|
||||||
|
blatRating(track3),
|
||||||
|
blatRating(track4),
|
||||||
|
]);
|
||||||
|
|
||||||
expect(axios.get).toHaveBeenCalledWith(
|
expect(axios.get).toHaveBeenCalledWith(
|
||||||
url.append({ pathname: "/rest/getAlbum" }).href(),
|
url.append({ pathname: "/rest/getAlbum" }).href(),
|
||||||
@@ -2665,38 +2696,48 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("when the album has only 1 track", () => {
|
describe("when the album has only 1 track", () => {
|
||||||
|
// todo: why do i care about the genre in here?
|
||||||
const flipFlop = asGenre("Flip-Flop");
|
const flipFlop = asGenre("Flip-Flop");
|
||||||
|
|
||||||
const album = anAlbum({
|
const albumSummary = anAlbumSummary({
|
||||||
id: "album1",
|
id: "album1",
|
||||||
name: "Burnin",
|
name: "Burnin",
|
||||||
genre: flipFlop,
|
genre: flipFlop,
|
||||||
});
|
});
|
||||||
|
|
||||||
const artist = anArtist({
|
const artistSummary = anArtistSummary({
|
||||||
id: "artist1",
|
id: "artist1",
|
||||||
name: "Bob Marley",
|
name: "Bob Marley"
|
||||||
albums: [album],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const track = aTrack({
|
const track = aTrack({
|
||||||
artist: artistToArtistSummary(artist),
|
artist: artistSummary,
|
||||||
album: albumToAlbumSummary(album),
|
album: albumSummary,
|
||||||
genre: flipFlop,
|
genre: flipFlop,
|
||||||
});
|
});
|
||||||
|
|
||||||
const tracks = [track];
|
const album = anAlbum({
|
||||||
|
...albumSummary,
|
||||||
|
tracks: [track]
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockGET.mockImplementationOnce(() =>
|
mockGET.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, tracks)))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return the album", async () => {
|
it("should return the album", async () => {
|
||||||
const result = await subsonic.tracks(album.id);
|
const result = await subsonic.tracks(album.id);
|
||||||
|
|
||||||
expect(result).toEqual([track]);
|
expect(result).toEqual([{
|
||||||
|
...track,
|
||||||
|
// todo: not sure about this
|
||||||
|
rating: {
|
||||||
|
love: false,
|
||||||
|
stars: 0
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
expect(axios.get).toHaveBeenCalledWith(
|
expect(axios.get).toHaveBeenCalledWith(
|
||||||
url.append({ pathname: "/rest/getAlbum" }).href(),
|
url.append({ pathname: "/rest/getAlbum" }).href(),
|
||||||
@@ -2711,20 +2752,14 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("when the album has only no tracks", () => {
|
describe("when the album has no tracks", () => {
|
||||||
const album = anAlbum({ id: "album1", name: "Burnin" });
|
const album = anAlbum({
|
||||||
|
tracks: []
|
||||||
const artist = anArtist({
|
})
|
||||||
id: "artist1",
|
|
||||||
name: "Bob Marley",
|
|
||||||
albums: [album],
|
|
||||||
});
|
|
||||||
|
|
||||||
const tracks: Track[] = [];
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockGET.mockImplementationOnce(() =>
|
mockGET.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, tracks)))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2751,17 +2786,16 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
const hipHop = asGenre("Hip-Hop");
|
const hipHop = asGenre("Hip-Hop");
|
||||||
const tripHop = asGenre("Trip-Hop");
|
const tripHop = asGenre("Trip-Hop");
|
||||||
|
|
||||||
const album = anAlbum({ id: "album1", name: "Burnin", genre: hipHop });
|
const albumSummary = anAlbumSummary({ id: "album1", name: "Burnin", genre: hipHop });
|
||||||
|
|
||||||
const artist = anArtist({
|
const artistSummary = anArtistSummary({
|
||||||
id: "artist1",
|
id: "artist1",
|
||||||
name: "Bob Marley",
|
name: "Bob Marley"
|
||||||
albums: [album],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const alac = aTrack({
|
const alac = aTrack({
|
||||||
artist: artistToArtistSummary(artist),
|
artist: artistSummary,
|
||||||
album: albumToAlbumSummary(album),
|
album: albumSummary,
|
||||||
encoding: {
|
encoding: {
|
||||||
player: "bonob",
|
player: "bonob",
|
||||||
mimeType: "audio/alac",
|
mimeType: "audio/alac",
|
||||||
@@ -2773,8 +2807,8 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const m4a = aTrack({
|
const m4a = aTrack({
|
||||||
artist: artistToArtistSummary(artist),
|
artist: artistSummary,
|
||||||
album: albumToAlbumSummary(album),
|
album: albumSummary,
|
||||||
encoding: {
|
encoding: {
|
||||||
player: "bonob",
|
player: "bonob",
|
||||||
mimeType: "audio/m4a",
|
mimeType: "audio/m4a",
|
||||||
@@ -2786,8 +2820,8 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
const mp3 = aTrack({
|
const mp3 = aTrack({
|
||||||
artist: artistToArtistSummary(artist),
|
artist: artistSummary,
|
||||||
album: albumToAlbumSummary(album),
|
album: albumSummary,
|
||||||
encoding: {
|
encoding: {
|
||||||
player: "bonob",
|
player: "bonob",
|
||||||
mimeType: "audio/mp3",
|
mimeType: "audio/mp3",
|
||||||
@@ -2799,6 +2833,11 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const album = anAlbum({
|
||||||
|
...albumSummary,
|
||||||
|
tracks: [alac, m4a, mp3]
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
customPlayers.encodingFor
|
customPlayers.encodingFor
|
||||||
.mockReturnValueOnce(
|
.mockReturnValueOnce(
|
||||||
@@ -2810,7 +2849,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
.mockReturnValueOnce(O.none);
|
.mockReturnValueOnce(O.none);
|
||||||
|
|
||||||
mockGET.mockImplementationOnce(() =>
|
mockGET.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [alac, m4a, mp3])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2824,6 +2863,11 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
player: "bonob+audio/alac",
|
player: "bonob+audio/alac",
|
||||||
mimeType: "audio/flac",
|
mimeType: "audio/flac",
|
||||||
},
|
},
|
||||||
|
// todo: this doesnt seem right? why dont the ratings come back?
|
||||||
|
rating: {
|
||||||
|
love: false,
|
||||||
|
stars: 0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...m4a,
|
...m4a,
|
||||||
@@ -2831,6 +2875,10 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
player: "bonob+audio/m4a",
|
player: "bonob+audio/m4a",
|
||||||
mimeType: "audio/opus",
|
mimeType: "audio/opus",
|
||||||
},
|
},
|
||||||
|
rating: {
|
||||||
|
love: false,
|
||||||
|
stars: 0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
...mp3,
|
...mp3,
|
||||||
@@ -2838,6 +2886,10 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
player: "bonob",
|
player: "bonob",
|
||||||
mimeType: "audio/mp3",
|
mimeType: "audio/mp3",
|
||||||
},
|
},
|
||||||
|
rating: {
|
||||||
|
love: false,
|
||||||
|
stars: 0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -2899,7 +2951,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.track(track.id);
|
const result = await subsonic.track(track.id);
|
||||||
@@ -2950,7 +3002,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.track(track.id);
|
const result = await subsonic.track(track.id);
|
||||||
@@ -3027,7 +3079,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
||||||
|
|
||||||
@@ -3064,7 +3116,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
||||||
|
|
||||||
@@ -3103,7 +3155,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
||||||
|
|
||||||
@@ -3153,7 +3205,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
||||||
|
|
||||||
@@ -3172,7 +3224,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.reject("IO error occured")
|
Promise.reject("IO error occured")
|
||||||
@@ -3209,7 +3261,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
||||||
|
|
||||||
@@ -3272,7 +3324,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(
|
Promise.resolve(
|
||||||
ok(getAlbumJson(artist, album, [trackWithCustomPlayer]))
|
ok(getAlbumJson(album))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
||||||
@@ -3314,7 +3366,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(
|
Promise.resolve(
|
||||||
ok(getAlbumJson(artist, album, [trackWithCustomPlayer]))
|
ok(getAlbumJson(album))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
.mockImplementationOnce(() => Promise.resolve(streamResponse));
|
||||||
@@ -3588,7 +3640,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(ok(EMPTY)));
|
.mockImplementationOnce(() => Promise.resolve(ok(EMPTY)));
|
||||||
|
|
||||||
@@ -3623,7 +3675,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(ok(EMPTY)));
|
.mockImplementationOnce(() => Promise.resolve(ok(EMPTY)));
|
||||||
|
|
||||||
@@ -3661,7 +3713,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.rate(trackId, { love: true, stars: 0 });
|
const result = await subsonic.rate(trackId, { love: true, stars: 0 });
|
||||||
@@ -3686,7 +3738,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(ok(EMPTY)));
|
.mockImplementationOnce(() => Promise.resolve(ok(EMPTY)));
|
||||||
|
|
||||||
@@ -3725,7 +3777,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.rate(trackId, { love: true, stars: 3 });
|
const result = await subsonic.rate(trackId, { love: true, stars: 3 });
|
||||||
@@ -3750,7 +3802,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track)))
|
Promise.resolve(ok(getSongJson(track)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(ok(EMPTY)))
|
.mockImplementationOnce(() => Promise.resolve(ok(EMPTY)))
|
||||||
.mockImplementationOnce(() => Promise.resolve(ok(EMPTY)));
|
.mockImplementationOnce(() => Promise.resolve(ok(EMPTY)));
|
||||||
@@ -4025,10 +4077,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
describe("searchAlbums", () => {
|
describe("searchAlbums", () => {
|
||||||
describe("when there is 1 search results", () => {
|
describe("when there is 1 search results", () => {
|
||||||
it("should return true", async () => {
|
it("should return true", async () => {
|
||||||
const album = anAlbum({
|
const album = anAlbum({ name: "foo woo" });
|
||||||
name: "foo woo",
|
|
||||||
genre: { id: b64Encode("pop"), name: "pop" },
|
|
||||||
});
|
|
||||||
const artist = anArtist({ name: "#1", albums: [album] });
|
const artist = anArtist({ name: "#1", albums: [album] });
|
||||||
|
|
||||||
mockGET.mockImplementationOnce(() =>
|
mockGET.mockImplementationOnce(() =>
|
||||||
@@ -4039,7 +4088,12 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
|
|
||||||
const result = await subsonic.searchAlbums("foo");
|
const result = await subsonic.searchAlbums("foo");
|
||||||
|
|
||||||
expect(result).toEqual([albumToAlbumSummary(album)]);
|
expect(result).toEqual([
|
||||||
|
{
|
||||||
|
...albumToAlbumSummary(album),
|
||||||
|
genre: undefined
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
expect(mockGET).toHaveBeenCalledWith(
|
expect(mockGET).toHaveBeenCalledWith(
|
||||||
url.append({ pathname: "/rest/search3" }).href(),
|
url.append({ pathname: "/rest/search3" }).href(),
|
||||||
@@ -4059,16 +4113,10 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
|
|
||||||
describe("when there are many search results", () => {
|
describe("when there are many search results", () => {
|
||||||
it("should return true", async () => {
|
it("should return true", async () => {
|
||||||
const album1 = anAlbum({
|
const album1 = anAlbum({ name: "album1" });
|
||||||
name: "album1",
|
|
||||||
genre: { id: b64Encode("pop"), name: "pop" },
|
|
||||||
});
|
|
||||||
const artist1 = anArtist({ name: "artist1", albums: [album1] });
|
const artist1 = anArtist({ name: "artist1", albums: [album1] });
|
||||||
|
|
||||||
const album2 = anAlbum({
|
const album2 = anAlbum({ name: "album2" });
|
||||||
name: "album2",
|
|
||||||
genre: { id: b64Encode("pop"), name: "pop" },
|
|
||||||
});
|
|
||||||
const artist2 = anArtist({ name: "artist2", albums: [album2] });
|
const artist2 = anArtist({ name: "artist2", albums: [album2] });
|
||||||
|
|
||||||
mockGET.mockImplementationOnce(() =>
|
mockGET.mockImplementationOnce(() =>
|
||||||
@@ -4087,8 +4135,14 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
const result = await subsonic.searchAlbums("moo");
|
const result = await subsonic.searchAlbums("moo");
|
||||||
|
|
||||||
expect(result).toEqual([
|
expect(result).toEqual([
|
||||||
albumToAlbumSummary(album1),
|
{
|
||||||
albumToAlbumSummary(album2),
|
...albumToAlbumSummary(album1),
|
||||||
|
genre: undefined
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...albumToAlbumSummary(album2),
|
||||||
|
genre: undefined
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(mockGET).toHaveBeenCalledWith(
|
expect(mockGET).toHaveBeenCalledWith(
|
||||||
@@ -4161,7 +4215,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
)
|
)
|
||||||
.mockImplementationOnce(() => Promise.resolve(ok(getSongJson(track))))
|
.mockImplementationOnce(() => Promise.resolve(ok(getSongJson(track))))
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album, [])))
|
Promise.resolve(ok(getAlbumJson(album)))
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.searchTracks("foo");
|
const result = await subsonic.searchTracks("foo");
|
||||||
@@ -4231,10 +4285,10 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSongJson(track2)))
|
Promise.resolve(ok(getSongJson(track2)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist1, album1, [])))
|
Promise.resolve(ok(getAlbumJson(album1)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist2, album2, [])))
|
Promise.resolve(ok(getAlbumJson(album2)))
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.searchTracks("moo");
|
const result = await subsonic.searchTracks("moo");
|
||||||
@@ -4606,7 +4660,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSimilarSongsJson([track1])))
|
Promise.resolve(ok(getSimilarSongsJson([track1])))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist1, album1, [])))
|
Promise.resolve(ok(getAlbumJson(album1)))
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.similarSongs(id);
|
const result = await subsonic.similarSongs(id);
|
||||||
@@ -4671,13 +4725,13 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getSimilarSongsJson([track1, track2, track3])))
|
Promise.resolve(ok(getSimilarSongsJson([track1, track2, track3])))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist1, album1, [])))
|
Promise.resolve(ok(getAlbumJson(album1)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist2, album2, [])))
|
Promise.resolve(ok(getAlbumJson(album2)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist1, album1, [])))
|
Promise.resolve(ok(getAlbumJson(album1)))
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.similarSongs(id);
|
const result = await subsonic.similarSongs(id);
|
||||||
@@ -4773,7 +4827,7 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getTopSongsJson([track1])))
|
Promise.resolve(ok(getTopSongsJson([track1])))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album1, [])))
|
Promise.resolve(ok(getAlbumJson(album1)))
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.topSongs(artistId);
|
const result = await subsonic.topSongs(artistId);
|
||||||
@@ -4835,13 +4889,13 @@ describe("SubsonicMusicLibrary", () => {
|
|||||||
Promise.resolve(ok(getTopSongsJson([track1, track2, track3])))
|
Promise.resolve(ok(getTopSongsJson([track1, track2, track3])))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album1, [])))
|
Promise.resolve(ok(getAlbumJson(album1)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album2, [])))
|
Promise.resolve(ok(getAlbumJson(album2)))
|
||||||
)
|
)
|
||||||
.mockImplementationOnce(() =>
|
.mockImplementationOnce(() =>
|
||||||
Promise.resolve(ok(getAlbumJson(artist, album1, [])))
|
Promise.resolve(ok(getAlbumJson(album1)))
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await subsonic.topSongs(artistId);
|
const result = await subsonic.topSongs(artistId);
|
||||||
|
|||||||
Reference in New Issue
Block a user