From c7352aefa32da8905a666a328f30ccf1b0600f03 Mon Sep 17 00:00:00 2001 From: simojenki Date: Fri, 10 Dec 2021 16:44:49 +1100 Subject: [PATCH] scroll indicies based on name, for nd needs to be based on sortname from nd api --- src/smapi.ts | 66 ++++++++++++++++++++++++++++++++++++++++++ tests/smapi.test.ts | 70 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 135 insertions(+), 1 deletion(-) diff --git a/src/smapi.ts b/src/smapi.ts index 9d5686b..d06c6d8 100644 --- a/src/smapi.ts +++ b/src/smapi.ts @@ -366,6 +366,54 @@ export const artist = (bonobUrl: URLBuilder, artist: ArtistSummary) => ({ albumArtURI: defaultArtistArtURI(bonobUrl, artist).href(), }); +export const scrollIndicesFrom = (artists: ArtistSummary[]) => { + const indicies: Record = { + "A":undefined, + "B":undefined, + "C":undefined, + "D":undefined, + "E":undefined, + "F":undefined, + "G":undefined, + "H":undefined, + "I":undefined, + "J":undefined, + "K":undefined, + "L":undefined, + "M":undefined, + "N":undefined, + "O":undefined, + "P":undefined, + "Q":undefined, + "R":undefined, + "S":undefined, + "T":undefined, + "U":undefined, + "V":undefined, + "W":undefined, + "X":undefined, + "Y":undefined, + "Z":undefined, + } + const sortedNames = artists.map(artist => artist.name.toUpperCase()).sort(); + for(var i = 0; i < sortedNames.length; i++) { + const char = sortedNames[i]![0]!; + if(Object.keys(indicies).includes(char) && indicies[char] == undefined) { + indicies[char] = i; + } + } + var lastIndex = 0; + const result: string[] = []; + Object.entries(indicies).forEach(([letter, index]) => { + result.push(letter); + if(index) { + lastIndex = index; + } + result.push(`${lastIndex}`); + }) + return result.join(",") +} + function splitId(id: string) { const [type, typeId] = id.split(":"); return (t: T) => ({ @@ -707,6 +755,7 @@ function bindSmapiSoapServiceToExpress( title: lang("artists"), albumArtURI: iconArtURI(bonobUrl, "artists").href(), itemType: "container", + canScroll: true, }, { id: "albums", @@ -945,6 +994,23 @@ function bindSmapiSoapServiceToExpress( throw `Unsupported getMetadata id=${id}`; } }), + getScrollIndices: async ( + { id }: { id: string }, + _, + soapyHeaders: SoapyHeaders + ) => { + switch(id) { + case "artists": { + return login(soapyHeaders?.credentials) + .then(({ musicLibrary }) => musicLibrary.artists({ _index: 0, _count: 999999999 })) + .then((artists) => ({ + getScrollIndicesResult: scrollIndicesFrom(artists.results) + })) + } + default: + throw `Unsupported getScrollIndices id=${id}`; + } + }, createContainer: async ( { title, seedId }: { title: string; seedId: string | undefined }, _, diff --git a/tests/smapi.test.ts b/tests/smapi.test.ts index fbbae83..df5b500 100644 --- a/tests/smapi.test.ts +++ b/tests/smapi.test.ts @@ -26,6 +26,7 @@ import { sonosifyMimeType, ratingAsInt, ratingFromInt, + scrollIndicesFrom, } from "../src/smapi"; import { keys as i8nKeys } from "../src/i8n"; @@ -56,7 +57,7 @@ import dayjs from "dayjs"; import url, { URLBuilder } from "../src/url_builder"; import { iconForGenre } from "../src/icon"; import { formatForURL } from "../src/burn"; -import { range } from "underscore"; +import _, { range } from "underscore"; import { FixedClock } from "../src/clock"; import { ExpiredTokenError, InvalidTokenError, SmapiAuthTokens, SmapiToken, ToSmapiFault } from "../src/smapi_auth"; @@ -860,6 +861,29 @@ describe("defaultArtistArtURI", () => { }); }); +describe("scrollIndicesFrom", () => { + describe("artists", () => { + it("should be scroll indicies", () => { + const artistNames = [ + "10,000 Maniacs", + "99 Bacon Sandwiches", + "Aerosmith", + "Bob Marley", + "beatles", // intentionally lower case + "Cans", + "egg heads", // intentionally lower case + "Moon Cakes", + "Moon Boots", + "Numpty", + "Yellow brick road" + ] + const scrollIndicies = scrollIndicesFrom(_.shuffle(artistNames).map(name => anArtist({ name }))) + + expect(scrollIndicies).toEqual("A,2,B,3,C,5,D,5,E,6,F,6,G,6,H,6,I,6,J,6,K,6,L,6,M,7,N,9,O,9,P,9,Q,9,R,9,S,9,T,9,U,9,V,9,W,9,X,9,Y,10,Z,10") + }); + }); +}); + describe("wsdl api", () => { const musicService = { generateToken: jest.fn(), @@ -1410,6 +1434,7 @@ describe("wsdl api", () => { title: "Artists", albumArtURI: iconArtURI(bonobUrl, "artists").href(), itemType: "container", + canScroll: true, }, { id: "albums", @@ -1498,6 +1523,7 @@ describe("wsdl api", () => { title: "Artiesten", albumArtURI: iconArtURI(bonobUrl, "artists").href(), itemType: "container", + canScroll: true, }, { id: "albums", @@ -3111,6 +3137,48 @@ describe("wsdl api", () => { }); }); + describe("getScrollIndices", () => { + itShouldHandleInvalidCredentials((ws) => + ws.getScrollIndicesAsync({ id: `artists` }) + ); + + describe("for artists", () => { + let ws: Client; + + const artist1 = anArtist({ name: "Aerosmith" }); + const artist2 = anArtist({ name: "Bob Marley" }); + const artist3 = anArtist({ name: "Beatles" }); + const artist4 = anArtist({ name: "Cat Empire" }); + const artist5 = anArtist({ name: "Metallica" }); + const artist6 = anArtist({ name: "Yellow Brick Road" }); + + beforeEach(async () => { + ws = await createClientAsync(`${service.uri}?wsdl`, { + endpoint: service.uri, + httpClient: supersoap(server), + }); + setupAuthenticatedRequest(ws); + musicLibrary.artists.mockResolvedValue({ + results: [artist1, artist2, artist3, artist4, artist5, artist6], + total: 6 + }); + }); + + it("should return paging information", async () => { + const root = await ws.getScrollIndicesAsync({ + id: `artists`, + }); + + expect(root[0]).toEqual({ + getScrollIndicesResult: scrollIndicesFrom([artist1, artist2, artist3, artist4, artist5, artist6]) + }); + expect(musicService.login).toHaveBeenCalledWith(serviceToken); + expect(apiTokens.mint).toHaveBeenCalledWith(serviceToken); + expect(musicLibrary.artists).toHaveBeenCalledWith({ _index: 0, _count: 999999999 }); + }); + }); + }); + describe("createContainer", () => { let ws: Client;