mirror of
https://github.com/wkulhanek/bonob.git
synced 2025-12-22 09:53:32 +01:00
no more subsonic in the library
This commit is contained in:
@@ -17,7 +17,7 @@ import {
|
|||||||
import { b64Encode, b64Decode } from "../b64";
|
import { b64Encode, b64Decode } from "../b64";
|
||||||
import { axiosImageFetcher, ImageFetcher } from "../images";
|
import { axiosImageFetcher, ImageFetcher } from "../images";
|
||||||
import { navidromeMusicLibrary, SubsonicGenericMusicLibrary } from "./library";
|
import { navidromeMusicLibrary, SubsonicGenericMusicLibrary } from "./library";
|
||||||
import { http, getJSON as getJSON2 } from "./http";
|
import { getJSON as getJSON2 } from "./subsonic_http";
|
||||||
|
|
||||||
export const t = (password: string, s: string) =>
|
export const t = (password: string, s: string) =>
|
||||||
Md5.hashStr(`${password}${s}`);
|
Md5.hashStr(`${password}${s}`);
|
||||||
@@ -61,6 +61,7 @@ export function isError(
|
|||||||
return (subsonicResponse as SubsonicError).error !== undefined;
|
return (subsonicResponse as SubsonicError).error !== undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo: is this a good name?
|
||||||
export type StreamClientApplication = (track: Track) => string;
|
export type StreamClientApplication = (track: Track) => string;
|
||||||
|
|
||||||
export const DEFAULT_CLIENT_APPLICATION = "bonob";
|
export const DEFAULT_CLIENT_APPLICATION = "bonob";
|
||||||
@@ -94,9 +95,12 @@ export interface SubsonicMusicLibrary extends MusicLibrary {
|
|||||||
|
|
||||||
export class Subsonic implements MusicService {
|
export class Subsonic implements MusicService {
|
||||||
url: string;
|
url: string;
|
||||||
|
|
||||||
|
// todo: does this need to be in here now?
|
||||||
streamClientApplication: StreamClientApplication;
|
streamClientApplication: StreamClientApplication;
|
||||||
// todo: why is this in here?
|
// todo: why is this in here?
|
||||||
externalImageFetcher: ImageFetcher;
|
externalImageFetcher: ImageFetcher;
|
||||||
|
|
||||||
base: Http;
|
base: Http;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -114,9 +118,6 @@ export class Subsonic implements MusicService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: delete
|
|
||||||
http = (credentials: Credentials) => http(this.url, credentials);
|
|
||||||
|
|
||||||
authenticated = (credentials: Credentials, wrap: Http = this.base) =>
|
authenticated = (credentials: Credentials, wrap: Http = this.base) =>
|
||||||
http2(wrap, {
|
http2(wrap, {
|
||||||
params: {
|
params: {
|
||||||
@@ -168,8 +169,7 @@ export class Subsonic implements MusicService {
|
|||||||
credentials: SubsonicCredentials
|
credentials: SubsonicCredentials
|
||||||
): Promise<SubsonicMusicLibrary> => {
|
): Promise<SubsonicMusicLibrary> => {
|
||||||
const subsonicGenericLibrary = new SubsonicGenericMusicLibrary(
|
const subsonicGenericLibrary = new SubsonicGenericMusicLibrary(
|
||||||
this,
|
this.streamClientApplication,
|
||||||
credentials,
|
|
||||||
this.authenticated(credentials, this.base)
|
this.authenticated(credentials, this.base)
|
||||||
);
|
);
|
||||||
if (credentials.type == "navidrome") {
|
if (credentials.type == "navidrome") {
|
||||||
|
|||||||
@@ -27,8 +27,9 @@ import {
|
|||||||
Sortable,
|
Sortable,
|
||||||
Track,
|
Track,
|
||||||
} from "../music_service";
|
} from "../music_service";
|
||||||
import Subsonic, {
|
import {
|
||||||
DODGY_IMAGE_NAME,
|
DODGY_IMAGE_NAME,
|
||||||
|
StreamClientApplication,
|
||||||
SubsonicCredentials,
|
SubsonicCredentials,
|
||||||
SubsonicMusicLibrary,
|
SubsonicMusicLibrary,
|
||||||
SubsonicResponse,
|
SubsonicResponse,
|
||||||
@@ -38,8 +39,8 @@ import axios from "axios";
|
|||||||
import { asURLSearchParams } from "../utils";
|
import { asURLSearchParams } from "../utils";
|
||||||
import { artistSummaryFromNDArtist, NDArtist } from "./navidrome";
|
import { artistSummaryFromNDArtist, NDArtist } from "./navidrome";
|
||||||
//todo: rename http2 -> http
|
//todo: rename http2 -> http
|
||||||
import { Http, http as http2 } from "../http";
|
import { Http, http as http2, RequestParams } from "../http";
|
||||||
import { getRaw2 } from "./http";
|
import { getRaw2, getJSON as getJSON2 } from "./subsonic_http";
|
||||||
|
|
||||||
type album = {
|
type album = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -275,20 +276,22 @@ const maybeAsGenre = (genreName: string | undefined): Genre | undefined =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
||||||
subsonic: Subsonic;
|
streamClientApplication: StreamClientApplication;
|
||||||
credentials: SubsonicCredentials;
|
|
||||||
http: Http;
|
http: Http;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
subsonic: Subsonic,
|
streamClientApplication: StreamClientApplication,
|
||||||
credentials: SubsonicCredentials,
|
|
||||||
http: Http
|
http: Http
|
||||||
) {
|
) {
|
||||||
this.subsonic = subsonic;
|
this.streamClientApplication = streamClientApplication;
|
||||||
this.credentials = credentials;
|
|
||||||
this.http = http;
|
this.http = http;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GET = (query: Partial<RequestParams>) => ({
|
||||||
|
asRAW: () => getRaw2(http2(this.http, query)),
|
||||||
|
asJSON: <T>() => getJSON2<T>(http2(this.http, query)),
|
||||||
|
});
|
||||||
|
|
||||||
flavour = () => "subsonic";
|
flavour = () => "subsonic";
|
||||||
|
|
||||||
bearerToken = (_: Credentials): TE.TaskEither<Error, string | undefined> =>
|
bearerToken = (_: Credentials): TE.TaskEither<Error, string | undefined> =>
|
||||||
@@ -315,8 +318,10 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
album = (id: string): Promise<Album> => this.getAlbum(id);
|
album = (id: string): Promise<Album> => this.getAlbum(id);
|
||||||
|
|
||||||
genres = () =>
|
genres = () =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetGenresResponse>(this.credentials, "/rest/getGenres")
|
url: "/rest/getGenres",
|
||||||
|
})
|
||||||
|
.asJSON<GetGenresResponse>()
|
||||||
.then((it) =>
|
.then((it) =>
|
||||||
pipe(
|
pipe(
|
||||||
it.genres.genre || [],
|
it.genres.genre || [],
|
||||||
@@ -328,10 +333,13 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
);
|
);
|
||||||
|
|
||||||
tracks = (albumId: string) =>
|
tracks = (albumId: string) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetAlbumResponse>(this.credentials, "/rest/getAlbum", {
|
url: "/rest/getAlbum",
|
||||||
|
params: {
|
||||||
id: albumId,
|
id: albumId,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON<GetAlbumResponse>()
|
||||||
.then((it) => it.album)
|
.then((it) => it.album)
|
||||||
.then((album) =>
|
.then((album) =>
|
||||||
(album.song || []).map((song) => asTrack(asAlbum(album), song))
|
(album.song || []).map((song) => asTrack(asAlbum(album), song))
|
||||||
@@ -352,21 +360,23 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
const thingsToUpdate = [];
|
const thingsToUpdate = [];
|
||||||
if (track.rating.love != rating.love) {
|
if (track.rating.love != rating.love) {
|
||||||
thingsToUpdate.push(
|
thingsToUpdate.push(
|
||||||
this.subsonic.getJSON(
|
this.GET({
|
||||||
this.credentials,
|
url: `/rest/${rating.love ? "star" : "unstar"}`,
|
||||||
`/rest/${rating.love ? "star" : "unstar"}`,
|
params: {
|
||||||
{
|
|
||||||
id: trackId,
|
id: trackId,
|
||||||
}
|
},
|
||||||
)
|
}).asJSON()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (track.rating.stars != rating.stars) {
|
if (track.rating.stars != rating.stars) {
|
||||||
thingsToUpdate.push(
|
thingsToUpdate.push(
|
||||||
this.subsonic.getJSON(this.credentials, `/rest/setRating`, {
|
this.GET({
|
||||||
|
url: `/rest/setRating`,
|
||||||
|
params: {
|
||||||
id: trackId,
|
id: trackId,
|
||||||
rating: rating.stars,
|
rating: rating.stars,
|
||||||
})
|
},
|
||||||
|
}).asJSON()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Promise.all(thingsToUpdate);
|
return Promise.all(thingsToUpdate);
|
||||||
@@ -383,12 +393,11 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
}) =>
|
}) =>
|
||||||
// todo: all these headers and stuff can be rolled into httpeee
|
// todo: all these headers and stuff can be rolled into httpeee
|
||||||
this.getTrack(trackId).then((track) =>
|
this.getTrack(trackId).then((track) =>
|
||||||
getRaw2(
|
this.GET({
|
||||||
http2(this.http, {
|
url: "/rest/stream",
|
||||||
url: `/rest/stream`,
|
|
||||||
params: {
|
params: {
|
||||||
id: trackId,
|
id: trackId,
|
||||||
c: this.subsonic.streamClientApplication(track),
|
c: this.streamClientApplication(track),
|
||||||
},
|
},
|
||||||
headers: pipe(
|
headers: pipe(
|
||||||
range,
|
range,
|
||||||
@@ -403,7 +412,7 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
),
|
),
|
||||||
responseType: "stream",
|
responseType: "stream",
|
||||||
})
|
})
|
||||||
)
|
.asRAW()
|
||||||
.then((res) => ({
|
.then((res) => ({
|
||||||
status: res.status,
|
status: res.status,
|
||||||
headers: {
|
headers: {
|
||||||
@@ -420,7 +429,7 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
Promise.resolve(coverArtURN)
|
Promise.resolve(coverArtURN)
|
||||||
.then((it) => assertSystem(it, "subsonic"))
|
.then((it) => assertSystem(it, "subsonic"))
|
||||||
.then((it) => it.resource.split(":")[1]!)
|
.then((it) => it.resource.split(":")[1]!)
|
||||||
.then((it) => this.getCoverArt(this.credentials, it, size))
|
.then((it) => this.getCoverArt(it, size))
|
||||||
.then((res) => ({
|
.then((res) => ({
|
||||||
contentType: res.headers["content-type"],
|
contentType: res.headers["content-type"],
|
||||||
data: Buffer.from(res.data, "binary"),
|
data: Buffer.from(res.data, "binary"),
|
||||||
@@ -433,20 +442,26 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
});
|
});
|
||||||
|
|
||||||
scrobble = async (id: string) =>
|
scrobble = async (id: string) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON(this.credentials, `/rest/scrobble`, {
|
url: `/rest/scrobble`,
|
||||||
|
params: {
|
||||||
id,
|
id,
|
||||||
submission: true,
|
submission: true,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON()
|
||||||
.then((_) => true)
|
.then((_) => true)
|
||||||
.catch(() => false);
|
.catch(() => false);
|
||||||
|
|
||||||
nowPlaying = async (id: string) =>
|
nowPlaying = async (id: string) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON(this.credentials, `/rest/scrobble`, {
|
url: `/rest/scrobble`,
|
||||||
|
params: {
|
||||||
id,
|
id,
|
||||||
submission: false,
|
submission: false,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON()
|
||||||
.then((_) => true)
|
.then((_) => true)
|
||||||
.catch(() => false);
|
.catch(() => false);
|
||||||
|
|
||||||
@@ -473,18 +488,21 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
);
|
);
|
||||||
|
|
||||||
playlists = async () =>
|
playlists = async () =>
|
||||||
this.subsonic
|
this.GET({ url: "/rest/getPlaylists" })
|
||||||
.getJSON<GetPlaylistsResponse>(this.credentials, "/rest/getPlaylists")
|
.asJSON<GetPlaylistsResponse>()
|
||||||
.then((it) => it.playlists.playlist || [])
|
.then((it) => it.playlists.playlist || [])
|
||||||
.then((playlists) =>
|
.then((playlists) =>
|
||||||
playlists.map((it) => ({ id: it.id, name: it.name }))
|
playlists.map((it) => ({ id: it.id, name: it.name }))
|
||||||
);
|
);
|
||||||
|
|
||||||
playlist = async (id: string) =>
|
playlist = async (id: string) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetPlaylistResponse>(this.credentials, "/rest/getPlaylist", {
|
url: "/rest/getPlaylist",
|
||||||
|
params: {
|
||||||
id,
|
id,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON<GetPlaylistResponse>()
|
||||||
.then((it) => it.playlist)
|
.then((it) => it.playlist)
|
||||||
.then((playlist) => {
|
.then((playlist) => {
|
||||||
let trackNumber = 1;
|
let trackNumber = 1;
|
||||||
@@ -510,43 +528,54 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
});
|
});
|
||||||
|
|
||||||
createPlaylist = async (name: string) =>
|
createPlaylist = async (name: string) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetPlaylistResponse>(this.credentials, "/rest/createPlaylist", {
|
url: "/rest/createPlaylist",
|
||||||
|
params: {
|
||||||
name,
|
name,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON<GetPlaylistResponse>()
|
||||||
.then((it) => it.playlist)
|
.then((it) => it.playlist)
|
||||||
.then((it) => ({ id: it.id, name: it.name }));
|
.then((it) => ({ id: it.id, name: it.name }));
|
||||||
|
|
||||||
deletePlaylist = async (id: string) =>
|
deletePlaylist = async (id: string) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetPlaylistResponse>(this.credentials, "/rest/deletePlaylist", {
|
url: "/rest/deletePlaylist",
|
||||||
|
params: {
|
||||||
id,
|
id,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON<GetPlaylistResponse>()
|
||||||
.then((_) => true);
|
.then((_) => true);
|
||||||
|
|
||||||
addToPlaylist = async (playlistId: string, trackId: string) =>
|
addToPlaylist = async (playlistId: string, trackId: string) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetPlaylistResponse>(this.credentials, "/rest/updatePlaylist", {
|
url: "/rest/updatePlaylist",
|
||||||
|
params: {
|
||||||
playlistId,
|
playlistId,
|
||||||
songIdToAdd: trackId,
|
songIdToAdd: trackId,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON<GetPlaylistResponse>()
|
||||||
.then((_) => true);
|
.then((_) => true);
|
||||||
|
|
||||||
removeFromPlaylist = async (playlistId: string, indicies: number[]) =>
|
removeFromPlaylist = async (playlistId: string, indicies: number[]) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetPlaylistResponse>(this.credentials, "/rest/updatePlaylist", {
|
url: "/rest/updatePlaylist",
|
||||||
|
params: {
|
||||||
playlistId,
|
playlistId,
|
||||||
songIndexToRemove: indicies,
|
songIndexToRemove: indicies,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON<GetPlaylistResponse>()
|
||||||
.then((_) => true);
|
.then((_) => true);
|
||||||
|
|
||||||
similarSongs = async (id: string) =>
|
similarSongs = async (id: string) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetSimilarSongsResponse>(
|
url: "/rest/getSimilarSongs2",
|
||||||
this.credentials,
|
params: { id, count: 50 },
|
||||||
"/rest/getSimilarSongs2",
|
})
|
||||||
{ id, count: 50 }
|
.asJSON<GetSimilarSongsResponse>()
|
||||||
)
|
|
||||||
.then((it) => it.similarSongs2.song || [])
|
.then((it) => it.similarSongs2.song || [])
|
||||||
.then((songs) =>
|
.then((songs) =>
|
||||||
Promise.all(
|
Promise.all(
|
||||||
@@ -558,11 +587,14 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
|
|
||||||
topSongs = async (artistId: string) =>
|
topSongs = async (artistId: string) =>
|
||||||
this.getArtist(artistId).then(({ name }) =>
|
this.getArtist(artistId).then(({ name }) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetTopSongsResponse>(this.credentials, "/rest/getTopSongs", {
|
url: "/rest/getTopSongs",
|
||||||
|
params: {
|
||||||
artist: name,
|
artist: name,
|
||||||
count: 50,
|
count: 50,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON<GetTopSongsResponse>()
|
||||||
.then((it) => it.topSongs.song || [])
|
.then((it) => it.topSongs.song || [])
|
||||||
.then((songs) =>
|
.then((songs) =>
|
||||||
Promise.all(
|
Promise.all(
|
||||||
@@ -576,8 +608,8 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
private getArtists = (): Promise<
|
private getArtists = (): Promise<
|
||||||
(IdName & { albumCount: number; image: BUrn | undefined })[]
|
(IdName & { albumCount: number; image: BUrn | undefined })[]
|
||||||
> =>
|
> =>
|
||||||
this.subsonic
|
this.GET({ url: "/rest/getArtists" })
|
||||||
.getJSON<GetArtistsResponse>(this.credentials, "/rest/getArtists")
|
.asJSON<GetArtistsResponse>()
|
||||||
.then((it) => (it.artists.index || []).flatMap((it) => it.artist || []))
|
.then((it) => (it.artists.index || []).flatMap((it) => it.artist || []))
|
||||||
.then((artists) =>
|
.then((artists) =>
|
||||||
artists.map((artist) => ({
|
artists.map((artist) => ({
|
||||||
@@ -601,16 +633,15 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
l: string | undefined;
|
l: string | undefined;
|
||||||
};
|
};
|
||||||
}> =>
|
}> =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetArtistInfoResponse>(
|
url: "/rest/getArtistInfo2",
|
||||||
this.credentials,
|
params: {
|
||||||
"/rest/getArtistInfo2",
|
|
||||||
{
|
|
||||||
id,
|
id,
|
||||||
count: 50,
|
count: 50,
|
||||||
includeNotPresent: true,
|
includeNotPresent: true,
|
||||||
}
|
},
|
||||||
)
|
})
|
||||||
|
.asJSON<GetArtistInfoResponse>()
|
||||||
.then((it) => it.artistInfo2)
|
.then((it) => it.artistInfo2)
|
||||||
.then((it) => ({
|
.then((it) => ({
|
||||||
images: {
|
images: {
|
||||||
@@ -630,8 +661,8 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
private getAlbum = (id: string): Promise<Album> =>
|
private getAlbum = (id: string): Promise<Album> =>
|
||||||
this.subsonic
|
this.GET({ url: "/rest/getAlbum", params: { id } })
|
||||||
.getJSON<GetAlbumResponse>(this.credentials, "/rest/getAlbum", { id })
|
.asJSON<GetAlbumResponse>()
|
||||||
.then((it) => it.album)
|
.then((it) => it.album)
|
||||||
.then((album) => ({
|
.then((album) => ({
|
||||||
id: album.id,
|
id: album.id,
|
||||||
@@ -648,10 +679,13 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
): Promise<
|
): Promise<
|
||||||
IdName & { artistImageUrl: string | undefined; albums: AlbumSummary[] }
|
IdName & { artistImageUrl: string | undefined; albums: AlbumSummary[] }
|
||||||
> =>
|
> =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetArtistResponse>(this.credentials, "/rest/getArtist", {
|
url: "/rest/getArtist",
|
||||||
|
params: {
|
||||||
id,
|
id,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON<GetArtistResponse>()
|
||||||
.then((it) => it.artist)
|
.then((it) => it.artist)
|
||||||
.then((it) => ({
|
.then((it) => ({
|
||||||
id: it.id,
|
id: it.id,
|
||||||
@@ -679,18 +713,21 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
private getCoverArt = (credentials: Credentials, id: string, size?: number) =>
|
private getCoverArt = (id: string, size?: number) =>
|
||||||
getRaw2(http2(this.subsonic.authenticated(credentials), {
|
this.GET({
|
||||||
url: "/rest/getCoverArt",
|
url: "/rest/getCoverArt",
|
||||||
params: { id, size },
|
params: { id, size },
|
||||||
responseType: "arraybuffer",
|
responseType: "arraybuffer",
|
||||||
}));
|
}).asRAW();
|
||||||
|
|
||||||
private getTrack = (id: string) =>
|
private getTrack = (id: string) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetSongResponse>(this.credentials, "/rest/getSong", {
|
url: "/rest/getSong",
|
||||||
|
params: {
|
||||||
id,
|
id,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON<GetSongResponse>()
|
||||||
.then((it) => it.song)
|
.then((it) => it.song)
|
||||||
.then((song) =>
|
.then((song) =>
|
||||||
this.getAlbum(song.albumId!).then((album) => asTrack(album, song))
|
this.getAlbum(song.albumId!).then((album) => asTrack(album, song))
|
||||||
@@ -708,13 +745,16 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
private search3 = (q: any) =>
|
private search3 = (q: any) =>
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<Search3Response>(this.credentials, "/rest/search3", {
|
url: "/rest/search3",
|
||||||
|
params: {
|
||||||
artistCount: 0,
|
artistCount: 0,
|
||||||
albumCount: 0,
|
albumCount: 0,
|
||||||
songCount: 0,
|
songCount: 0,
|
||||||
...q,
|
...q,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
.asJSON<Search3Response>()
|
||||||
.then((it) => ({
|
.then((it) => ({
|
||||||
artists: it.searchResult3.artist || [],
|
artists: it.searchResult3.artist || [],
|
||||||
albums: it.searchResult3.album || [],
|
albums: it.searchResult3.album || [],
|
||||||
@@ -726,17 +766,16 @@ export class SubsonicGenericMusicLibrary implements SubsonicMusicLibrary {
|
|||||||
this.getArtists().then((it) =>
|
this.getArtists().then((it) =>
|
||||||
inject(it, (total, artist) => total + artist.albumCount, 0)
|
inject(it, (total, artist) => total + artist.albumCount, 0)
|
||||||
),
|
),
|
||||||
this.subsonic
|
this.GET({
|
||||||
.getJSON<GetAlbumListResponse>(
|
url: "/rest/getAlbumList2",
|
||||||
this.credentials,
|
params: {
|
||||||
"/rest/getAlbumList2",
|
|
||||||
{
|
|
||||||
type: AlbumQueryTypeToSubsonicType[q.type],
|
type: AlbumQueryTypeToSubsonicType[q.type],
|
||||||
...(q.genre ? { genre: b64Decode(q.genre) } : {}),
|
...(q.genre ? { genre: b64Decode(q.genre) } : {}),
|
||||||
size: 500,
|
size: 500,
|
||||||
offset: q._index,
|
offset: q._index,
|
||||||
}
|
},
|
||||||
)
|
})
|
||||||
|
.asJSON<GetAlbumListResponse>()
|
||||||
.then((response) => response.albumList2.album || [])
|
.then((response) => response.albumList2.album || [])
|
||||||
.then(this.toAlbumSummary),
|
.then(this.toAlbumSummary),
|
||||||
]).then(([total, albums]) => ({
|
]).then(([total, albums]) => ({
|
||||||
@@ -758,11 +797,12 @@ export const navidromeMusicLibrary = (
|
|||||||
pipe(
|
pipe(
|
||||||
TE.tryCatch(
|
TE.tryCatch(
|
||||||
() =>
|
() =>
|
||||||
|
// todo: not hardcode axios in here
|
||||||
axios({
|
axios({
|
||||||
method: 'post',
|
method: "post",
|
||||||
baseURL: url,
|
baseURL: url,
|
||||||
url: `/auth/login`,
|
url: `/auth/login`,
|
||||||
data: _.pick(credentials, "username", "password")
|
data: _.pick(credentials, "username", "password"),
|
||||||
}),
|
}),
|
||||||
() => new AuthFailure("Failed to get bearerToken")
|
() => new AuthFailure("Failed to get bearerToken")
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -54,28 +54,6 @@ export interface HTTP {
|
|||||||
): Promise<HttpResponse>;
|
): Promise<HttpResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// export const basic = (opts : AxiosRequestConfig) => axios(opts);
|
|
||||||
|
|
||||||
// function whatDoesItLookLike() {
|
|
||||||
// const basic = axios;
|
|
||||||
|
|
||||||
// const authenticatedClient = httpee(axios, chain(
|
|
||||||
// baseUrl("http://foobar"),
|
|
||||||
// subsonicAuth({username: 'bob', password: 'foo'})
|
|
||||||
// ));
|
|
||||||
// const jsonClient = httpee(authenticatedClient, formatJson())
|
|
||||||
|
|
||||||
// jsonClient({ })
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .then((response) => response.data as SubsonicEnvelope)
|
|
||||||
// .then((json) => json["subsonic-response"])
|
|
||||||
// .then((json) => {
|
|
||||||
// if (isError(json)) throw `Subsonic error:${json.error.message}`;
|
|
||||||
// else return json as unknown as T;
|
|
||||||
// });
|
|
||||||
|
|
||||||
export const raw = (response: AxiosPromise<any>) =>
|
export const raw = (response: AxiosPromise<any>) =>
|
||||||
response
|
response
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
@@ -87,7 +65,6 @@ export const raw = (response: AxiosPromise<any>) =>
|
|||||||
} else return response;
|
} else return response;
|
||||||
});
|
});
|
||||||
|
|
||||||
// todo: delete
|
|
||||||
export const getRaw2 = (http: Http) =>
|
export const getRaw2 = (http: Http) =>
|
||||||
http({ method: "get" })
|
http({ method: "get" })
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
@@ -44,7 +44,7 @@ import {
|
|||||||
SubsonicGenericMusicLibrary,
|
SubsonicGenericMusicLibrary,
|
||||||
} from "../../src/subsonic/library";
|
} from "../../src/subsonic/library";
|
||||||
import { EMPTY, error, FAILURE, subsonicOK, ok } from "../subsonic.test";
|
import { EMPTY, error, FAILURE, subsonicOK, ok } from "../subsonic.test";
|
||||||
import Subsonic, { DODGY_IMAGE_NAME, t } from "../../src/subsonic";
|
import { DODGY_IMAGE_NAME, t } from "../../src/subsonic";
|
||||||
import { b64Encode } from "../../src/b64";
|
import { b64Encode } from "../../src/b64";
|
||||||
import { http as http2 } from "../../src/http";
|
import { http as http2 } from "../../src/http";
|
||||||
|
|
||||||
@@ -490,7 +490,6 @@ describe("SubsonicGenericMusicLibrary", () => {
|
|||||||
const salt = "saltysalty";
|
const salt = "saltysalty";
|
||||||
|
|
||||||
const streamClientApplication = jest.fn();
|
const streamClientApplication = jest.fn();
|
||||||
const subsonic = new Subsonic(url, streamClientApplication)
|
|
||||||
|
|
||||||
const authParams = {
|
const authParams = {
|
||||||
u: username,
|
u: username,
|
||||||
@@ -510,13 +509,7 @@ describe("SubsonicGenericMusicLibrary", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const generic = new SubsonicGenericMusicLibrary(
|
const generic = new SubsonicGenericMusicLibrary(
|
||||||
subsonic,
|
streamClientApplication,
|
||||||
{
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
type: 'subsonic',
|
|
||||||
bearer: undefined
|
|
||||||
},
|
|
||||||
// todo: all this stuff doesnt need to be defaulted in here.
|
// todo: all this stuff doesnt need to be defaulted in here.
|
||||||
http2(mockAxios, {
|
http2(mockAxios, {
|
||||||
baseURL,
|
baseURL,
|
||||||
|
|||||||
Reference in New Issue
Block a user