Support for paging of albums for artist

This commit is contained in:
simojenki
2021-03-05 21:59:38 +11:00
parent 979f72206e
commit 5c75a3d50b
3 changed files with 288 additions and 139 deletions

View File

@@ -6,12 +6,7 @@ import path from "path";
import logger from "./logger"; import logger from "./logger";
import { LinkCodes } from "./link_codes"; import { LinkCodes } from "./link_codes";
import { import { Album, MusicLibrary, MusicService, slice2 } from "./music_service";
Album,
ArtistSummary,
MusicLibrary,
MusicService,
} from "./music_service";
export const LOGIN_ROUTE = "/login"; export const LOGIN_ROUTE = "/login";
export const SOAP_PATH = "/ws/sonos"; export const SOAP_PATH = "/ws/sonos";
@@ -152,7 +147,7 @@ export type Container = {
title: string; title: string;
}; };
export const container = ({ const container = ({
id, id,
title, title,
}: { }: {
@@ -164,6 +159,12 @@ export const container = ({
title, title,
}); });
const album = (album: Album) => ({
itemType: "album",
id: `album:${album.id}`,
title: album.name,
});
type SoapyHeaders = { type SoapyHeaders = {
credentials?: Credentials; credentials?: Credentials;
}; };
@@ -223,25 +224,16 @@ function bindSmapiSoapServiceToExpress(
case "root": case "root":
return getMetadataResult({ return getMetadataResult({
mediaCollection: [ mediaCollection: [
{ itemType: "container", id: "artists", title: "Artists" }, container({ id: "artists", title: "Artists" }),
{ itemType: "container", id: "albums", title: "Albums" }, container({ id: "albums", title: "Albums" }),
], ],
index: 0, index: 0,
total: 2, total: 2,
}); });
case "artists": case "artists":
return await musicLibrary return await musicLibrary.artists(paging).then((result) =>
.artists(paging)
.then(
({
results,
total,
}: {
results: ArtistSummary[];
total: number;
}) =>
getMetadataResult({ getMetadataResult({
mediaCollection: results.map((it) => ({ mediaCollection: result.results.map((it) => ({
itemType: "artist", itemType: "artist",
id: `artist:${it.id}`, id: `artist:${it.id}`,
artistId: it.id, artistId: it.id,
@@ -249,23 +241,27 @@ function bindSmapiSoapServiceToExpress(
albumArtURI: it.image.small, albumArtURI: it.image.small,
})), })),
index: paging._index, index: paging._index,
total: result.total,
})
);
case "artist":
return await musicLibrary
.artist(typeId!)
.then((artist) => artist.albums)
.then(slice2(paging))
.then(([page, total]) =>
getMetadataResult({
mediaCollection: page.map(album),
index: 0,
total, total,
}) })
); );
case "albums": case "albums":
return await musicLibrary return await musicLibrary.albums(paging).then((result) =>
.albums(paging)
.then(
({ results, total }: { results: Album[]; total: number }) =>
getMetadataResult({ getMetadataResult({
mediaCollection: results.map((it) => mediaCollection: result.results.map(album),
container({
id: `album:${it.id}`,
title: it.name,
})
),
index: paging._index, index: paging._index,
total, total: result.total,
}) })
); );
default: default:

View File

@@ -3,7 +3,7 @@ import { v4 as uuid } from "uuid";
import { Credentials } from "../src/smapi"; import { Credentials } from "../src/smapi";
import { Service, Device } from "../src/sonos"; import { Service, Device } from "../src/sonos";
import { Artist } from "../src/music_service"; import { Album, Artist } from "../src/music_service";
const randomInt = (max: number) => Math.floor(Math.random() * max); const randomInt = (max: number) => Math.floor(Math.random() * max);
const randomIpAddress = () => `127.0.${randomInt(255)}.${randomInt(255)}`; const randomIpAddress = () => `127.0.${randomInt(255)}.${randomInt(255)}`;
@@ -68,20 +68,31 @@ export function someCredentials(token: string): Credentials {
}; };
} }
export const BOB_MARLEY: Artist = { export function anArtist(fields: Partial<Artist> = {}): Artist {
id: uuid(), const id = uuid();
name: "Bob Marley", return {
albums: [ id,
{ id: uuid(), name: "Burin'", year: "1973", genre: "Reggae" }, name: `Artist ${id}`,
{ id: uuid(), name: "Exodus", year: "1977", genre: "Reggae" }, albums: [],
{ id: uuid(), name: "Kaya", year: "1978", genre: "Ska" },
],
image: { image: {
small: "http://localhost/BOB_MARLEY/sml", small: undefined,
medium: "http://localhost/BOB_MARLEY/med", medium: undefined,
large: "http://localhost/BOB_MARLEY/lge", large: undefined
}, },
}; ...fields
}
}
export function anAlbum(fields: Partial<Album> = {}): Album {
const id = uuid();
return {
id,
name: `Album ${id}`,
genre: "Metal",
year: "1900",
...fields
}
}
export const BLONDIE: Artist = { export const BLONDIE: Artist = {
id: uuid(), id: uuid(),
@@ -107,6 +118,21 @@ export const BLONDIE: Artist = {
}, },
}; };
export const BOB_MARLEY: Artist = {
id: uuid(),
name: "Bob Marley",
albums: [
{ id: uuid(), name: "Burin'", year: "1973", genre: "Reggae" },
{ id: uuid(), name: "Exodus", year: "1977", genre: "Reggae" },
{ id: uuid(), name: "Kaya", year: "1978", genre: "Ska" },
],
image: {
small: "http://localhost/BOB_MARLEY/sml",
medium: "http://localhost/BOB_MARLEY/med",
large: "http://localhost/BOB_MARLEY/lge",
},
};
export const MADONNA: Artist = { export const MADONNA: Artist = {
id: uuid(), id: uuid(),
name: "Madonna", name: "Madonna",
@@ -142,9 +168,6 @@ export const METALLICA: Artist = {
}, },
}; };
export const ALL_ALBUMS = [ export const ALL_ARTISTS = [BOB_MARLEY, BLONDIE, MADONNA, METALLICA]
...BOB_MARLEY.albums,
...BLONDIE.albums, export const ALL_ALBUMS = ALL_ARTISTS.flatMap(it => it.albums || []);
...MADONNA.albums,
...METALLICA.albums,
];

View File

@@ -6,19 +6,14 @@ import X2JS from "x2js";
import { InMemoryLinkCodes, LinkCodes } from "../src/link_codes"; import { InMemoryLinkCodes, LinkCodes } from "../src/link_codes";
import makeServer from "../src/server"; import makeServer from "../src/server";
import { bonobService, SONOS_DISABLED } from "../src/sonos"; import { bonobService, SONOS_DISABLED } from "../src/sonos";
import { import { STRINGS_ROUTE, LOGIN_ROUTE, getMetadataResult } from "../src/smapi";
STRINGS_ROUTE,
LOGIN_ROUTE,
getMetadataResult,
container,
} from "../src/smapi";
import { import {
aService, aService,
BLONDIE,
BOB_MARLEY,
getAppLinkMessage, getAppLinkMessage,
someCredentials, someCredentials,
anArtist,
anAlbum,
} from "./builders"; } from "./builders";
import { InMemoryMusicService } from "./in_memory_music_service"; import { InMemoryMusicService } from "./in_memory_music_service";
import supersoap from "./supersoap"; import supersoap from "./supersoap";
@@ -356,8 +351,8 @@ describe("api", () => {
expect(root[0]).toEqual( expect(root[0]).toEqual(
getMetadataResult({ getMetadataResult({
mediaCollection: [ mediaCollection: [
container({ id: "artists", title: "Artists" }), { itemType: "container", id: "artists", title: "Artists" },
container({ id: "albums", title: "Albums" }), { itemType: "container", id: "albums", title: "Albums" },
], ],
index: 0, index: 0,
total: 2, total: 2,
@@ -366,18 +361,90 @@ describe("api", () => {
}); });
}); });
describe("asking for artists", () => { describe("asking for a single artist", () => {
it("should return it", async () => { const artistWithManyAlbums = anArtist({
musicService.hasArtists(BLONDIE, BOB_MARLEY); albums: [
anAlbum(),
anAlbum(),
anAlbum(),
anAlbum(),
anAlbum(),
],
});
const artists = await ws.getMetadataAsync({ beforeEach(() => {
musicService.hasArtists(artistWithManyAlbums);
});
describe("asking for all albums", () => {
it("should return a collection of albums", async () => {
const result = await ws.getMetadataAsync({
id: `artist:${artistWithManyAlbums.id}`,
index: 0,
count: 100,
});
expect(result[0]).toEqual(
getMetadataResult({
mediaCollection: artistWithManyAlbums.albums.map((it) => ({
itemType: "album",
id: `album:${it.id}`,
title: it.name,
})),
index: 0,
total: artistWithManyAlbums.albums.length,
})
);
});
});
describe("asking for a page of albums", () => {
it("should return just that page", async () => {
const result = await ws.getMetadataAsync({
id: `artist:${artistWithManyAlbums.id}`,
index: 2,
count: 2,
});
expect(result[0]).toEqual(
getMetadataResult({
mediaCollection: [
artistWithManyAlbums.albums[2]!,
artistWithManyAlbums.albums[3]!,
].map((it) => ({
itemType: "album",
id: `album:${it.id}`,
title: it.name,
})),
index: 0,
total: artistWithManyAlbums.albums.length,
})
);
});
});
});
describe("asking for artists", () => {
const artists = [
anArtist(),
anArtist(),
anArtist(),
anArtist(),
anArtist(),
];
beforeEach(() => {
musicService.hasArtists(...artists);
});
describe("asking for all artists", () => {
it("should return them all", async () => {
const result = await ws.getMetadataAsync({
id: "artists", id: "artists",
index: 0, index: 0,
count: 100, count: 100,
}); });
expect(artists[0]).toEqual( expect(result[0]).toEqual(
getMetadataResult({ getMetadataResult({
mediaCollection: [BLONDIE, BOB_MARLEY].map((it) => ({ mediaCollection: artists.map((it) => ({
itemType: "artist", itemType: "artist",
id: `artist:${it.id}`, id: `artist:${it.id}`,
artistId: it.id, artistId: it.id,
@@ -385,62 +452,124 @@ describe("api", () => {
albumArtURI: it.image.small, albumArtURI: it.image.small,
})), })),
index: 0, index: 0,
total: 2, total: artists.length,
}) })
); );
}); });
}); });
describe("asking for all albums", () => { describe("asking for a page of artists", () => {
it("should return it", async () => { it("should return it", async () => {
musicService.hasArtists(BLONDIE, BOB_MARLEY); const result = await ws.getMetadataAsync({
id: "artists",
index: 1,
count: 3,
});
expect(result[0]).toEqual(
getMetadataResult({
mediaCollection: [artists[1]!, artists[2]!, artists[3]!].map(
(it) => ({
itemType: "artist",
id: `artist:${it.id}`,
artistId: it.id,
title: it.name,
albumArtURI: it.image.small,
})
),
index: 1,
total: artists.length,
})
);
});
});
});
const albums = await ws.getMetadataAsync({ // describe("asking for an album by id", () => {
// it("should return it", async () => {
// musicService.hasArtists(BLONDIE, BOB_MARLEY);
// const album = BOB_MARLEY.albums[0]!;
// const result = await ws.getMetadataAsync({
// id: `album:${album.id}`,
// index: 0,
// count: 100,
// });
// expect(result).toEqual(
// getMetadataResult({
// mediaCollection: [
// ...BLONDIE.albums,
// ...BOB_MARLEY.albums,
// ].map((it) =>
// ({ itemType: "album", id: `album:${it.id}`, title: it.name })
// ),
// index: 0,
// total: BLONDIE.albums.length + BOB_MARLEY.albums.length,
// })
// );
// });
// });
describe("asking for albums", () => {
const artist1 = anArtist({
albums: [anAlbum(), anAlbum(), anAlbum()],
});
const artist2 = anArtist({
albums: [anAlbum(), anAlbum()],
});
const artist3 = anArtist({
albums: [],
});
const artist4 = anArtist({
albums: [anAlbum()],
});
beforeEach(() => {
musicService.hasArtists(artist1, artist2, artist3, artist4);
});
describe("asking for all albums", () => {
it("should return them all", async () => {
const result = await ws.getMetadataAsync({
id: "albums", id: "albums",
index: 0, index: 0,
count: 100, count: 100,
}); });
expect(albums[0]).toEqual( expect(result[0]).toEqual(
getMetadataResult({ getMetadataResult({
mediaCollection: [ mediaCollection: [artist1, artist2, artist3, artist4]
...BLONDIE.albums, .flatMap((it) => it.albums)
...BOB_MARLEY.albums, .map((it) => ({
].map((it) => itemType: "album",
container({ id: `album:${it.id}`, title: it.name }) id: `album:${it.id}`,
), title: it.name,
})),
index: 0, index: 0,
total: BLONDIE.albums.length + BOB_MARLEY.albums.length, total: 6,
}) })
); );
}); });
}); });
describe("asking for albums with paging", () => { describe("asking for a page of albums", () => {
it("should return it", async () => { it("should return only that page", async () => {
musicService.hasArtists(BLONDIE, BOB_MARLEY); const result = await ws.getMetadataAsync({
expect(BLONDIE.albums.length).toEqual(2);
expect(BOB_MARLEY.albums.length).toEqual(3);
const albums = await ws.getMetadataAsync({
id: "albums", id: "albums",
index: 2, index: 2,
count: 2, count: 3,
}); });
expect(albums[0]).toEqual( expect(result[0]).toEqual(
getMetadataResult({ getMetadataResult({
mediaCollection: [ mediaCollection: [
container({ artist1.albums[2]!,
id: `album:${BOB_MARLEY.albums[0]!.id}`, artist2.albums[0]!,
title: BOB_MARLEY.albums[0]!.name, artist2.albums[1]!,
}), ].map((it) => ({
container({ itemType: "album",
id: `album:${BOB_MARLEY.albums[1]!.id}`, id: `album:${it.id}`,
title: BOB_MARLEY.albums[1]!.name, title: it.name,
}), })),
],
index: 2, index: 2,
total: BLONDIE.albums.length + BOB_MARLEY.albums.length, total: 6,
}) })
); );
}); });
@@ -448,4 +577,5 @@ describe("api", () => {
}); });
}); });
}); });
});
}); });