diff --git a/src/subsonic.ts b/src/subsonic.ts index 9783aee..926f491 100644 --- a/src/subsonic.ts +++ b/src/subsonic.ts @@ -1,4 +1,4 @@ -import { option as O } from "fp-ts"; +import { option as O, taskEither as TE } from "fp-ts"; import * as A from "fp-ts/Array"; import { ordString } from "fp-ts/lib/Ord"; import { pipe } from "fp-ts/lib/function"; @@ -15,6 +15,7 @@ import { Encoding, albumToAlbumSummary, TrackSummary, + AuthFailure } from "./music_library"; import sharp from "sharp"; import _ from "underscore"; @@ -531,10 +532,9 @@ export class Subsonic { } else return response; }); - // todo: make private // todo: should I put a catch in here and force a subsonic fail status? // or there is a catch above, that then throws, perhaps can go in there? - getJSON = async ( + private getJSON = async ( { username, password }: Credentials, path: string, q: {} = {} @@ -547,6 +547,17 @@ export class Subsonic { else return json as unknown as T; }); + ping = (credentials: Credentials): TE.TaskEither => + TE.tryCatch( + () => this.getJSON(credentials, "/rest/ping.view") + .then(it => ({ + authenticated: it.status == "ok", + type: it.type + })), + (e) => new AuthFailure(e as string) + ) + + getArtists = ( credentials: Credentials ): Promise<(IdName & { albumCount: number; image: BUrn | undefined })[]> => @@ -861,5 +872,19 @@ export class Subsonic { .then((it) => (it.topSongs.song || []).map(it => asTrackSummary(it, this.customPlayers)) ); - -} + + getInternetRadioStations = (credentials: Credentials) => + this.getJSON( + credentials, + "/rest/getInternetRadioStations" + ) + .then((it) => it.internetRadioStations.internetRadioStation || []) + .then((stations) => + stations.map((it) => ({ + id: it.id, + name: it.name, + url: it.streamUrl, + homePage: it.homePageUrl, + })) + ); +}; diff --git a/src/subsonic_music_library.ts b/src/subsonic_music_library.ts index ae59d30..5010d6c 100644 --- a/src/subsonic_music_library.ts +++ b/src/subsonic_music_library.ts @@ -19,12 +19,10 @@ import { import { Subsonic, CustomPlayers, - PingResponse, NO_CUSTOM_PLAYERS, asToken, parseToken, artistImageURN, - GetInternetRadioStationsResponse, asYear, isValidImage } from "./subsonic"; @@ -48,39 +46,23 @@ export class SubsonicMusicService implements MusicService { generateToken = ( credentials: Credentials - ): TE.TaskEither => { - const x: TE.TaskEither = TE.tryCatch( - () => - this.subsonic.getJSON( - _.pick(credentials, "username", "password"), - "/rest/ping.view" - ), - (e) => new AuthFailure(e as string) - ); - return pipe( - x, - TE.flatMap(({ type }) => - pipe( - TE.tryCatch( - () => this.libraryFor({ ...credentials, type }), - () => new AuthFailure("Failed to get library") - ), - TE.map((library) => ({ type, library })) - ) - ), - TE.flatMap(({ library, type }) => - pipe( - library.bearerToken(credentials), - TE.map((bearer) => ({ bearer, type })) - ) - ), - TE.map(({ bearer, type }) => ({ + ): TE.TaskEither => + pipe( + this.subsonic.ping(credentials), + TE.flatMap(({ type }) => TE.tryCatch( + () => this.libraryFor({ ...credentials, type }).then(library => ({ type, library })), + () => new AuthFailure("Failed to get library") + )), + TE.flatMap(({ library, type }) => pipe( + library.bearerToken(credentials), + TE.map(bearer => ({ bearer, type })) + )), + TE.map(({ bearer, type}) => ({ serviceToken: asToken({ ...credentials, bearer, type }), userId: credentials.username, nickname: credentials.username, })) ); - }; refreshToken = (serviceToken: string) => this.generateToken(parseToken(serviceToken)); @@ -310,20 +292,7 @@ export class SubsonicMusicLibrary implements MusicLibrary { ); radioStations = async () => - this.subsonic - .getJSON( - this.credentials, - "/rest/getInternetRadioStations" - ) - .then((it) => it.internetRadioStations.internetRadioStation || []) - .then((stations) => - stations.map((it) => ({ - id: it.id, - name: it.name, - url: it.streamUrl, - homePage: it.homePageUrl, - })) - ); + this.subsonic.getInternetRadioStations(this.credentials); radioStation = async (id: string) => this.radioStations().then((it) => it.find((station) => station.id === id)!);