mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-21 17:33:29 +01:00
Ability to browse Random Albums
This commit is contained in:
@@ -6,6 +6,14 @@ In theory as Navidrome implements the subsonic API, it *may* work with other sub
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Features
|
||||||
|
- Integrates with Navidrome
|
||||||
|
- Browse by Artist, Albums, Genres, Random
|
||||||
|
- Artist Art
|
||||||
|
- Album Art
|
||||||
|
- View Related Artists via Artist -> '...' -> Menu -> Related Arists
|
||||||
|
- Track scrobbling
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
bonob is ditributed via docker and can be run in a number of ways
|
bonob is ditributed via docker and can be run in a number of ways
|
||||||
|
|||||||
@@ -94,7 +94,10 @@ export const asResult = <T>([results, total]: [T[], number]) => ({
|
|||||||
|
|
||||||
export type ArtistQuery = Paging;
|
export type ArtistQuery = Paging;
|
||||||
|
|
||||||
|
export type AlbumQueryType = 'alphabeticalByArtist' | 'byGenre' | 'random' | 'recent' | 'frequent';
|
||||||
|
|
||||||
export type AlbumQuery = Paging & {
|
export type AlbumQuery = Paging & {
|
||||||
|
type: AlbumQueryType;
|
||||||
genre?: string;
|
genre?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ import {
|
|||||||
} from "./music_service";
|
} from "./music_service";
|
||||||
import X2JS from "x2js";
|
import X2JS from "x2js";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
|
import { pick } from "underscore";
|
||||||
|
|
||||||
import axios, { AxiosRequestConfig } from "axios";
|
import axios, { AxiosRequestConfig } from "axios";
|
||||||
import { Encryption } from "./encryption";
|
import { Encryption } from "./encryption";
|
||||||
import randomString from "./random_string";
|
import randomString from "./random_string";
|
||||||
import { fold } from "fp-ts/lib/Option";
|
|
||||||
|
|
||||||
export const BROWSER_HEADERS = {
|
export const BROWSER_HEADERS = {
|
||||||
accept:
|
accept:
|
||||||
@@ -391,17 +391,9 @@ export class Navidrome implements MusicService {
|
|||||||
albums: (q: AlbumQuery): Promise<Result<AlbumSummary>> =>
|
albums: (q: AlbumQuery): Promise<Result<AlbumSummary>> =>
|
||||||
navidrome
|
navidrome
|
||||||
.getJSON<GetAlbumListResponse>(credentials, "/rest/getAlbumList", {
|
.getJSON<GetAlbumListResponse>(credentials, "/rest/getAlbumList", {
|
||||||
...fold(
|
...pick(q, 'type', 'genre'),
|
||||||
() => ({
|
size: Math.min(MAX_ALBUM_LIST, q._count),
|
||||||
type: "alphabeticalByArtist",
|
offset: q._index,
|
||||||
}),
|
|
||||||
(genre) => ({
|
|
||||||
type: "byGenre",
|
|
||||||
genre,
|
|
||||||
})
|
|
||||||
)(O.fromNullable(q.genre)),
|
|
||||||
size: MAX_ALBUM_LIST,
|
|
||||||
offset: 0,
|
|
||||||
})
|
})
|
||||||
.then((response) => response.albumList.album || [])
|
.then((response) => response.albumList.album || [])
|
||||||
.then((albumList) =>
|
.then((albumList) =>
|
||||||
|
|||||||
60
src/smapi.ts
60
src/smapi.ts
@@ -449,9 +449,10 @@ function bindSmapiSoapServiceToExpress(
|
|||||||
container({ id: "artists", title: "Artists" }),
|
container({ id: "artists", title: "Artists" }),
|
||||||
container({ id: "albums", title: "Albums" }),
|
container({ id: "albums", title: "Albums" }),
|
||||||
container({ id: "genres", title: "Genres" }),
|
container({ id: "genres", title: "Genres" }),
|
||||||
|
container({ id: "randomAlbums", title: "Random" }),
|
||||||
],
|
],
|
||||||
index: 0,
|
index: 0,
|
||||||
total: 3,
|
total: 4,
|
||||||
});
|
});
|
||||||
case "artists":
|
case "artists":
|
||||||
return await musicLibrary.artists(paging).then((result) => {
|
return await musicLibrary.artists(paging).then((result) => {
|
||||||
@@ -465,16 +466,31 @@ function bindSmapiSoapServiceToExpress(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
case "albums":
|
case "albums":
|
||||||
return await musicLibrary.albums(paging).then((result) => {
|
return await musicLibrary
|
||||||
const accessToken = accessTokens.mint(authToken);
|
.albums({ type: "alphabeticalByArtist", ...paging })
|
||||||
return getMetadataResult({
|
.then((result) => {
|
||||||
mediaCollection: result.results.map((it) =>
|
const accessToken = accessTokens.mint(authToken);
|
||||||
album(webAddress, accessToken, it)
|
return getMetadataResult({
|
||||||
),
|
mediaCollection: result.results.map((it) =>
|
||||||
index: paging._index,
|
album(webAddress, accessToken, it)
|
||||||
total: result.total,
|
),
|
||||||
|
index: paging._index,
|
||||||
|
total: result.total,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
case "randomAlbums":
|
||||||
|
return await musicLibrary
|
||||||
|
.albums({ type: "random", ...paging })
|
||||||
|
.then((result) => {
|
||||||
|
const accessToken = accessTokens.mint(authToken);
|
||||||
|
return getMetadataResult({
|
||||||
|
mediaCollection: result.results.map((it) =>
|
||||||
|
album(webAddress, accessToken, it)
|
||||||
|
),
|
||||||
|
index: paging._index,
|
||||||
|
total: result.total,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
case "genres":
|
case "genres":
|
||||||
return await musicLibrary
|
return await musicLibrary
|
||||||
.genres()
|
.genres()
|
||||||
@@ -530,18 +546,20 @@ function bindSmapiSoapServiceToExpress(
|
|||||||
total,
|
total,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
case "genre":
|
case "genre":
|
||||||
return await musicLibrary.albums({ ...paging, genre: typeId }).then((result) => {
|
return await musicLibrary
|
||||||
const accessToken = accessTokens.mint(authToken);
|
.albums({ type: "byGenre", genre: typeId, ...paging })
|
||||||
return getMetadataResult({
|
.then((result) => {
|
||||||
mediaCollection: result.results.map((it) =>
|
const accessToken = accessTokens.mint(authToken);
|
||||||
album(webAddress, accessToken, it)
|
return getMetadataResult({
|
||||||
),
|
mediaCollection: result.results.map((it) =>
|
||||||
index: paging._index,
|
album(webAddress, accessToken, it)
|
||||||
total: result.total,
|
),
|
||||||
});
|
index: paging._index,
|
||||||
|
total: result.total,
|
||||||
});
|
});
|
||||||
default:
|
});
|
||||||
|
default:
|
||||||
throw `Unsupported id:${id}`;
|
throw `Unsupported id:${id}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,7 +6,16 @@ import {
|
|||||||
albumToAlbumSummary,
|
albumToAlbumSummary,
|
||||||
} from "../src/music_service";
|
} from "../src/music_service";
|
||||||
import { v4 as uuid } from "uuid";
|
import { v4 as uuid } from "uuid";
|
||||||
import { anArtist, anAlbum, aTrack, POP, ROCK, METAL, HIP_HOP, SKA } from "./builders";
|
import {
|
||||||
|
anArtist,
|
||||||
|
anAlbum,
|
||||||
|
aTrack,
|
||||||
|
POP,
|
||||||
|
ROCK,
|
||||||
|
METAL,
|
||||||
|
HIP_HOP,
|
||||||
|
SKA,
|
||||||
|
} from "./builders";
|
||||||
|
|
||||||
describe("InMemoryMusicService", () => {
|
describe("InMemoryMusicService", () => {
|
||||||
const service = new InMemoryMusicService();
|
const service = new InMemoryMusicService();
|
||||||
@@ -147,7 +156,6 @@ describe("InMemoryMusicService", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe("tracks", () => {
|
describe("tracks", () => {
|
||||||
const artist1Album1 = anAlbum();
|
const artist1Album1 = anAlbum();
|
||||||
const artist1Album2 = anAlbum();
|
const artist1Album2 = anAlbum();
|
||||||
@@ -201,8 +209,6 @@ describe("InMemoryMusicService", () => {
|
|||||||
const artist3_album1 = anAlbum({ genre: HIP_HOP });
|
const artist3_album1 = anAlbum({ genre: HIP_HOP });
|
||||||
const artist3_album2 = anAlbum({ genre: POP });
|
const artist3_album2 = anAlbum({ genre: POP });
|
||||||
|
|
||||||
const totalAlbumCount = 8;
|
|
||||||
|
|
||||||
const artist1 = anArtist({
|
const artist1 = anArtist({
|
||||||
albums: [
|
albums: [
|
||||||
artist1_album1,
|
artist1_album1,
|
||||||
@@ -216,16 +222,56 @@ describe("InMemoryMusicService", () => {
|
|||||||
const artist3 = anArtist({ albums: [artist3_album1, artist3_album2] });
|
const artist3 = anArtist({ albums: [artist3_album1, artist3_album2] });
|
||||||
const artistWithNoAlbums = anArtist({ albums: [] });
|
const artistWithNoAlbums = anArtist({ albums: [] });
|
||||||
|
|
||||||
|
const allAlbums = [artist1, artist2, artist3, artistWithNoAlbums].flatMap(
|
||||||
|
(it) => it.albums
|
||||||
|
);
|
||||||
|
const totalAlbumCount = allAlbums.length;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
service.hasArtists(artist1, artist2, artist3, artistWithNoAlbums);
|
service.hasArtists(artist1, artist2, artist3, artistWithNoAlbums);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("fetching random albums", () => {
|
||||||
|
describe("with no paging", () => {
|
||||||
|
it("should return all albums for all the artists in a random order", async () => {
|
||||||
|
const albums = await musicLibrary.albums({
|
||||||
|
_index: 0,
|
||||||
|
_count: 100,
|
||||||
|
type: "random",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(albums.total).toEqual(totalAlbumCount);
|
||||||
|
expect(albums.results.map((it) => it.id).sort()).toEqual(
|
||||||
|
allAlbums.map((it) => it.id).sort()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("with no paging", () => {
|
||||||
|
it("should return only a page of results", async () => {
|
||||||
|
const albums = await musicLibrary.albums({
|
||||||
|
_index: 2,
|
||||||
|
_count: 3,
|
||||||
|
type: "random",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(albums.total).toEqual(totalAlbumCount);
|
||||||
|
expect(albums.results.length).toEqual(3)
|
||||||
|
// cannot really assert the results and they will change every time
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("fetching multiple albums", () => {
|
describe("fetching multiple albums", () => {
|
||||||
describe("with no filtering", () => {
|
describe("with no filtering", () => {
|
||||||
describe("fetching all on one page", () => {
|
describe("fetching all on one page", () => {
|
||||||
it("should return all the albums for all the artists", async () => {
|
it("should return all the albums for all the artists", async () => {
|
||||||
expect(
|
expect(
|
||||||
await musicLibrary.albums({ _index: 0, _count: 100 })
|
await musicLibrary.albums({
|
||||||
|
_index: 0,
|
||||||
|
_count: 100,
|
||||||
|
type: "alphabeticalByArtist",
|
||||||
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
results: [
|
results: [
|
||||||
albumToAlbumSummary(artist1_album1),
|
albumToAlbumSummary(artist1_album1),
|
||||||
@@ -246,23 +292,31 @@ describe("InMemoryMusicService", () => {
|
|||||||
|
|
||||||
describe("fetching a page", () => {
|
describe("fetching a page", () => {
|
||||||
it("should return only that page", async () => {
|
it("should return only that page", async () => {
|
||||||
expect(await musicLibrary.albums({ _index: 4, _count: 3 })).toEqual(
|
expect(
|
||||||
{
|
await musicLibrary.albums({
|
||||||
results: [
|
_index: 4,
|
||||||
albumToAlbumSummary(artist1_album5),
|
_count: 3,
|
||||||
albumToAlbumSummary(artist2_album1),
|
type: "alphabeticalByArtist",
|
||||||
albumToAlbumSummary(artist3_album1),
|
})
|
||||||
],
|
).toEqual({
|
||||||
total: totalAlbumCount,
|
results: [
|
||||||
}
|
albumToAlbumSummary(artist1_album5),
|
||||||
);
|
albumToAlbumSummary(artist2_album1),
|
||||||
|
albumToAlbumSummary(artist3_album1),
|
||||||
|
],
|
||||||
|
total: totalAlbumCount,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("fetching the last page", () => {
|
describe("fetching the last page", () => {
|
||||||
it("should return only that page", async () => {
|
it("should return only that page", async () => {
|
||||||
expect(
|
expect(
|
||||||
await musicLibrary.albums({ _index: 6, _count: 100 })
|
await musicLibrary.albums({
|
||||||
|
_index: 6,
|
||||||
|
_count: 100,
|
||||||
|
type: "alphabeticalByArtist",
|
||||||
|
})
|
||||||
).toEqual({
|
).toEqual({
|
||||||
results: [
|
results: [
|
||||||
albumToAlbumSummary(artist3_album1),
|
albumToAlbumSummary(artist3_album1),
|
||||||
@@ -279,6 +333,7 @@ describe("InMemoryMusicService", () => {
|
|||||||
it("should return all the albums of that genre for all the artists", async () => {
|
it("should return all the albums of that genre for all the artists", async () => {
|
||||||
expect(
|
expect(
|
||||||
await musicLibrary.albums({
|
await musicLibrary.albums({
|
||||||
|
type: "byGenre",
|
||||||
genre: POP.id,
|
genre: POP.id,
|
||||||
_index: 0,
|
_index: 0,
|
||||||
_count: 100,
|
_count: 100,
|
||||||
@@ -300,6 +355,7 @@ describe("InMemoryMusicService", () => {
|
|||||||
it("should return only the albums for that page", async () => {
|
it("should return only the albums for that page", async () => {
|
||||||
expect(
|
expect(
|
||||||
await musicLibrary.albums({
|
await musicLibrary.albums({
|
||||||
|
type: "byGenre",
|
||||||
genre: POP.id,
|
genre: POP.id,
|
||||||
_index: 1,
|
_index: 1,
|
||||||
_count: 2,
|
_count: 2,
|
||||||
@@ -318,6 +374,7 @@ describe("InMemoryMusicService", () => {
|
|||||||
it("should return only the albums for the last page", async () => {
|
it("should return only the albums for the last page", async () => {
|
||||||
expect(
|
expect(
|
||||||
await musicLibrary.albums({
|
await musicLibrary.albums({
|
||||||
|
type: "byGenre",
|
||||||
genre: POP.id,
|
genre: POP.id,
|
||||||
_index: 3,
|
_index: 3,
|
||||||
_count: 100,
|
_count: 100,
|
||||||
@@ -333,6 +390,7 @@ describe("InMemoryMusicService", () => {
|
|||||||
it("should return empty list if there are no albums for the genre", async () => {
|
it("should return empty list if there are no albums for the genre", async () => {
|
||||||
expect(
|
expect(
|
||||||
await musicLibrary.albums({
|
await musicLibrary.albums({
|
||||||
|
type: "byGenre",
|
||||||
genre: "genre with no albums",
|
genre: "genre with no albums",
|
||||||
_index: 0,
|
_index: 0,
|
||||||
_count: 100,
|
_count: 100,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import * as A from "fp-ts/Array";
|
|||||||
import { fromEquals } from "fp-ts/lib/Eq";
|
import { fromEquals } from "fp-ts/lib/Eq";
|
||||||
import { pipe } from "fp-ts/lib/function";
|
import { pipe } from "fp-ts/lib/function";
|
||||||
import { ordString, fromCompare } from "fp-ts/lib/Ord";
|
import { ordString, fromCompare } from "fp-ts/lib/Ord";
|
||||||
|
import { shuffle } from "underscore";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
MusicService,
|
MusicService,
|
||||||
@@ -17,17 +18,10 @@ import {
|
|||||||
asResult,
|
asResult,
|
||||||
artistToArtistSummary,
|
artistToArtistSummary,
|
||||||
albumToAlbumSummary,
|
albumToAlbumSummary,
|
||||||
Album,
|
|
||||||
Track,
|
Track,
|
||||||
Genre,
|
Genre,
|
||||||
} from "../src/music_service";
|
} from "../src/music_service";
|
||||||
|
|
||||||
type P<T> = (t: T) => boolean;
|
|
||||||
const all: P<any> = (_: any) => true;
|
|
||||||
|
|
||||||
const albumWithGenre = (genreId: string): P<[Artist, Album]> => ([_, album]) =>
|
|
||||||
album.genre?.id === genreId;
|
|
||||||
|
|
||||||
export class InMemoryMusicService implements MusicService {
|
export class InMemoryMusicService implements MusicService {
|
||||||
users: Record<string, string> = {};
|
users: Record<string, string> = {};
|
||||||
artists: Artist[] = [];
|
artists: Artist[] = [];
|
||||||
@@ -75,17 +69,25 @@ export class InMemoryMusicService implements MusicService {
|
|||||||
),
|
),
|
||||||
albums: (q: AlbumQuery) =>
|
albums: (q: AlbumQuery) =>
|
||||||
Promise.resolve(
|
Promise.resolve(
|
||||||
this.artists
|
this.artists.flatMap((artist) =>
|
||||||
.flatMap((artist) => artist.albums.map((album) => [artist, album]))
|
artist.albums.map((album) => ({ artist, album }))
|
||||||
.filter(
|
)
|
||||||
pipe(
|
|
||||||
O.fromNullable(q.genre),
|
|
||||||
O.map(albumWithGenre),
|
|
||||||
O.getOrElse(() => all)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.then((matches) => matches.map(([_, album]) => album as Album))
|
.then((artist2Album) => {
|
||||||
|
switch (q.type) {
|
||||||
|
case "alphabeticalByArtist":
|
||||||
|
return artist2Album;
|
||||||
|
case "byGenre":
|
||||||
|
return artist2Album.filter(
|
||||||
|
(it) => it.album.genre?.id === q.genre
|
||||||
|
);
|
||||||
|
case "random":
|
||||||
|
return shuffle(artist2Album);
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((matches) => matches.map((it) => it.album))
|
||||||
.then((it) => it.map(albumToAlbumSummary))
|
.then((it) => it.map(albumToAlbumSummary))
|
||||||
.then(slice2(q))
|
.then(slice2(q))
|
||||||
.then(asResult),
|
.then(asResult),
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import {
|
|||||||
AlbumSummary,
|
AlbumSummary,
|
||||||
artistToArtistSummary,
|
artistToArtistSummary,
|
||||||
NO_IMAGES,
|
NO_IMAGES,
|
||||||
|
AlbumQuery,
|
||||||
} from "../src/music_service";
|
} from "../src/music_service";
|
||||||
import { anAlbum, anArtist, aTrack } from "./builders";
|
import { anAlbum, anArtist, aTrack } from "./builders";
|
||||||
|
|
||||||
@@ -867,7 +868,7 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should pass the filter to navidrome", async () => {
|
it("should pass the filter to navidrome", async () => {
|
||||||
const q = { _index: 0, _count: 500, genre: "Pop" };
|
const q: AlbumQuery = { _index: 0, _count: 500, genre: "Pop", type: 'byGenre' };
|
||||||
const result = await navidrome
|
const result = await navidrome
|
||||||
.generateToken({ username, password })
|
.generateToken({ username, password })
|
||||||
.then((it) => it as AuthSuccess)
|
.then((it) => it as AuthSuccess)
|
||||||
@@ -910,12 +911,12 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return the album", async () => {
|
it("should return the album", async () => {
|
||||||
const paging = { _index: 0, _count: 500 };
|
const q: AlbumQuery = { _index: 0, _count: 500, type: 'alphabeticalByArtist' };
|
||||||
const result = await navidrome
|
const result = await navidrome
|
||||||
.generateToken({ username, password })
|
.generateToken({ username, password })
|
||||||
.then((it) => it as AuthSuccess)
|
.then((it) => it as AuthSuccess)
|
||||||
.then((it) => navidrome.login(it.authToken))
|
.then((it) => navidrome.login(it.authToken))
|
||||||
.then((it) => it.albums(paging));
|
.then((it) => it.albums(q));
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
results: albums,
|
results: albums,
|
||||||
@@ -951,12 +952,12 @@ describe("Navidrome", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should return the album", async () => {
|
it("should return the album", async () => {
|
||||||
const paging = { _index: 0, _count: 500 };
|
const q: AlbumQuery = { _index: 0, _count: 500, type: 'alphabeticalByArtist' };
|
||||||
const result = await navidrome
|
const result = await navidrome
|
||||||
.generateToken({ username, password })
|
.generateToken({ username, password })
|
||||||
.then((it) => it as AuthSuccess)
|
.then((it) => it as AuthSuccess)
|
||||||
.then((it) => navidrome.login(it.authToken))
|
.then((it) => navidrome.login(it.authToken))
|
||||||
.then((it) => it.albums(paging));
|
.then((it) => it.albums(q));
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
results: albums,
|
results: albums,
|
||||||
@@ -1001,12 +1002,12 @@ describe("Navidrome", () => {
|
|||||||
|
|
||||||
describe("querying for all of them", () => {
|
describe("querying for all of them", () => {
|
||||||
it("should return all of them with corrent paging information", async () => {
|
it("should return all of them with corrent paging information", async () => {
|
||||||
const paging = { _index: 0, _count: 500 };
|
const q : AlbumQuery= { _index: 0, _count: 500, type: 'alphabeticalByArtist' };
|
||||||
const result = await navidrome
|
const result = await navidrome
|
||||||
.generateToken({ username, password })
|
.generateToken({ username, password })
|
||||||
.then((it) => it as AuthSuccess)
|
.then((it) => it as AuthSuccess)
|
||||||
.then((it) => navidrome.login(it.authToken))
|
.then((it) => navidrome.login(it.authToken))
|
||||||
.then((it) => it.albums(paging));
|
.then((it) => it.albums(q));
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
results: albums,
|
results: albums,
|
||||||
@@ -1027,12 +1028,12 @@ describe("Navidrome", () => {
|
|||||||
|
|
||||||
describe("querying for a page of them", () => {
|
describe("querying for a page of them", () => {
|
||||||
it("should return the page with the corrent paging information", async () => {
|
it("should return the page with the corrent paging information", async () => {
|
||||||
const paging = { _index: 2, _count: 2 };
|
const q : AlbumQuery = { _index: 2, _count: 2, type: 'alphabeticalByArtist' };
|
||||||
const result = await navidrome
|
const result = await navidrome
|
||||||
.generateToken({ username, password })
|
.generateToken({ username, password })
|
||||||
.then((it) => it as AuthSuccess)
|
.then((it) => it as AuthSuccess)
|
||||||
.then((it) => navidrome.login(it.authToken))
|
.then((it) => navidrome.login(it.authToken))
|
||||||
.then((it) => it.albums(paging));
|
.then((it) => it.albums(q));
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
results: [albums[2], albums[3]],
|
results: [albums[2], albums[3]],
|
||||||
@@ -1042,8 +1043,8 @@ describe("Navidrome", () => {
|
|||||||
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getAlbumList`, {
|
expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getAlbumList`, {
|
||||||
params: {
|
params: {
|
||||||
type: "alphabeticalByArtist",
|
type: "alphabeticalByArtist",
|
||||||
size: 500,
|
size: 2,
|
||||||
offset: 0,
|
offset: 2,
|
||||||
...authParams,
|
...authParams,
|
||||||
},
|
},
|
||||||
headers,
|
headers,
|
||||||
@@ -1079,12 +1080,12 @@ describe("Navidrome", () => {
|
|||||||
|
|
||||||
describe("querying for all of them", () => {
|
describe("querying for all of them", () => {
|
||||||
it("will return only the first 500 with the correct paging information", async () => {
|
it("will return only the first 500 with the correct paging information", async () => {
|
||||||
const paging = { _index: 0, _count: 1000 };
|
const q: AlbumQuery = { _index: 0, _count: 1000, type: 'alphabeticalByArtist' };
|
||||||
const result = await navidrome
|
const result = await navidrome
|
||||||
.generateToken({ username, password })
|
.generateToken({ username, password })
|
||||||
.then((it) => it as AuthSuccess)
|
.then((it) => it as AuthSuccess)
|
||||||
.then((it) => navidrome.login(it.authToken))
|
.then((it) => navidrome.login(it.authToken))
|
||||||
.then((it) => it.albums(paging));
|
.then((it) => it.albums(q));
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
results: first500Albums.map(albumToAlbumSummary),
|
results: first500Albums.map(albumToAlbumSummary),
|
||||||
|
|||||||
@@ -556,9 +556,10 @@ describe("api", () => {
|
|||||||
{ itemType: "container", id: "artists", title: "Artists" },
|
{ itemType: "container", id: "artists", title: "Artists" },
|
||||||
{ itemType: "container", id: "albums", title: "Albums" },
|
{ itemType: "container", id: "albums", title: "Albums" },
|
||||||
{ itemType: "container", id: "genres", title: "Genres" },
|
{ itemType: "container", id: "genres", title: "Genres" },
|
||||||
|
{ itemType: "container", id: "randomAlbums", title: "Random" },
|
||||||
],
|
],
|
||||||
index: 0,
|
index: 0,
|
||||||
total: 3,
|
total: 4,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -889,6 +890,19 @@ describe("api", () => {
|
|||||||
musicService.hasArtists(artist1, artist2, artist3, artist4);
|
musicService.hasArtists(artist1, artist2, artist3, artist4);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("asking for random albums", () => {
|
||||||
|
it("should return some", async () => {
|
||||||
|
const result = await ws.getMetadataAsync({
|
||||||
|
id: "randomAlbums",
|
||||||
|
index: 0,
|
||||||
|
count: 100,
|
||||||
|
});
|
||||||
|
expect(result[0].getMetadataResult.index).toEqual(0);
|
||||||
|
expect(result[0].getMetadataResult.count).toEqual(6);
|
||||||
|
expect(result[0].getMetadataResult.total).toEqual(6);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("asking for all albums", () => {
|
describe("asking for all albums", () => {
|
||||||
it("should return them all", async () => {
|
it("should return them all", async () => {
|
||||||
const result = await ws.getMetadataAsync({
|
const result = await ws.getMetadataAsync({
|
||||||
|
|||||||
Reference in New Issue
Block a user