diff --git a/src/music_service.ts b/src/music_service.ts index 11b550c..aec11ca 100644 --- a/src/music_service.ts +++ b/src/music_service.ts @@ -42,8 +42,21 @@ export type Paging = { _count?: number; }; +export type Result = { + results: T[], + total: number +} + +export function slice2({ _index, _count }: Paging) { + const i0 = _index || 0; + const i1 = _count ? i0 + _count : undefined; + return (things: T[]): [T[], number] => [things.slice(i0, i1), things.length] +} + +export const asResult = ([results, total]: [T[], number]) => ({ results, total }) + export interface MusicLibrary { - artists({ _index, _count }: Paging): Promise<[Artist[], number]>; + artists({ _index, _count }: Paging): Promise>; artist(id: string): Artist; albums({ artistId, @@ -51,5 +64,5 @@ export interface MusicLibrary { _count, }: { artistId?: string; - } & Paging): Promise<[Album[], number]>; + } & Paging): Promise>; } diff --git a/src/navidrome.ts b/src/navidrome.ts index 48ca91d..984801c 100644 --- a/src/navidrome.ts +++ b/src/navidrome.ts @@ -1,5 +1,5 @@ import { Md5 } from "ts-md5/dist/md5"; -import { Credentials, MusicService, Paging, Album, Artist } from "./music_service"; +import { Credentials, MusicService, Paging, Album, Artist, Result, slice2, asResult } from "./music_service"; import X2JS from "x2js"; import axios from "axios"; @@ -98,18 +98,15 @@ export class Navidrome implements MusicService { const navidrome = this; const credentials: Credentials = this.parseToken(token); return Promise.resolve({ - artists: ({ _index, _count }: Paging): Promise<[Artist[], number]> => + artists: (paging: Paging): Promise> => navidrome .get(credentials, "/rest/getArtists") .then((it) => it.artists.index.flatMap((it) => it.artist)) .then((artists) => artists.map((it) => ({ id: it._id, name: it._name })) ) - .then((artists) => { - const i0 = _index || 0; - const i1 = _count ? i0 + _count : undefined; - return [artists.slice(i0, i1), artists.length]; - }), + .then(slice2(paging)) + .then(asResult), artist: (id: string) => ({ id, name: id, @@ -118,9 +115,9 @@ export class Navidrome implements MusicService { artistId, }: { artistId?: string; - } & Paging): Promise<[Album[], number]> => { + } & Paging): Promise> => { console.log(artistId); - return Promise.resolve([[], 0]); + return Promise.resolve({ results: [], total: 0}); }, }); } diff --git a/src/smapi.ts b/src/smapi.ts index fc8a001..f83d0ab 100644 --- a/src/smapi.ts +++ b/src/smapi.ts @@ -6,7 +6,7 @@ import path from "path"; import logger from "./logger"; import { LinkCodes } from "./link_codes"; -import { MusicLibrary, MusicService } from "./music_service"; +import { Artist, MusicLibrary, MusicService } from "./music_service"; export const LOGIN_ROUTE = "/login"; export const SOAP_PATH = "/ws/sonos"; @@ -227,9 +227,9 @@ function bindSmapiSoapServiceToExpress( case "artists": return await musicLibrary .artists(paging) - .then(([artists, total]) => + .then(({ results, total }: { results: Artist[], total: number}) => getMetadataResult({ - mediaCollection: artists.map((it) => + mediaCollection: results.map((it) => container({ id: `artist:${it.id}`, title: it.name, @@ -242,9 +242,9 @@ function bindSmapiSoapServiceToExpress( case "albums": return await musicLibrary .albums(paging) - .then(([albums, total]) => + .then(({ results, total }: { results: Artist[], total: number}) => getMetadataResult({ - mediaCollection: albums.map((it) => + mediaCollection: results.map((it) => container({ id: `album:${it.id}`, title: it.name, diff --git a/tests/in_memory_music_service.test.ts b/tests/in_memory_music_service.test.ts index bb4911f..7d30482 100644 --- a/tests/in_memory_music_service.test.ts +++ b/tests/in_memory_music_service.test.ts @@ -66,7 +66,10 @@ describe("InMemoryMusicService", () => { { id: BLONDIE.id, name: BLONDIE.name }, { id: METALLICA.id, name: METALLICA.name }, ]; - expect(await musicLibrary.artists({})).toEqual([artists, 4]); + expect(await musicLibrary.artists({})).toEqual({ + results: artists, + total: 4, + }); }); }); @@ -76,10 +79,10 @@ describe("InMemoryMusicService", () => { { id: BLONDIE.id, name: BLONDIE.name }, { id: METALLICA.id, name: METALLICA.name }, ]; - expect(await musicLibrary.artists({ _index: 2, _count: 2 })).toEqual([ - artists, - 4, - ]); + expect(await musicLibrary.artists({ _index: 2, _count: 2 })).toEqual({ + results: artists, + total: 4, + }); }); }); @@ -92,7 +95,7 @@ describe("InMemoryMusicService", () => { ]; expect( await musicLibrary.artists({ _index: 1, _count: 50 }) - ).toEqual([artists, 4]); + ).toEqual({ results: artists, total: 4 }); }); }); }); @@ -123,33 +126,33 @@ describe("InMemoryMusicService", () => { describe("albums", () => { describe("fetching with no filtering", () => { it("should return all the albums for all the artists", async () => { - expect(await musicLibrary.albums({})).toEqual([ - ALL_ALBUMS, - ALL_ALBUMS.length, - ]); + expect(await musicLibrary.albums({})).toEqual({ + results: ALL_ALBUMS, + total: ALL_ALBUMS.length, + }); }); }); describe("fetching for a single artist", () => { it("should return them all if the artist has some", async () => { - expect(await musicLibrary.albums({ artistId: BLONDIE.id })).toEqual([ - BLONDIE.albums, - BLONDIE.albums.length, - ]); + expect(await musicLibrary.albums({ artistId: BLONDIE.id })).toEqual({ + results: BLONDIE.albums, + total: BLONDIE.albums.length, + }); }); it("should return empty list of the artists does not have any", async () => { - expect(await musicLibrary.albums({ artistId: MADONNA.id })).toEqual([ - [], - 0, - ]); + expect(await musicLibrary.albums({ artistId: MADONNA.id })).toEqual({ + results: [], + total: 0, + }); }); it("should return empty list if the artist id is not valid", async () => { - expect(await musicLibrary.albums({ artistId: uuid() })).toEqual([ - [], - 0, - ]); + expect(await musicLibrary.albums({ artistId: uuid() })).toEqual({ + results: [], + total: 0, + }); }); }); @@ -161,10 +164,10 @@ describe("InMemoryMusicService", () => { ...MADONNA.albums, ...METALLICA.albums, ]; - expect(await musicLibrary.albums({ _index: 2 })).toEqual([ - albums, - ALL_ALBUMS.length, - ]); + expect(await musicLibrary.albums({ _index: 2 })).toEqual({ + results: albums, + total: ALL_ALBUMS.length, + }); }); }); @@ -175,33 +178,33 @@ describe("InMemoryMusicService", () => { BOB_MARLEY.albums[1], BOB_MARLEY.albums[2], ]; - expect(await musicLibrary.albums({ _count: 3 })).toEqual([ - albums, - ALL_ALBUMS.length, - ]); + expect(await musicLibrary.albums({ _count: 3 })).toEqual({ + results: albums, + total: ALL_ALBUMS.length, + }); }); }); describe("fetching with index and count", () => { it("should be able to return the first page", async () => { const albums = [BOB_MARLEY.albums[0], BOB_MARLEY.albums[1]]; - expect(await musicLibrary.albums({ _index: 0, _count: 2 })).toEqual([ - albums, - ALL_ALBUMS.length, - ]); + expect(await musicLibrary.albums({ _index: 0, _count: 2 })).toEqual({ + results: albums, + total: ALL_ALBUMS.length, + }); }); it("should be able to return the second page", async () => { const albums = [BOB_MARLEY.albums[2], BLONDIE.albums[0]]; - expect(await musicLibrary.albums({ _index: 2, _count: 2 })).toEqual([ - albums, - ALL_ALBUMS.length, - ]); + expect(await musicLibrary.albums({ _index: 2, _count: 2 })).toEqual({ + results: albums, + total: ALL_ALBUMS.length, + }); }); it("should be able to return the last page", async () => { - expect(await musicLibrary.albums({ _index: 5, _count: 2 })).toEqual([ - METALLICA.albums, - ALL_ALBUMS.length, - ]); + expect(await musicLibrary.albums({ _index: 5, _count: 2 })).toEqual({ + results: METALLICA.albums, + total: ALL_ALBUMS.length, + }); }); }); }); diff --git a/tests/in_memory_music_service.ts b/tests/in_memory_music_service.ts index dc39046..edc6b80 100644 --- a/tests/in_memory_music_service.ts +++ b/tests/in_memory_music_service.ts @@ -10,6 +10,8 @@ import { Artist, MusicLibrary, Paging, + slice2, + asResult, } from "../src/music_service"; const artistWithAlbumsToArtist = (it: ArtistWithAlbums): Artist => ({ @@ -55,12 +57,10 @@ export class InMemoryMusicService implements MusicService { if (this.users[credentials.username] != credentials.password) return Promise.reject("Invalid auth token"); return Promise.resolve({ - artists: ({ _index, _count }: Paging) => { - const i0 = _index || 0; - const i1 = _count ? i0 + _count : undefined; - const artists = this.artists.map(artistWithAlbumsToArtist); - return Promise.resolve([artists.slice(i0, i1), artists.length]); - }, + artists: (paging: Paging) => + Promise.resolve(this.artists.map(artistWithAlbumsToArtist)) + .then(slice2(paging)) + .then(asResult), artist: (id: string) => pipe( this.artists.find((it) => it.id === id), @@ -76,20 +76,19 @@ export class InMemoryMusicService implements MusicService { artistId?: string; _index?: number; _count?: number; - }) => { - const i0 = _index || 0; - const i1 = _count ? i0 + _count : undefined; - const albums = this.artists - .filter( + }) => + Promise.resolve( + this.artists.filter( pipe( O.fromNullable(artistId), O.map(artistWithId), O.getOrElse(() => all) ) ) - .flatMap((it) => it.albums); - return Promise.resolve([albums.slice(i0, i1), albums.length]); - }, + ) + .then((artists) => artists.flatMap((it) => it.albums)) + .then(slice2({ _index, _count })) + .then(asResult), }); } diff --git a/tests/navidrome.test.ts b/tests/navidrome.test.ts index 8bb3a4c..a5c12f1 100644 --- a/tests/navidrome.test.ts +++ b/tests/navidrome.test.ts @@ -114,7 +114,7 @@ describe("navidrome", () => { { id: "3c5113007a6b11eb87173bfb9b07f9b1", name: "AAAB" }, { id: "3ca781c27a6b11eb897ebbb5773603ad", name: "BAAB" }, ]; - expect(artists).toEqual([expectedArtists, 4]); + expect(artists).toEqual({ results: expectedArtists, total: 4 }); expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtists`, { params: authParams, @@ -133,7 +133,7 @@ describe("navidrome", () => { { id: "3c0b9d7a7a6b11eb9773f398e6236ad6", name: "1200 Ounces" }, { id: "3c5113007a6b11eb87173bfb9b07f9b1", name: "AAAB" }, ]; - expect(artists).toEqual([expectedArtists, 4]); + expect(artists).toEqual({ results: expectedArtists, total: 4 }); expect(axios.get).toHaveBeenCalledWith(`${url}/rest/getArtists`, { params: authParams,